Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
CVE-2021-4045 — Befehlseinschleusungs-Exploit für die TP-Link Tapo C200 Kamera (CVE-2021-4045), der Root-Shell-Zugriff über UART und Analyse der rückentwickelten uhttpd-Binärdatei bereitstellt. | Kitploit
Tools/GitHubGitHub/kaleth4/cve-2021-4045
Embedded-System-SicherheitIoT-SicherheitSchwachstellenanalyseExploitationReverse EngineeringHardware-HackingPenetrationstestsCommand and ControlFirmware-Analyse
GitHubkaleth4/cve-2021-4045

CVE-2021-4045

Befehlseinschleusungs-Exploit für die TP-Link Tapo C200 Kamera (CVE-2021-4045), der Root-Shell-Zugriff über UART und Analyse der rückentwickelten uhttpd-Binärdatei bereitstellt.

Repository anzeigen
vor 2 MonatenNoch nicht geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

🔍 CVE-2021-4045: Befehlsinjektions-Schwachstelle in TP-Link Tapo C200

image

CVE-2021-4045


📌 Zusammenfassung

CVE-2021-4045 ist eine Befehlsinjektions-Schwachstelle, die in der Kamera TP-Link Tapo C200 entdeckt wurde und es einem Angreifer ermöglicht, die vollständige Kontrolle über das Gerät mit root-Berechtigungen zu übernehmen. Diese Schwachstelle betrifft alle Firmware-Versionen vor 1.1.16 Build 211209 Rel. 37726N.

🔗 Offizielle Mitteilung von INCIBE: https://www.incibe.es/incibe-cert/alerta-temprana/vulnerabilidades/cve-2021-4045 (Durch den tatsächlichen Link ersetzen)

🔧 Empfohlene Lösung: Firmware auf Version 1.1.16 oder höher aktualisieren.



🛠

Wie man sehen kann, hat das Gerät einige interessante offene Ports. Das erste, was ich testete, war Port 443. Obwohl nmap deutlich anzeigt, dass es https verwendet, habe ich beim ersten Scan dies übersehen und lange Zeit gedacht, Port 443 würde http verwenden. Deshalb habe ich nur http://192.168.1.81:443 statt https://192.168.1.81:443 ausprobiert und nur 400-Antworten erhalten. Wie ich in der Einleitung sagte, war dieser Prozess voller Fehler. Was die anderen Ports betrifft, waren mir die darauf laufenden Dienste völlig unbekannt und ich fand keine klaren Informationen dazu. Zu diesem Zeitpunkt hatte ich keine bekannten Optionen mehr, also war es an der Zeit, tiefer zu graben. Ich analysierte die Funktion `main()` und andere wichtige, um die Logik der Binärdatei und ihre Struktur zu verstehen. Ich fand einige interessante, bereits identifizierte, darunter `do_login()` und `uh_slp_proto_request()`. Über Letztere werde ich später mehr sprechen.
Erste Erkundung

Gerätekonfiguration und -funktionen

  • Günstige IP-Kamera (~30€) mit erweiterten Funktionen:
    • Aufnahme auf SD-Karte.
    • Horizontale Drehung um 360° und vertikale um 90°.
    • Echtzeit-Audiowiedergabe über die mobile App.

Portscan```bash

$ nmap -sV -p- 192.168.1.81

root@kitploit:~
**Ergebnis**:```
PORT     STATE SERVICE
443/tcp  open  https
554/tcp  open  rtsp
2020/tcp open  xinupageserver
8800/tcp open  sunwebadmin
image

----[ Eine Shell bekommen ]-------------------------------

Bevor ich die Kamera kaufte, suchte ich im Internet nach früheren Recherchen zu dem Gerät und fand glücklicherweise dieses GitHub-Repository, in dem Leute gemeinsam Reverse Engineering betrieben. Eines der Issues erklärte, wie man über den UART-Port Zugriff auf die Konsole erhält – etwas, das mir damals völlig unbekannt war. Also lernte ich die Grundlagen und kaufte einen USB-auf-TTL-Konverter, um mich zu verbinden. image Mit Hilfe des erwähnten Issues konnte ich das Gerät mit einem Messer und einem Schraubendreher öffnen und schnell den UART lokalisieren. Nach ein paar Versuchen und viel Geduld gelang es mir schließlich, einige Kabel an die Pads zu löten.

image

Dann war es an der Zeit zu prüfen, ob die Lötstelle gut genug für die Datenübertragung war. Ich schloss die Kabel an den USB-Adapter an, wobei ich bedenken musste, dass Rx des UART an Tx des Adapters geht und umgekehrt, und verband den Adapter mit meinem Computer. Dank des erwähnten Issues wusste ich, dass die Baudrate für die serielle Verbindung 57600 betrug, also führte ich aus:

$ sudo screen /dev/tty.usbserial-0001 57600

Dabei ist '/dev/tty.usbserial-0001' der USB-Port, an dem der Adapter angeschlossen ist und der das Gerät mit Strom versorgt. Sofort begann ich, Daten zu empfangen – großartig.

Allerdings hatte ich noch keinen Zugriff auf die Konsole. Was ich empfing, war lediglich die Boot-Sequenz des Geräts, die eigentlich der U-Boot-Bootloader war. Es sah ungefähr so aus:

U-Boot 2014.01-v1.2 (Jul 16 2021 - 18:41:10)

Board: IPCAM RTS3903 CPU: 500M :rx5281 prid=0xdc02 force spi nor mode DRAM: 64 MiB @ 1066 MHz Skipping flash_init Flash: 0 Bytes flash status is 0, 0, 0 SF: Detected XM25QH64A with page size 256 Bytes, erase size 64 KiB, total 8 MiB Using default environment

Autobooting in 1 seconds copying flash to 0x81500000 flash status is 0, 0, 0 SF: Detected XM25QH64A with page size 256 Bytes, erase size 64 KiB, total 8 MiB SF: 8388608 bytes @ 0x0 Read: OK

[...]

Beim Drücken der Eingabetaste werden wir aufgefordert, einen Benutzernamen und ein Passwort einzugeben. Dank dieses GitHub-Issues kennen wir die Zugangsdaten, sodass wir uns erfolgreich mit dem Benutzer 'root' und dem Passwort 'slprealtek' anmelden und schließlich Zugriff auf die Konsole erhalten.

Nachdem ich überprüft hatte, dass die Verbindung funktioniert, musste ich die Lötstelle verstärken, da sie während des Zusammenbaus des Gehäuses zweimal gebrochen war. Ich trug Heißkleber auf, um alle Kabel zu sichern, und schloss das Gerät, wobei ich alle Motoren abklemmte. Nun war meine Testeinheit bereit.

image

----[ Das Gerät erkunden ]--------------------------

Jetzt, wo wir eine Shell haben, erkunden wir das Gerät:

root@SLP:~# uname -a Linux SLP 3.10.27 #1 PREEMPT Wed Nov 11 20:42:05 CST 2020 rlx GNU/Linux

root@SLP:~# cat /etc/openwrt_version 12.09-rc1

Wie wir sehen, handelt es sich um eine OpenWRT-Maschine, die Linux 3.10.27 ausführt. Schauen wir uns nun die aktiven Prozesse und offenen Ports an:

root@SLP:~# ps PID USER VSZ STAT COMMAND 1 root 2328 S init 2 root 0 SW [kthreadd] 3 root 0 SW [ksoftirqd/0] 4 root 0 SW [kworker/0:0] 5 root 0 SW< [kworker/0:0H] 6 root 0 SW [kworker/u2:0] 7 root 0 SW [rcu_preempt] 8 root 0 SW [rcu_bh] 9 root 0 SW [rcu_sched] 10 root 0 SW< [khelper] 11 root 0 SW< [writeback] 12 root 0 SW< [bioset] 13 root 0 SW< [kblockd] 14 root 0 SW [khubd] 15 root 0 SW [kworker/0:1] 16 root 0 SW [kswapd0] 17 root 0 SW [fsnotify_mark] 18 root 0 SW< [crypto] 27 root 0 SW [kworker/u2:1] 46 root 0 SW< [deferwq] 47 root 0 SW< [kworker/0:1H] 247 root 2328 S -ash 262 root 0 SW [irq/27-gpio res] 273 root 0 SW< [cryptodev_queue] 282 root 860 S /sbin/hotplug2 --override --persistent --set-rules-f 304 root 888 S /sbin/ubusd 325 root 8152 S tp_manage 357 root 3416 S /usr/bin/ledd 361 root 3408 S /sbin/msglogd 367 root 3220 S /usr/sbin/netlinkd 370 root 5468 S < /usr/bin/system_state_audio 379 root 10180 S /usr/sbin/wlan-manager 491 root 1636 S /sbin/netifd 492 root 1520 S /usr/sbin/connModed 494 root 11488 S /usr/bin/dsd 496 root 1532 S /usr/sbin/connModed 502 root 7640 S /bin/cloud-service 520 root 4360 S /bin/cloud-brd -c /var/etc/cloud_brd_conf 653 root 15020 S /bin/cloud-client 830 root 2320 S /usr/sbin/telnetd -b 127.0.0.1 861 root 3852 S /usr/sbin/uhttpd -f -h /www -T 180 -A 0 -n 8 -R -r C 870 root 6048 S /usr/bin/relayd 872 root 5948 S /usr/bin/rtspd 879 root 4612 S /usr/bin/p2pd 884 root 11152 S /bin/dn_switch 889 root 4180 S /bin/storage_manager 920 root 40940 S /bin/cet 956 root 32336 S /bin/vda 960 root 3808 S /bin/wtd 970 root 11288 S /bin/nvid 1019 root 2332 S udhcpc -p /var/run/static-dhcpc.pid -s /lib/netifd/s 1037 root 0 SW [RTW_CMD_THREAD] 1059 root 1212 S wpa_supplicant -B -Dwext -iwlan0 -P/tmp/supplicant_p 1089 root 2332 S /usr/sbin/ntpd -n -p time.nist.gov -p 133.100.9.2 -p 1103 root 3840 S /usr/bin/motord 1447 root 2324 R ps

root@SLP:~# netstat -natpu 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:8800 0.0.0.0:* LISTEN 920/cet tcp 0 0 127.0.0.1:929 0.0.0.0:* LISTEN 875/p2pd tcp 0 0 0.0.0.0:20002 0.0.0.0:* LISTEN 325/tp_manage tcp 0 0 0.0.0.0:2020 0.0.0.0:* LISTEN 969/nvid tcp 0 0 0.0.0.0:554 0.0.0.0:* LISTEN 920/cet tcp 0 0 127.0.0.1:23 0.0.0.0:* LISTEN 832/telnetd tcp 0 0 127.0.0.1:921 0.0.0.0:* LISTEN 878/relayd tcp 0 0 127.0.0.1:922 0.0.0.0:* LISTEN 877/rtspd tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 863/uhttpd tcp 0 0 192.168.1.80:37380 52.19.66.90:443 ESTABLISHED 507/cloud-brd udp 0 0 0.0.0.0:20002 0.0.0.0:* 325/tp_manage udp 0 0 0.0.0.0:38000 0.0.0.0:* 1087/ntpd udp 0 0 0.0.0.0:3702 0.0.0.0:* 969/nvid

Wir können die Prozesse hinter den offenen Ports sehen, die auch im Nmap-Scan auftauchten, wie uhttpd oder cet. Ich konzentrierte mich speziell auf den uhttpd-Prozess, da er hinter dem https-Server steckt (von dem ich damals noch dachte, es sei http) und ich bereits sehr vertraut mit http-Protokollen war.

uhttpd ist ein von OpenWRT erstellter Webserver, der in eingebetteten Geräten mit dieser Distribution verwendet wird. An diesem Punkt wollte ich wissen, ob ich mehr darüber erfahren kann, wie den Quellcode oder zumindest die Pfade. Ich besuchte das OpenWRT-Wiki und lernte über uhttpd und OpenWRT im Allgemeinen. Auf OpenWRT-Maschinen gibt es ein System namens Unified Configuration Interface (UCI), das grundsätzlich zur einfachen Konfiguration von Systemdiensten verwendet wird. Damit können wir die Konfiguration von uhttpd abrufen:

root@SLP:~# uci show | grep uhttpd ucitrack.@uhttpd[0]=uhttpd ucitrack.@uhttpd[0].init=uhttpd uhttpd.main=uhttpd uhttpd.main.listen_https=443 uhttpd.main.home=/ww uhttpd.main.rfc1918_filter=1 uhttpd.main.max_requests=8 uhttpd.main.cert=/tmp/uhttpd.crt uhttpd.main.key=/tmp/uhttpd.key uhttpd.main.cgi_prefix=/cgi-bin uhttpd.main.lua_prefix=/luci uhttpd.main.lua_handler=/usr/lib/lua/luci/sgi/uhttpd.lua uhttpd.main.script_timeout=180 uhttpd.main.network_timeout=180 uhttpd.main.tcp_keepalive=0 uhttpd.px5g=cert uhttpd.px5g.days=3600 uhttpd.px5g.bits=1024 uhttpd.px5g.country=CN uhttpd.px5g.state=China uhttpd.px5g.location=China uhttpd.px5g.commonname=TP-Link upnpc.uhttpd=entry upnpc.uhttpd.proto=TCP upnpc.uhttpd.ext_port=80 upnpc.uhttpd.desc=uhttpd

Hier gibt es einige interessante Parameter. Erstens zeigt 'uhttpd.main.home' auf das Stammverzeichnis des Servers, sodass wir dort einige Webserver-Dateien finden könnten. Zweitens zeigt 'uhttpd.main.lua_handler' auf das Lua-Handler-Skript, das zur Initialisierung der Lua-Laufzeitumgebung beim Start des Servers verwendet wird, da uhttpd Lua-Skripte unterstützt – daher könnte es dort weitere interessante Dateien geben. Allerdings ist das Verzeichnis '/www' leer und es gibt weder ein 'sgi'-Verzeichnis unter '/usr/lib/lua/luci' noch die Datei 'uhttpd.lua' auf dem System. Ich versuchte, Informationen darüber zu finden, wie diese uhttpd-Instanz funktioniert, fand aber nichts – nur Konfigurationsparameter, die nirgendwo hinführen.

An diesem Punkt wusste ich, dass die Lösung darin bestand, die Binärdatei 'uhttpd' direkt zu analysieren und Reverse Engineering zu betreiben, aber zuerst wollte ich eine Testumgebung schaffen, um zu sehen, was im Webserver bei Anfragen passiert, da der Prozess aufgrund seiner Erstellungsweise keine Ausgabe irgendwohin lieferte.

Ich versuchte, den Befehl aus der ps-Ausgabe für Prozess 861 auszuführen:

$ /usr/sbin/uhttpd -f -h /www -T 180 -A 0 -n 8 -R -r C

Allerdings bekam ich viele Fehler und konnte es nicht zum Laufen bringen. Da ich den gleichen uhttpd-Prozess nicht auf einem anderen Port erstellen konnte, versuchte ich, die fehlende Ausgabe zu finden, indem ich den '/proc'-Eintrag des Prozesses überprüfte, um zu sehen, ob sie dort lesbar war (wie in diesem Video von PwnFunction erklärt). Aber es gab ein großes Problem:

root@SLP:~# sudo ls -l /proc/864/fd/ lrwx------ 1 root root 64 Nov 10 22:44 0 -> /dev/null lrwx------ 1 root root 64 Nov 10 22:44 1 -> /dev/null lrwx------ 1 root root 64 Nov 10 22:44 2 -> /dev/null lrwx------ 1 root root 64 Nov 10 22:44 3 -> anon_inode:[eventpoll] lrwx------ 1 root root 64 Nov 10 22:44 4 -> socket:[1830]

Alle Dateideskriptoren für 'stdin', 'stdout' und 'stderr' wurden nach '/dev/null' umgeleitet, was sie im Wesentlichen in ein schwarzes Loch leitet, wo sie nicht gefunden werden können. Ich steckte fest und wusste nicht, was ich tun sollte. Da ich bereits im '/proc'-Eintrag war, begann ich zu recherchieren, weil ich nicht wusste, dass '/proc'-Einträge so viele Informationen über einen Prozess enthalten, und ich war neugierig. Dank dieser glücklichen Neugier stieß ich auf den Eintrag 'environ', der alle Umgebungsvariablen für diesen Prozess enthält. Eine dieser Umgebungsvariablen war:

UHTTPD_ARGS=-h /www -T 180 -A 0 -n 8 -R -r C200 -C /tmp/uhttpd.crt -K /tmp/uhttpd.key -s 443

Mir wurde sofort klar, dass der von ps angezeigte Befehl falsch war, und später entdeckte ich, dass dies daran lag, dass die UART-Schnittstelle nicht genug Breite hatte, um alle Zeichen anzuzeigen. Ein weiterer Fehler, der mir wichtige Lektionen lehrte: Man sollte sich niemals auf die Ausgabe eines UART-Ports verlassen.

Jetzt endlich konnte ich eine andere Instanz von uhttpd mit denselben Parametern und ohne Umleitung nach '/dev/null' erstellen, um die Binärdatei zu testen, während ich sie Reverse Engineering unterzog.

----[ Reverse Engineering von uhttpd mit Ghidra ]--------

Dies war das erste Mal, dass ich Ghidra verwendete. Ich hatte einige Videos gesehen und einige Artikel darüber gelesen (danke an stacksmashing und liveoverflow für den großartigen und leicht verständlichen Inhalt), aber ich hatte es nie wirklich benutzt, also war dies eine sehr gute Gelegenheit zum Lernen.

Ich öffnete die Binärdatei uhttpd und stellte nach mehreren Versuchen fest, dass die Sprache MIPS32, Little Endian, mit mips16e war. Einige Funktionsnamen waren standardmäßig in der Binärdatei enthalten, andere nicht. Ich widmete auch einige Zeit dem Umbenennen von Funktionen, da Ghidra offenbar oft mit externen Funktionen durcheinanderkommt und seltsame Wrapper für sie erzeugt, wie:

image

Nach diesem ersten Kontakt begann ich, nach Fehlern zu suchen. Da ich ein kompletter Neuling in Bezug auf Überlaufschwachstellen bin, suchte ich zuerst nach Aufrufen von system(), exec() und popen(), um zu prüfen, ob es eine Befehlsinjektionsschwachstelle gibt, die ich leicht ausnutzen könnte. Und was für ein Glück ich hatte.

ejecutar_y_leer_json

Die Funktion 'exec_and_read_json()' verwendet 'popen()', um Befehle auszuführen:

Die Funktion 'exec_and_read_json()' wird von zwei namenlosen Funktionen verwendet, die ich 'set_language()' und 'wifi_connect()' genannt habe. Diese Funktionen kümmern sich jeweils um die Spracheinstellung und die WLAN-Verbindung (offensichtlich). 'wifi_connect()' scheint einfache Anführungszeichen (') zu parsen, während 'set_language()' dies nicht tut. Das bedeutet, dass wir, wenn wir die Eingabe der Funktion 'set_language()' kontrollieren können, erfolgreich eigene Befehle injizieren können:

conexión wifi
establecer_idioma

Die Funktion 'set_language()' wird von 'uh_slp_proto_request()' verwendet, der Funktion, die ich zuvor erwähnt habe. Diese übergibt als Eingabe einige geparste Daten, die vom Benutzer empfangen wurden.

función_principal_1
función_principal_2

Um die Benutzerdaten zu analysieren, prüft uh_slp_proto_request(), ob es sich um ein gültiges JSON-Objekt handelt. Anschließend erhält es einen String-Wert, der durch den Schlüssel method identifiziert wird, und einen Dictionary-Wert, der durch params identifiziert wird (zumindest glaube ich das, da Ghidra den Funktionsaufruf nicht auflösen konnte, aber es schien so zu funktionieren). Abhängig von der gewählten Methode wählt uh_slp_proto_request() die auszuführende Funktion aus.

Wenn wir also die folgende Nutzlast senden:{"method": "setLanguage", "params":{}}

Wir rufen die Funktion 'set_language()' korrekt auf und übergeben '{}' als Parameter 'language_json'. Dann wird innerhalb von 'set_language()' das Objekt 'language_json' in einen String umgewandelt und direkt in "ubus call system_state_audio set_language '%s'" zur Ausführung eingefügt.

Beim Senden dieser Nutzlast:{"method": "setLanguage", "params": {"payload": "'; touch poc;'"}}

Es wird Folgendes ausgeführt.

ubus call system_state_audio set_language '{"payload": "'; touch poc;'"}'

Das enthält eigentlich 3 Befehle:

ubus call system_state_audio set_language '{"payload": "' touch poc '"}'

Der zweite ermöglicht die vollständige Codeausführung.

Nun wird die Funktion 'uh_slp_proto_request()' von einer anderen unbenannten Funktion verwendet, die alle Anfragen verwaltet und die ich 'main_server_function()' genannt habe. Wenn eine Anfrage gültig ist (nicht die maximale Länge überschreitet, 'http' oder 'https' je nach Serverkonfiguration verwendet usw.), prüft 'main_server_function()', ob die URL '/cgi-bin/luci' oder '/web-static' enthält. Wenn nicht, wird 'uh_slp_proto_request()' aufgerufen.

uh_slp_proto_request_entrypoint

Bei einem Test und dem Senden einiger Anfragen an die Kamera können wir feststellen, dass die von 'uh_slp_proto_request()' verwendeten Daten Standard-POST-Daten sind. Wenn wir also eine POST-Anfrage an '/' mit der obigen Payload senden, verarbeitet 'uh_slp_proto_request()' diese Daten, ruft 'set_language()' auf und unsere Payload wird in den von 'exec_and_get_result()' ausgeführten Befehl injiziert.

Wie Sie sehen können, habe ich nichts zur Authentifizierung erwähnt, da die Funktion 'setLanguage()' ohne Anmeldung aufgerufen werden kann. Dies ermöglicht es jedem Benutzer, mit einer einzigen Anfrage ohne Authentifizierung die vollständige Kontrolle über die Kamera zu übernehmen.

----[ Exploitierung ]----------------------------------

Nun ist es Zeit, den Exploit zu schreiben. Ich habe etwas Zeit damit verbracht herauszufinden, wie man eine Reverse Shell mit netcat erhält. Es schien einfach, aber ich konnte es nicht zum Laufen bringen. Ich stellte fest, dass die auf BusyBox installierte Version von netcat funktional ziemlich eingeschränkt ist, sodass herkömmliche Reverse Shells nicht gültig waren. Allerdings fand ich, was ich suchte, im PayloadsAllTheThings-Repository (wie immer) und erhielt die perfekte Reverse Shell. Da uhttpd als root läuft (danke an TP-Link), erhalten wir eine Shell mit höchsten Privilegien, indem wir einfach eine bösartige POST-Anfrage senden. Der Exploit ist auf der GitHub-Seite verfügbar: https://github.com/hacefresko/CVE-2021-4045/blob/master/pwntapo.py image

🚨 Gelernte Lektion:

  • Ich habe https mit http auf Port 443 verwechselt und Zeit verloren, bis ich den Fehler entdeckte.
  • Die Dienste auf den Ports 2020, 554 und 8800 waren unbekannt, was zusätzliche Recherche erforderte.

🔓 Erlangen einer Shell

Konsolenzugriff über UART

  1. Vorherige Recherche:
    • Ich fand ein GitHub-Repository mit Informationen zur Reverse Engineering des Geräts.
    • Ich lernte, einen USB-zu-TTL-Konverter zu verwenden, um auf den UART-Port zuzugreifen.
Tool herunterladen