#!/usr/bin/env python3 """Agent for testing business logic vulnerabilities during authorized assessments.""" import requests import json import argparse import urllib3 import threading from datetime import datetime from urllib.parse import urljoin urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) def test_price_manipulation(base_url, token, cart_endpoint="/api/cart/add"): """Test price and quantity manipulation in purchase flows.""" print("\n[*] Testing price/quantity manipulation...") findings = [] headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} url = urljoin(base_url, cart_endpoint) test_cases = [ {"name": "negative_quantity", "payload": {"product_id": 1, "quantity": -1}, "severity": "CRITICAL"}, {"name": "zero_price", "payload": {"product_id": 1, "quantity": 1, "price": 0}, "severity": "CRITICAL"}, {"name": "float_quantity", "payload": {"product_id": 1, "quantity": 0.001}, "severity": "HIGH"}, {"name": "huge_quantity", "payload": {"product_id": 1, "quantity": 999999999}, "severity": "HIGH"}, {"name": "negative_price", "payload": {"product_id": 1, "quantity": 1, "price": -99.99}, "severity": "CRITICAL"}, ] for tc in test_cases: try: resp = requests.post(url, headers=headers, json=tc["payload"], timeout=10, verify=False) if resp.status_code in (200, 201): findings.append({ "type": "PRICE_MANIPULATION", "test": tc["name"], "payload": tc["payload"], "status": resp.status_code, "severity": tc["severity"], }) print(f" [!] {tc['name']}: Accepted (status {resp.status_code})") else: print(f" [+] {tc['name']}: Rejected (status {resp.status_code})") except requests.RequestException as e: print(f" [-] {tc['name']}: Error - {e}") return findings def test_checkout_total_override(base_url, token, checkout_endpoint="/api/checkout"): """Test if the checkout total can be overridden in the request.""" print("\n[*] Testing checkout total override...") findings = [] headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} url = urljoin(base_url, checkout_endpoint) payloads = [ {"cart_id": "test", "total": 0.01, "payment_method": "card"}, {"cart_id": "test", "total": 0, "payment_method": "card"}, {"cart_id": "test", "amount": 0.01}, ] for payload in payloads: try: resp = requests.post(url, headers=headers, json=payload, timeout=10, verify=False) if resp.status_code in (200, 201): findings.append({ "type": "TOTAL_OVERRIDE", "payload": payload, "status": resp.status_code, "severity": "CRITICAL", }) print(f" [!] Checkout accepted with total={payload.get('total', payload.get('amount'))}") except requests.RequestException: continue return findings def test_coupon_reuse(base_url, token, coupon_endpoint="/api/cart/apply-coupon", code="DISCOUNT50"): """Test if a coupon can be applied multiple times.""" print(f"\n[*] Testing coupon reuse ({code})...") findings = [] headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} url = urljoin(base_url, coupon_endpoint) success_count = 0 for i in range(5): try: resp = requests.post(url, headers=headers, json={"coupon_code": code}, timeout=10, verify=False) if resp.status_code in (200, 201): success_count += 1 except requests.RequestException: break if success_count > 1: findings.append({ "type": "COUPON_REUSE", "code": code, "times_applied": success_count, "severity": "HIGH", }) print(f" [!] Coupon applied {success_count} times!") else: print(f" [+] Coupon properly limited") return findings def test_workflow_bypass(base_url, token, steps): """Test if workflow steps can be skipped.""" print("\n[*] Testing workflow step bypass...") findings = [] headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} for step in steps: url = urljoin(base_url, step["endpoint"]) try: resp = requests.request(step.get("method", "POST"), url, headers=headers, json=step.get("payload", {}), timeout=10, verify=False) if resp.status_code in (200, 201): findings.append({ "type": "WORKFLOW_BYPASS", "step": step["name"], "endpoint": step["endpoint"], "status": resp.status_code, "severity": "HIGH", }) print(f" [!] Step '{step['name']}' bypassed (status {resp.status_code})") else: print(f" [+] Step '{step['name']}' enforced (status {resp.status_code})") except requests.RequestException: continue return findings def test_race_condition(base_url, token, endpoint, payload, concurrent=10): """Test for race conditions by sending concurrent requests.""" print(f"\n[*] Testing race condition on {endpoint} ({concurrent} concurrent requests)...") findings = [] headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} url = urljoin(base_url, endpoint) results = [] def send_request(): try: resp = requests.post(url, headers=headers, json=payload, timeout=10, verify=False) results.append({"status": resp.status_code, "body": resp.text[:200]}) except requests.RequestException: results.append({"status": 0, "body": "error"}) threads = [threading.Thread(target=send_request) for _ in range(concurrent)] for t in threads: t.start() for t in threads: t.join() successes = sum(1 for r in results if r["status"] in (200, 201)) if successes > 1: findings.append({ "type": "RACE_CONDITION", "endpoint": endpoint, "concurrent": concurrent, "successes": successes, "severity": "CRITICAL", }) print(f" [!] {successes}/{concurrent} requests succeeded (potential race condition)") else: print(f" [+] {successes}/{concurrent} succeeded (properly serialized)") return findings def test_self_referral(base_url, token, referral_endpoint="/api/referrals/invite", email="self@test.com"): """Test if self-referral is possible.""" print("\n[*] Testing self-referral...") headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} url = urljoin(base_url, referral_endpoint) try: resp = requests.post(url, headers=headers, json={"referral_email": email}, timeout=10, verify=False) if resp.status_code in (200, 201): print(f" [!] Self-referral accepted") return [{"type": "SELF_REFERRAL", "severity": "MEDIUM"}] else: print(f" [+] Self-referral blocked (status {resp.status_code})") except requests.RequestException: pass return [] def generate_report(findings, output_path): """Generate business logic vulnerability report.""" report = { "assessment_date": datetime.now().isoformat(), "total_findings": len(findings), "by_type": {}, "findings": findings, } for f in findings: t = f.get("type", "UNKNOWN") report["by_type"][t] = report["by_type"].get(t, 0) + 1 with open(output_path, "w") as fh: json.dump(report, fh, indent=2) print(f"\n[*] Report: {output_path} | Findings: {len(findings)}") def main(): parser = argparse.ArgumentParser(description="Business Logic Vulnerability Testing Agent") parser.add_argument("base_url", help="Base URL of the target application") parser.add_argument("--token", required=True, help="Bearer token for authentication") parser.add_argument("--cart-endpoint", default="/api/cart/add") parser.add_argument("--checkout-endpoint", default="/api/checkout") parser.add_argument("--coupon-code", default="DISCOUNT50") parser.add_argument("-o", "--output", default="business_logic_report.json") args = parser.parse_args() print(f"[*] Business Logic Vulnerability Assessment: {args.base_url}") findings = [] findings.extend(test_price_manipulation(args.base_url, args.token, args.cart_endpoint)) findings.extend(test_checkout_total_override(args.base_url, args.token, args.checkout_endpoint)) findings.extend(test_coupon_reuse(args.base_url, args.token, code=args.coupon_code)) findings.extend(test_race_condition(args.base_url, args.token, args.cart_endpoint, {"coupon_code": args.coupon_code})) findings.extend(test_self_referral(args.base_url, args.token)) generate_report(findings, args.output) if __name__ == "__main__": main()