Editorial
#easy #HackTheBox #Linux #OSCP #eWPT #BSCP
Reconhecimento de Rede
Verificando a conectividade via Ping
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.
Varredura de Portas e Serviços
Em seguida, executei um escaneamento de porta no alvo para identificar quaisquer portas abertas que pudessem ser usadas para novos ataques. Usei a ferramenta nmap para verificar todas as portas (1-65535) do alvo e garantir que nenhum serviço passasse despercebido. Além disso, habilitamos a detecção de versões de serviços e scripts para ter mais informações detalhadas.
-sSCV: Realiza a descoberta de portas com SYN Scan, e em seguida escaneia as portas abertas, detectando versões de serviços e usando scripts do nmap.-p-: Escaneia todas as portas possíveis (1-65535).--min-rate=5000: Define uma taxa mínima de pacotes para acelerar a varredura.-vv: Mostra mais detalhes durante o processo de varredura.-n: Desabilita a resolução de DNS para acelerar o processo.-Pn: Não realiza o ping, útil em casos de firewalls ou filtros ICMP.-oNe-oX: Salvam os resultados em arquivos de texto e XML, respectivamente, para posterior análise.
A saída da varredura identificou duas portas abertas:
22/tcp: OpenSSH 8.9p1, usado para conexões SSH.
80/tcp: nginx 1.18.0, serviço HTTP.
Exploração HTTP
Ao investigar a porta 80, notamos que o site responde com o domínio editorial.htb. Esse domínio não é automaticamente resolvido pelo sistema, então é necessário adicionar manualmente o mapeamento no arquivo /etc/hosts para permitir que possamos acessá-lo corretamente.
Após isso, podemos testar a comunicação com o domínio:
Agora, estamos prontos para interagir com o site.
O próximo passo é entender mais sobre o site rodando no servidor web. Utilizamos a ferramenta httpx para extrair informações sobre o servidor HTTP, como o status do servidor, tipos de conteúdo, e tecnologias utilizadas.
A saída mostra:
Status 200, o que indica que a página está acessível.
O título da página é Editorial Tiempo Arriba.
O servidor está rodando nginx 1.18.0 no Ubuntu.
Acessando o site no navegador, a página inicial sugere que é o site de uma loja editorial de livros. Nesse momento, temos uma visão geral da funcionalidade básica do site.

Para garantir que não estamos perdendo páginas ocultas ou recursos não documentados, usamos a ferramenta gospider para varrer o site em busca de links, formulários e outras possíveis páginas de interesse.
A enumeração revelou alguns recursos importantes:
/upload: Uma página de envio de arquivos, que pode ser explorada para uploads maliciosos./about: Uma página com informações sobre o site, incluindo um e-mail de contato (submissions@tiempoarriba.htb).Detalhes de livros e autores, que podem ser úteis para a criação de dicionários de ataque se necessário.
Esses recursos são essenciais para planejar a próxima etapa da exploração, especialmente o ponto de upload de arquivos, que pode permitir uma superfície de ataque caso nós como usuários tivermos controle sob o que pode ser enviado.
Exploração do Upload de Arquivos
Geralmente, funcionalidades de upload podem ser exploradas para comprometer um servidor, dependendo de como o upload é tratado e quais restrições existem para o tipo de arquivos aceitos. Decidi explorar essa funcionalidade para entender se haveria uma maneira de usá-la como vetor de ataque.
Primeiro Teste: Upload e Imagem
Para começar, eu realizei um teste básico com um arquivo de imagem legítimo, verificando como o sistema processa uploads normais. Baixei uma imagem qualquer para isso:
A partir deste ponto, abri o Burp Suite para monitorar e capturar todas as requisições relacionadas ao upload, a fim de observar o comportamento da aplicação.

Comportamento da Aplicação
A página de upload permite ao usuário fazer o upload de uma imagem diretamente de seu sistema ou fornecer uma URL para que o servidor faça o download da imagem. Aqui, temos dois possíveis caminhos para manipulação:
Upload de arquivo local: Enviar um arquivo malicioso a partir do meu sistema.
Upload via URL: Enviar uma URL que aponte para um servidor controlado por mim, permitindo monitorar as interações.
Decidi começar com o segundo caminho, fornecendo uma URL controlada para verificar se a aplicação buscaria a imagem diretamente do meu servidor. Para isso, iniciei um servidor HTTP simples na minha máquina de ataque, usando Netcat:
Ao fornecer a URL do meu servidor na página de upload e clicar em "Preview", observei que a aplicação realmente faz uma requisição para baixar a imagem diretamente do meu servidor.

Aqui está o tráfego capturado com Netcat:
Aqui podemos observar que o alvo fez um GET request para minha máquina, confirmando que o servidor está buscando arquivos de URLs externas.
Entendendo a Requisição
No Burp Suite, pude analisar mais detalhadamente o comportamento da aplicação. A requisição ao servidor é enviada para o endpoint /upload-cover, como mostrado abaixo:

A imagem foi carregada no servidor, e a resposta indica que o arquivo foi armazenado em um caminho específico. O servidor retorna um identificador gerado aleatoriamente, o qual pode ser acessado no seguinte diretório:
Agora, podemos acessar a imagem através da URL http://editorial.htb/static/uploads/b58eacb7-8ce2-4f35-ae82-eb6b66fb3e8b.

Além disso, analisando a resposta da uma página "Not Found" que é rotornada, percebi que a aplicação parece ser baseada no framework Flask. Isso pode ser útil em futuras explorações, uma vez que o Flask tem comportamentos previsíveis e vulnerabilidades bem documentadas.
A resposta é típica de Flask, o que reforça a suposição:

Explorando Vulnerabilidade SSRF
Observei que, ao alterar a URL de upload para localhost, a aplicação demorou consideravelmente para responder, indicando que uma tentativa de resolução da URL ocorreu. Em contraste, URLs não existentes (como "haxor") retornaram instantaneamente, sugerindo que não houve resolução. Isso sugere que a aplicação pode estar vulnerável a SSRF.
O teste com localhost foi realizado da seguinte forma:
O status da resposta foi 200 OK, e a aplicação demorou cerca de 20 segundos para responder. Isso indica que a aplicação tentou resolver a URL e possivelmente se conectou ao serviço em localhost.

Já, ao colocar URLs não existentes como "http://haxor", a resposta continua a ser um 200 OK, porém não houve demora na resposta.

Teste com Portas
Similarmente, ao testar portas:
Porta 80:
Requisição:
http://localhost:80Demora: ~20 segundos

Porta 1:
Requisição:
http://localhost:1Demora: ~48 milissegundos (resposta rápida)

Isso reforça a hipótese de que a aplicação está vulnerável a SSRF, já que a demora sugere uma conexão tentada.
Enumeração de Portas via SSRF
Agora, podemos explorar a possibilidade de um port discovery via SSRF. O objetivo é verificar quais portas estão abertas, utilizando o tempo e o conteúdo da resposta como indicativo.
O comando curl será utilizado com a opção -w para medir o tempo de resposta:
-o /dev/null: descarta o conteúdo da resposta, para que ele não apareça no terminal.-s: modo silencioso, para suprimir as mensagens de progresso.-w: personaliza a saída, mostrando o tempo total da requisição usando otime_total.
Utilizaremos o xargs para fazer uma requisição em paralelo a múltiplas portas e registrar o tempo de resposta. A ideia é criar um oneliner que faça requisições em massa.
P100: Realiza até 100 requisições em paralelo.I@: Substitui@pelo número da porta na URL.
Os resultados devem ser armazenados em um arquivo ou visualizados no terminal, permitindo que você identifique rapidamente quais portas apresentaram uma resposta lenta. As portas que demoram mais para responder são as que potencialmente estão abertas no serviço alvo.
O primeiro passo foi copiar uma requisição HTTP do burp para ser utilizada com curl (selecionar “Copy as curl command (bash)”). A ideia é adaptar o comando para incluir o tempo da requisição, o que facilita a análise das portas.
Usamos a flag -w para exibir o tempo total de resposta (%{time_total}), e capturamos o comprimento da resposta (%{size_download}), o que nos permite observar diferenças de comportamento nas respostas.
Porta 1: a requisição foi rápida (0.094s) e retornou uma resposta com tamanho pequeno, indicando que não há nada significativo rodando nessa porta além da imagem padrão da aplicação:
Porta 80: como já haviamos visto com
burp, a requisição demorou consideravelmente mais (~20 segundos), sugerindo que algo significativo pode estar rodando nessa porta. Este comportamento diferente é um indicativo para focarmos mais nela:
Para agilizar o processo, paralelizamos a execução dos comandos usando o xargs para testar várias portas simultaneamente. Aqui, testamos as portas 79, 80 e 81. Como esperado, a porta 80 teve um tempo de resposta muito maior (~20s) em relação às outras, demonstrando que nosso oneliner funciona como esperávamos, trazendo a porta escaneada, o tamanho de resposta, e quanto tempo tardou em responder:
Limitando a busca a portas específicas
Embora seja possível tentar escanear todas as 65.535 portas disponíveis, isso seria ineficiente e poderia sobrecarregar o servidor alvo. Como alternativa, optamos por limitar a busca às top 100 portas mais comuns que o Nmap escanearia em uma análise padrão de portas TCP.
A ideia aqui é que temos um total de 65.535 portas, mas realizar uma varredura em todas elas não é viável para esse contexto. Isso não apenas poderia resultar em um grande número de requisições e, consequentemente, sobrecarregar o servidor alvo, como também aumentaria o risco de um bloqueio devido ao volume de acessos. Além disso, isso poderia consumir tempo desnecessário, sem garantir que resultados relevantes sejam encontrados rapidamente.
Portanto, precisamos ser estratégicos na escolha das portas a serem escaneadas. Existem várias maneiras de limitar essa busca:
Escanear as portas mais frequentemente utilizadas por serviços específicos (como portas para serviços web: 80, 443, 8080, 8443, entre outras).
Buscar apenas portas associadas a determinados serviços, como aquelas mais comuns em servidores Ubuntu ou em aplicações Python/Flask, tecnologias que identificamos no alvo.
Usar uma lista pré-definida das portas mais escaneadas por ferramentas como o Nmap.
Nesse caso, decidi optar pelas top 100 portas TCP que o Nmap usa por padrão em um de seus modos de escaneamento. Essa abordagem permite focar em portas que são amplamente conhecidas por hospedar serviços comuns, maximizando as chances de sucesso sem sobrecarregar o alvo.
Gerando uma lista das top 100 portas
Para gerar essa lista, eu utilizei o Nmap para escanear as top 100 portas no localhost da minha máquina e salvei o resultado em um arquivo, para facilitar o processamento e reutilização posterior.
O arquivo de saída, top100.ports, contém as portas escaneadas:
Com isso, o próximo passo foi extrair as portas em formato utilizável. Algumas delas estavam em intervalos (ex: 21-23), então precisei expandir esses intervalos.
Para extrair os intervalos e convertê-los em uma lista de portas individuais, usei um comando para buscar os intervalos presentes no arquivo e expandi-los com a ajuda de seq. Esse comando gera as portas em sequência e as salva no arquivo ports.txt:
Após executar o comando, o conteúdo do arquivo ports.txt ficou assim:
Ainda faltavam algumas portas listadas individualmente no arquivo do Nmap. Para resolver isso, utilizei um segundo comando para capturar essas portas isoladas e adicioná-las ao arquivo ports.txt:
Agora, ports.txt contém as 99 portas mais comuns que o Nmap utilizaria em um escaneamento básico:
Execução do Port Scan
Finalmente executamos nosso oneliner. O objetivo é observar o tempo de resposta e o tamanho da resposta retornada. Essas informações podem indicar se a porta está aberta ou se há algo relevante rodando nessa porta.
Filtragem de resultados
Primeiro, agrupamos os resultados com base no tempo de resposta:
O resultado indica que a maioria das portas responde em menos de 1 segundo, com exceção de duas (porta 80, como já sabíamos, e porta 9):
Em seguida, analisamos o tamanho da resposta:
O resultado revela algumas respostas diferentes:
167 bytes: Em oito portas.
71 bytes: Em uma porta.
0 bytes: Em uma porta (indicando uma resposta vazia).
Observação com Burp Suite
Após identificar essas portas, passei a analisá-las usando o Burp Suite para confirmar se havia respostas válidas ou apenas falsos positivos. A porta 5000 chamou atenção ao retornar uma resposta diferente das demais. Ao tentar acessá-la, percebi que havia um comportamento semelhante a da rota de upload de arquivos.
O corpo da resposta indicava o caminho para um arquivo específico. Ao fazer o download desse arquivo, descobri que se tratava do output de uma API que fornecia algumas rotas úteis para futuras explorações.
Enumerando as rotas da API
O conteúdo obtido ao acessar o arquivo da porta 5000 apresentou várias rotas da API:

Filtrando o conteúdo do arquivo com o comando jq, extraí todas as rotas disponíveis:
Das rotas extraídas, o endpoint /authors revelou-se particularmente interessante. Ao acessá-lo, obtive um arquivo JSON que continha uma credencial de acesso.
Requisição no Burp:
Conteúdo do arquivo gerado:
Identifiquei as seguintes credenciais:
Username:
devPassword:
dev080217_devAPI!@
Acesso ao Alvo via SSH
Sabendo que a porta 22 (SSH) estava aberta no alvo, utilizei essas credenciais para tentar acesso via SSH e, para minha surpresa, funcionaram perfeitamente.
Com o acesso garantido ao sistema como usuário dev, consegui finalmente capturar a user flag:
Movimentação Lateral - Dumping Git Secrets
Após ganhar acesso inicial com a conta dev, comecei a explorar o sistema para identificar possíveis caminhos de movimentação lateral. Primeiramente, observei os usuários configurados no sistema:
Além do usuário dev, havia um outro usuário chamado prod, que parecia ser uma conta relevante, provavelmente ligada ao ambiente de produção do sistema. O próximo passo foi inspecionar os diretórios dos usuários:
Notei que o diretório do usuário prod estava presente, mas eu não tinha permissão de acesso direto. No entanto, ao explorar o diretório do usuário dev, encontrei uma pasta .git, que indicava que um projeto estava sendo versionado com Git. Esta é uma pista importante, pois commits de código às vezes podem expor informações sensíveis.
A partir dessa informação, decidi investigar os commits realizados, na esperança de encontrar informações úteis para avançar. Exibi o histórico de commits com o comando git log e percebi várias mudanças importantes no código relacionadas à API do sistema.
Entre esses commits, o que mais chamou minha atenção foi o commit 1e84a036b2f33c59e2390730699a488c65643d28, que mencionava a criação de uma API contendo informações internas sobre o editorial. Exibindo o conteúdo do commit com o comando git show, eu encontrei algo extremamente valioso: credenciais de acesso para o usuário prod!
Aqui estavam as credenciais de acesso para o usuário prod:
Username:
prodPassword:
080217_Producti0n_2023!@
Decidi testar essas credenciais para ver se poderia migrar para a conta prod. Para minha satisfação, a senha funcionou, me permitindo realizar a movimentação lateral para a conta prod usando o comando su.
Com a conta prod, agora eu tinha acesso a novos recursos e possivelmente mais privilégios no sistema.
Escalada de Privilégios via CVE-2022-24439
Após obtermos acesso ao usuário prod, decidi verificar as permissões de sudo que ele possuía. Ele pode rodar um script Python específico como root, o que poderia ser a chave para escalar privilégios. Executando o comando sudo -l, vejo que prod pode executar o script /opt/internal_apps/clone_changes/clone_prod_change.py com qualquer argumento:
Fui analisar o conteúdo do script para entender melhor como ele funciona. O script em si é bastante simples, basicamente clona um repositório de uma URL especificada:
No entanto, com o uso do sudo sendo restringido por variáveis como env_reset e secure_path, parecia difícil encontrar um ponto de hijacking no script. Contudo, notei que ele utilizava a biblioteca GitPython.
Verifiquei os pacotes Python instalados no host e confirmei que a versão 3.1.29 da biblioteca GitPython estava instalada:
Pesquisando sobre esta versão da biblioteca, descobri que ela era vulnerável a um problema grave de Remote Code Execution (RCE) devido à forma como ela tratava as URLs dos repositórios Git. Essa vulnerabilidade foi corrigida na versão 3.1.30, mas o host ainda estava rodando a versão anterior.

O CVE associado a esta falha era o CVE-2022-24439, que permitia injeção de comandos ao fornecer uma URL maliciosa para o comando clone:
Temos a seguinte instrução:
How to fix?
Upgrade
GitPythonto version 3.1.30 or higher.Overview
GitPython is a python library used to interact with Git repositories
Affected versions of this package are vulnerable to Remote Code Execution (RCE) due to improper user input validation, which makes it possible to inject a maliciously crafted remote URL into the clone command. Exploiting this vulnerability is possible because the library makes external calls to
gitwithout sufficient sanitization of input arguments. This is only relevant when enabling theexttransport protocol.PoC
Testei a PoC injetando o comando que cria um arquivo chamado "pwned" no diretório /tmp:
O comando retornou um erro relacionado ao Git, mas é apenas um erro superficial. Verificando o diretório /tmp vemos que o arquivo "pwned" foi criado com sucesso, e o dono era o root:
Sabendo que era possível executar comandos como root, aproveitei para injetar um comando mais útil. Criei um script Bash que copiava o binário /bin/bash para o diretório /tmp com permissão SUID, garantindo que ele pudesse ser executado como root posteriormente:
O script foi salvo como bash.sh, e com o mesmo processo de injeção, executei o script como root:
Mais uma vez, o comando retornou o mesmo erro de GitCommandError, mas o script foi executado com sucesso. Verifiquei o diretório /tmp e, como esperado, o binário brunobash foi criado com as permissões corretas:
Agora, ao executar esse binário com a permissão SUID, obtive uma shell como root:
Com a shell de root, o próximo passo foi localizar e ler a flag root.txt:
Com a escalada de privilégios bem-sucedida e acesso root obtido, todas as flags foram coletadas e a máquina foi totalmente comprometida.

Atualizado
