# 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!