
D'une carte nue à l'accès root : piratage matériel d'un ZyXEL P-870HN (BCM6368) via UART — CVE-2025-0890 + CVE-2024-40891, sur mon propre matériel.
Deux photos prises au téléphone d'une carte de routeur non étiquetée → console série → bypass d'authentification → shell root — reproduction de la chaîne réelle, toujours non corrigée, CVE-2025-0890 (compte supervisor caché) + CVE-2024-40891 (injection de commande CLI) sur du matériel qui m'appartient.
par damik0 · 2026-08-13 · un walkthrough de hardware-hacking
📷 PCB photos ─▶ 🔬 chip recon ─▶ 🏷️ model ID ─▶ 📍 find UART (J2)
└─▶ 🔌 serial console ─▶ 🚪 hidden account ─▶ ⛓️ break out of CLI
└─▶ 🐚 root shell ─▶ 🔓 dump + crack every credential
J'avais une carte de routeur lambda qui traînait sur mon bureau. Sans boîtier, sans étiquette, sans la moindre idée de ce que c'était. En partant de rien, seulement deux photos, j'ai déchiffré chaque puce du PCB, déterminé le modèle exact, trouvé le connecteur série (UART), m'y suis branché, suis entré par un compte backdoor d'usine, me suis échappé d'un menu constructeur verrouillé vers un shell Linux root, et j'ai extrait tous les mots de passe de la machine — cassés en moins d'une seconde.
Aucun de ces bugs n'est de moi. Ce sont des problèmes ZyXEL documentés que le constructeur a dit qu'il ne corrigerait pas. Ce repo porte sur le comment, de bout en bout, et le raisonnement à chaque étape.
J'ai déduit l'identité du modèle à partir du numéro de carte sérigraphié 45-402-000022 et du code-barres, recoupés avec TechInfoDepot, la table matérielle d'OpenWrt et l'ID FCC I88P870HN51B :
| Modèle | ZyXEL P-870HN-53b — passerelle VDSL2/ADSL2+ WiFi, fabriquée par MitraStar |
| Époque | ~2013 (les codes de date indiquent la semaine 21 de 2013) ; une unité distribuée par un FAI |
| SoC | Broadcom BCM6368UKPBG — MIPS double cœur (BMIPS4350), ~400 MHz |
| RAM | 2× Winbond W9425G6JH-5 — 256 Mbit DDR2 ×16 chacune = 64 Mo sur un bus 32 bits |
| Flash | Macronix MX29LV640EBTI-70G — NOR parallèle de 8 Mo, TSOP-48 |
| WiFi | Broadcom BCM43222 (802.11n ; silicium double bande, câblé en 2,4 GHz uniquement) |
| AFE DSL | Broadcom BCM6302 |
| Firmware | Linux 2.6.30 + BusyBox v1.00, bootloader Broadcom CFE (compilé le 2012-06-11) |
Pourquoi le numéro de carte est la clé qui ouvre tout : les passerelles ODM comme celle-ci sont des designs de référence. Une fois que le numéro de pièce sérigraphié correspond à une carte documentée, vous héritez des devoirs de quelqu'un d'autre — la puce flash exacte, l'emplacement et le brochage de l'UART, les comptes par défaut. L'identification n'est pas une formalité ; c'est ce qui transforme le sondage à l'aveugle en attaque ciblée.
Petite amélioration du confort d'abord : j'ai bricolé un petit uploader de photos en LAN (stdlib Python, zéro dépendance) pour pouvoir photographier la carte avec mon téléphone et que les photos arrivent directement sur ma machine. La moitié d'une bonne session, c'est d'éliminer ce genre de frictions.
Je suis passé puce par puce sur les photos haute résolution. Les types de boîtiers racontent l'histoire avant même de lire les marquages : le BGA au centre est le cerveau, le TSOP-48 est presque toujours la flash parallèle, la petite boîte métallique avec un câble coaxial est la radio. Le SoC était caché sous un blindage RF (là pour l'EMI et comme dissipateur thermique), j'ai donc ouvert le capot pour lire le marquage en dessous — un Broadcom BCM6368.

La carte, cartographiée : (1) SoC BCM6368, (2) RAM DDR2, (3) WiFi BCM43222, (4) transformateur de ligne DSL, (5) magnétiques Ethernet, (6) connecteurs USB internes, (7)(8) marquages de la carte, (9) le connecteur console J2, (10) une empreinte JTAG non soudée.
La sérigraphie 45-402-000022 correspondait exactement à la carte documentée, et chaque puce — SoC, flash, WiFi, les 64 Mo de RAM — correspondait pièce par pièce à la variante -53b. Désormais, je ne savais pas seulement ce que c'était ; je savais où vivait son UART et quel compte me laisserait entrer.
Juste à côté du SoC, il y avait un connecteur 6 broches à angle droit, sérigraphié J2, avec un triangle marquant la broche 1. Confirmé avec la page OpenWrt du modèle :
| Broche | Signal | |
|---|---|---|
| 1 | VCC 3,3 V | laisser déconnecté |
| 2 | Tx | → RX de l'adaptateur |
| 3 | Rx | → TX de l'adaptateur |
| 4 | GND | → GND de l'adaptateur |
| 5 | NC |
115200 8N1, TTL 3,3 V.

Comment trouver un UART quand aucun wiki ne donne le brochage — la partie qui montre que ça ne consiste pas juste à suivre une recette :
- GND — continuité (bip) avec le plan de masse / le blindage, carte non alimentée.
- VCC — un rail qui se maintient à ~3,3 V stable une fois alimenté.
- TX — reste au niveau haut à ~3,3 V (état mark de l'UART) et chute/clignote visiblement dès que l'appareil démarre et crache son journal. Ce clignotement, c'est la console qui parle.
- RX — généralement la discrète : flottante ou faiblement tirée, aucune activité au démarrage.
Et si vous ne connaissiez pas le débit ?
115200est la valeur par défaut du CFE Broadcom, mais à l'aveugle vous balayeriez les débits courants (9600 → 115200) jusqu'à ce que le charabia devienne de l'ASCII, ou vous mesureriez la période de bit la plus courte sur un oscilloscope et feriez1 / t.
J'ai câblé un adaptateur USB-TTL (HW-597, PL2303) avec le cavalier sur 3,3 V. Ça compte : l'UART du BCM6368 est en TTL 3,3 V, pas en RS-232 (±12 V) — branchez-y un vrai port série ou un adaptateur 5 V et vous grillez la broche. La masse d'abord, puis RX/TX croisés, VCC laissé flottant pour que rien n'alimente la carte en retour. Avant de mettre sous tension, j'ai vérifié GND et VCC au multimètre, puis :
screen /dev/ttyUSB0 115200
Je l'ai alimentée, j'ai regardé le CFE passer la main au noyau, j'ai regardé BusyBox init démarrer, et j'ai atterri sur une invite de login.
Le login a cédé devant un compte d'usine caché que ZyXEL ne documente jamais pour l'utilisateur final :
user: supervisor
pass: zyad1234
Privilège système complet. C'est le cœur de CVE-2025-0890.
Ce login m'a fait atterrir dans une CLI constructeur verrouillée (consoled, une invite >) : pas de sh, aucun des outils Linux habituels — une prison enveloppant le vrai système. Mais elle exposait un diagnostic ping, et cette commande construisait son argument directement dans une chaîne shell avec zéro assainissement. Je lui ai donc fourni un métacaractère shell :
> ping 127.0.0.1; sh
…et j'ai atterri dans un shell BusyBox root (#).