Уже второй раз на конференции Positive Hack Days проходил конкурс IDS Bypass. Как и в прошлый раз (прошлый разбор), участникам предстояло не просто найти слабости в шести сервисах и утащить флаги, но и обойти IDS, которая будет им мешать. Помочь в обходе правил IDS должны были сообщения об их срабатываниях, алерты. И как известно по прошлому конкурсу, количество решений для заданий совершенно неограниченно. Поехали!
192.168.30.10 — Apache Tomcat
На порте 8080 нас встречает Apache Tomcat версии 9.0.17. Первый же поиск эксплойта под эту версию должен привести к CVE-2019-0232.
Это задание задумывалось как ознакомительное и было самым простым (как оказалось, не самым). В эксплойте видим тестовый URL с командой /cgi/test.bat?&dir
. Но при таком запросе он просто виснет, а участник видит алерт IDS:
ATTACK [PTsecurity] Apache Tomcat RCE on Windows (CVE-2019-0232)
Задумывалось так, что участник будет видоизменять URL тем же образом, каким бы он делал это для обхода WAF. Регулярное выражение в правиле выглядит так: pcre: "/\.(?:bat|cmd)\?\&/U";
, и вскоре бы IDS уступила участнику. Кроме того, в некоторых эксплойтах уже есть пример URL с байпасом, например такой: http://localhost:8080/cgi/test.bat%20%20?&dir
. В итоге многие с легкостью выполнили задание. С конкурсом ознакомились, переходим дальше.
192.168.30.20 — PHP Bypass
Главная страница встречает нас предложением протестировать команду ls. Предупреждает, что может не сработать.
Как и ожидалось — не работает. В логе сообщение:
ATTACK [PTsecurity] file_name parameter possible command injection
Можно подумать, что в задании необходимо проэксплуатировать RCE и забрать флаг, но идея заключалась в другом. Летом 2019 года автор под псевдонимом @Menin_TheMiddle опубликовал статью о байпасе IDS и WAF. В ней шла речь о том, что ряд символов в имени GET-параметра PHP интерпретатор приводит к знаку подчеркивания («_»). Наша IDS, в отличие от PHP, этого не делает. Для примера автор использовал одно из публичных правил IDS нашей команды AttackDetection. А поскольку решающая часть правила Suricata выглядела так: pcre: "/file_name\s*=\s*[a-zA-Z\.]*[^a-zA-Z\.]/U";
, то и обойти его можно было просто заменив параметр file_name на, скажем, file[name. Из-за ошибки в правиле Suricata получить флаг можно было просто послав file_name=.
192.168.30.30 — He said yes
Нас встречает форма и инструкция, как получить тот самый флаг. Достаточно просто ответить «yes» на HTTP-запрос.
Участники запускали на своих узлах веб-сервер, отвечали «yes» на входящие запросы и видели следующую строчку в логах:
JOKE [PTsecurity] Sometimes Positive Technologies hurts! No 'yes' allowed
Правило проверяло все HTTP-ответы и не пропускало те, внутри которых была строка «yes». Игровой узел также принимал только строку «yes» в нижнем регистре, и это задание набрало наибольшее число разных решений!
Задумка заключалась в том, чтобы перенаправить входящий HTTP-запрос на протокол HTTPS и уже в новом запросе ответить «yes». Для этого вектора в библиотеке requests были специально использованы параметры allow_redirects=True
и verify=False
. Решение выглядело так:echo -ne "HTTP/1.1 302 Redirect\r\nLocation: https://10.8.0.2/hi_there\r\nContent-Length: 0\r\n\r\n" | sudo nc -nkvlp 80
echo -ne "HTTP/1.1 200 OK\r\nContent-Length: 3\r\nContent-Type: text/html\r\n\r\nyes" | sudo ncat -nvklp 443 –ssl
Участник @vos использовал стократное вложенное сжатие gzip для HTTP-ответа, а участник @webr0ck использовал заполнение нулями размером почти в 2 мегабайта перед строкой «yes». И в том и в другом случае Suricata оказалась бессильной.
192.168.30.40 — DCERPC
Самый дорогой таск на этом конкурсе. Участникам давались данные УЗ администратора и предлагалось проявить свои знания Windows-протоколов. Для получения флага необходимо было вытащить список всех пользователей на устройстве.
Те, кто знаком с безопасностью AD, могут сразу подумать о скрипте samrdump.py из набора impacket, но скрипт «стучится» на закрытый на узле порт 445 SMB. Строка биндинга в скрипте ncacn_np:*hostname*[\pipe\samr]
фиксирована и ведет на SMB-пайп. Кроме того, из открытых портов на узле обнаруживается только 135, его слушает т. н. Endpoint Mapper. EPM отвечает за резолв RPC-интерфейсов.
Другой скрипт из набора impacket, rpcdump.py, как раз и использует EPM, чтобы узнать список текущих активных RPC-интерфейсов.
> python rpcdump.py Administrator:TastesG00d@192.168.30.40
Protocol: [MS-SAMR]: Security Account Manager (SAM) Remote Protocol
Provider: samsrv.dll
UUID : 12345778-1234-ABCD-EF00-0123456789AC v1.0
Bindings:
ncacn_ip_tcp:192.168.30.40[49668]
ncacn_np:\\TASK4[\pipe\lsass]
Среди всех интерфейсов видим необходимый нам SAMR, который отвечает среди прочего и за управление пользователями. С помощью интерфейса SAM скрипт samrdump.py вытаскивает список пользователей. То есть кроме SMB-пайпа мы можем напрямую подключиться к порту 49668 и запросить список пользователей по протоколу DCERPC. Для этого необходимо пропатчить скрипт samrdump.py, чтобы он обращался вместо пайпа напрямую на интерфейс SAMR. По иному пути пошли участники @vos и @Abr1k0s. Они использовали готовый инструмент walksam из набора rpctools, единственная сложность которого заключается в использовании флага RPC_C_AUTHN_LEVEL_PKT_PRIVACY вместо RPC_C_AUTHN_LEVEL_CALL. Альтернативными способами были использование интерфейсов atsvc, svcctl, dcom и других. Все они позволяют произвольное исполнение кода и закрыты правилами IDS. Интерфейс shutdown также был закрыт. Самый необычный способ решения этого задания подразумевал удаленный поиск событий создания пользователя с помощью wevtutil:
192.168.30.50 — RDP me
Задача проста: подключиться по RDP с известной учетной записью и прочитать флаг с рабочего стола. Сложность в том, что большинство из известных RDP-клиентов заблокированы правилами IDS. Участники то и дело получали одно из следующих сообщений:
TOOLS [PTsecurity] xfreerdp/vinagre/remmina RDP client
TOOLS [PTsecurity] xfreerdp/remmina RDP client
TOOLS [PTsecurity] MSTSC Win10 RDP client
TOOLS [PTsecurity] MSTSC Win7 RDP client
TOOLS [PTsecurity] Rdesktop RDP client
Иногда разные RDP-клиенты ведут себя одинаково: например, многие Linux-клиенты построены вокруг одной и той же библиотеки. Таск имеет несколько решений.
Первое решение, которое было предусмотрено изначально, — это решение в лоб. Участник перебирает разные опции запуска или пробует разные клиенты и видит разные алерты IDS. После анализа своего трафика он узнает, какой именно пакет блокирует IDS. На основе этого можно сделать вывод о том, как работают правила. Правила срабатывают на определенные последовательности каналов (channelDef) в поле ClientNetworkData и на сам порядок заголовков.
Перебирая опции запуска клиента xfreerdp последних версий, можно наткнуться на опцию echo: xfreerdp /v:192.168.30.50 /u:user /p:letmein +echo
И xfreerdp именно с такими параметрами проскочит мимо правил IDS.
Другой способ продемонстрировали @vos, @Abr1k0s и @astalavista — они подключились к серверу с использованием мобильного клиента Mocha RDP Lite. Принципиально другое решение с использованием netsed нашел участник @webr0ck. Netsed, как и обычный sed, способен подменять сетевые данные на лету. Участник просто занулил все имена каналов в ClientData пакете RDP.
192.168.30.60 — LDAP
В описании дан IP-адрес с открытым портом 389. Данных УЗ в задании нет, но сервис LDAP поддерживает анонимные подключения (bind). Однако при простом коннекте при помощи Python-библиотеки ldap3 в три строчки видим алерт IDS.
server = ldap3.Server('192.168.30.60', port=389)
connection = ldap3.Connection(server)
connection.bind()
TEST [PTsecurity] LDAP ASN1 single byte length fields prohibited
Засняли дамп своего трафика и видим, что сам bind происходит успешно, но searchRequest, который после этого послала библиотека, остается без ответа. На него и срабатывает правило IDS.
Windows-утилиты ADSIEdit и ldp, а также линуксовый ldapsearch дают похожие результаты, но с другими алертами:
TEST [PTsecurity] LDAP ASN1 1-byte length encoded found
TEST [PTsecurity] LDAP ASN1 2-byte length encoded found
TEST [PTsecurity] LDAP ASN1 4-byte length encoded found
Все дело оказывается в том, как кодируются длины отдельных полей в сообщениях LDAP. Байт x в последовательности байтов 30 8x yy yy yy
отвечает за длину поля длины в байтах. Например, последовательность 30 82 00 02
кодирует два байта поля длины 00 02
. Таким образом, участнику требовалось попробовать поля другой длины и обнаружить, что IDS не срабатывает на поле длиной 3 байта. Флаг лежит в ответе среди полей namingContexts. Таск предполагал единственное решение, и лишь двое справились с заданием.
Результаты:
1-е место: @vos — Apple Watch Series 6 + рюкзак
2-е место: @psih1337 — денежное вознаграждение + рюкзак
3-е место: @Abr1k0s — рюкзак