Deutschlands Bester Hacker - Grabstein (Reversing) - Das Writeup

Disclaimer: Im August 2026 fand die Qualifikation für das Finale von Deutschlands Bester Hacker statt, welches ich auf Platz 1 abschließen konnte. Nur zwei Teilnehmer waren in der Lage, alle Challenges zu lösen. Dieses Writeup wurde mit KI auf Basis meiner Notizen erstellt und kann Fehler enthalten, bei Fragen bitte direkt an mich wenden im DBH-Discord.

Wettbewerb Deutschlands Bester Hacker 2026 — Qualifikation
Kategorie ReversingReverse EngineeringAnalyse eines Systems oder Programms zur Rekonstruktion seiner Funktionsweise.
Punkte 316
Angriffsklasse Statisches Reversing einer Passwortprüfung (aarch64)
Passwort Baaaaaaoaaaa

Die Challenge

Bei der Challenge liegt eine einzelne ausführbare Datei namens tombstone vor. Laut Beschreibung soll noch ein alter Prozess auf Port 1338 laufen. Auf der Festplatte gibt es jedoch nur dieses Binary, keinen Quellcode und keine weitere Dokumentation.

Ziel ist es, die Logik der Binärdatei zu verstehen und die korrekte Eingabe zu finden, die zur Ausgabe der Flag führt.

Aufklärung

Zuerst der Dateityp:

file tombstone
ELF 64-bit LSB executable, ARM aarch64, dynamically linked, stripped

Wichtige Erkenntnisse: ein Linux-Binary für aarch64, gestrippt, also ohne Symbolnamen. Klassisches statisches Reverse Engineering ist hier der schnellste Weg.

Dazu die eingebetteten Strings:

strings -a tombstone

Auffällig waren unter anderem:

password:
/home/ctf/flag.txt
flag error
wrong.

Damit war früh klar: Das Programm fragt nach einem Passwort, bei Erfolg wird /home/ctf/flag.txt geöffnet. Die Flag liegt also nicht im Binary, sondern in einer Datei zur Laufzeit.

Die Schwachstelle

Die relevante Funktion per Disassembly rekonstruiert, lässt sich die Logik auf fünf Schritte reduzieren:

  1. Das Programm gibt password: aus.
  2. Es liest bis zu 64 Byte Eingabe mit fgets.
  3. Ein abschließendes Newline wird entfernt.
  4. Das Passwort wird geprüft.
  5. Bei Erfolg wird /home/ctf/flag.txt geöffnet und die erste Zeile ausgegeben.

Die Passwortprüfung besteht aus genau drei Bedingungen:

1. Länge muss 12 sein. Die Eingabe muss exakt 12 Zeichen lang sein.

2. Summe aller Bytes muss 0x47b sein. Also dezimal 1147. Das Programm summiert die ASCII-Werte aller 12 Zeichen und vergleicht mit 1147.

3. Zeichen 4 xor Zeichen 8 muss 0x0e ergeben. Genauer, nullbasiert:

input[3] ^ input[7] == 0x0e

Drei Bedingungen über 12 Zeichen — das System ist massiv unterbestimmt und lässt sich von Hand lösen. Brute ForceBrute-Force-AngriffSystematisches Ausprobieren vieler Zugangsdaten oder kryptografischer Schlüssel. ist unnötig.

Der Angriff

Ein gültiger Kandidat ist:

Baaaaaaoaaaa

Prüfung. Länge: 12. ASCII-Summe:

'B'  = 66
'a'  = 97   (x6)
'o'  = 111
'a'  = 97   (x4)
----------------
Summe = 1147

XOR-Bedingung:

'a' ^ 'o' = 0x61 ^ 0x6f = 0x0e

Damit erfüllt Baaaaaaoaaaa alle drei Bedingungen.

Bei korrekter Eingabe führt das Binary sinngemäß aus:

FILE *f = fopen("/home/ctf/flag.txt", "r");
fgets(buf, 0x80, f);
printf("%s", buf);

Einordnung des Ports 1338. Im Binary selbst wurde keine Socket- oder Netzwerklogik gefunden — keine Hinweise auf socket, bind, listen oder accept. Sehr wahrscheinlich ist der Dienst auf Port 1338 nur ein Wrapper um das Binary, der stdin/stdout weiterleitet. Die eigentliche Challenge-Logik steckt vollständig in tombstone. Bei der lokalen Prüfung am 20. August 2026 war 127.0.0.1:1338 nicht erreichbar, sodass in dieser Umgebung keine Live-Flag abgeholt werden konnte.

Die Flag

Das gefundene gültige Passwort lautet:

Baaaaaaoaaaa

An den laufenden Dienst oder direkt an das Binary übergeben, gibt es die Flag aus /home/ctf/flag.txt aus — im Format DBH{...}.

Was ich mitnehme

Ein kleines Reverse-Engineering-Task mit einer manuellen Passwortprüfung: feste Länge, feste ASCII-Summe, eine XOR-Beziehung zwischen zwei Zeichen.

Der entscheidende Schritt war, die Bedingungen direkt aus dem Disassembly zu rekonstruieren statt dynamisch zu probieren. Danach ließ sich eine gültige Eingabe konstruieren, ohne irgendetwas zu raten. Und: dass die Flag nicht im Binary liegt, sondern zur Laufzeit aus einer Datei kommt, sieht man an einem einzigen String — was den Aufwand einer Patch-den-Sprung-Lösung sofort sinnlos macht.