quarta-feira, 2 de janeiro de 2013

Exploração automatizada com Nmap Scripting Engine (NSE)


Introdução


Este artigo visa demonstrar o quão poderosa e flexível é a Engine de script do Nmap. O exemplo utilizado neste artigo é inteiramente didático, apesar de explorar uma falha publicamente conhecida. Nem a Conviso® nem o autor se responsabilizam pelo mau uso do material aqui apresentado.

O principal objetivo desta engine é automatizar uma série de tarefas de rede, através do uso de uma linguagem de domínio especifico, que facilita a elaboração de scripts que realizam atividades relacionadas a teste de segurança em redes. 

A base do NSE ( Nmap Scripting Engine )[1] é um interpretador LUA embutido na ferramenta. LUA[2] é uma linguagem interpretada, multiparadigma, fortemente tipada e que tem como foco legibilidade, foi projetada para expandir aplicações em geral. LUA foi desenvolvida por um time de desenvolvedores da PUC-RIO, a principio apenas para um projeto específico e devido a sua eficiência passou a ser usada em diversos ramos da programação. O Adobe Photoshop Lightroom, o jogo Angry Birds, o nmap entre outros, já usam LUA como uma extensão dos seus aplicativos[3].

Tanto o manual do NSE[9] quanto o livro Nmap Exploration and Security auditing Cookbook[10] são ótimas referências para desenvolver os scripts. O próprio NMAP possui um script protótipo[12] que pode ser usado como exemplo para desenvolvimento dos scripts

ShellCode

Para demonstrar a flexibilidade do NSE sem criar um manual de how-to, foi utilizado o artigo[4] do pesquisador Maycon Vitali onde fez uma excelente análise da falha publicada no CVE-2012-5905[5]. No experimento descrito no artigo, Maycon  demonstra a execução de código remoto em uma falha que descrevia apenas a possibilidade de negação de serviço.

Na análise, Maycon executou um código remoto como demonstração e provou que muitas vezes as falhas em softwares publicadas não representam o real problema existente na aplicação. Neste artigo você verá este real problema, pois o código executado remoto é um bind shell.

Adaptação do exploit


O exploit original[6] foi escrito em python e explora o serviço KnFTPD[7] no WinXP SP3 – English, e em minha estrutura utilizei WinXP SP3 – pt-BR, para isso foi necessário adaptar o exploit. Foi necessário apenas alterar o endereço que contém a instrução JMP ESP do WinXP pt-BR.

O Ollydbg[8] foi utilizado para localizar o endereço de alguma instrução "JMP ESP" em alguma biblioteca que já estivesse carregada em memória. Achamos o código procurado e foi encontrado dentro da kernel32.dll no endereço 0x7C86467B".

Ollydbg - JMP ESP


No exploit foi alterado o endereço utilizado no método struct.pack, que codifica o endereço de hexadecimal para binário, ordenado no padrão (little-endian), que armazena o dado do último byte para o primeiro.

Endereço JMP ESP alterado no exploit


Assim o exploit original, que abre a calc.exe passa a funcionar na minha estrutura.

O próximo passo foi sair da prova de conceito e provar o quão perigosa é este tipo de falha, ganhando a shell da máquina.

Foi utilizado o shellcode bindshell [9] que possui 214 bytes e coube nos 284 bytes que tínhamos disponível de acordo com a vulnerabilidade explorada. A Imagem abaixo mostra o shellcode mencionado.

Shellcode


Podemos notar de acordo com a imagem abaixo que a vulnerabilidade foi explorada abrindo a porta 28876.

Porta 28876 aberta no host alvo, exploit executado com sucesso


Ao estabelecermos conexão com o host na porta 28876, que foi aberta pelo exploit no host alvo, bingo!

Shell do host alvo


Automatizando com NSE


A ideia é mostrar o quanto a Engine de Script do Nmap é flexível e eficaz para os mesmos propósitos descritos na sessão anterior, para isso foi criado um script NSE, que ao encontrar um KnFTPD em execução ele executa os seguintes passos: (I) monta e envia o payload, (II) executa o Egg-hunter[19], (III) checa se o exploit foi executado com sucesso e se a máquina foi realmente explorada, mostrando o IP e Porta que foi aberta caso a máquina tenha sido explorada. A porta fica em listen enquanto o serviço estiver aberto apenas aguardando a conexão, ao se conectar bingo! está com a shell da máquina exatamente como foi mostrado com o exploit em pyhton, só que automatizado pelo Nmap.

Script NSE

Segue abaixo o cabeçalho do script, que contém a descrição e a referência, logo depois as tags @usage e @output que descreve como devemos utilizar este script e o exemplo da saída do script. Também o autor a licença e a categoria, conforme pede a documentação do NSE. É importante definir corretamente em qual categoria seu script se enquadra, pois faz parte da usabilidade do nmap, ao executar um scan com script é possível escolher rodar todos os scripts de uma determinada categoria. As categorias e suas descrições encontram-se na referência [13].

Inicio do script knftpd-exploit.nse


LUA, como a maioria das linguagens, permite a criação de bibliotecas com um grupo de funções comuns. Para este script foi utilizado as bibliotecas abaixo. As bibliotecas existentes podem ser consultadas a partir da referência [14].

Bibliotecas


Para um script NSE ser executado é necessário definirmos uma regra e a condição dessa regra retorne verdadeiro. A regra é uma função LUA que possui condições que retornam verdadeiro ou falso e pode ser definida como: prerule, hostrule, portrule e postrule.

Neste script foi utilizado a regra portrule, que tem como condição o IP, porta e/ou o estado da porta como open, filtered ou unfiltered. O script é executado quando a condição definida na regra for verdadeira. Para funções mais comuns como um serviço em execução ou uma porta aberta, geralmente utilizamos a biblioteca shortport[16] como foi usada neste artigo. Mais detalhes sobre as rules, você encontra na referência[15].

Rule Portrule declarada


Em seguida foi definido a função action, que é o coração do NSE pois contém todas as instruções que serão executadas, a action só é executada quando o critério definido na rule for verdadeiro, no nosso caso usamos a rule portrule, e o nosso critério é encontrar um serviço FTP rodando no host alvo.

Função Action declarada


Foi definido que a ação do script tem como parâmetro, o host e a porta. A seguir veremos o desenvolvimento da ação do script.

A variável shellcode foi declarada em hexadecimal, isto é necessário para montagem do payload.

Shellcode


Declarando a variável payload, esta parte do script foi a mais importante, destaco duas bibliotecas, a stdnse[17] com a função tohex, está função codifica uma string ou um número em hexadecimal e a biblioteca bin[18] com a função pack, que codifica o dado para binário, no nosso caso o dado está em hexadacimal e será codificado para binário. Ambas são cruciais para o exploit funcionar.

Detalhes do exploit está na pesquisa[4], para não prolongar muito o artigo não vou entrar na questão da construção do exploit, vou explicar as funções que foram utilizadas para escrever o script em LUA.

O formato utilizado segue o mesmo padrão do que foi utilizado no exploit do Maycon[6], [nops] + [shellcode] + [return address] + [padding] + [tag Egg-hunter (w00tw00t)]. Apenas tive que fazer algumas conversões para hexadecimal e binário, utilizando as bibliotecas stdnse e bin.

Montagem do Payload


E para finalizar o payload, foi declarado o endereço de retorno e o Egg-hunter.

Endereço de retorno e o Egg Hunter


Foi criado um socket tcp, conectando no host e porta que foi escaneado e verificamos se o serviço ftp é o KnFTPD, se não for o script não é executado, se for o script continua e envia o exploit para a máquina alvo.

Há um detalhe interessante nessa parte do script, precisamos enviar uma string de 7 carácteres junto com o payload para que o exploit seja executado, para isso foi criado a string de forma randômica, o que evita que o exploit seja bloqueado por algum IDS, WAF ou algum ativo que use pattern matching. A string foi gerada com a lib stdnse[17] com a função generate_random_string.    

Abrindo o socket e enviando o exploit


Após a execução do exploit, foi verificado se a máquina foi realmente explorada, conectando no host alvo na porta 28876, o script tenta durante 10 segundos se conectar ao host, se não conectar, não foi aberto o socket e o host não foi explorado.

Validando se o host foi explorado


Sendo assim o usuário receberá uma mensagem dizendo, “Command shell hasn't been opened!”

Mensagem no script para o host não explorado

Saída do payload enviado e host não explorado


E finalmente, se o socket na porta 28876 estiver aberto o usuário receberá a mensagem “Command shell has been opened. (<host>:28876).

No script saída de host explorado

Saída do payload enviado e host explorado


Conforme o exemplo, basta se conectar no IP 192.168.208.138 na porta 28876 para ganhar a shell da máquina.

Shell do host


O script completo pode ser baixado na referência[18].

Conclusão


Lua é uma linguagem simples de programar, leve e muito flexível, junto com o Nmap as possibilidades de automação são infinitas. O Nmap está na versão 6.25 e possui 433 scripts, caso precise de algum script customizado para alguma demanda específica, como vocês viram, é simples criar scripts que atendam necessidades pontuais.

Gostaria de agradecer ao Maycon Vitali pelo apoio no shellcode e ao Ulisses Castro pela ajuda durante a pesquisa e desenvolvimento deste artigo.

Referência:


[20] http://projectshellcode.com/?q=node/23



Nenhum comentário:

Postar um comentário