mirror of
https://github.com/CyberSecurityUP/NeuroSploit.git
synced 2026-05-20 13:45:43 +02:00
NeuroSploit v3.2 - Autonomous AI Penetration Testing Platform
116 modules | 100 vuln types | 18 API routes | 18 frontend pages Major features: - VulnEngine: 100 vuln types, 526+ payloads, 12 testers, anti-hallucination prompts - Autonomous Agent: 3-stream auto pentest, multi-session (5 concurrent), pause/resume/stop - CLI Agent: Claude Code / Gemini CLI / Codex CLI inside Kali containers - Validation Pipeline: negative controls, proof of execution, confidence scoring, judge - AI Reasoning: ReACT engine, token budget, endpoint classifier, CVE hunter, deep recon - Multi-Agent: 5 specialists + orchestrator + researcher AI + vuln type agents - RAG System: BM25/TF-IDF/ChromaDB vectorstore, few-shot, reasoning templates - Smart Router: 20 providers (8 CLI OAuth + 12 API), tier failover, token refresh - Kali Sandbox: container-per-scan, 56 tools, VPN support, on-demand install - Full IA Testing: methodology-driven comprehensive pentest sessions - Notifications: Discord, Telegram, WhatsApp/Twilio multi-channel alerts - Frontend: React/TypeScript with 18 pages, real-time WebSocket updates
This commit is contained in:
Executable
+4
@@ -0,0 +1,4 @@
|
||||
from .network_scanner import NetworkScanner
|
||||
from .osint_collector import OSINTCollector
|
||||
from .dns_enumerator import DNSEnumerator
|
||||
from .subdomain_finder import SubdomainFinder
|
||||
Executable
+165
@@ -0,0 +1,165 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
DNSEnumerator - Enumerates DNS records for target domains
|
||||
"""
|
||||
import logging
|
||||
import socket
|
||||
import subprocess
|
||||
from typing import Dict, List
|
||||
import re
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class DNSEnumerator:
|
||||
"""
|
||||
A class for enumerating DNS records.
|
||||
Queries various DNS record types including A, AAAA, MX, NS, TXT, CNAME, and SOA.
|
||||
"""
|
||||
def __init__(self, config: Dict):
|
||||
"""
|
||||
Initializes the DNSEnumerator.
|
||||
|
||||
Args:
|
||||
config (Dict): The configuration dictionary for the framework.
|
||||
"""
|
||||
self.config = config
|
||||
logger.info("DNSEnumerator initialized")
|
||||
|
||||
def enumerate(self, target: str) -> Dict:
|
||||
"""
|
||||
Enumerates DNS records for a given domain.
|
||||
|
||||
Args:
|
||||
target (str): The domain name to enumerate.
|
||||
|
||||
Returns:
|
||||
Dict: A dictionary containing DNS records.
|
||||
"""
|
||||
logger.info(f"Starting DNS enumeration for {target}")
|
||||
|
||||
# Remove protocol if present
|
||||
domain = target.replace('http://', '').replace('https://', '').split('/')[0]
|
||||
|
||||
records = {
|
||||
"target": domain,
|
||||
"records": {
|
||||
"A": self._get_a_records(domain),
|
||||
"AAAA": self._get_aaaa_records(domain),
|
||||
"MX": self._get_mx_records(domain),
|
||||
"NS": self._get_ns_records(domain),
|
||||
"TXT": self._get_txt_records(domain),
|
||||
"CNAME": self._get_cname_records(domain)
|
||||
},
|
||||
"notes": "DNS enumeration completed"
|
||||
}
|
||||
|
||||
logger.info(f"DNS enumeration completed for {domain}")
|
||||
return records
|
||||
|
||||
def _get_a_records(self, domain: str) -> List[str]:
|
||||
"""Get A records (IPv4 addresses)"""
|
||||
try:
|
||||
records = socket.gethostbyname_ex(domain)[2]
|
||||
logger.info(f"Found {len(records)} A records for {domain}")
|
||||
return records
|
||||
except socket.gaierror as e:
|
||||
logger.warning(f"Could not resolve A records for {domain}: {e}")
|
||||
return []
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting A records: {e}")
|
||||
return []
|
||||
|
||||
def _get_aaaa_records(self, domain: str) -> List[str]:
|
||||
"""Get AAAA records (IPv6 addresses)"""
|
||||
try:
|
||||
records = socket.getaddrinfo(domain, None, socket.AF_INET6)
|
||||
ipv6_addrs = list(set([record[4][0] for record in records]))
|
||||
logger.info(f"Found {len(ipv6_addrs)} AAAA records for {domain}")
|
||||
return ipv6_addrs
|
||||
except socket.gaierror:
|
||||
logger.debug(f"No AAAA records found for {domain}")
|
||||
return []
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting AAAA records: {e}")
|
||||
return []
|
||||
|
||||
def _get_mx_records(self, domain: str) -> List[str]:
|
||||
"""Get MX records using nslookup/dig fallback"""
|
||||
return self._query_dns_tool(domain, "MX")
|
||||
|
||||
def _get_ns_records(self, domain: str) -> List[str]:
|
||||
"""Get NS records using nslookup/dig fallback"""
|
||||
return self._query_dns_tool(domain, "NS")
|
||||
|
||||
def _get_txt_records(self, domain: str) -> List[str]:
|
||||
"""Get TXT records using nslookup/dig fallback"""
|
||||
return self._query_dns_tool(domain, "TXT")
|
||||
|
||||
def _get_cname_records(self, domain: str) -> List[str]:
|
||||
"""Get CNAME records using nslookup/dig fallback"""
|
||||
try:
|
||||
result = socket.getfqdn(domain)
|
||||
if result != domain:
|
||||
logger.info(f"Found CNAME for {domain}: {result}")
|
||||
return [result]
|
||||
return []
|
||||
except Exception as e:
|
||||
logger.debug(f"No CNAME records found for {domain}")
|
||||
return []
|
||||
|
||||
def _query_dns_tool(self, domain: str, record_type: str) -> List[str]:
|
||||
"""
|
||||
Query DNS using nslookup (fallback method when dnspython not available)
|
||||
"""
|
||||
try:
|
||||
# Try using nslookup
|
||||
cmd = ['nslookup', '-type=' + record_type, domain]
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10,
|
||||
shell=False
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
records = self._parse_nslookup_output(result.stdout, record_type)
|
||||
logger.info(f"Found {len(records)} {record_type} records for {domain}")
|
||||
return records
|
||||
else:
|
||||
logger.debug(f"nslookup failed for {record_type} records")
|
||||
return []
|
||||
|
||||
except FileNotFoundError:
|
||||
logger.warning("nslookup not found. DNS enumeration limited to A/AAAA records.")
|
||||
return []
|
||||
except subprocess.TimeoutExpired:
|
||||
logger.warning(f"DNS query timeout for {record_type} records")
|
||||
return []
|
||||
except Exception as e:
|
||||
logger.error(f"Error querying {record_type} records: {e}")
|
||||
return []
|
||||
|
||||
def _parse_nslookup_output(self, output: str, record_type: str) -> List[str]:
|
||||
"""Parse nslookup output to extract DNS records"""
|
||||
records = []
|
||||
|
||||
if record_type == "MX":
|
||||
# MX records format: "mail exchanger = 10 mail.example.com"
|
||||
pattern = r'mail exchanger = \d+ (.+)'
|
||||
matches = re.findall(pattern, output)
|
||||
records = [match.strip().rstrip('.') for match in matches]
|
||||
|
||||
elif record_type == "NS":
|
||||
# NS records format: "nameserver = ns1.example.com"
|
||||
pattern = r'nameserver = (.+)'
|
||||
matches = re.findall(pattern, output)
|
||||
records = [match.strip().rstrip('.') for match in matches]
|
||||
|
||||
elif record_type == "TXT":
|
||||
# TXT records format: "text = "v=spf1 ...""
|
||||
pattern = r'text = "([^"]+)"'
|
||||
matches = re.findall(pattern, output)
|
||||
records = matches
|
||||
|
||||
return records
|
||||
Executable
+64
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
NetworkScanner - A tool for scanning networks to find open ports.
|
||||
"""
|
||||
import socket
|
||||
import logging
|
||||
from typing import Dict, List
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class NetworkScanner:
|
||||
"""
|
||||
A class to scan for open ports on a target machine.
|
||||
"""
|
||||
def __init__(self, config: Dict):
|
||||
"""
|
||||
Initializes the NetworkScanner.
|
||||
|
||||
Args:
|
||||
config (Dict): The configuration dictionary for the framework.
|
||||
"""
|
||||
self.config = config
|
||||
self.common_ports = [
|
||||
21, 22, 23, 25, 53, 80, 110, 111, 135, 139, 143, 443, 445,
|
||||
993, 995, 1723, 3306, 3389, 5900, 8080
|
||||
]
|
||||
|
||||
def scan(self, target: str) -> Dict:
|
||||
"""
|
||||
Scans a target for open ports.
|
||||
|
||||
Args:
|
||||
target (str): The IP address or hostname to scan.
|
||||
|
||||
Returns:
|
||||
Dict: A dictionary containing the list of open ports found.
|
||||
"""
|
||||
logger.info(f"Starting network scan on {target}")
|
||||
open_ports = []
|
||||
|
||||
try:
|
||||
target_ip = socket.gethostbyname(target)
|
||||
logger.info(f"Resolved {target} to {target_ip}")
|
||||
|
||||
for port in self.common_ports:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
socket.setdefaulttimeout(1)
|
||||
|
||||
result = sock.connect_ex((target_ip, port))
|
||||
if result == 0:
|
||||
logger.info(f"Port {port} is open on {target}")
|
||||
open_ports.append(port)
|
||||
sock.close()
|
||||
|
||||
except socket.gaierror:
|
||||
logger.error(f"Hostname could not be resolved: {target}")
|
||||
return {"error": "Hostname could not be resolved."}
|
||||
except socket.error:
|
||||
logger.error(f"Could not connect to server: {target}")
|
||||
return {"error": "Could not connect to server."}
|
||||
|
||||
logger.info(f"Network scan finished. Found {len(open_ports)} open ports.")
|
||||
return {"target": target, "open_ports": open_ports}
|
||||
|
||||
Executable
+147
@@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
OSINTCollector - Collects Open Source Intelligence from various sources
|
||||
"""
|
||||
import logging
|
||||
import re
|
||||
import requests
|
||||
from typing import Dict, List
|
||||
import socket
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class OSINTCollector:
|
||||
"""
|
||||
A class for collecting Open Source Intelligence from publicly available sources.
|
||||
Collects information like WHOIS data, IP addresses, email patterns, and more.
|
||||
"""
|
||||
def __init__(self, config: Dict):
|
||||
"""
|
||||
Initializes the OSINTCollector.
|
||||
|
||||
Args:
|
||||
config (Dict): The configuration dictionary for the framework.
|
||||
"""
|
||||
self.config = config
|
||||
logger.info("OSINTCollector initialized")
|
||||
|
||||
def collect(self, target: str) -> Dict:
|
||||
"""
|
||||
Collects OSINT data for a given target.
|
||||
|
||||
Args:
|
||||
target (str): The target (e.g., domain name, company name).
|
||||
|
||||
Returns:
|
||||
Dict: A dictionary containing OSINT findings.
|
||||
"""
|
||||
logger.info(f"Starting OSINT collection for {target}")
|
||||
|
||||
results = {
|
||||
"target": target,
|
||||
"ip_addresses": self._get_ip_addresses(target),
|
||||
"email_patterns": self._find_email_patterns(target),
|
||||
"technologies": self._detect_technologies(target),
|
||||
"social_media": self._find_social_media(target),
|
||||
"metadata": "OSINT collection completed"
|
||||
}
|
||||
|
||||
logger.info(f"OSINT collection completed for {target}")
|
||||
return results
|
||||
|
||||
def _get_ip_addresses(self, target: str) -> List[str]:
|
||||
"""Resolve target domain to IP addresses"""
|
||||
try:
|
||||
# Remove protocol if present
|
||||
domain = target.replace('http://', '').replace('https://', '').split('/')[0]
|
||||
ip_list = socket.gethostbyname_ex(domain)[2]
|
||||
logger.info(f"Resolved {domain} to IPs: {ip_list}")
|
||||
return ip_list
|
||||
except socket.gaierror as e:
|
||||
logger.warning(f"Could not resolve {target}: {e}")
|
||||
return []
|
||||
except Exception as e:
|
||||
logger.error(f"Error resolving IP for {target}: {e}")
|
||||
return []
|
||||
|
||||
def _find_email_patterns(self, target: str) -> List[str]:
|
||||
"""Find common email patterns for the target domain"""
|
||||
try:
|
||||
domain = target.replace('http://', '').replace('https://', '').split('/')[0]
|
||||
# Common email patterns
|
||||
patterns = [
|
||||
f"info@{domain}",
|
||||
f"contact@{domain}",
|
||||
f"admin@{domain}",
|
||||
f"support@{domain}",
|
||||
f"security@{domain}"
|
||||
]
|
||||
logger.info(f"Generated {len(patterns)} common email patterns for {domain}")
|
||||
return patterns
|
||||
except Exception as e:
|
||||
logger.error(f"Error generating email patterns: {e}")
|
||||
return []
|
||||
|
||||
def _detect_technologies(self, target: str) -> Dict:
|
||||
"""Detect web technologies used by the target"""
|
||||
try:
|
||||
if not target.startswith('http'):
|
||||
target = f"http://{target}"
|
||||
|
||||
response = requests.get(target, timeout=10, allow_redirects=True)
|
||||
headers = response.headers
|
||||
|
||||
technologies = {
|
||||
"server": headers.get('Server', 'Unknown'),
|
||||
"powered_by": headers.get('X-Powered-By', 'Unknown'),
|
||||
"framework": self._detect_framework(response.text, headers),
|
||||
"status_code": response.status_code
|
||||
}
|
||||
|
||||
logger.info(f"Detected technologies for {target}: {technologies}")
|
||||
return technologies
|
||||
except requests.RequestException as e:
|
||||
logger.warning(f"Could not detect technologies for {target}: {e}")
|
||||
return {"error": str(e)}
|
||||
except Exception as e:
|
||||
logger.error(f"Error detecting technologies: {e}")
|
||||
return {"error": str(e)}
|
||||
|
||||
def _detect_framework(self, html_content: str, headers: Dict) -> str:
|
||||
"""Detect web framework from HTML and headers"""
|
||||
frameworks = {
|
||||
'WordPress': ['wp-content', 'wp-includes'],
|
||||
'Drupal': ['drupal.js', 'sites/default'],
|
||||
'Joomla': ['joomla', 'option=com_'],
|
||||
'Django': ['csrfmiddlewaretoken'],
|
||||
'Laravel': ['laravel', '_token'],
|
||||
'React': ['react', '__REACT'],
|
||||
'Angular': ['ng-version', 'angular'],
|
||||
'Vue': ['vue', '__VUE__']
|
||||
}
|
||||
|
||||
for framework, indicators in frameworks.items():
|
||||
for indicator in indicators:
|
||||
if indicator.lower() in html_content.lower():
|
||||
return framework
|
||||
|
||||
return "Unknown"
|
||||
|
||||
def _find_social_media(self, target: str) -> Dict:
|
||||
"""Find potential social media accounts for the target"""
|
||||
try:
|
||||
domain = target.replace('http://', '').replace('https://', '').split('/')[0]
|
||||
company_name = domain.split('.')[0]
|
||||
|
||||
social_media = {
|
||||
"twitter": f"https://twitter.com/{company_name}",
|
||||
"linkedin": f"https://linkedin.com/company/{company_name}",
|
||||
"github": f"https://github.com/{company_name}",
|
||||
"facebook": f"https://facebook.com/{company_name}"
|
||||
}
|
||||
|
||||
logger.info(f"Generated social media URLs for {company_name}")
|
||||
return social_media
|
||||
except Exception as e:
|
||||
logger.error(f"Error generating social media links: {e}")
|
||||
return {}
|
||||
Executable
+2857
File diff suppressed because it is too large
Load Diff
Executable
+127
@@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
SubdomainFinder - Discovers subdomains using multiple techniques
|
||||
"""
|
||||
import logging
|
||||
import requests
|
||||
import socket
|
||||
from typing import Dict, List, Set
|
||||
import re
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class SubdomainFinder:
|
||||
"""
|
||||
A class for finding subdomains of a given domain.
|
||||
Uses Certificate Transparency logs, DNS brute-forcing, and common patterns.
|
||||
"""
|
||||
def __init__(self, config: Dict):
|
||||
"""
|
||||
Initializes the SubdomainFinder.
|
||||
|
||||
Args:
|
||||
config (Dict): The configuration dictionary for the framework.
|
||||
"""
|
||||
self.config = config
|
||||
self.common_subdomains = [
|
||||
'www', 'mail', 'ftp', 'localhost', 'webmail', 'smtp', 'pop', 'ns1', 'ns2',
|
||||
'webdisk', 'ns', 'cpanel', 'whm', 'autodiscover', 'autoconfig', 'test',
|
||||
'dev', 'staging', 'api', 'admin', 'portal', 'beta', 'demo', 'vpn',
|
||||
'blog', 'shop', 'store', 'forum', 'support', 'm', 'mobile', 'cdn',
|
||||
'static', 'assets', 'img', 'images', 'git', 'jenkins', 'jira'
|
||||
]
|
||||
logger.info("SubdomainFinder initialized")
|
||||
|
||||
def find(self, target: str) -> List[str]:
|
||||
"""
|
||||
Finds subdomains for a given domain using multiple techniques.
|
||||
|
||||
Args:
|
||||
target (str): The domain name to search subdomains for.
|
||||
|
||||
Returns:
|
||||
List[str]: A list of found subdomains.
|
||||
"""
|
||||
logger.info(f"Starting subdomain enumeration for {target}")
|
||||
|
||||
# Remove protocol if present
|
||||
domain = target.replace('http://', '').replace('https://', '').split('/')[0]
|
||||
|
||||
found_subdomains: Set[str] = set()
|
||||
|
||||
# Method 1: Certificate Transparency logs
|
||||
ct_subdomains = self._check_crtsh(domain)
|
||||
found_subdomains.update(ct_subdomains)
|
||||
|
||||
# Method 2: Common subdomain brute-forcing
|
||||
brute_subdomains = self._brute_force_common(domain)
|
||||
found_subdomains.update(brute_subdomains)
|
||||
|
||||
result = sorted(list(found_subdomains))
|
||||
logger.info(f"Found {len(result)} subdomains for {domain}")
|
||||
return result
|
||||
|
||||
def _check_crtsh(self, domain: str) -> List[str]:
|
||||
"""
|
||||
Query Certificate Transparency logs via crt.sh
|
||||
"""
|
||||
subdomains = []
|
||||
try:
|
||||
url = f"https://crt.sh/?q=%.{domain}&output=json"
|
||||
logger.info(f"Querying crt.sh for {domain}")
|
||||
|
||||
response = requests.get(url, timeout=15)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
|
||||
for entry in data:
|
||||
name_value = entry.get('name_value', '')
|
||||
# Split by newlines (crt.sh returns multiple names per entry sometimes)
|
||||
names = name_value.split('\n')
|
||||
for name in names:
|
||||
name = name.strip().lower()
|
||||
# Remove wildcards
|
||||
name = name.replace('*.', '')
|
||||
# Only include valid subdomains for this domain
|
||||
if name.endswith(domain) and name != domain:
|
||||
subdomains.append(name)
|
||||
|
||||
logger.info(f"Found {len(subdomains)} subdomains from crt.sh")
|
||||
else:
|
||||
logger.warning(f"crt.sh returned status code {response.status_code}")
|
||||
|
||||
except requests.RequestException as e:
|
||||
logger.warning(f"Error querying crt.sh: {e}")
|
||||
except Exception as e:
|
||||
logger.error(f"Unexpected error in crt.sh query: {e}")
|
||||
|
||||
return list(set(subdomains)) # Remove duplicates
|
||||
|
||||
def _brute_force_common(self, domain: str) -> List[str]:
|
||||
"""
|
||||
Brute-force common subdomain names
|
||||
"""
|
||||
found = []
|
||||
logger.info(f"Brute-forcing common subdomains for {domain}")
|
||||
|
||||
for subdomain in self.common_subdomains:
|
||||
full_domain = f"{subdomain}.{domain}"
|
||||
if self._check_subdomain_exists(full_domain):
|
||||
found.append(full_domain)
|
||||
logger.debug(f"Found subdomain: {full_domain}")
|
||||
|
||||
logger.info(f"Found {len(found)} subdomains via brute-force")
|
||||
return found
|
||||
|
||||
def _check_subdomain_exists(self, subdomain: str) -> bool:
|
||||
"""
|
||||
Check if a subdomain exists by attempting to resolve it
|
||||
"""
|
||||
try:
|
||||
socket.gethostbyname(subdomain)
|
||||
return True
|
||||
except socket.gaierror:
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.debug(f"Error checking {subdomain}: {e}")
|
||||
return False
|
||||
Reference in New Issue
Block a user