Каждый новый специалист нашей практики Защиты приложений проходит нечто среднее между посвящением и стажировкой. Обычно в рамках задачи нужно развернуть уязвимое приложение, WAF одного из наших фокусных партнеров, а потом найти конкретную уязвимость, проэксплуатировать ее, посмотреть что видно на WAF в режиме мониторинга, а затем настроить WAF, чтобы он начал обнаруживать и блокировать данную уязвимость. Конечно, уязвимости при этом выбираются не такие, чтобы сразу по сигнатуре можно было бы ее обнаружить.
В этой статье младший системный инженер “К2 Кибербезопасность” Даниил Золотарев поделится задачей, которая выпала ему.
В ходе работы Даниилу пришлось защищать Juice Shop средствами платформы «Вебмониторэкс» и столкнуться с некоторыми аспектами негативной и позитивной моделей данного WAF. Далее мы рассмотрим примеры создания пользовательских правил, для блокировки атак Improper Input Validation (Неправильная проверка ввода). Таким образом наглядно продемонстрируем одну из ключевых возможностей WAF – закрытие дыр приложения в проде до фикса.
![Рисунок 1 - Домашняя страница Juice Shop Рисунок 1 - Домашняя страница Juice Shop](https://habrastorage.org/getpro/habr/upload_files/408/ea7/555/408ea75553a11e5e1c96d25f43f43688.png)
Juice Shop в качестве уязвимого приложения
В качестве уязвимого веб-приложения было выбрано довольно популярная разработка от сообщества OWASP – Juice Shop. Она представляет из себя веб-приложение – интернет-магазин по продаже соков. Но с одной ключевой особенностью - в нем спрятаны известные уязвимости из списка OWASP. Поэтому это идеальный кандидат, чтобы проверить некоторые функции.
Мы разворачивали приложение и WAF на отдельных серверах в нашем Облаке, чтобы сымитировать реальные кейсы внедрения СЗИ. Juice Shop написан на Node.js, Express и Angular. Его можно развернуть в Docker, Vagrant, популярных облачных провайдерах и Node.js c утилитой npm. Так был выбран последний вариант. Все просто – команды с комментариями дальше в листинге:
sudo apt update
# Установка Node.js
sudo apt install nodejs
sudo apt install npm
# Клонируем репозиторий
git clone https://github.com/juice-shop/juice-shop.git --depth 1
cd juice-shop
# Устанавливаем Juice Shop
npm install
# Запускаем Juice Shop
npm start
С другими вариантами установки можно ознакомиться на Гитхабе Juice Shop - https://github.com/juice-shop/juice-shop .
После запуска проверяем в браузере. Приложение работает на 3000 порту. Прописываем в браузере https://localhost:3000 , чтобы проверить работоспособность.
WAF Вебмониторэкс
Данный WAF устанавливается в нескольких вариантах, мы выбрали вариант on-prem lite, то есть фильтрующая нода устанавливается на виртуальный или физический сервер, а управления и сбор событий WAF происходит через API и Личный кабинет, которые располагаются на отдельных серверах с протекцией вендора.
В нашем случае мы будем устанавливать фильтрующую ноду на сервер в нашем Облаке, так что получается серверная архитектура как на рисунке 2 ниже. Сервер с WAF будет работать как обратный прокси-сервер для приложения. Поэтому определяем ему внешний IP-адрес (Elastic IP), а оба сервера будут в одной локальной подсети уязвимого локального веб-приложения. Также мы настроили группы безопасности сетевых интерфейсов серверов, чтобы не было доступа из глобальной сети или сторонних подсетей.
![Рисунок 2 - Серверная архитектура, вид с облака Рисунок 2 - Серверная архитектура, вид с облака](https://habrastorage.org/getpro/habr/upload_files/fc1/036/980/fc1036980ac008fc3f6d6464323db60c.png)
Платформу «Вебмониторэкс» можно развернуть на серверах с ОС Linux (Debian, Ubuntu, CentOS, AmazonLinux, AlmaLinux, Rocky Linux, Oracle Linux, SuSe Linux) на основе веб-сервера NGINX Stable и Plus, а также на платформе API-шлюза Kong. Помимо этого, реализована поддержка отечественных ОС: РЕД ОС, Альт Сервер 10 на основе веб-сервера Angie и Angie PRO.
Доступно развертывание на облачных платформах AWS, Google CP, Яндекс.Облако, Microsoft Azure, Alibaba Cloud и на частных облаках. В качестве инструментов развертывания выступают готовые образы платформ для первых двух вариантов, а также Docker образы NGINX, Kubernetes образы с Ingress-контролером или Sidecar-прокси NGINX и установочные пакеты DEB и RPM.
На нашем стенде мы разворачиваем фильтрующую ноду на сервере с ОС Linux Ubuntu 22.04 LTС в качестве модуля NGINX Stable 1.24. Установка ПО СЗИ приведена в листинге ниже.
# Установка Nginx stable и зависимостей
sudo apt install curl gnupg2 ca-certificates lsb-release
echo "deb http://nginx.org/packages/debian `lsb_release -cs` nginx"
sudo tee /etc/apt/sources.list.d/nginx.list
curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo apt-key add -
sudo apt update
sudo apt install nginx
# Добавление репозитория и установка пакетов платформы «Вебмониторэкс»
curl -fsSL https://repo.webmonitorx.ru/wmx.gpg | sudo apt-key add -
sh -c "echo 'deb https://repo.webmonitorx.ru/ubuntu/webmonitorx-node jammy/4.6/' | sudo tee /etc/apt/sources.list.d/wmx.list"
sudo apt update
sudo apt install --no-install-recommends wallarm-node nginx-module-wallarm
Далее редактируем конфигурацию NGINX следующим образом, добавляя выделенные строки. Логи устанавливаются в конфигурацию автоматически при установке пакетов платформы «Вебмониторэкс». Также мы установили TLS сертификаты Let`s Encrypt с помощью Certbot, подробнее можно почитать здесь - https://certbot.eff.org/ . Файл /etc/nginx/nginx.conf представлен ниже.
user www-data;
worker_processes auto;
worker_rlimit_nofile 2048;
load_module modules/ngx_http_wallarm_module.so;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 1024;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
log_format wallarm_combined '$remote_addr - $remote_user [$time_local] '
'"$request" $request_id $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$wallarm_request_cpu_time $wallarm_request_mono_time
$wallarm_serialized_size $wallarm_is_input_valid
$wallarm_attack_type $wallarm_attack_type_list';
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
include /etc/nginx/conf.d/*.conf;
}
Теперь настраиваем обратный прокси-сервер в /etc/nginx/conf.d/default.conf (представлен в листинге ниже) с апстримом на наше уязвимое веб-приложение. И добавляем заголовки платформы «Вебмониторэкс»:
wallarm_mode monitoring;
wallarm_application 1005;
wallarm_mode_allow_override on;
Первые два заголовка отвечают за режим работы WAF. По умолчанию у нас будет режим мониторинга, но второй заголовок говорит о том, что можно будет поменять на другие в личном кабинете. Третий заголовок отвечает за сущность, которая определяется в настройках в личном кабинете, для сбора и просмотра статистики только для конкретных приложений. Подробнее про заголовки можно прочитать в документации.
upstream vuln_app {
server 172.31.85.6:3000;
}
server{
wallarm_mode_allow_override on;
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
server_name juiceshop.k2.local;
ssl_certificate /etc/letsencrypt/live/juiceshop.k2.local/cert.pem;
ssl_certificate_key /etc/letsencrypt/live/ juiceshop.k2.local /privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
location / {
wallarm_mode monitoring;
wallarm_application 1005;
set_real_ip_from 172.31.85.11;
real_ip_header X-Forwarded-For;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://vuln_app;
}
}
server {
if ($host = juiceshop.k2.local) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80 ;
listen [::]:80 ;
server_name juiceshop.k2.local;
return 404; # managed by Certbot
}
В облачном личном кабинете создается новая нода – DEMO NODE (рисунок 3) и формируется токен для фильтрующего модуля. Командой sudo /usr/share/wallarm-common/register-node -t <NODE_TOKEN> -H api.wallarm.ru токен регистрируется. После чего командами sudo service wallarm-tarantool restart и sudo service nginx restart перезапускаются сервисы NGINX и «Вебмониторэкс». В личном кабинете отображается белый IP-адрес. Для настроек на другие платформы обращайтесь к документации - https://docs.webmonitorx.ru/
![Рисунок 3 - Личный кабинет Рисунок 3 - Личный кабинет](https://habrastorage.org/getpro/habr/upload_files/af5/b54/b67/af5b54b673e740f9165315426f9c5781.png)
После настройки приведу архитектуру нашего развернутого стенда для общего понимания на рисунке ниже.
![Рисунок 4 - Архитектура стенда Рисунок 4 - Архитектура стенда](https://habrastorage.org/getpro/habr/upload_files/cd0/23d/65d/cd023d65d688a4dc89d744ae86a17bb3.jpeg)
Пользовательские правила на основе регулярных выражений и тестирование
В этом разделе создадим пользовательские правила для устранения некоторых уязвимостей в веб-приложении Juice Shop. В WAF от компании Вебмониторэкс расширенная функциональность работы с запросами.
Improper input validation (Неправильная проверка ввода)
Одной из главных возможностей WAF является предотвращение эксплуатации уязвимостей в стадии эксплуатации приложения до релиза с исправлением. Так частой ошибкой во время разработки веб-приложений является неправильная проверка ввода. UI предлагает выбор вариантов ввода, и разработчик не задумывается над тем, что данные ввода надо дополнительно парсить в бэкенде, потому что запрос может быть скомпрометирован. Яркий пример такого поведения – задача Zero Stars.
Zero Stars
Перейдем во вкладку «Отзыв пользователя» и заполним форму, выбрав 3 звезды. Затем перехватим сгенерированный запрос к серверу и изменим рейтинг на 0. Отправим запрос на сервер. Заметим, что запрос обработался сервером и успешно прошел валидацию.
![](https://habrastorage.org/getpro/habr/upload_files/578/241/350/578241350c89aa307eda7a87c3d64451.png)
![](https://habrastorage.org/getpro/habr/upload_files/d98/d69/295/d98d692958f48cfca3b55bf78e798c65.png)
![](https://habrastorage.org/getpro/habr/upload_files/eec/ed7/d31/eeced7d310dcf60bff05a7b3fb244bf5.png)
Решение данной задачи с помощью платформы «Вебмониторэкс» состоит в создании пользовательского правила с обнаружением атаки на основе регулярного выражения. Создадим его в разделе «Rules», выберем «Создать индикатор атаки на основе регулярного выражения», выберем тип атаки – «Виртуальный патч» и заполним регулярное выражение - ^[^1-5]$ . Данной правило будет блокировать все POST запросы на URL - /api/Feedbacks с любыми значениями в теле json по ключу rating, кроме цифр от 1 до 5.
![](https://habrastorage.org/getpro/habr/upload_files/864/d89/630/864d8963077a7a65b5481e2ceb6c4f98.png)
![](https://habrastorage.org/getpro/habr/upload_files/eaf/b00/7a4/eafb007a4a446a5e028346ffc168a299.png)
Проверим. Генерируем такой же запрос с рейтингом 0 и наблюдаем, что в ответе возвращается ошибка 403. Также формируется событие в личном кабинете WAF. Развернув его, можно получить подробности заблокированного запроса.
![](https://habrastorage.org/getpro/habr/upload_files/82f/d51/b20/82fd51b20f338abf55a5dd28ff7691aa.png)
![](https://habrastorage.org/getpro/habr/upload_files/e9a/477/31f/e9a47731ff5ec4c5970ef80d3d9bf7f4.png)
Admin Registration
Данная задача заключается также в эксплуатации неправильной проверки ввода. Добавив лишнее поле в тело json POST запроса при регистрации аккаунта, можно получить права администратора. Перехватим запрос и добавим значение admin с ключом role. Ответ сервера говорит об успешной обработке и регистрации аккаунта с ролью администратора.
![](https://habrastorage.org/getpro/habr/upload_files/81c/b17/92c/81cb1792c82f217e0912c887bf26071d.png)
![](https://habrastorage.org/getpro/habr/upload_files/720/695/e37/720695e3775e857d09dd5b1b8bf949b6.png)
![](https://habrastorage.org/getpro/habr/upload_files/50e/da9/785/50eda97859dd043f16cb82f6379f74c9.png)
Для решение данной задачи достаточно создать виртуальный патч, который блокирует POST запросы пользователей на URL /api/Users, в теле json которых будет ключ role. После создания виртуального патча видим, что запрос заблокирован.
![](https://habrastorage.org/getpro/habr/upload_files/5c7/208/ee8/5c7208ee885c4dca6514cdd6c12701ea.png)
![](https://habrastorage.org/getpro/habr/upload_files/444/648/bda/444648bdac5d5fe218e56a1dd33d6809.png)
![](https://habrastorage.org/getpro/habr/upload_files/d43/f18/ab8/d43f18ab8c482fc1ada3a1bae4f0085b.png)
![](https://habrastorage.org/getpro/habr/upload_files/f4b/fbb/2c3/f4bfbb2c39b1529f2d351a60b0529530.png)
Upload Type
Данная задача указывает на то, что активная проверка на тип загружаемого файла есть только на фронте. Но перехватив запрос, изменив в нем заголовок Content-Type в multipart и добавив файл в двоичном формате непосредственно в запрос, можно обойти установленные ограничения.
![](https://habrastorage.org/getpro/habr/upload_files/229/c94/5ce/229c945ce47b185fa626b0d9c207223d.png)
![](https://habrastorage.org/getpro/habr/upload_files/dc2/702/514/dc27025144224fec863a74dee2877804.png)
![](https://habrastorage.org/getpro/habr/upload_files/b51/fed/042/b51fed0424660302424ee2e9d4353c19.png)
Изменим Content-Type и cкопируем двоичное представление juice.jpg, открыв картинку с помощью блокнота. Вставим в запрос.
![](https://habrastorage.org/getpro/habr/upload_files/8e9/1b2/ff9/8e91b2ff99cf2de9f34cde661428dc96.png)
Важное преимущество WAF от компании Вебмониторэкс в том, что он использует регулярные выражения Pire от Яндекс (подробнее здесь - https://github.com/yandex/pire). И в данной разработке есть ключевая функция – инвертор регулярного выражения - значок тильды «~». Таким образом полностью раскрываются обе модели работы WAF – негативная и позитивная. В редких случаях, как в первой рассмотренной нами задаче, можно обойтись без него. Так мы создали позитивное правило с помощью конструкции [^ … ], где … - набор одиночных символов, которые не должны встречаться в искомом слове. То есть при создании блокирующего правила мы наоборот разрешаем только символы на месте … . Проблема в том, что внутри конструкции можно использовать только одиночные символы, а не группу, которые могут составить слово. И проблема решается тильдой. Понятно также то, что негативная модель предусмотрена изначально.
Снова создадим пользовательское правило индикатора атаки на основе регулярного выражения. На это раз будет проверяться не поле в теле json, а заголовок Content-Type в multipart. Запишем регулярное выражение ~(^application/(pdf|zip)$) . Тильда инвертирует выражение. Поэтому допустимы только те запросы, которые содержат в заголовке Content-Type либо application/pdf, либо application/zip .
![](https://habrastorage.org/getpro/habr/upload_files/fb2/dc8/7f5/fb2dc87f56427209ad183a650054be0e.png)
![](https://habrastorage.org/getpro/habr/upload_files/5ce/c25/801/5cec25801ca91b06ec0c48995545fbda.png)
Проверим, как отрабатывает WAF. Отправим нелегитимный запрос снова. Запрос заблокирован.
![](https://habrastorage.org/getpro/habr/upload_files/03e/e54/f17/03ee54f1728f37ddcbec00f4be8ac667.png)
Изменение и добавление заголовков в ответ сервера
Для пользователей, знакомых с NGINX, известно, что в режиме обратного прокси, сервер может добавлять дополнительные заголовки к запросам, которые он проксирует дальше. Таким образом дополнительно защищая трафик. Но можно пойти с другой стороны…
В данном разделе посмотрим на еще одну функцию пользовательских правил платформы «Вебмониторэкс» – добавление/замена заголовков в ответе на запрос. Это позволяет настроить дополнительную защиту приложение от атак.
Для демонстрации данной возможности развернем приложение Vulnbank, которое имитирует уязвимый онлайн-банк. Делается это с помощью докера одной командой.
docker run --name vulnbank -p 80:80 -d vulnbank/vulnbank
Теперь заходим в аккаунт с логином – j.doe и паролем – password. И создаем транзакцию, в комментариях к которой напишем вредоносный скрипт. Это так называемая Stored XSS атака. Написанный нами скрипт будет хранится в базе данных и исполнятся каждый раз, когда мы захотим посмотреть историю транзакций. Вредоносный скрипт вызывает открытие нового окна с официальным сайтом К2Тех.
![](https://habrastorage.org/getpro/habr/upload_files/567/f3f/559/567f3f559424dc95a0e7e9d8de46b047.png)
Но перед тем, как отправим запрос с транзакцией, мы на примере посмотрим еще одну возможность WAF – игнорирование некоторых типов атак. В реальных кейсах это служит для игнорирования False Positive запросов, который поступают на защищенную точку входа. Но в нашем случае мы делаем это, чтобы WAF не заблокировал сразу нашу XSS инъекцию. Создаем правило с исключением. Данные запроса поступает в /vulnbank/online/api.php в строке urlencoded .
![](https://habrastorage.org/getpro/habr/upload_files/ca6/9ff/d3e/ca69ffd3e48d18c15fc4429eb5fca622.png)
![](https://habrastorage.org/getpro/habr/upload_files/0d0/bfd/8ad/0d0bfd8add6f93377aaaef1149b1bb95.png)
![](https://habrastorage.org/getpro/habr/upload_files/cb6/e88/467/cb6e884673fbcbd81eca42a84fcbd5c5.png)
Отправляем транзакцию и переходим в раздел «История». Видим, что скрипт отрабатывает.
![](https://habrastorage.org/getpro/habr/upload_files/66a/0e4/ed0/66a0e4ed02e6928f90f6c093e73cf0ff.png)
В данном примере рассмотрим, как можно дополнительно защитить приложение от атаки хранимой XSS. Делается это с помощью заголовка Content-Security-Policy. С помощью значения script-src можно ограничить источник встраиваемых скриптов, в также добавить nonce. В примере приведен демонстрационный пример. Так в реальных кейсах не делают, так как nonce должен быть динамическим. Создадим правило со статическим nonce, который ограничивает исполнение скриптов с отличным значением или без него вовсе.
![](https://habrastorage.org/getpro/habr/upload_files/d0e/f25/ee7/d0ef25ee7d10e4e922adea9e21027705.png)
Проверяем. Скрипт не исполняется, новая окно не открывается. Видим также, что не формируется лишних запросов, которые вызывал бы вредоносный код.
![](https://habrastorage.org/getpro/habr/upload_files/a64/a6b/9c4/a64a6b9c485847d55c6a6656f5e55540.png)