Статья CVE-2026-41070: обход аутентификации OpenVPN - как OIDC-плагин пропускает клиентов без SSO

Криминалистический стол сверху: Raspberry Pi, ноутбук с подсвеченной строкой кода, монитор с глитч-текстом об обходе OIDC. Синяя нитриловая перчатка касается щупом GPIO-разъёма.


На аудите VPN-периметра финтеха в начале 2026-го наткнулся на OpenVPN-сервер с плагином openvpn-auth-oauth2 в экспериментальном режиме plugin. Подключился штатным openvpn CLI из Kali - без токена, без SSO-потока, без каких-либо credentials - и получил маршруты ко всей внутренней сети. Три минуты от первого SYN до полноценного VPN-туннеля. Root cause - одна строчка в Go-коде, которая возвращала FUNC_SUCCESS при отказе в аутентификации. CVSS 10.0, CWE-287 (Improper Authentication), advisory GHSA-246w-jgmq-88fg. Ниже - полный разбор: что ломается в коде, как воспроизвести на стенде и по каким маркерам выловить в логах.

Бизнес-логика атаки: зачем атакующему обход аутентификации VPN​

1784486641721.webp

CVE-2026-41070 по MITRE ATT&CK - Exploit Public-Facing Application (T1190, Initial Access) через External Remote Services (T1133). Атакующий получает unauthenticated initial access напрямую во внутреннюю сеть, минуя весь OIDC-стек, IdP, MFA и любую логику, которую компания настраивала для защиты VPN-периметра.

Что это даёт на практике:
  • Полный сетевой доступ к ресурсам за VPN без единого credential. IdP (Keycloak, Azure AD, Okta) даже не фиксирует попытку аутентификации - для него ничего не произошло
  • Точка входа для lateral movement. После получения IP из VPN-пула атакующий видит внутренние подсети и может переходить к SMB-перечислению, LLMNR/NBNS poisoning, атакам на Active Directory
  • Низкий шанс обнаружения. VPN-сервер продолжает работать штатно, availability не затрагивается (A:N в CVSS-векторе) - incident response может запоздать на часы или дни
Вектор CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N: удалённая сетевая эксплуатация, низкая сложность, не нужны ни привилегии (PR:N), ни действия пользователя (UI:N). Scope Changed (S:C) - компрометируется не сам плагин, а вся инфраструктура за VPN. Confidentiality и Integrity - High: атакующий и читает, и модифицирует данные в скомпрометированной сети.

Механика уязвимости openvpn-auth-oauth2: что ломается в коде​

Два режима работы плагина​

openvpn-auth-oauth2 - Go-приложение для интеграции OIDC SSO в OpenVPN. Работает в двух режимах:

Management interface mode (по умолчанию, рекомендуемый). Плагин подключается к OpenVPN через management socket и передаёт решения об аутентификации через протокол управления. Этот режим не затронут CVE-2026-41070 - решения идут напрямую через management protocol, минуя механизм return codes.

Experimental plugin mode. Плагин загружается как shared library через директиву plugin в server.conf. OpenVPN вызывает функции плагина напрямую и принимает решение об аутентификации на основе return code. Тут-то всё и ломается.

Критический нюанс: когда плагин возвращает OPENVPN_PLUGIN_FUNC_SUCCESS (status=0), OpenVPN немедленно считает аутентификацию пройденной. Файл auth_control_file проверяется только при возврате FUNC_DEFERRED - отложенной аутентификации. Это задокументированное поведение OpenVPN, и именно на нём строится вся уязвимость.

Root cause: return code mismatch​

Согласно advisory GHSA-246w-jgmq-88fg и данным NVD, баг находился в файле lib/openvpn-auth-oauth2/openvpn/handle.go - в ветке ClientAuthDeny функции handleAuthUserPassVerify. Когда клиент не поддерживает WebAuth/SSO (не отправляет IV_SSO=webauth), плагин корректно определял, что аутентификация невозможна, записывал "0" (deny) в auth_control_file - но возвращал FUNC_SUCCESS:
Код:
case management.ClientAuthDeny:
    if err := openVPNClient.WriteToAuthFile("0"); err != nil {
        return c.OpenVPNPluginFuncError
    }
    return c.OpenVPNPluginFuncSuccess // BUG: OpenVPN трактует как "auth passed"
OpenVPN получал status=0 и немедленно допускал клиента. Содержимое auth_control_file с записью "0" (deny) игнорировалось - для синхронного FUNC_SUCCESS оно не проверяется. Плагин считал, что отказ зафиксирован. OpenVPN считал, что плагин одобрил подключение. Два компонента смотрят на один и тот же return code - и видят разное.

Патч в коммите 36f69a6 заменил возврат на OpenVPNPluginFuncError. Одно слово в return statement - разница между CVSS 0.0 и CVSS 10.0.

Место в цепочке атаки: от recon до internal network​

Fingerprinting уязвимого OpenVPN-сервера​

Эксплуатация начинается с определения, работает ли целевой OpenVPN-сервер с openvpn-auth-oauth2 в experimental plugin mode. И тут есть фундаментальная проблема: определить режим работы плагина извне, до подключения, практически невозможно.

Стандартный nmap -sU -p 1194 <target> покажет, что на порту OpenVPN, но не раскроет ни тип плагина, ни его режим. Плагин аутентификации - серверная сторона, клиенту он не анонсируется в handshake.

Косвенные признаки, доступные на этапе OSINT:
  • Упоминания OIDC/SSO в документации для сотрудников или на внутреннем портале компании (если он доступен)
  • Наличие OAuth2/OIDC endpoint'ов (Keycloak, Azure AD) в DNS-записях рядом с VPN-инфраструктурой
  • Job postings с упоминанием openvpn-auth-oauth2 или OIDC VPN integration
Единственный надёжный способ подтвердить уязвимость - попытаться подключиться клиентом без SSO-поддержки. В этом и специфика: проверка уязвимости и её эксплуатация совпадают. На пентесте это допустимо, если scope включает VPN-эндпоинт. На bug bounty - зависит от программы.

Эксплуатация: подключение без OIDC SSO​

Требования к окружению:
  • ОС: GNU/Linux (Kali, Ubuntu, Debian) с установленным пакетом openvpn (CLI-версия, без GUI)
  • RAM: минимальные требования ОС (512 МБ достаточно)
  • Сеть: прямая видимость до целевого OpenVPN-сервера (порт 1194/UDP или TCP)
  • Файл: валидный .ovpn-конфиг для целевого сервера (публичный endpoint, CA-сертификат)
Стандартный openvpn CLI на Linux не поддерживает IV_SSO=webauth - ограничение CLI-клиента, который рассчитан на username/password аутентификацию. При подключении к уязвимому серверу в plugin mode:
Bash:
sudo openvpn --config target.ovpn --auth-user-pass /dev/null
--auth-user-pass /dev/null передаёт пустые credentials. Плагин обрабатывает запрос, обнаруживает отсутствие SSO-capabilities у клиента, выполняет ветку ClientAuthDeny - но возвращает FUNC_SUCCESS. OpenVPN выдаёт IP из пула, пушит маршруты. Всё. Ты внутри.

После успешного подключения атакующий оказывается во внутренней сети. Дальше - стандартный kill chain: внутренний recon (nmap по полученным маршрутам), перечисление сервисов, поиск AD-контроллеров, LLMNR/NBNS poisoning для перехвата хэшей - всё, что входит в типовой internal pentest после получения foothold.

Предусловия и ограничения CVE-2026-41070​

1784486664409.webp

Уязвимость эксплуатируема только при одновременном выполнении всех условий:

УсловиеДетали
Версия плагинаopenvpn-auth-oauth2 >= 1.26.3, < 1.27.3 (Go-пакет jkroepke/openvpn-auth-oauth2)
Режим работыExperimental plugin mode - директива plugin в конфигурации OpenVPN
Тип клиентаКлиент без поддержки IV_SSO=webauth (стандартный openvpn CLI на Linux)

Когда техника НЕ работает:
  • Management interface mode (по умолчанию). Большинство production-деплоев используют этот режим - он рекомендован авторами плагина. Аутентификация управляется через management protocol, return codes плагина не задействованы. Attack surface радикально сужается.
  • Клиенты с WebAuth/SSO. OpenVPN Connect 3+ и аналогичные GUI-клиенты отправляют IV_SSO=webauth и проходят полноценный OIDC-поток. Уязвимая ветка кода для них не вызывается.
  • Версии за пределами диапазона. До 1.26.3 баг не существовал, с 1.27.3 - исправлен.
CISA SSVC (ADP enrichment) на момент анализа: Exploitation = none, Automatable = no, Technical Impact = total. EPSS = 0.0044, percentile 0.3550 - ниже медианы. Массовой эксплуатации в дикой среде не зафиксировано, что логично: experimental plugin mode используется ограниченным числом деплоев.

Но technical impact - total. Если предусловия выполнены, атакующий получает полный доступ без каких-либо credentials. На пентесте это категория "one-click internal network" - при удачном стечении обстоятельств самый быстрый путь от периметра до domain admin.

Обнаружение обхода аутентификации OpenVPN​

Серверные логи и корреляция с IdP​

Основной detection signal: рассинхронизация между логами OpenVPN и логами IdP. Клиент подключился к VPN и получил IP-адрес, но в Keycloak / Azure AD / Okta нет записи о выдаче OIDC-токена для этого пользователя или сессии.

Маркеры в логах OpenVPN, на которые стоит настроить мониторинг:
  • Подключения от клиентов, не передавших IV_SSO=webauth в capabilities
  • Успешные подключения, которым не предшествовал HTTP redirect на OIDC authorize endpoint
  • Client info с user-agent, указывающим на CLI-клиент openvpn вместо корпоративного OpenVPN Connect
Если openvpn-auth-oauth2 ведёт отдельный лог (slog output) - в нём будут записи о ClientAuthDeny для клиентов без SSO. Сопоставляем: плагин зафиксировал deny, но клиент всё равно получил IP из пула - однозначный индикатор эксплуатации.

Правила корреляции для SIEM​

Для Elastic SIEM или Splunk логика детекции сводится к отрицательной корреляции: VPN connection event минус IdP token issuance event = алерт. Конкретная реализация зависит от стека, но принцип один: если VPN-клиент получил сетевой доступ без прохождения OIDC flow - это аномалия.

Дополнительный слой: мониторинг появления новых IP в VPN-пуле, за которыми следует нетипичный трафик - SMB-сканирование, LDAP-запросы к контроллерам домена, DNS-запросы к внутренним зонам. Это уже пост-эксплуатационные индикаторы, но в связке с отсутствием IdP-записи они формируют высокоприоритетный алерт.

Чеклист: патч и защитные меры​

Уязвимость исправлена в openvpn-auth-oauth2 v1.27.3 (коммит 36f69a6). Чеклист для сетевого администратора:
📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме

CVSS 10.0 на бумаге - число внушительное, но реальный attack surface определяется тем, сколько организаций используют experimental plugin mode в production. По данным CISA SSVC: automatable = no, exploitation in the wild = none на момент анализа. Это не значит, что можно не патчить. Это значит, что у вас есть окно для спокойного, но оперативного обновления - не для паники.

Парадокс этой CVE в том, что она бьёт именно тех, кто пытался сделать безопаснее - интегрировал OIDC SSO для VPN, настроил MFA через IdP, выстроил zero-trust flow. А потом выбрал experimental plugin mode вместо штатного management interface - может, из-за специфики деплоя, может, из-за ограничений инфраструктуры. Одна директива plugin в конфигурации, один некорректный return code в плагине - и вся цепочка SSO превращается в декорацию. Management interface mode при этом работает корректно, рекомендован авторами и не требует ни дополнительных ресурсов, ни сложной настройки. Вывод простой: experimental - это не маркетинговое слово, а предупреждение. Если видите его в документации продукта, через который проходит аутентификация на периметре - трижды подумайте, прежде чем тащить в production. Если хочешь системно разобраться в аудите VPN-периметра и протокольных багов подобного класса - на курсе "Сетевая безопасность" от Codeby это одна из ключевых тем с лабораторным стендом, где такие сценарии прогоняются от fingerprinting до пост-эксплуатации.
 
Последнее редактирование модератором:
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

Похожие темы

🚀 Первый раз на Codeby?
Гайд для новичков: что делать в первые 15 минут, ключевые разделы, правила
Начать здесь →
🧭 Навигатор · ИБ 2026
Не знаешь, какой трек твой?
5 направлений ИБ, реальные зарплаты и точка входа для каждого — в одном треде.
JuniorSenior+
100K → 600K+ ₽ /мес
Открыть навигатор →
🔴 Свежие CVE, 0-day и инциденты
То, о чём ChatGPT ещё не знает — обсуждаем в реальном времени
Threat Intel →
💼 Вакансии и заказы в ИБ
Pentest, SOC, DevSecOps, bug bounty — работа и проекты от проверенных компаний
Карьера в ИБ →

HackerLab