Се­год­ня я покажу при­мер под­делки зап­росов на сто­роне сер­вера (SSRF). Мы получим дос­туп к внут­рянке сай­та и извле­чем кри­тичес­ки важ­ные дан­ные, затем най­дем дан­ные тех­ничес­кой учет­ной записи и повысим при­виле­гии в Linux через уяз­вимость в биб­лиоте­ке GitPython.

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

warning

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

Разведка

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

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

10.10.11.20 editorial.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).

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

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

Идем смот­реть, что там на веб‑сер­вере.

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

Точка входа

На сай­те находим фор­му для отправ­ки информа­ции о кни­ге.

Содержимое страницы Upload
Со­дер­жимое стра­ницы Upload

В фор­ме есть поля для URL и заг­рузки фай­ла. Если сер­вер будет обра­щать­ся к ука­зан­ному нами URL, обя­затель­но нуж­но про­верить, нет ли тут уяз­вимос­ти SSRF. Давай выпол­ним зап­рос на свой сер­вер, для чего у себя запус­тим лис­тенер:

nc -nlvp 8000

И ука­жем адрес нашей машины в поле URL. Осталь­ные поля для начала запол­ним тес­товыми дан­ными.

Содержимое страницы Upload
Со­дер­жимое стра­ницы Upload

Пос­ле отправ­ки зап­роса стра­ница обновля­ется, но в логах лис­тенера пус­то. Прос­матри­ваем зап­рос в Burp History и не находим там ука­зан­ного URL.

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

Вер­немся к фор­ме на сай­те, сно­ва запол­ним ее дан­ными, но в этот раз не будем их отправ­лять, а наж­мем кноп­ку Preview. В этот момент на лис­тенер при­ходит зап­рос от сер­вера.

Запрос на сервер
Зап­рос на сер­вер
Логи листенера
Ло­ги лис­тенера

А теперь ради экспе­римен­та в поле URL вмес­то сво­его лис­тенера ука­зыва­ем 127.0.0.1. В ответ получа­ем какой‑то путь.

Запрос и ответ сервера в Burp History
Зап­рос и ответ сер­вера в Burp History

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

Точка опоры

Так как мы можем получить дос­туп к внут­ренним ресур­сам, поп­робу­ем най­ти еще какие‑нибудь внут­ренние сер­висы. Для это­го проб­рутим пор­ты в отправ­ляемом URL через Burp Intruder.

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

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

Burp Intruder — результат перебора
Burp Intruder — резуль­тат перебо­ра

Ком­бинаци­ей кла­виш Ctrl-R пересы­лаем зап­рос в Burp Repeater и про­веря­ем ответ. В отве­те получа­ем уже дру­гой путь, на этот раз файл в катало­ге с заг­рузка­ми.

Ответ сервера
От­вет сер­вера

Пе­рехо­дим по получен­ному URI в бра­узе­ре и в исто­рии зап­росов видим, что мы можем пос­мотреть содер­жимое фай­ла.

Ответ сервера
От­вет сер­вера

Дан­ные пред­став­лены в фор­мате JSON, поэто­му копиру­ем их в файл и прос­матри­ваем с помощью ути­литы jq.

Содержимое скачанного файла
Со­дер­жимое ска­чан­ного фай­ла

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

http://127.0.0.1:5000/api/latest/metadata/messages/authors
Ответ сервера
От­вет сер­вера

Сно­ва ска­чива­ем файл и кра­сиво выводим через jq.

Содержимое скачанного файла
Со­дер­жимое ска­чан­ного фай­ла

В боль­шом сооб­щении от авто­ра рас­кры­вают­ся учет­ные дан­ные записи dev. Успешно под­клю­чаем­ся от ее име­ни к сер­веру по SSH и получа­ем пер­вый флаг.

Флаг пользователя
Флаг поль­зовате­ля

Продвижение

Те­перь нам необ­ходимо соб­рать информа­цию. Я буду исполь­зовать для это­го скрип­ты PEASS.

Справка: скрипты PEASS

Что делать пос­ле того, как мы получи­ли дос­туп в сис­тему от име­ни поль­зовате­ля? Вари­антов даль­нейшей экс­плу­ата­ции и повыше­ния при­виле­гий может быть очень мно­го, как в Linux, так и в Windows. Что­бы соб­рать информа­цию и наметить цели, мож­но исполь­зовать Privilege Escalation Awesome Scripts SUITE (PEASS) — набор скрип­тов, которые про­веря­ют сис­тему на авто­мате и выда­ют под­робный отчет о потен­циаль­но инте­рес­ных фай­лах, про­цес­сах и нас­трой­ках.

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

В катало­ге /opt есть два про­екта: apps и internal_apps.

Содержимое каталога /opt
Со­дер­жимое катало­га /opt

Из фай­ла с нас­трой­ками веб‑сер­вера Nginx узна­ём о Unix-сокете editorial.sock в про­екте /opt/apps.

Конфиги PHP
Кон­фиги PHP

В катало­ге /home/dev/apps есть репози­торий Git.

Найденные файлы .git
Най­ден­ные фай­лы .git

Для удоб­ного ана­лиза Git-репози­тория заар­хивиру­ем каталог /home/dev/apps и ска­чаем его на свою машину.

zip -r apps.zip apps
scp dev@10.10.11.20:/home/dev/apps.zip ./

За­тем я открыл каталог в Visual Studio Code и пос­мотрел исто­рию ком­митов.

История коммитов в репозитории apps
Ис­тория ком­митов в репози­тории apps

В одном из ком­митов есть все­го одно изме­нение: в сооб­щении вмес­то учет­ных дан­ных записи prod записа­ны учет­ные дан­ные для поль­зовате­ля dev.

Изменения в файле
Из­менения в фай­ле

Ис­поль­зуем учет­ную запись prod для сме­ны кон­тек­ста безопас­ности и получа­ем сес­сию нового поль­зовате­ля.

Сессия пользователя prod
Сес­сия поль­зовате­ля prod

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

Раз­ведку мы уже про­води­ли, поэто­му при изме­нении кон­тек­ста сно­ва все про­верять не нуж­но. Пос­мотрим толь­ко самое важ­ное. Одно из мест, куда сто­ит заг­лянуть пер­вым делом, — это файл sudoers.

Справка: sudoers

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

Настройки sudoers
Нас­трой­ки sudoers

Та­ким обра­зом, мож­но выпол­нить скрипт /opt/internal_apps/clone_changes/clone_prod_change.py от име­ни поль­зовате­ля root. При этом скрип­ту мож­но передать какие‑то парамет­ры. Прос­мотрим его исходный код.

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

В самом коде ничего инте­рес­ного най­ти не уда­лось. Одна­ко есть импорт из пакета git. Час­то в таких слу­чаях мож­но обна­ружить, что исполь­зуют­ся ста­рые биб­лиоте­ки, в которых есть извес­тные уяз­вимос­ти. Давай пос­мотрим, какая биб­лиоте­ка здесь.

Определение библиотеки
Оп­ределе­ние биб­лиоте­ки

Google выводит нас на биб­лиоте­ку GitPython. Поищем экс­пло­иты для нее.

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

Вер­сии GitPython мень­ше 3.1.35 уяз­вимы к CVE-2022-24439 — внед­рению кода за счет внеш­него вызова Git без дос­таточ­ной очис­тки вход­ных аргу­мен­тов. Про­верим, какая вер­сия биб­лиоте­ки исполь­зует­ся на сер­вере.

Версии pip-пакетов
Вер­сии pip-пакетов

На сер­вере — GitPython 3.1.29, а зна­чит, мы можем внед­рить свой код, который будет выпол­нен в при­виле­гиро­ван­ном кон­тек­сте. Как это сде­лать, мож­но под­смот­реть на GitHub.

Описание уязвимости
Опи­сание уяз­вимос­ти

В качес­тве выпол­няемо­го кода будем исполь­зовать скрипт exp.sh, который уста­новит фай­лу коман­дной обо­лоч­ки /bin/bash бит SUID.

#!/bin/bash
chmod u+s /bin/bash

Справка: бит SUID

Ког­да у фай­ла уста­нов­лен атри­бут setuid (S-атри­бут), обыч­ный поль­зователь, запус­кающий этот файл, получа­ет повыше­ние прав до поль­зовате­ля — вла­дель­ца фай­ла в рам­ках запущен­ного про­цес­са. Пос­ле получе­ния повышен­ных прав при­ложе­ние может выпол­нять задачи, которые недос­тупны обыч­ному поль­зовате­лю. Из‑за воз­можнос­ти сос­тояния гон­ки мно­гие опе­раци­онные сис­темы игно­риру­ют S-атри­бут, уста­нов­ленный shell-скрип­там.

Экс­плу­ати­руем уяз­вимость и пос­ле кучи оши­бок про­веря­ем пра­ва на бинар­ник bash.

sudo /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py "ext::sh -c '/tmp/exp.sh'"
Эксплуатация уязвимости
Экс­плу­ата­ция уяз­вимос­ти
Флаг рута
Флаг рута

Все получи­лось, машина зах­вачена!