
베어 PCB에서 루트 권한까지: 내 하드웨어에서 UART로 ZyXEL P-870HN (BCM6368) 하드웨어 해킹 — CVE-2025-0890 + CVE-2024-40891
라벨 없는 라우터 보드의 휴대폰 사진 두 장 → 시리얼 콘솔 → 인증 우회 → root 셸 — 제가 소유한 하드웨어에서 실제로 아직 패치되지 않은 체인 CVE-2025-0890(숨겨진 supervisor 계정) + CVE-2024-40891(CLI 명령어 인젝션)을 재현한 과정
by damik0 · 2026-08-13 · 하드웨어 해킹 워크스루
📷 PCB photos ─▶ 🔬 chip recon ─▶ 🏷️ model ID ─▶ 📍 find UART (J2)
└─▶ 🔌 serial console ─▶ 🚪 hidden account ─▶ ⛓️ break out of CLI
└─▶ 🐚 root shell ─▶ 🔓 dump + crack every credential
책상 위에 정체를 알 수 없는 라우터 보드가 하나 있었습니다. 케이스도, 라벨도, 그것이 무엇인지에 대한 단서도 전혀 없었죠. 사진 두 장만 가지고 시작해서, PCB의 모든 칩을 읽어내고 정확한 모델을 특정했으며, 시리얼(UART) 헤더를 찾아 연결했습니다. 공장 백도어 계정으로 들어가 잠긴 벤더 메뉴를 뚫고 root Linux 셸을 얻었고, 박스 안의 모든 비밀번호를 빼내는 데 성공했습니다 — 1초도 채 걸리지 않았습니다.
이 버그들 중 어느 것도 제가 발견한 것이 아닙니다. 이들은 벤더가 패치하지 않겠다고 밝힌, 문서화된 ZyXEL 문제입니다. 이 저장소가 다루는 것은 처음부터 끝까지 **그 방법(how)**과 각 단계에서의 추론 과정입니다.
실크스크린 보드 번호 **45-402-000022**와 바코드에서 ID를 알아냈고, TechInfoDepot, OpenWrt 하드웨어 테이블, FCC ID **I88P870HN51B**와 대조했습니다:
| 모델 | ZyXEL P-870HN-53b — MitraStar 제조 VDSL2/ADSL2+ WiFi 게이트웨이 |
| 시대 | 약 2013년 (날짜 코드상 2013년 제21주); ISP 배포 장치 |
| SoC | Broadcom BCM6368UKPBG — 듀얼 코어 MIPS (BMIPS4350), 약 400 MHz |
| RAM | 2× Winbond W9425G6JH-5 — 각 256 Mbit DDR2 ×16 = 32비트 버스에서 64 MB |
| 플래시 | Macronix MX29LV640EBTI-70G — 8 MB 병렬 NOR, TSOP-48 |
| WiFi | Broadcom BCM43222 (802.11n; 듀얼 밴드 실리콘이지만 2.4 GHz에만 연결) |
| DSL AFE | Broadcom BCM6302 |
| 펌웨어 | Linux 2.6.30 + BusyBox v1.00, Broadcom CFE 부트로더 (2012-06-11 빌드) |
보드 번호가 모든 것을 푸는 열쇠인 이유: 이런 ODM 게이트웨이는 레퍼런스 디자인입니다. 실크스크린 부품 번호가 문서화된 보드와 일치하면, 정확한 플래시 칩, UART 위치와 핀아웃, 기본 계정 같은 다른 사람의 숙제를 그대로 물려받게 됩니다. 식별은 형식적인 절차가 아니라, 맹목적인 탐색을 표적 공격으로 바꿔주는 과정입니다.
먼저 사소한 편의 작업: Python 표준 라이브러리만 사용해 의존성 없는 작은 LAN 사진 업로더를 만들어, 휴대폰으로 보드를 촬영하면 사진이 바로 제 머신에 저장되도록 했습니다. 좋은 작업의 절반은 이런 마찰을 없애는 데 있습니다.
고해상도 사진을 통해 칩 하나하나를 살펴봤습니다. 패키지 타입만 봐도 표기를 읽기 전에 많은 것을 알 수 있습니다. 가운데 있는 BGA는 두뇌(SoC), TSOP-48은 거의 항상 병렬 플래시, 동축 꼬리선이 달린 작은 캔은 무선 모듈입니다. SoC는 RF 실드 아래 숨어 있었는데(EMI 차폐 및 방열판 역할), 캔을 열어 아래 표기를 확인하니 Broadcom BCM6368이었습니다.

보드 매핑: (1) BCM6368 SoC, (2) DDR2 RAM, (3) BCM43222 WiFi, (4) DSL 회선 변압기, (5) 이더넷 자기 소자, (6) 내부 USB 헤더, (7)(8) 보드 표기, (9) J2 콘솔 헤더, (10) 미실장 JTAG 풋프린트.
실크스크린 45-402-000022은 문서화된 보드와 정확히 일치했고, SoC, 플래시, WiFi, 64 MB RAM 등 모든 칩이 -53b 변형과 하나하나 맞아떨어졌습니다. 이제 그것이 무엇인지 알았을 뿐 아니라 UART가 어디에 있고 어떤 계정으로 들어갈 수 있는지도 알게 되었습니다.
SoC 바로 옆에 실크스크린 **J2**로 표시된 6핀 직각 헤더가 실장되어 있었고, 핀 1을 가리키는 삼각형 마크가 있었습니다. OpenWrt의 해당 모델 페이지와 대조해 확인했습니다:
| 핀 | 신호 | |
|---|---|---|
| 1 | VCC 3.3V | 연결하지 않은 채로 둡니다 |
| 2 | Tx | → 어댑터 RX |
| 3 | Rx | → 어댑터 TX |
| 4 | GND | → 어댑터 GND |
| 5 | NC |
115200 8N1, 3.3V TTL.

위키에 핀아웃이 없을 때 UART를 찾는 방법 — 이 부분이 단순히 레시피를 따르는 것이 아님을 보여줍니다:
- GND — 보드 전원을 끈 상태에서 접지면/실드에 도통(비프음) 확인.
- VCC — 전원을 켜면 약 3.3 V로 안정적으로 유지되는 레일.
- TX — 약 3.3 V에서 하이(HIGH)로 유휴 상태(UART 마크 상태)이며, 기기가 부팅되어 로그를 쏟아내는 순간 눈에 띄게 떨어지거나/깜빡입니다. 이 깜빡임이 콘솔이 말을 거는 것입니다.
- RX — 보통 조용한 편입니다. 플로팅이거나 약하게 풀업/풀다운되어 있고 부팅 활동이 없습니다.
보레이트(baud)를 모른다면?
115200이 Broadcom CFE의 기본값이지만, 맹목적으로는 일반적인 속도(9600 → 115200)를 스캔해 깨진 데이터가 ASCII로 바뀌는 지점을 찾거나, 오실로스코프에서 가장 좁은 비트 폭을 측정해1 / t로 계산하면 됩니다.
점퍼를 3.3V에 맞춘 **USB-TTL 어댑터(HW-597, PL2303)**를 연결했습니다. 이는 중요합니다. BCM6368의 UART는 3.3V TTL이지 RS-232(±12V)가 아닙니다. 실제 시리얼 포트나 5V 어댑터를 연결하면 핀이 타버립니다. GND를 먼저 연결하고, RX/TX는 교차 연결했으며, VCC는 플로팅 상태로 두어 보드에 역전원이 들어가지 않게 했습니다. 전원을 넣기 전에 멀티미터로 GND와 VCC를 확인한 다음:
screen /dev/ttyUSB0 115200
전원을 켜고 CFE가 커널로 핸드오프하는 과정, BusyBox init이 기동하는 과정을 지켜본 뒤 login 프롬프트에 도달했습니다.
로그인은 ZyXEL이 최종 사용자에게 결코 문서화하지 않는 숨겨진 공장 계정으로 통과했습니다:
user: supervisor
pass: zyad1234
전체 시스템 권한을 가집니다. 이것이 CVE-2025-0890의 핵심입니다.
그 로그인은 잠긴 벤더 CLI(consoled, > 프롬프트)로 떨어뜨렸습니다. sh도, 일반적인 Linux 도구도 없었고, 실제 시스템을 감싼 감옥이었습니다. 하지만 ping 진단 명령이 노출되어 있었고, 그 명령은 인자를 셸 문자열에 아무 검증 없이 그대로 넣었습니다. 그래서 셸 메타문자를 입력했습니다:
> ping 127.0.0.1; sh
…그리고 root BusyBox 셸(#)로 떨어졌습니다.
세미콜론 하나로 충분한 이유: CLI 핸들러는
system("ping " + userinput)와 동일한 동작을 합니다.;은ping명령을 종료하고 두 번째 명령인sh를 시작하며, 이는 콘솔의 stdin/stdout을 상속받으므로 대화형 셸을 얻습니다. 계정은 이미 uid 0이었고, CLI만이 실제 셸과 저 사이를 가로막고 있었는데, 검증되지 않은 입력이 그 벽을 무너뜨린 것입니다. 숨겨진 계정에 인증된 CLI 인젝션을 연쇄적으로 연결하는 것이 바로 CVE-2024-40891로 추적되는 패턴입니다.
root 권한으로 시스템과 플래시를 살펴봤습니다:
# 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
/proc/mtd이 알려주는 것: 플래시는 병렬 NOR, 메모리 매핑 방식입니다. CPU는 이를 평평한 주소 범위(MIPS KSEG1에서 약0xB8000000부근)로 보며, 단일 MTD 파티션으로 노출됩니다. 덤프 관점에서 보면 아주 좋은 소식입니다. NOR은 NAND에서 겪는 스페어/OOB 바이트와 ECC 문제 없이 깨끗하게 읽힙니다./dev/mtdblock0이 곧 펌웨어 이미지입니다.
그다음은 비밀번호입니다. /etc/shadow가 없고, 해시가 고대의 DES crypt를 사용해 /etc/passwd에 그대로 들어 있습니다:
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
해시를 추출해 John the Ripper(--format=descrypt)로 오프라인 크래킹했습니다. 6개 모두 1초도 안 되어 깨졌습니다:
| 계정 | 비밀번호 | UID | GID | 권한 |
|---|---|---|---|---|
| supervisor | zyad1234 | 0 | 0 | root |
| support | support | 1 | 0 | root 그룹 |
| user | user | 2 | 0 | root 그룹 |
| admin | admin | 100 | 0 | root 그룹 |
| nobody | zyad1234 | 99 | 99 | ftp |
이것들이 즉시 깨지는 이유: DES
crypt(3)는 비밀번호의 처음 8자와 12비트 솔트만 사용해 DES 25라운드를 돌렸습니다. 현대 CPU에서 bitslice DES 크래커는 초당 수천만 개의 후보를 처리하므로,admin이나support같은 단순한 공장 기본 비밀번호는 작업량으로도 잡히지 않습니다. 이 방식은 수십 년 전에 이미 구식이었고, 출시된 펌웨어에서 이것을 발견한 것이 진짜 발견입니다.
root 수준 권한을 가진 공장 기본 계정 4개, 단순한 비밀번호, 이 모델의 모든 장치에서 동일합니다.
| # | 발견 사항 | CWE | 심각도 |
|---|---|---|---|
| F-1 | root 권한의 숨겨진 공장 기본 계정 (supervisor, support, user, admin) — 모든 장치에서 동일한 하드코딩된 자격 증명. | CWE-798 | 높음 |
| F-2 | 인증된 명령어 인젝션 — CLI ping 진단 기능에서 root 셸로 탈출하여 CLI의 권한 경계를 점프. | CWE-78 | 높음 |
| F-3 | 취약한 자격 증명 저장 — /etc/passwd의 DES-crypt 해시(shadow 없음) → 즉시 오프라인 크래킹 가능. | CWE-916 / CWE-256 | 중간 |
전체 체인을 한 줄로 표현하면:
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)
저는 이 중 어떤 것도 발견하지 않았습니다. 이는 물리적 접근을 통한, 공개적이고 현재 유효한 버그들의 재현입니다:
supervisor:zyad1234를 포함한 취약/숨겨진 자격 증명. → F-1이것들은 ZyXEL이 패치하지 않겠다고 밝힌 수명 종료(EOL) 장비를 타격하며, 실제 환경에서 악용된 사례가 확인되었습니다. 그래서 그 메커니즘을 직접 다뤄보는 것이 중요한 이유입니다. 여기서의 가치는 새로운 버그가 아니라, 완전한 실리콘-투-셸(silicon-to-shell) 워크플로우를 실행하고 문서화한 데 있습니다.