Предисловие и карма
Впервые, с задачей генерации PIN кода я столкнулся во время прохождения лаборатории у Offensive, за день перед экзаменом OSCP. Мне было жутко лень решать машину Reconstruction, так как за спиной было много проделанной работы и я был почти уверен, что сдам экзамен, но на душе оставалось ощущение, что с данной задачей я еще встречусь. Так и произошло. В ходе противостояния The Standoff было не менее 5 машин, точкой входа для которых как раз была генерация PIN и удаленное выполнение Python кода через консоль Debug мода.
В процессе противостояния The Standoff данный вектор пыталось эксплуатировать множество команд, что привело к постоянной блокировке панели ввода PIN кода. Основная задача была – найти способ обойти ограничения на количество неудачных попыток ввода PIN кода, что удалось сделать моему коллеге.
Генерация PIN кода
Flask — это Framework для создания веб-приложений на языке программирования Python, использующий набор инструментов Werkzeug.
Когда веб-сервер запущен с включенным Debug mode, в случае ошибки справа располагается иконка консоли (или необходимо перейти в веб-директорию console), где можно выполнять Python код, однако для этого необходимо предоставить PIN, генерируемый при старте веб-сервера.
Код генерации PIN находится в файле __init__.py, который может располагаться в следующих директориях:
/usr/local/lib/python*/site-packages/werkzeug/debug/__init__.py
/usr/local/lib/python*/dist-packages/werkzeug/debug/__init__.py
~/.local/lib/python*/site-packages/werkzeug/debug/__init__.py
~/.local/lib/python*/dist-packages/werkzeug/debug/__init__.py
За генерацию PIN отвечает метод get_pin_and_cookie_name(). Сам код генерируется на основе данных из массивов probably_public_bits и private_bits. При наличии уязвимости, позволяющей читать локальные файлы системы, можно собрать необходимые данные и сгенерировать собственный PIN код.
Необходимые данные для генерации PIN кода:
probably_public_bits[0] – пользователь, который запустил веб-сервер (анализируем /etc/passwd)
probably_public_bits[1] – по умолчанию flask.app
probably_public_bits[2] – по умолчанию Flask
probably_public_bits[3] – абсолютный путь к файлу app.py во flask директории, данное значение можем найти из ошибки в Debug mode (/usr/local/lib/python3.6/dist-packages/flask/app.py)
private_bits[0] – MAC адрес сетевого интерфейса (/sys/class/net/<INTERFACE>/address), который необходимо перевести в десятичную систему счисления.
private_bits[1] – machine id, можно получить из файла /etc/machine-id, также необходимо прочитать файл /proc/self/cgroup, если в первой cтроке после крайнего слеша будет некоторое значение, то его необходимо добавить к machine id, иначе используем только machine id.
Для генерации собственного PIN кода можно использовать следующий скрипт:
Однако, генерация PIN может немного различаться под разные версии Python. В моем случае основное отличие заключалось в различных алгоритмах хеширования. Рекомендую сравнить код метода get_pin_and_cookie_name() из файла __init__.py уязвимой машины с вашим кодом для генерации PIN, если это возможно.
Сгенерировав собственный PIN, вводим его и получаем доступ к консоли, где производим удаленное выполнение Python кода.
Генерация Cookie и обход ограничений
При превышении количества неудачных попыток ввода PIN, панель блокируется и спасти в данной ситуации может только рестарт веб-сервера.
Однако, если просмотреть запрос на выполнение Python кода, можно заметить некоторую куку, которая была присвоена после ввода легитимного PIN.
Данная кука является уникальной и состоит из трех частей:
- имя: __wzd4db50c13a80774d469cb, которое генерируется на стадии генерации PIN в методе get_pin_and_cookie_name();
- время: 1623743328, количество секунд с 1 января 1970 00:00:00 UTC минус високосные секунды;
- посоленный хешированный легитимный PIN: за генерацию данного значения отвечает функция hash_pin(), по умолчанию соль прописана в теле функции и равна “shittysalt”;
Получается, для генерации собственной куки, которая позволит сразу перейти к этапу выполнения Python кода даже если панель ввода PIN заблокирована, необходимо сгенерировать легитимный PIN и вызвать функцию hash_pin() для генерации 3-тей части куки, которая не известна.
Далее перехватываем запрос на ввод любого PIN кода, заменяем GET параметр pin на frm=0 и добавляем сгенерированную нами куку.
В результате получаем удаленное выполнение Python кода обойдя заблокированную панель ввода PIN.
Ссылка на скрипт для генерации PIN кода и куки:
WiIs0n/Flask-cookie-generation-based-on-PIN-code
This script generates a Cookie based on a legitimate PIN code. - WiIs0n/Flask-cookie-generation-based-on-PIN-code
Платная лаборатория с только новыми тачками от Offensive (рекомендую для тех кто планирует сдать OSCP):
Offensive Security
Дополнительную информацию по генерации PIN можно найти перейдя по ссылкам:
werkzeug
python flask debugger pin, find and exploit
어떤 CTF에서 python flask 관련 문제가 나왔는데, flask의 debugger pin을 Leak해서 exploit 하는 문제 였다. flask debugger pin은 뭐길래 exploit이 가능한지 알아보자. What is Flask debugger PIN debugger..
Flask RCE Debug Mode - Ghostlulz Hacks
Django, Flask remote code execution (RCE) via enabling debug mode. Werkzeug debug mode enables anonymous command shell execution via the web.
ghostlulz.com