
Оригинальное исследование и неразрушающий PoC для переполнения буфера стека в декодируемом Base64 пароле до аутентификации в login.cgi устройства Netis NC63
login.cgi, приводящее к RCEИсследователь: Özcan Ersan (@ozcanpng)
CVE-2026-76070NC63_V3.0.0.3327/bin/netis.cgiPOST /cgi-bin/login.cgipassword в Base64-кодировкеПубличный обработчик входа в прошивке Netis NC63 V3.0.0.3327 получает
контролируемый атакующим параметр password и декодирует его с помощью
пользовательской Base64-процедуры FUN_00402bd4. Вызывающий код предоставляет
64-байтовый локальный буфер на стеке, но не передаёт его ёмкость декодеру.
Декодер исходит из закодированных входных данных и записывает декодированные
байты, не проверяя границы приёмника.
Сохранённый адрес возврата MIPS находится в 136 байтах от начала декодированного буфера. Динамические тесты против продакшн-CGI с исходными хэшами подтвердили:
B вызывает сбой по адресу 0x42424242;ra на 0x0041a2e0 приводит ко второму наблюдаемому
входу в обработчик входа, что доказывает контроль над программным счётчиком;system()
в исходном бинарном файле с выбранным атакующим значением MIPS a0. Подменённый
/bin/sh зафиксировал /bin/sh -c NC63_RCE_PROOF и не выполнил ни одной команды.Публичный PoC в этом репозитории намеренно останавливается на краш-шаблоне. Он не содержит return-цепочки, шеллкода, команд, reverse shell или персистентности.
193f6a5e2ce65972b1805bf076f8d3521379a8441c8aaeb5ad0ba174bbee0792 netis_NC63_V3.0.0.3327.bin
23faa747b7d2f067aa5431bcc227ceca97a7977cf3e7c372f715cbba57f9209b squashfs-root/bin/boa
eb298774c27070dc595fefcabb4e8c12a46cb5f4fd08f91c3ca92282c3a289a2 squashfs-root/bin/netis.cgi
Динамически протестированная копия /bin/netis.cgi имеет тот же SHA-256, что и
исполняемый файл, извлечённый производителем.

Фронтенд производителя отправляет пароль на публичную конечную точку в Base64:
obj.password = base64encode(utf16to8(password));
request({
url: "/cgi-bin/login.cgi",
data: obj
});
HTML-поле использует maxlength="63", но это лишь ограничение на стороне
браузера. Прямой HTTP-клиент может отправить большее закодированное значение.

login.cgi должен быть доступен до аутентификации. Небезопасное декодирование
происходит до сравнения декодированного пароля с настроенным паролем
администратора. Действительный сеанс, заголовок Cookie, заголовок Authorization
или правильный пароль не требуются.
Unauthenticated HTTP client
|
| POST /cgi-bin/login.cgi
| password=<attacker-controlled Base64>
v
/bin/netis.cgi: FUN_0041a2e0
|
| get_request_param("password")
v
FUN_00402bd4(decoded_stack_buffer, encoded_password)
|
| no destination-capacity argument
| decoded output exceeds 64 bytes
v
saved s8 at decoded offset 132
saved ra at decoded offset 136
|
v
attacker-selected MIPS PC
Псевдокод, полученный через Ghidra, с нормализованными для читаемости именами:
int login_cgi(void *request)
{
char decoded[64];
char stored[68];
char *password;
memset(decoded, 0, 64);
memset(stored, 0, 64);
password = get_request_param(request, "password");
if (password != NULL)
FUN_00402bd4(decoded, password); /* no capacity argument */
apmib_get(0x15e, stored);
if (strcmp(decoded, stored) == 0)
printf("[\"SUCCESS\"]");
else {
system("echo 0 >/tmp/boa_auth");
printf("[\"%d\"]", 0x15);
}
return 0;
}

Декодер FUN_00402bd4 получает только указатели на приёмник и источник. Его
цикл продвигает указатель приёмника и сохраняет до трёх декодированных байтов
на каждые четыре Base64-символа. Никакое сравнение не проверяет приёмник
относительно decoded + 64.

Base64 — это входное преобразование, а не первопричина дефекта. Корневая причина — несоответствие между контролируемой атакующим декодированной длиной и фиксированным размером приёмника, чья ёмкость никогда не проверяется. Для обычного дополненного ввода четыре закодированных символа представляют до трёх декодированных байтов; серверные проверки поэтому должны вычислять и проверять декодированный размер до записи.
FUN_0041a2e0 начинается с 0x0041a2e0 и создаёт кадр размером 0xa8:
0041a2e0 addiu sp,sp,-168
0041a2e4 sw ra,164(sp)
0041a2e8 sw s8,160(sp)
0041a2ec move s8,sp
Декодированный приёмник начинается с s8+0x1c; сохранённый s8 и сохранённый
ra находятся по адресам s8+0xa0 и s8+0xa4:
decoded[64] s8+0x1c decoded offset 0
saved s8 s8+0xa0 decoded offset 132
saved ra s8+0xa4 decoded offset 136
Точное расстояние до адреса возврата: 0xa4 - 0x1c = 0x88, то есть 136 байт.

140-байтовый декодированный шаблон B заменил четырёхбайтовый сохранённый адрес
возврата:
--- SIGSEGV {si_signo=SIGSEGV, si_code=1, si_addr=0x42424242} ---
qemu: uncaught target signal 11 (Segmentation fault)

Отдельный 140-байтовый ввод установил сохранённый ra в 0x0041a2e0.
Трассировка CPU QEMU зафиксировала обычный первый вход в обработчик, за которым
последовал второй вход с s8=0x41414141 и ra=0x0041a2e0.

Исходный бинарный файл содержит прямой jal system по адресу 0x0041a3cc. В
приватной изолированной проверке существующие инструкции с фиксированной базой
загрузили маркер в a0 и достигли этого вызова. Статическая программа наблюдения
была смонтирована поверх /bin/sh; она зафиксировала аргументы интерпретатора
команд и ничего не выполнила:
argv[0]=</bin/sh>
argv[1]=<-c>
argv[2]=<NC63_RCE_PROOF>
CONTROLLED_MARKER_REACHED
PASS: attacker-controlled a0 reached system() and /bin/sh argv.
PASS: the guard logged the request and executed no command.
Это демонстрирует RCE-примитив в изолированном продакшн-пути кода. Это не подтверждает идентичную надёжность эксплуатации на физическом маршрутизаторе в условиях его развёрнутого ядра и конфигурации рандомизации стека.
Исходная конфигурация Boa указывает User root, Group root и путь CGI,
содержащий /bin и /web/cgi-bin. Продакшн-исполняемый файл имеет
фиксированную базу (0x00400000), не имеет стек-канарейки или RELRO и объявляет
исполняемый GNU-стек с RWX-сегментами.


Включённый скрипт по умолчанию использует режим пробного запуска и только
генерирует Base64-тело формы, содержащее 140 байт B после декодирования:
python3 poc/poc.py
Отправка требует явно указанной авторизованной цели и флага --send:
python3 poc/poc.py --target http://192.168.1.1 --send
Отправка шаблона может вызвать сбой CGI-процесса. Используйте её только в авторизованной одноразовой среде. PoC не реализует приватную цепочку проверки RCE.
Успешная эксплуатация может выполнить выбранный атакующим код или команды в контексте управления маршрутизатором. В исходной конфигурации Boa этот контекст работает от root. Возможные последствия включают раскрытие конфигурации и секретов, манипулирование DNS/межсетевым экраном/маршрутизацией, перенаправление трафика, нарушение работы сервисов и полный компрометацию устройства.
FUN_00402bd4.См. evidence/README.md для скриншотов и сопоставления
трасс. Нормализованные выдержки из Ghidra находятся в
attachments/decompiled-functions/.
CVE-2026-76070 и разрешил публичное
раскрытие.Ни один физический маршрутизатор не был перепрошит. Не использовались реальные команды оболочки, reverse shell, персистентность, внешние подключения, кража учётных данных или деструктивные операции с прошивкой.