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.
Files changed (326) hide show
  1. souleyez/__init__.py +1 -1
  2. souleyez/ai/__init__.py +7 -7
  3. souleyez/ai/action_mapper.py +3 -2
  4. souleyez/ai/chain_advisor.py +2 -1
  5. souleyez/ai/claude_provider.py +2 -2
  6. souleyez/ai/context_builder.py +4 -2
  7. souleyez/ai/executor.py +9 -6
  8. souleyez/ai/feedback_handler.py +4 -2
  9. souleyez/ai/llm_provider.py +2 -2
  10. souleyez/ai/ollama_provider.py +2 -2
  11. souleyez/ai/ollama_service.py +10 -26
  12. souleyez/ai/path_scorer.py +2 -1
  13. souleyez/ai/recommender.py +6 -4
  14. souleyez/ai/report_context.py +2 -2
  15. souleyez/ai/report_service.py +5 -5
  16. souleyez/ai/result_parser.py +3 -2
  17. souleyez/ai/safety.py +5 -2
  18. souleyez/auth/__init__.py +6 -6
  19. souleyez/auth/audit.py +2 -2
  20. souleyez/auth/engagement_access.py +5 -7
  21. souleyez/auth/permissions.py +1 -1
  22. souleyez/auth/session_manager.py +5 -5
  23. souleyez/auth/user_manager.py +4 -5
  24. souleyez/commands/audit.py +6 -5
  25. souleyez/commands/auth.py +6 -5
  26. souleyez/commands/deliverables.py +2 -3
  27. souleyez/commands/engagement.py +3 -3
  28. souleyez/commands/license.py +3 -2
  29. souleyez/commands/screenshots.py +5 -4
  30. souleyez/commands/user.py +10 -8
  31. souleyez/config.py +4 -2
  32. souleyez/core/credential_tester.py +4 -2
  33. souleyez/core/cve_mappings.py +2 -1
  34. souleyez/core/cve_matcher.py +2 -1
  35. souleyez/core/msf_auto_mapper.py +2 -0
  36. souleyez/core/msf_chain_engine.py +3 -1
  37. souleyez/core/msf_database.py +7 -13
  38. souleyez/core/msf_integration.py +2 -2
  39. souleyez/core/msf_rpc_client.py +3 -2
  40. souleyez/core/msf_rpc_manager.py +4 -4
  41. souleyez/core/msf_sync_manager.py +7 -7
  42. souleyez/core/network_utils.py +1 -1
  43. souleyez/core/parser_handler.py +2 -1
  44. souleyez/core/pending_chains.py +4 -3
  45. souleyez/core/templates.py +5 -2
  46. souleyez/core/tool_chaining.py +297 -230
  47. souleyez/core/version_utils.py +1 -0
  48. souleyez/core/vuln_correlation.py +3 -2
  49. souleyez/core/web_utils.py +2 -1
  50. souleyez/detection/__init__.py +1 -1
  51. souleyez/detection/attack_signatures.py +1 -1
  52. souleyez/detection/mitre_mappings.py +1 -2
  53. souleyez/detection/validator.py +5 -4
  54. souleyez/devtools.py +4 -2
  55. souleyez/docs/README.md +2 -2
  56. souleyez/engine/background.py +168 -7
  57. souleyez/engine/base.py +2 -1
  58. souleyez/engine/loader.py +4 -2
  59. souleyez/engine/log_sanitizer.py +1 -0
  60. souleyez/engine/manager.py +3 -1
  61. souleyez/engine/result_handler.py +50 -67
  62. souleyez/engine/worker_manager.py +6 -4
  63. souleyez/export/evidence_bundle.py +1 -0
  64. souleyez/handlers/base.py +1 -0
  65. souleyez/handlers/bash_handler.py +1 -0
  66. souleyez/handlers/bloodhound_handler.py +1 -0
  67. souleyez/handlers/certipy_handler.py +1 -0
  68. souleyez/handlers/crackmapexec_handler.py +2 -20
  69. souleyez/handlers/dnsrecon_handler.py +2 -1
  70. souleyez/handlers/enum4linux_handler.py +65 -37
  71. souleyez/handlers/evil_winrm_handler.py +1 -0
  72. souleyez/handlers/ffuf_handler.py +3 -1
  73. souleyez/handlers/gobuster_handler.py +7 -6
  74. souleyez/handlers/gpp_extract_handler.py +1 -0
  75. souleyez/handlers/hashcat_handler.py +1 -0
  76. souleyez/handlers/hydra_handler.py +5 -1
  77. souleyez/handlers/impacket_getuserspns_handler.py +1 -0
  78. souleyez/handlers/impacket_psexec_handler.py +1 -0
  79. souleyez/handlers/impacket_secretsdump_handler.py +1 -0
  80. souleyez/handlers/john_handler.py +1 -0
  81. souleyez/handlers/katana_handler.py +39 -2
  82. souleyez/handlers/kerbrute_handler.py +1 -0
  83. souleyez/handlers/ldapsearch_handler.py +90 -17
  84. souleyez/handlers/lfi_extract_handler.py +1 -0
  85. souleyez/handlers/msf_auxiliary_handler.py +2 -0
  86. souleyez/handlers/msf_exploit_handler.py +1 -0
  87. souleyez/handlers/nikto_handler.py +2 -1
  88. souleyez/handlers/nmap_handler.py +2 -1
  89. souleyez/handlers/nuclei_handler.py +2 -1
  90. souleyez/handlers/nxc_handler.py +50 -19
  91. souleyez/handlers/rdp_sec_check_handler.py +1 -0
  92. souleyez/handlers/registry.py +1 -0
  93. souleyez/handlers/responder_handler.py +1 -0
  94. souleyez/handlers/service_explorer_handler.py +2 -1
  95. souleyez/handlers/smbclient_handler.py +1 -0
  96. souleyez/handlers/smbmap_handler.py +3 -2
  97. souleyez/handlers/sqlmap_handler.py +6 -4
  98. souleyez/handlers/theharvester_handler.py +2 -1
  99. souleyez/handlers/web_login_test_handler.py +1 -0
  100. souleyez/handlers/whois_handler.py +3 -2
  101. souleyez/handlers/wpscan_handler.py +2 -1
  102. souleyez/history.py +4 -3
  103. souleyez/importers/msf_importer.py +5 -3
  104. souleyez/importers/smart_importer.py +6 -4
  105. souleyez/integrations/siem/__init__.py +6 -6
  106. souleyez/integrations/siem/base.py +1 -1
  107. souleyez/integrations/siem/elastic.py +3 -3
  108. souleyez/integrations/siem/factory.py +1 -2
  109. souleyez/integrations/siem/googlesecops.py +4 -4
  110. souleyez/integrations/siem/rule_mappings/wazuh_rules.py +1 -1
  111. souleyez/integrations/siem/sentinel.py +3 -3
  112. souleyez/integrations/siem/splunk.py +3 -3
  113. souleyez/integrations/siem/wazuh.py +4 -4
  114. souleyez/integrations/wazuh/__init__.py +1 -1
  115. souleyez/integrations/wazuh/client.py +3 -2
  116. souleyez/integrations/wazuh/config.py +3 -2
  117. souleyez/integrations/wazuh/host_mapper.py +3 -1
  118. souleyez/integrations/wazuh/sync.py +4 -1
  119. souleyez/intelligence/__init__.py +1 -1
  120. souleyez/intelligence/correlation_analyzer.py +6 -5
  121. souleyez/intelligence/exploit_knowledge.py +4 -4
  122. souleyez/intelligence/exploit_suggestions.py +4 -3
  123. souleyez/intelligence/gap_analyzer.py +5 -3
  124. souleyez/intelligence/gap_detector.py +2 -0
  125. souleyez/intelligence/sensitive_tables.py +1 -1
  126. souleyez/intelligence/service_parser.py +1 -0
  127. souleyez/intelligence/surface_analyzer.py +9 -9
  128. souleyez/intelligence/target_parser.py +1 -0
  129. souleyez/licensing/__init__.py +3 -3
  130. souleyez/main.py +25 -18
  131. souleyez/migrations/fix_job_counter.py +2 -1
  132. souleyez/parsers/bloodhound_parser.py +1 -0
  133. souleyez/parsers/crackmapexec_parser.py +2 -1
  134. souleyez/parsers/dalfox_parser.py +3 -2
  135. souleyez/parsers/dnsrecon_parser.py +2 -1
  136. souleyez/parsers/enum4linux_parser.py +2 -1
  137. souleyez/parsers/ffuf_parser.py +2 -1
  138. souleyez/parsers/gobuster_parser.py +2 -1
  139. souleyez/parsers/hashcat_parser.py +3 -2
  140. souleyez/parsers/http_fingerprint_parser.py +2 -1
  141. souleyez/parsers/hydra_parser.py +2 -1
  142. souleyez/parsers/impacket_parser.py +2 -1
  143. souleyez/parsers/john_parser.py +4 -3
  144. souleyez/parsers/katana_parser.py +134 -2
  145. souleyez/parsers/msf_parser.py +2 -1
  146. souleyez/parsers/nikto_parser.py +2 -1
  147. souleyez/parsers/nmap_parser.py +14 -3
  148. souleyez/parsers/nuclei_parser.py +3 -2
  149. souleyez/parsers/responder_parser.py +1 -0
  150. souleyez/parsers/searchsploit_parser.py +3 -2
  151. souleyez/parsers/service_explorer_parser.py +1 -0
  152. souleyez/parsers/smbmap_parser.py +2 -1
  153. souleyez/parsers/sqlmap_parser.py +36 -2
  154. souleyez/parsers/theharvester_parser.py +2 -1
  155. souleyez/parsers/whois_parser.py +2 -1
  156. souleyez/parsers/wpscan_parser.py +3 -2
  157. souleyez/plugins/afp.py +3 -1
  158. souleyez/plugins/afp_brute.py +3 -1
  159. souleyez/plugins/ard.py +3 -1
  160. souleyez/plugins/bloodhound.py +3 -2
  161. souleyez/plugins/certipy.py +1 -0
  162. souleyez/plugins/crackmapexec.py +11 -7
  163. souleyez/plugins/dalfox.py +5 -2
  164. souleyez/plugins/dns_hijack.py +3 -1
  165. souleyez/plugins/dnsrecon.py +3 -1
  166. souleyez/plugins/enum4linux.py +3 -1
  167. souleyez/plugins/evil_winrm.py +1 -0
  168. souleyez/plugins/ffuf.py +3 -1
  169. souleyez/plugins/firmware_extract.py +3 -2
  170. souleyez/plugins/gobuster.py +6 -3
  171. souleyez/plugins/gpp_extract.py +1 -0
  172. souleyez/plugins/hashcat.py +2 -1
  173. souleyez/plugins/http_fingerprint.py +149 -40
  174. souleyez/plugins/hydra.py +5 -3
  175. souleyez/plugins/impacket_common.py +40 -0
  176. souleyez/plugins/impacket_getnpusers.py +19 -2
  177. souleyez/plugins/impacket_getuserspns.py +158 -0
  178. souleyez/plugins/impacket_psexec.py +19 -2
  179. souleyez/plugins/impacket_secretsdump.py +19 -2
  180. souleyez/plugins/impacket_smbclient.py +19 -2
  181. souleyez/plugins/john.py +2 -1
  182. souleyez/plugins/katana.py +48 -6
  183. souleyez/plugins/kerbrute.py +1 -0
  184. souleyez/plugins/lfi_extract.py +1 -0
  185. souleyez/plugins/macos_ssh.py +3 -1
  186. souleyez/plugins/mdns.py +3 -1
  187. souleyez/plugins/msf_auxiliary.py +3 -2
  188. souleyez/plugins/msf_exploit.py +6 -5
  189. souleyez/plugins/nikto.py +5 -2
  190. souleyez/plugins/nmap.py +6 -4
  191. souleyez/plugins/nuclei.py +3 -1
  192. souleyez/plugins/nxc.py +1 -0
  193. souleyez/plugins/plugin_base.py +3 -2
  194. souleyez/plugins/plugin_template.py +3 -2
  195. souleyez/plugins/rdp_sec_check.py +1 -0
  196. souleyez/plugins/responder.py +2 -1
  197. souleyez/plugins/router_http_brute.py +3 -1
  198. souleyez/plugins/router_ssh_brute.py +3 -1
  199. souleyez/plugins/router_telnet_brute.py +3 -1
  200. souleyez/plugins/routersploit.py +5 -3
  201. souleyez/plugins/routersploit_exploit.py +5 -3
  202. souleyez/plugins/searchsploit.py +1 -0
  203. souleyez/plugins/service_explorer.py +2 -1
  204. souleyez/plugins/smbmap.py +3 -1
  205. souleyez/plugins/smbpasswd.py +1 -0
  206. souleyez/plugins/sqlmap.py +3 -1
  207. souleyez/plugins/theharvester.py +3 -1
  208. souleyez/plugins/tr069.py +3 -1
  209. souleyez/plugins/upnp.py +3 -1
  210. souleyez/plugins/upnp_abuse.py +4 -2
  211. souleyez/plugins/vnc_access.py +4 -2
  212. souleyez/plugins/vnc_brute.py +3 -1
  213. souleyez/plugins/web_login_test.py +1 -0
  214. souleyez/plugins/whois.py +3 -1
  215. souleyez/plugins/wpscan.py +49 -1
  216. souleyez/reporting/attack_chain.py +2 -1
  217. souleyez/reporting/charts.py +1 -0
  218. souleyez/reporting/compliance_mappings.py +1 -0
  219. souleyez/reporting/detection_report.py +10 -10
  220. souleyez/reporting/formatters.py +7 -12
  221. souleyez/reporting/generator.py +34 -46
  222. souleyez/reporting/metrics.py +2 -1
  223. souleyez/scanner.py +6 -3
  224. souleyez/security/__init__.py +7 -5
  225. souleyez/security/scope_validator.py +5 -4
  226. souleyez/security/validation.py +14 -0
  227. souleyez/security.py +5 -2
  228. souleyez/storage/credentials.py +14 -19
  229. souleyez/storage/crypto.py +7 -4
  230. souleyez/storage/database.py +6 -6
  231. souleyez/storage/db.py +8 -8
  232. souleyez/storage/deliverable_evidence.py +2 -1
  233. souleyez/storage/deliverable_exporter.py +3 -2
  234. souleyez/storage/deliverable_templates.py +2 -1
  235. souleyez/storage/deliverables.py +2 -1
  236. souleyez/storage/engagements.py +6 -4
  237. souleyez/storage/evidence.py +5 -4
  238. souleyez/storage/execution_log.py +4 -2
  239. souleyez/storage/exploit_attempts.py +3 -2
  240. souleyez/storage/exploits.py +3 -1
  241. souleyez/storage/findings.py +3 -1
  242. souleyez/storage/hosts.py +5 -2
  243. souleyez/storage/migrate_to_engagements.py +14 -24
  244. souleyez/storage/migrations/_001_add_credential_enhancements.py +12 -21
  245. souleyez/storage/migrations/_003_add_execution_log.py +8 -13
  246. souleyez/storage/migrations/_005_screenshots.py +2 -4
  247. souleyez/storage/migrations/_006_deliverables.py +2 -4
  248. souleyez/storage/migrations/_007_deliverable_templates.py +4 -8
  249. souleyez/storage/migrations/_008_add_nuclei_table.py +2 -4
  250. souleyez/storage/migrations/_010_evidence_linking.py +6 -12
  251. souleyez/storage/migrations/_012_team_collaboration.py +12 -24
  252. souleyez/storage/migrations/_013_add_host_tags.py +2 -4
  253. souleyez/storage/migrations/_014_exploit_attempts.py +10 -20
  254. souleyez/storage/migrations/_015_add_mac_os_fields.py +4 -8
  255. souleyez/storage/migrations/_016_add_domain_field.py +2 -4
  256. souleyez/storage/migrations/_017_msf_sessions.py +8 -16
  257. souleyez/storage/migrations/_018_add_osint_target.py +4 -8
  258. souleyez/storage/migrations/_019_add_engagement_type.py +4 -8
  259. souleyez/storage/migrations/_020_add_rbac.py +9 -17
  260. souleyez/storage/migrations/_021_wazuh_integration.py +4 -8
  261. souleyez/storage/migrations/_023_fix_detection_results_fk.py +2 -4
  262. souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +4 -8
  263. souleyez/storage/migrations/_026_add_engagement_scope.py +4 -8
  264. souleyez/storage/migrations/_027_multi_siem_persistence.py +8 -16
  265. souleyez/storage/migrations/__init__.py +1 -4
  266. souleyez/storage/migrations/migration_manager.py +6 -9
  267. souleyez/storage/msf_sessions.py +1 -1
  268. souleyez/storage/osint.py +3 -1
  269. souleyez/storage/recommendation_engine.py +3 -2
  270. souleyez/storage/screenshots.py +2 -1
  271. souleyez/storage/smb_shares.py +3 -1
  272. souleyez/storage/sqlmap_data.py +6 -4
  273. souleyez/storage/team_collaboration.py +3 -2
  274. souleyez/storage/timeline_tracker.py +2 -1
  275. souleyez/storage/wazuh_vulns.py +3 -1
  276. souleyez/storage/web_paths.py +3 -1
  277. souleyez/testing/credential_tester.py +2 -0
  278. souleyez/ui/__init__.py +2 -1
  279. souleyez/ui/ai_quotes.py +1 -1
  280. souleyez/ui/attack_surface.py +50 -28
  281. souleyez/ui/chain_rules_view.py +6 -3
  282. souleyez/ui/correlation_view.py +3 -2
  283. souleyez/ui/dashboard.py +85 -139
  284. souleyez/ui/deliverables_view.py +1 -1
  285. souleyez/ui/design_system.py +5 -3
  286. souleyez/ui/errors.py +3 -1
  287. souleyez/ui/evidence_linking_view.py +2 -1
  288. souleyez/ui/evidence_vault.py +11 -6
  289. souleyez/ui/exploit_suggestions_view.py +11 -7
  290. souleyez/ui/export_view.py +3 -1
  291. souleyez/ui/gap_analysis_view.py +6 -3
  292. souleyez/ui/help_system.py +4 -1
  293. souleyez/ui/intelligence_view.py +7 -3
  294. souleyez/ui/interactive.py +1512 -584
  295. souleyez/ui/interactive_selector.py +3 -2
  296. souleyez/ui/log_formatter.py +1 -0
  297. souleyez/ui/menu_components.py +3 -1
  298. souleyez/ui/msf_auxiliary_menu.py +4 -1
  299. souleyez/ui/pending_chains_view.py +15 -12
  300. souleyez/ui/progress_indicators.py +5 -2
  301. souleyez/ui/recommendations_view.py +4 -2
  302. souleyez/ui/rule_builder.py +4 -1
  303. souleyez/ui/setup_wizard.py +10 -8
  304. souleyez/ui/shortcuts.py +1 -1
  305. souleyez/ui/splunk_gap_analysis_view.py +7 -4
  306. souleyez/ui/splunk_vulns_view.py +4 -1
  307. souleyez/ui/team_dashboard.py +7 -5
  308. souleyez/ui/template_selector.py +2 -1
  309. souleyez/ui/terminal.py +3 -2
  310. souleyez/ui/timeline_view.py +2 -1
  311. souleyez/ui/tool_setup.py +92 -31
  312. souleyez/ui/tutorial.py +7 -4
  313. souleyez/ui/tutorial_state.py +3 -2
  314. souleyez/ui/wazuh_vulns_view.py +5 -2
  315. souleyez/ui/wordlist_browser.py +4 -3
  316. souleyez/ui.py +13 -7
  317. souleyez/utils/tool_checker.py +61 -12
  318. souleyez/utils.py +4 -4
  319. souleyez/wordlists.py +1 -0
  320. {souleyez-2.43.34.dist-info → souleyez-3.0.7.dist-info}/METADATA +2 -2
  321. souleyez-3.0.7.dist-info/RECORD +445 -0
  322. souleyez-2.43.34.dist-info/RECORD +0 -443
  323. {souleyez-2.43.34.dist-info → souleyez-3.0.7.dist-info}/WHEEL +0 -0
  324. {souleyez-2.43.34.dist-info → souleyez-3.0.7.dist-info}/entry_points.txt +0 -0
  325. {souleyez-2.43.34.dist-info → souleyez-3.0.7.dist-info}/licenses/LICENSE +0 -0
  326. {souleyez-2.43.34.dist-info → souleyez-3.0.7.dist-info}/top_level.txt +0 -0
@@ -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
- from typing import List, Dict, Any, Optional
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
- "ports": [80, 443, 8080, 8000, 8443, 3000, 5000, 8888, 9000, 9090],
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 (common SQLi target)
1506
- ChainRule(
1507
- trigger_tool="gobuster",
1508
- trigger_condition="finding:phpmyadmin",
1509
- target_tool="sqlmap",
1510
- priority=8,
1511
- args_template=[
1512
- "-u",
1513
- "{target}",
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="Domain discovered via SMB - enumerating users via Kerberos",
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="Domain discovered via SMB - enumerating users via Kerberos",
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="Domain discovered via LDAP - enumerating users via Kerberos",
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 once to find parametrized URLs
2179
- # NOTE: Special handling in auto_chain() limits to ONE crawl per base URL
2180
- self.rules.append(
2181
- ChainRule(
2182
- trigger_tool="gobuster",
2183
- trigger_condition="has:php_files",
2184
- target_tool="sqlmap",
2185
- priority=7,
2186
- args_template=[
2187
- "-u",
2188
- "{target}",
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 once to find parametrized URLs
2201
- # Same logic as PHP files - ASP is common in Windows/IIS environments
2202
- self.rules.append(
2203
- ChainRule(
2204
- trigger_tool="gobuster",
2205
- trigger_condition="has:asp_files",
2206
- target_tool="sqlmap",
2207
- priority=7,
2208
- args_template=[
2209
- "-u",
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
- self.rules.append(
2889
- ChainRule(
2890
- trigger_tool="ffuf",
2891
- trigger_condition="has:parameters_found & !is:lfi_scan",
2892
- target_tool="sqlmap",
2893
- priority=9,
2894
- args_template=["-u", "{target}", "--batch", "--level=2", "--risk=2"],
2895
- description="Parameters discovered, testing for SQL injection",
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
- self.rules.append(
2901
- ChainRule(
2902
- trigger_tool="ffuf",
2903
- trigger_condition="has:parameters_found & !is:lfi_scan",
2904
- target_tool="nuclei",
2905
- priority=8,
2906
- args_template=[
2907
- "-tags",
2908
- "xss,rxss",
2909
- "-severity",
2910
- "critical,high,medium",
2911
- "-rate-limit",
2912
- "50",
2913
- "-c",
2914
- "10",
2915
- ],
2916
- description="Parameters discovered, testing for XSS",
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
- self.rules.append(
3140
- ChainRule(
3141
- trigger_tool="gobuster",
3142
- trigger_condition="category:custom_php",
3143
- target_tool="sqlmap",
3144
- priority=7,
3145
- args_template=[
3146
- "-u",
3147
- "{target}",
3148
- "--batch",
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 Features.is_enabled(Feature.AUTO_CHAINING, user_tier):
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", "suggest")
4763
+ return config.get("ai.chain_mode", "off")
4695
4764
  except Exception:
4696
- return "suggest" # Default to suggest mode
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
- # Trigger appropriate CMS scanner
6035
- if "wordpress" in cms_name:
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
- prioritize_tables,
6407
+ is_sensitive_table_name,
6333
6408
  is_system_database,
6334
6409
  is_system_table,
6335
- is_sensitive_table_name,
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
- smb_creds = cred
9843
- break
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 handles this with empty creds
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
- # === Hydra credentials found SSH shell access (Linux) ===
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, _lock
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", "")