Ultimatum is an easy Sherlock where you are tasked with analysing a compromised Linux server running a vulnerable WordPress plugin.
An attacker fingerprinted a WordPress blog with a WordPress scanner, then abused a privilege escalation flaw in the Ultimate Member plugin through its public registration form to create an administrator account. They came back from a second address, logged in and browsed wp-admin by hand. From the theme editor they dropped a PHP reverse shell into a theme file, which gave them a shell as the web server user and a callback to an external host. They then staged a well-known Linux enumeration script in shared memory to look for privilege escalation paths. The Apache logs, a Catscale triage collection and its process and socket listings cover the whole chain.
var-log.tar.gz → access.log read with grep and awkUltimatum.tar.gz → text listings (ps, ss, file listings of /dev/shm) and full-timeline.csv filtered with grepTimestamp note: The archive extracts with unreadable permissions. Fix them on a working copy (
chmod -R u+rwX) and leave the original untouched.
Start with the Apache access.log and count requests by user agent. One address sends 1895 POSTs to xmlrpc.php with a user agent that names its own tool and version, the signature of a WordPress user and password enumeration run. The question asks for name/version, so take both straight from the user agent string.
awk -F'"' '{print $6}' access.log | sort | uniq -c | sort -rn | head
Follow the scanner IP past the xmlrpc noise. It requests the Ultimate Member plugin’s readme.txt to read the version, then sends a POST to /index.php/register/ that returns a 302. A registration POST right after a version check on this plugin points to the Ultimate Member privilege escalation through registration. This one was reasoned from the request pattern and the plugin identity rather than read from an artifact, because nothing in the logs names the CVE.
grep 23.106.60.163 access.log | grep -E 'ultimate-member|/register/'
The registration POST from Q2 comes from the same address as the scanner. It is a public cloud address, which matters for Q5.
grep 'POST /index.php/register/' access.log
Ultimate Member serves a profile page per user. Immediately after the registration POST, the same IP requests a /index.php/user/<name>/ page, which is the newly created account being checked. The format mask (seven masked characters then n) fits the name.
grep 23.106.60.163 access.log | grep '/index.php/user/'
Look for the login POST that follows the registration. It arrives at 08:37:28 from a different address, and the requests after it are the irregular, page-by-page browsing of wp-admin that a person produces, unlike the scanner’s machine-paced traffic.
grep -E 'POST /wp-login.php|/wp-admin/' access.log | grep -v 23.106.60.163
Theme-editor requests from the manual session point at a theme file. In the Catscale full-timeline.csv, one PHP file under the active theme was modified at 08:58:02, right after that activity, and its content is a stock PHP reverse shell. The filename is built to look like part of the block theme.
grep -i 'twentytwentythree' full-timeline.csv | grep '08:58'
Read the file from Q6. It is the common PHP reverse shell, which sets $shell near the top. The same line also appears in the Misc list of known web shell content, which gives a quick way to match the file to a public tool.
grep -n '\$shell' hidden-comments.php
Gotcha: The format hint says “full variable”, but the accepted answer is only the quoted value followed by the semicolon, not the
$shell =assignment.
The timeline CSV carries a size column for every file. Filter it for the Q6 path and read the size.
grep 'hidden-comments.php' full-timeline.csv
The reverse shell sets $ip and $port at the top of the file. The collected socket listing confirms it: an established connection to that address owned by apache2, sh and bash.
grep -E '^\$(ip|port)' hidden-comments.php
grep ESTAB ss-anepo.txt
In the process listing, the Apache worker (apache2, 234471) spawned sh (234517), which spawned an interactive bash -i running as www-data (234521). The interactive shell is the process the attacker is typing into.
grep -E 'www-data.*(sh|bash)' ps.txt
Gotcha: The parent
apache2PID and the intermediateshPID are both on the chain, but the question wants the process that gave the hands-on shell, which isbash -i.
Writable world locations are where a www-data shell can drop tools. The collected listing of /dev/shm shows an enumeration script staged there.
grep -E '/dev/shm|/tmp' dev-dir-files.txt
| Phase | Action |
|---|---|
| Reconnaissance | WPScan fingerprints the blog and hammers xmlrpc.php from a cloud IP |
| Initial Access | Version check on Ultimate Member, then a registration POST abusing CVE-2023-3460 |
| Persistence | Backdoor admin account secragon created and its profile page checked |
| Initial Access | Login from a second IP at 08:37:28, followed by manual wp-admin browsing |
| Persistence | Theme editor used to write a PHP reverse shell into the active theme, modified 08:58:02 |
| Command and Control | Web shell is triggered, apache2 spawns sh and bash -i, connecting to the C2 |
| Discovery | LinEnum.sh staged in /dev/shm to look for privilege escalation paths |
| Type | Value |
|---|---|
| IP (scanner and exploit) | 23.106.60[.]163 |
| IP (manual session) | 198.16.74[.]45 |
| IP:port (C2) | 43.204.24[.]76:6969 |
| User agent | WPScan/3.8.24 |
| Account (backdoor) | secragon |
| File (web shell) | /var/www/html/wp-content/themes/twentytwentythree/patterns/hidden-comments.php (2592 bytes) |
| File (enumeration) | /dev/shm/LinEnum.sh |
| Technique | ID | Description |
|---|---|---|
| Active Scanning: Vulnerability Scanning | T1595.002 | WPScan fingerprinting and xmlrpc enumeration |
| Exploit Public-Facing Application | T1190 | Ultimate Member registration flaw, CVE-2023-3460 |
| Create Account: Local Account | T1136.001 | Backdoor administrator secragon on the blog |
| Server Software Component: Web Shell | T1505.003 | PHP reverse shell written into a theme pattern file |
| Command and Scripting Interpreter: Unix Shell | T1059.004 | bash -i spawned by the web server process |
| System Information Discovery | T1082 | LinEnum.sh run for local enumeration |
An outdated plugin on a public WordPress site is the whole attack surface. The exploit was a single POST to the registration page. Patch Ultimate Member, disable open registration if it isn’t needed, and alert on a new administrator appearing seconds after a registration request.
Scanner user agents are free detections. WPScan announced itself by name and version and sent thousands of xmlrpc.php POSTs. Block or rate-limit xmlrpc.php, and alert on that user agent.
Disable the theme and plugin editor. DISALLOW_FILE_EDIT would have removed the path the attacker used to write PHP into the theme. File integrity monitoring on the theme directory would have flagged the modified file as well.
A web server process that spawns a shell is abnormal. The apache2 to sh to bash -i chain and an established outbound connection from www-data are easy to detect with process lineage rules and egress filtering. A web server has little reason to reach an arbitrary external port.
The attacker went from a plugin flaw to an interactive shell on the web server using a single registration request and the built-in theme editor. Every step left a plain trace in the Apache logs and the process and socket listings, but the exposure that mattered was that the site was both unpatched and allowed code edits from the admin panel.