📒
My Pentesting Cheatsheet
  • Home
  • Commands Only Summary
    • Some other cool websites
  • Preparation
    • Documents
    • Contract - Checklist
    • Rules of Engagement - Checklist
    • Contractors Agreement - Checklist for Physical Assessments
  • Information Gathering
  • Vulnerability Assessment
  • Pentesting Machine
  • Enumeration
    • NMAP Scan types explained
    • Firewall and IDS/IPS Evasion
  • Footprinting
    • Google Dorks
    • Samba (smb)
    • NFS
    • DNS
    • SMTP
    • IMAP/POP3
    • SNMP
    • MySQL
    • MSSQL
    • Oracle TNS
    • IPMI
    • SSH
    • RDP
    • WinRM
  • Web Information Gathering
    • Whois
    • DNS & Subdomains
    • Fingerprinting
    • Crawlers
    • Search Engine Discovery
    • Automating Recon
  • Vulnerability Assessment
  • File Transfers
    • Windows Target
    • Linux Target
    • Transferring Files with Code
    • Miscellaneous File Transfer Methods
    • Protected Files Transfer
    • Catching Files over HTTP/S (Nginx)
    • Living Off The Land
    • Evading Detection
  • Shells & Payloads
    • Reverse Shells + Bind + Web
  • Password Attacks
    • John the ripper
    • Remote password attacks
    • Password mutations
    • Password Reuse / Default Passwords
    • Windows Local Password Attacks
    • Linux Local Password Attacks
    • Windows Lateral Movement
    • Cracking Files
  • Attacking Common Services
    • FTP
    • SMB
    • SQL
    • RDP
    • DNS
    • Email Services
  • Pivoting, Tunneling, and Port Forwarding
    • Choosing The Dig Site & Starting Our Tunnels
    • Playing Pong with Socat
    • Pivoting Around Obstacles
    • Branching Out Our Tunnels
    • Double Pivots
    • Final considerations
  • Active Directory Enumeration & Attacks
    • Initial Enumeration
    • Sniffing out a Foothold
    • Sighting In, Hunting For A User
    • Spray Responsibly
    • Deeper Down the Rabbit Hole
    • Kerberoasting - Cooking with Fire
    • Access Control List (ACL)
    • Advanced Privilege Escalation in Active Directory: Stacking The Deck
    • Domain trusts
    • Domain Trusts - Cross Forest
    • Defensive Considerations
  • Using Web Proxies
  • Login Brute Forcing
  • SQL Injection Fundamentals
    • Mitigating SQL Injection
  • SQLMap Essentials
    • Building Attacks
    • Database Enumeration
    • Advanced SQLMap Usage
  • Cross-Site Scripting (XSS)
    • Prevention
  • File Inclusion
  • File Upload Attacks
    • Basic Exploitation
    • Bypassing Filters
    • Other Upload Attacks
    • Prevention
  • Command Injections
    • Exploitation
    • Filter Evasion
  • Web Attacks
    • HTTP Verb Tampering
    • Insecure Direct Object References (IDOR)
    • XML External Entity (XXE) Injection
    • GraphQL
  • Attacking Common Applications
    • Application Discovery & Enumeration
    • Content Management Systems (CMS)
    • Servlet Containers/Software Development
    • Infrastructure/Network Monitoring Tools
    • Customer Service Mgmt & Configuration Management
    • Common Gateway Interfaces
    • Thick Client Applications
    • Miscellaneous Applications
  • Privilege Escalation
    • Linux Privilege Escalation
      • Information Gathering
      • Environment-based Privilege Escalation
      • Service-based Privilege Escalation
      • Linux Internals-based Privilege Escalation
      • Recent 0-Days
      • Linux Hardening
    • Windows Privilege Escalation
      • Getting the Lay of the Land
      • Windows User Privileges
      • Windows Group Privileges
      • Attacking the OS
      • Credential Theft
      • Restricted Environments
      • Additional Techniques
      • Dealing with End of Life Systems
      • Windows Hardening
    • Windows (old page)
  • Documentation & Reporting
    • Preparation
    • Reporting
  • Attacking Enterprise Networks
    • Pre-Engagement
    • External Testing
    • Internal Testing
    • Lateral Movement & Privilege Escalation
    • Wrapping Up
  • Deobfuscation
  • Metasploit
    • msfvenom
  • Custom compiled files
  • XSS
  • Azure AD (Entra ID)
Powered by GitBook
On this page
  • Client-Side Validation
  • Disabling Front-end Validation
  • Blacklist Filters
  • Fuzzing Extensions
  • Non-Blacklisted Extensions
  • Whitelist Filters
  • Whitelisting Extensions
  • Double Extensions
  • Reverse Double Extension
  • Character Injection
  • Type Filters
  • Content-Type
  • MIME-Type
  1. File Upload Attacks

Bypassing Filters

PreviousBasic ExploitationNextOther Upload Attacks

Last updated 4 months ago

Client-Side Validation

To bypass these protections, we can either modify the upload request to the back-end server, or we can manipulate the front-end code to disable these type validations.

Let's start by examining a normal request through Burp. When we select an image, we see that it gets reflected as our profile image, and when we click on Upload, our profile image gets updated and persists through refreshes. This indicates that our image was uploaded to the server, which is now displaying it back to us

If we capture the upload request with Burp, we see the following request being sent by the web application:

The web application appears to be sending a standard HTTP upload request to /upload.php. This way, we can now modify this request to meet our needs without having the front-end type validation restrictions. If the back-end server does not validate the uploaded file type, then we should theoretically be able to send any file type/content, and it would be uploaded to the server.

The two important parts in the request are filename="HTB.png" and the file content at the end of the request. If we modify the filename to shell.php and modify the content to the web shell we used in the previous section; we would be uploading a PHP web shell instead of an image.

So, let's capture another image upload request, and then modify it accordingly:

We may also modify the Content-Type of the uploaded file, though this should not play an important role at this stage, so we'll keep it unmodified.

Disabling Front-end Validation

To start, we can click [CTRL+SHIFT+C] to toggle the browser's Page Inspector, and then click on the profile image, which is where we trigger the file selector for the upload form:

This will highlight the following HTML file input on line 18:

<input type="file" name="uploadFile" id="uploadFile" onchange="checkFile(this)" accept=".jpg,.jpeg,.png">

Here, we see that the file input specifies (.jpg,.jpeg,.png) as the allowed file types within the file selection dialog. However, we can easily modify this and select All Files as we did before, so it is unnecessary to change this part of the page.

The more interesting part is onchange="checkFile(this)", which appears to run a JavaScript code whenever we select a file, which appears to be doing the file type validation. To get the details of this function, we can go to the browser's Console by clicking [CTRL+SHIFT+K], and then we can type the function's name (checkFile) to get its details:

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...
    }
}

The key thing we take from this function is where it checks whether the file extension is an image, and if it is not, it prints the error message we saw earlier (Only images are allowed!) and disables the Upload button. We can add PHP as one of the allowed extensions or modify the function to remove the extension check.

Luckily, we do not need to get into writing and modifying JavaScript code. We can remove this function from the HTML code since its primary use appears to be file type validation, and removing it should not break anything.

To do so, we can go back to our inspector, click on the profile image again, double-click on the function name (checkFile) on line 18, and delete it

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.

Once we upload our web shell using either of the above methods and then refresh the page, we can use the Page Inspector once more with [CTRL+SHIFT+C], click on the profile image, and we should see the URL of our uploaded web shell:

<img src="/profile_images/shell.php" class="profile-image" id="profile-image">

Blacklist Filters

As we can see, our attack did not succeed this time, as we got Extension not allowed. This indicates that the web application may have some form of file type validation on the back-end, in addition to the front-end validations.

There are generally two common forms of validating a file extension on the back-end:

  1. Testing against a blacklist of types

  2. Testing against a whitelist of types

Furthermore, the validation may also check the file type or the file content for type matching. The weakest form of validation amongst these is testing the file extension against a blacklist of extension to determine whether the upload request should be blocked. For example, the following piece of code checks if the uploaded file extension is PHP and drops the request if it is:

$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();
}

The code is taking the file extension ($extension) from the uploaded file name ($fileName) and then comparing it against a list of blacklisted extensions ($blacklist). However, this validation method has a major flaw. It is not comprehensive, as many other extensions are not included in this list, which may still be used to execute PHP code on the back-end server if uploaded.

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.

Fuzzing Extensions

We'll keep the file content for this attack, as we are only interested in fuzzing file extensions. Finally, we can Load the PHP extensions list from above in the Payloads tab under Payload Options. We will also un-tick the URL Encoding option to avoid encoding the (.) before the file extension. Once this is done, we can click on Start Attack to start fuzzing for file extensions that are not blacklisted:

We can sort the results by Length, and we will see that all requests with the Content-Length (229) passed the extension validation, as they all responded with File successfully uploaded. In contrast, the rest responded with an error message saying Extension not allowed.

Non-Blacklisted Extensions

Now, we can try uploading a file using any of the allowed extensions from above, and some of them may allow us to execute PHP code. 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.

Let's use the .phtml extension, which PHP web servers often allow for code execution rights. We can right-click on its request in the Intruder results and select Send to Repeater. Now, all we have to do is repeat what we have done in the previous two sections by changing the file name to use the .phtml extension and changing the content to that of a PHP web shell

Whitelist Filters

Whitelisting Extensions

The following is an example of a file extension whitelist test:

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

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

We see that the script uses a Regular Expression (regex) to test whether the filename contains any whitelisted image extensions. The issue here lies within the regex, as it only checks whether the file name contains the extension and not if it actually ends with it. Many developers make such mistakes due to a weak understanding of regex patterns.

So, let's see how we can bypass these tests to upload PHP scripts.

Double Extensions

For example, if the .jpg extension was allowed, we can add it in our uploaded file name and still end our filename with .php (e.g. shell.jpg.php), in which case we should be able to pass the whitelist test, while still uploading a PHP script that can execute PHP code.

However, this may not always work, as some web applications may use a strict regex pattern, as mentioned earlier, like the following:

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

This pattern should 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. So, the above attack would not work. Nevertheless, some exploitation techniques may allow us to bypass this pattern, but most rely on misconfigurations or outdated systems.

Reverse Double Extension

In some cases, the file upload functionality itself may not be vulnerable, but the web server configuration may lead to a vulnerability. For example, an organization may use an open-source web application, which has a file upload functionality. Even if the file upload functionality uses a strict regex pattern that only matches the final extension in the file name, the organization may use the insecure configurations for the web server.

For example, the /etc/apache2/mods-enabled/php7.4.conf for the Apache2 web server may include the following configuration:

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

The above configuration is how the web server determines which files to allow PHP code execution. It specifies a whitelist with a regex pattern that matches .phar, .php, and .phtml. However, this regex pattern can have the same mistake we saw earlier if we forget to end it with ($). In such cases, any file that contains the above extensions will be allowed PHP code execution, even if it does not end with the PHP extension. For example, the file name (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.

Character Injection

Finally, let's discuss another method of bypassing a whitelist validation test through 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

  • /

  • .\

  • .

  • …

  • :

Each character has a specific use case that may trick the web application to misinterpret the file extension. For example, (shell.php%00.jpg) works with PHP servers with version 5.X or earlier, as it causes the PHP web server to end the file name after the (%00), and store it as (shell.php), while still passing the whitelist. The same may be used with web applications hosted on a Windows server by injecting a colon (:) before the allowed file extension (e.g. shell.aspx:.jpg), which should also write the file as (shell.aspx). Similarly, each of the other characters has a use case that may allow us to upload a PHP script while bypassing the type validation test.

We can write a small bash script that generates all permutations of the file name, where the above characters would be injected before and after both the PHP and JPG extensions, as follows:

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

With this custom wordlist, we can run a fuzzing scan with Burp Intruder, similar to the ones we did earlier. If either the back-end or the web server is outdated or has certain misconfigurations, some of the generated filenames may bypass the whitelist test and execute PHP code.

Type Filters

There are two common methods for validating the file content: Content-Type Header or File Content. Let's see how we can identify each filter and how to bypass both of them.

Content-Type

The following is an example of how a PHP web application tests the Content-Type header to validate the file type:

$type = $_FILES['uploadFile']['type'];

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

The code sets the ($type) variable from the uploaded file's Content-Type header. Our browsers automatically set the Content-Type header when selecting a file through the file selector dialog, usually derived from the file extension. However, since our browsers set this, this operation is a client-side operation, and we can manipulate it to change the perceived file type and potentially bypass the type filter.

wget https://raw.githubusercontent.com/danielmiessler/SecLists/refs/heads/master/Discovery/Web-Content/web-all-content-types.txt
cat web-all-content-types.txt | grep 'image/' > image-content-types.txt

MIME-Type

The second and more common type of file content validation is testing the uploaded file's MIME-Type. Multipurpose Internet Mail Extensions (MIME) is an internet standard that determines the type of a file through its general format and bytes structure.

Many other image types have non-printable bytes for their file signatures, while a GIF image starts with ASCII printable bytes (as shown above), so it is the easiest to imitate. Furthermore, as the string GIF8 is common between both GIF signatures, it is usually enough to imitate a GIF image.

Web servers can also utilize this standard to determine file types, which is usually more accurate than testing the file extension. The following example shows how a PHP web application can test 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();
}

Once we forward our request, we notice that we get the error message Only images are allowed. Now, let's try to add GIF8 before our PHP code to try to imitate a GIF image while keeping our file extension as .php, so it would execute PHP code regardless:

We can use a combination of the two methods discussed in this section, which may help us bypass some more robust content filters. For example, we can try using an Allowed MIME type with a disallowed Content-Type, an Allowed MIME/Content-Type with a disallowed extension, or a Disallowed MIME/Content-Type with an allowed extension, and so on. Similarly, we can attempt other combinations and permutations to try to confuse the web server, and depending on the level of code security, we may be able to bypass various filters.

There are many lists of extensions we can utilize in our fuzzing scan. PayloadsAllTheThings provides lists of extensions for and web applications. We may also use SecLists list of common .

We may use any of the above lists for our fuzzing scan. As we are testing a PHP application, we will download and use the above list. Then, from Burp History, we can locate our last request to /upload.php, right-click on it, and select Send to Intruder. From the Positions tab, we can Clear any automatically set positions, and then select the .php extension in filename="HTB.php" and click the Add button to add it as a fuzzing position:

We may start by fuzzing the Content-Type header with SecLists' through Burp Intruder, to see which types are allowed. However, the message tells us that only images are allowed, so we can limit our scan to image types, which reduces the wordlist to 45 types only (compared to around 700 originally). We can do so as follows:

This is usually done by inspecting the first few bytes of the file's content, which contain the or . For example, if a file starts with (GIF87a or GIF89a), this indicates that it is a GIF image, while a file starting with plaintext is usually considered a Text file. If we change the first bytes of any file to the GIF magic bytes, its MIME type would be changed to a GIF image, regardless of its remaining content or extension.

PHP
.NET
Web Extensions
PHP
Content-Type Wordlist
File Signature
Magic Bytes