Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
도구/GitHubGitHub/harbingerse7en/cve-2024-20154
Embedded Systems SecurityIoT SecurityMemory ForensicsVulnerability AnalysisReverse EngineeringMobile SecurityHardware & IoT SecurityBinary AnalysisPapers & ResearchLearning & EducationFirmware Analysis
3개월 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유
GitHub
harbingerse7en/cve-2024-20154

CVE-2024-20154

CVE-2024-20154에 대한 기술 분석 문서로, MediaTek MT6769 NB-IoT 베이스밴드 펌웨어의 스택 기반 버퍼 오버플로우를 다루며 리버스 엔지니어링과 익스플로잇 체인을 설명합니다.

저장소 보기

CVE-2024-20154: MediaTek MT6769 베이스밴드의 NB-IoT SIB1-NB 스택 오버플로

분류: CWE-121 — 스택 기반 버퍼 오버플로
심각도: 치명적 (MediaTek 공지) · 8.8 높음, 공격 벡터: 인접 (CISA-ADP)
유형: 원격 코드 실행 — 사용자 상호작용 없음, 사전 연관 없음
공개: MediaTek 보안 공지, 2025년 1월 6일 https://corp.mediatek.com/product-security-bulletin/January-2025 분석 대상: Samsung Galaxy A14 SM-A145R — MT6769 패밀리 (Helio G80), MediaTek의 영향받는 칩셋 목록 내 - 펌웨어는 안전한 조건에서 에뮬레이션되었습니다. 상태: 패치됨.


배경 및 동기

이것은 나의 첫 번째로 발표한 베이스밴드 연구였다. 나는 통신 인프라, 합법적 감청 중재 계층, 스팅레이 및 IMSI-캐처 분석, 그리고 임베디드 기기 보안과는 거리가 먼 배경 출신이었다 — 나는 이전에 셀룰러 모뎀에 대한 심층 펌웨어 리버스 엔지니어링을 해본 적이 없었다. 나는 구조화된 분석 방법론이 대상에 관계없이 적용될 수 있다는 것, 그리고 특정 플랫폼에 대한 친숙함이 엄격한 체인 추적으로 대체될 수 있다는 것을 스스로에게 증명하고 싶었다. NB-IoT는 진정으로 위험한 교차점에 위치하기 때문에 눈에 띄었다: 이 프로토콜은 제약된 IoT 기기를 위해 설계되었고, 공격 표면은 사전 연관 단계이며, 모뎀 스택은 핸드셋 사용자가 무엇을 하고 있든 관계없이 이를 처리한다.

패치된 펌웨어가 분석되고 취약한 패턴이 부재함이 확인되었을 때, 특정 기능을 대상으로 하기 전에 펌웨어의 대량 분석에 사용된 AI 시스템은

재구성된 버그 클래스, 조건, 영향을 받는 펌웨어 패밀리를 CVE-2024-20154의 설명과

독립적으로 일치시켰다.

기술적 결론은 분석가 자신의 것이다.


1. 서론

주머니 속의 휴대폰에는 적어도 두 대의 별도 컴퓨터가 들어 있다. 당신이 상호작용하는 하나는 Android를 실행한다. 다른 하나 — 베이스밴드 — 는 완전히 독립적으로 실행되며, 모든 무선 통신을 처리하고, 그 위의 운영체제에는 거의 완전히 보이지 않는다. Android는 완전히 패치되었을 수 있다. 브라우저는 샌드박스 처리되었을 수 있다. 사용자는 결코 악성 링크를 탭하지 않을 수 있다. 취약한 코드가 애플리케이션 프로세서가 관여하기 전에 무선 신호를 처리하는 모뎀 펌웨어에 있다면, 그 어느 것도 중요하지 않다.

CVE-2024-20154는 바로 그런 종류의 취약점이다.

변조된 NB-IoT 시스템 정보 브로드캐스트는 MediaTek 모뎀 펌웨어가 공격자가 제어하는 스케줄링 카운트를 받아들이고, 그 카운트를 RRC-to-L1 구성 경로를 통해 한 번도 클램핑하지 않고 전달하며, 결국 NB-IoT 브로드캐스트 채널 핸들러 내부의 스택 쓰기 루프의 루프 바운드로 사용하도록 만든다. 카운트가 대상 배열의 용량을 초과하면, 루프는 그 너머에 쓰고, 스택의 저장된 레지스터에 도달하며, 저장된 반환 주소를 덮어쓴다. 그런 다음 함수는 손상된 값을 반환 주소 레지스터에 복원하고 그곳으로 점프한다.

심각도를 그렇게 만드는 요소:

  • 취약한 코드 경로는 셀 캠핑 중에 실행된다 — 셀에 동기화한 후 이지만 어떤 RRC 연결, 어떤 인증, 어떤 사용자 상호작용보다도 이전이다.
  • 입력은 무선 브로드캐스트다. 휴대폰은 출처를 인증할 수 없다.
  • 분석된 빌드의 베이스밴드 펌웨어는 ASLR, 스택 카나리, 비실행 스택, 제어 흐름 무결성 없이 실행된다. 저장된 반환 주소 덮어쓰기는 직접적으로 프로그램 카운터 제어로 이어진다.

이 취약점은 MediaTek의 2025년 1월 6일 보안 공지에 치명적 심각도 등급으로 게시되었으며, 무엇보다도 LR12A 모뎀 패밀리에 영향을 미친다. Samsung은 수정 사항을 2025년 2월 보안 유지보수 릴리스에 통합했다.

이 글은 무기화된 익스플로잇을 공개하지 않으며 여기에 게시된 내용만으로는 재현할 수 없다. 목표는 체인이 어디서 끊어지는지, 각 계층이 왜 이를 막지 못했는지, 그리고 라이브 모뎀에 디버거를 붙일 수 없을 때 베이스밴드 버그를 책임감 있게 검증하는 데 무엇이 필요한지를 보여주는 것이다.


2. 대상 및 환경

2.1 기기 및 펌웨어

주 대상: Samsung Galaxy A14 (SM-A145R). 무선 서브시스템은 MT6769 칩셋 패밀리 (Helio G80)의 MediaTek 베이스밴드 프로세서에 의해 구동된다. MT6769 패밀리는 CVE-2024-20154에 대해 MediaTek의 영향받는 칩셋 목록에 명시적으로 기재되어 있다.``` AP/CP firmware: A145RXXU1AWD1 Modem software: MOLY LR12A.R3.TC10.6M.A14.PR.SP.V1.P5 Build date: 2023-04-18

root@kitploit:~
베이스밴드 펌웨어는 Android 코드가 아니다. 이는 SoC의 무선 서브시스템에 있는 별도의 임베디드 시스템으로, 자체 CPU, 자체 RTOS, 자체 메모리 공간을 가지며 Android 프로세스 샌드박스 외부에 존재한다.

### 2.2 모뎀 아키텍처

추출된 바이너리 분석 결과, 모뎀 프로세서는 리틀 엔디언 모드에서 MIPS16e2 압축 명령어를 사용하는 MIPS32를 실행한다. MIPS16e2는 임베디드 코드 크기 축소를 위한 16비트 인코딩 확장으로, Helio 세대 베이스밴드에 대한 MediaTek의 접근 방식과 일치하며, 이 SoC 제품군에 대한 독립적으로 발표된 베이스밴드 연구로 확인되었다.

운영체제는 Nucleus RTOS로, 태스크 스케줄링, IPC 메시지 큐, 풀 기반 메모리 할당자를 제공한다. 커널/사용자 권한 분리, 태스크 간 메모리 보호 유닛 적용, 하드웨어 스택 가드 메커니즘이 없다.

이 글의 모든 주소는 Ghidra에서 베이스 `0x90000000`으로 로드된 가상 주소이다.

### 2.3 완화 기법 (분석된 빌드에서 관찰됨)

| 완화 기법 | 상태 | 효과 |
|---|---|---|
| ASLR | 없음 | 펌웨어 주소가 정적이며 이미지에서 예측 가능 |
| 스택 카나리 | 없음 | `SAVE`/`RESTORE`가 가드 값 없이 callee-saved 레지스터를 저장 |
| NX / W^X | 없음 | 스택 메모리가 실행 가능 |
| CFI | 없음 | 반환 주소가 어떤 정책에 대해서도 검증되지 않음 |

### 2.4 분석 접근 방식

세 가지 병렬 트랙:

**정적 분석.** Samsung 펌웨어 패키지 → CP 파티션 추출 → `md1img.img` → Ghidra (MIPS LE 32-bit, 베이스 `0x90000000`), NCC Group `mtk_bp` 툴셋을 사용하여 펌웨어 디버그 섹션에서 복구된 MediaTek 엔지니어링 심볼과 함께.

**동적 검증.** Unicorn Engine (MIPS32 에뮬레이션)을 사용하여 두 단계에 걸쳐 특정 펌웨어 루틴을 격리 실행했다. 1단계에서는 네이티브 명령어 쌍을 통해 `si_count`가 채널 컨텍스트로 클램프 없이 복사되는 것을 입증하려 시도했다. 2단계에서는 실제 펌웨어 바이트에서 취약한 루프를 실행하여 펌웨어 자체의 명령어가 저장된 반환 주소를 손상시키는 것을 확인했다. 1단계가 완전히 네이티브로 실행될 수 없었던 경우 — CPHY 디스패치 경로에 필요한 RTOS 서비스 객체 환경이 재구성되지 않았기 때문 — 부작용을 직접 모델링하고 모든 출력에서 그렇게 표시했다.

**무선 측 검증.** ZMQ 루프백을 사용한 srsRAN 4G — 소프트웨어 전용, RF 방출 없음 — 는 테스트 페이로드가 NB-IoT PHY 인코딩과 전송 블록 전달을 견뎌낸다는 것을 확인했다.

---

## 3. 공격 표면: NB-IoT 및 SIB1-NB

### 3.1 사전 연관 공격 표면

NB-IoT (Narrowband Internet of Things)는 3GPP Release 13으로, 기존 라이선스 LTE 스펙트럼을 사용하여 제약된 IoT 기기를 연결하도록 설계되었다. 소비자 스마트폰을 포함한 다양한 최신 셀룰러 SoC에 구현되어 있다.

RRC_IDLE 상태에서, RRC 연결이 설정되기 전에, 서비스를 검색하는 기기는:

1. 셀의 타이밍 신호(NPSS/NSSS)와 동기화
2. NPBCH를 통해 Master Information Block 디코딩 (640 ms 전송 윈도우)
3. NPDSCH에서 SIB1-NB 디코딩 (2560 ms 스케줄)
4. SIB1-NB의 스케줄링 정보를 사용하여 추가 시스템 정보 블록 위치 파악

3단계에서 모뎀은 인증하지 않은 엔티티로부터 온 메시지를, 어떤 연결이나 사용자 상호작용도 있기 전에 처리한다. 정상적인 셀 선택 조건을 충족하는 악성 송신기가 처리될 것이다.```
+------------------+                   +---------------------+
|  Rogue Base Stn  |                   |  Target UE (Modem)  |
+--------+---------+                   +----------+----------+
         |                                        |
         |   NPSS/NSSS sync                      |
         |--------------------------------------->|
         |   MIB-NB (640 ms cycle)                |
         |--------------------------------------->|
         |   SIB1-NB (malformed, si_count > 8)    |
         |--------------------------------------->|   ← vulnerability triggered
         |   [no RRC connection established]      |

3.2 스케줄링 카운트 필드

SIB1-NB는 3GPP TS 36.331에 정의되어 있습니다. 이의 schedulingInfoList 필드는 셀이 브로드캐스트하는 System Information 메시지의 수를 전달하며, 스펙에 의해 최대 8개 항목(1..maxSI-Message-NB-r13 = 8)으로 제한됩니다. 이는 프로토콜 계층의 제약입니다. 메모리 안전성 제약 — 즉, 리스트 길이가 목적지 배열의 용량을 초과하지 않아야 한다는 것 — 은 펌웨어에 의해 별도로 강제되어야 합니다.

그러나 그렇게 되지 않았습니다.


4. 펌웨어 추출 및 심볼 복구

펌웨어는 Samsung CP 패키지에서 획득되었으며, NCC Group의 mtk_bp 툴셋을 사용하여 추출되었습니다:``` md1img.img → md1_extract.py → 000_md1rom (17.8 MB code image) → 017_md1_dbginfo (XZ-compressed CATI debug symbols)

root@kitploit:~
CATI 디버그 섹션은 `mtk_dbg_extract.py symbols`로 압축 해제 및 파싱한 후,
`ImportSymbolsScript.py`를 통해 Ghidra로 임포트했습니다. 그 결과 모뎀 스택 전반에 걸쳐
전체 내부 함수 이름을 확보할 수 있었습니다 — ERRC 계층, L1 채널 관리, IPC 서브시스템,
그리고 NB-IoT BCCH 핸들러 체인 — 이를 통해 의미론 기반 체인 재구성이 가능해졌습니다.

이 글의 모든 함수 이름은 펌웨어 이미지에서 추출한 MediaTek 자체 내장 디버그 심볼에서
가져온 것입니다.

---

## 5. 취약점

### 5.1 취약한 루프

`el1_ch_nbcch_resume_req` (`0x90213940`)는 NB-IoT 브로드캐스트 채널 재개 이벤트를
처리합니다. 해당 MIPS16e2 함수 프롤로그:```asm
90213940:  save  0xE8, ra, s0-s1

SAVE 명령어는 sp를 0xE8만큼 감소시키고 callee-saved 레지스터들을 아래 방향으로 저장합니다:``` old_sp (= new_sp + 0xE8) new_sp + 0xE4 saved ra ← overflow target new_sp + 0xE0 saved s1 new_sp + 0xDC saved s0 new_sp + 0x98 si_sched_arr [34 halfwords = 68 bytes] new_sp + 0x78 si_type_arr [32 bytes] new_sp + 0x00 ← stack pointer after SAVE

root@kitploit:~
Ghidra로 실제 펌웨어 바이너리를 디컴파일한 결과:```c
for (uVar6 = 0; uVar6 < (byte)param_2[0x40a]; uVar6 = uVar6 + 1) {
    si_type_arr[uVar6]  = /* SI type byte */;          // 1 byte/iter, base new_sp+0x78
    si_sched_arr[uVar6] = /* SI schedule halfword */;  // 2 bytes/iter, base new_sp+0x98
}

param_2[0x40a]는 ch_ctx[+0x40A]입니다 — 채널 컨텍스트 BSS 구조체 내의 영구 바이트입니다. 루프 바운드는 배열 용량과의 사전 비교 없이 직접 사용됩니다.

5.2 오버플로 산술

스트림 A(하프워드 sh 쓰기)는 new_sp+0x98에서 시작하여 반복마다 2바이트씩 진행합니다. 반복 38에서 new_sp+0xE4에 있는 저장된 RA에 도달합니다:``` new_sp + 0x98 + i×2 = new_sp + 0xE4 i = (0xE4 - 0x98) / 2 = 0x4C / 2 = 38

root@kitploit:~
Stream B(바이트 `sb` 쓰기)는 `new_sp+0x78`에서 시작하며 RA 슬롯에 도달하려면 반복 108이 필요합니다:```
new_sp + 0x78 + i = new_sp + 0xE4
i = 0xE4 - 0x78 = 108

si_count = 40(오버플로 임계값 38을 초과하도록 선택된 데모 값)을 사용하면 루프는 40회 반복을 실행한다. Stream B는 RA 슬롯에 도달하지 않는다. RA 손상은 전적으로 Stream A에서 발생한다.

40회 반복 후 MIPS16e2 RESTORE 명령어는 손상된 값을 스택에서 $ra로 다시 로드하고, jrc ra가 제어를 전환한다.

5.3 클램프되지 않은 복사 — Layer B

ch_ctx[+0x40A]는 0x90213444의 el1_ch_nbcch_start에 의해 기록된다. 사이에 아무것도 없는 두 개의 연속된 MIPS 명령어:```asm ; el1_ch_nbcch_start @ 0x90213444 lbu v0, 0x99(s0) ; read IPC_msg[+0x99] = si_count from CPHY_CFG_REQ sb v0, 0x40A(s1) ; write to ch_ctx[+0x40A] — no clamp, no mask, no compare

root@kitploit:~
### 5.4 ERRC 빌더 — 레이어 A

CPHY_CFG_REQ 버퍼는 디코딩된 SIB1-NB로부터 ERRC 레이어에서 구축된다. 함수
`errc_chm_l1_set_bcch_si_reception`은 `IPC_msg[+0x99]`를 기록한다:```c
// errc_chm_l1_set_bcch_si_reception — Layer A
// Loop bound: decoded schedulingInfoList entry count from SIB1-NB

while (bVar3 < *(byte *)(param_3 + 0x44) && (param_2 != 0)) {
    bVar3++;
    if (uVar2 != param_2) {
        *param_5 = *param_5 + 1;   // CPHY_CFG_REQ[+0x99]++ — no upper bound check
    }
}

루프 바운드는 SIB1-NB에서 디코딩된 엔트리 개수입니다. 카운터는 디코딩된 엔트리마다 한 번씩 증가하며, 디코딩된 엔트리 수만큼 반복되고 최대 가드는 없습니다.

5.5 IPC 경로

CPHY_CFG_REQ 버퍼가 채워지면, ERRC는 sap_id = 0x501F를 라우팅 키로 사용하여 이를 L1로 디스패치합니다:``` el1_ch_rcv_ilm (sap 0x501F) → el1_chmgm_errc_cfg_req_in_idle ← stores CPHY_CFG_REQ @ L1_ctx[+0x323C] → el1_chmgm_nbcch_handler → el1_ch_nbcch_main → el1_ch_nbcch_cphy_cfg_req_process ← validates cell identity, no si_count check → el1_ch_nbcch_start @ 0x90213444 ← Layer B: lbu + sb, no clamp

root@kitploit:~
### 5.6 클램핑 부재의 3계층

| 계층 | 함수 | 주소 | 클램프 존재? |
|---|---|---|---|
| A — ERRC 빌더 | `errc_chm_l1_set_bcch_si_reception` | ERRC 범위 | **없음** |
| B — L1 복사 | `el1_ch_nbcch_start` | `0x90213444` | **없음** |
| C — L1 루프 | `el1_ch_nbcch_resume_req` | `0x90213940` | **없음** |

어느 한 계층에서든 단 하나의 검사만 있었어도 체인은 끊겼을 것이다.

### 5.7 근본 원인

하나의 위반된 불변식:

> SI 스케줄링 항목의 수는 절대 대상 배열 용량을 초과해서는 안 된다.

3GPP는 의도된 프로토콜 한계(8개 항목)를 제공한다. 펌웨어는 카운트가 인덱스나 루프 한계가 되는
모든 계층에서 메모리 안전성 한계를 강제해야 했다. 취약한 빌드에서는 카운트가 SIB1-NB 브로드캐스트
필드에서 ERRC 디코더를 거쳐, CPHY_CFG_REQ 메시지로, IPC 경계를 넘어 L1 태스크로, 채널 컨텍스트
BSS로, 그리고 스택에 쓰는 루프로 이동했다 — 어느 계층에서도 이를 클램핑하지 않았다.

---

## 6. 호출 체인 — 어떻게 발견되었는가

### 6.1 두 경로의 혼동

구조적으로 유사하지만 서로 다른 두 경로가 0x760바이트 구성 버퍼를
`el1_ch_nbcch_main`에 전달할 수 있다:

| 경로 | 출처 | `[+0x99]` 값 | 관련성 |
|---|---|---|---|
| 경로 A (ERRC → L1 IPC) | ERRC가 디코딩된 SIB1-NB로부터 CPHY_CFG_REQ를 빌드 | OTA로부터의 `schedulingInfoList.count` | **취약한 경로** |
| 경로 B (L1 내부) | `el1_ch_scs_ind_send`가 내부 IPC 본문을 빌드 | 하드코딩된 `1` | 취약하지 않음 |

경로 B는 메시지 형식을 확인해 주었다 — 바이트 `+0x99`는 `el1_ch_nbcch_start`가 소비하는
SI 카운트이다. 이 카운트는 항상 1로 하드코딩되어 있기 때문에 오버플로할 수 없다. 외부에서
영향을 받는 경로는 경로 A이다.

### 6.2 라이터 검색

오프셋 `+0x99`에 쓰는 모든 명령어를 펌웨어에서 패턴 검색한 결과 노이즈가 발생했다:
T1 (직접 `sb`, 약 100건), T2 (분할 베이스, 5건), T3 (계산된 오프셋, 0건), T4
(중첩 `sh`/`sw`, 약 465건). ERRC 채널 관리 함수들은 `msg_send6` 상호 참조 목록에
없었는데, ERRC가 `errc_com_send_msg`를 사용하기 때문이다. 에뮬레이션 프로브를 통해
해당 쓰기가 ERRC 측임을 확인했다: L1 디스패처에서 Unicorn 하네스를 실행하고 버퍼의
`+0x99` 오프셋에 대한 쓰기를 감시했지만 L1 측에서는 아무것도 포착되지 않았다.```
[RUN] dispatcher=0x90214418  watching IPC_msg+0x99

NO writes to IPC_msg+0x99 caught from dispatcher.
Write happened before el1_ch_nbcch_main — confirmed ERRC-side.

6.3 IPC 라우팅 키를 통한 해석

errc_com_send_msg에서 sap_id = 0x501F를 추적한 결과 el1_chmgm_errc_cfg_req_in_idle로의 라우팅이 확인되었으며, 이는 CPHY_CFG_REQ 포인터를 L1_ctx[+0x323C]에 저장하고 다운스트림 디스패치를 시작합니다.

6.4 확인된 호출 체인```

SIB1-NB schedulingInfoList.count (demonstrator: 40) │ ▼ [ERRC task] errc_chm_ch_ctrl_req_hdlr → errc_chm_call_ctrl → errc_chm_l1_main → errc_chm_l1_call_ctrl → errc_chm_l1_snd_cphy_cfg_req ← allocates 0x760-byte CPHY_CFG_REQ → errc_chm_l1_set_cphy_req_nbcch_cfg → errc_chm_l1_set_bcch_inf → errc_chm_l1_set_bcch_si_reception ← Layer A: CPHY_CFG_REQ[+0x99] = 40 → errc_com_send_msg(sap=0x501F) ← IPC to L1 │ ▼ [L1 task] el1_ch_rcv_ilm (sap 0x501F) → el1_chmgm_errc_cfg_req_in_idle ← stores CPHY_CFG_REQ @ L1_ctx[+0x323C] → el1_chmgm_nbcch_handler → el1_ch_nbcch_main → el1_ch_nbcch_cphy_cfg_req_process ← no si_count validation → el1_ch_nbcch_start @ 0x90213444 ← Layer B: lbu + sb, no clamp │ ▼ ch_ctx[0x40A] = 40 (persists in BSS) │ ▼ [state machine advances to 0x0B, event 0x2C] el1_ch_nbcch_resume_req @ 0x90213940 ← Layer C: loop bound from ch_ctx[0x40A] │ ▼ stack overflow → jrc ra → PC = attacker-controlled

root@kitploit:~
---

## 7. 검증

이 특정 SM-A145R 모델은 정상적인 ship-build 동작에서 취약한 NB-IoT 코드 경로를 실행하지 않는 것으로 보이며, 이 때문에 직접적인 하드웨어 재현 대신 하이브리드 에뮬레이션과 정적 분석이 사용되었다.

**정적으로 입증됨.** 펌웨어 바이너리에는 취약한 명령어 쌍이 포함되어 있다. 호출 체인은 심볼, 상호 참조, 디컴파일된 함수 본문으로부터 재구성되었다.

**에뮬레이션에서 네이티브로 실행됨.** `el1_ch_nbcch_resume_req` 내부의 루프는 Unicorn Engine(MIPS32)에서 실제 MediaTek 펌웨어 바이트로 실행되었다. 펌웨어 자체의 `sh` 명령어가 `0x90213B02`에서 저장된 반환 주소 슬롯에 기록했다. `RESTORE` 명령어가 손상된 값을 `$ra`에 로드했고, `jrc ra`가 제어를 전환했다.

**명시적으로 모델링됨.** `el1_ch_nbcch_start` 내부의 `lbu`/`sb` 복사는 완전히 네이티브로 실행될 수 없었는데, `el1_ch_nbcch_cphy_cfg_req_process` 내부의 RTOS 콜백 테이블 디스패치 경로가 플랫 에뮬레이터가 제공하지 않는 라이브 Nucleus 힙 객체를 기대했기 때문이다. Phase 2의 resume 핸들러(`el1_ch_nbcch_resume_req`)는 BSS에 상주하는 채널 컨텍스트에서만 읽고 동일한 디스패치 경로에 도달하지 않으며, 이 때문에 Phase 2는 동일한 스캐폴딩 없이 네이티브로 실행되었다. Phase 1의 부작용 — `IPC_msg[+0x99]`에서 `ch_ctx[+0x40A]`로 기록된 1바이트 — 은 직접 모델링되었고 `[PHASE1-MODEL]`로 표시되었다.

**주장하지 않음.** 완전한 종단 간 over-the-air 하드웨어 재현.

### 7.1 Anti-tamper

하네스는 한 가지 규칙을 강제했다: 어떤 훅도 저장된 반환 주소 슬롯에 증명 마커를 기록하는 것이 허용되지 않았다. 모든 비펌웨어 메모리 쓰기가 추적되었다.```
[PROOF-W] saved_RA write: pc=0x90213b02  addr=new_sp+0xE4  value=0xbeef
[PROOF-W] saved_RA write: pc=0x90213b02  addr=new_sp+0xE6  value=0xdead
[ANTI-TAMPER] no hook wrote RA=0xDEADBEEF — firmware only
anti_tamper_fail = False

0x90213B02는 루프 본문 내부의 sh 명령어입니다. 펌웨어는 이 바이트들을 예측된 스택 오프셋에 배치했습니다. 리틀 엔디언 저장 방식에서 new_sp+0xE4와 new_sp+0xE6에 위치한 하프워드 0xBEEF와 0xDEAD는 결합하여 [ef be ad de] = 0xDEADBEEF가 됩니다.

7.2 에뮬레이션 결과```

[REGS] RA = 0xdeadbeef ← firmware wrote this; decisive proof PC = 0x00000000 ← Unicorn unmapped-fetch artifact, not a hardware exception vector

[STACK] new_sp+0xE4: [ef be ad de] ← little-endian 0xDEADBEEF

[PC-EXPLAIN] PC=0 is an unmapped-fetch artifact; decisive proof is RA=0xDEADBEEF + stack bytes [ef be ad de] + [PROOF-JRC] CONFIRMED: saved RA = 0xDEADBEEF

root@kitploit:~
### 7.3 ZMQ 전달

테스트 페이로드가 NB-IoT PHY 인코딩과 전송 블록 전달을 견디는지 확인하기 위해, ZMQ 루프백(무선 주파수 방출 없음)을 사용한 srsRAN 4G가 사용되었다. 페이로드는 의도적으로 비준수 벡터였다 — `schedulingInfoList`에 대한 ASN.1 SIZE 제약이 40개 항목을 허용하도록 완화되었으며, pycrate 왕복 디코딩으로 바이트 14의 카운트 필드를 확인했다.```
SIB1 received
SIB2 activated
exit 0

This confirms transport-layer delivery. Firmware-side ASN.1 behavior is established by the Layer A static analysis.


8. Control-Flow Mechanics

8.1 Two phases, one task, persistent state

The el1_ch task has one stack. Phase 1 and Phase 2 are two separate IPC events processed sequentially by the same task, with the stack fully unwinding between them. The count persists in channel context BSS, not on the stack:``` EVENT: CPHY_CFG_REQ (Phase 1) el1_ch_nbcch_start lbu v0, 0x99(s0) sb v0, 0x40A(s1) ← ch_ctx[0x40A] = attacker value, written to BSS returns — stack fully unwound

EVENT: resume (state 0x0B, msg 0x2C) (Phase 2) el1_ch_nbcch_resume_req ← frame 0xE8, reads ch_ctx[0x40A] as loop bound loop × 40 → saved RA corrupted jrc ra → attacker-controlled PC

root@kitploit:~
`ch_ctx[0x40A]`는 BSS에 존재하며 모뎀이 리셋되거나 이후 채널 구성이 이를 덮어쓸 때까지 공격자의 값을 유지한다.

### 8.2 스택 프레임```
old_sp  (= new_sp + 0xE8)
new_sp + 0xE4   saved ra
new_sp + 0xE0   saved s1
new_sp + 0xDC   saved s0
new_sp + 0x98   si_sched_arr  [34 halfwords = 68 bytes]
new_sp + 0x78   si_type_arr   [32 bytes]

에뮬레이션 측정으로 프레임 크기가 확인되었다. 배열 위치는 에뮬레이션 결과로 확인되었다 — RA는 반복 38에서 기록되었으며, 이는 si_sched_arr가 new_sp+0x98에서 시작하는 것과 일치한다.


9. 증거

9.1 취약한 루프 이전의 게이트

9.2 si_count를 클램프하지 않는 것으로 확인된 함수


10. 패치 분석

10.1 버전

빌드모뎀 펌웨어빌드 날짜
취약A145RXXU1AWD1, MOLY LR12A...V1.P52023-04-18
패치됨A145RXXUDDZC2, MOLY LR12A...V3.P82025-04-23

빌드 날짜는 두 이미지 모두에서 md1_dbginfo 메타데이터를 추출하여 확인되었다. 패치 ID: MOLY00720348 · 이슈 ID: MSV-2392.

10.2 변경된 사항

el1_ch_nbcch_start (취약한 바이너리에서 0x90213444): lbu/sb 명령어 쌍이 없다. IPC_msg[+0x99]를 ch_ctx[+0x40A]로 직접 복사하는 부분이 사라졌다.

el1_ch_nbcch_resume_req (취약한 바이너리에서 0x90213940): ch_ctx[0x40A]에 대한 스택 기록 루프가 없다. 신뢰할 수 없는 바이트가 고정 크기 스택 배열에 대한 루프 바운드가 되는 구조는 더 이상 존재하지 않는다. 함수 본문은 다른 디스패치 구조로 대체되었다.

errc_chm_l1_set_bcch_si_reception: 무제한 카운터 루프가 검증 지향 헬퍼 호출로 대체되었다.

10.3 새로운 검증 함수

패치된 바이너리에 존재하지만 취약한 바이너리에는 없는 두 함수:``` el1_ch_nbcch_param_check el1_ch_scell_param_check

root@kitploit:~
기존 함수 내 동일한 주소에 몇 개의 명령어가 추가된 단일 행 경계 검사로 나타날 것이다. 패치된 바이너리가 보여주는 것은 아키텍처적 개정이다: NBCCH 스케줄링 경로가 재설계되어 `si_count`를 루프 경계로 사용하는 패턴이 해당 경로 어디에도 더 이상 존재하지 않는다.

### 10.4 CVE 귀속

여기서 설명하는 취약점은 MediaTek이 2025년 1월 6일에 발표한 CVE-2024-20154와 일치한다. 확인 근거:

- 영향을 받는 펌웨어 제품군(LR12A)이 MediaTek의 공지와 일치한다.
- 취약점 분류 — 스택 오버플로, 경계 검사 누락, 악성 기지국으로부터의 RCE, 사용자 상호작용 없음 — 가 CVE 설명 및 NVD 기록과 일치한다.
- 패치된 펌웨어는 취약한 것으로 식별된 코드 구조를 정확히 제거한다.
- 패치 ID MOLY00720348이 MediaTek 공지 및 Android Security Bulletin(A-376809176)에서 확인되었다.
- AI 지원 분석이 수동 확인 이전에 재구성된 패턴을 CVE-2024-20154와 독립적으로 일치시켰다.

---

## 11. 윤리 및 책임 있는 공개

### 11.1 이 글에 포함되지 않은 것

무기화된 익스플로잇 없음. 펌웨어 바이너리 내용 없음. 변조된 페이로드 바이트 없음. 실제 기기를 대상으로 취약점을 트리거하는 단계별 절차 없음. 동작하는 공격을 재현하는 데 필요한 정보 — 특정 모뎀 디코더를 위한 완전한 페이로드 구성, 전체 Phase 1 에뮬레이션을 위한 RTOS 힙 객체 스캐폴딩, 무선 주파수 구성 — 는 의도적으로 빠져 있다.

### 11.2 ZMQ 루프백과 에뮬레이션이 윤리적 접근인 이유

ZMQ 루프백은 어떤 신호도 무선으로 전송되지 않았음을 의미한다. 실제 기기가 대상이 되지 않았다. 통신사 네트워크가 관여하지 않았다. 증명은 분석가가 소유한 하드웨어의 완전히 격리된 소프트웨어 환경에서 실행된다. 이는 사전 연관 무선 취약점을 검증하는 올바른 접근 방식이다 — 변조된 브로드캐스트를 전송하면 범위 내 모든 기기에 영향을 미칠 수 있다.

Unicorn 에뮬레이션은 취약한 동작이 펌웨어 바이너리 자체에 존재하며, 특정 기기 상태나 무선 환경과 무관하게 재현 가능함을 보여준다. 이는 단일 하드웨어 충돌보다 기술적으로 더 강력한 주장이며, 무선으로 무엇도 배포하지 않는다.

### 11.3 MediaTek 지적 재산

모든 분석은 소비자 기기 및 삼성의 공개 펌웨어 패키지에서 합법적으로 획득한 펌웨어를 대상으로 수행되었다. 독점 문서는 사용되지 않았다. MediaTek 내부 구조체 정의와 IPC 메시지 형식은 메모리 안전성 실패를 설명하는 데 필요한 범위까지만 기술된다. 이들은 사양으로 공개되지 않는다.

---
도구 다운로드
반복sh가 쓰는 위치효과
0–33si_sched_arr[0..33]범위 내
34–35new_sp+0xDC — 저장된 s0s0 손상
36–37new_sp+0xE0 — 저장된 s1s1 손상
38new_sp+0xE4 — 저장된 ra [15:0]RA 하위 하프워드
39new_sp+0xE6 — 저장된 ra [31:16]RA 상위 하프워드
게이트조건확인 위치
NB-IoT 활성 경로cfg_type == 1el1_ch_nbcch_cphy_cfg_req_process
채널 미완료done_flag == 0ch_ctx[0x438]
상태 머신 0x0BNBCCH가 resume 상태el1_ch_nbcch_main 디스패치
si_count 0이 아님ch_ctx[0x40A] > 0루프 조건
밴드 클래스 ≤ 2유효한 NB-IoT 모드el1_ch_nbcch_resume_req 진입
el1_chmgm_cell_info_get 0이 아님서비스 테이블에 셀 존재el1_ch_nbcch_start에서 호출됨
함수주소역할클램프?
errc_chm_l1_set_bcch_si_receptionERRC 범위CPHY_CFG_REQ[+0x99] 기록없음
el1_ch_nbcch_cphy_cfg_req_process0x90213F80CPHY 설정을 L1으로 라우팅해당 없음 — si_count를 읽지 않음
el1_chmgm_cell_info_get0x9020E0D0EARFCN/PCI 검증해당 없음
el1_ch_nbcch_start0x90213444카운트를 BSS로 복사없음
el1_ch_nbcch_resume_req0x90213940카운트를 루프 바운드로 사용없음