Added writeups
This commit is contained in:
104
de/echo_chamber.md
Normal file
104
de/echo_chamber.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# 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:
|
||||
|
||||
```text
|
||||
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:
|
||||
|
||||
```c
|
||||
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` = `f`
|
||||
- `6c` = `l`
|
||||
- `61` = `a`
|
||||
- `67` = `g`
|
||||
- `7b` = `{`
|
||||
|
||||
Der Wert `0x7b67616c66` repräsentiert also `flag{` in umgekehrter Reihenfolge!
|
||||
|
||||
## 5. Alles zusammenfügen
|
||||
|
||||
Um die Challenge zu lösen:
|
||||
1. Verbinde dich mit dem Dienst.
|
||||
2. Sende viele `%p`-Spezifizierer, um den Stack auszulesen (Leak).
|
||||
3. Identifiziere die Hex-Werte, die wie lesbarer Text aussehen (beginnend mit `0x...` und ASCII-Werte enthaltend).
|
||||
4. Kehre die Bytes um (Endianness) und wandle sie in Zeichen um.
|
||||
5. 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!
|
||||
Reference in New Issue
Block a user