Се­год­ня я покажу, как регис­три­ровать сис­темные вызовы в Linux, что поможет нам перех­ватить дан­ные поль­зовате­ля. Но на пути к сис­теме нам понадо­бит­ся заюзать SSRF на сай­те через X-Skipper-Proxy и уста­новить свой модуль в CMS Blazor.

На­ша цель — получе­ние прав супер­поль­зовате­ля на машине Lantern с учеб­ной пло­щад­ки Hack The Box. Уро­вень задания — слож­ный.

warning

Под­клю­чать­ся к машинам с HTB рекомен­дует­ся с при­мене­нием средств ано­ними­зации и вир­туали­зации. Не делай это­го с компь­юте­ров, где есть важ­ные для тебя дан­ные, так как ты ока­жешь­ся в общей сети с дру­гими учас­тни­ками.

Разведка

Сканирование портов

До­бав­ляем IP-адрес машины в /etc/hosts:

10.10.11.29 lantern.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).

Результат работы скрипта
Ре­зуль­тат работы скрип­та

Ска­нер нашел три откры­тых пор­та:

Сра­зу видим, что на сер­вере уста­нов­лен Skipper Proxy — прок­си‑сер­вер и HTTP-роутер, написан­ный на Go. Он мар­шру­тизи­рует HTTP-зап­росы на осно­ве раз­ных пра­вил и нас­тро­ек.

Главная страница сайта
Глав­ная стра­ница сай­та

Точка входа

На сай­те на 80-м пор­те находим инте­рес­ную информа­цию о коман­де раз­работ­чиков. Мож­но сде­лать выводы об исполь­зуемых тех­нологи­ях.

Описание вакансий
Опи­сание вакан­сий

Так­же находим фор­му отправ­ки резюме.

Форма отправки файла
Фор­ма отправ­ки фай­ла

На пор­те 3000 нас встре­чает фор­ма авто­риза­ции.

Форма авторизации
Фор­ма авто­риза­ции

Ес­ли прос­мотреть исто­рию зап­росов в Burp History, заметим мно­го зап­росов, типич­ных для CMS Blazor. С этой сис­темой я уже стал­кивал­ся, ког­да писал рай­тап по HTB Blazorized.

Burp History
Burp History

Ос­мотрев сай­ты, углу­бим­ся в изу­чение най­ден­ных про­дук­тов. У нас есть Skipper Proxy и Blazor, нач­нем с пер­вого. Сна­чала сто­ит про­верить, есть ли для обна­ружен­ной вер­сии CMS акту­аль­ные экс­пло­иты.

Поиск эксплоитов в Google
По­иск экс­пло­итов в Google

Из Google узна­ём, что Skipper Proxy 0.13.237 уяз­вим к CVE-2022-38580. Как ска­зано в опи­сании экс­пло­ита, мож­но экс­плу­ати­ровать SSRF для дос­тупа к внут­ренним ресур­сам, прос­то ука­зав адрес ресур­са в заголов­ке X-Skipper-Proxy.

Описание эксплоита
Опи­сание экс­пло­ита

Пе­рей­дем в Burp Repeater и добавим заголо­вок X-Skipper-Proxy: http://127.0.0.1:3000.

Эксплуатация SSRF
Экс­плу­ата­ция SSRF

Сер­вер отдаст нам содер­жимое сай­та на пор­те 3000, что под­твержда­ет наличие уяз­вимос­ти.

Точка опоры

Пе­ренап­равля­ем зап­рос в Burp Repeater для перебо­ра пор­тов через SSRF. Так мы смо­жем узнать о работа­ющих внут­ренних сер­висах.

Burp Repeater — вкладка Positions
Burp Repeater — вклад­ка Positions
Burp Repeater — вкладка Payloads
Burp Repeater — вклад­ка Payloads

Сор­тиру­ем резуль­таты ска­ниро­вания по коду отве­та и получа­ем четыре откры­тых пор­та для адре­са 127.0.0.1.

Результаты сканирования
Ре­зуль­таты ска­ниро­вания

Порт 5000 отве­чает за тех­нологию Blazor, а зна­чит, через SSRF мы можем получить дос­туп к фай­лу /_framework/blazor.boot.json, который содер­жит информа­цию обо всех заг­ружа­емых сбор­ках.

Содержимое файла blazor.boot.json
Со­дер­жимое фай­ла blazor.boot.json

Из всех сбо­рок наибо­лее инте­рес­на кас­томная биб­лиоте­ка InternaLantern.dll, в которой, видимо, и реали­зова­ны основные воз­можнос­ти. Нам нуж­но ее ска­чать, и для это­го обра­тим­ся к фай­лу через спе­циаль­ный прок­си. Для это­го в кон­текс­тном меню перехо­дим к Request in browser → In current browser session.

Запрос в Burp Repeater
Зап­рос в Burp Repeater

По­луча­ем ссыл­ку, по которой нуж­но перей­ти в бра­узе­ре Burp. В резуль­тате получа­ем ска­чан­ную биб­лиоте­ку.

Ссылка на страницу
Ссыл­ка на стра­ницу
Скачанный DLL-файл
Ска­чан­ный DLL-файл

Так как биб­лиоте­ка написа­на на C#, мы можем деком­пилиро­вать исходный код для ана­лиза. В этом нам поможет бес­плат­ная ути­лита dotPeek. В исходном коде сре­ди перечис­ления дан­ных о поль­зовате­лях находим длин­ные стро­ки в Base64.

Исходный код InternaLantern.Pages
Ис­ходный код InternaLantern.Pages

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

Результат декодирования Base64
Ре­зуль­тат декоди­рова­ния Base64

Де­коди­руем все стро­ки из фай­ла и получа­ем пароль поль­зовате­ля admin.

Данные пользователей
Дан­ные поль­зовате­лей

С эти­ми учет­ными дан­ными заходим на основной сайт.

Главная страница сайта
Глав­ная стра­ница сай­та

Продвижение

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

Форма загрузки файлов
Фор­ма заг­рузки фай­лов
Структура файлов
Струк­тура фай­лов

Прос­мотрим основной файл app.py. Мое вни­мание прив­лек эндпо­инт /PrivacyAndPolicy, который при­нима­ет парамет­ры lang и ext и исполь­зует их в фор­мирова­нии пути без какой‑либо филь­тра­ции (стро­ки 38–45).

Содержимое файла app.py
Со­дер­жимое фай­ла app.py

То есть на сер­вере есть уяз­вимость, поз­воля­ющая получить содер­жимое про­изволь­ного фай­ла. Для про­вер­ки получим содер­жимое фай­ла /etc/passwd.

http://lantern.htb/PrivacyAndPolicy?lang=../../../../&ext=./etc/passwd
Содержимое файла /etc/passwd
Со­дер­жимое фай­ла /etc/passwd

Рас­кру­тить эту уяз­вимость не выш­ло, поэто­му я обра­тил вни­мание на фун­кцию исполь­зования модулей. Если изме­нить стан­дар­тный модуль Logs на любой про­изволь­ный, то получим ошиб­ку, рас­кры­вающую путь к ком­понен­там.

Ошибка загрузки модуля
Ошиб­ка заг­рузки модуля

По­луча­ется, что, если заг­рузить в каталог свой модуль, его мож­но будет под­клю­чить в прог­рамму. Отло­вим зап­рос на заг­рузку фай­ла evil.dll в Burp Proxy.

Burp Proxy — запрос на сервер
Burp Proxy — зап­рос на сер­вер

Дан­ные переда­ются в сери­али­зован­ном виде, поэто­му прос­то изме­нить их не получит­ся. Для десери­али­зации и сери­али­зации дан­ных будем исполь­зовать рас­ширение Blazor Traffic Processor.

Установка расширения
Ус­танов­ка рас­ширения
Blazor Traffic Processor
Blazor Traffic Processor

Имя заг­ружа­емо­го фай­ла переда­ется в парамет­ре name. Сде­лаем свой ком­понент и пов­торим заг­рузку. Для это­го спер­ва соз­дадим про­ект.

mkdir evil
cd evil
dotnet new classlib -n exploit
Создание проекта
Соз­дание про­екта

Те­перь перей­дем к фай­лу Class1.cs внут­ри про­екта и изме­ним код. Наш модуль будет счи­тывать и выводить на стра­ницу при­ват­ный ключ поль­зовате­ля tomas (о нем узна­ли из фай­ла /etc/passwd).

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using System.IO;
namespace exploit;
public class Component : ComponentBase
{
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
base.BuildRenderTree(builder);
string file = File.ReadAllText("/home/tomas/.ssh/id_rsa");
builder.AddContent(0, file);
}
}

Ос­талось добавить необ­ходимые ком­понен­ты AspNetCore и соб­рать нашу DLL.

cd exploit
dotnet add package Microsoft.AspNetCore.Components --version 6.0.0
dotnet add package Microsoft.AspNetCore.Components.Web --version 6.0.0
dotnet build -c release
Релиз проекта
Ре­лиз про­екта

Пов­торя­ем заг­рузку фай­ла, на момен­те десери­али­зации дан­ных изме­няем путь заг­рузки фай­ла и выпол­няем пов­торную сери­али­зацию.

Сериализация данных
Се­риали­зация дан­ных

При­меня­ем сери­али­зован­ные дан­ные в Burp Proxy и отправ­ляем оста­нов­ленный ранее зап­рос. Файл успешно заг­ружен по ука­зан­ному пути.

Результат загрузки файла
Ре­зуль­тат заг­рузки фай­ла

Пе­рехо­дим к панели адми­нис­тра­тора и ука­зыва­ем имя модуля. В отве­те сер­вера получа­ем при­ват­ный SSH-ключ поль­зовате­ля.

SSH-ключ
SSH-ключ

С этим клю­чом авто­ризу­емся по SSH и чита­ем пер­вый флаг.

Локальное повышение привилегий

Пер­вое, что мы про­веря­ем на машине с Linux, — есть ли у нас воз­можность выпол­нить коман­ды через sudo.

sudo -l
Настройки sudoers
Нас­трой­ки sudoers

Справка: sudoers

Файл /etc/sudoers в Linux содер­жит спис­ки команд, которые раз­ные груп­пы поль­зовате­лей могут выпол­нять от име­ни адми­нис­тра­тора сис­темы. Мож­но прос­мотреть его как нап­рямую, так и при помощи коман­ды sudo -l.

Су­дя по содер­жимому фай­ла sudoers, мы можем выпол­нить коман­ду /usr/bin/procmon в при­виле­гиро­ван­ном кон­тек­сте без вво­да пароля. Про­верим тип фай­ла /usr/bin/procmon и узна­ем, что это исполня­емый ELF.

file /usr/bin/procmon
Проверка файла procmon
Про­вер­ка фай­ла procmon

Справ­ка дает понять, что это ана­лог Process Monitor для Windows, толь­ко в Linux.

Справка procmon
Справ­ка procmon

Та­ким обра­зом, запус­кая при­ложе­ние через sudo, мы можем прос­матри­вать сис­темные вызовы (параметр -e) для любого про­цес­са на хос­те. Давай выберем про­цесс для отсле­жива­ния. Меня заин­тересо­вал nano /root/automation.sh.

Список процессов
Спи­сок про­цес­сов

Про­цесс пос­тоян­но завер­шает­ся и запус­кает­ся заново, поэто­му его PID меня­ется. Запус­тим procmon и ука­жем ему, что нуж­но собирать сис­темный вызов write у про­цес­са с PID 909 и сох­ранять лог в базу дан­ных db.db.

sudo /usr/bin/procmon -e write -p 909 -c db.db
Отслеживание процесса через procmon
От­сле­жива­ние про­цес­са через procmon

Ми­нуты через две завер­шаем прог­рамму и забира­ем базу дан­ных на свой хост для ана­лиза.

Логи после завершения procmon
Ло­ги пос­ле завер­шения procmon

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

Данные вызова с малой задержкой
Дан­ные вызова с малой задер­жкой
Данные вызова с большой задержкой
Дан­ные вызова с боль­шой задер­жкой

Я написал SQL-зап­рос, который извле­кает из каж­дого вызова с боль­шой задер­жкой (duration > 100000000) девятый сим­вол. Поз­же зап­рос под­коррек­тировал, так как девятым может идти про­бел, в этом слу­чае будем брать десятый.

SELECT GROUP_CONCAT(
CASE
WHEN hex(substr(arguments, 9, 1)) = '20' THEN substr(arguments, 10, 1)
ELSE substr(arguments, 9, 1)
END,'') as data
FROM ebpf where duration > 100000000;
Результат выполнения запроса
Ре­зуль­тат выпол­нения зап­роса

И получа­ем вве­ден­ный в nano текст: echo Q3Eddtdw3pMB | sudo ./backup.sh. У нас есть пароль рута, и нич­то не меша­ет заб­рать пос­ледний флаг.

Флаг рута
Флаг рута

Ма­шина зах­вачена!