Великий пакостник. Пробираемся через дебри IPv6 к root-флагу виртуалки с Hack The Box

В этой статье я покажу, как получить права суперпользователя на виртуальной машине Mischief с CTF-площадки Hack The Box. Этот забег даст нам навыки работы с протоколом SNMP, поможет понять принцип IPv6-маршрутизации и разобраться с механизмом распределения прав доступа ACL, а под занавес мы построим и протестируем полноценный ICMP-шелл на Python.

По уровню сложности эта виртуалка на Linux находится где-то между Medium и Hard (6,3 балла из 10), хотя изначальный ее рейтинг был определен как Insane. Если бы не некоторые ошибки создателя машины, она и правда была бы «безумной».

Вот что нас ждет на пути к финальному флагу:

  • сбор и анализ информации, предоставляемой протоколом SNMP, с последующим извлечением авторизационных данных из аргументов командной строки для простого Python-сервера;
  • получение IPv6-адреса машины из того же вывода SNMP (первый способ) либо через pivoting другого хоста на Hack The Box из его MAC-адреса (второй способ — алгоритм EUI-64);
  • обнаружение веб-сервера, живущего по найденному IPv6-адресу и содержащего панель удаленного выполнения команд на атакуемой машине;
  • обход фильтрации, который даст возможность инъекции произвольных команд, и захват авторизационных данных пользователя;
  • получение реверс-шелла по IPv6 в обход правил iptables для запуска su от имени www-data (так как оказывается, что пользователя блокирует механизм распределения прав доступа ACL) и получения root-сессии с паролем, забытым в .bash_history;
  • наконец, на сладкое создание ICMP-шелла с помощью модуля Scapy на Python. Он даст возможность просматривать результаты удаленного исполнения команд при помощи утилиты ping.

Разведка

Nmap

Итак, по традиции начнем подготовительный этап сбора информации со сканирования портов с помощью Nmap.

TCP

Сперва сканируем весь диапазон TCP-портов хоста простым сканом SYN.

root@kali:~# nmap -n -v -Pn --min-rate 5000 -oA nmap/initial -p- 10.10.10.92
root@kali:~# cat nmap/initial.nmap
# Nmap 7.70 scan initiated Mon Apr 1 16:17:45 2019 as: nmap -n -v -Pn --min-rate 5000 -oA nmap/initial -p- 10.10.10.92
Nmap scan report for 10.10.10.92
Host is up (0.045s latency).
Not shown: 65533 filtered ports
PORT STATE SERVICE
22/tcp open ssh
3366/tcp open creativepartnr

Read data files from: /usr/bin/../share/nmap
# Nmap done at Mon Apr 1 16:18:11 2019 -- 1 IP address (1 host up) scanned in 26.45 seconds

И теперь делаем точечный запрос на порты 22 и 3366, используя определение версии сервисов и дефолтные скрипты NSE.

root@kali:~# nmap -n -v -Pn -sV -sC -oA nmap/version -p22,3366 10.10.10.92
root@kali:~# cat nmap/version.nmap
# Nmap 7.70 scan initiated Mon Apr 1 16:18:20 2019 as: nmap -n -v -Pn -sV -sC -oA nmap/version -p22,3366 10.10.10.92
Nmap scan report for 10.10.10.92
Host is up (0.042s latency).

PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 2a:90:a6:b1:e6:33:85:07:15:b2:ee:a7:b9:46:77:52 (RSA)
| 256 d0:d7:00:7c:3b:b0:a6:32:b2:29:17:8d:69:a6:84:3f (ECDSA)
|_ 256 3f:1c:77:93:5c:c0:6c:ea:26:f4:bb:6c:59:e9:7c:b0 (ED25519)
3366/tcp open caldav Radicale calendar and contacts server (Python BaseHTTPServer)
| http-auth:
| HTTP/1.0 401 Unauthorized\x0D
|_ Basic realm=Test
| http-methods:
|_ Supported Methods: GET HEAD
|_http-server-header: SimpleHTTP/0.6 Python/2.7.15rc1
|_http-title: Site doesn't have a title (text/html).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon Apr 1 16:18:44 2019 -- 1 IP address (1 host up) scanned in 24.05 seconds

Информации немного. Мы видим вот что.

  • Это относительно новая сборка Ubuntu. Проверив баннер со строкой версии SSH OpenSSH 7.6p1 Ubuntu 4 на launchpad.net — ресурсе, на котором хостятся репозитории пакетов для Ubuntu, — убеждаемся, что мы имеем дело с версией Bionic от 2018.03.07, поэтому искать эксплоиты для Secure Shell бессмысленно.
  • На 3366-м порте TCP работает простой Python HTTP-сервер с авторизацией. С самого начала начинать брутить вслепую что бы то ни было — откровенный моветон, поэтому расширим поверхность атаки сканированием UDP-портов.
Узнаем версию ОС по баннеру SSH

UDP

Как и раньше, сначала «прощупаем почву», используя на этот раз флаг -sU для диапазона UDP.

root@kali:~# nmap -n -v -Pn --min-rate 5000 -oA nmap/udp-initial -sU -p- 10.10.10.92
root@kali:~# cat nmap/udp-initial.nmap
# Nmap 7.70 scan initiated Mon Apr 1 16:26:41 2019 as: nmap -n -v -Pn --min-rate 5000 -oA nmap/udp-initial -sU -p- 10.10.10.92
Nmap scan report for 10.10.10.92
Host is up (0.048s latency).
Not shown: 65534 open|filtered ports
PORT STATE SERVICE
161/udp open snmp

Read data files from: /usr/bin/../share/nmap
# Nmap done at Mon Apr 1 16:27:08 2019 -- 1 IP address (1 host up) scanned in 26.56 seconds

И «добьем» расширенным запросом.

root@kali:~# nmap -n -v -Pn -sV -sC -oA nmap/udp-version -sU -p161 10.10.10.92
root@kali:~# cat nmap/udp-version.nmap
# Nmap 7.70 scan initiated Mon Apr 1 16:27:39 2019 as: nmap -n -v -Pn -sV -sC -oA nmap/udp-version -sU -p161 10.10.10.92
Nmap scan report for 10.10.10.92
Host is up (0.043s latency).

PORT STATE SERVICE VERSION
161/udp open snmp SNMPv1 server; net-snmp SNMPv3 server (public)
| snmp-info:
| enterprise: net-snmp
| engineIDFormat: unknown
| engineIDData: b6a9f84e18fef95a00000000
| snmpEngineBoots: 19
|_ snmpEngineTime: 16h02m33s
| snmp-interfaces:
| lo
| IP address: 127.0.0.1 Netmask: 255.0.0.0
| Type: softwareLoopback Speed: 10 Mbps
| Status: up
| Traffic stats: 0.00 Kb sent, 0.00 Kb received
| Intel Corporation 82545EM Gigabit Ethernet Controller (Copper)
| IP address: 10.10.10.92 Netmask: 255.255.255.0
| MAC address: 00:50:56:b9:7c:aa (VMware)
| Type: ethernetCsmacd Speed: 1 Gbps
| Status: up
|_ Traffic stats: 456.93 Kb sent, 39.49 Mb received
| snmp-netstat:
| TCP 0.0.0.0:22 0.0.0.0:0
| TCP 0.0.0.0:3366 0.0.0.0:0
| TCP 127.0.0.1:3306 0.0.0.0:0
| TCP 127.0.0.53:53 0.0.0.0:0
| UDP 0.0.0.0:161 *:*
| UDP 0.0.0.0:38577 *:*
|_ UDP 127.0.0.53:53 *:*
| snmp-processes:
| [... Вывод SNMP ...]
Service Info: Host: Mischief

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon Apr 1 16:30:03 2019 -- 1 IP address (1 host up) scanned in 143.80 seconds

Уже интереснее — есть SNMP-сервер на 161-м порте UDP и даже подробности, полученные скриптами Nmap. Хотя полный вывод опущен (от Nmap он длинный и не очень информативный), от этого можно смело оттолкнуться при поиске пути проникновения в систему.

Исследование SNMP — порт 161 UDP

Что такое SNMP? Статья в «Википедии» гласит:

SNMP (англ. Simple Network Management Protocol — простой протокол сетевого управления) — стандартный интернет-протокол для управления устройствами в IP-сетях на основе архитектур TCP/UDP. К поддерживающим SNMP устройствам относятся маршрутизаторы, коммутаторы, серверы, рабочие станции, принтеры, модемные стойки и другие. Протокол обычно используется в системах сетевого управления для контроля подключенных к сети устройств на предмет условий, которые требуют внимания администратора.

SNMP возглавляет составленный SANS Institute список «Common Default Configuration Issues» с вопросом изначальной установки строк сообщества на значения «public» и «private» и занимал десятую позицию в SANS Top 10 самых критических угроз интернет-безопасности за 2000 год.

То есть SNMP позволяет собирать и расшаривать информацию о том, что происходит на хостах в сети. Информация такого рода инкапсулируется в базу управляющей информации MIB (Management Information Base), а идентификаторы объектов OID (Object Identifiers) однозначно определяют записи в этой базе. К примеру, идентификатор 1.3.6.1.2.1.4.34 описывает сущность ipAddressTable (таблица IP-адресов), а 1.3.6.1.2.1.4.34.1.3 описывает сущность ipAddressIfIndex (индекс интерфейса).

MIB основана на нотации ASN.1 и используется для упрощения представления данных в формате, понятном человеку, следовательно, она не является необходимым компонентом SNMP. Ближайшая аналогия — DNS-сервер, резолвящий легко читаемые доменные имена в такие чуждые нам цифры IP-адресов.

Рассмотрим, как можно выжать информацию из службы SNMP штатными средствами Kali Linux.

Настройка snmpwalk

Для анализа информации, которую целевой хост предоставляет по SNMP, воспользуемся snmpwalk — стандартной утилитой для разведки SNMP в Linux.

Если запустить snmpwalk при базовых настройках, ничего, кроме непонятных для человеческого взгляда идентификаторов OID, мы не получим. Здесь на помощь приходит пакет snmp-mibs-downloader, который загружает и инсталлирует базу MIB. Установим его.

root@kali:~# apt install snmp-mibs-downloader -y

А затем разрешим использование MIB, закомментировав единственную значащую строку в /etc/snmp/snmp.conf.

Собираем дамп

С помощью snmpwalk сдампим весь трафик SNMP с указанием версии протокола 2c (самая распространенная) и строки сообщества public, которая, по сути, служит паролем по умолчанию для местного способа аутентификации.

root@kali:~# snmpwalk -v 2c -c public 10.10.10.92 | tee snmpwalk.out

Вывод массивный, поэтому он был перенаправлен в файл snmpwalk.out для дальнейшей работы.

К слову: если тебе нужно было оставаться более «бесшумным», было бы рационально запросить у snmpwalk только ту информацию, которая тебе нужна. Например, чтобы получить список запущенных процессов, достаточно уточнить запрос опцией hrSWRunName (OID 1.3.6.1.2.1.25.4.2.1.2).

root@kali:~# snmpwalk -v 2c -c public 10.10.10.92 hrSWRunName
HOST-RESOURCES-MIB::hrSWRunName.1 = STRING: "systemd"
HOST-RESOURCES-MIB::hrSWRunName.2 = STRING: "kthreadd"
HOST-RESOURCES-MIB::hrSWRunName.4 = STRING: "kworker/0:0H"
HOST-RESOURCES-MIB::hrSWRunName.6 = STRING: "mm_percpu_wq"
HOST-RESOURCES-MIB::hrSWRunName.7 = STRING: "ksoftirqd/0"
HOST-RESOURCES-MIB::hrSWRunName.8 = STRING: "rcu_sched"
HOST-RESOURCES-MIB::hrSWRunName.9 = STRING: "rcu_bh"
HOST-RESOURCES-MIB::hrSWRunName.10 = STRING: "migration/0"
HOST-RESOURCES-MIB::hrSWRunName.11 = STRING: "watchdog/0"
HOST-RESOURCES-MIB::hrSWRunName.12 = STRING: "cpuhp/0"
HOST-RESOURCES-MIB::hrSWRunName.13 = STRING: "kdevtmpfs"
HOST-RESOURCES-MIB::hrSWRunName.14 = STRING: "netns"
HOST-RESOURCES-MIB::hrSWRunName.15 = STRING: "rcu_tasks_kthre"
...

Список запущенных процессов

Вспомним, что мы видели питоновский HTTP-сервер на 3366-м порте TCP и он запрашивал авторизацию. Логин с паролем для такого сервака подаются питону в качестве аргументов командной строки в виде SimpleHTTPAuthServer [-h] [--dir DIR] [--https] port key, поэтому мы можем попробовать отыскать их в захваченном дампе.

Для этого среди записей типа hrSWRunName найдем процесс интерпретатора Python:

root@kali:~# cat snmpwalk.out | grep hrSWRunName | grep python
HOST-RESOURCES-MIB::hrSWRunName.593 = STRING: "python"

И далее по полученному индексу 593 выведем все, что относится к этому процессу в табличке hrSWRunTable.

root@kali:~# cat snmpwalk.out | grep 593
HOST-RESOURCES-MIB::hrSWRunIndex.593 = INTEGER: 593
HOST-RESOURCES-MIB::hrSWRunName.593 = STRING: "python"
HOST-RESOURCES-MIB::hrSWRunID.593 = OID: SNMPv2-SMI::zeroDotZero
HOST-RESOURCES-MIB::hrSWRunPath.593 = STRING: "python"
HOST-RESOURCES-MIB::hrSWRunParameters.593 = STRING: "-m SimpleHTTPAuthServer 3366 loki:godofmischiefisloki --dir /home/loki/hosted/"
HOST-RESOURCES-MIB::hrSWRunType.593 = INTEGER: application(4)
HOST-RESOURCES-MIB::hrSWRunStatus.593 = INTEGER: runnable(2)
HOST-RESOURCES-MIB::hrSWRunPerfCPU.593 = INTEGER: 1129
HOST-RESOURCES-MIB::hrSWRunPerfMem.593 = INTEGER: 13852 KBytes
HOST-RESOURCES-MIB::hrSWInstalledIndex.593 = INTEGER: 593
HOST-RESOURCES-MIB::hrSWInstalledName.593 = STRING: "tzdata-2018d-1"
HOST-RESOURCES-MIB::hrSWInstalledID.593 = OID: SNMPv2-SMI::zeroDotZero
HOST-RESOURCES-MIB::hrSWInstalledType.593 = INTEGER: application(4)
HOST-RESOURCES-MIB::hrSWInstalledDate.593 = STRING: 0-1-1,0:0:0.0

Запись hrSWRunParameters дает нам параметры запуска сервера -m SimpleHTTPAuthServer 3366 loki:godofmischiefisloki --dir /home/loki/hosted/, где находятся нужные нам авторизационные данные loki:godofmischiefisloki.

IPv6-адрес

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

root@kali:~# cat snmpwalk.out| grep hrSWRunName | grep apache
HOST-RESOURCES-MIB::hrSWRunName.770 = STRING: "apache2"
HOST-RESOURCES-MIB::hrSWRunName.2549 = STRING: "apache2"
HOST-RESOURCES-MIB::hrSWRunName.2550 = STRING: "apache2"
HOST-RESOURCES-MIB::hrSWRunName.2551 = STRING: "apache2"
HOST-RESOURCES-MIB::hrSWRunName.2552 = STRING: "apache2"
HOST-RESOURCES-MIB::hrSWRunName.2553 = STRING: "apache2"

При этом Nmap его не показал... Это может означать, что сервер крутится в мире IPv6, и было бы неплохо вытащить соответствующий IP-адрес, чтобы позже инициировать сканирование Nmap повторно (но на этот раз для IPv6-адреса).

root@kali:~# cat snmpwalk.out| grep ipAddressType | grep ipv6
IP-MIB::ipAddressType.ipv6."00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:01" = INTEGER: unicast(1)
IP-MIB::ipAddressType.ipv6."de:ad:be:ef:00:00:00:00:02:50:56:ff:fe:b9:7c:aa" = INTEGER: unicast(1)
IP-MIB::ipAddressType.ipv6."fe:80:00:00:00:00:00:00:02:50:56:ff:fe:b9:7c:aa" = INTEGER: unicast(1)

root@kali:~# ping6 -c2 dead:beef::0250:56ff:feb9:7caa
PING dead:beef::0250:56ff:feb9:7caa(dead:beef::250:56ff:feb9:7caa) 56 data bytes
64 bytes from dead:beef::250:56ff:feb9:7caa: icmp_seq=1 ttl=63 time=43.5 ms
64 bytes from dead:beef::250:56ff:feb9:7caa: icmp_seq=2 ttl=63 time=42.9 ms

--- dead:beef::0250:56ff:feb9:7caa ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 4ms
rtt min/avg/max/mdev = 42.922/43.214/43.507/0.358 ms

Видим маршрутизируемый IPv6-адрес de:ad:be:ef::02:50:56:ff:fe:b9:7c:aa и link-local IPv6-адрес fe:80::02:50:56:ff:fe:b9:7c:aa, которые, кстати, будут меняться при каждом ресете виртуалки.

EUI-64

Рассмотрим, как работает механизм автоматической генерации link-local IPv6-адреса на основе идентификатора EUI-64 из MAC-адреса на примере Mischief.

Для этого нам нужно находиться на одном канальном уровне (OSI layer 2) с тем хостом, адрес которого мы хотим узнать. Чтобы посмотреть, как это работает, залогинимся на другой виртуальной машине с Hack The Box, которая называется Hawk. Так как все запущенные инстансы находятся в одном логическом сегменте (виртуальной) сети 10.10.10.0/24, то мы можем достучаться с Hawk до Mischief. Другими словами, пусть Hawk станет нашим связующим звеном — Pivot Point.

Дадим пинг от Hawk до Mischief и запросим ARP-таблицу для того, чтобы вытащить MAC Mischief.

root@hawk:~$ ping 10.10.10.92
PING 10.10.10.92 (10.10.10.92) 56(84) bytes of data.
64 bytes from 10.10.10.92: icmp_seq=1 ttl=64 time=64.0 ms
^C
--- 10.10.10.92 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 64.063/64.063/64.063/0.000 ms

root@hawk:~$ arp -a
_gateway (10.10.10.2) at 00:50:56:aa:f1:dd [ether] on ens33
? (10.10.10.92) at 00:50:56:b9:7c:aa [ether] on ens33

Добыли MAC-адрес — 00:50:56:b9:7c:aa. Чтобы получить из него link-local IPv6-адрес, нужно провести следующие нехитрые манипуляции:

  1. Сгруппируем MAC в привычной для IPv6 форме, а именно по два октета — 0050:56b9:7caa.
  2. В начало MAC допишем fe80::fe80::0050:56b9:7caa.
  3. В середину MAC вставим ff:fefe80::0050:56ff:feb9:7caa.
  4. Инвертируем шестой бит MAC — fe80::0250:56ff:feb9:7caa (было 0000 0000, стало 0000 0010, или 0x02).
  5. Через символ процента укажем интерфейс (так как в мире IPv6 адреса привязываются к интерфейсам, а не к узлам и, если не указать интерфейс, трафик не будет знать, куда ему идти) — fe80::0250:56ff:feb9:7caa%ens33.

Проверяем, что получилось.

root@hawk:~$ ping6 -c4 fe80::0250:56ff:feb9:7caa%ens33
PING fe80::0250:56ff:feb9:7caa%ens33(fe80::250:56ff:feb9:7caa%ens33) 56 data bytes
64 bytes from fe80::250:56ff:feb9:7caa%ens33: icmp_seq=1 ttl=64 time=136 ms
64 bytes from fe80::250:56ff:feb9:7caa%ens33: icmp_seq=2 ttl=64 time=0.236 ms
64 bytes from fe80::250:56ff:feb9:7caa%ens33: icmp_seq=3 ttl=64 time=0.240 ms
64 bytes from fe80::250:56ff:feb9:7caa%ens33: icmp_seq=4 ttl=64 time=0.272 ms

--- fe80::0250:56ff:feb9:7caa%ens33 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3031ms
rtt min/avg/max/mdev = 0.236/34.259/136.290/58.907 ms

It’s alive! Теоретически можно было бы продолжать прохождение через проксирование Hawk, реализуя схему Proxy Pivoting, если бы была такая необходимость. Но к счастью, нас ждет другой путь.

WWW

Еще несколько инструментов для взаимодействия с SNMP.

  • snmp-check — «из коробки» дает читабельный (но не самый подробный) результат без необходимости ставить MIB. Входит в состав Kali Linux.
  • onesixtyone — утилита для брутфорса строк сообщества. Пригодилась бы нам, если бы дефолтная строка public не подошла.
  • enyx — небольшой скрипт на Python, позволяющий по SNMP узнать IPv6-адрес хоста в одно действие. Интересно, что написал скрипт создатель самой ВМ Mischief.

Продолжение доступно только участникам

Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», увеличит личную накопительную скидку и позволит накапливать профессиональный рейтинг Xakep Score! Подробнее

Вариант 2. Открой один материал

Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.


snovvcrash: Безопасник, временами питонщик, местами криптоана(рхист)литик, по необходимости системный администратор

Комментарии (5)

  • Тоже подумал об опросе в колонке главреда и отметил про себя тогда, что хочется больше именно такого. Очень круто!

  • Статья супер! К слову о статье(опросе) от главреда - Вот ТАКИХ статей надо больше!

  • Качественная статья. Видно затраченное время и усилия, все подробно, как в мануале. Спасибо!