A primeira ação para reconhecimento do alvo é testar a conectividade com ele, utilizando o comando ping. Ao enviar um pacote ICMP, podemos confirmar se a máquina alvo está acessível na rede.
No retorno, o TTL de 63 é uma indicação de que possivelmente estamos lidando com um sistema Linux. O TTL inicial para sistemas Linux costuma ser 64, e o valor observado no ping geralmente é reduzido conforme os pacotes atravessam roteadores na rede. Como o TTL está próximo de 64, é provável que o sistema alvo seja uma máquina Linux.
──╼#ping -c 1 10.10.11.221PING10.10.11.221 (10.10.11.221) 56(84) bytes of data.64bytesfrom10.10.11.221:icmp_seq=1ttl=63time=164ms---10.10.11.221pingstatistics---1packetstransmitted,1received,0%packetloss,time0msrttmin/avg/max/mdev=163.925/163.925/163.925/0.000ms
Varredura de Portas e Serviços
Em seguida, realizei um scan para identificar as portas TCP abertas na máquina:
O scan revelou duas portas abertas: 22 (SSH) e 80 (HTTP). Para obter mais informações sobre os serviços que estão sendo executados nessas portas, executei um scan mais detalhado:
O serviço SSH está rodando OpenSSH 8.9p1 e o serviço HTTP está rodando Nginx. O título da página HTTP sugere que o site pode estar configurado para redirecionar para http://2million.htb/.
Para verificar o funcionamento do redirecionamento, adicionei o domínio ao arquivo /etc/hosts, apontando-o para o IP da máquina alvo.
A análise indicou que o site está rodando Nginx como servidor web e utiliza cookies PHP (PHPSESSID).
O título da página sugere que o site é uma versão antiga do Hack The Box.
Detecção de Tecnologias
Utilizei o wappalyzer para identificar tecnologias utilizadas no site. O scan revelou que o site está utilizando a biblioteca JavaScript "toaster" versão 2.1.2.
Web Crawling
Através do gospider, identifiquei as principais páginas do site:
Este script pode ser um ponto importante para entender a lógica de validação dos códigos de convite e potencialmente encontrar vulnerabilidades ou informações sensíveis.
Pesquisando sobre o assunto, encontrei um blog que descreve um processo semelhante de obfuscação de JavaScript.
Usando a ferramenta Beautifier.io, conseguimos desofuscar o código JavaScript para uma forma mais legível:
O código possui duas funções principais: verifyInviteCode e makeInviteCode. A função verifyInviteCode envia uma solicitação POST para verificar um código de convite, enquanto a função makeInviteCode solicita a geração de um novo código de convite.
Decodificação e Geração de Código de Convite
Utilizei o Postman para enviar uma requisição POST para http://2million.htb/api/v1/invite/how/to/generate. A resposta contém um código encriptado em ROT13:
{"0":200,"success":1,"data": {"data":"Va beqre gb trarengr gur vaivgr pbqr, znxr n CBFG erdhrfg gb /ncv/i1/vaivgr/trarengr","enctype":"ROT13" },"hint":"Data is encrypted ... We should probbably check the encryption type in order to decrypt it..."}
Decodificando o texto ROT13 usando rot13.com, obtivemos a seguinte mensagem:
“In order to generate the invite code, make a POST request to /api/v1/invite/generate”
Enviei uma requisição POST para /api/v1/invite/generate e obtive um código de convite:
Com o código de convite decodificado, consegui acessar o painel de registro:
Após criar a conta com sucesso, conseguimos fazer login na aplicação:
Com o login efetuado, conseguimos acessar a página principal do site:
Enumeração Web Autenticado
Após o login, a aplicação exibe algumas páginas acessíveis, mas nenhuma delas parece revelar informações particularmente sensíveis. Entre as páginas disponíveis, há uma para gerar a VPN de acesso aos laboratórios:
Fuzzing na API
Explorando a API da aplicação, observei que a rota /api fornece a versão da API:
A rota /api/v1 lista diversas rotas e, curiosamente, inclui endpoints para administração:
{"v1": {"user": {"GET": {"/api/v1":"Route List","/api/v1/invite/how/to/generate":"Instructions on invite code generation","/api/v1/invite/generate":"Generate invite code","/api/v1/invite/verify":"Verify invite code","/api/v1/user/auth":"Check if user is authenticated","/api/v1/user/vpn/generate":"Generate a new VPN configuration","/api/v1/user/vpn/regenerate":"Regenerate VPN configuration","/api/v1/user/vpn/download":"Download OVPN file" },"POST": {"/api/v1/user/register":"Register a new user","/api/v1/user/login":"Login with existing user" } },"admin": {"GET": {"/api/v1/admin/auth":"Check if user is admin" },"POST": {"/api/v1/admin/vpn/generate":"Generate VPN for specific user" },"PUT": {"/api/v1/admin/settings/update":"Update user settings" } } }}
Para verificar o status de autenticação, consultei a rota /api/v1/user/auth. O usuário autenticado, no caso, é bruno, mas ele não possui privilégios administrativos.
Explorando a Rota Admin
Observando a rota /api/v1/admin/vpn/generate, notei que inicialmente recebi uma mensagem de erro indicando um content-type inválido:
O erro ocorre porque o Content-Type da requisição não está especificado. Como estou trabalhando com JSON, defini o cabeçalho Content-Type como application/json para ver o que aconteceria.
Ao especificar o Content-Type, a resposta da API agora indicou que o parâmetro email estava faltando:
Isso fez sentido, pois estava tentando atualizar uma configuração de administrador e precisava fornecer o e-mail associado.
Adicionei o parâmetro email ao corpo da requisição. No entanto, recebi uma nova mensagem informando que o parâmetro is_admin estava faltando, o que provavelmente é necessário para promover a conta a administrador:
Como verifiquei anteriormente, minha conta tinha a flag is_admin definida como 0. Então, tentei definir o valor para 1 e realizar a requisição novamente.
A alteração foi bem-sucedida:
Para confirmar, utilizei a rota /api/v1/admin/auth e verifiquei que minha conta agora tinha privilégios administrativos:
Geração da VPN
Com a conta agora configurada como administradora, testei a rota /api/v1/admin/vpn/generate para gerar uma configuração de VPN para um usuário específico. A API inicialmente solicitou que eu especificasse o parâmetro username:
Adicionei o parâmetro username e consegui gerar o arquivo de configuração VPN com sucesso:
Para confirmar que a injeção de comandos estava funcionando, enviei um ping para minha máquina atacante e capturei a comunicação com o comando tcpdump -i tun0. O alvo respondeu ao ping, o que confirmou que os comandos estavam sendo executados:
Após obter o acesso inicial com o shell reverso, o próximo passo foi melhorar a interatividade da shell para garantir que tivéssemos um controle mais robusto sobre o sistema alvo.
Gerando um console bash em segundo plano: Para garantir que tivéssemos um bash funcional, foi necessário criar um processo de bash através do comando script, que abre uma sessão interativa mais apropriada, logo após, colocamos em background com CTRL+Z:
Verificando as dimensões do terminal na máquina atacante: Antes de ajustar o terminal no alvo, verificamos as dimensões da janela na máquina atacante para mantê-las iguais:
Com o terminal configurado, explorei o sistema e descobri que existe o usuário admin. No diretório /home/admin, encontrei a user.txt, mas não consegui acessá-la devido às permissões:
Para conseguir acessar o conteúdo da user.txt do usuário admin, precisava migrar de www-data para o usuário admin. Com a shell privilegiada e as permissões corretas ajustadas, poderia então explorar possíveis caminhos para a escalada de privilégios e o acesso completo ao sistema.
Migração de Usuário
Enquanto explorava o diretório web root, encontrei um arquivo .env que continha variáveis de ambiente importantes, incluindo um nome de usuário e uma senha para o banco de dados:
Com essas credenciais em mãos, resolvi testar a senha para ver se o usuário admin a reutilizava em outras partes do sistema. Para minha surpresa, a senha era reutilizada para o login no sistema. Assim, consegui mudar para o usuário admin e finalmente acessar o conteúdo da user.txt:
www-data@2million:~/html$suadminPassword:SuperDuperPass123Torunacommandasadministrator (user "root"), use "sudo <command>".See"man sudo_root"fordetails.admin@2million:/var/www/html$cat/home/admin/user.txtb638f81c2686dc87e6af4a8671cb6560
Escalada de Privilégios
Para validar ainda mais o acesso, tentei usar essas mesmas credenciais para acessar o sistema via SSH, e funcionou. Logo após o login, notei uma notificação que indicava a presença de um email para o usuário admin:
Resolvi investigar as aplicações de email para verificar a mensagem que me foi notificada. Encontrei os arquivos de email em /var/mail/admin e /var/spool/mail/admin.
Ao visualizar o conteúdo, descobri um email importante:
admin@2million:~$cat/var/mail/adminFrom:ch4p<ch4p@2million.htb>To:admin<admin@2million.htb>Cc:g0blin<g0blin@2million.htb>Subject:Urgent:PatchSystemOSDate:Tue,1June202310:45:22-0700Message-ID:<9876543210@2million.htb>X-Mailer:ThunderMailPro5.2Heyadmin,I'm know you're working as fast as you can to do the DB migration. While we're partially down, can you also upgrade the OS on our web host? There have been a few serious Linux kernel CVEs already this year. That one in OverlayFS / FUSE looks nasty. We can't get popped by that.
HTBGodfather
O email mencionava uma vulnerabilidade crítica no kernel Linux, especificamente relacionada ao OverlayFS e FUSE. Buscando mais informações, encontrei um artigo da Datadog explicando como essa vulnerabilidade funciona e um repositório no GitHub com um exploit funcional.
Compilei e executei o exploit para obter acesso root:
Compilação do exploit:
O comando make all foi usado para compilar os arquivos fonte fuse.c, exp.c, e getshell.c, criando os binários fuse, exp, e gc. Esses binários são necessários para explorar a vulnerabilidade.
Durante a compilação, foram geradas algumas advertências, mas elas não impediram a criação dos executáveis necessários.
admin@2million:/tmp/.../CVE-2023-0386$makeallgccfuse.c-ofuse-D_FILE_OFFSET_BITS=64-static-pthread-lfuse-ldlfuse.c:Infunction‘read_buf_callback’:fuse.c:106:21: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘off_t’ {aka ‘long int’} [-Wformat=]
106|printf("offset %d\n",off);|~^~~~||||intoff_t{akalongint}|%ldfuse.c:107:19: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘size_t’ {aka ‘long unsigned int’} [-Wformat=]
107|printf("size %d\n",size);|~^~~~~||||intsize_t{akalongunsignedint}|%ldfuse.c:Infunction‘main’:fuse.c:214:12: warning: implicit declaration of function ‘read’; did you mean ‘fread’? [-Wimplicit-function-declaration]
214|while (read(fd,content+clen,1) >0)|^~~~|freadfuse.c:216:5: warning: implicit declaration of function ‘close’; did you mean ‘pclose’? [-Wimplicit-function-declaration]
216|close(fd);|^~~~~|pclosefuse.c:221:5:warning:implicitdeclarationoffunction‘rmdir’ [-Wimplicit-function-declaration]221|rmdir(mount_path);|^~~~~/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libfuse.a(fuse.o): in function `fuse_new_common':
(.text+0xaf4e): warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
gcc-oexpexp.c-lcapgcc-ogcgetshell.c
Execução do binário fuse:
O comando ./fuse ./ovlcap/lower ./gc & foi usado para montar um sistema de arquivos vulnerável com as permissões necessárias para a exploração.
Esse processo fica em segundo plano, permitindo que o exploit manipule as permissões de arquivos e diretórios.
Finalmente, executei ./exp para explorar a vulnerabilidade. O exploit montou um sistema de arquivos e manipulou permissões para criar um arquivo file com permissões de root, permitindo a execução de comandos como root.
Após a execução do exploit, a escalada de privilégios foi bem-sucedida, concedendo acesso como root.
admin@2million:/tmp/.../CVE-2023-0386$ [+] len of gc: 0x3ee0./expuid:1000gid:1000[+] mount success[+] readdir[+] getattr_callback/filetotal8drwxrwxr-x1rootroot4096Sep2116:35.drwxr-xr-x6rootroot4096Sep2116:35..-rwsrwxrwx1nobodynogroup16096Jan11970file[+] open_callback/file[+] read buf callbackoffset0size16384path/file[+] open_callback/file[+] open_callback/file[+] ioctl callbackpath/filecmd0x80086601[+] exploit success!Torunacommandasadministrator (user "root"), use "sudo <command>".See"man sudo_root"fordetails.
Com o sucesso do exploit, consegui escalar meus privilégios e me tornar root. Finalmente, acessei a flag root: