← all walkthroughs

Tenten

Linux· Medium· Web
owned
2026-07-02
time to own
12m42s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I fingerprinted a WordPress site running an outdated job-application plugin, retrieved a candidate's uploaded image file from a predictable, publicly accessible path (CVE-2015-6668), and extracted a hidden SSH private key from that image using freely available steganography tools. The key's weak passphrase was cracked offline in seconds.

I then logged into the server as a low-privilege user and exploited a sudo rule that blindly executed any command passed to it as root, completing full system takeover.

Command conventions

The commands below refer to the target by variable rather than by address. Bind them in your shell before running anything; recovered credentials are withheld and shown as [REDACTED: recovered credential].

export TARGET="<retired-instance-ip>"

Attack path — how the box was taken

1EnumerationNetwork port scanning / HTTP banner and source analysis
Discovered open services and identified WordPress installation
A port scan of the target revealed SSH on port 22 (OpenSSH 7.2p2) and an Apache web server on port 80 responding under the virtual hostname tenten.htb. Inspecting the HTTP response body confirmed a WordPress installation with the WP Job Manager plugin active — visible in page source as 'wp-content', 'wordpress', and 'job-manager' strings. This immediately flagged CVE-2015-6668 as a candidate vulnerability.
Exact commands 2
Confirm open ports and collect service banners.
nmap -Pn -n -p22,80 --open -T4 -sV $TARGET
Confirm WordPress and WP Job Manager plugin presence in page HTML.
curl -s -L http://tenten.htb/ | grep -Eoi 'wp-content|wordpress|job-manager'
2EnumerationWordPress post-ID enumeration
Enumerated job listings to confirm active plugin content
I iterated WordPress post IDs from 1 to 40 by requesting /?p=<n> and reading the page title. Post ID 8 returned 'Job: Pen Tester – Job Portal', confirming the WP Job Manager plugin had live job listings with associated candidate file uploads. This is the entry point for CVE-2015-6668: each listing can have CV/resume files attached, stored at a predictable path.
Exact commands 2
Enumerate all WordPress posts by ID; look for job listings with upload attachments.
for i in $(seq 1 40); do echo -n "p=$i: "; curl -s -L "http://tenten.htb/?p=$i" | grep -oP '(?<=<title>).*?(?=</title>)'; done
Alternative: WPScan flags the vulnerable WP Job Manager version directly.
wpscan --url http://tenten.htb/ --enumerate p,t,u --plugins-detection aggressive
FixUpdate WP Job Manager and block direct HTTP access to the uploads directoryCritical
WeaknessWP Job Manager versions before 1.28.0 (CVE-2015-6668) store every candidate-uploaded file in a publicly accessible, predictable URL path (/wp-content/uploads/<year>/<month>/<filename>). Apache serves this directory without authentication, so anyone who can guess or iterate the path can download files that should be visible only to hiring managers — no account or login required.
Fix1. Update WP Job Manager to version 1.28.0 or later immediately. 2. Add an .htaccess file in /wp-content/uploads/ that denies direct HTTP access (Options -Indexes; Deny from all) and whitelist only paths that genuinely need to be public. 3. For stronger protection, move candidate file storage entirely outside the web root and serve downloads through an authenticated WordPress endpoint so Apache never exposes them directly.
3Initial AccessUnauthenticated file disclosure — CVE-2015-6668 (WP Job Manager)
Downloaded private candidate upload file from unauthenticated predictable path (CVE-2015-6668)
CVE-2015-6668 affects WP Job Manager before version 1.28.0: uploaded resume/CV files are stored under /wp-content/uploads/<year>/<month>/<filename> with no access control. Apache serves this directory directly, so any internet user who can guess the path can download files that were intended to be visible only to site administrators reviewing applications. I guessed the 2017/04 directory and retrieved HackerAccessGranted.jpg — 262 KB, returning HTTP 200 — without authenticating at any point.
Exact commands 2
Probe the predictable upload path; HTTP 200 confirms the file is publicly accessible without credentials.
curl -s -i http://tenten.htb/wp-content/uploads/2017/04/HackerAccessGranted.jpg
Download the candidate-uploaded image for offline analysis.
curl -s -L http://tenten.htb/wp-content/uploads/2017/04/HackerAccessGranted.jpg -o HackerAccessGranted.jpg
4Credential AccessSteganography extraction (steghide)
Extracted hidden SSH private key from the image using steganography
The downloaded JPEG contained an SSH private key hidden inside it using steghide — a common steganography tool that embeds arbitrary data within image files. The hiding passphrase was empty (blank), so a single command with no guessing was required to extract the key. The output file id_rsa was a valid PEM-encoded RSA private key for the server's takis user account. The data hiding provided no real security; I needed only to know which tool to try.
Exact commands 2
Extract embedded content using a blank steghide passphrase; outputs id_rsa to the current directory.
steghide extract -sf HackerAccessGranted.jpg -p ""
Set required permissions and confirm the file is a PEM-encoded RSA private key.
chmod 600 id_rsa && file id_rsa && head -3 id_rsa
FixRemove all credentials and keys from web-accessible files and treat current key as compromisedCritical
WeaknessAn SSH private key was hidden inside an image file stored in the WordPress uploads directory. Although steganography conceals data from casual inspection, anyone who obtained the image (made trivial by CVE-2015-6668) could extract the key with a single, freely available command. The hiding passphrase was blank, requiring no additional effort.
Fix1. Treat the current id_rsa / takis key pair as fully compromised: remove its public key from all authorized_keys files on every host and generate a new key pair. 2. Audit /wp-content/uploads/ and any other web-accessible directory for files that are unexpectedly large, have unusual extensions, or contain embedded non-image data. 3. Establish and enforce a policy that credentials, keys, tokens, and configuration secrets are never placed in or near the web root under any form of obfuscation.
5Credential AccessOffline private-key passphrase cracking (ssh2john + John the Ripper)
Cracked the SSH private key passphrase offline in seconds
The extracted private key was itself passphrase-protected — a standard second layer of defense. However, the key used a cost-factor-1 key derivation function, meaning each password guess is evaluated almost instantaneously. I converted the key to a hash format recognised by John the Ripper and ran it against the rockyou.txt wordlist. The passphrase '[REDACTED: recovered credential]' — a common dictionary word — was recovered within seconds.
Exact commands 2
Convert the SSH private key to a John the Ripper crackable hash format.
ssh2john id_rsa > id_rsa.hash
Crack the passphrase; '[REDACTED: recovered credential]' recovered in seconds due to cost-factor-1 KDF.
john --wordlist=/usr/share/wordlists/rockyou.txt id_rsa.hash
FixEnforce strong SSH key passphrases and a high key-derivation cost factorHigh
WeaknessThe takis SSH private key was protected with the passphrase '[REDACTED: recovered credential]', a word present in common password lists. The key also used a cost-factor-1 key derivation function, meaning an unauthorised user could evaluate millions of guesses per second and recover the passphrase within seconds of obtaining the key file.
Fix1. Revoke and regenerate the key pair. Use ssh-keygen -t ed25519 (or rsa -b 4096) and choose a passphrase of at least 20 characters or four unrelated random words. 2. Generate the new key with ssh-keygen -a 100 to apply a high bcrypt round count (cost factor), making offline cracking orders of magnitude slower even if the key file is stolen again. 3. For highest assurance, bind the SSH key to a hardware security token (FIDO2/YubiKey) so the private key material never leaves the device.
6FootholdSSH authentication with stolen private key
Logged into the server as user takis via SSH
With the private key and cracked passphrase, I authenticated over SSH as the takis account (uid=1000) on the Ubuntu 16.04 host. This provided a full interactive shell and access to the user-level flag. The legitimate SSH service was used as the entry channel — no exploit was needed at this stage; the damage was entirely caused by the credential theft in prior steps.
Sshpass -p '[REDACTED: recovered credential]' ssh -i id_rsa takis@$TARGET succeeded; kill-chain commands confirm id, hostname, and user.txt retrieval under takis
Exact commands 2
Authenticate as takis using the stolen key; enter '[REDACTED: recovered credential]' when prompted for the key passphrase.
ssh -i id_rsa takis@$TARGET
Read the user-level flag: <user.txt>
cat /home/takis/user.txt
7Privilege EscalationSudo misconfiguration / GTFOBins arbitrary command execution
Escalated to root via an insecure sudo wrapper script
Running 'sudo -l' as takis revealed that /bin/fuckin was executable as root with no password required. Inspecting the script showed two lines: a shebang and then '$1 $2 $3 $4' — it unconditionally runs its four positional arguments as a shell command. Because it runs under sudo, those arguments execute with full root privileges. Passing /bin/bash as the first argument immediately dropped my into an interactive root shell. The root flag was read moments later.
Sudo /bin/fuckin invoked; script content #!/bin/bash\n$1 $2 $3 $4 confirmed; root.txt captured
Exact commands 4
List allowed sudo commands as takis; confirms '(ALL) NOPASSWD: /bin/fuckin'.
sudo -l
Inspect the script; should show: #!/bin/bash and $1 $2 $3 $4 — confirms the argument-passthrough vulnerability.
cat /bin/fuckin
Pass /bin/bash as $1; the script executes it as root, spawning a root shell.
sudo /bin/fuckin /bin/bash
Read the root-level flag: <root.txt>
cat /root/root.txt
FixRemove the unsafe sudo rule and audit all sudoers entries for argument-passthrough risksCritical
WeaknessThe sudoers configuration granted the takis account passwordless sudo access to /bin/fuckin, a script that executes its positional arguments verbatim as a shell command ($1 $2 $3 $4). This is equivalent to giving takis unrestricted root access: any user with this rule can run any program as root by passing it as an argument to the wrapper.
Fix1. Remove the /bin/fuckin entry from /etc/sudoers and /etc/sudoers.d/ immediately and delete or restrict the script itself. 2. Audit the entire sudoers configuration for any rule that allows running a shell interpreter (bash, python, perl, etc.), a script that executes user-supplied arguments, or a file-reading utility (less, vim, awk) — all are documented root-escalation paths per GTFOBins. 3. Apply the principle of least privilege: grant sudo access only for the narrowest specific command a role genuinely requires, with hardcoded arguments where possible, and require a password for every sudo invocation.

Attack patterns used

The transferable techniques behind this compromise.

CMS Exploitation (WordPress/Joomla/Drupal)WebT1190

What it is

Content management systems and their plugins/themes are a large attack surface: known-vulnerable versions, exposed admin panels, weak credentials, and insecure plugins lead to authenticated or unauthenticated RCE. wpscan enumerates WordPress versions/plugins/users; Joomla and Drupal have their own well-known RCE chains (e.g. Drupalgeddon).

Why it works

CMS deployments lag on patching and accumulate third-party plugins of varying quality, while admin interfaces are exposed. Remediate by patching core+plugins promptly, removing unused extensions, restricting admin access, and enforcing strong auth.

Read more

SSH Private Key / Credential TheftCredential Access · Lateral MovementT1552.004

What it is

Foothold access frequently exposes reusable secrets: SSH private keys (~/.ssh/id_rsa), authorized_keys, config files, history, and backups. Recovering a private key lets an unauthorised user authenticate as that user (or pivot to other hosts that trust the key), often upgrading a shaky webshell into a stable SSH session.

Why it works

Keys and credentials get left in home directories, world-readable backups, and version control. Remediate by passphrase-protecting keys, scoping authorized_keys, and scanning for secrets at rest.

Read more

Sudo Misconfiguration (GTFOBins)Linux · Privilege EscalationT1548.003

What it is

When a low-privileged user is allowed (via sudo -l) to run a specific binary as root, many binaries can be coerced into spawning a root shell or reading root-owned files. GTFOBins catalogs the escape for each binary — e.g. sudo perl -e 'exec "/bin/sh"', sudo vim -c ':!sh', sudo find . -exec /bin/sh \;.

Why it works

Admins grant narrow sudo rights assuming the binary is 'safe', but interpreters, editors, and many utilities have shell-out features. Remediate by avoiding sudo rules on interpreter-class binaries, using NOEXEC, and least-privilege review. Always run sudo -l first on a foothold.

Read more

Exposed services

22/tcp
80/tcp