Upload-Labs File Upload Bypass Techniques and Implementation Strategies

Overview

File upload vulnerabilities arise when web applications inadequately validate or sanitize user-supplied files before storing and serving them. Exploiting these flaws allows attackers to inject executable code—such as PHP webshells—into the server’s filesystem, often leading to remote code execution. Upload-Labs is a deliberately vulnerable training platform designed to demonstrate common file upload defence mechanisms and their bypass methods.

Challenge Walkthrough

Level 1: Client-Side Extension Filtering

The frontend uses JavaScript to block .php extensions. Since client-side validation is trivially bypassed, disabling JavaScript (e.g., via Quick JavaScript Switcher in Firefox) permits direct upload of shell.php.

Level 2: Server-Side Extension Check with MIME-Type Blindness

No client-side JS, but server rejects .php. Uploading shell.png succeeds. Intercepting the request with Burp Suite and modifying the filename from shell.png to shell.php in the Content-Disposition header bypasses the check.

Level 3: Blacklisted Extensions and Apache Handler Abuse

A hardcoded blacklist blocks common PHP extensions. By editing httpd.conf to add:

AddType application/x-httpd-php .php5

and uploading shell.php5, the file evades filtering while remaining executable by the web server.

Level 4: .htaccess-Driven Content-Type Override

The server blocks .htaccess, but this restriction is not yet enforced. A valid .htaccess file with:

AddType application/x-httpd-php .jpg

is crafted (using ren 1.txt .htaccess on Windows) and uploaded first. Then shell.jpg is uploaded and interpreted as PHP due to the directive.

Level 5: Incomplete Trimming Logic

The backend trims trailing whitespace and . characters once, then converts the extension to lowercase and strips ::$DATA. Submitting shell.php. (with trailing space) results in shell.php. after trimming—retaining the dot and breaking the blacklist match.

Level 6: Case-Insensitive Blacklist Without Normalization

The filter checks against lowercase extensions but does not enforce case normalization before comparison. Uploading shell.pHp (mixed case) avoids detection while still being parsed as PHP by Apache.

Level 7: Trailing Space Bypass in Extension Parsing

The logic strips trailing spaces after extracting the extension. Sending shell.php (space appended) causes the extension extraction to yield php , which fails the blacklist comparison ('php ''php').

Level 8: Trailing Dot Bypass

Similar to Level 7, but the trailing character is . instead of space. Submitting shell.php. leads to extension parsing returning php., which again evades exact-match blacklisting.

Level 9: ::$DATA Alternate Data Stream Injection

Windows NTFS alternate data streams are used to append metadata. Sending shell.php::$DATA causes the backend to strip only one occurrence of ::$DATA, leaving the base name intact for execution.

Level 10: Single-Step Dot Removal Flaw

The script removes only one trailing dot. Uploading shell.php.. becomes shell.php. after processing—reproducing the Level 8 bypass condition.

Level 11: Single-Occurrence Extension Stripping

The blacklist removal logic deletes only the first matching extension substring. Submitting shell.php.php results in shell.php after stripping one .php, making it executable.

Level 12 & 13: Whitelist-Based Validation with Image File Abuse

Only .jpg, .png, and .gif are allowed. Embedding PHP code inside an otherwise valid image (e.g., appending <?php system($_GET['cmd']); ?> to shell.png) creates a polyglot file. The server accepts it as an image; later inclusion (e.g., via LFI) executes the embedded payload.

Level 14: Magic Byte Validation Bypass

The script validates the first two bytes using exif_imagetype(). To pass, prepend valid image headers (e.g., \xFF\xD8 for JPEG) to a PHP payload. Then combine with a local file inclusion endpoint:

copy /b logo.jpg + shell.php combined.jpg

Upload combined.jpg, then include it via the LFI vector.

Level 15: getimagesize() Header Validation

Unlike raw byte checks, getimagesize() parses full image structure. A minimal valid PNG header suffices:

b'\x89PNG\r\n\x1a\n' + b'<?php eval($_POST["x"]); ?>'

Save as payload.png, upload, and trigger via an include endpoint like:

http://target/upload/include.php?file=uploads/payload.png

Tags: web-security file-upload bypass CTF php-exploitation

Posted on Thu, 14 May 2026 14:50:47 +0000 by jrforrester