CVE-2025-55423 분석을 직접 재현해보기로 하고 제일 먼저 한 일이 펌웨어를 손에 넣는 거였다. 펌웨어가 단일 ELF 바이너리 하나면 편하겠지만, 임베디드 라우터 펌웨어는 거의 다 부트로더, 커널 이미지, 루트 파일시스템(squashfs 등) 세 덩어리가 한 파일에 통째로 들어 있고 각각 압축까지 돼 있다. 그래서 cat이나 strings로 열어봐야 알아먹을 수가 없다. 이걸 표준적으로 분석하는 도구가 binwalk이다.
Kali Linux에는 binwalk이 기본으로 들어 있다. 없으면 sudo apt install -y binwalk squashfs-tools로 깔면 되는데, 여기서 squashfs-tools를 같이 까는 게 중요하다. 라우터 펌웨어의 루트 파일시스템은 거의 항상 squashfs라, binwalk이 squashfs 시그니처를 찾아도 실제로 풀려면 unsquashfs 바이너리가 있어야 한다. 이게 없으면 시그니처는 찾아놓고 추출 단계에서 조용히 실패한다.
ipTIME 공식 사이트에서 N2V용 펌웨어 n2v_ml_12_168.bin(12.16.8 버전, 패치 안 된 취약 버전)을 받아서 시작했다. file 명령으로 먼저 확인해봤는데 결과가 그냥 data로 나왔다. 이건 표준 시그니처에 안 걸린다는 뜻일 뿐이지 내용이 없다는 얘기가 아니다. 라우터 펌웨어는 벤더마다 자체 헤더로 시작하는 경우가 많아서 file 명령은 여기서 거의 도움이 안 된다.
binwalk n2v_ml_12_168.bin
binwalk은 파일을 처음부터 끝까지 훑으면서 알려진 시그니처(매직 넘버)를 찾는다. uImage 헤더, gzip/lzma 압축 데이터, squashfs 파일시스템, ELF 헤더 같은 것들. 출력이 대략 이렇게 나왔다.
DECIMAL HEXADECIMAL DESCRIPTION
-----------------------------------------------------------
0 0x0 <벤더 헤더>
112 0x70 LZMA compressed data, ...
1310864 0x140090 Squashfs filesystem, little endian,
version 4.0, compression: lzma, ...
이걸 보는 순간 펌웨어 구조가 눈에 그려졌다. 처음에 헤더, 그다음 LZMA로 압축된 커널 이미지, 그리고 1.31MB 오프셋에 squashfs 파일시스템.
dd로 오프셋을 직접 잘라낼 수도 있지만 -e 옵션으로 자동 추출하는 게 훨씬 편하다.
binwalk -e n2v_ml_12_168.bin
같은 디렉토리에 _n2v_ml_12_168.bin.extracted/ 폴더가 생기고, 그 안에 squashfs-root/라는 폴더가 나온다.
cd _n2v_ml_12_168.bin.extracted/squashfs-root
ls
# bin dev etc home lib mnt proc root sbin sys tmp usr var www
라우터의 진짜 루트 디렉토리 구조가 그대로 드러나는 걸 보고 좀 신기했다. 이 안의 모든 파일이 실제로 그 라우터에서 돌아가는 파일 그대로다.
분석할 때 첫 번째로 보는 자리가 대충 정해져 있다. /etc/passwd와 /etc/shadow에는 종종 하드코딩된 계정이나 백도어 계정의 해시가 그대로 박혀 있다. 우리 N2V는 여기서 특별한 게 안 나왔다. /www/에는 라우터 관리 페이지의 HTML/CGI 코드가 전부 들어 있어서, cgi-bin 안의 바이너리를 보면서 인증 우회나 command injection 가능성을 살필 수 있다.
이번에 진짜 필요했던 건 /lib/였다. CVE-2025-55423의 취약 함수 upnp_relay()가 있다고 알려진 libcgi.so가 여기 있다.
file lib/libcgi.so
# ELF 32-bit LSB shared object, MIPS, MIPS32 rel2 version 1 (SYSV), ...
MIPS 32비트 빌드. ipTIME 공유기 대부분이 MIPS라 예상했던 대로다. 이건 호스트(x86)에서 그냥 실행은 못 하고, Ghidra/IDA로 정적 분석하거나 qemu-mipsel-static으로 사용자 모드 에뮬레이션을 걸어야 한다. 이번 글은 추출까지가 목표라 여기서 끊고, libcgi.so를 Ghidra로 디컴파일한 내용은 다음 회차에 정리할 생각이다.
binwalk을 돌렸는데 squashfs-root가 안 생기는 경우가 있었다. 원인은 두 가지였는데, 하나는 squashfs-tools가 안 깔려서였다. binwalk이 squashfs를 발견은 했지만 추출 단계에서 unsquashfs를 못 찾아 실패하는 거였고, 출력을 잘 보면 WARNING: extractor 'unsquashfs' not found가 뜬다. 다른 하나는 어떤 펌웨어는 한 겹 더 안에 압축이 또 있는 경우인데, 이럴 땐 -e 대신 -Me(matryoshka 모드)로 재귀 추출을 걸면 된다.
binwalk -Me n2v_ml_12_168.bin
binwalk은 본질적으로 시그니처 스캐너다. 파일시스템·압축 포맷·바이너리 헤더의 매직 넘버 데이터베이스를 갖고 있고, 입력 파일을 1바이트씩 밀면서 패턴이 나오는지 확인한다. 발견되면 오프셋을 출력하고, -e 옵션이면 그 시그니처에 맞는 추출 도구를 호출해서 풀어준다.
당연히 한계도 있다. 시그니처 기반이라 암호화된 펌웨어는 그냥 랜덤 데이터로 보여서 못 푼다. 펌웨어가 암호화됐는지 확인하려면 binwalk -E로 엔트로피 분석을 걸어보면 된다. 전체 구간이 0.97~1.0 근처로 평탄하면 암호화나 강한 압축 신호고, 정상 펌웨어는 헤더·코드 영역 엔트로피가 낮고 압축 영역만 높아서 그래프가 들쭉날쭉하다.
대부분의 SOHO 라우터는 부트로더로 U-Boot, 커널로 Linux(MIPS 또는 ARM), 루트 파일시스템으로 SquashFS(가끔 JFFS2/UBIFS), 압축으로 LZMA/gzip/xz를 쓰는 구성이다. OpenWRT 같은 오픈 펌웨어로 갈아탈 수 있는 라우터가 많은 것도 결국 이 구조가 표준 임베디드 Linux 위에서 돌아가기 때문이다.
binwalk이 명령어 한 줄로 라우터의 내장을 통째로 까볼 수 있게 해준다는 게 처음엔 좀 비현실적으로 느껴졌다. 동시에 이 정도로 쉽게 열리는 게 라우터 보안의 현주소라는 것도 같이 느꼈다. 펌웨어가 평문으로 배포되고, 시그니처도 표준이고, 추출 도구도 다 무료 공개돼 있으니까. 벤더가 못 막은 건지 아니면 분석 가능하게 일부러 둔 건지는 모르겠지만, 어느 쪽이든 분석하는 입장에선 나쁘지 않다.
다음 단계는 Ghidra 환경 잡고 libcgi.so 디컴파일하는 거다. 분석 대상 펌웨어는 본인 소유 ipTIME N2V 공유기용 공식 펌웨어고, 분석은 격리된 Kali Linux VM에서만 진행하고 있다.
펌웨어를 추출한 뒤 확인한 실제 취약점은 ipTIME N2V CVE-2025-55423 Command Injection 분석에서 이어진다.
binwalk이 서명과 오프셋을 찾아주는 단계, SquashFS 도구가 파일시스템을 실제로 푸는 단계, hexdump로 헤더와 경계를 직접 확인하는 단계를 나눠두면 자동 추출 실패 때 원인을 찾기 쉽다. 자동화 결과만 믿지 않고 오프셋과 파일 형식을 한 번 더 확인하는 것이 펌웨어 분석의 재현성을 높였다.
| AIxCC 우승 시스템 ATLANTIS·Crete 로컬 설치 후기 (1) | 2026.07.26 |
|---|---|
| CTF - Cascading the Seven Seas: 브라우저 DevTools로 숨겨진 플래그 찾기 (0) | 2026.07.25 |
| 드림핵 시스템 해킹 입문 — 무료 자료만으로 시작하는 루트 (0) | 2026.07.24 |
| AFL++로 네트워크 포렌식 파서 퍼징해서 GHSA 10건 제보·게재까지 (0) | 2026.07.23 |
| ipTIME N2V 공유기 CVE-2025-55423 분석 — UPnP Relay에 박힌 Command Injection (0) | 2026.07.23 |