
De uma PCB nua até root: hackeando o hardware de um ZyXEL P-870HN (BCM6368) via UART — CVE-2025-0890 + CVE-2024-40891, no meu próprio hardware.
Duas fotos de celular de uma placa de roteador sem etiqueta → console serial → bypass de autenticação → shell root — reproduzindo a cadeia real, ainda sem patch, CVE-2025-0890 (conta supervisor oculta) + CVE-2024-40891 (injeção de comando no CLI) no hardware que eu possuo.
por damik0 · 2026-08-13 · um passo a passo de hardware-hacking
📷 Fotos da PCB ─▶ 🔬 recon de chips ─▶ 🏷️ ID do modelo ─▶ 📍 encontrar UART (J2)
└─▶ 🔌 console serial ─▶ 🚪 conta oculta ─▶ ⛓️ escapar do CLI
└─▶ 🐚 shell root ─▶ 🔓 dump + quebrar todas as credenciais
Eu tinha uma placa de roteador aleatória em cima da mesa. Sem carcaça, sem etiqueta, sem ideia do que era. Partindo do nada além de duas fotos, li cada chip da PCB, identifiquei o modelo exato, encontrei o header serial (UART), liguei nele, entrei por uma conta de backdoor de fábrica, escapei de um menu do fornecedor bloqueado para um shell Linux root e extraí todas as senhas do equipamento — quebradas em menos de um segundo.
Nenhum desses bugs é meu. São problemas documentados da ZyXEL que o fornecedor disse que não vai corrigir. O que este repo mostra é o como, de ponta a ponta, e o raciocínio em cada etapa.
Descobri a identificação a partir do número da placa na serigrafia 45-402-000022 e do código de barras, cruzando com o TechInfoDepot, a tabela de hardware do OpenWrt e o FCC ID I88P870HN51B:
| Modelo | ZyXEL P-870HN-53b — gateway WiFi VDSL2/ADSL2+, fabricado pela MitraStar |
| Era | ~2013 (os códigos de data apontam para a semana 21 de 2013); unidade distribuída por ISP |
| SoC | Broadcom BCM6368UKPBG — MIPS dual-core (BMIPS4350), ~400 MHz |
| RAM | 2× Winbond W9425G6JH-5 — 256 Mbit DDR2 ×16 cada = 64 MB em um barramento de 32 bits |
| Flash | Macronix MX29LV640EBTI-70G — NOR paralela de 8 MB, TSOP-48 |
| WiFi | Broadcom BCM43222 (802.11n; silício dual-band, ligado apenas a 2,4 GHz) |
| DSL AFE | Broadcom BCM6302 |
| Firmware | Linux 2.6.30 + BusyBox v1.00, bootloader Broadcom CFE (compilado em 2012-06-11) |
Por que o número da placa é a chave que destranca tudo: gateways ODM como este são projetos de referência. Quando o número de parte na serigrafia corresponde a uma placa documentada, você herda o dever de casa de outra pessoa — o chip de flash exato, a localização e o pinout da UART, as contas padrão. A identificação não é uma formalidade; é o que transforma sondagem às cegas em um ataque direcionado.
Primeiro, uma pequena melhoria de qualidade de vida: montei rapidinho um uploader de fotos via LAN (stdlib do Python, zero dependências) para fotografar a placa com o celular e as fotos caírem direto na minha máquina. Metade de uma boa sessão é eliminar esse tipo de atrito.
Fui chip por chip nas fotos em alta resolução. Os tipos de encapsulamento contam a história antes mesmo de você ler as marcações: o BGA no centro é o cérebro, o TSOP-48 quase sempre é a flash paralela, a latinha com o cabo coaxial é o rádio. O SoC estava escondido sob um escudo de RF (ali por causa de EMI + como dissipador de calor), então tirei a tampa para ler a marcação embaixo — um Broadcom BCM6368.

A placa mapeada: (1) SoC BCM6368, (2) RAM DDR2, (3) WiFi BCM43222, (4) transformador de linha DSL, (5) magnéticos Ethernet, (6) headers USB internos, (7)(8) marcações da placa, (9) header de console J2, (10) footprint JTAG não populado.
A serigrafia 45-402-000022 correspondeu exatamente à placa documentada, e cada chip — SoC, flash, WiFi, os 64 MB de RAM — bateu peça por peça com a variante -53b. Agora eu não sabia apenas o que era; eu sabia onde vivia a UART e qual conta me deixaria entrar.
Bem ao lado do SoC havia um header de 6 pinos em ângulo reto, populado, com serigrafia J2 e um triângulo marcando o pino 1. Confirmei na página do OpenWrt para o modelo:
| Pino | Sinal | |
|---|---|---|
| 1 | VCC 3.3V | deixe-o desconectado |
| 2 | Tx | → RX do adaptador |
| 3 | Rx | → TX do adaptador |
| 4 | GND | → GND do adaptador |
| 5 | NC |
115200 8N1, TTL de 3,3 V.

Como encontrar uma UART quando não há wiki informando o pinout — a parte que mostra que isto não é apenas seguir uma receita:
- GND — continuidade (bip) com o plano de terra / a blindagem, com a placa desenergizada.
- VCC — um trilho que se mantém estável em ~3,3 V quando energizado.
- TX — fica em nível alto em ~3,3 V (estado de marca da UART) e oscila/pisca visivelmente no instante em que o dispositivo inicia e começa a despejar o log. Essa oscilação é o console falando.
- RX — normalmente o quieto: flutuante ou com pull fraco, sem atividade no boot.
E se você não soubesse o baud?
115200é o padrão do CFE da Broadcom, mas às cegas você varreria as taxas comuns (9600 → 115200) até o lixo virar ASCII, ou mediria o período de bit mais estreito num osciloscópio e faria1 / t.
Liguei um adaptador USB-TTL (HW-597, PL2303) com o jumper em 3,3 V. Isso importa: a UART do BCM6368 é TTL de 3,3 V, não RS-232 (±12 V) — conecte uma porta serial de verdade ou um adaptador de 5 V nela e você queima o pino. Terra primeiro, depois RX/TX cruzados, VCC deixado flutuante para que nada realimente a placa. Antes de aplicar energia, confirmei GND e VCC com um multímetro e então:
screen /dev/ttyUSB0 115200
Liguei, vi o CFE passar o controle para o kernel, vi o init do BusyBox subir e parei em um prompt de login.
O login cedeu a uma conta de fábrica oculta que a ZyXEL nunca documenta para o usuário final:
user: supervisor
pass: zyad1234
Privilégio total do sistema. Esse é o núcleo da CVE-2025-0890.
Esse login me jogou em um CLI do fornecedor bloqueado (consoled, um prompt >): sem sh, nenhuma das ferramentas Linux usuais — uma jaula em volta do sistema real. Mas ele expunha um diagnóstico ping, e esse comando montava o argumento direto numa string de shell com zero sanitização. Então dei a ele um metacaractere de shell:
> ping 127.0.0.1; sh
…e caí em um shell BusyBox root (#).
Por que um ponto e vírgula é suficiente: o handler do CLI faz o equivalente a
system("ping " + userinput). O;encerra o comandopinge inicia um segundo —sh— que herda o stdin/stdout do console, então você obtém um shell interativo. A conta já era uid 0; o CLI era a única coisa entre mim e um shell de verdade, e a entrada não sanitizada derrubou essa parede. Encadear uma injeção de CLI autenticada na conta oculta é exatamente o padrão rastreado como CVE-2024-40891.
Com root, examinei o sistema e a flash: