
De una PCB desnuda a root: hackeo de hardware de una ZyXEL P-870HN (BCM6368) a través de UART — CVE-2025-0890 + CVE-2024-40891, en mi propio hardware.
Dos fotos de móvil de una placa de router sin etiquetar → consola serie → bypass de autenticación → shell root — reproduciendo la cadena real, aún sin parchear, CVE-2025-0890 (cuenta supervisor oculta) + CVE-2024-40891 (inyección de comandos en la CLI) en hardware que poseo.
por damik0 · 2026-08-13 · una guía paso a paso 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
Tenía una placa de router cualquiera sobre mi mesa. Sin carcasa, sin etiqueta, sin idea de qué era. Partiendo de nada más que dos fotos, leí cada chip del PCB, identifiqué el modelo exacto, encontré el conector serie (UART), me conecté a él, entré a través de una cuenta backdoor de fábrica, escapé de un menú de fabricante restringido hacia una shell root de Linux, y extraje todas las contraseñas del equipo — crackeadas en menos de un segundo.
Ninguno de estos fallos es mío. Son problemas documentados de ZyXEL que el fabricante ha dicho que no va a parchear. De lo que trata este repo es del cómo, de principio a fin, y del razonamiento en cada paso.
Identifiqué el modelo a partir del número de serigrafía de la placa 45-402-000022 y del código de barras, contrastándolo con TechInfoDepot, la tabla de hardware de OpenWrt y el ID de la FCC I88P870HN51B:
| Modelo | ZyXEL P-870HN-53b — pasarela VDSL2/ADSL2+ WiFi, fabricada por MitraStar |
| Época | ~2013 (los códigos de fecha apuntan a la semana 21 de 2013); una unidad distribuida por un ISP |
| SoC | Broadcom BCM6368UKPBG — MIPS de doble núcleo (BMIPS4350), ~400 MHz |
| RAM | 2× Winbond W9425G6JH-5 — 256 Mbit DDR2 ×16 cada una = 64 MB en un bus de 32 bits |
| Flash | Macronix MX29LV640EBTI-70G — NOR paralelo de 8 MB, TSOP-48 |
| WiFi | Broadcom BCM43222 (802.11n; silicio de doble banda, conectado solo a 2.4 GHz) |
| AFE DSL | Broadcom BCM6302 |
| Firmware | Linux 2.6.30 + BusyBox v1.00, bootloader CFE de Broadcom (compilado el 2012-06-11) |
Por qué el número de placa es la clave que lo desbloquea todo: las pasarelas ODM como esta son diseños de referencia. Una vez que el número de pieza de serigrafía coincide con una placa documentada, heredas el trabajo de otro — el chip de flash exacto, la ubicación y el pinout de la UART, las cuentas por defecto. La identificación no es una formalidad; es lo que convierte el sondeo a ciegas en un ataque dirigido.
Primero, una pequeña mejora de calidad de vida: monté un minúsculo subidor de fotos por LAN (stdlib de Python, cero dependencias) para poder fotografiar la placa con el móvil y que las imágenes aterrizaran directamente en mi máquina. La mitad de una buena sesión consiste en eliminar fricciones como esta.
Fui chip por chip a partir de las fotos en alta resolución. Los tipos de encapsulado te cuentan la historia antes incluso de leer las marcas: el BGA del centro es el cerebro, el TSOP-48 casi siempre es flash paralelo, la pequeña lata con el latiguillo coaxial es la radio. El SoC estaba oculto bajo un escudo RF (presente por EMI y como disipador de calor), así que desmonté la lata para leer la marca de debajo — un Broadcom BCM6368.

La placa, mapeada: (1) SoC BCM6368, (2) RAM DDR2, (3) WiFi BCM43222, (4) transformador de línea DSL, (5) magnéticos de Ethernet, (6) cabeceras USB internas, (7)(8) marcas de la placa, (9) la cabecera de consola J2, (10) una huella JTAG sin poblar.
La serigrafía 45-402-000022 coincidía exactamente con la placa documentada, y cada chip — SoC, flash, WiFi, los 64 MB de RAM — encajaba pieza a pieza con la variante -53b. Ahora no solo sabía qué era; sabía dónde vivía su UART y qué cuenta me dejaría entrar.
Justo al lado del SoC había una cabecera poblada de 6 pines en ángulo recto, con la serigrafía J2 y un triángulo que marca el pin 1. Lo confirmé con la página de OpenWrt para el modelo:
| Pin | Señal | |
|---|---|---|
| 1 | VCC 3.3V | déjalo desconectado |
| 2 | Tx | → RX del adaptador |
| 3 | Rx | → TX del adaptador |
| 4 | GND | → GND del adaptador |
| 5 | NC |
115200 8N1, 3.3V TTL.

Cómo encuentras una UART cuando no hay ninguna wiki que te diga el pinout — la parte que demuestra que esto no es solo seguir una receta:
- GND — continuidad (pitido) con el plano de tierra / escudo, con la placa sin alimentar.
- VCC — un riel que se mantiene estable en ~3.3 V una vez alimentada.
- TX — se mantiene en alto a ~3.3 V (estado de marca UART) y visiblemente baja/parpadea en el instante en que el dispositivo arranca y empieza a escupir su log. Ese parpadeo es la consola hablando.
- RX — normalmente la tranquila: flotante o con pull débil, sin actividad de arranque.
¿Y si no sabías el baudrate?
115200es el valor por defecto del CFE de Broadcom, pero a ciegas barrerías las velocidades habituales (9600 → 115200) hasta que la basura se convirtiera en ASCII, o medirías el periodo de bit más estrecho en un osciloscopio y harías1 / t.
Conecté un adaptador USB-TTL (HW-597, PL2303) con el jumper en 3.3V. Esto importa: la UART del BCM6368 es TTL de 3.3 V, no RS-232 (±12 V) — si le cuelgas un puerto serie real o un adaptador de 5 V, fríes el pin. Primero tierra, luego RX/TX cruzados, VCC flotante para que nada realimente la placa. Antes de aplicar alimentación confirmé GND y VCC con un multímetro, y luego:
screen /dev/ttyUSB0 115200
Lo encendí, vi al CFE pasar el control al kernel, vi arrancar el init de BusyBox y aterricé en un prompt de login.
El login cedió ante una cuenta oculta de fábrica que ZyXEL nunca documenta para el usuario final:
user: supervisor
pass: zyad1234
Privilegio total del sistema. Este es el núcleo de CVE-2025-0890.
Ese login me dejó en una CLI de fabricante bloqueada (consoled, un prompt >): sin sh, sin ninguna de las herramientas habituales de Linux — una jaula alrededor del sistema real. Pero exponía un diagnóstico ping, y ese comando insertaba su argumento directamente en una cadena de shell con cero sanitización. Así que le pasé un metacarácter de shell:
> ping 127.0.0.1; sh
…y caí en una shell root de BusyBox (#).