Debug pin Werkzeug через SSTI

Debug pin Werkzeug через SSTI

SidneyJob


Содержание

- Предисловие

- Словарик

- Поиск репозитория

- Подготовка к запуску

- Анализ исходного кода

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


Предисловие

Недавно я участвовал в одной очень классной ctf'ке. На этой ctf'ке нам дали один очень интресный таск, которым я и хочу с вами поделиться.



Словарик

В статье используются сокращения и чтобы вам было понятнее вот их значение:

Flask - это микрофреймворк для создания веб-приложений на языке Python. Flask позволяет быстро и просто создавать веб-приложения, не требуя от разработчика огромного объема кода и дополнительных библиотек.


OCR (Optical Character Recognition) - это процесс распознавания текста на изображениях, сканированных документах или фотографиях и преобразования его в электронный формат, который может быть редактирован и сохранен на компьютере.


SSTI (Server-Side Template Injection) - это тип угрозы безопасности приложения, при котором злоумышленник может внедрить и выполнить код на сервере приложений, используя уязвимости в функциях шаблонизации. Злоумышленник может передать в шаблон код, который будет выполнен на сервере и может привести к утечке или краже конфиденциальной информации, или даже к контролю сервера.


Поиск репозитория

Чтобы поделиться с вами моим опытом сначала нужно было найти исходники данного проекта. Это было веб-приложение, которое с помощью технологии OCR распозновало текст на фотографиях и выводила результат на отдельной странице. На глаза мне попался один проект от lucadibello, который очень сильно был похож на таск в нашей ctf'ке. Cсылка на репозиторий автора: [LINK]


Подготовка к запуску

Установка проекта довольно простая, но все же есть некие моменты, которые я считаю нужным показать здесь. Установку буду показывать для UNIX систем, для виндовс почти все тоже самое)


Этапы запуска проекта:

1) Скачать сам репозиторий

git clone [URL]


2) Установить записимости для python'а

pip3 install -r requirements.txt


3) Скачать tesseract и добавить путь к нему в main.py


Установка tesseract:
sudo apt install tesseract-ocr -y

Поиск расположения бинарного файла:
where tesseract                  
> /bin/tesseract

Далее в конце файла main.py видим следующий код:
if __name__ == '__main__':
   # Setup Tesseract executable path
   pytesseract.pytesseract.tesseract_cmd = r'/bin/tesseract'
   print(generate_debug_pin(app.secret_key))
   app.run(debug=True)

Нам необходимо вставить в переменную <tesseract_cmd> путь к нашему бинарному файлу.

4) На этом все, остается только запустить код нашего проекта!
python3 main.py


Пример запуска проекта:


Анализ исходного кода

Проект запущен, все работает отлично, а значит пора искать уязвимости! На данном этапе самым главным было найти данные, которые мы можем контролировать. Единственное место, куда мы можем внедрить свои данные - это ввод картинки, которая после рендерится. Поскольку у нас есть исходный код, то мы можем протестировать это приложение методом whitebox.


Посмотрим на исходный код ниже, который рендерит наши результаты:



В данном коде для рендеринга нашего кода используется функция render_template.


Внедрение SSTI в функцию render_template() в Flask возможно. Но для этого необходимо разрешить использовать возможности SSTI по умолчанию в Flask при использовании функции render_template(). Для этого нужно установить в настройках приложения Flask опцию autoescape в значение False:



Однако, в нашем приложении данная опция отстутвует, поэтому мы немного изменим исходный код приложения и получим следующеий код:


Как итог получим уязвимую функцию, которая будет уязвима к SSTI, так как ввод с картинки(которую мы прочитали ранее) никак не санитизируется.

 

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

Пойдем немного нестандартным путем. Я не хочу делать стандартное исполнение команд, вместо этого я хочу получить доступ к дебаг консоли Werkzeug. Это может быть полезно если есть фильтрация некоторых символов, которые мешают нам получить reverse shell. Debug оболочка Werkzeug работает без каких-либо ограничений и поможет нам получить хороший веб-шел.


Сначала опишу все этапы, которые нам надо будет пройти.

- Проверяем работоспособность SSTI

- Генерация DEBUG-PIN

- Получение доступа к python оболочке для выполнения удаленного кода


Debug PIN в Flask нужен для безопасного удаленного отладочного доступа к приложению в режиме debug. Некоторые режимы debug, такие как bare, - позволяют возможность удаленного подключения к отладчику приложения, но у этих режимов может быть уязвимость безопасности, поэтому был предложен Debug PIN.


Проверяем работоспособность SSTI

Загружаем следующее изображения чтобы проверить, что нам отдаст flask после обработки нашей фотографии:





Результат отработки flask:



На скрине выше вы можете увидеть, что SSTI работает нормально и в качестве ответа мы получили ответ на математическое выражение 7*7. Фигурные скобки нужны были чтобы flask распознал наш текст как шаблон, который нужно выполить, а не как простой текст.



Генерация своего DEBUG-PIN

И так начнем. В интернете есть множество скриптов, но я решил написать свой, поскольку все скрипты, которые я пробовал работают либо только для старых версий Flask, либо вообще не работают. Вот ссылка на мой гит со скриптом: https://github.com/SidneyJob/Werkzeuger


Процесс генерации PIN

Процесс гернерации PIN не сложный, ниже прилагается исходный код для более углубленного исследования. Но все же основные аспекты генерации ключа я приведу.


Нужные наборы байт вы сможете взять не только с помощью ssti, но и с помощью любой уязвимости, которая позволяет читать файла. Так например если у вас есть уязвимость LFI, но вы не можете ее раскрутить до RCE, то если на сервере есть приложение с Flask и включенным дебаг модом, то вы сможете получить RCE

Исходный код werkzeug: [ссылка]


Данные, которые необходимы для геренации DEBUG-PIN:

- Имя текущего пользователя

- MAC address

- machine-id

- cgroup

- Путь к приложению Flask


В процессе генерации ключа участвуют 2 набора байт:

- Публичный набор

- Приватный набор


Публичный набор состоит из:

- Имя пользователя

- Modname

- getattr(app, "__name__", type(app).__name__),

- getattr(mod, "__file__", None),


Приватный набор состоит из:

- MAC адреса интерфейса в десятичной системе

- machine_id



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


Имя пользователя

Имя текущего пользователя можно получить несколькими способами

1) Вывод: /proc/self/cgroup

2) whoami


modname

- По дефолту стоит <flask.app>, скорее всего менять не придется


getattr(app, "__name__", type(app).__name__)

- По дефолту стоит `<Flask>`, скорее всего менять не придется


getattr(mod, "__file__", None)

- Это путь до приложения фласк, процесс его получения будет описан ниже


MAC

При получении интерфейса могут возьникнуть небольшие проблеммы, если этих интерфейсов несколько. Тут стоит пропробовать все интерфейсты, которые имеют доступ к интернету. Их можете увидеть с помощью следующей команды `ifconfig`. Поэтому если у вас не подходит deubg pin, то попробуйте сменить интерфейс.


Machine_id

Этот байт состоит из нескольких частей:

1) Полное содержимое файла /etc/machine-id

2) Часть файла после последнего слеша /proc/self/cgroup

cat /proc/self/cgroup 
// 0::/user.slice/user-1000.slice/session-2.scope

Из всего этого вывода вам понадобится лишь следующая часть:
session-2.scope

Примечание: Если вы будете использовать мой скрипт, то вам следует вводить полное вывод файла, программа сама обрежет ввод до нужного состояния



Получение пути до Flask

И так, чтобы получить путь до приложения flask, ну или 4 байта в публичном наборе вы должны ввести любой некорректный шаблон после которой flask выдаст вам ошибку, в которой будет указан путь до flask.


Работа скрипта


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


Давайте попробуем запустить скрипт и сгенерировать PIN!



Как вы можете видеть мы сгенерировали точно такой же PIN и теперь мы можем получить доступ к консоли!


Получение доступа к python оболочке для выполнения удаленного кода


Теперь у нас есть все данные чтобы войти в дебаг консоль как разработчик. Обычно консоль находится по адресу: http://127.0.0.1:5000/console. При входе в консоль нас встречает окошко с просьбой ввести PIN.



Но поскольку мы только что его сгенерировали мы лишь улыбаясь вводим пин в окно ввода и наслаждаемся интерпритатором python'а.


Команды испольнять с помощью следующего шаблона, где CMD - ваша команда

__import__('os').popen('CMD').read()


Если вы случайно заблокировали панель входа если пробовали брутить, то это можно обойти если поставить куку, которая сразу генерируется моим скриптом!

Report Page