Diese Challenge (three eyed fish) verrät zunächst nicht viel über das eigentliche Ziel. Nur eine Binärdatei wird zum Download bereit gestellt und der legendäre “Ban Hammer” als Belohnung für dieses Quest versprochen 😉
rup0rt@linux64:~/Plaid2013# file three_eyed_fish
three_eyed_fish: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, stripped
Eine erste Untersuchung ergibt, dass es sich um ein 64bit Linux-Binary handelt. Um das normale Verhalten des Programmes zu testen, starten wir die Datei direkt.
rup0rt@linux64:~/Plaid2013# ./three_eyed_fish
Eine Ausgabe erzeugt das Programm nach dem Starten nicht. Beendet wird es jedoch auch nicht sofort – es muss also eine andere Funktion geben. Nach weiterer Betrachtung stellt man fest, dass immer nach dem Starten die Umschalt-LED des Keyboards aufleuchtet und zu blinken beginnt.
Die LED blinkt jedoch nicht einheitlich oder nach einem festen Muster. Alles was erkennbar ist, dass regelmäßig größere Pausen zwischen den Sequenzen liegen. Es könnte sich demnach um eine Art Morse-Code handeln, wobei die Pausen die Trennung zwischen den einzelnen Buchstaben darstellt.
Ein erster Versuch, das doch recht schnelle Blinken selbst zu protokollieren führt folgender Erkenntnis: 1x Kurz, 1x Lang, Pause. Das Nachschlagen im Internet nach Morse-Codes führt zu Wikipedia und dieser Übersicht:
Demnach könnte es sich beim ersten Zeichen um ein “A” handeln. Das Blinken der Umschalt-LED ist jedoch so schnell, dass sich nur sehr umständlich und mit großer Fehlerwahrscheinlichkeit weitere Zeichen interepretieren lassen. Es wäre also wünschenswert, diesen Vorgang zu automatisieren.
Dazu starten wir das Binary zunächst mit strace um die system calls zu erhalten:
rup0rt@linux64:~/Plaid2013# strace ./three_eyed_fish
execve("./three_eyed_fish", ["./three_eyed_fish"], [/* 17 vars */]) = 0
brk(0) = 0x1192000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f751713c000
[...]
mprotect(0x7f7516f15000, 16384, PROT_READ) = 0
mprotect(0x7f751713e000, 4096, PROT_READ) = 0
munmap(0x7f7517123000, 102105) = 0
ptrace(PTRACE_TRACEME, 0, 0, 0) = -1 EPERM (Operation not permitted)
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
+++ killed by SIGSEGV +++
Segmentation fault
Die Ausführung wird hierbei jedoch durch die Funktion “ptrace()” verweigert. Der Aufruf dieser Funktion scheitert, wenn bereits ein Debugger – hier strace – an den Prozess angebunden ist. Es handelt sich also um eine Debugger-Protection.
Um zu deaktivieren, lässt sich entweder LD_PRELOAD mit einer manipulierten ptrace-Funktion verwenden oder der Aufruf einfach aus dem Binary patchen. Für den zweiten Weg entscheide ich mich hier. Ein “objdump” des Programmes zeigt uns:
400727: 31 d2 xor edx,edx
400729: 31 c9 xor ecx,ecx
40072b: e8 88 fd ff ff call 4004b8 <ptrace@plt>
400730: 50 push rax
400731: 48 31 c0 xor rax,rax
Diesen Opcode von ptrace (0xe888fdffff) suchen mir nun mit einem Hex-Editor und ersetzen den Aufruf durch den Opcode von NO OPERATION (NOP == 0x90). Das Ergebnis sieht so aus:
Nachdem die ptrace-Funktion nun entfernt wurde, führen wir das Binary erneut mittels “strace” aus. Diesmal bricht der Aufruf nicht ab und man erkennt, dass “strace” immer wiederholend folgende Ausgaben erzeugt:
ioctl(3, KDSETLED, 0x4) = 0 nanosleep({0, 250000000}, 0x4b32) = 0 ioctl(3, KDSETLED, 0) = 0
Die Funktion ioctrl() dient hierbei zur Kontrolle von IO-Dateien (das Binary verwendet hier den Dateideskriptor 3, der vorher /dev/tty0 zugewiesen wird). Mit dem Parameter “KDSETLED” werden die LEDs des Keyboards gesetzt, wobei der Parameter 0x4 die LED_CAP ansteuert – was der Umschalt-LED entspricht. Danach erfolgt eine Pause von 25 Millisekunden, bevor die LEDs mit dem Parameter 0 wieder ausgeschalten werden.
Der obere strace-Ausschnitt sollte demnach einem kurzen Blinken und somit einem Punkt im Morse-Alphabet entsprechen.
Weiterhin lassen sich folgende Ausschnitte identifizieren:
(Langes Blinken – entspricht dem Strich im Morse-Alphabet.)
ioctl(3, KDSETLED, 0x4) = 0 nanosleep({0, 250000000}, 0x4b32) = 0 nanosleep({0, 250000000}, 0x4b32) = 0 nanosleep({0, 250000000}, 0x4b32) = 0 ioctl(3, KDSETLED, 0) = 0
Sowie die kurze und lange Pause, ohne das LEDs leuchten:
ioctl(3, KDSETLED, 0) = 0 nanosleep({0, 250000000}, 0x4b32) = 0 ioctl(3, KDSETLED, 0) = 0 nanosleep({0, 250000000}, 0x4b32) = 0 nanosleep({0, 250000000}, 0x4b32) = 0 nanosleep({0, 250000000}, 0x4b32) = 0
Die strace-Ausführung des gesamten Programmes protokollieren wir nun in einer externen Datei:
rup0rt@linux64@:~/Plaid2013# strace ./fish &> fish.txt
Die oben heraus gerarbeiteten Abschnitte werden nun mit einem kleinen Perl-Skript aus der strace-Ausgabe heraus gefiltert und als Morse-Code interpretiert ausgegeben:
#!/usr/bin/perl $on = "ioctl(3, KDSETLED, 0x4)"; $off = "ioctl(3, KDSETLED, 0)"; $sleep = "nanosleep({0, 250000000}"; open(LOG, "fish.txt"); $state = "OFF"; $timer = 0; while($line = <LOG>) { if (substr($line, 0, length($on)) eq $on) { if ($state eq "OFF") { if ($timer == 3) { print " "; } } $timer = 0; $state = "ON"; } if (substr($line, 0, length($off)) eq $off) { if ($state eq "ON") { if ($timer == 1) { print "."; $timer = 0; } if ($timer == 3) { print "-"; $timer = 0; } } $state = "OFF"; } if (substr($line, 0, length($sleep)) eq $sleep) { $timer++; } } print "\n"; close(LOG);
Die Ausführung des Skriptes liefert diese Ausgabe:
rup0rt@linux64:~/Plaid2013# ./fish.pl .- -. -.. ----- ..- ----- -.. .. -.. -. - ----- . ...- . -. ----- -. . . -.. ----- .- -. ----- .- .-. -.. ..- .. -. ---
Diese Morse-Codes können wir nun entweder per Hand in lesbare Zeichen umwandeln oder aber mit einem beliebigen Online Morse-Code Konverter übersetzen lassen. Dies liefert die Antwort zur Challenge!
Die Lösung lautet somit “AND0U0DIDNT0EVEN0NEED0AN0ARDUINO“.
Und damit haben wir uns den legendären “Ban Hammer” wirklich verdient!! 😉