souleyez 2.43.28__py3-none-any.whl → 2.43.32__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 (356) hide show
  1. souleyez/__init__.py +1 -2
  2. souleyez/ai/__init__.py +21 -15
  3. souleyez/ai/action_mapper.py +249 -150
  4. souleyez/ai/chain_advisor.py +116 -100
  5. souleyez/ai/claude_provider.py +29 -28
  6. souleyez/ai/context_builder.py +80 -62
  7. souleyez/ai/executor.py +158 -117
  8. souleyez/ai/feedback_handler.py +136 -121
  9. souleyez/ai/llm_factory.py +27 -20
  10. souleyez/ai/llm_provider.py +4 -2
  11. souleyez/ai/ollama_provider.py +6 -9
  12. souleyez/ai/ollama_service.py +44 -37
  13. souleyez/ai/path_scorer.py +91 -76
  14. souleyez/ai/recommender.py +176 -144
  15. souleyez/ai/report_context.py +74 -73
  16. souleyez/ai/report_service.py +84 -66
  17. souleyez/ai/result_parser.py +222 -229
  18. souleyez/ai/safety.py +67 -44
  19. souleyez/auth/__init__.py +23 -22
  20. souleyez/auth/audit.py +36 -26
  21. souleyez/auth/engagement_access.py +65 -48
  22. souleyez/auth/permissions.py +14 -3
  23. souleyez/auth/session_manager.py +54 -37
  24. souleyez/auth/user_manager.py +109 -64
  25. souleyez/commands/audit.py +40 -43
  26. souleyez/commands/auth.py +35 -15
  27. souleyez/commands/deliverables.py +55 -50
  28. souleyez/commands/engagement.py +47 -28
  29. souleyez/commands/license.py +32 -23
  30. souleyez/commands/screenshots.py +36 -32
  31. souleyez/commands/user.py +82 -36
  32. souleyez/config.py +52 -44
  33. souleyez/core/credential_tester.py +87 -81
  34. souleyez/core/cve_mappings.py +179 -192
  35. souleyez/core/cve_matcher.py +162 -148
  36. souleyez/core/msf_auto_mapper.py +100 -83
  37. souleyez/core/msf_chain_engine.py +294 -256
  38. souleyez/core/msf_database.py +153 -70
  39. souleyez/core/msf_integration.py +679 -673
  40. souleyez/core/msf_rpc_client.py +40 -42
  41. souleyez/core/msf_rpc_manager.py +77 -79
  42. souleyez/core/msf_sync_manager.py +241 -181
  43. souleyez/core/network_utils.py +22 -15
  44. souleyez/core/parser_handler.py +34 -25
  45. souleyez/core/pending_chains.py +114 -63
  46. souleyez/core/templates.py +158 -107
  47. souleyez/core/tool_chaining.py +9592 -2879
  48. souleyez/core/version_utils.py +79 -94
  49. souleyez/core/vuln_correlation.py +136 -89
  50. souleyez/core/web_utils.py +33 -32
  51. souleyez/data/wordlists/ad_users.txt +378 -0
  52. souleyez/data/wordlists/api_endpoints_large.txt +769 -0
  53. souleyez/data/wordlists/home_dir_sensitive.txt +39 -0
  54. souleyez/data/wordlists/lfi_payloads.txt +82 -0
  55. souleyez/data/wordlists/passwords_brute.txt +1548 -0
  56. souleyez/data/wordlists/passwords_crack.txt +2479 -0
  57. souleyez/data/wordlists/passwords_spray.txt +386 -0
  58. souleyez/data/wordlists/subdomains_large.txt +5057 -0
  59. souleyez/data/wordlists/usernames_common.txt +694 -0
  60. souleyez/data/wordlists/web_dirs_large.txt +4769 -0
  61. souleyez/detection/__init__.py +1 -1
  62. souleyez/detection/attack_signatures.py +12 -17
  63. souleyez/detection/mitre_mappings.py +61 -55
  64. souleyez/detection/validator.py +97 -86
  65. souleyez/devtools.py +23 -10
  66. souleyez/docs/README.md +4 -4
  67. souleyez/docs/api-reference/cli-commands.md +2 -2
  68. souleyez/docs/developer-guide/adding-new-tools.md +562 -0
  69. souleyez/docs/user-guide/auto-chaining.md +30 -8
  70. souleyez/docs/user-guide/getting-started.md +1 -1
  71. souleyez/docs/user-guide/installation.md +26 -3
  72. souleyez/docs/user-guide/metasploit-integration.md +2 -2
  73. souleyez/docs/user-guide/rbac.md +1 -1
  74. souleyez/docs/user-guide/scope-management.md +1 -1
  75. souleyez/docs/user-guide/siem-integration.md +1 -1
  76. souleyez/docs/user-guide/tools-reference.md +1 -8
  77. souleyez/docs/user-guide/worker-management.md +1 -1
  78. souleyez/engine/background.py +1238 -535
  79. souleyez/engine/base.py +4 -1
  80. souleyez/engine/job_status.py +17 -49
  81. souleyez/engine/log_sanitizer.py +103 -77
  82. souleyez/engine/manager.py +38 -7
  83. souleyez/engine/result_handler.py +2198 -1550
  84. souleyez/engine/worker_manager.py +50 -41
  85. souleyez/export/evidence_bundle.py +72 -62
  86. souleyez/feature_flags/features.py +16 -20
  87. souleyez/feature_flags.py +5 -9
  88. souleyez/handlers/__init__.py +11 -0
  89. souleyez/handlers/base.py +188 -0
  90. souleyez/handlers/bash_handler.py +277 -0
  91. souleyez/handlers/bloodhound_handler.py +243 -0
  92. souleyez/handlers/certipy_handler.py +311 -0
  93. souleyez/handlers/crackmapexec_handler.py +486 -0
  94. souleyez/handlers/dnsrecon_handler.py +344 -0
  95. souleyez/handlers/enum4linux_handler.py +400 -0
  96. souleyez/handlers/evil_winrm_handler.py +493 -0
  97. souleyez/handlers/ffuf_handler.py +815 -0
  98. souleyez/handlers/gobuster_handler.py +1114 -0
  99. souleyez/handlers/gpp_extract_handler.py +334 -0
  100. souleyez/handlers/hashcat_handler.py +444 -0
  101. souleyez/handlers/hydra_handler.py +563 -0
  102. souleyez/handlers/impacket_getuserspns_handler.py +343 -0
  103. souleyez/handlers/impacket_psexec_handler.py +222 -0
  104. souleyez/handlers/impacket_secretsdump_handler.py +426 -0
  105. souleyez/handlers/john_handler.py +286 -0
  106. souleyez/handlers/katana_handler.py +425 -0
  107. souleyez/handlers/kerbrute_handler.py +298 -0
  108. souleyez/handlers/ldapsearch_handler.py +636 -0
  109. souleyez/handlers/lfi_extract_handler.py +464 -0
  110. souleyez/handlers/msf_auxiliary_handler.py +408 -0
  111. souleyez/handlers/msf_exploit_handler.py +380 -0
  112. souleyez/handlers/nikto_handler.py +413 -0
  113. souleyez/handlers/nmap_handler.py +821 -0
  114. souleyez/handlers/nuclei_handler.py +359 -0
  115. souleyez/handlers/nxc_handler.py +371 -0
  116. souleyez/handlers/rdp_sec_check_handler.py +353 -0
  117. souleyez/handlers/registry.py +288 -0
  118. souleyez/handlers/responder_handler.py +232 -0
  119. souleyez/handlers/service_explorer_handler.py +434 -0
  120. souleyez/handlers/smbclient_handler.py +344 -0
  121. souleyez/handlers/smbmap_handler.py +510 -0
  122. souleyez/handlers/smbpasswd_handler.py +296 -0
  123. souleyez/handlers/sqlmap_handler.py +1116 -0
  124. souleyez/handlers/theharvester_handler.py +601 -0
  125. souleyez/handlers/whois_handler.py +277 -0
  126. souleyez/handlers/wpscan_handler.py +554 -0
  127. souleyez/history.py +32 -16
  128. souleyez/importers/msf_importer.py +106 -75
  129. souleyez/importers/smart_importer.py +208 -147
  130. souleyez/integrations/siem/__init__.py +10 -10
  131. souleyez/integrations/siem/base.py +17 -18
  132. souleyez/integrations/siem/elastic.py +108 -122
  133. souleyez/integrations/siem/factory.py +207 -80
  134. souleyez/integrations/siem/googlesecops.py +146 -154
  135. souleyez/integrations/siem/rule_mappings/__init__.py +1 -1
  136. souleyez/integrations/siem/rule_mappings/wazuh_rules.py +8 -5
  137. souleyez/integrations/siem/sentinel.py +107 -109
  138. souleyez/integrations/siem/splunk.py +246 -212
  139. souleyez/integrations/siem/wazuh.py +65 -71
  140. souleyez/integrations/wazuh/__init__.py +5 -5
  141. souleyez/integrations/wazuh/client.py +70 -93
  142. souleyez/integrations/wazuh/config.py +85 -57
  143. souleyez/integrations/wazuh/host_mapper.py +28 -36
  144. souleyez/integrations/wazuh/sync.py +78 -68
  145. souleyez/intelligence/__init__.py +4 -5
  146. souleyez/intelligence/correlation_analyzer.py +309 -295
  147. souleyez/intelligence/exploit_knowledge.py +661 -623
  148. souleyez/intelligence/exploit_suggestions.py +159 -139
  149. souleyez/intelligence/gap_analyzer.py +132 -97
  150. souleyez/intelligence/gap_detector.py +251 -214
  151. souleyez/intelligence/sensitive_tables.py +266 -129
  152. souleyez/intelligence/service_parser.py +137 -123
  153. souleyez/intelligence/surface_analyzer.py +407 -268
  154. souleyez/intelligence/target_parser.py +159 -162
  155. souleyez/licensing/__init__.py +6 -6
  156. souleyez/licensing/validator.py +17 -19
  157. souleyez/log_config.py +79 -54
  158. souleyez/main.py +1505 -687
  159. souleyez/migrations/fix_job_counter.py +16 -14
  160. souleyez/parsers/bloodhound_parser.py +41 -39
  161. souleyez/parsers/crackmapexec_parser.py +178 -111
  162. souleyez/parsers/dalfox_parser.py +72 -77
  163. souleyez/parsers/dnsrecon_parser.py +103 -91
  164. souleyez/parsers/enum4linux_parser.py +183 -153
  165. souleyez/parsers/ffuf_parser.py +29 -25
  166. souleyez/parsers/gobuster_parser.py +301 -41
  167. souleyez/parsers/hashcat_parser.py +324 -79
  168. souleyez/parsers/http_fingerprint_parser.py +350 -103
  169. souleyez/parsers/hydra_parser.py +131 -111
  170. souleyez/parsers/impacket_parser.py +231 -178
  171. souleyez/parsers/john_parser.py +98 -86
  172. souleyez/parsers/katana_parser.py +316 -0
  173. souleyez/parsers/msf_parser.py +943 -498
  174. souleyez/parsers/nikto_parser.py +346 -65
  175. souleyez/parsers/nmap_parser.py +262 -174
  176. souleyez/parsers/nuclei_parser.py +40 -44
  177. souleyez/parsers/responder_parser.py +26 -26
  178. souleyez/parsers/searchsploit_parser.py +74 -74
  179. souleyez/parsers/service_explorer_parser.py +279 -0
  180. souleyez/parsers/smbmap_parser.py +180 -124
  181. souleyez/parsers/sqlmap_parser.py +434 -308
  182. souleyez/parsers/theharvester_parser.py +75 -57
  183. souleyez/parsers/whois_parser.py +135 -94
  184. souleyez/parsers/wpscan_parser.py +278 -190
  185. souleyez/plugins/afp.py +44 -36
  186. souleyez/plugins/afp_brute.py +114 -46
  187. souleyez/plugins/ard.py +48 -37
  188. souleyez/plugins/bloodhound.py +95 -61
  189. souleyez/plugins/certipy.py +303 -0
  190. souleyez/plugins/crackmapexec.py +186 -85
  191. souleyez/plugins/dalfox.py +120 -59
  192. souleyez/plugins/dns_hijack.py +146 -41
  193. souleyez/plugins/dnsrecon.py +97 -61
  194. souleyez/plugins/enum4linux.py +91 -66
  195. souleyez/plugins/evil_winrm.py +291 -0
  196. souleyez/plugins/ffuf.py +166 -90
  197. souleyez/plugins/firmware_extract.py +133 -29
  198. souleyez/plugins/gobuster.py +387 -190
  199. souleyez/plugins/gpp_extract.py +393 -0
  200. souleyez/plugins/hashcat.py +100 -73
  201. souleyez/plugins/http_fingerprint.py +854 -267
  202. souleyez/plugins/hydra.py +566 -200
  203. souleyez/plugins/impacket_getnpusers.py +117 -69
  204. souleyez/plugins/impacket_psexec.py +84 -64
  205. souleyez/plugins/impacket_secretsdump.py +103 -69
  206. souleyez/plugins/impacket_smbclient.py +89 -75
  207. souleyez/plugins/john.py +86 -69
  208. souleyez/plugins/katana.py +313 -0
  209. souleyez/plugins/kerbrute.py +237 -0
  210. souleyez/plugins/lfi_extract.py +541 -0
  211. souleyez/plugins/macos_ssh.py +117 -48
  212. souleyez/plugins/mdns.py +35 -30
  213. souleyez/plugins/msf_auxiliary.py +253 -130
  214. souleyez/plugins/msf_exploit.py +239 -161
  215. souleyez/plugins/nikto.py +134 -78
  216. souleyez/plugins/nmap.py +275 -91
  217. souleyez/plugins/nuclei.py +180 -89
  218. souleyez/plugins/nxc.py +285 -0
  219. souleyez/plugins/plugin_base.py +35 -36
  220. souleyez/plugins/plugin_template.py +13 -5
  221. souleyez/plugins/rdp_sec_check.py +130 -0
  222. souleyez/plugins/responder.py +112 -71
  223. souleyez/plugins/router_http_brute.py +76 -65
  224. souleyez/plugins/router_ssh_brute.py +118 -41
  225. souleyez/plugins/router_telnet_brute.py +124 -42
  226. souleyez/plugins/routersploit.py +91 -59
  227. souleyez/plugins/routersploit_exploit.py +77 -55
  228. souleyez/plugins/searchsploit.py +91 -77
  229. souleyez/plugins/service_explorer.py +1160 -0
  230. souleyez/plugins/smbmap.py +122 -72
  231. souleyez/plugins/smbpasswd.py +215 -0
  232. souleyez/plugins/sqlmap.py +301 -113
  233. souleyez/plugins/theharvester.py +127 -75
  234. souleyez/plugins/tr069.py +79 -57
  235. souleyez/plugins/upnp.py +65 -47
  236. souleyez/plugins/upnp_abuse.py +73 -55
  237. souleyez/plugins/vnc_access.py +129 -42
  238. souleyez/plugins/vnc_brute.py +109 -38
  239. souleyez/plugins/whois.py +77 -58
  240. souleyez/plugins/wpscan.py +173 -69
  241. souleyez/reporting/__init__.py +2 -1
  242. souleyez/reporting/attack_chain.py +411 -346
  243. souleyez/reporting/charts.py +436 -501
  244. souleyez/reporting/compliance_mappings.py +334 -201
  245. souleyez/reporting/detection_report.py +126 -125
  246. souleyez/reporting/formatters.py +828 -591
  247. souleyez/reporting/generator.py +386 -302
  248. souleyez/reporting/metrics.py +72 -75
  249. souleyez/scanner.py +35 -29
  250. souleyez/security/__init__.py +37 -11
  251. souleyez/security/scope_validator.py +175 -106
  252. souleyez/security/validation.py +223 -149
  253. souleyez/security.py +22 -6
  254. souleyez/storage/credentials.py +247 -186
  255. souleyez/storage/crypto.py +296 -129
  256. souleyez/storage/database.py +73 -50
  257. souleyez/storage/db.py +58 -36
  258. souleyez/storage/deliverable_evidence.py +177 -128
  259. souleyez/storage/deliverable_exporter.py +282 -246
  260. souleyez/storage/deliverable_templates.py +134 -116
  261. souleyez/storage/deliverables.py +135 -130
  262. souleyez/storage/engagements.py +109 -56
  263. souleyez/storage/evidence.py +181 -152
  264. souleyez/storage/execution_log.py +31 -17
  265. souleyez/storage/exploit_attempts.py +93 -57
  266. souleyez/storage/exploits.py +67 -36
  267. souleyez/storage/findings.py +48 -61
  268. souleyez/storage/hosts.py +176 -144
  269. souleyez/storage/migrate_to_engagements.py +43 -19
  270. souleyez/storage/migrations/_001_add_credential_enhancements.py +22 -12
  271. souleyez/storage/migrations/_002_add_status_tracking.py +10 -7
  272. souleyez/storage/migrations/_003_add_execution_log.py +14 -8
  273. souleyez/storage/migrations/_005_screenshots.py +13 -5
  274. souleyez/storage/migrations/_006_deliverables.py +13 -5
  275. souleyez/storage/migrations/_007_deliverable_templates.py +12 -7
  276. souleyez/storage/migrations/_008_add_nuclei_table.py +10 -4
  277. souleyez/storage/migrations/_010_evidence_linking.py +17 -10
  278. souleyez/storage/migrations/_011_timeline_tracking.py +20 -13
  279. souleyez/storage/migrations/_012_team_collaboration.py +34 -21
  280. souleyez/storage/migrations/_013_add_host_tags.py +12 -6
  281. souleyez/storage/migrations/_014_exploit_attempts.py +22 -10
  282. souleyez/storage/migrations/_015_add_mac_os_fields.py +15 -7
  283. souleyez/storage/migrations/_016_add_domain_field.py +10 -4
  284. souleyez/storage/migrations/_017_msf_sessions.py +16 -8
  285. souleyez/storage/migrations/_018_add_osint_target.py +10 -6
  286. souleyez/storage/migrations/_019_add_engagement_type.py +10 -6
  287. souleyez/storage/migrations/_020_add_rbac.py +36 -15
  288. souleyez/storage/migrations/_021_wazuh_integration.py +20 -8
  289. souleyez/storage/migrations/_022_wazuh_indexer_columns.py +6 -4
  290. souleyez/storage/migrations/_023_fix_detection_results_fk.py +16 -6
  291. souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +26 -10
  292. souleyez/storage/migrations/_025_multi_siem_support.py +3 -5
  293. souleyez/storage/migrations/_026_add_engagement_scope.py +31 -12
  294. souleyez/storage/migrations/_027_multi_siem_persistence.py +32 -15
  295. souleyez/storage/migrations/__init__.py +26 -26
  296. souleyez/storage/migrations/migration_manager.py +19 -19
  297. souleyez/storage/msf_sessions.py +100 -65
  298. souleyez/storage/osint.py +17 -24
  299. souleyez/storage/recommendation_engine.py +269 -235
  300. souleyez/storage/screenshots.py +33 -32
  301. souleyez/storage/smb_shares.py +136 -92
  302. souleyez/storage/sqlmap_data.py +183 -128
  303. souleyez/storage/team_collaboration.py +135 -141
  304. souleyez/storage/timeline_tracker.py +122 -94
  305. souleyez/storage/wazuh_vulns.py +64 -66
  306. souleyez/storage/web_paths.py +33 -37
  307. souleyez/testing/credential_tester.py +221 -205
  308. souleyez/ui/__init__.py +1 -1
  309. souleyez/ui/ai_quotes.py +12 -12
  310. souleyez/ui/attack_surface.py +2439 -1516
  311. souleyez/ui/chain_rules_view.py +914 -382
  312. souleyez/ui/correlation_view.py +312 -230
  313. souleyez/ui/dashboard.py +2382 -1130
  314. souleyez/ui/deliverables_view.py +148 -62
  315. souleyez/ui/design_system.py +13 -13
  316. souleyez/ui/errors.py +49 -49
  317. souleyez/ui/evidence_linking_view.py +284 -179
  318. souleyez/ui/evidence_vault.py +393 -285
  319. souleyez/ui/exploit_suggestions_view.py +555 -349
  320. souleyez/ui/export_view.py +100 -66
  321. souleyez/ui/gap_analysis_view.py +315 -171
  322. souleyez/ui/help_system.py +105 -97
  323. souleyez/ui/intelligence_view.py +436 -293
  324. souleyez/ui/interactive.py +23142 -10430
  325. souleyez/ui/interactive_selector.py +75 -68
  326. souleyez/ui/log_formatter.py +47 -39
  327. souleyez/ui/menu_components.py +22 -13
  328. souleyez/ui/msf_auxiliary_menu.py +184 -133
  329. souleyez/ui/pending_chains_view.py +336 -172
  330. souleyez/ui/progress_indicators.py +5 -3
  331. souleyez/ui/recommendations_view.py +195 -137
  332. souleyez/ui/rule_builder.py +343 -225
  333. souleyez/ui/setup_wizard.py +678 -284
  334. souleyez/ui/shortcuts.py +217 -165
  335. souleyez/ui/splunk_gap_analysis_view.py +452 -270
  336. souleyez/ui/splunk_vulns_view.py +139 -86
  337. souleyez/ui/team_dashboard.py +498 -335
  338. souleyez/ui/template_selector.py +196 -105
  339. souleyez/ui/terminal.py +6 -6
  340. souleyez/ui/timeline_view.py +198 -127
  341. souleyez/ui/tool_setup.py +264 -164
  342. souleyez/ui/tutorial.py +202 -72
  343. souleyez/ui/tutorial_state.py +40 -40
  344. souleyez/ui/wazuh_vulns_view.py +235 -141
  345. souleyez/ui/wordlist_browser.py +260 -107
  346. souleyez/ui.py +464 -312
  347. souleyez/utils/tool_checker.py +427 -367
  348. souleyez/utils.py +33 -29
  349. souleyez/wordlists.py +134 -167
  350. {souleyez-2.43.28.dist-info → souleyez-2.43.32.dist-info}/METADATA +1 -1
  351. souleyez-2.43.32.dist-info/RECORD +441 -0
  352. {souleyez-2.43.28.dist-info → souleyez-2.43.32.dist-info}/WHEEL +1 -1
  353. souleyez-2.43.28.dist-info/RECORD +0 -379
  354. {souleyez-2.43.28.dist-info → souleyez-2.43.32.dist-info}/entry_points.txt +0 -0
  355. {souleyez-2.43.28.dist-info → souleyez-2.43.32.dist-info}/licenses/LICENSE +0 -0
  356. {souleyez-2.43.28.dist-info → souleyez-2.43.32.dist-info}/top_level.txt +0 -0
@@ -56,168 +56,189 @@ def parse_smbmap_output(output: str, target: str = "") -> Dict[str, Any]:
56
56
  }
57
57
  """
58
58
  result = {
59
- 'target': target,
60
- 'status': None,
61
- 'shares': [],
62
- 'files': [],
63
- 'smb_detected': False,
64
- 'hosts_count': 0,
65
- 'error': None
59
+ "target": target,
60
+ "status": None,
61
+ "shares": [],
62
+ "files": [],
63
+ "smb_detected": False,
64
+ "hosts_count": 0,
65
+ "error": None,
66
66
  }
67
67
 
68
68
  # Check for SMB detection (even if tool crashes later)
69
69
  # [*] Detected 1 hosts serving SMB
70
- smb_detected_match = re.search(r'\[\*\]\s*Detected\s+(\d+)\s+hosts?\s+serving\s+SMB', output)
70
+ smb_detected_match = re.search(
71
+ r"\[\*\]\s*Detected\s+(\d+)\s+hosts?\s+serving\s+SMB", output
72
+ )
71
73
  if smb_detected_match:
72
- result['smb_detected'] = True
73
- result['hosts_count'] = int(smb_detected_match.group(1))
74
+ result["smb_detected"] = True
75
+ result["hosts_count"] = int(smb_detected_match.group(1))
74
76
 
75
77
  # Check for Python traceback (tool crash)
76
- if 'Traceback (most recent call last):' in output:
78
+ if "Traceback (most recent call last):" in output:
77
79
  # Extract error message from traceback
78
- error_match = re.search(r'(?:Error|Exception).*?[\'"]([^\'"]+)[\'"]', output, re.DOTALL)
80
+ error_match = re.search(
81
+ r'(?:Error|Exception).*?[\'"]([^\'"]+)[\'"]', output, re.DOTALL
82
+ )
79
83
  if error_match:
80
- result['error'] = error_match.group(1)
84
+ result["error"] = error_match.group(1)
81
85
  else:
82
86
  # Try to get the last line of the traceback
83
- traceback_lines = output.split('Traceback (most recent call last):')[-1].strip().split('\n')
87
+ traceback_lines = (
88
+ output.split("Traceback (most recent call last):")[-1]
89
+ .strip()
90
+ .split("\n")
91
+ )
84
92
  for line in reversed(traceback_lines):
85
93
  line = line.strip()
86
- if line and not line.startswith('File') and not line.startswith('raise'):
87
- result['error'] = line[:200] # Limit length
94
+ if (
95
+ line
96
+ and not line.startswith("File")
97
+ and not line.startswith("raise")
98
+ ):
99
+ result["error"] = line[:200] # Limit length
88
100
  break
89
101
 
90
- lines = output.split('\n')
102
+ lines = output.split("\n")
91
103
  in_share_table = False
92
104
  current_share = None
93
105
 
94
106
  for i, line in enumerate(lines):
95
107
  # Remove ANSI color codes and control characters more thoroughly
96
- line = re.sub(r'\x1b\[[0-9;]*[a-zA-Z]', '', line) # All ANSI escape sequences
97
- line = re.sub(r'\x1b\].*?\x07', '', line) # OSC sequences
108
+ line = re.sub(r"\x1b\[[0-9;]*[a-zA-Z]", "", line) # All ANSI escape sequences
109
+ line = re.sub(r"\x1b\].*?\x07", "", line) # OSC sequences
98
110
  # Only remove leading progress indicators, not all brackets
99
- line = re.sub(r'^[\[\]\|/\\-]+\s*', '', line)
111
+ line = re.sub(r"^[\[\]\|/\\-]+\s*", "", line)
100
112
  line = line.strip()
101
113
 
102
114
  # Extract target and status
103
115
  # [+] IP: 10.0.0.82:445 Name: 10.0.0.82 Status: Authenticated
104
- if line.startswith('+') and 'IP:' in line and 'Status:' in line:
105
- status_match = re.search(r'Status:\s+(\w+)', line)
116
+ if line.startswith("+") and "IP:" in line and "Status:" in line:
117
+ status_match = re.search(r"Status:\s+(\w+)", line)
106
118
  if status_match:
107
- result['status'] = status_match.group(1)
119
+ result["status"] = status_match.group(1)
108
120
 
109
121
  # Extract target IP if not provided
110
- if not result['target']:
111
- ip_match = re.search(r'IP:\s+([\d\.]+)', line)
122
+ if not result["target"]:
123
+ ip_match = re.search(r"IP:\s+([\d\.]+)", line)
112
124
  if ip_match:
113
- result['target'] = ip_match.group(1)
125
+ result["target"] = ip_match.group(1)
114
126
 
115
127
  # Detect share table header
116
128
  # Disk Permissions Comment
117
- elif 'Disk' in line and 'Permissions' in line and 'Comment' in line:
129
+ elif "Disk" in line and "Permissions" in line and "Comment" in line:
118
130
  in_share_table = True
119
131
  continue
120
132
 
121
133
  # Skip separator line
122
- elif line.startswith('----') or line.startswith('==='):
134
+ elif line.startswith("----") or line.startswith("==="):
123
135
  continue
124
136
 
125
137
  # Parse share entries
126
- elif in_share_table and line and not line.startswith('*') and not line.startswith('Closed'):
138
+ elif (
139
+ in_share_table
140
+ and line
141
+ and not line.startswith("*")
142
+ and not line.startswith("Closed")
143
+ ):
127
144
  # Try to parse share line
128
145
  # Format: sharename <tabs/spaces> permissions <tabs/spaces> comment
129
146
  # tmp READ, WRITE oh noes!
130
147
 
131
148
  share_name = None
132
149
  permissions = None
133
- comment = ''
150
+ comment = ""
134
151
 
135
152
  # Try tab split first
136
- parts = re.split(r'\t+', line)
153
+ parts = re.split(r"\t+", line)
137
154
  if len(parts) >= 2:
138
155
  share_name = parts[0].strip()
139
156
  # Find permissions in remaining parts
140
157
  for p in parts[1:]:
141
158
  p = p.strip().upper()
142
- if any(x in p for x in ['READ', 'WRITE', 'NO ACCESS', 'NOACCESS']):
159
+ if any(x in p for x in ["READ", "WRITE", "NO ACCESS", "NOACCESS"]):
143
160
  permissions = p
144
161
  break
145
162
  # Comment is everything after permissions
146
163
  if permissions and len(parts) > 2:
147
- perm_idx = next((i for i, p in enumerate(parts) if permissions in p.upper()), -1)
164
+ perm_idx = next(
165
+ (i for i, p in enumerate(parts) if permissions in p.upper()), -1
166
+ )
148
167
  if perm_idx >= 0 and perm_idx + 1 < len(parts):
149
- comment = ' '.join(parts[perm_idx + 1:]).strip()
168
+ comment = " ".join(parts[perm_idx + 1 :]).strip()
150
169
 
151
170
  # No tabs or tab parse failed - try space-based parsing
152
171
  if not permissions:
153
172
  # Match patterns with flexible spacing and permission variations
154
173
  permission_patterns = [
155
- r'^\s*(\S+)\s{2,}(READ,?\s*WRITE|READ\s*ONLY|WRITE\s*ONLY|NO\s*ACCESS|READ|WRITE)(?:\s{2,}(.*))?$',
156
- r'^\s*(\S+)\s+(READ,?\s*WRITE|READ\s*ONLY|WRITE\s*ONLY|NO\s*ACCESS|READ|WRITE)\s*(.*)$',
174
+ r"^\s*(\S+)\s{2,}(READ,?\s*WRITE|READ\s*ONLY|WRITE\s*ONLY|NO\s*ACCESS|READ|WRITE)(?:\s{2,}(.*))?$",
175
+ r"^\s*(\S+)\s+(READ,?\s*WRITE|READ\s*ONLY|WRITE\s*ONLY|NO\s*ACCESS|READ|WRITE)\s*(.*)$",
157
176
  ]
158
177
  for pattern in permission_patterns:
159
178
  match = re.match(pattern, line, re.IGNORECASE)
160
179
  if match:
161
180
  share_name = match.group(1).strip()
162
181
  permissions = match.group(2).strip().upper()
163
- comment = match.group(3).strip() if match.group(3) else ''
182
+ comment = match.group(3).strip() if match.group(3) else ""
164
183
  break
165
184
 
166
185
  if not share_name or not permissions:
167
186
  continue
168
187
 
169
188
  # Skip empty lines or non-share lines
170
- if not share_name or share_name in ['Disk', 'IPC', '', '*']:
189
+ if not share_name or share_name in ["Disk", "IPC", "", "*"]:
171
190
  continue
172
191
 
173
192
  # Skip separator lines (----, ===, etc.)
174
- if re.match(r'^[\-=]+$', share_name):
193
+ if re.match(r"^[\-=]+$", share_name):
175
194
  continue
176
195
 
177
196
  # Determine share type (Disk vs IPC)
178
- share_type = 'IPC' if share_name.endswith('$') and 'IPC' in comment else 'Disk'
197
+ share_type = (
198
+ "IPC" if share_name.endswith("$") and "IPC" in comment else "Disk"
199
+ )
179
200
 
180
201
  # Parse permissions
181
- readable = 'READ' in permissions.upper()
182
- writable = 'WRITE' in permissions.upper()
202
+ readable = "READ" in permissions.upper()
203
+ writable = "WRITE" in permissions.upper()
183
204
 
184
205
  share_info = {
185
- 'name': share_name,
186
- 'type': share_type,
187
- 'permissions': permissions,
188
- 'comment': comment,
189
- 'readable': readable,
190
- 'writable': writable
206
+ "name": share_name,
207
+ "type": share_type,
208
+ "permissions": permissions,
209
+ "comment": comment,
210
+ "readable": readable,
211
+ "writable": writable,
191
212
  }
192
213
 
193
- result['shares'].append(share_info)
214
+ result["shares"].append(share_info)
194
215
  current_share = share_name
195
216
 
196
217
  # Parse file listings (if -R was used)
197
218
  # dr--r--r-- 0 Sat May 16 14:06:55 2009 .
198
219
  # dr--r--r-- 0 Sat May 16 14:06:55 2009 ..
199
220
  # fr--r--r-- 512 Sat May 16 14:06:55 2009 script.sh
200
- elif current_share and re.match(r'^[df]r', line):
221
+ elif current_share and re.match(r"^[df]r", line):
201
222
  # File listing format: permissions size timestamp filename
202
- file_match = re.match(r'^([df]r[\-rwx]+)\s+(\d+)\s+(.+?)\s{2,}(.+)$', line)
223
+ file_match = re.match(r"^([df]r[\-rwx]+)\s+(\d+)\s+(.+?)\s{2,}(.+)$", line)
203
224
  if file_match:
204
225
  perms, size, timestamp, filename = file_match.groups()
205
226
 
206
227
  # Skip . and .. entries
207
- if filename in ['.', '..']:
228
+ if filename in [".", ".."]:
208
229
  continue
209
230
 
210
231
  file_info = {
211
- 'share': current_share,
212
- 'path': filename,
213
- 'size': int(size),
214
- 'timestamp': timestamp.strip(),
215
- 'is_directory': perms.startswith('d')
232
+ "share": current_share,
233
+ "path": filename,
234
+ "size": int(size),
235
+ "timestamp": timestamp.strip(),
236
+ "is_directory": perms.startswith("d"),
216
237
  }
217
- result['files'].append(file_info)
238
+ result["files"].append(file_info)
218
239
 
219
240
  # Detect end of output
220
- elif 'Closed' in line and 'connections' in line:
241
+ elif "Closed" in line and "connections" in line:
221
242
  in_share_table = False
222
243
  current_share = None
223
244
 
@@ -240,86 +261,121 @@ def extract_findings(parsed_data: Dict[str, Any]) -> List[Dict[str, Any]]:
240
261
  findings = []
241
262
 
242
263
  # Finding 1: Writable shares (HIGH severity)
243
- writable_shares = [s for s in parsed_data['shares'] if s['writable']]
264
+ writable_shares = [s for s in parsed_data["shares"] if s["writable"]]
244
265
  if writable_shares:
245
- share_names = ', '.join([s['name'] for s in writable_shares])
246
- findings.append({
247
- 'title': 'Writable SMB Shares Detected',
248
- 'severity': 'high',
249
- 'description': f"Found {len(writable_shares)} SMB share(s) with WRITE permissions: {share_names}. "
250
- "Writable shares can be exploited to upload malicious files, plant ransomware, "
251
- "or exfiltrate sensitive data.",
252
- 'evidence': '\n'.join([
253
- f"- {s['name']}: {s['permissions']} ({s['comment']})"
254
- for s in writable_shares
255
- ])
256
- })
266
+ share_names = ", ".join([s["name"] for s in writable_shares])
267
+ findings.append(
268
+ {
269
+ "title": "Writable SMB Shares Detected",
270
+ "severity": "high",
271
+ "description": f"Found {len(writable_shares)} SMB share(s) with WRITE permissions: {share_names}. "
272
+ "Writable shares can be exploited to upload malicious files, plant ransomware, "
273
+ "or exfiltrate sensitive data.",
274
+ "evidence": "\n".join(
275
+ [
276
+ f"- {s['name']}: {s['permissions']} ({s['comment']})"
277
+ for s in writable_shares
278
+ ]
279
+ ),
280
+ }
281
+ )
257
282
 
258
283
  # Finding 2: Readable shares (MEDIUM severity)
259
- readable_shares = [s for s in parsed_data['shares']
260
- if s['readable'] and not s['writable'] and s['type'] == 'Disk']
284
+ readable_shares = [
285
+ s
286
+ for s in parsed_data["shares"]
287
+ if s["readable"] and not s["writable"] and s["type"] == "Disk"
288
+ ]
261
289
  if readable_shares:
262
- share_names = ', '.join([s['name'] for s in readable_shares])
263
- findings.append({
264
- 'title': 'Readable SMB Shares Detected',
265
- 'severity': 'medium',
266
- 'description': f"Found {len(readable_shares)} SMB share(s) with READ permissions: {share_names}. "
267
- "These shares may contain sensitive information accessible without proper authentication.",
268
- 'evidence': '\n'.join([
269
- f"- {s['name']}: {s['permissions']} ({s['comment']})"
270
- for s in readable_shares
271
- ])
272
- })
290
+ share_names = ", ".join([s["name"] for s in readable_shares])
291
+ findings.append(
292
+ {
293
+ "title": "Readable SMB Shares Detected",
294
+ "severity": "medium",
295
+ "description": f"Found {len(readable_shares)} SMB share(s) with READ permissions: {share_names}. "
296
+ "These shares may contain sensitive information accessible without proper authentication.",
297
+ "evidence": "\n".join(
298
+ [
299
+ f"- {s['name']}: {s['permissions']} ({s['comment']})"
300
+ for s in readable_shares
301
+ ]
302
+ ),
303
+ }
304
+ )
273
305
 
274
306
  # Finding 3: Anonymous access (MEDIUM severity)
275
- if parsed_data.get('status') in ['Guest', 'Anonymous']:
276
- accessible_shares = [s for s in parsed_data['shares']
277
- if s['readable'] or s['writable']]
307
+ if parsed_data.get("status") in ["Guest", "Anonymous"]:
308
+ accessible_shares = [
309
+ s for s in parsed_data["shares"] if s["readable"] or s["writable"]
310
+ ]
278
311
  if accessible_shares:
279
- findings.append({
280
- 'title': 'Anonymous SMB Access Allowed',
281
- 'severity': 'medium',
282
- 'description': f"Anonymous/guest access is permitted, allowing access to {len(accessible_shares)} share(s) "
283
- "without authentication. This violates the principle of least privilege.",
284
- 'evidence': f"Authentication Status: {parsed_data.get('status')}\n"
285
- + '\n'.join([f"- {s['name']}: {s['permissions']}" for s in accessible_shares])
286
- })
312
+ findings.append(
313
+ {
314
+ "title": "Anonymous SMB Access Allowed",
315
+ "severity": "medium",
316
+ "description": f"Anonymous/guest access is permitted, allowing access to {len(accessible_shares)} share(s) "
317
+ "without authentication. This violates the principle of least privilege.",
318
+ "evidence": f"Authentication Status: {parsed_data.get('status')}\n"
319
+ + "\n".join(
320
+ [
321
+ f"- {s['name']}: {s['permissions']}"
322
+ for s in accessible_shares
323
+ ]
324
+ ),
325
+ }
326
+ )
287
327
 
288
328
  # Finding 4: Sensitive files exposed (if files were enumerated)
289
329
  sensitive_patterns = [
290
- (r'\.config$', 'Configuration files'),
291
- (r'password|passwd|pwd', 'Password files'),
292
- (r'\.key|\.pem|\.crt', 'Cryptographic keys/certificates'),
293
- (r'backup|\.bak|\.old', 'Backup files'),
294
- (r'\.sql|\.db|\.sqlite', 'Database files'),
295
- (r'id_rsa|id_dsa|\.ssh', 'SSH private keys')
330
+ (r"\.config$", "Configuration files"),
331
+ (r"password|passwd|pwd", "Password files"),
332
+ (r"\.key|\.pem|\.crt", "Cryptographic keys/certificates"),
333
+ (r"backup|\.bak|\.old", "Backup files"),
334
+ (r"\.sql|\.db|\.sqlite", "Database files"),
335
+ (r"id_rsa|id_dsa|\.ssh", "SSH private keys"),
296
336
  ]
297
337
 
298
338
  for pattern, desc in sensitive_patterns:
299
- matching_files = [f for f in parsed_data['files']
300
- if re.search(pattern, f['path'], re.IGNORECASE)]
339
+ matching_files = [
340
+ f
341
+ for f in parsed_data["files"]
342
+ if re.search(pattern, f["path"], re.IGNORECASE)
343
+ ]
301
344
  if matching_files:
302
- findings.append({
303
- 'title': f'Sensitive Files Exposed: {desc}',
304
- 'severity': 'high',
305
- 'description': f"Found {len(matching_files)} potentially sensitive file(s) ({desc}) "
306
- "accessible via SMB shares.",
307
- 'evidence': '\n'.join([
308
- f"- {f['share']}/{f['path']} ({f['size']} bytes)"
309
- for f in matching_files[:10] # Limit to first 10
310
- ]) + (f"\n... and {len(matching_files) - 10} more" if len(matching_files) > 10 else "")
311
- })
345
+ findings.append(
346
+ {
347
+ "title": f"Sensitive Files Exposed: {desc}",
348
+ "severity": "high",
349
+ "description": f"Found {len(matching_files)} potentially sensitive file(s) ({desc}) "
350
+ "accessible via SMB shares.",
351
+ "evidence": "\n".join(
352
+ [
353
+ f"- {f['share']}/{f['path']} ({f['size']} bytes)"
354
+ for f in matching_files[:10] # Limit to first 10
355
+ ]
356
+ )
357
+ + (
358
+ f"\n... and {len(matching_files) - 10} more"
359
+ if len(matching_files) > 10
360
+ else ""
361
+ ),
362
+ }
363
+ )
312
364
 
313
365
  # Finding 5: Info - All shares enumerated
314
- if parsed_data['shares']:
315
- findings.append({
316
- 'title': 'SMB Share Enumeration Successful',
317
- 'severity': 'info',
318
- 'description': f"Successfully enumerated {len(parsed_data['shares'])} SMB share(s) on target.",
319
- 'evidence': '\n'.join([
320
- f"- {s['name']} ({s['type']}): {s['permissions']}"
321
- for s in parsed_data['shares']
322
- ])
323
- })
366
+ if parsed_data["shares"]:
367
+ findings.append(
368
+ {
369
+ "title": "SMB Share Enumeration Successful",
370
+ "severity": "info",
371
+ "description": f"Successfully enumerated {len(parsed_data['shares'])} SMB share(s) on target.",
372
+ "evidence": "\n".join(
373
+ [
374
+ f"- {s['name']} ({s['type']}): {s['permissions']}"
375
+ for s in parsed_data["shares"]
376
+ ]
377
+ ),
378
+ }
379
+ )
324
380
 
325
381
  return findings