ForbiddenBITS CTF 2013 – Old

ForbiddenBITS CTF 2013 - old - task description

Wie gewohnt ist Weniger auch diesmal Mehr und wir erhalten als Aufgabe (old) nur einen Kommentar, dass es sich um einen “scheinbaren binären Auftrag” handelt. Zusätzlich wird eine Datei zum Download bereit gestellt.

Die erste Betrachtung mit dem Werkzeug “file” um festzustellen, um was für einen Typ von Datei es sich in Wirklichkeit handelt, liefert:

rup0rt@lambda:~/FB2013/old$ file bin.bin
bin.bin: x86 boot sector, code offset 0x6e

Uns liegt also ein bootbares Image vor, das durch Übergabe an einen Emulator, wie QEMU gestartet werden könnte. Da die Datei allerdings nur 512 Bytes groß ist und das simple Starten per Emulator ohnehin nicht die Flagge liefern wird, sehen wir uns direkt etwas genauer an. Zunächst prüfen wir, welche Zeichenketten enthalten sind:

rup0rt@lambda:~/FB2013/old$ strings bin.bin
Password: 
,jNice, 16bits world is nice :D. Validate using that password :)
No :(, try again.

Offensichtlich scheint eine Passwortabfrage implementiert zu sein, die abhängig von der korrekten Eingabe eine positive oder negative Rückmeldung erzeugt. Unser Ziel zur Lösung der Challenge wird es also sein, an genau dieses Passwort zu gelangen.
Um die Überprüfung bzw. Berechnung des Passwortes in Erfahrung zu bringen, bleibt nichts anderes übrig als noch tiefer in das Binary hinein zu sehen – vorzugsweise mit Hilfe einer disassemblierten Darstellung, die mit dem Werkzeug ndisasm erzeugt werden kann.

Nach ein paar Minuten, in denen sich ein Überblick über die Funktionalität verschafft wurde (und vorrangig nach Operationen wie XOR Ausschau gehalten wurde), können wir folgendes Erkennen:

00000099  BEBF7C            mov si,0x7cbf
0000009C  BFCF7C            mov di,0x7ccf
0000009F  EB0D              jmp short 0xae
000000A1  8B02              mov ax,[bp+si]
000000A3  8B1B              mov bx,[bp+di]
000000A5  31D8              xor ax,bx
000000A7  89E9              mov cx,bp
000000A9  D2C8              ror al,cl
000000AB  8802              mov [bp+si],al
000000AD  45                inc bp
000000AE  83FD07            cmp bp,byte +0x7
000000B1  72EE              jc 0xa1
000000B3  C3                ret

Das Programm liest zwei Werte aus dem Speicher, der Stelle, an die der Boot-Sektor geladen wurde (Zeilen 1-2). Hierbei handelt es sich um 0x7cbf (die Stelle an die das eingegebene Passwort gespeichert wurde) und 0x7ccf (eine Zeichenkette, aus dem Binary).

Danach wird eine Schleife sieben Mal durchlaufen, wobei das Register “bp” als Zählvariable verwendet wird (Zeilen 3-11). Inerhalb der Schleife passiert für jedes Zeichen des eingegebenen Passwortes folgendes:

Das Zeichen wird per XOR mit der Zeichenkette aus dem Binary verknüpft (Zeile 6). Anschließend wird die Position des Zeichens (0-6) in das Register “cx” gespeichert (Zeile 7) und ein Rechtsschift unter Erhalt aller Bits (ROR) mit der Anzahl der Zeichenposition vorgenommen (Zeile 8). Die ROR-Operation bewirkt im Beispiel für die Zahl 5 (0x00000101) und ein Verschieben um zwei Bit nach rechts, die Zahl 65 (0x01000001).

Anschließend an diese Schleife führt das Programm noch folgenden Code aus:

00000024  BEBF7C            mov si,0x7cbf
00000027  BFC77C            mov di,0x7cc7
[...]
00000080  8B02              mov ax,[bp+si]
00000082  8B1B              mov bx,[bp+di]
00000084  39D8              cmp ax,bx
00000086  750C              jnz 0x94
00000088  83C501            add bp,byte +0x1
0000008B  83FD07            cmp bp,byte +0x7

Wieder werden zwei Werte gelesen, 0x7cbf (unser per XOR und ROR veränderte Passwort) sowie 0x7cc7 (eine weitere Speicherstelle aus dem Binary selbst). Erneut wird eine Schleife über sieben Zeichen durchlaufen und unser verändertes Passwort mit dem Wert aus dem Binary verglichen (Zeile 6).

Zusammengefasst tut das Programm also folgendes:

  • Einlesen eines vom Benutzer einzugebenen Passwortes
  • XOR mit einem statischen 7-Zeichen-String aus dem Binary
  • ROR jedes Zeichens entsprechend seiner Position
  • Vergleich der Ergebnisses mit einem weiteren 7-Zeichen-String aus dem Binary

Dieses Verhalten sollte sich relativ leicht in ein Skript überführen lassen. Vorher extrahieren wir jedoch die beiden statischen Zeichenketten aus dem Binary, indem wir deren Positionen (0x7ccf und 0x7cc7) direkt aus der Datei kopieren. Dazu müssen wir nur  die Werte nur um die Basis 0x7c00 verringern und erhalten die Positionen 0xcf und 0xc7.

ForbiddenBITS CTF 2013 - old - encrypted password

Die beiden Zeichenketten lauten also “0xBE 0x21 0x03 0x15 0x1F 0x2C 0x6A” und “0xD8 0xA6 0xCD 0x4E 0x04 0x0A 0x3C”. Alles zusammen ergibt nun folgendes Skript zur Berechnung des einzugebenen Passwortes:

#!/usr/bin/perl -w

$one = "\xD8\xA6\xCD\x4E\x04\x0A\x3C";
$two = "\xBE\x21\x03\x15\x1F\x2C\x6A";

sub bin2dec {
  my $zahl = unpack("N", pack("B32", substr("0" x 32 . $_[0], -32)));
  return $zahl;
}

sub dec2bin {
  my $zahl = unpack("B32", pack("N", $_[0]));
  $zahl =~ s/^0+(\d+)/$1/;
  return $zahl;
}

$key = "";
for ($i=0; $i<length($one); $i++) {
  $a = ord(substr($one, $i, 1));
  $b = ord(substr($two, $i, 1));
  for ($p=20; $p<=126; $p++) {
    $res = ($p ^ $b);

    # reimplementation of ROR
    $bin = sprintf("%8d", dec2bin($res));
    $bin = substr($bin, 8-$i, $i) . substr($bin, 0, 8-$i);
    $res = bin2dec($bin);

    if ($res == $a) {
      $key .= chr($p);
    }
  }
}

print "KEY: $key\n";

Die Ausführung des Skriptes liefert die Ausgabe:

rup0rt@lambda:~/FB2013/old$ ./old.pl
KEY: fl4g_me

Die Lösung lautet somit “fl4g_me“.

Leave a Reply

Your email address will not be published. Required fields are marked *