souleyez 2.43.34__py3-none-any.whl → 3.0.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- souleyez/__init__.py +1 -1
- souleyez/ai/__init__.py +7 -7
- souleyez/ai/action_mapper.py +3 -2
- souleyez/ai/chain_advisor.py +2 -1
- souleyez/ai/claude_provider.py +2 -2
- souleyez/ai/context_builder.py +4 -2
- souleyez/ai/executor.py +9 -6
- souleyez/ai/feedback_handler.py +4 -2
- souleyez/ai/llm_provider.py +2 -2
- souleyez/ai/ollama_provider.py +2 -2
- souleyez/ai/ollama_service.py +10 -26
- souleyez/ai/path_scorer.py +2 -1
- souleyez/ai/recommender.py +6 -4
- souleyez/ai/report_context.py +2 -2
- souleyez/ai/report_service.py +5 -5
- souleyez/ai/result_parser.py +3 -2
- souleyez/ai/safety.py +5 -2
- souleyez/auth/__init__.py +6 -6
- souleyez/auth/audit.py +2 -2
- souleyez/auth/engagement_access.py +5 -7
- souleyez/auth/permissions.py +1 -1
- souleyez/auth/session_manager.py +5 -5
- souleyez/auth/user_manager.py +4 -5
- souleyez/commands/audit.py +6 -5
- souleyez/commands/auth.py +6 -5
- souleyez/commands/deliverables.py +2 -3
- souleyez/commands/engagement.py +3 -3
- souleyez/commands/license.py +3 -2
- souleyez/commands/screenshots.py +5 -4
- souleyez/commands/user.py +10 -8
- souleyez/config.py +4 -2
- souleyez/core/credential_tester.py +4 -2
- souleyez/core/cve_mappings.py +2 -1
- souleyez/core/cve_matcher.py +2 -1
- souleyez/core/msf_auto_mapper.py +2 -0
- souleyez/core/msf_chain_engine.py +3 -1
- souleyez/core/msf_database.py +7 -13
- souleyez/core/msf_integration.py +2 -2
- souleyez/core/msf_rpc_client.py +3 -2
- souleyez/core/msf_rpc_manager.py +4 -4
- souleyez/core/msf_sync_manager.py +7 -7
- souleyez/core/network_utils.py +1 -1
- souleyez/core/parser_handler.py +2 -1
- souleyez/core/pending_chains.py +4 -3
- souleyez/core/templates.py +5 -2
- souleyez/core/tool_chaining.py +297 -230
- souleyez/core/version_utils.py +1 -0
- souleyez/core/vuln_correlation.py +3 -2
- souleyez/core/web_utils.py +2 -1
- souleyez/detection/__init__.py +1 -1
- souleyez/detection/attack_signatures.py +1 -1
- souleyez/detection/mitre_mappings.py +1 -2
- souleyez/detection/validator.py +5 -4
- souleyez/devtools.py +4 -2
- souleyez/docs/README.md +2 -2
- souleyez/engine/background.py +168 -7
- souleyez/engine/base.py +2 -1
- souleyez/engine/loader.py +4 -2
- souleyez/engine/log_sanitizer.py +1 -0
- souleyez/engine/manager.py +3 -1
- souleyez/engine/result_handler.py +50 -67
- souleyez/engine/worker_manager.py +6 -4
- souleyez/export/evidence_bundle.py +1 -0
- souleyez/handlers/base.py +1 -0
- souleyez/handlers/bash_handler.py +1 -0
- souleyez/handlers/bloodhound_handler.py +1 -0
- souleyez/handlers/certipy_handler.py +1 -0
- souleyez/handlers/crackmapexec_handler.py +2 -20
- souleyez/handlers/dnsrecon_handler.py +2 -1
- souleyez/handlers/enum4linux_handler.py +65 -37
- souleyez/handlers/evil_winrm_handler.py +1 -0
- souleyez/handlers/ffuf_handler.py +3 -1
- souleyez/handlers/gobuster_handler.py +7 -6
- souleyez/handlers/gpp_extract_handler.py +1 -0
- souleyez/handlers/hashcat_handler.py +1 -0
- souleyez/handlers/hydra_handler.py +5 -1
- souleyez/handlers/impacket_getuserspns_handler.py +1 -0
- souleyez/handlers/impacket_psexec_handler.py +1 -0
- souleyez/handlers/impacket_secretsdump_handler.py +1 -0
- souleyez/handlers/john_handler.py +1 -0
- souleyez/handlers/katana_handler.py +39 -2
- souleyez/handlers/kerbrute_handler.py +1 -0
- souleyez/handlers/ldapsearch_handler.py +90 -17
- souleyez/handlers/lfi_extract_handler.py +1 -0
- souleyez/handlers/msf_auxiliary_handler.py +2 -0
- souleyez/handlers/msf_exploit_handler.py +1 -0
- souleyez/handlers/nikto_handler.py +2 -1
- souleyez/handlers/nmap_handler.py +2 -1
- souleyez/handlers/nuclei_handler.py +2 -1
- souleyez/handlers/nxc_handler.py +50 -19
- souleyez/handlers/rdp_sec_check_handler.py +1 -0
- souleyez/handlers/registry.py +1 -0
- souleyez/handlers/responder_handler.py +1 -0
- souleyez/handlers/service_explorer_handler.py +2 -1
- souleyez/handlers/smbclient_handler.py +1 -0
- souleyez/handlers/smbmap_handler.py +3 -2
- souleyez/handlers/sqlmap_handler.py +6 -4
- souleyez/handlers/theharvester_handler.py +2 -1
- souleyez/handlers/web_login_test_handler.py +1 -0
- souleyez/handlers/whois_handler.py +3 -2
- souleyez/handlers/wpscan_handler.py +2 -1
- souleyez/history.py +4 -3
- souleyez/importers/msf_importer.py +5 -3
- souleyez/importers/smart_importer.py +6 -4
- souleyez/integrations/siem/__init__.py +6 -6
- souleyez/integrations/siem/base.py +1 -1
- souleyez/integrations/siem/elastic.py +3 -3
- souleyez/integrations/siem/factory.py +1 -2
- souleyez/integrations/siem/googlesecops.py +4 -4
- souleyez/integrations/siem/rule_mappings/wazuh_rules.py +1 -1
- souleyez/integrations/siem/sentinel.py +3 -3
- souleyez/integrations/siem/splunk.py +3 -3
- souleyez/integrations/siem/wazuh.py +4 -4
- souleyez/integrations/wazuh/__init__.py +1 -1
- souleyez/integrations/wazuh/client.py +3 -2
- souleyez/integrations/wazuh/config.py +3 -2
- souleyez/integrations/wazuh/host_mapper.py +3 -1
- souleyez/integrations/wazuh/sync.py +4 -1
- souleyez/intelligence/__init__.py +1 -1
- souleyez/intelligence/correlation_analyzer.py +6 -5
- souleyez/intelligence/exploit_knowledge.py +4 -4
- souleyez/intelligence/exploit_suggestions.py +4 -3
- souleyez/intelligence/gap_analyzer.py +5 -3
- souleyez/intelligence/gap_detector.py +2 -0
- souleyez/intelligence/sensitive_tables.py +1 -1
- souleyez/intelligence/service_parser.py +1 -0
- souleyez/intelligence/surface_analyzer.py +9 -9
- souleyez/intelligence/target_parser.py +1 -0
- souleyez/licensing/__init__.py +3 -3
- souleyez/main.py +25 -18
- souleyez/migrations/fix_job_counter.py +2 -1
- souleyez/parsers/bloodhound_parser.py +1 -0
- souleyez/parsers/crackmapexec_parser.py +2 -1
- souleyez/parsers/dalfox_parser.py +3 -2
- souleyez/parsers/dnsrecon_parser.py +2 -1
- souleyez/parsers/enum4linux_parser.py +2 -1
- souleyez/parsers/ffuf_parser.py +2 -1
- souleyez/parsers/gobuster_parser.py +2 -1
- souleyez/parsers/hashcat_parser.py +3 -2
- souleyez/parsers/http_fingerprint_parser.py +2 -1
- souleyez/parsers/hydra_parser.py +2 -1
- souleyez/parsers/impacket_parser.py +2 -1
- souleyez/parsers/john_parser.py +4 -3
- souleyez/parsers/katana_parser.py +134 -2
- souleyez/parsers/msf_parser.py +2 -1
- souleyez/parsers/nikto_parser.py +2 -1
- souleyez/parsers/nmap_parser.py +14 -3
- souleyez/parsers/nuclei_parser.py +3 -2
- souleyez/parsers/responder_parser.py +1 -0
- souleyez/parsers/searchsploit_parser.py +3 -2
- souleyez/parsers/service_explorer_parser.py +1 -0
- souleyez/parsers/smbmap_parser.py +2 -1
- souleyez/parsers/sqlmap_parser.py +36 -2
- souleyez/parsers/theharvester_parser.py +2 -1
- souleyez/parsers/whois_parser.py +2 -1
- souleyez/parsers/wpscan_parser.py +3 -2
- souleyez/plugins/afp.py +3 -1
- souleyez/plugins/afp_brute.py +3 -1
- souleyez/plugins/ard.py +3 -1
- souleyez/plugins/bloodhound.py +3 -2
- souleyez/plugins/certipy.py +1 -0
- souleyez/plugins/crackmapexec.py +11 -7
- souleyez/plugins/dalfox.py +5 -2
- souleyez/plugins/dns_hijack.py +3 -1
- souleyez/plugins/dnsrecon.py +3 -1
- souleyez/plugins/enum4linux.py +3 -1
- souleyez/plugins/evil_winrm.py +1 -0
- souleyez/plugins/ffuf.py +3 -1
- souleyez/plugins/firmware_extract.py +3 -2
- souleyez/plugins/gobuster.py +6 -3
- souleyez/plugins/gpp_extract.py +1 -0
- souleyez/plugins/hashcat.py +2 -1
- souleyez/plugins/http_fingerprint.py +149 -40
- souleyez/plugins/hydra.py +5 -3
- souleyez/plugins/impacket_common.py +40 -0
- souleyez/plugins/impacket_getnpusers.py +19 -2
- souleyez/plugins/impacket_getuserspns.py +158 -0
- souleyez/plugins/impacket_psexec.py +19 -2
- souleyez/plugins/impacket_secretsdump.py +19 -2
- souleyez/plugins/impacket_smbclient.py +19 -2
- souleyez/plugins/john.py +2 -1
- souleyez/plugins/katana.py +48 -6
- souleyez/plugins/kerbrute.py +1 -0
- souleyez/plugins/lfi_extract.py +1 -0
- souleyez/plugins/macos_ssh.py +3 -1
- souleyez/plugins/mdns.py +3 -1
- souleyez/plugins/msf_auxiliary.py +3 -2
- souleyez/plugins/msf_exploit.py +6 -5
- souleyez/plugins/nikto.py +5 -2
- souleyez/plugins/nmap.py +6 -4
- souleyez/plugins/nuclei.py +3 -1
- souleyez/plugins/nxc.py +1 -0
- souleyez/plugins/plugin_base.py +3 -2
- souleyez/plugins/plugin_template.py +3 -2
- souleyez/plugins/rdp_sec_check.py +1 -0
- souleyez/plugins/responder.py +2 -1
- souleyez/plugins/router_http_brute.py +3 -1
- souleyez/plugins/router_ssh_brute.py +3 -1
- souleyez/plugins/router_telnet_brute.py +3 -1
- souleyez/plugins/routersploit.py +5 -3
- souleyez/plugins/routersploit_exploit.py +5 -3
- souleyez/plugins/searchsploit.py +1 -0
- souleyez/plugins/service_explorer.py +2 -1
- souleyez/plugins/smbmap.py +3 -1
- souleyez/plugins/smbpasswd.py +1 -0
- souleyez/plugins/sqlmap.py +3 -1
- souleyez/plugins/theharvester.py +3 -1
- souleyez/plugins/tr069.py +3 -1
- souleyez/plugins/upnp.py +3 -1
- souleyez/plugins/upnp_abuse.py +4 -2
- souleyez/plugins/vnc_access.py +4 -2
- souleyez/plugins/vnc_brute.py +3 -1
- souleyez/plugins/web_login_test.py +1 -0
- souleyez/plugins/whois.py +3 -1
- souleyez/plugins/wpscan.py +49 -1
- souleyez/reporting/attack_chain.py +2 -1
- souleyez/reporting/charts.py +1 -0
- souleyez/reporting/compliance_mappings.py +1 -0
- souleyez/reporting/detection_report.py +10 -10
- souleyez/reporting/formatters.py +7 -12
- souleyez/reporting/generator.py +34 -46
- souleyez/reporting/metrics.py +2 -1
- souleyez/scanner.py +6 -3
- souleyez/security/__init__.py +7 -5
- souleyez/security/scope_validator.py +5 -4
- souleyez/security/validation.py +14 -0
- souleyez/security.py +5 -2
- souleyez/storage/credentials.py +14 -19
- souleyez/storage/crypto.py +7 -4
- souleyez/storage/database.py +6 -6
- souleyez/storage/db.py +8 -8
- souleyez/storage/deliverable_evidence.py +2 -1
- souleyez/storage/deliverable_exporter.py +3 -2
- souleyez/storage/deliverable_templates.py +2 -1
- souleyez/storage/deliverables.py +2 -1
- souleyez/storage/engagements.py +6 -4
- souleyez/storage/evidence.py +5 -4
- souleyez/storage/execution_log.py +4 -2
- souleyez/storage/exploit_attempts.py +3 -2
- souleyez/storage/exploits.py +3 -1
- souleyez/storage/findings.py +3 -1
- souleyez/storage/hosts.py +5 -2
- souleyez/storage/migrate_to_engagements.py +14 -24
- souleyez/storage/migrations/_001_add_credential_enhancements.py +12 -21
- souleyez/storage/migrations/_003_add_execution_log.py +8 -13
- souleyez/storage/migrations/_005_screenshots.py +2 -4
- souleyez/storage/migrations/_006_deliverables.py +2 -4
- souleyez/storage/migrations/_007_deliverable_templates.py +4 -8
- souleyez/storage/migrations/_008_add_nuclei_table.py +2 -4
- souleyez/storage/migrations/_010_evidence_linking.py +6 -12
- souleyez/storage/migrations/_012_team_collaboration.py +12 -24
- souleyez/storage/migrations/_013_add_host_tags.py +2 -4
- souleyez/storage/migrations/_014_exploit_attempts.py +10 -20
- souleyez/storage/migrations/_015_add_mac_os_fields.py +4 -8
- souleyez/storage/migrations/_016_add_domain_field.py +2 -4
- souleyez/storage/migrations/_017_msf_sessions.py +8 -16
- souleyez/storage/migrations/_018_add_osint_target.py +4 -8
- souleyez/storage/migrations/_019_add_engagement_type.py +4 -8
- souleyez/storage/migrations/_020_add_rbac.py +9 -17
- souleyez/storage/migrations/_021_wazuh_integration.py +4 -8
- souleyez/storage/migrations/_023_fix_detection_results_fk.py +2 -4
- souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +4 -8
- souleyez/storage/migrations/_026_add_engagement_scope.py +4 -8
- souleyez/storage/migrations/_027_multi_siem_persistence.py +8 -16
- souleyez/storage/migrations/__init__.py +1 -4
- souleyez/storage/migrations/migration_manager.py +6 -9
- souleyez/storage/msf_sessions.py +1 -1
- souleyez/storage/osint.py +3 -1
- souleyez/storage/recommendation_engine.py +3 -2
- souleyez/storage/screenshots.py +2 -1
- souleyez/storage/smb_shares.py +3 -1
- souleyez/storage/sqlmap_data.py +6 -4
- souleyez/storage/team_collaboration.py +3 -2
- souleyez/storage/timeline_tracker.py +2 -1
- souleyez/storage/wazuh_vulns.py +3 -1
- souleyez/storage/web_paths.py +3 -1
- souleyez/testing/credential_tester.py +2 -0
- souleyez/ui/__init__.py +2 -1
- souleyez/ui/ai_quotes.py +1 -1
- souleyez/ui/attack_surface.py +50 -28
- souleyez/ui/chain_rules_view.py +6 -3
- souleyez/ui/correlation_view.py +3 -2
- souleyez/ui/dashboard.py +85 -139
- souleyez/ui/deliverables_view.py +1 -1
- souleyez/ui/design_system.py +5 -3
- souleyez/ui/errors.py +3 -1
- souleyez/ui/evidence_linking_view.py +2 -1
- souleyez/ui/evidence_vault.py +11 -6
- souleyez/ui/exploit_suggestions_view.py +11 -7
- souleyez/ui/export_view.py +3 -1
- souleyez/ui/gap_analysis_view.py +6 -3
- souleyez/ui/help_system.py +4 -1
- souleyez/ui/intelligence_view.py +7 -3
- souleyez/ui/interactive.py +1512 -584
- souleyez/ui/interactive_selector.py +3 -2
- souleyez/ui/log_formatter.py +1 -0
- souleyez/ui/menu_components.py +3 -1
- souleyez/ui/msf_auxiliary_menu.py +4 -1
- souleyez/ui/pending_chains_view.py +15 -12
- souleyez/ui/progress_indicators.py +5 -2
- souleyez/ui/recommendations_view.py +4 -2
- souleyez/ui/rule_builder.py +4 -1
- souleyez/ui/setup_wizard.py +10 -8
- souleyez/ui/shortcuts.py +1 -1
- souleyez/ui/splunk_gap_analysis_view.py +7 -4
- souleyez/ui/splunk_vulns_view.py +4 -1
- souleyez/ui/team_dashboard.py +7 -5
- souleyez/ui/template_selector.py +2 -1
- souleyez/ui/terminal.py +3 -2
- souleyez/ui/timeline_view.py +2 -1
- souleyez/ui/tool_setup.py +92 -31
- souleyez/ui/tutorial.py +7 -4
- souleyez/ui/tutorial_state.py +3 -2
- souleyez/ui/wazuh_vulns_view.py +5 -2
- souleyez/ui/wordlist_browser.py +4 -3
- souleyez/ui.py +13 -7
- souleyez/utils/tool_checker.py +61 -12
- souleyez/utils.py +4 -4
- souleyez/wordlists.py +1 -0
- {souleyez-2.43.34.dist-info → souleyez-3.0.7.dist-info}/METADATA +2 -2
- souleyez-3.0.7.dist-info/RECORD +445 -0
- souleyez-2.43.34.dist-info/RECORD +0 -443
- {souleyez-2.43.34.dist-info → souleyez-3.0.7.dist-info}/WHEEL +0 -0
- {souleyez-2.43.34.dist-info → souleyez-3.0.7.dist-info}/entry_points.txt +0 -0
- {souleyez-2.43.34.dist-info → souleyez-3.0.7.dist-info}/licenses/LICENSE +0 -0
- {souleyez-2.43.34.dist-info → souleyez-3.0.7.dist-info}/top_level.txt +0 -0
souleyez/core/tool_chaining.py
CHANGED
|
@@ -4,12 +4,12 @@ souleyez.core.tool_chaining - Intelligent tool chaining and workflow automation
|
|
|
4
4
|
|
|
5
5
|
Automatically triggers follow-up scans based on discovered services and findings.
|
|
6
6
|
"""
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
from dataclasses import dataclass, field
|
|
9
9
|
from datetime import datetime
|
|
10
|
+
from typing import Any, Dict, List, Optional
|
|
10
11
|
from urllib.parse import urlparse
|
|
11
12
|
|
|
12
|
-
|
|
13
13
|
# Category constants for chain rules
|
|
14
14
|
CATEGORY_CTF = "ctf" # Lab/learning scenarios - vulnerable by design
|
|
15
15
|
CATEGORY_ENTERPRISE = "enterprise" # Real-world enterprise testing
|
|
@@ -58,7 +58,8 @@ CATEGORY_ICONS = {
|
|
|
58
58
|
SERVICE_GROUPS = {
|
|
59
59
|
"http": {
|
|
60
60
|
"services": ["http", "https", "http-alt", "http-proxy", "https-alt"],
|
|
61
|
-
|
|
61
|
+
# Port 11434 is Ollama API - runs HTTP but nmap often identifies as "unknown"
|
|
62
|
+
"ports": [80, 443, 8080, 8000, 8443, 3000, 5000, 8888, 9000, 9090, 11434],
|
|
62
63
|
},
|
|
63
64
|
"smb": {
|
|
64
65
|
"services": ["microsoft-ds", "netbios-ssn", "smb"],
|
|
@@ -113,6 +114,103 @@ SERVICE_GROUPS = {
|
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
|
|
117
|
+
def should_test_url_for_sqli(endpoint_url: str) -> bool:
|
|
118
|
+
"""
|
|
119
|
+
Determine if a URL should be tested for SQL injection.
|
|
120
|
+
|
|
121
|
+
This filters out URLs that are unlikely to have injectable parameters,
|
|
122
|
+
while allowing URLs that might have forms or query parameters.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
endpoint_url: The URL to evaluate
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
True if the URL should be tested, False if it should be skipped
|
|
129
|
+
|
|
130
|
+
Examples:
|
|
131
|
+
>>> should_test_url_for_sqli("http://example.com/payroll_app.php")
|
|
132
|
+
True # Dynamic page, might have forms
|
|
133
|
+
>>> should_test_url_for_sqli("http://example.com/index.php")
|
|
134
|
+
False # Common default page, rarely injectable
|
|
135
|
+
>>> should_test_url_for_sqli("http://example.com/search.php?q=test")
|
|
136
|
+
True # Has query parameters
|
|
137
|
+
>>> should_test_url_for_sqli("http://example.com/phpinfo.php")
|
|
138
|
+
False # phpinfo output, not injectable
|
|
139
|
+
>>> should_test_url_for_sqli("http://example.com/cgi-bin/")
|
|
140
|
+
False # Directory, not a script
|
|
141
|
+
>>> should_test_url_for_sqli("http://example.com/")
|
|
142
|
+
False # No path, no params
|
|
143
|
+
"""
|
|
144
|
+
from urllib.parse import urlparse
|
|
145
|
+
|
|
146
|
+
path_lower = endpoint_url.lower()
|
|
147
|
+
has_params = "?" in endpoint_url
|
|
148
|
+
|
|
149
|
+
# URLs with query params should generally be tested - they have injection points
|
|
150
|
+
# But still skip known non-injectable apps even with params
|
|
151
|
+
if has_params:
|
|
152
|
+
# Even with params, skip known non-injectable applications
|
|
153
|
+
hard_skip = [
|
|
154
|
+
"/phpmyadmin/", # phpMyAdmin - DB admin tool, not a target
|
|
155
|
+
"/phpmyadmin?", # phpMyAdmin with params
|
|
156
|
+
]
|
|
157
|
+
if any(pattern in path_lower for pattern in hard_skip):
|
|
158
|
+
return False
|
|
159
|
+
# Skip static files with version/cache-busting params
|
|
160
|
+
# These are not injectable: /jquery.js?v=1.2.3, /style.css?ver=5.0
|
|
161
|
+
if ".js?" in path_lower or ".css?" in path_lower:
|
|
162
|
+
return False
|
|
163
|
+
# Has params and not in hard skip - test it
|
|
164
|
+
return True
|
|
165
|
+
|
|
166
|
+
# No query params - apply stricter filtering
|
|
167
|
+
# Skip known non-injectable paths (only when no params)
|
|
168
|
+
skip_patterns = [
|
|
169
|
+
"/twiki/", # TWiki wiki - not SQLi vulnerable
|
|
170
|
+
"/phpmyadmin/", # phpMyAdmin - DB admin, not SQLi target
|
|
171
|
+
"/phpmyadmin.", # phpMyAdmin CSS/JS files
|
|
172
|
+
"/phpinfo", # phpinfo() output - no injection
|
|
173
|
+
"/cgi-bin/", # Base CGI dir without script - no injection
|
|
174
|
+
"/misc/", # Drupal/CMS static assets directory
|
|
175
|
+
"/modules/", # Drupal modules directory (static files)
|
|
176
|
+
]
|
|
177
|
+
if any(pattern in path_lower for pattern in skip_patterns):
|
|
178
|
+
return False
|
|
179
|
+
|
|
180
|
+
# No params - require dynamic extension that might have forms
|
|
181
|
+
dynamic_extensions = (".php", ".asp", ".aspx", ".jsp", ".do", ".action", ".cgi")
|
|
182
|
+
is_dynamic = any(path_lower.endswith(ext) for ext in dynamic_extensions)
|
|
183
|
+
|
|
184
|
+
if not is_dynamic:
|
|
185
|
+
# No params and not a dynamic page - skip
|
|
186
|
+
return False
|
|
187
|
+
|
|
188
|
+
# Skip common default/utility pages that rarely have injectable forms
|
|
189
|
+
useless_dynamic_pages = [
|
|
190
|
+
"/index.php",
|
|
191
|
+
"/index.asp",
|
|
192
|
+
"/index.aspx",
|
|
193
|
+
"/index.jsp",
|
|
194
|
+
"/default.php",
|
|
195
|
+
"/default.asp",
|
|
196
|
+
"/default.aspx",
|
|
197
|
+
"/home.php",
|
|
198
|
+
"/home.asp",
|
|
199
|
+
"/info.php",
|
|
200
|
+
"/test.php",
|
|
201
|
+
]
|
|
202
|
+
if not has_params:
|
|
203
|
+
# Only check this list for pages without params
|
|
204
|
+
try:
|
|
205
|
+
parsed = urlparse(endpoint_url)
|
|
206
|
+
if parsed.path.lower() in useless_dynamic_pages:
|
|
207
|
+
return False
|
|
208
|
+
except Exception:
|
|
209
|
+
pass
|
|
210
|
+
|
|
211
|
+
return True
|
|
212
|
+
|
|
213
|
+
|
|
116
214
|
def classify_os_device(os_string: str, services: list) -> dict:
|
|
117
215
|
"""
|
|
118
216
|
Classify OS and device type from nmap output.
|
|
@@ -271,8 +369,8 @@ def get_managed_hosting_platform(
|
|
|
271
369
|
# These patterns identify URLs/paths that should be skipped for specific tools
|
|
272
370
|
# to reduce noise and wasted scans.
|
|
273
371
|
|
|
274
|
-
import re
|
|
275
372
|
import logging
|
|
373
|
+
import re
|
|
276
374
|
|
|
277
375
|
logger = logging.getLogger(__name__)
|
|
278
376
|
|
|
@@ -777,9 +875,9 @@ class ChainRule:
|
|
|
777
875
|
# Check product version (e.g., 'version:nginx:<1.19', 'version:apache:>=2.4.49,<=2.4.50')
|
|
778
876
|
# cond_value format: 'product:version_conditions'
|
|
779
877
|
from souleyez.core.version_utils import (
|
|
780
|
-
parse_version_spec,
|
|
781
878
|
matches_version,
|
|
782
879
|
normalize_product_name,
|
|
880
|
+
parse_version_spec,
|
|
783
881
|
)
|
|
784
882
|
|
|
785
883
|
target_product, version_conditions = parse_version_spec(cond_value)
|
|
@@ -916,7 +1014,7 @@ class ChainRule:
|
|
|
916
1014
|
# For has:services condition, extract port from the services array
|
|
917
1015
|
# Prioritize HTTP services for web tools (gobuster, nuclei, etc.)
|
|
918
1016
|
services = context.get("services", [])
|
|
919
|
-
http_ports = {80, 443, 8080, 8443, 8000, 8888, 3000, 5000}
|
|
1017
|
+
http_ports = {80, 443, 8080, 8443, 8000, 8888, 3000, 5000, 11434}
|
|
920
1018
|
|
|
921
1019
|
# First pass: look for HTTP service by name or common HTTP ports
|
|
922
1020
|
for svc in services:
|
|
@@ -1502,23 +1600,15 @@ class ToolChaining:
|
|
|
1502
1600
|
],
|
|
1503
1601
|
description="WordPress detected, scanning for vulnerabilities with WPScan",
|
|
1504
1602
|
),
|
|
1505
|
-
# phpMyAdmin → SQLMap
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
"--batch",
|
|
1515
|
-
"--forms",
|
|
1516
|
-
"--level=2",
|
|
1517
|
-
"--risk=2",
|
|
1518
|
-
"--threads=5",
|
|
1519
|
-
],
|
|
1520
|
-
description="phpMyAdmin found, testing for SQL injection",
|
|
1521
|
-
),
|
|
1603
|
+
# phpMyAdmin → SQLMap - DISABLED
|
|
1604
|
+
# phpMyAdmin is a database management tool with proper authentication.
|
|
1605
|
+
# It doesn't have SQLi in its login form. Use nuclei/searchsploit instead.
|
|
1606
|
+
# ChainRule(
|
|
1607
|
+
# trigger_tool="gobuster",
|
|
1608
|
+
# trigger_condition="finding:phpmyadmin",
|
|
1609
|
+
# target_tool="sqlmap",
|
|
1610
|
+
# ...
|
|
1611
|
+
# ),
|
|
1522
1612
|
# === END WordPress-specific chains ===
|
|
1523
1613
|
# Dalfox - Deep XSS scan if nuclei hints at XSS
|
|
1524
1614
|
ChainRule(
|
|
@@ -1677,9 +1767,11 @@ class ToolChaining:
|
|
|
1677
1767
|
description="Domain member detected, scanning subnet for Domain Controller",
|
|
1678
1768
|
),
|
|
1679
1769
|
# Kerbrute chains - enumerate users via Kerberos when domain discovered
|
|
1770
|
+
# IMPORTANT: Require port 88 (Kerberos) to be open - not just "has:domains"
|
|
1771
|
+
# Samba workgroups report as "domains" but are NOT Active Directory
|
|
1680
1772
|
ChainRule(
|
|
1681
1773
|
trigger_tool="enum4linux",
|
|
1682
|
-
trigger_condition="has:domains",
|
|
1774
|
+
trigger_condition="has:domains & port:88",
|
|
1683
1775
|
target_tool="kerbrute",
|
|
1684
1776
|
priority=6,
|
|
1685
1777
|
args_template=[
|
|
@@ -1690,11 +1782,11 @@ class ToolChaining:
|
|
|
1690
1782
|
"{dc_ip}",
|
|
1691
1783
|
"data/wordlists/ad_users.txt",
|
|
1692
1784
|
],
|
|
1693
|
-
description="
|
|
1785
|
+
description="Active Directory detected (Kerberos port 88 open) - enumerating users",
|
|
1694
1786
|
),
|
|
1695
1787
|
ChainRule(
|
|
1696
1788
|
trigger_tool="crackmapexec",
|
|
1697
|
-
trigger_condition="has:domains",
|
|
1789
|
+
trigger_condition="has:domains & port:88",
|
|
1698
1790
|
target_tool="kerbrute",
|
|
1699
1791
|
priority=6,
|
|
1700
1792
|
args_template=[
|
|
@@ -1705,7 +1797,7 @@ class ToolChaining:
|
|
|
1705
1797
|
"{dc_ip}",
|
|
1706
1798
|
"data/wordlists/ad_users.txt",
|
|
1707
1799
|
],
|
|
1708
|
-
description="
|
|
1800
|
+
description="Active Directory detected (Kerberos port 88 open) - enumerating users",
|
|
1709
1801
|
),
|
|
1710
1802
|
]
|
|
1711
1803
|
)
|
|
@@ -1981,9 +2073,10 @@ class ToolChaining:
|
|
|
1981
2073
|
description="Domain discovered - enumerating all objects to find hidden users",
|
|
1982
2074
|
),
|
|
1983
2075
|
# Kerbrute user enumeration (works even when anonymous LDAP is blocked)
|
|
2076
|
+
# IMPORTANT: Require port 88 (Kerberos) - LDAP alone doesn't mean AD
|
|
1984
2077
|
ChainRule(
|
|
1985
2078
|
trigger_tool="ldapsearch",
|
|
1986
|
-
trigger_condition="has:domains",
|
|
2079
|
+
trigger_condition="has:domains & port:88",
|
|
1987
2080
|
target_tool="kerbrute",
|
|
1988
2081
|
priority=6,
|
|
1989
2082
|
args_template=[
|
|
@@ -1994,7 +2087,7 @@ class ToolChaining:
|
|
|
1994
2087
|
"{dc_ip}",
|
|
1995
2088
|
"data/wordlists/ad_users.txt",
|
|
1996
2089
|
],
|
|
1997
|
-
description="
|
|
2090
|
+
description="Active Directory detected (Kerberos port 88 open) - enumerating users",
|
|
1998
2091
|
),
|
|
1999
2092
|
]
|
|
2000
2093
|
)
|
|
@@ -2175,49 +2268,28 @@ class ToolChaining:
|
|
|
2175
2268
|
)
|
|
2176
2269
|
)
|
|
2177
2270
|
|
|
2178
|
-
# Gobuster discovered PHP files → crawl base URL
|
|
2179
|
-
#
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
"--batch",
|
|
2190
|
-
"--crawl=2",
|
|
2191
|
-
"--risk=2",
|
|
2192
|
-
"--level=3",
|
|
2193
|
-
"--forms",
|
|
2194
|
-
"--threads=5",
|
|
2195
|
-
],
|
|
2196
|
-
description="PHP files discovered, crawling site to find parametrized pages and forms",
|
|
2197
|
-
)
|
|
2198
|
-
)
|
|
2271
|
+
# Gobuster discovered PHP files → crawl base URL - DISABLED
|
|
2272
|
+
# Reason: katana→sqlmap handles this better by targeting specific parametrized URLs.
|
|
2273
|
+
# Crawling the base URL with SQLMap is slow and often wasteful.
|
|
2274
|
+
# self.rules.append(
|
|
2275
|
+
# ChainRule(
|
|
2276
|
+
# trigger_tool="gobuster",
|
|
2277
|
+
# trigger_condition="has:php_files",
|
|
2278
|
+
# target_tool="sqlmap",
|
|
2279
|
+
# ...
|
|
2280
|
+
# )
|
|
2281
|
+
# )
|
|
2199
2282
|
|
|
2200
|
-
# Gobuster discovered ASP/ASPX files → crawl base URL
|
|
2201
|
-
#
|
|
2202
|
-
self.rules.append(
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
"{target}",
|
|
2211
|
-
"--batch",
|
|
2212
|
-
"--crawl=2",
|
|
2213
|
-
"--risk=2",
|
|
2214
|
-
"--level=3",
|
|
2215
|
-
"--forms",
|
|
2216
|
-
"--threads=5",
|
|
2217
|
-
],
|
|
2218
|
-
description="ASP/ASPX files discovered, crawling site to find parametrized pages and forms",
|
|
2219
|
-
)
|
|
2220
|
-
)
|
|
2283
|
+
# Gobuster discovered ASP/ASPX files → crawl base URL - DISABLED
|
|
2284
|
+
# Reason: katana→sqlmap handles this better by targeting specific parametrized URLs.
|
|
2285
|
+
# self.rules.append(
|
|
2286
|
+
# ChainRule(
|
|
2287
|
+
# trigger_tool="gobuster",
|
|
2288
|
+
# trigger_condition="has:asp_files",
|
|
2289
|
+
# target_tool="sqlmap",
|
|
2290
|
+
# ...
|
|
2291
|
+
# )
|
|
2292
|
+
# )
|
|
2221
2293
|
|
|
2222
2294
|
# SMART API DISCOVERY CHAIN
|
|
2223
2295
|
# Replaced broken direct SQLMap rules with intelligent two-step approach
|
|
@@ -2885,37 +2957,44 @@ class ToolChaining:
|
|
|
2885
2957
|
)
|
|
2886
2958
|
|
|
2887
2959
|
# ffuf found parameters → test with SQLMap (skip for LFI scans)
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2960
|
+
# DISABLED: This rule passes {target} (original ffuf target) to sqlmap,
|
|
2961
|
+
# which is useless - we need actual discovered endpoints with parameters.
|
|
2962
|
+
# The smart chain (rule #-1) in auto_chain() handles ffuf → sqlmap properly
|
|
2963
|
+
# by parsing ffuf output and testing discovered endpoints.
|
|
2964
|
+
# self.rules.append(
|
|
2965
|
+
# ChainRule(
|
|
2966
|
+
# trigger_tool="ffuf",
|
|
2967
|
+
# trigger_condition="has:parameters_found & !is:lfi_scan",
|
|
2968
|
+
# target_tool="sqlmap",
|
|
2969
|
+
# priority=9,
|
|
2970
|
+
# args_template=["-u", "{target}", "--batch", "--level=2", "--risk=2"],
|
|
2971
|
+
# description="Parameters discovered, testing for SQL injection",
|
|
2972
|
+
# )
|
|
2973
|
+
# )
|
|
2898
2974
|
|
|
2899
2975
|
# ffuf found parameters → test with Nuclei XSS (skip for LFI scans)
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2976
|
+
# DISABLED: Same issue as above - uses {target} instead of discovered parameters.
|
|
2977
|
+
# Running XSS scans on bare directories like /cgi-bin/ is useless.
|
|
2978
|
+
# Smart chains in auto_chain() handle ffuf → nuclei properly.
|
|
2979
|
+
# self.rules.append(
|
|
2980
|
+
# ChainRule(
|
|
2981
|
+
# trigger_tool="ffuf",
|
|
2982
|
+
# trigger_condition="has:parameters_found & !is:lfi_scan",
|
|
2983
|
+
# target_tool="nuclei",
|
|
2984
|
+
# priority=8,
|
|
2985
|
+
# args_template=[
|
|
2986
|
+
# "-tags",
|
|
2987
|
+
# "xss,rxss",
|
|
2988
|
+
# "-severity",
|
|
2989
|
+
# "critical,high,medium",
|
|
2990
|
+
# "-rate-limit",
|
|
2991
|
+
# "50",
|
|
2992
|
+
# "-c",
|
|
2993
|
+
# "10",
|
|
2994
|
+
# ],
|
|
2995
|
+
# description="Parameters discovered, testing for XSS",
|
|
2996
|
+
# )
|
|
2997
|
+
# )
|
|
2919
2998
|
|
|
2920
2999
|
# Gobuster found API endpoints → parameter fuzzing with ffuf
|
|
2921
3000
|
self.rules.extend(
|
|
@@ -3135,27 +3214,17 @@ class ToolChaining:
|
|
|
3135
3214
|
)
|
|
3136
3215
|
)
|
|
3137
3216
|
|
|
3138
|
-
# Custom PHP → SQLMap standard crawl
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
"--crawl=2",
|
|
3150
|
-
"--forms",
|
|
3151
|
-
"--level=2",
|
|
3152
|
-
"--risk=2",
|
|
3153
|
-
"--smart",
|
|
3154
|
-
"--threads=5",
|
|
3155
|
-
],
|
|
3156
|
-
description="Custom PHP app detected, scan forms for SQL injection",
|
|
3157
|
-
)
|
|
3158
|
-
)
|
|
3217
|
+
# Custom PHP → SQLMap standard crawl - DISABLED
|
|
3218
|
+
# Reason: katana→sqlmap handles this better by targeting specific parametrized URLs.
|
|
3219
|
+
# The "custom_php" category is too broad (default for unrecognized paths).
|
|
3220
|
+
# self.rules.append(
|
|
3221
|
+
# ChainRule(
|
|
3222
|
+
# trigger_tool="gobuster",
|
|
3223
|
+
# trigger_condition="category:custom_php",
|
|
3224
|
+
# target_tool="sqlmap",
|
|
3225
|
+
# ...
|
|
3226
|
+
# )
|
|
3227
|
+
# )
|
|
3159
3228
|
|
|
3160
3229
|
# === END Directory Category Chain Rules ===
|
|
3161
3230
|
|
|
@@ -4518,8 +4587,8 @@ class ToolChaining:
|
|
|
4518
4587
|
"""Generate ChainRules from CVE database for version-aware chaining."""
|
|
4519
4588
|
try:
|
|
4520
4589
|
from souleyez.core.cve_mappings import (
|
|
4521
|
-
get_all_cves,
|
|
4522
4590
|
generate_version_condition,
|
|
4591
|
+
get_all_cves,
|
|
4523
4592
|
)
|
|
4524
4593
|
except ImportError:
|
|
4525
4594
|
return # CVE mappings not available
|
|
@@ -4650,8 +4719,8 @@ class ToolChaining:
|
|
|
4650
4719
|
|
|
4651
4720
|
# Verify license allows auto-chaining
|
|
4652
4721
|
try:
|
|
4722
|
+
from souleyez.feature_flags.features import Feature, FeatureFlags
|
|
4653
4723
|
from souleyez.licensing import get_active_license
|
|
4654
|
-
from souleyez.feature_flags.features import Features, Feature
|
|
4655
4724
|
|
|
4656
4725
|
license_info = get_active_license()
|
|
4657
4726
|
# Check tier directly - if license exists with PRO tier, allow it
|
|
@@ -4663,7 +4732,7 @@ class ToolChaining:
|
|
|
4663
4732
|
elif license_info.is_valid and license_info.tier:
|
|
4664
4733
|
user_tier = license_info.tier
|
|
4665
4734
|
|
|
4666
|
-
if not
|
|
4735
|
+
if not FeatureFlags.is_enabled(Feature.AUTO_CHAINING, user_tier):
|
|
4667
4736
|
# User doesn't have PRO - disable auto-chaining
|
|
4668
4737
|
from souleyez.log_config import get_logger
|
|
4669
4738
|
|
|
@@ -4691,9 +4760,9 @@ class ToolChaining:
|
|
|
4691
4760
|
try:
|
|
4692
4761
|
from souleyez import config
|
|
4693
4762
|
|
|
4694
|
-
return config.get("ai.chain_mode", "
|
|
4763
|
+
return config.get("ai.chain_mode", "off")
|
|
4695
4764
|
except Exception:
|
|
4696
|
-
return "
|
|
4765
|
+
return "off" # Default to off - AI advisor is opt-in
|
|
4697
4766
|
|
|
4698
4767
|
def get_ai_recommendations(
|
|
4699
4768
|
self,
|
|
@@ -5266,9 +5335,9 @@ class ToolChaining:
|
|
|
5266
5335
|
...
|
|
5267
5336
|
]
|
|
5268
5337
|
"""
|
|
5269
|
-
from souleyez.log_config import get_logger
|
|
5270
5338
|
from souleyez.core.web_utils import check_http_redirect
|
|
5271
5339
|
from souleyez.engine.background import get_job
|
|
5340
|
+
from souleyez.log_config import get_logger
|
|
5272
5341
|
|
|
5273
5342
|
logger = get_logger(__name__)
|
|
5274
5343
|
|
|
@@ -5939,9 +6008,9 @@ class ToolChaining:
|
|
|
5939
6008
|
if is_wordlist:
|
|
5940
6009
|
logger.info(f"Discovered potential wordlist: {path_url}")
|
|
5941
6010
|
try:
|
|
5942
|
-
import urllib.request
|
|
5943
|
-
import ssl
|
|
5944
6011
|
import os as os_module
|
|
6012
|
+
import ssl
|
|
6013
|
+
import urllib.request
|
|
5945
6014
|
|
|
5946
6015
|
# Create discovered wordlists directory
|
|
5947
6016
|
wordlist_dir = os_module.path.join(
|
|
@@ -6031,8 +6100,14 @@ class ToolChaining:
|
|
|
6031
6100
|
f"http_fingerprint detected CMS: {cms_detected.get('name')} ({cms_confidence} confidence)"
|
|
6032
6101
|
)
|
|
6033
6102
|
|
|
6034
|
-
#
|
|
6035
|
-
|
|
6103
|
+
# Only trigger CMS scanners with high confidence detection
|
|
6104
|
+
# Medium/low confidence often means false positives (e.g., SPAs returning
|
|
6105
|
+
# non-404 for /wp-admin/ paths). Require 2+ paths matched for high confidence.
|
|
6106
|
+
if cms_confidence != "high":
|
|
6107
|
+
logger.info(
|
|
6108
|
+
f"Skipping CMS scanner - {cms_confidence} confidence is insufficient (need 'high')"
|
|
6109
|
+
)
|
|
6110
|
+
elif "wordpress" in cms_name:
|
|
6036
6111
|
try:
|
|
6037
6112
|
enqueue_job(
|
|
6038
6113
|
tool="wpscan",
|
|
@@ -6329,10 +6404,10 @@ class ToolChaining:
|
|
|
6329
6404
|
if is_tables_phase and not is_dump_phase and tables and len(tables) > 0:
|
|
6330
6405
|
# Just finished --tables phase, use hybrid approach
|
|
6331
6406
|
from souleyez.intelligence.sensitive_tables import (
|
|
6332
|
-
|
|
6407
|
+
is_sensitive_table_name,
|
|
6333
6408
|
is_system_database,
|
|
6334
6409
|
is_system_table,
|
|
6335
|
-
|
|
6410
|
+
prioritize_tables,
|
|
6336
6411
|
)
|
|
6337
6412
|
from souleyez.log_config import get_logger
|
|
6338
6413
|
|
|
@@ -6632,8 +6707,8 @@ class ToolChaining:
|
|
|
6632
6707
|
if is_dump_phase:
|
|
6633
6708
|
# === Chain to Hydra for credential reuse testing ===
|
|
6634
6709
|
from souleyez.engine.background import enqueue_job
|
|
6635
|
-
from souleyez.storage.hosts import HostManager
|
|
6636
6710
|
from souleyez.log_config import get_logger
|
|
6711
|
+
from souleyez.storage.hosts import HostManager
|
|
6637
6712
|
|
|
6638
6713
|
logger = get_logger(__name__)
|
|
6639
6714
|
|
|
@@ -6777,8 +6852,8 @@ class ToolChaining:
|
|
|
6777
6852
|
hash_groups[hash_type].append(cred)
|
|
6778
6853
|
|
|
6779
6854
|
if hash_groups:
|
|
6780
|
-
import tempfile
|
|
6781
6855
|
import os
|
|
6856
|
+
import tempfile
|
|
6782
6857
|
|
|
6783
6858
|
# Map hash types to hashcat modes
|
|
6784
6859
|
hashcat_modes = {
|
|
@@ -6927,10 +7002,11 @@ class ToolChaining:
|
|
|
6927
7002
|
# Check if databases were enumerated (trigger --tables per database)
|
|
6928
7003
|
elif databases and len(databases) > 0:
|
|
6929
7004
|
# Filter out system databases (zero pentest value)
|
|
7005
|
+
import re
|
|
7006
|
+
|
|
6930
7007
|
from souleyez.intelligence.sensitive_tables import (
|
|
6931
7008
|
is_system_database,
|
|
6932
7009
|
)
|
|
6933
|
-
import re
|
|
6934
7010
|
|
|
6935
7011
|
def is_garbage_db_name(name: str) -> bool:
|
|
6936
7012
|
"""Detect SQLMap marker strings or broken extraction results."""
|
|
@@ -8544,6 +8620,11 @@ class ToolChaining:
|
|
|
8544
8620
|
|
|
8545
8621
|
# === SQLMap for testable endpoints ===
|
|
8546
8622
|
# Skip SQLMap if this was an LFI fuzz scan - results are LFI payloads, not SQLi targets
|
|
8623
|
+
|
|
8624
|
+
# Use helper function to filter non-injectable URLs
|
|
8625
|
+
if not should_test_url_for_sqli(endpoint_url):
|
|
8626
|
+
continue
|
|
8627
|
+
|
|
8547
8628
|
if (
|
|
8548
8629
|
not is_lfi_scan
|
|
8549
8630
|
and status_code in testable_statuses
|
|
@@ -8698,8 +8779,8 @@ class ToolChaining:
|
|
|
8698
8779
|
lfi_extract_args = ["--max-urls", "10"]
|
|
8699
8780
|
|
|
8700
8781
|
# Write URLs to temp file for batch processing
|
|
8701
|
-
import tempfile
|
|
8702
8782
|
import os as os_module
|
|
8783
|
+
import tempfile
|
|
8703
8784
|
|
|
8704
8785
|
tmp_dir = os_module.path.join(
|
|
8705
8786
|
os_module.path.expanduser("~"), ".souleyez", "tmp"
|
|
@@ -8942,8 +9023,8 @@ class ToolChaining:
|
|
|
8942
9023
|
path = ""
|
|
8943
9024
|
|
|
8944
9025
|
# Create temp file with usernames
|
|
8945
|
-
import tempfile
|
|
8946
9026
|
import os as os_module
|
|
9027
|
+
import tempfile
|
|
8947
9028
|
|
|
8948
9029
|
tmp_dir = os_module.path.join(
|
|
8949
9030
|
os_module.path.expanduser("~"), ".souleyez", "tmp"
|
|
@@ -9190,6 +9271,54 @@ class ToolChaining:
|
|
|
9190
9271
|
):
|
|
9191
9272
|
continue
|
|
9192
9273
|
|
|
9274
|
+
# Skip external URLs - only test URLs on the original target host
|
|
9275
|
+
try:
|
|
9276
|
+
from urllib.parse import urlparse
|
|
9277
|
+
|
|
9278
|
+
parsed_url = urlparse(url)
|
|
9279
|
+
parsed_target = urlparse(target)
|
|
9280
|
+
if parsed_url.netloc and parsed_target.netloc:
|
|
9281
|
+
if (
|
|
9282
|
+
parsed_url.netloc.lower()
|
|
9283
|
+
!= parsed_target.netloc.lower()
|
|
9284
|
+
):
|
|
9285
|
+
logger.debug(f" Skipping external URL: {url}")
|
|
9286
|
+
continue
|
|
9287
|
+
except Exception:
|
|
9288
|
+
pass
|
|
9289
|
+
|
|
9290
|
+
# Skip non-injectable paths (TWiki, phpMyAdmin, Apache dir params)
|
|
9291
|
+
skip_patterns = [
|
|
9292
|
+
"/twiki/", # TWiki wiki - not SQLi vulnerable
|
|
9293
|
+
"/phpmyadmin/", # phpMyAdmin - DB admin, not SQLi
|
|
9294
|
+
"/phpmyadmin.", # phpMyAdmin CSS/JS files
|
|
9295
|
+
"?c=d",
|
|
9296
|
+
"?c=s",
|
|
9297
|
+
"?c=m",
|
|
9298
|
+
"?c=n", # Apache dir listing sort params
|
|
9299
|
+
"?o=a",
|
|
9300
|
+
"?o=d", # Apache dir listing order params
|
|
9301
|
+
";o=a",
|
|
9302
|
+
";o=d", # Apache dir listing (semicolon variant)
|
|
9303
|
+
"/misc/", # Drupal/CMS static assets directory
|
|
9304
|
+
"/modules/", # Drupal modules directory (static files)
|
|
9305
|
+
]
|
|
9306
|
+
# Also skip static files with version/cache-busting params
|
|
9307
|
+
# These are not injectable: /jquery.js?v=1.2.3, /style.css?ver=5.0
|
|
9308
|
+
if ".js?" in path_lower or ".css?" in path_lower:
|
|
9309
|
+
logger.debug(
|
|
9310
|
+
f" Skipping static file with cache param: {url}"
|
|
9311
|
+
)
|
|
9312
|
+
continue
|
|
9313
|
+
if any(pattern in path_lower for pattern in skip_patterns):
|
|
9314
|
+
logger.debug(f" Skipping non-injectable path: {url}")
|
|
9315
|
+
continue
|
|
9316
|
+
|
|
9317
|
+
# Skip URLs without real parameters (just base URL or path)
|
|
9318
|
+
if "?" not in url and url not in forms_found:
|
|
9319
|
+
logger.debug(f" Skipping URL without parameters: {url}")
|
|
9320
|
+
continue
|
|
9321
|
+
|
|
9193
9322
|
# Determine if this is a form (POST) or URL param (GET)
|
|
9194
9323
|
is_form = url in forms_found
|
|
9195
9324
|
|
|
@@ -9593,8 +9722,8 @@ class ToolChaining:
|
|
|
9593
9722
|
|
|
9594
9723
|
if asrep_hashes:
|
|
9595
9724
|
# Create temp file with AS-REP hashes for hashcat
|
|
9596
|
-
import tempfile
|
|
9597
9725
|
import os
|
|
9726
|
+
import tempfile
|
|
9598
9727
|
|
|
9599
9728
|
# Create hash file (uses secure tempdir)
|
|
9600
9729
|
hash_file = tempfile.NamedTemporaryFile(
|
|
@@ -9643,8 +9772,8 @@ class ToolChaining:
|
|
|
9643
9772
|
|
|
9644
9773
|
if hashes:
|
|
9645
9774
|
# Create temp file with NTLM hashes for hashcat
|
|
9646
|
-
import tempfile
|
|
9647
9775
|
import os
|
|
9776
|
+
import tempfile
|
|
9648
9777
|
|
|
9649
9778
|
# Create hash file in format: username:hash (uses secure tempdir)
|
|
9650
9779
|
hash_file = tempfile.NamedTemporaryFile(
|
|
@@ -9831,7 +9960,8 @@ class ToolChaining:
|
|
|
9831
9960
|
stored_creds = cred_mgr.list_credentials(
|
|
9832
9961
|
engagement_id, host_id=host["id"]
|
|
9833
9962
|
)
|
|
9834
|
-
# Find SMB/Windows credentials
|
|
9963
|
+
# Find SMB/Windows credentials - prefer passwords over hashes
|
|
9964
|
+
# Hashes require different auth flags and may be from later chain stages
|
|
9835
9965
|
for cred in stored_creds:
|
|
9836
9966
|
if cred.get("service") in [
|
|
9837
9967
|
"smb",
|
|
@@ -9839,8 +9969,12 @@ class ToolChaining:
|
|
|
9839
9969
|
"ldap",
|
|
9840
9970
|
"windows",
|
|
9841
9971
|
]:
|
|
9842
|
-
|
|
9843
|
-
|
|
9972
|
+
# Only use password-type creds, not hashes
|
|
9973
|
+
# Hashes need --hash flag and may be stale from previous runs
|
|
9974
|
+
cred_type = cred.get("credential_type", "password")
|
|
9975
|
+
if cred_type in ["password", "plaintext"]:
|
|
9976
|
+
smb_creds = cred
|
|
9977
|
+
break
|
|
9844
9978
|
except Exception as e:
|
|
9845
9979
|
logger.debug(f"Could not get stored credentials: {e}")
|
|
9846
9980
|
|
|
@@ -9877,14 +10011,10 @@ class ToolChaining:
|
|
|
9877
10011
|
f"Using stored credentials ({username}) for share access"
|
|
9878
10012
|
)
|
|
9879
10013
|
else:
|
|
9880
|
-
# Use null session - smbmap
|
|
10014
|
+
# Use null session - smbmap defaults to anonymous without -u/-p
|
|
9881
10015
|
smbmap_args = [
|
|
9882
10016
|
"-H",
|
|
9883
10017
|
target,
|
|
9884
|
-
"-u",
|
|
9885
|
-
"",
|
|
9886
|
-
"-p",
|
|
9887
|
-
"",
|
|
9888
10018
|
"-r",
|
|
9889
10019
|
share_name,
|
|
9890
10020
|
"--depth",
|
|
@@ -9991,8 +10121,9 @@ class ToolChaining:
|
|
|
9991
10121
|
|
|
9992
10122
|
if password_changed and username and new_password:
|
|
9993
10123
|
# Check for existing evil-winrm job for same user to avoid duplicates
|
|
10124
|
+
from datetime import datetime, timedelta, timezone
|
|
10125
|
+
|
|
9994
10126
|
from souleyez.engine.background import list_jobs
|
|
9995
|
-
from datetime import datetime, timezone, timedelta
|
|
9996
10127
|
|
|
9997
10128
|
try:
|
|
9998
10129
|
all_jobs = list_jobs(limit=500)
|
|
@@ -10077,9 +10208,10 @@ class ToolChaining:
|
|
|
10077
10208
|
f"Secretsdump extracted {hashes_count} NTLM hash(es), chaining to hashcat"
|
|
10078
10209
|
)
|
|
10079
10210
|
|
|
10080
|
-
from souleyez.engine.background import enqueue_job
|
|
10081
|
-
import tempfile
|
|
10082
10211
|
import os
|
|
10212
|
+
import tempfile
|
|
10213
|
+
|
|
10214
|
+
from souleyez.engine.background import enqueue_job
|
|
10083
10215
|
|
|
10084
10216
|
# Create a temporary hash file for hashcat
|
|
10085
10217
|
# Format: username:rid:lm:nt::: (but hashcat mode 1000 just needs NT hash)
|
|
@@ -10139,9 +10271,10 @@ class ToolChaining:
|
|
|
10139
10271
|
f"GetNPUsers extracted {hashes_count} AS-REP hash(es), chaining to hashcat"
|
|
10140
10272
|
)
|
|
10141
10273
|
|
|
10142
|
-
from souleyez.engine.background import enqueue_job
|
|
10143
10274
|
import os
|
|
10144
10275
|
|
|
10276
|
+
from souleyez.engine.background import enqueue_job
|
|
10277
|
+
|
|
10145
10278
|
# Create a hash file for hashcat
|
|
10146
10279
|
hash_dir = os.path.join(
|
|
10147
10280
|
os.path.expanduser("~"), ".souleyez", "hashes"
|
|
@@ -10330,10 +10463,11 @@ class ToolChaining:
|
|
|
10330
10463
|
hashes = parse_results.get("hashes", [])
|
|
10331
10464
|
|
|
10332
10465
|
if hashes:
|
|
10466
|
+
import os
|
|
10467
|
+
import tempfile
|
|
10468
|
+
|
|
10333
10469
|
from souleyez.engine.background import enqueue_job
|
|
10334
10470
|
from souleyez.log_config import get_logger
|
|
10335
|
-
import tempfile
|
|
10336
|
-
import os
|
|
10337
10471
|
|
|
10338
10472
|
logger = get_logger(__name__)
|
|
10339
10473
|
logger.info(
|
|
@@ -10382,9 +10516,9 @@ class ToolChaining:
|
|
|
10382
10516
|
|
|
10383
10517
|
if users:
|
|
10384
10518
|
# Create temp file with enumerated WordPress usernames
|
|
10385
|
-
import tempfile
|
|
10386
10519
|
import os
|
|
10387
10520
|
import re
|
|
10521
|
+
import tempfile
|
|
10388
10522
|
|
|
10389
10523
|
fd, usernames_file = tempfile.mkstemp(
|
|
10390
10524
|
suffix=".txt", prefix="wpscan_users_"
|
|
@@ -10446,8 +10580,8 @@ class ToolChaining:
|
|
|
10446
10580
|
|
|
10447
10581
|
if usernames:
|
|
10448
10582
|
# Create temp file with validated usernames
|
|
10449
|
-
import tempfile
|
|
10450
10583
|
import os
|
|
10584
|
+
import tempfile
|
|
10451
10585
|
|
|
10452
10586
|
fd, usernames_file = tempfile.mkstemp(
|
|
10453
10587
|
suffix=".txt", prefix="hydra_users_"
|
|
@@ -10496,8 +10630,8 @@ class ToolChaining:
|
|
|
10496
10630
|
credentials = parse_results.get("credentials", [])
|
|
10497
10631
|
if credentials:
|
|
10498
10632
|
from souleyez.engine.background import enqueue_job
|
|
10499
|
-
from souleyez.storage.hosts import HostManager
|
|
10500
10633
|
from souleyez.log_config import get_logger
|
|
10634
|
+
from souleyez.storage.hosts import HostManager
|
|
10501
10635
|
|
|
10502
10636
|
logger = get_logger(__name__)
|
|
10503
10637
|
|
|
@@ -10571,82 +10705,15 @@ class ToolChaining:
|
|
|
10571
10705
|
except Exception as e:
|
|
10572
10706
|
logger.debug(f"Evil-WinRM chain check failed: {e}")
|
|
10573
10707
|
|
|
10574
|
-
#
|
|
10575
|
-
if credentials:
|
|
10576
|
-
from souleyez.engine.background import enqueue_job
|
|
10577
|
-
from souleyez.storage.hosts import HostManager
|
|
10578
|
-
from souleyez.log_config import get_logger
|
|
10579
|
-
|
|
10580
|
-
logger = get_logger(__name__)
|
|
10581
|
-
|
|
10582
|
-
try:
|
|
10583
|
-
host_manager = HostManager()
|
|
10584
|
-
target_host = target.split(":")[0] if ":" in target else target
|
|
10585
|
-
|
|
10586
|
-
host = host_manager.get_host_by_ip(engagement_id, target_host)
|
|
10587
|
-
if host:
|
|
10588
|
-
services = host_manager.get_host_services(host["id"])
|
|
10589
|
-
ssh_svc = next(
|
|
10590
|
-
(s for s in services if s.get("port") == 22), None
|
|
10591
|
-
)
|
|
10592
|
-
|
|
10593
|
-
if ssh_svc:
|
|
10594
|
-
# SSH is available - create SSH command execution job
|
|
10595
|
-
for cred in credentials: # Process all credentials
|
|
10596
|
-
username = cred.get("username")
|
|
10597
|
-
password = cred.get("password")
|
|
10598
|
-
|
|
10599
|
-
if username and password:
|
|
10600
|
-
# Check if SSH chain already ran for this user
|
|
10601
|
-
from souleyez.storage.database import Database
|
|
10602
|
-
|
|
10603
|
-
try:
|
|
10604
|
-
db = Database()
|
|
10605
|
-
existing = db.execute(
|
|
10606
|
-
"""SELECT id FROM jobs WHERE engagement_id = ?
|
|
10607
|
-
AND tool = 'nxc' AND args LIKE '%ssh%'
|
|
10608
|
-
AND args LIKE ? AND status != 'killed' LIMIT 1""",
|
|
10609
|
-
(engagement_id, f'%-u", "{username}%'),
|
|
10610
|
-
)
|
|
10611
|
-
if existing:
|
|
10612
|
-
continue
|
|
10613
|
-
except Exception:
|
|
10614
|
-
pass
|
|
10615
|
-
|
|
10616
|
-
# Use nxc (netexec) to test SSH shell access
|
|
10617
|
-
ssh_job_id = enqueue_job(
|
|
10618
|
-
tool="nxc",
|
|
10619
|
-
target=target_host,
|
|
10620
|
-
args=[
|
|
10621
|
-
"ssh",
|
|
10622
|
-
target_host,
|
|
10623
|
-
"-u",
|
|
10624
|
-
username,
|
|
10625
|
-
"-p",
|
|
10626
|
-
password,
|
|
10627
|
-
"-x",
|
|
10628
|
-
"whoami && id && hostname",
|
|
10629
|
-
],
|
|
10630
|
-
label="hydra",
|
|
10631
|
-
engagement_id=engagement_id,
|
|
10632
|
-
parent_id=job.get("id"),
|
|
10633
|
-
reason=f"Auto-triggered by hydra: Testing SSH shell access with {username}",
|
|
10634
|
-
rule_id=-28, # Smart chain: hydra → nxc ssh (shell)
|
|
10635
|
-
)
|
|
10636
|
-
job_ids.append(ssh_job_id)
|
|
10637
|
-
logger.info(
|
|
10638
|
-
f" nxc SSH job #{ssh_job_id} for {username}"
|
|
10639
|
-
)
|
|
10640
|
-
except Exception as e:
|
|
10641
|
-
logger.debug(f"SSH shell chain check failed: {e}")
|
|
10708
|
+
# NOTE: SSH shell chain removed - spawn shell directly from Hydra job via [s] option
|
|
10642
10709
|
|
|
10643
10710
|
return job_ids
|
|
10644
10711
|
|
|
10645
10712
|
# === NetExec (nxc) credential chain: valid creds → evil_winrm, Kerberoasting, secretsdump ===
|
|
10646
10713
|
elif tool == "nxc":
|
|
10647
10714
|
from souleyez.engine.background import enqueue_job
|
|
10648
|
-
from souleyez.storage.hosts import HostManager
|
|
10649
10715
|
from souleyez.log_config import get_logger
|
|
10716
|
+
from souleyez.storage.hosts import HostManager
|
|
10650
10717
|
|
|
10651
10718
|
logger = get_logger(__name__)
|
|
10652
10719
|
logger.info(
|
|
@@ -11370,8 +11437,8 @@ class ToolChaining:
|
|
|
11370
11437
|
host_manager = None
|
|
11371
11438
|
credentials_manager = None
|
|
11372
11439
|
try:
|
|
11373
|
-
from souleyez.storage.hosts import HostManager
|
|
11374
11440
|
from souleyez.storage.credentials import CredentialsManager
|
|
11441
|
+
from souleyez.storage.hosts import HostManager
|
|
11375
11442
|
|
|
11376
11443
|
host_manager = HostManager()
|
|
11377
11444
|
credentials_manager = CredentialsManager()
|
|
@@ -11632,8 +11699,8 @@ class ToolChaining:
|
|
|
11632
11699
|
spray_password = credentials_found[0].get("password", "")
|
|
11633
11700
|
if spray_password and len(all_users) > 1:
|
|
11634
11701
|
# Create temporary user list file
|
|
11635
|
-
import tempfile
|
|
11636
11702
|
import os
|
|
11703
|
+
import tempfile
|
|
11637
11704
|
|
|
11638
11705
|
# Write users to temp file
|
|
11639
11706
|
users_file = os.path.join(
|
|
@@ -11724,8 +11791,8 @@ class ToolChaining:
|
|
|
11724
11791
|
|
|
11725
11792
|
if domain:
|
|
11726
11793
|
# Create temp file with discovered users
|
|
11727
|
-
import tempfile
|
|
11728
11794
|
import os as os_module
|
|
11795
|
+
import tempfile
|
|
11729
11796
|
|
|
11730
11797
|
users_file = os_module.path.join(
|
|
11731
11798
|
tempfile.gettempdir(),
|
|
@@ -12076,7 +12143,7 @@ class ToolChaining:
|
|
|
12076
12143
|
# === END PRE-DEDUPLICATION ===
|
|
12077
12144
|
|
|
12078
12145
|
try:
|
|
12079
|
-
from souleyez.engine.background import enqueue_job, list_jobs
|
|
12146
|
+
from souleyez.engine.background import _lock, enqueue_job, list_jobs
|
|
12080
12147
|
|
|
12081
12148
|
for cmd in commands:
|
|
12082
12149
|
cmd_target = cmd.get("target", "")
|