
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:
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:
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 (#).
Por qué basta un punto y coma: el manejador de la CLI hace el equivalente de
system("ping " + userinput). El;cierra el comandopinge inicia un segundo —sh— que hereda el stdin/stdout de la consola, así que obtienes una shell interactiva. La cuenta ya era uid 0; la CLI era lo único que se interponía entre yo y una shell real, y la entrada sin sanitizar derribó esa pared. Encadenar una inyección autenticada en la CLI con la cuenta oculta es exactamente el patrón registrado como CVE-2024-40891.
Con root, eché un vistazo al sistema y al flash:
# cat /proc/version
Linux version 2.6.30 ... (Buildroot 2010.02) #1 Mon Jun 11 2012
# cat /proc/mtd
dev: size erasesize name
mtd0: 004f5000 004f5000 "Physically mapped flash" # 5,197,824 bytes = the whole firmware
Lo que te está diciendo
/proc/mtd: el flash es NOR paralelo, mapeado en memoria — la CPU lo ve como un rango de direcciones plano (hacia0xB8000000en MIPS KSEG1), expuesto como una única partición MTD. Son excelentes noticias para un volcado: el NOR se lee limpio, sin los bytes spare/OOB ni las rarezas de ECC con las que peleas en NAND./dev/mtdblock0es la imagen del firmware.
Luego las contraseñas. No hay /etc/shadow — los hashes están directamente en /etc/passwd, usando el antiguo DES crypt:
supervisor:SuO7vycdWI/rU:0:0:Administrator:/:/bin/sh
support:xoKf506EVkGKw:1:0:Technical Support:/:/bin/sh
user:QWOftoXez8Goo:2:0:Normal User:/:/bin/sh
admin:OJGXQ9dWyb9m2:100:0:Administrator:/:/bin/sh
Los extraje y los crackeé sin conexión con John the Ripper (--format=descrypt). Los seis cayeron en menos de un segundo:
Por qué se evaporan al instante: el
crypt(3)de DES solo usaba los primeros 8 caracteres de la contraseña y una sal de 12 bits, pasados por 25 rondas de DES. En una CPU moderna, un cracker de DES bitslice mastica decenas de millones de candidatos por segundo — así que contraseñas de fábrica triviales comoadminosupportni siquiera se notan como trabajo. Este esquema quedó obsoleto hace décadas; encontrarlo en firmware en circulación es el hallazgo real.
Cuatro cuentas de fábrica con privilegio a nivel root, contraseñas triviales, idénticas en todas las unidades de este modelo.
Toda la cadena, en una línea:
PCB photo → ID the SoC → find the UART (J2) → serial console
→ hidden account (F-1) → locked CLI → command injection (F-2)
→ root shell → dump + crack every credential (F-3)
No descubrí nada de esto. Es reproducción, mediante acceso físico, de fallos públicos y actualmente relevantes:
supervisor:zyad1234, en CPE DSL heredados de ZyXEL. → F-1Estos afectan a equipos al final de su vida útil que ZyXEL ha dicho que no arreglará, y se ha visto que se explotan en el mundo real — que es exactamente por lo que importa ensuciarse las manos con la mecánica. El valor aquí no es un fallo nuevo; es el flujo de trabajo completo de silicio a shell, hecho y documentado.
execve con un vector de argumentos, no system())./etc/shadow con un hash moderno (bcrypt / SHA-512-crypt), no DES.mtd0 (/dev/mtdblock0, 5,197,824 bytes) por el propio WiFi del router + nc, y luego desempaquetarlo con binwalk (espera cabecera CFE + kernel comprimido con LZMA + rootfs SquashFS + nvram). Método completo en evidence/dump-methods.md.Análisis del PCB a partir de fotos (ImageMagick), un multímetro, un adaptador USB-TTL PL2303 (HW-597) a 3.3 V, screen para la consola serie (115200 8N1), y John the Ripper (descrypt) para los hashes. Identificación mediante OpenWrt, TechInfoDepot y la base de datos de la FCC.
Identificación del dispositivo
45-402-000022, flash MX29LV640EBTI-70G)Vulnerabilidades
supervisor ocultaHardware y técnica
Herramientas
Hecho en hardware mío, aislado de la red, sin sistemas de terceros de por medio. Los fallos están documentados públicamente y citados arriba. Lo que pongo bajo mi nombre es el método de hardware-hacking de extremo a extremo y su ejecución — no el descubrimiento de los fallos.
| 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) |
| 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 |
| Cuenta | Contraseña | UID | GID | Privilegio |
|---|
| supervisor | zyad1234 | 0 | 0 | root |
| support | support | 1 | 0 | grupo root |
| user | user | 2 | 0 | grupo root |
| admin | admin | 100 | 0 | grupo root |
| nobody | zyad1234 | 99 | 99 | ftp |
| # | Hallazgo | CWE | Severidad |
|---|
| F-1 | Cuentas ocultas de fábrica con privilegio root (supervisor, support, user, admin) — credenciales hardcodeadas, idénticas en todas las unidades. | CWE-798 | Alta |
| F-2 | Inyección de comandos autenticada en el diagnóstico ping de la CLI → fuga a una shell root, saltando el límite de privilegios de la CLI. | CWE-78 | Alta |
| F-3 | Almacenamiento de credenciales débil — hashes DES-crypt en /etc/passwd (sin shadow) → crackeo offline instantáneo. | CWE-916 / CWE-256 | Media |