
Автоматизированный инструмент проверки уязвимостей для 6 CVE dnsmasq (CVE-2026-2291, 4890, 4891, 4892, 4893, 5172)
Автоматизированный инструмент «чёрного ящика» для проверки 6 уязвимостей dnsmasq (май 2026 г.). Отправляет атакующие пакеты на работающее устройство (DUT) и сообщает PASS/FAIL — доступ к исходному коду не требуется.
# Установите DNS DUT на WAN-IP вашего ноутбука через GUI, затем:
sudo python3 dnsmasq_cve_verify.py --laptop <ВАШ_WAN_IP> --dut <LAN_IP_DUT> --dut-pass <SSH_ПАРОЛЬ>
# Пример:
sudo python3 dnsmasq_cve_verify.py --laptop 10.0.0.211 --dut 192.168.1.1 --dut-pass '12345Asdf@'
| CVE | CVSS | Тип | Вектор атаки | Затрагиваемая функция |
|---|---|---|---|---|
| CVE-2026-2291 | 9.2 | Переполнение буфера кучи | Удалённый | extract_name() — всегда активна |
| CVE-2026-5172 | 7.5 | Чтение за границами (OOB) / сбой | Удалённый | extract_addresses() — всегда активна |
| CVE-2026-4890 | 7.5 | DoS через бесконечный цикл | Удалённый | Разбор битовой карты NSEC (--dnssec) |
| CVE-2026-4891 | 5.3 | Чтение за границами кучи (OOB) | Удалённый | Проверка RRSIG (--dnssec) |
| CVE-2026-4892 | 8.4 | Переполнение кучи → root | Локальный/Соседний | CLID DHCPv6 (--dhcp-script + DHCPv6) |
| CVE-2026-4893 | 5.3 | Обход проверки | Удалённый | Проверка источника ECS (--add-subnet) |
Корневая причина: union bigname объявляет char name[MAXDNAME], но управляющие символы могут расширить имя до 2*MAXDNAME+1 байт, вызывая переполнение кучи.
Метод тестирования: Отправляет DNS-запросы с доменными именами, содержащими символы с высоким битом (0x80+), которые внутренне преобразуются в \DDD (4 байта на байт ввода). Если dnsmasq аварийно завершается или перестаёт отвечать, он уязвим.
Исправленное поведение: Отвергает имена с превышением размера корректно (FORMERR/REFUSED) или использует увеличенный буфер.
Корневая причина: Поддельное поле rdlen позволяет extract_name() продвинуть указатель за конец записи. Отрицательное значение оставшихся байтов даёт огромное значение → массовое чтение OOB → сбой.
Метод тестирования: Отправляет DNS-ответы с записями CNAME, где rdlen меньше, чем фактическая закодированное имя. Если dnsmasq аварийно завершается — уязвим.
Исправленное поведение: Проверяет, что указатель остаётся в пределах объявленной границы rdlen после extract_name().
Корневая причина: Разбор битовой карты NSEC продвигает указатель на p[1] вместо p[1]+2 (пропущен размер заголовка окна). При bitmap_length=0 указатель никогда не двигается → бесконечный цикл.
Метод тестирования: Отправляет сфабрикованную запись NSEC с window=0, bitmap_length=0. Если dnsmasq перестаёт отвечать на ВСЕ запросы (зависает, не вылетает), он уязвим. Эксплуатируется ДО проверки RRSIG.
Исправленное поведение: Продвигает указатель на p[1]+2 и пропускает битовые карты нулевой длины.
Корневая причина: rdlen в RRSIG не проверяется на минимальный размер (18 + имя подписывающего). Вычисленная длина подписи становится отрицательной → трактуется как огромная → чтение OOB.
Метод тестирования: Отправляет записи RRSIG с rdlen=10 (значительно ниже минимума в 31+ байт). Сбой = уязвим.
Исправленное поведение: Проверяет rdlen >= fixed_fields + signer_name_length перед вычислением длины подписи.
Корневая причина: CLID DHCPv6 (до 65535 байт) кодируется в hex через sprintf("%.2x") в daemon->packet (5131 байт). CLID размером 3000 байт → hex-строка 6000 байт → переполнение. Вспомогательный процесс работает от root.
Метод тестирования: Отправляет DHCPv6 SOLICIT с Client Identifier размером 3000 байт. Требуется IPv6-соседство и настроенный --dhcp-script. Сбой вспомогательного процесса = уязвим.
Исправленное поведение: Усекает или проверяет длину CLID перед hex-кодированием.
Примечание: Некоторые сборки скомпилированы с -DNO_DHCP6 и НЕ подвержены этой CVE.
Корневая причина: process_reply() передаёт длину OPT-записи (~23 байта) вместо полной длины пакета в check_source(). Все проверки границ не срабатывают → функция всегда возвращает 1 (допустимо).
Метод тестирования: Отправляет DNS-запросы с опцией EDNS Client Subnet, содержащей поддельные префиксы источника. Если dnsmasq эхом возвращает ECS без проверки, он уязвим.
Исправленное поведение: Передаёт полную длину пакета в check_source(), что позволяет проводить корректные проверки границ согласно RFC 7871, раздел 9.2.
Обновление до dnsmasq 2.92rel2 (рекомендуется)
dnsmasq_cve_verify.py)Основной инструмент контроля качества. Запускается на тестовом ноутбуке, отправляет атакующие пакеты на DUT и выдаёт чёткий результат PASS/FAIL для каждой CVE. Не требует модификации DUT, кроме SSH-доступа только для чтения для проверки состояния.
┌─────────────────────────────────────────────────────────────────────┐
│ Testing Laptop │
│ │
│ LAN interface WAN interface │
│ <LAPTOP_LAN_IP> <LAPTOP_WAN_IP> │
│ │ │ │
│ │ ┌────┴──────────────┐ │
│ │ │ Malicious DNS │ │
│ │ │ Server (port 53) │ │
│ │ └────┬──────────────┘ │
│ │ │ │
└────────┼───────────────────────────────┼────────────────────────────┘
│ LAN subnet │ WAN subnet
│ │
┌────────┼───────────────────────────────┼────────────────────────────┐
│ │ │ │
│ LAN: <DUT_LAN_IP> WAN: <DUT_WAN_IP> │
│ (LAN gateway) (WAN uplink) │
│ │
│ DUT (Linksys Router) │
│ dnsmasq (any version < 2.92rel2) │
│ │
│ resolv-file=/etc/resolv.conf │
│ → nameserver <LAPTOP_WAN_IP> ← set via GUI, forwards to us │
│ │
└─────────────────────────────────────────────────────────────────────┘
Data flow:
1. Tool sends DNS query to DUT LAN IP (port 53)
2. DUT's dnsmasq can't resolve locally → forwards upstream to LAPTOP_WAN_IP
3. Our malicious server on WAN interface replies with exploit payload
4. DUT's dnsmasq processes the malicious response → crash/hang/survive
5. Tool checks DUT state via SSH (read-only)
Пример настройки (ваши IP будут отличаться):
| Роль | IP (пример) |
|---|---|
| LAN ноутбука | 192.168.1.254 |
| WAN ноутбука | 10.0.0.211 |
| LAN DUT | 192.168.1.1 |
| WAN DUT | 10.0.0.214 |
Ключевое требование: WAN-IP ноутбука и WAN-IP DUT должны быть в одной подсети, чтобы DUT мог обращаться к ноутбуку как к вышестоящему DNS-серверу.
┌──────────┐ ┌───────────┐ ┌──────────────────┐ ┌──────────┐
│ SETUP │ ──► │ TRIGGER │ ──► │ STATE INSPECT │ ──► │ VERDICT │
│ │ │ │ │ │ │ │
│ Start │ │ Send DNS │ │ SSH to DUT: │ │ PASS: │
│ malicious│ │ query to │ │ - pidof dnsmasq │ │ survived │
│ DNS srv │ │ DUT→DUT │ │ - PID changed? │ │ │
│ on WAN │ │ forwards │ │ - dmesg crash? │ │ FAIL: │
│ interface│ │ to us→we │ │ - /var/log/msg │ │ crashed/ │
│ (10.0.0. │ │ reply w/ │ │ │ │ hung │
│ 211:53) │ │ exploit │ │ Liveness query │ │ │
│ │ │ payload │ │ (version.bind) │ │ │
└──────────┘ └───────────┘ └──────────────────┘ └──────────┘
ПРИМЕЧАНИЕ: Инструмент НЕ изменяет настройки DUT. Пользователь должен установить DNS на 10.0.0.211 через GUI.
paramiko (pip install paramiko)Подключите тестовый ноутбук к DUT двумя кабелями:
| Порт ноутбука | Подключение к | Назначение |
|---|---|---|
| Порт LAN | Порт LAN DUT | SSH-доступ + отправка DNS-запросов на DUT |
| Порт WAN | Подсеть WAN DUT (например, порт вышестоящего коммутатора/модема) | Выступать в роли вышестоящего DNS-сервера |
После подключения запишите IP-адреса вашего ноутбука:
# Найдите свои IP
ip addr show | grep "inet "
# Пример вывода:
# inet 192.168.1.254/24 ... ← это ваш LAN IP
# inet 10.0.0.211/24 ... ← это ваш WAN IP (используйте его для --laptop)
http://192.168.1.1 или http://myrouter.local10.0.0.211)cd /путь/к/dnsmasq-cve-2026/
# Запуск всех 6 тестов CVE:
sudo python3 dnsmasq_cve_verify.py --laptop <ВАШ_WAN_IP> --dut <LAN_IP_DUT> --dut-pass <SSH_ПАРОЛЬ>
# Пример:
sudo python3 dnsmasq_cve_verify.py --laptop 10.0.0.211 --dut 192.168.1.1 --dut-pass '12345Asdf@'
Инструмент:
Все протестированные маршрутизаторы Linksys практически не эксплуатируемы для любой из 6 CVE в их производственных конфигурациях сборки. Опасные функции (DNSSEC, DHCPv6 через dnsmasq) либо не скомпилированы, либо не настроены. Патчи всё же рекомендуются как защита от будущих угроз.
До исправления (dnsmasq 2.78, сборка без DNSSEC):
До исправления (dnsmasq 2.90 с включённым DNSSEC):
После исправления (dnsmasq 2.92rel2 или применённые патчи): Все 6 CVE → PASS
paramiko (pip install paramiko)test_dnsmasq_cve_remote.py)Облегчённая проверка только версии — запрашивает version.bind, чтобы определить, ниже ли версия dnsmasq исправленной. Без SSH, настройки и эксплойт-полезных нагрузок.
python3 test_dnsmasq_cve_remote.py 192.168.1.1
test_dnsmasq_cve_on_device.sh)Запускается напрямую на DUT через SSH/последовательный порт. Проверяет версию бинарного файла и параметры компиляции.
scp test_dnsmasq_cve_on_device.sh [email protected]:/tmp/
ssh [email protected] "sh /tmp/test_dnsmasq_cve_on_device.sh"
malicious_dns_server.py)Автономный сервер эксплойтов для ручного тестирования. Запустите его, укажите DUT вышестоящий DNS на этот хост, затем отправляйте запросы к crash-5172.evil.test, crash-2291.evil.test и т.д.
sudo python3 malicious_dns_server.py --port 53
# Затем на DUT: настройте вышестоящий DNS → этот хост
# Затем отправьте: dig @192.168.1.1 crash-5172.evil.test
После применения патчей и прошивки новой прошивки запустите повторно:
sudo python3 dnsmasq_cve_verify.py --laptop <ВАШ_WAN_IP> --dut <LAN_IP_DUT> --dut-pass <SSH_ПАРОЛЬ>
# Ожидается: все 6 PASS
| Проблема | Решение |
|---|
| «DUT не перенаправляет запросы на нас» | Проверьте, что Шаг 2 выполнен корректно. Убедитесь, что WAN-IP ноутбука совпадает с введённым в GUI. |
| «Не удаётся подключиться к DUT» | Проверьте учётные данные SSH. Попробуйте вручную: ssh [email protected] |
| «Не удаётся привязать порт 53» | Запустите с sudo. Или используйте --dns-port 5353 (требует ручной настройки DUT). |
| «Версия: неизвестна» | Возможно, dnsmasq не находится в стандартном пути. Инструмент всё равно тестирует корректно. |
| CVE | Результат | Причина |
|---|
| CVE-2026-2291 | PASS | DNSSEC не скомпилирован |
| CVE-2026-4890 | PASS | DNSSEC не скомпилирован |
| CVE-2026-4891 | PASS | DNSSEC не скомпилирован |
| CVE-2026-4892 | PASS | dnsmasq не обслуживает DHCPv6 (используется отдельный DHCPv6-сервер) |
| CVE-2026-4893 | PASS | Только логическая ошибка — без сбоя |
| CVE-2026-5172 | PASS | Пережил эксплойт (код уязвимости отсутствует в версии 2.78) |
| CVE | Результат | Причина |
|---|
| CVE-2026-2291 | PASS | DNSSEC не скомпилирован |
| CVE-2026-4890 | PASS | DNSSEC не скомпилирован |
| CVE-2026-4891 | PASS | DNSSEC не скомпилирован |
| CVE-2026-4892 | PASS | DHCPv6 не скомпилирован |
| CVE-2026-4893 | PASS | Только логическая ошибка — без сбоя |
| CVE-2026-5172 | PASS | Пережил варианты эксплойта |
| CVE | Результат | Причина |
|---|
| CVE-2026-2291 | PASS | DNSSEC не скомпилирован — не эксплуатируется |
| CVE-2026-4890 | PASS | DNSSEC не скомпилирован — не эксплуатируется |
| CVE-2026-4891 | PASS | DNSSEC не скомпилирован — не эксплуатируется |
| CVE-2026-4892 | PASS/FAIL | DHCPv6 скомпилирован + активирован dhcp-script |
| CVE-2026-4893 | PASS | Логическая ошибка — без сбоя (только по версии) |
| CVE-2026-5172 | PASS | Путь blockdata_expand отсутствует в 2.78 |
| CVE | Результат | Причина |
|---|
| CVE-2026-2291 | FAIL | Переполнение кучи через управляющие символы |
| CVE-2026-4890 | FAIL | Бесконечный цикл (зависание) |
| CVE-2026-4891 | FAIL | Сбой при чтении OOB в RRSIG |
| CVE-2026-4892 | PASS/FAIL | Зависит от настройки DHCPv6 + скрипта |
| CVE-2026-4893 | PASS | Логическая ошибка — без сбоя |
| CVE-2026-5172 | FAIL | Чтение OOB через поддельный rdlen |
| Флаг | По умолчанию | Описание |
|---|
--laptop | (обязательно) | WAN-IP ноутбука (привязывает вредоносный DNS-сервер сюда) |
--dut | 192.168.1.1 | LAN-IP DUT (сюда отправляются SSH и DNS-запросы) |
--dut-user | root | Имя пользователя SSH на DUT |
--dut-pass | (запрашивается) | Пароль SSH на DUT |
--dns-port | 53 | Порт для вредоносного DNS-сервера |
--cve | все 6 | Конкретная CVE для тестирования (можно повторять) |
| Инструмент | Python | root | SSH | Сеть |
|---|
dnsmasq_cve_verify.py | 3.6+ paramiko | Да (порт 53) | Да (только чтение) | LAN + WAN до DUT |
test_dnsmasq_cve_remote.py | 3.6+ stdlib | Нет | Нет | UDP 53 до DUT |
test_dnsmasq_cve_on_device.sh | Н/Д (shell) | Нет | Запуск на DUT | Н/Д |
malicious_dns_server.py | 3.6+ stdlib | Да (порт 53) | Нет | DUT перенаправляет на нас |