
Da un PCB nudo alla root: hardware-hacking di uno ZyXEL P-870HN (BCM6368) via UART — CVE-2025-0890 + CVE-2024-40891, sul mio stesso hardware.
Due foto dal telefono di una scheda router senza etichetta → console seriale → bypass dell'autenticazione → shell di root — riproduzione della catena reale, ancora non patchata, CVE-2025-0890 (account supervisor nascosto) + CVE-2024-40891 (command injection nella CLI) su hardware di mia proprietà.
di damik0 · 2026-08-13 · un walkthrough di 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
Avevo una scheda router qualsiasi sulla scrivania. Niente case, niente etichetta, nessuna idea di cosa fosse. Partendo da nient'altro che due foto, ho letto ogni chip del PCB, ho individuato il modello esatto, ho trovato il connettore seriale (UART), mi ci sono collegato, sono entrato attraverso un account backdoor di fabbrica, sono uscito da un menu vendor bloccato in una shell Linux di root e ho estratto ogni password dal dispositivo — crackate in meno di un secondo.
Nessuna di queste vulnerabilità è farina del mio sacco. Sono problemi ZyXEL documentati che il vendor ha dichiarato di non voler patchare. Questo repo riguarda il come, dall'inizio alla fine, e il ragionamento dietro ogni passaggio.
Ho risalito all'ID partendo dal numero di scheda serigrafato 45-402-000022 e dal codice a barre, incrociando i dati con TechInfoDepot, la tabella hardware di OpenWrt e l'FCC ID I88P870HN51B:
| Modello | ZyXEL P-870HN-53b — gateway WiFi VDSL2/ADSL2+, prodotto da MitraStar |
| Epoca | ~2013 (i date-code indicano la settimana 21 del 2013); unità distribuita da un ISP |
| SoC | Broadcom BCM6368UKPBG — MIPS dual-core (BMIPS4350), ~400 MHz |
| RAM | 2× Winbond W9425G6JH-5 — 256 Mbit DDR2 ×16 ciascuna = 64 MB su bus a 32 bit |
| Flash | Macronix MX29LV640EBTI-70G — 8 MB NOR parallelo, TSOP-48 |
| WiFi | Broadcom BCM43222 (802.11n; silicio dual-band, collegato solo a 2,4 GHz) |
| AFE DSL | Broadcom BCM6302 |
| Firmware | Linux 2.6.30 + BusyBox v1.00, bootloader Broadcom CFE (compilato il 2012-06-11) |
Perché il numero di scheda è la chiave che sblocca tutto: i gateway ODM come questo sono reference design. Una volta che il numero di parte serigrafato corrisponde a una scheda documentata, erediti i compiti di qualcun altro — il chip di flash esatto, la posizione e il pinout della UART, gli account predefiniti. L'identificazione non è una formalità: è ciò che trasforma il probing alla cieca in un attacco mirato.
Prima una piccola miglioria di comodità: ho buttato giù un minuscolo uploader di foto via LAN (solo stdlib Python, zero dipendenze) così potevo fotografare la scheda col telefono e far arrivare le immagini direttamente sulla mia macchina. Metà di una buona sessione è eliminare attriti come questo.
Sono andato chip per chip partendo dalle foto ad alta risoluzione. I tipi di package raccontano la storia ancor prima di leggere le marcature: il BGA al centro è il cervello, il TSOP-48 è quasi sempre flash parallelo, la piccola scatoletta con il cavetto coassiale è la radio. Il SoC era nascosto sotto uno schermo RF (presente per l'EMI e come diffusore di calore), quindi ho aperto la scatoletta per leggere la marcatura sottostante — un Broadcom BCM6368.

La scheda, mappata: (1) SoC BCM6368, (2) RAM DDR2, (3) WiFi BCM43222, (4) trasformatore di linea DSL, (5) componenti magnetici Ethernet, (6) connettori USB interni, (7)(8) marcature della scheda, (9) connettore console J2, (10) footprint JTAG non popolato.
La serigrafia 45-402-000022 corrispondeva esattamente alla scheda documentata, e ogni chip — SoC, flash, WiFi, i 64 MB di RAM — combaciava pezzo per pezzo con la variante -53b. Ora non sapevo solo cosa fosse; sapevo dove viveva la sua UART e quale account mi avrebbe fatto entrare.
Proprio accanto al SoC c'era un connettore a 6 pin ad angolo retto, popolato, serigrafato J2, con un triangolo che marca il pin 1. Confermato incrociando la pagina di OpenWrt per il modello:
| Pin | Segnale | |
|---|---|---|
| 1 | VCC 3,3 V | lasciarlo scollegato |
| 2 | Tx | → RX adattatore |
| 3 | Rx | → TX adattatore |
| 4 | GND | → GND adattatore |
| 5 | NC |
115200 8N1, 3,3 V TTL.

Come trovi una UART quando non c'è nessuna wiki che ti dice il pinout — la parte che dimostra che non è solo questione di seguire una ricetta:
- GND — continuità (beep) con il piano di massa / lo schermo, scheda non alimentata.
- VCC — un rail che si assesta su ~3,3 V stabili una volta alimentato.
- TX — riposa su livello alto a ~3,3 V (stato mark UART) e cala/sfarfalla visibilmente nell'istante in cui il dispositivo si avvia e inizia a sputare fuori il suo log. Quel sfarfallio è la console che parla.
- RX — di solito quella silenziosa: flottante o debolmente pullata, nessuna attività all'avvio.
E se non conoscessi il baud?
115200è il default del CFE Broadcom, ma alla cieca scorreresti le velocità comuni (9600 → 115200) finché la spazzatura non diventa ASCII, oppure misureresti il periodo di bit più stretto su un oscilloscopio e faresti1 / t.
Ho collegato un adattatore USB-TTL (HW-597, PL2303) con il jumper su 3,3 V. Questo è importante: la UART del BCM6368 è TTL a 3,3 V, non RS-232 (±12 V) — se ci appendi una vera porta seriale o un adattatore a 5 V friggi il pin. Prima la massa, poi RX/TX incrociati, VCC lasciato flottante così nulla alimenta la scheda all'indietro. Prima di dare tensione ho verificato GND e VCC con un multimetro, poi:
screen /dev/ttyUSB0 115200
L'ho alimentata, ho visto il CFE passare il controllo al kernel, ho visto partire l'init di BusyBox e sono finito su un prompt di login.
Il login è caduto su un account di fabbrica nascosto che ZyXEL non documenta mai per l'utente finale:
user: supervisor
pass: zyad1234
Privilegi di sistema completi. Questo è il cuore di CVE-2025-0890.
Quel login mi ha fatto finire in una CLI vendor bloccata (consoled, un prompt >): niente sh, nessuno dei soliti strumenti Linux — una gabbia avvolta attorno al sistema reale. Ma esponeva un comando diagnostico ping, e quel comando costruiva il suo argomento direttamente in una stringa di shell con zero sanitizzazione. Così gli ho passato un metacarattere di shell:
> ping 127.0.0.1; sh
…e sono finito in una shell BusyBox di root (#).