4.4 KiB
Echo Chamber
Hallo! Willkommen zum Write-up für Echo Chamber. Dies ist eine klassische "pwn"-Challenge, die eine sehr verbreitete, aber mächtige Schwachstelle demonstriert: die Format-String-Schwachstelle.
In dieser Challenge erhalten wir ein kompiliertes Binary. Wenn wir den ursprünglichen Quellcode nicht haben, verwenden wir Tools wie Ghidra, um das Binary zu dekompilieren und zu sehen, was das Programm "unter der Haube" macht.
1. Erste Erkundung (Reconnaissance)
Wenn wir das Programm ausführen, bittet es um eine Eingabe und gibt sie "echoförmig" zurück:
Welcome to the Echo Chamber!
Give me a phrase, and I will shout it back: Hello!
You said: Hello!
Die Beschreibung des Entwicklers gibt uns einen Hinweis:
"Der Entwickler behauptet, es sei vollkommen sicher, weil 'es keinen Code ausführt, sondern nur Text druckt'."
Dies ist eine klassische "Berühmte letzte Worte"-Situation in der IT-Sicherheit! Schauen wir uns den dekompilierten Code an, um zu verstehen, warum.
2. Analyse des dekompilierten Codes (Ghidra)
Beim Öffnen des Binarys in Ghidra finden wir die Funktion vuln(). Hier ist der Pseudocode, den wir erhalten:
void vuln(void)
{
char acStack_a0 [64]; // Unser Eingabepuffer
char local_60 [72]; // Hier wird das Flag gespeichert
FILE *local_18;
local_18 = fopen64("flag.txt","r");
if (local_18 == (FILE *)0x0) {
puts("Flag file is missing!");
exit(0);
}
// 1. Das Flag wird in local_60 eingelesen
fgets(local_60,0x40,local_18);
fclose(local_18);
puts("Welcome to the Echo Chamber!");
printf("Give me a phrase, and I will shout it back: ");
// 2. Unsere Eingabe wird in acStack_a0 eingelesen
fgets(acStack_a0,0x40,(FILE *)stdin);
printf("You said: ");
// 3. SCHWACHSTELLE: Unsere Eingabe wird direkt an printf übergeben!
printf(acStack_a0);
putchar(10);
return;
}
Siehst du die Zeile printf(acStack_a0);? Das ist unser "goldenes Ticket".
3. Die Schwachstelle: Format-Strings
In C erwartet printf, dass sein erstes Argument ein Format-String ist (wie "%s" oder "Hallo %s"). Wenn ein Entwickler die Benutzereingabe direkt an printf übergibt, kann der Benutzer seine eigenen Format-Spezifizierer angeben.
Wenn printf einen Spezifizierer wie %p (Pointer drucken) oder %x (Hexadezimalwert drucken) sieht, sucht es nach dem nächsten Argument auf dem Stack. Wenn wir keine Argumente angeben, beginnt printf einfach damit, alles auszugeben, was sich bereits auf dem Stack befindet!
Wo ist das Flag?
Wenn wir uns die Ghidra-Ausgabe ansehen, bemerken wir, dass sowohl acStack_a0 (unsere Eingabe) als auch local_60 (das Flag) lokale Variablen sind. Das bedeutet, dass beide direkt nebeneinander auf dem Stack gespeichert sind.
4. Ausnutzen des "Echos"
Wenn wir eine Kette von Format-Spezifizierern wie %p %p %p %p %p %p... senden, können wir printf dazu bringen, den Inhalt des Stacks auszugeben. Da das Flag auf dem Stack liegt, wird es schließlich mit ausgedruckt!
Versuche dies als Eingabe:
%p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p
Das Programm wird mit einer Reihe von Hexadezimal-Adressen antworten. Einige dieser Werte sind tatsächlich die ASCII-Zeichen unseres Flags.
Little Endianness
Wenn du die Hex-Werte siehst, denke daran, dass moderne Systeme die Little Endian-Byte-Reihenfolge verwenden. Das bedeutet, dass die Bytes in umgekehrter Reihenfolge gespeichert werden.
Wenn du zum Beispiel 0x7b67616c66 siehst und diese Bytes von Hexadezimal in ASCII umwandelst:
66=f6c=l61=a67=g7b={
Der Wert 0x7b67616c66 repräsentiert also flag{ in umgekehrter Reihenfolge!
5. Alles zusammenfügen
Um die Challenge zu lösen:
- Verbinde dich mit dem Dienst.
- Sende viele
%p-Spezifizierer, um den Stack auszulesen (Leak). - Identifiziere die Hex-Werte, die wie lesbarer Text aussehen (beginnend mit
0x...und ASCII-Werte enthaltend). - Kehre die Bytes um (Endianness) und wandle sie in Zeichen um.
- Kombiniere die Teile, um das Flag zu finden!
Gelernte Lektionen
Selbst wenn ein Programm "nur Text druckt", ist es nicht sicher, wenn es printf falsch verwendet. Die Lösung ist einfach: Verwende immer printf("%s", buffer);. Dies stellt sicher, dass die Eingabe als reine Zeichenkette behandelt wird und nicht als Code oder Anweisungen für die Funktion.
Viel Erfolg beim Hacken!