souleyez 2.43.29__py3-none-any.whl → 3.0.0__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 (358) 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 +9564 -2881
  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 +1239 -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 +2200 -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 +564 -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 +409 -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 +417 -0
  116. souleyez/handlers/rdp_sec_check_handler.py +353 -0
  117. souleyez/handlers/registry.py +292 -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/web_login_test_handler.py +327 -0
  126. souleyez/handlers/whois_handler.py +277 -0
  127. souleyez/handlers/wpscan_handler.py +554 -0
  128. souleyez/history.py +32 -16
  129. souleyez/importers/msf_importer.py +106 -75
  130. souleyez/importers/smart_importer.py +208 -147
  131. souleyez/integrations/siem/__init__.py +10 -10
  132. souleyez/integrations/siem/base.py +17 -18
  133. souleyez/integrations/siem/elastic.py +108 -122
  134. souleyez/integrations/siem/factory.py +207 -80
  135. souleyez/integrations/siem/googlesecops.py +146 -154
  136. souleyez/integrations/siem/rule_mappings/__init__.py +1 -1
  137. souleyez/integrations/siem/rule_mappings/wazuh_rules.py +8 -5
  138. souleyez/integrations/siem/sentinel.py +107 -109
  139. souleyez/integrations/siem/splunk.py +246 -212
  140. souleyez/integrations/siem/wazuh.py +65 -71
  141. souleyez/integrations/wazuh/__init__.py +5 -5
  142. souleyez/integrations/wazuh/client.py +70 -93
  143. souleyez/integrations/wazuh/config.py +85 -57
  144. souleyez/integrations/wazuh/host_mapper.py +28 -36
  145. souleyez/integrations/wazuh/sync.py +78 -68
  146. souleyez/intelligence/__init__.py +4 -5
  147. souleyez/intelligence/correlation_analyzer.py +309 -295
  148. souleyez/intelligence/exploit_knowledge.py +661 -623
  149. souleyez/intelligence/exploit_suggestions.py +159 -139
  150. souleyez/intelligence/gap_analyzer.py +132 -97
  151. souleyez/intelligence/gap_detector.py +251 -214
  152. souleyez/intelligence/sensitive_tables.py +266 -129
  153. souleyez/intelligence/service_parser.py +137 -123
  154. souleyez/intelligence/surface_analyzer.py +407 -268
  155. souleyez/intelligence/target_parser.py +159 -162
  156. souleyez/licensing/__init__.py +6 -6
  157. souleyez/licensing/validator.py +17 -19
  158. souleyez/log_config.py +79 -54
  159. souleyez/main.py +1505 -687
  160. souleyez/migrations/fix_job_counter.py +16 -14
  161. souleyez/parsers/bloodhound_parser.py +41 -39
  162. souleyez/parsers/crackmapexec_parser.py +178 -111
  163. souleyez/parsers/dalfox_parser.py +72 -77
  164. souleyez/parsers/dnsrecon_parser.py +103 -91
  165. souleyez/parsers/enum4linux_parser.py +183 -153
  166. souleyez/parsers/ffuf_parser.py +29 -25
  167. souleyez/parsers/gobuster_parser.py +301 -41
  168. souleyez/parsers/hashcat_parser.py +324 -79
  169. souleyez/parsers/http_fingerprint_parser.py +350 -103
  170. souleyez/parsers/hydra_parser.py +131 -111
  171. souleyez/parsers/impacket_parser.py +231 -178
  172. souleyez/parsers/john_parser.py +98 -86
  173. souleyez/parsers/katana_parser.py +316 -0
  174. souleyez/parsers/msf_parser.py +943 -498
  175. souleyez/parsers/nikto_parser.py +346 -65
  176. souleyez/parsers/nmap_parser.py +262 -174
  177. souleyez/parsers/nuclei_parser.py +40 -44
  178. souleyez/parsers/responder_parser.py +26 -26
  179. souleyez/parsers/searchsploit_parser.py +74 -74
  180. souleyez/parsers/service_explorer_parser.py +279 -0
  181. souleyez/parsers/smbmap_parser.py +180 -124
  182. souleyez/parsers/sqlmap_parser.py +434 -308
  183. souleyez/parsers/theharvester_parser.py +75 -57
  184. souleyez/parsers/whois_parser.py +135 -94
  185. souleyez/parsers/wpscan_parser.py +278 -190
  186. souleyez/plugins/afp.py +44 -36
  187. souleyez/plugins/afp_brute.py +114 -46
  188. souleyez/plugins/ard.py +48 -37
  189. souleyez/plugins/bloodhound.py +95 -61
  190. souleyez/plugins/certipy.py +303 -0
  191. souleyez/plugins/crackmapexec.py +186 -85
  192. souleyez/plugins/dalfox.py +120 -59
  193. souleyez/plugins/dns_hijack.py +146 -41
  194. souleyez/plugins/dnsrecon.py +97 -61
  195. souleyez/plugins/enum4linux.py +91 -66
  196. souleyez/plugins/evil_winrm.py +291 -0
  197. souleyez/plugins/ffuf.py +166 -90
  198. souleyez/plugins/firmware_extract.py +133 -29
  199. souleyez/plugins/gobuster.py +387 -190
  200. souleyez/plugins/gpp_extract.py +393 -0
  201. souleyez/plugins/hashcat.py +100 -73
  202. souleyez/plugins/http_fingerprint.py +913 -267
  203. souleyez/plugins/hydra.py +566 -200
  204. souleyez/plugins/impacket_getnpusers.py +117 -69
  205. souleyez/plugins/impacket_psexec.py +84 -64
  206. souleyez/plugins/impacket_secretsdump.py +103 -69
  207. souleyez/plugins/impacket_smbclient.py +89 -75
  208. souleyez/plugins/john.py +86 -69
  209. souleyez/plugins/katana.py +313 -0
  210. souleyez/plugins/kerbrute.py +237 -0
  211. souleyez/plugins/lfi_extract.py +541 -0
  212. souleyez/plugins/macos_ssh.py +117 -48
  213. souleyez/plugins/mdns.py +35 -30
  214. souleyez/plugins/msf_auxiliary.py +253 -130
  215. souleyez/plugins/msf_exploit.py +239 -161
  216. souleyez/plugins/nikto.py +134 -78
  217. souleyez/plugins/nmap.py +275 -91
  218. souleyez/plugins/nuclei.py +180 -89
  219. souleyez/plugins/nxc.py +285 -0
  220. souleyez/plugins/plugin_base.py +35 -36
  221. souleyez/plugins/plugin_template.py +13 -5
  222. souleyez/plugins/rdp_sec_check.py +130 -0
  223. souleyez/plugins/responder.py +112 -71
  224. souleyez/plugins/router_http_brute.py +76 -65
  225. souleyez/plugins/router_ssh_brute.py +118 -41
  226. souleyez/plugins/router_telnet_brute.py +124 -42
  227. souleyez/plugins/routersploit.py +91 -59
  228. souleyez/plugins/routersploit_exploit.py +77 -55
  229. souleyez/plugins/searchsploit.py +91 -77
  230. souleyez/plugins/service_explorer.py +1160 -0
  231. souleyez/plugins/smbmap.py +122 -72
  232. souleyez/plugins/smbpasswd.py +215 -0
  233. souleyez/plugins/sqlmap.py +301 -113
  234. souleyez/plugins/theharvester.py +127 -75
  235. souleyez/plugins/tr069.py +79 -57
  236. souleyez/plugins/upnp.py +65 -47
  237. souleyez/plugins/upnp_abuse.py +73 -55
  238. souleyez/plugins/vnc_access.py +129 -42
  239. souleyez/plugins/vnc_brute.py +109 -38
  240. souleyez/plugins/web_login_test.py +417 -0
  241. souleyez/plugins/whois.py +77 -58
  242. souleyez/plugins/wpscan.py +219 -69
  243. souleyez/reporting/__init__.py +2 -1
  244. souleyez/reporting/attack_chain.py +411 -346
  245. souleyez/reporting/charts.py +436 -501
  246. souleyez/reporting/compliance_mappings.py +334 -201
  247. souleyez/reporting/detection_report.py +126 -125
  248. souleyez/reporting/formatters.py +828 -591
  249. souleyez/reporting/generator.py +386 -302
  250. souleyez/reporting/metrics.py +72 -75
  251. souleyez/scanner.py +35 -29
  252. souleyez/security/__init__.py +37 -11
  253. souleyez/security/scope_validator.py +175 -106
  254. souleyez/security/validation.py +237 -149
  255. souleyez/security.py +22 -6
  256. souleyez/storage/credentials.py +247 -186
  257. souleyez/storage/crypto.py +296 -129
  258. souleyez/storage/database.py +73 -50
  259. souleyez/storage/db.py +58 -36
  260. souleyez/storage/deliverable_evidence.py +177 -128
  261. souleyez/storage/deliverable_exporter.py +282 -246
  262. souleyez/storage/deliverable_templates.py +134 -116
  263. souleyez/storage/deliverables.py +135 -130
  264. souleyez/storage/engagements.py +109 -56
  265. souleyez/storage/evidence.py +181 -152
  266. souleyez/storage/execution_log.py +31 -17
  267. souleyez/storage/exploit_attempts.py +93 -57
  268. souleyez/storage/exploits.py +67 -36
  269. souleyez/storage/findings.py +48 -61
  270. souleyez/storage/hosts.py +176 -144
  271. souleyez/storage/migrate_to_engagements.py +43 -19
  272. souleyez/storage/migrations/_001_add_credential_enhancements.py +22 -12
  273. souleyez/storage/migrations/_002_add_status_tracking.py +10 -7
  274. souleyez/storage/migrations/_003_add_execution_log.py +14 -8
  275. souleyez/storage/migrations/_005_screenshots.py +13 -5
  276. souleyez/storage/migrations/_006_deliverables.py +13 -5
  277. souleyez/storage/migrations/_007_deliverable_templates.py +12 -7
  278. souleyez/storage/migrations/_008_add_nuclei_table.py +10 -4
  279. souleyez/storage/migrations/_010_evidence_linking.py +17 -10
  280. souleyez/storage/migrations/_011_timeline_tracking.py +20 -13
  281. souleyez/storage/migrations/_012_team_collaboration.py +34 -21
  282. souleyez/storage/migrations/_013_add_host_tags.py +12 -6
  283. souleyez/storage/migrations/_014_exploit_attempts.py +22 -10
  284. souleyez/storage/migrations/_015_add_mac_os_fields.py +15 -7
  285. souleyez/storage/migrations/_016_add_domain_field.py +10 -4
  286. souleyez/storage/migrations/_017_msf_sessions.py +16 -8
  287. souleyez/storage/migrations/_018_add_osint_target.py +10 -6
  288. souleyez/storage/migrations/_019_add_engagement_type.py +10 -6
  289. souleyez/storage/migrations/_020_add_rbac.py +36 -15
  290. souleyez/storage/migrations/_021_wazuh_integration.py +20 -8
  291. souleyez/storage/migrations/_022_wazuh_indexer_columns.py +6 -4
  292. souleyez/storage/migrations/_023_fix_detection_results_fk.py +16 -6
  293. souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +26 -10
  294. souleyez/storage/migrations/_025_multi_siem_support.py +3 -5
  295. souleyez/storage/migrations/_026_add_engagement_scope.py +31 -12
  296. souleyez/storage/migrations/_027_multi_siem_persistence.py +32 -15
  297. souleyez/storage/migrations/__init__.py +26 -26
  298. souleyez/storage/migrations/migration_manager.py +19 -19
  299. souleyez/storage/msf_sessions.py +100 -65
  300. souleyez/storage/osint.py +17 -24
  301. souleyez/storage/recommendation_engine.py +269 -235
  302. souleyez/storage/screenshots.py +33 -32
  303. souleyez/storage/smb_shares.py +136 -92
  304. souleyez/storage/sqlmap_data.py +183 -128
  305. souleyez/storage/team_collaboration.py +135 -141
  306. souleyez/storage/timeline_tracker.py +122 -94
  307. souleyez/storage/wazuh_vulns.py +64 -66
  308. souleyez/storage/web_paths.py +33 -37
  309. souleyez/testing/credential_tester.py +221 -205
  310. souleyez/ui/__init__.py +1 -1
  311. souleyez/ui/ai_quotes.py +12 -12
  312. souleyez/ui/attack_surface.py +2439 -1516
  313. souleyez/ui/chain_rules_view.py +914 -382
  314. souleyez/ui/correlation_view.py +312 -230
  315. souleyez/ui/dashboard.py +2382 -1130
  316. souleyez/ui/deliverables_view.py +148 -62
  317. souleyez/ui/design_system.py +13 -13
  318. souleyez/ui/errors.py +49 -49
  319. souleyez/ui/evidence_linking_view.py +284 -179
  320. souleyez/ui/evidence_vault.py +393 -285
  321. souleyez/ui/exploit_suggestions_view.py +555 -349
  322. souleyez/ui/export_view.py +100 -66
  323. souleyez/ui/gap_analysis_view.py +315 -171
  324. souleyez/ui/help_system.py +105 -97
  325. souleyez/ui/intelligence_view.py +436 -293
  326. souleyez/ui/interactive.py +23034 -10679
  327. souleyez/ui/interactive_selector.py +75 -68
  328. souleyez/ui/log_formatter.py +47 -39
  329. souleyez/ui/menu_components.py +22 -13
  330. souleyez/ui/msf_auxiliary_menu.py +184 -133
  331. souleyez/ui/pending_chains_view.py +336 -172
  332. souleyez/ui/progress_indicators.py +5 -3
  333. souleyez/ui/recommendations_view.py +195 -137
  334. souleyez/ui/rule_builder.py +343 -225
  335. souleyez/ui/setup_wizard.py +678 -284
  336. souleyez/ui/shortcuts.py +217 -165
  337. souleyez/ui/splunk_gap_analysis_view.py +452 -270
  338. souleyez/ui/splunk_vulns_view.py +139 -86
  339. souleyez/ui/team_dashboard.py +498 -335
  340. souleyez/ui/template_selector.py +196 -105
  341. souleyez/ui/terminal.py +6 -6
  342. souleyez/ui/timeline_view.py +198 -127
  343. souleyez/ui/tool_setup.py +264 -164
  344. souleyez/ui/tutorial.py +202 -72
  345. souleyez/ui/tutorial_state.py +40 -40
  346. souleyez/ui/wazuh_vulns_view.py +235 -141
  347. souleyez/ui/wordlist_browser.py +260 -107
  348. souleyez/ui.py +464 -312
  349. souleyez/utils/tool_checker.py +427 -367
  350. souleyez/utils.py +33 -29
  351. souleyez/wordlists.py +134 -167
  352. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/METADATA +2 -2
  353. souleyez-3.0.0.dist-info/RECORD +443 -0
  354. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/WHEEL +1 -1
  355. souleyez-2.43.29.dist-info/RECORD +0 -379
  356. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/entry_points.txt +0 -0
  357. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/licenses/LICENSE +0 -0
  358. {souleyez-2.43.29.dist-info → souleyez-3.0.0.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