
Технический разбор CVE-2024-20154 — переполнения буфера в стеке в прошивке baseband NB-IoT MediaTek MT6769, включая реверс-инжиниринг и цепочку эксплуатации.
Классификация: CWE-121 — переполнение буфера в стеке
Критичность: Критическая (бюллетень MediaTek) · 8.8 Высокая, вектор атаки: смежный (CISA-ADP)
Тип: удалённое выполнение кода — без взаимодействия с пользователем, без предварительной ассоциации
Раскрытие: бюллетень безопасности MediaTek, 6 января 2025 г. https://corp.mediatek.com/product-security-bulletin/January-2025
Проанализированная цель: Samsung Galaxy A14 SM-A145R — семейство MT6769 (Helio G80), входящее в список затронутых чипсетов MediaTek — прошивка эмулировалась в безопасных условиях.
Статус: исправлено.
Это было моё первое опубликованное исследование базовой станции. Я пришёл из области, далёкой от телекоммуникационной инфраструктуры, промежуточных слоёв законного перехвата, анализа stingray и IMSI-ловушек, а также безопасности встраиваемых устройств — я ранее не занимался глубоким реверс-инжинирингом прошивок сотовых модемов. Я хотел доказать себе, что структурированная аналитическая методология адаптируется к разным целям, а знакомство с конкретной платформой можно заменить строгим прослеживанием цепочек. NB-IoT выделялся тем, что находится на действительно опасном пересечении: протокол разработан для ограниченных IoT-устройств, поверхность атаки — до ассоциации, и стек модема обрабатывает его независимо от того, что делает пользователь аппарата.
Когда прошивка с исправлением была проанализирована и уязвимый шаблон подтверждён как отсутствующий, система ИИ, использовавшаяся для массового анализа прошивки перед нацеливанием на конкретные функции,
независимо сопоставила реконструированный класс ошибки, условия и затронутое семейство прошивок с описанием CVE-2024-20154.
Технические выводы принадлежат аналитику.
В телефоне в вашем кармане находится как минимум два отдельных компьютера. Тот, с которым вы взаимодействуете, работает под управлением Android. Другой — базовая станция — работает полностью независимо, обрабатывает всю радиосвязь и почти полностью невидим для операционной системы над ним. Android может быть полностью пропатчен. Браузер может быть изолирован. Пользователь может никогда не нажать на вредоносную ссылку. Ничто из этого не имеет значения, если уязвимый код находится в прошивке модема, которая обрабатывает радиосигналы до того, как в дело вступает процессор приложений.
CVE-2024-20154 — именно такая уязвимость.
Некорректное широковещательное сообщение системной информации NB-IoT заставляет прошивку модема MediaTek принять контролируемое атакующим значение количества планирований, провести это значение через путь конфигурации от RRC к L1 без какого-либо ограничения и в конечном итоге использовать его в качестве границы цикла для цикла записи в стек внутри обработчика широковещательного канала NB-IoT. Когда количество превышает ёмкость массивов назначения, цикл записывает за их пределы, достигает сохранённых регистров в стеке и перезаписывает сохранённый адрес возврата. Затем функция восстанавливает повреждённое значение в регистр адреса возврата и выполняет переход по нему.
Что делает серьёзность именно такой:
Уязвимость была опубликована в бюллетене безопасности MediaTek от 6 января 2025 г. с рейтингом критичности «Критическая», затрагивая среди прочих семейство модемов LR12A. Samsung включил исправление в свой выпуск обслуживания безопасности за февраль 2025 г.
В этом посте не публикуется вооружённый эксплойт, и он не воспроизводим по тому, что здесь опубликовано. Цель — показать, где разрывается цепочка, почему каждый слой не смог её остановить, и что требуется для ответственной проверки ошибки базовой станции, когда вы не можете подключить отладчик к живому модему.
Основная цель: Samsung Galaxy A14 (SM-A145R). Радиоподсистема управляется процессором базовой станции MediaTek из семейства чипсетов MT6769 (Helio G80). Семейство MT6769 явно указано в списке затронутых чипсетов MediaTek для CVE-2024-20154.``` AP/CP firmware: A145RXXU1AWD1 Modem software: MOLY LR12A.R3.TC10.6M.A14.PR.SP.V1.P5 Build date: 2023-04-18
Прошивка baseband — это не код Android. Это отдельная встраиваемая система на радиоподсистеме SoC с собственным CPU, собственной RTOS и собственным адресным пространством, вне песочницы процессов Android.
### 2.2 Архитектура модема
Анализ извлечённого бинарного файла показывает, что процессор модема работает на MIPS32 со сжатыми инструкциями MIPS16e2 в режиме little-endian. MIPS16e2 — это 16-битное расширение кодирования для уменьшения размера встраиваемого кода, что согласуется с подходом MediaTek для baseband поколения Helio и подтверждается независимыми опубликованными исследованиями baseband для этого семейства SoC.
Операционная система — Nucleus RTOS, обеспечивающая планирование задач, очереди IPC-сообщений и пуловый аллокатор памяти. Здесь нет разделения привилегий ядра и пользователя, нет принудительного применения блока защиты памяти между задачами и нет аппаратного механизма защиты стека.
Все адреса в этом посте — виртуальные адреса в том виде, в каком они загружены в Ghidra с базой `0x90000000`.
### 2.3 Механизмы защиты (наблюдаемые в проанализированной сборке)
| Механизм защиты | Статус | Эффект |
|---|---|---|
| ASLR | Отсутствует | Адреса прошивки статичны и предсказуемы по образу |
| Stack canary | Отсутствует | `SAVE`/`RESTORE` сохраняет callee-saved регистры без защитного значения |
| NX / W^X | Отсутствует | Память стека исполняема |
| CFI | Отсутствует | Адреса возврата не проверяются ни по какой политике |
### 2.4 Подход к анализу
Три параллельных направления:
**Статический анализ.** Пакет прошивки Samsung → извлечение раздела CP → `md1img.img` → Ghidra (MIPS LE 32-bit, база `0x90000000`) с инженерными символами MediaTek, восстановленными из отладочной секции прошивки с помощью набора инструментов `mtk_bp` от NCC Group.
**Динамическая валидация.** Unicorn Engine (эмуляция MIPS32) использовался для изолированного выполнения конкретных процедур прошивки в двух фазах. Фаза 1 пыталась доказать неограниченное копирование `si_count` в контекст канала через нативную пару инструкций. Фаза 2 выполнила уязвимый цикл на реальных байтах прошивки и подтвердила, что собственные инструкции прошивки повреждают сохранённый адрес возврата. Там, где Фаза 1 не могла выполняться полностью нативно — поскольку окружение сервисных объектов RTOS, требуемое путём диспетчеризации CPHY, не было реконструировано — побочный эффект моделировался напрямую и помечался как таковой во всех выходных данных.
**Валидация на стороне радио.** srsRAN 4G с ZMQ loopback — только программное обеспечение, без радиоизлучения — подтвердил, что тестовая полезная нагрузка выживает при кодировании NB-IoT PHY и доставке транспортного блока.
---
## 3. Поверхность атаки: NB-IoT и SIB1-NB
### 3.1 Поверхность атаки до ассоциации
NB-IoT (Narrowband Internet of Things) — это 3GPP Release 13, разработанный для подключения ограниченных IoT-устройств с использованием существующего лицензированного спектра LTE. Он реализован в широком спектре современных сотовых SoC, включая те, что используются в потребительских смартфонах.
Находясь в RRC_IDLE, до установления любого RRC-соединения, устройство, ищущее обслуживание, будет:
1. Синхронизироваться с сигналами тайминга соты (NPSS/NSSS)
2. Декодировать Master Information Block по NPBCH (окно передачи 640 мс)
3. Декодировать SIB1-NB из NPDSCH (расписание 2560 мс)
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] |
SIB1-NB определён в 3GPP TS 36.331. Его поле schedulingInfoList содержит информацию о том, сколько сообщений System Information транслирует сота, и по спецификации ограничено максимумом в 8 записей (1..maxSI-Message-NB-r13 = 8). Это ограничение уровня протокола. Ограничение безопасности памяти — длина списка не должна превышать ёмкость массивов назначения — должно обеспечиваться прошивкой отдельно.
Это не было сделано.
Прошивка была получена из пакета Samsung CP и извлечена с помощью набора инструментов mtk_bp от NCC Group:```
md1img.img → md1_extract.py → 000_md1rom (17.8 MB code image)
→ 017_md1_dbginfo (XZ-compressed CATI debug symbols)
Секция отладки CATI была распакована и разобрана с помощью `mtk_dbg_extract.py symbols`, затем
импортирована в Ghidra через `ImportSymbolsScript.py`. Результатом стали полные внутренние имена
функций по всему стеку модема — слой 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 и сохраняет регистры, сохраняемые вызываемым, в порядке убывания:```
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
Из декомпиляции 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-структуре контекста канала.
Граница цикла используется напрямую, без предварительного сравнения с ёмкостями массивов.
Поток A (записи полуслов sh) начинается с new_sp+0x98 и продвигается на 2 байта за итерацию.
Он достигает сохранённого RA по адресу new_sp+0xE4 на итерации 38:```
new_sp + 0x98 + i×2 = new_sp + 0xE4
i = (0xE4 - 0x98) / 2 = 0x4C / 2 = 38
Поток B (запись байта `sb`) начинается с `new_sp+0x78` и для достижения слота RA потребовал бы итерации 108:```
new_sp + 0x78 + i = new_sp + 0xE4
i = 0xE4 - 0x78 = 108
При si_count = 40 (демонстрационное значение, выбранное так, чтобы превысить порог переполнения 38)
цикл выполняется 40 итераций. Поток B никогда не достигает слота RA. Повреждение RA
полностью происходит из-за потока A.
После 40 итераций инструкция MIPS16e2 RESTORE перезагружает повреждённое значение из
стека в $ra, и jrc ra передаёт управление.
ch_ctx[+0x40A] записывается функцией el1_ch_nbcch_start по адресу 0x90213444. Две последовательные инструкции 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
### 5.4 Конструктор ERRC — Уровень A
Буфер CPHY_CFG_REQ формируется на уровне ERRC из декодированного SIB1-NB. Функция
`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. Счётчик увеличивается один раз на каждую декодированную запись, для стольких записей, сколько было декодировано, без ограничения максимума.
Как только буфер CPHY_CFG_REQ заполнен, ERRC отправляет его в L1 с sap_id = 0x501F
в качестве ключа маршрутизации:```
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
### 5.6 Трёхуровневое отсутствие ограничения
| Уровень | Функция | Адрес | Ограничение присутствует? |
|---|---|---|---|
| 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 строит CPHY_CFG_REQ из декодированного SIB1-NB | `schedulingInfoList.count` из OTA | **Уязвимый путь** |
| Путь B (внутренний L1) | `el1_ch_scs_ind_send` строит внутреннее тело IPC | Жёстко заданное `1` | Не уязвим |
Путь B подтвердил формат сообщения — байт `+0x99` представляет собой счётчик SI,
потребляемый `el1_ch_nbcch_start`. Поскольку его счётчик всегда жёстко задан как 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:
запуск стенда Unicorn из диспетчера L1 и отслеживание записей по смещению `+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.
Отслеживание sap_id = 0x501F в errc_com_send_msg выявило маршрутизацию к
el1_chmgm_errc_cfg_req_in_idle, которая сохраняет указатель CPHY_CFG_REQ по
адресу L1_ctx[+0x323C] и запускает последующую диспетчеризацию.
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
---
## 7. Проверка
Данная конкретная модель SM-A145R, по-видимому, не задействует уязвимый путь кода NB-IoT
при обычной работе в составе судовой сборки, поэтому вместо прямого воспроизведения на
аппаратном обеспечении использовались гибридная эмуляция и статический анализ.
**Доказано статически.** Бинарный файл прошивки содержит уязвимую пару инструкций. Цепочка
вызовов восстановлена по символам, перекрёстным ссылкам и декомпилированным телам функций.
**Выполнено нативно в эмуляции.** Цикл внутри `el1_ch_nbcch_resume_req` выполнялся на реальных
байтах прошивки MediaTek в Unicorn Engine (MIPS32). Собственная инструкция `sh` прошивки по
адресу `0x90213B02` записала в сохранённый слот адреса возврата. Инструкция `RESTORE` загрузила
повреждённое значение в `$ra`, а `jrc ra` передала управление.
**Смоделировано явно.** Копирование `lbu`/`sb` в `el1_ch_nbcch_start` не удалось выполнить полностью
нативно, поскольку путь диспетчеризации через таблицу обратных вызовов RTOS внутри `el1_ch_nbcch_cphy_cfg_req_
process` ожидал живые объекты кучи Nucleus, которые плоский эмулятор не предоставлял. Обработчик
возобновления (`el1_ch_nbcch_resume_req`) в фазе 2 читает только из контекста канала, размещённого в BSS,
и не задействует тот же путь диспетчеризации, поэтому фаза 2 выполнялась нативно без
необходимости в той же оснастке. Побочный эффект фазы 1 — один байт из `IPC_msg[+0x99]`,
записанный в `ch_ctx[+0x40A]` — был смоделирован напрямую и помечен `[PHASE1-MODEL]`.
**Не утверждается.** Полное сквозное воспроизведение на аппаратном обеспечении по радиоэфиру.
### 7.1 Защита от вмешательства
Оснастка обеспечивала соблюдение одного правила: ни одному хуку не разрешалось записывать маркер
доказательства в сохранённый слот адреса возврата. Каждая запись в память, не относящаяся к прошивке,
отслеживалась.```
[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 внутри тела цикла. Прошивка разместила эти байты по
предсказанному смещению стека. В little-endian представлении полуслова 0xBEEF и 0xDEAD
по адресам new_sp+0xE4 и new_sp+0xE6 объединяются в [ef be ad de] = 0xDEADBEEF.
[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
### 7.3 Доставка через ZMQ
Чтобы убедиться, что тестовая полезная нагрузка выдерживает кодирование NB-IoT PHY и доставку транспортных блоков, использовался srsRAN 4G с петлевым соединением ZMQ (без радиочастотного излучения). Полезная нагрузка представляла собой намеренно несоответствующий вектор — ограничение ASN.1 SIZE на `schedulingInfoList` было ослаблено, чтобы допустить 40 записей, при этом декодирование с круговым обходом pycrate подтвердило поле счётчика в байте 14.```
SIB1 received
SIB2 activated
exit 0
Это подтверждает доставку на транспортном уровне. Поведение ASN.1 на стороне прошивки устанавливается статическим анализом Layer A.
Задача el1_ch имеет один стек. Фаза 1 и Фаза 2 — это два отдельных события IPC, обрабатываемых
последовательно одной и той же задачей, при этом стек полностью разворачивается между ними. Счётчик
сохраняется в BSS контекста канала, а не в стеке:```
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
`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.
| Сборка | Прошивка модема | Дата сборки |
|---|---|---|
| Уязвимая | A145RXXU1AWD1, MOLY LR12A...V1.P5 | 2023-04-18 |
| Исправленная | A145RXXUDDZC2, MOLY LR12A...V3.P8 |
Даты сборки подтверждены извлечением метаданных md1_dbginfo из обоих образов. ID патча:
MOLY00720348 · ID проблемы: MSV-2392.
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: Неограниченный цикл по счётчику заменён
на вспомогательные вызовы, ориентированные на валидацию.
Две функции, присутствующие в исправленном бинарнике, отсутствуют в уязвимом бинарнике:``` el1_ch_nbcch_param_check el1_ch_scell_param_check
Проверка границ в одну строку выглядела бы как несколько добавленных инструкций внутри
существующей функции по тому же адресу. То, что показывает пропатченный бинарник, — это
архитектурная переработка: путь планирования NBCCH был переработан так, что шаблон
`si_count`-как-границы-цикла больше нигде в этом пути не существует.
### 10.4 Атрибуция CVE
Описанная здесь уязвимость соответствует CVE-2024-20154, опубликованной MediaTek 6 января
2025 года. Основание для подтверждения:
- Затронутое семейство прошивок (LR12A) совпадает с бюллетенем MediaTek.
- Класс уязвимости — переполнение стека, отсутствие проверки границ, RCE с поддельной базовой
станции, без взаимодействия с пользователем — совпадает с описанием CVE и записью NVD.
- Пропатченная прошивка удаляет именно те структуры кода, которые были идентифицированы как
уязвимые.
- Идентификатор патча MOLY00720348 подтверждён по бюллетеню MediaTek и Android Security Bulletin
(A-376809176).
- Анализ с помощью ИИ независимо сопоставил реконструированный шаблон с CVE-2024-20154
до ручного подтверждения.
---
## 11. Этика и ответственное раскрытие
### 11.1 Чего нет в этом посте
Никакого боевого эксплойта. Никакого содержимого бинарника прошивки. Никаких байтов
некорректной полезной нагрузки. Никакой пошаговой процедуры запуска уязвимости против живого
устройства. Информация, необходимая для воспроизведения работающей атаки — полное построение
полезной нагрузки для конкретного декодера модема, создание каркаса RTOS-объектов кучи для
полной эмуляции Фазы 1, конфигурация радиоинтерфейса для передачи по воздуху — намеренно
отсутствует.
### 11.2 Почему ZMQ loopback и эмуляция — этичный подход
ZMQ loopback означает, что сигнал никогда не передавался по воздуху. Ни одно реальное
устройство не было целью. Ни одна сеть оператора не была задействована. Доказательство
работает полностью в изолированной программной среде на оборудовании, принадлежащем
аналитику. Это правильный подход для валидации радиоуязвимости на этапе до ассоциации —
передача некорректного широковещательного сообщения затронула бы любое устройство в радиусе
действия.
Эмуляция Unicorn демонстрирует, что уязвимое поведение находится в самом бинарнике прошивки,
воспроизводимо, независимо от конкретного состояния устройства или радиоокружения. Это
технически более сильное утверждение, чем единичный аппаратный сбой, и оно позволяет избежать
развёртывания чего-либо по воздуху.
### 11.3 Интеллектуальная собственность MediaTek
Весь анализ выполнялся на прошивке, законно полученной с потребительского устройства и из
публично выпущенных Samsung пакетов прошивок. Никакая проприетарная документация не
использовалась. Внутренние определения структур MediaTek и форматы IPC-сообщений описываются
лишь в объёме, необходимом для объяснения нарушения безопасности памяти. Они не публикуются
как спецификации.
---
| Итерация | sh записывает в | Эффект |
|---|
| 0–33 | si_sched_arr[0..33] | В пределах границ |
| 34–35 | new_sp+0xDC — сохранённый s0 | s0 повреждён |
| 36–37 | new_sp+0xE0 — сохранённый s1 | s1 повреждён |
| 38 | new_sp+0xE4 — сохранённый ra [15:0] | Младшее полуслово RA |
| 39 | new_sp+0xE6 — сохранённый ra [31:16] | Старшее полуслово RA |
| Проверка | Условие | Где проверяется |
|---|
| Активный путь NB-IoT | cfg_type == 1 | el1_ch_nbcch_cphy_cfg_req_process |
| Канал не завершён | done_flag == 0 | ch_ctx[0x438] |
Конечный автомат 0x0B | NBCCH в состоянии возобновления | диспетчеризация el1_ch_nbcch_main |
| si_count не равен нулю | ch_ctx[0x40A] > 0 | Условие цикла |
| Класс диапазона ≤ 2 | Допустимый режим NB-IoT | Вход в el1_ch_nbcch_resume_req |
el1_chmgm_cell_info_get не равен нулю | Сота в таблице обслуживания | Вызывается в el1_ch_nbcch_start |
| Функция | Адрес | Роль | Ограничение? |
|---|
errc_chm_l1_set_bcch_si_reception | Диапазон ERRC | Записывает CPHY_CFG_REQ[+0x99] | Нет |
el1_ch_nbcch_cphy_cfg_req_process | 0x90213F80 | Маршрутизирует конфигурацию CPHY в L1 | Н/П — не читает si_count |
el1_chmgm_cell_info_get | 0x9020E0D0 | Проверяет EARFCN/PCI | Н/П |
el1_ch_nbcch_start | 0x90213444 | Копирует счётчик в BSS | Нет |
el1_ch_nbcch_resume_req | 0x90213940 | Использует счётчик как границу цикла | Нет |
| 2025-04-23 |