
Von einer blanken Platine bis zum Root-Zugriff: Hardware-Hacking eines ZyXEL P-870HN (BCM6368) über UART — CVE-2025-0890 + CVE-2024-40891, auf meiner eigenen Hardware.
Zwei Handyfotos einer unbeschrifteten Router-Platine → serielle Konsole → Auth-Bypass → Root-Shell – Reproduktion der realen, weiterhin ungepatchten Kette CVE-2025-0890 (verstecktes supervisor-Konto) + CVE-2024-40891 (CLI-Befehlsinjektion) auf eigener Hardware.
von damik0 · 2026-08-13 · ein Hardware-Hacking-Walkthrough
📷 PCB photos ─▶ 🔬 chip recon ─▶ 🏷️ model ID ─▶ 📍 find UART (J2)
└─▶ 🔌 serial console ─▶ 🚪 hidden account ─▶ ⛓️ break out of CLI
└─▶ 🐚 root shell ─▶ 🔓 dump + crack every credential
Auf meinem Schreibtisch lag eine x-beliebige Router-Platine. Kein Gehäuse, kein Label, keine Ahnung, was es war. Ausgehend von nichts als zwei Fotos habe ich jeden Chip auf der Platine identifiziert, das genaue Modell bestimmt, den seriellen Header (UART) gefunden, mich eingeklinkt, bin durch ein Werks-Hintertürkonto hineinspaziert, aus einem abgeriegelten Herstellermenü in eine Root-Linux-Shell ausgebrochen und jedes Passwort von dem Gerät gezogen – in unter einer Sekunde geknackt.
Keiner dieser Bugs stammt von mir. Es sind dokumentierte ZyXEL-Probleme, die der Hersteller laut eigener Aussage nicht patchen wird. Worum es in diesem Repo geht, ist das Wie, Ende zu Ende, und die Argumentation hinter jedem Schritt.
Ich habe die ID anhand der Siebdruck-Platinennummer 45-402-000022 und des Barcodes ermittelt und mit TechInfoDepot, der OpenWrt-Hardwaretabelle und der FCC-ID I88P870HN51B abgeglichen:
| Modell | ZyXEL P-870HN-53b – VDSL2/ADSL2+-WLAN-Gateway, hergestellt von MitraStar |
| Zeitraum | ~2013 (Datumsangaben deuten auf KW 21 / 2013); ein von einem ISP verteiltes Gerät |
| SoC | Broadcom BCM6368UKPBG – Dual-Core-MIPS (BMIPS4350), ~400 MHz |
| RAM | 2× Winbond W9425G6JH-5 – je 256 Mbit DDR2 ×16 = 64 MB auf einem 32-Bit-Bus |
| Flash | Macronix MX29LV640EBTI-70G – 8 MB paralleler NOR, TSOP-48 |
| WLAN | Broadcom BCM43222 (802.11n; Dual-Band-Silizium, nur mit 2,4 GHz verdrahtet) |
| DSL-AFE | Broadcom BCM6302 |
| Firmware | Linux 2.6.30 + BusyBox v1.00, Broadcom-CFE-Bootloader (gebaut 2012-06-11) |
Warum die Platinennummer der Schlüssel ist, der alles öffnet: ODM-Gateways wie dieses sind Referenzdesigns. Sobald die Siebdruck-Teilenummer zu einer dokumentierten Platine passt, erbt man die Hausaufgaben von jemand anderem – den genauen Flash-Chip, die UART-Position und Pinbelegung, die Standardkonten. Die Identifizierung ist keine Formalie; sie macht aus blindem Sondieren einen gezielten Angriff.
Zuerst eine kleine Komfortsache: Ich habe einen winzigen LAN-Foto-Uploader zusammengebaut (Python-Standardbibliothek, null Abhängigkeiten), damit ich die Platine mit dem Handy abfotografieren konnte und die Bilder direkt auf meinem Rechner landeten. Die halbe Miete einer guten Session ist es, solche Reibungspunkte zu beseitigen.
Ich bin Chip für Chip die hochauflösenden Fotos durchgegangen. Die Gehäusetypen erzählen die Geschichte, bevor man überhaupt die Beschriftungen liest: Das BGA in der Mitte ist das Gehirn, das TSOP-48 ist fast immer paralleler Flash, die kleine Blechkappe mit Koaxial-Pigtail ist das Funkmodul. Der SoC versteckte sich unter einer RF-Abschirmung (für EMV und als Wärmeverteiler), also habe ich die Kappe abgehebelt, um die Markierung darunter zu lesen – ein Broadcom BCM6368.

Die Platine, kartiert: (1) BCM6368-SoC, (2) DDR2-RAM, (3) BCM43222-WLAN, (4) DSL-Leitungsübertrager, (5) Ethernet-Magnetics, (6) interne USB-Header, (7)(8) Platinenmarkierungen, (9) der J2-Konsolen-Header, (10) ein unbestücktes JTAG-Footprint.
Der Siebdruck 45-402-000022 passte exakt zur dokumentierten Platine, und jeder Chip – SoC, Flash, WLAN, die 64 MB RAM – fügte sich Stück für Stück zur -53b-Variante. Jetzt wusste ich nicht nur, was es war, sondern auch, wo sein UART lag und welches Konto mich hineinlassen würde.
Direkt neben dem SoC befand sich ein bestückter 6-Pin-Winkelstecker mit Siebdruck J2 und einer Dreiecksmarkierung für Pin 1. Bestätigt durch die OpenWrt-Seite für das Modell:
| Pin | Signal | |
|---|---|---|
| 1 | VCC 3,3 V | nicht anschließen |
| 2 | Tx | → Adapter RX |
| 3 | Rx | → Adapter TX |
| 4 | GND | → Adapter GND |
| 5 | NC |
115200 8N1, 3,3-V-TTL.

So findet man ein UART, wenn einem kein Wiki die Pinbelegung verrät – der Teil, der zeigt, dass das nicht nur einer Anleitung folgt:
- GND – Durchgang (Piepser) zur Massefläche / Abschirmung, Platine ohne Strom.
- VCC – eine Schiene, die nach dem Einschalten konstant bei ~3,3 V liegt.
- TX – ruht auf High bei ~3,3 V (UART-Markenzustand) und zuckt/flackert sichtbar in dem Moment, in dem das Gerät bootet und sein Log ausspeit. Dieses Flackern ist die Konsole, die spricht.
- RX – normalerweise der ruhige Kandidat: floating oder schwach gepullt, keine Boot-Aktivität.
Und wenn man die Baudrate nicht wüsste?
115200ist die Broadcom-CFE-Standardeinstellung, aber im Blindflug würde man entweder die üblichen Raten durchlaufen (9600 → 115200), bis der Müll zu ASCII wird, oder die schmalste Bitbreite am Oszilloskop messen und1 / trechnen.
Ich habe einen USB-TTL-Adapter (HW-597, PL2303) mit dem Jumper auf 3,3 V verdrahtet. Das ist wichtig: Das UART des BCM6368 ist 3,3-V-TTL, nicht RS-232 (±12 V) – wenn man einen echten seriellen Port oder einen 5-V-Adapter daranhängt, brät man den Pin. Masse zuerst, dann RX/TX gekreuzt, VCC auf Float gelassen, damit nichts die Platine rückwärts mit Strom versorgt. Vor dem Anlegen der Spannung habe ich GND und VCC mit einem Multimeter bestätigt, dann:
screen /dev/ttyUSB0 115200
Eingeschaltet, zugesehen, wie der CFE an den Kernel übergibt, wie BusyBox-init hochkommt, und landete an einem Login-Prompt.
Der Login fiel einem versteckten Werkskonto zum Opfer, das ZyXEL für Endnutzer nie dokumentiert:
user: supervisor
pass: zyad1234
Volle Systemrechte. Das ist der Kern von CVE-2025-0890.
Dieser Login warf mich in eine abgeriegelte Hersteller-CLI (consoled, ein >-Prompt): kein sh, keins der üblichen Linux-Werkzeuge – ein Jail um das eigentliche System. Aber sie bot eine ping-Diagnose an, und dieser Befehl baute sein Argument direkt mit null Bereinigung in einen Shell-String ein. Also habe ich ihm ein Shell-Metazeichen gefüttert:
> ping 127.0.0.1; sh
… und landete in einer Root-BusyBox-Shell (#).