Каждый новый специалист нашей практики Защиты приложений проходит нечто среднее между посвящением и стажировкой. Обычно в рамках задачи нужно развернуть уязвимое приложение, WAF одного из наших фокусных партнеров, а потом найти конкретную уязвимость, проэксплуатировать ее, посмотреть что видно на WAF в режиме мониторинга, а затем настроить WAF, чтобы он начал обнаруживать и блокировать данную уязвимость. Конечно, уязвимости при этом выбираются не такие, чтобы сразу по сигнатуре можно было бы ее обнаружить.
В этой статье младший системный инженер “К2 Кибербезопасность” Даниил Золотарев поделится задачей, которая выпала ему.
В ходе работы Даниилу пришлось защищать Juice Shop средствами платформы «Вебмониторэкс» и столкнуться с некоторыми аспектами негативной и позитивной моделей данного WAF. Далее мы рассмотрим примеры создания пользовательских правил, для блокировки атак Improper Input Validation (Неправильная проверка ввода). Таким образом наглядно продемонстрируем одну из ключевых возможностей WAF – закрытие дыр приложения в проде до фикса.
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), а оба сервера будут в одной локальной подсети уязвимого локального веб-приложения. Также мы настроили группы безопасности сетевых интерфейсов серверов, чтобы не было доступа из глобальной сети или сторонних подсетей.
Платформу «Вебмониторэкс» можно развернуть на серверах с ОС 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/
После настройки приведу архитектуру нашего развернутого стенда для общего понимания на рисунке ниже.
Пользовательские правила на основе регулярных выражений и тестирование
В этом разделе создадим пользовательские правила для устранения некоторых уязвимостей в веб-приложении Juice Shop. В WAF от компании Вебмониторэкс расширенная функциональность работы с запросами.
Improper input validation (Неправильная проверка ввода)
Одной из главных возможностей WAF является предотвращение эксплуатации уязвимостей в стадии эксплуатации приложения до релиза с исправлением. Так частой ошибкой во время разработки веб-приложений является неправильная проверка ввода. UI предлагает выбор вариантов ввода, и разработчик не задумывается над тем, что данные ввода надо дополнительно парсить в бэкенде, потому что запрос может быть скомпрометирован. Яркий пример такого поведения – задача Zero Stars.
Zero Stars
Перейдем во вкладку «Отзыв пользователя» и заполним форму, выбрав 3 звезды. Затем перехватим сгенерированный запрос к серверу и изменим рейтинг на 0. Отправим запрос на сервер. Заметим, что запрос обработался сервером и успешно прошел валидацию.
Решение данной задачи с помощью платформы «Вебмониторэкс» состоит в создании пользовательского правила с обнаружением атаки на основе регулярного выражения. Создадим его в разделе «Rules», выберем «Создать индикатор атаки на основе регулярного выражения», выберем тип атаки – «Виртуальный патч» и заполним регулярное выражение - ^[^1-5]$ . Данной правило будет блокировать все POST запросы на URL - /api/Feedbacks с любыми значениями в теле json по ключу rating, кроме цифр от 1 до 5.
Проверим. Генерируем такой же запрос с рейтингом 0 и наблюдаем, что в ответе возвращается ошибка 403. Также формируется событие в личном кабинете WAF. Развернув его, можно получить подробности заблокированного запроса.
Admin Registration
Данная задача заключается также в эксплуатации неправильной проверки ввода. Добавив лишнее поле в тело json POST запроса при регистрации аккаунта, можно получить права администратора. Перехватим запрос и добавим значение admin с ключом role. Ответ сервера говорит об успешной обработке и регистрации аккаунта с ролью администратора.
Для решение данной задачи достаточно создать виртуальный патч, который блокирует POST запросы пользователей на URL /api/Users, в теле json которых будет ключ role. После создания виртуального патча видим, что запрос заблокирован.
Upload Type
Данная задача указывает на то, что активная проверка на тип загружаемого файла есть только на фронте. Но перехватив запрос, изменив в нем заголовок Content-Type в multipart и добавив файл в двоичном формате непосредственно в запрос, можно обойти установленные ограничения.
Изменим Content-Type и cкопируем двоичное представление juice.jpg, открыв картинку с помощью блокнота. Вставим в запрос.
Важное преимущество WAF от компании Вебмониторэкс в том, что он использует регулярные выражения Pire от Яндекс (подробнее здесь - https://github.com/yandex/pire). И в данной разработке есть ключевая функция – инвертор регулярного выражения - значок тильды «~». Таким образом полностью раскрываются обе модели работы WAF – негативная и позитивная. В редких случаях, как в первой рассмотренной нами задаче, можно обойтись без него. Так мы создали позитивное правило с помощью конструкции [^ … ], где … - набор одиночных символов, которые не должны встречаться в искомом слове. То есть при создании блокирующего правила мы наоборот разрешаем только символы на месте … . Проблема в том, что внутри конструкции можно использовать только одиночные символы, а не группу, которые могут составить слово. И проблема решается тильдой. Понятно также то, что негативная модель предусмотрена изначально.
Снова создадим пользовательское правило индикатора атаки на основе регулярного выражения. На это раз будет проверяться не поле в теле json, а заголовок Content-Type в multipart. Запишем регулярное выражение ~(^application/(pdf|zip)$) . Тильда инвертирует выражение. Поэтому допустимы только те запросы, которые содержат в заголовке Content-Type либо application/pdf, либо application/zip .
Проверим, как отрабатывает WAF. Отправим нелегитимный запрос снова. Запрос заблокирован.
Изменение и добавление заголовков в ответ сервера
Для пользователей, знакомых с NGINX, известно, что в режиме обратного прокси, сервер может добавлять дополнительные заголовки к запросам, которые он проксирует дальше. Таким образом дополнительно защищая трафик. Но можно пойти с другой стороны…
В данном разделе посмотрим на еще одну функцию пользовательских правил платформы «Вебмониторэкс» – добавление/замена заголовков в ответе на запрос. Это позволяет настроить дополнительную защиту приложение от атак.
Для демонстрации данной возможности развернем приложение Vulnbank, которое имитирует уязвимый онлайн-банк. Делается это с помощью докера одной командой.
docker run --name vulnbank -p 80:80 -d vulnbank/vulnbank
Теперь заходим в аккаунт с логином – j.doe и паролем – password. И создаем транзакцию, в комментариях к которой напишем вредоносный скрипт. Это так называемая Stored XSS атака. Написанный нами скрипт будет хранится в базе данных и исполнятся каждый раз, когда мы захотим посмотреть историю транзакций. Вредоносный скрипт вызывает открытие нового окна с официальным сайтом К2Тех.
Но перед тем, как отправим запрос с транзакцией, мы на примере посмотрим еще одну возможность WAF – игнорирование некоторых типов атак. В реальных кейсах это служит для игнорирования False Positive запросов, который поступают на защищенную точку входа. Но в нашем случае мы делаем это, чтобы WAF не заблокировал сразу нашу XSS инъекцию. Создаем правило с исключением. Данные запроса поступает в /vulnbank/online/api.php в строке urlencoded .
Отправляем транзакцию и переходим в раздел «История». Видим, что скрипт отрабатывает.
В данном примере рассмотрим, как можно дополнительно защитить приложение от атаки хранимой XSS. Делается это с помощью заголовка Content-Security-Policy. С помощью значения script-src можно ограничить источник встраиваемых скриптов, в также добавить nonce. В примере приведен демонстрационный пример. Так в реальных кейсах не делают, так как nonce должен быть динамическим. Создадим правило со статическим nonce, который ограничивает исполнение скриптов с отличным значением или без него вовсе.
Проверяем. Скрипт не исполняется, новая окно не открывается. Видим также, что не формируется лишних запросов, которые вызывал бы вредоносный код.