souleyez 2.43.26__py3-none-any.whl → 2.43.34__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 +9526 -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 +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 +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 +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 +854 -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 +173 -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 +223 -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 +23434 -10286
  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.26.dist-info → souleyez-2.43.34.dist-info}/METADATA +1 -1
  353. souleyez-2.43.34.dist-info/RECORD +443 -0
  354. {souleyez-2.43.26.dist-info → souleyez-2.43.34.dist-info}/WHEEL +1 -1
  355. souleyez-2.43.26.dist-info/RECORD +0 -379
  356. {souleyez-2.43.26.dist-info → souleyez-2.43.34.dist-info}/entry_points.txt +0 -0
  357. {souleyez-2.43.26.dist-info → souleyez-2.43.34.dist-info}/licenses/LICENSE +0 -0
  358. {souleyez-2.43.26.dist-info → souleyez-2.43.34.dist-info}/top_level.txt +0 -0
@@ -15,7 +15,7 @@ from typing import Dict, Any, List
15
15
  def parse_secretsdump(log_path: str, target: str) -> Dict[str, Any]:
16
16
  """
17
17
  Parse secretsdump output for credentials and hashes.
18
-
18
+
19
19
  Output format:
20
20
  [*] Dumping local SAM hashes
21
21
  Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
@@ -25,11 +25,13 @@ def parse_secretsdump(log_path: str, target: str) -> Dict[str, Any]:
25
25
  credentials = []
26
26
  hashes = []
27
27
  tickets = []
28
-
28
+ lsa_secrets = []
29
+ kerberos_keys = []
30
+
29
31
  try:
30
- with open(log_path, 'r', encoding='utf-8') as f:
32
+ with open(log_path, "r", encoding="utf-8") as f:
31
33
  content = f.read()
32
-
34
+
33
35
  # Parse NTLM hashes with multiple format support
34
36
  # Format 1: username:RID:LM:NT::: (standard secretsdump)
35
37
  # Format 2: username:RID:LM:NT:: (without trailing colon)
@@ -38,10 +40,11 @@ def parse_secretsdump(log_path: str, target: str) -> Dict[str, Any]:
38
40
 
39
41
  # Standard format with 32-char hashes and trailing colons
40
42
  hash_patterns = [
41
- r'([^:\s\\]+):(\d+):([0-9a-fA-F]{32}):([0-9a-fA-F]{32}):::?', # Standard
42
- r'([^:\s]+)\\([^:\s]+):(\d+):([0-9a-fA-F]{32}):([0-9a-fA-F]{32}):::?', # Domain\user
43
+ r"([^:\s\\]+):(\d+):([0-9a-fA-F]{32}):([0-9a-fA-F]{32}):::?", # Standard
44
+ r"([^:\s]+)\\([^:\s]+):(\d+):([0-9a-fA-F]{32}):([0-9a-fA-F]{32}):::?", # Domain\user
43
45
  ]
44
46
 
47
+ seen_hashes = set() # Deduplicate hashes
45
48
  for pattern in hash_patterns:
46
49
  for match in re.finditer(pattern, content):
47
50
  groups = match.groups()
@@ -54,18 +57,60 @@ def parse_secretsdump(log_path: str, target: str) -> Dict[str, Any]:
54
57
  else:
55
58
  continue
56
59
 
57
- # Skip empty hashes (blank password indicator)
58
- if nt_hash.lower() == '31d6cfe0d16ae931b73c59d7e0c089c0':
60
+ # Skip empty hashes (blank password indicator) - but keep Guest for completeness
61
+ if (
62
+ nt_hash.lower() == "31d6cfe0d16ae931b73c59d7e0c089c0"
63
+ and username.lower() != "guest"
64
+ ):
65
+ continue
66
+
67
+ # Deduplicate by username:nt_hash
68
+ hash_key = f"{username}:{nt_hash}"
69
+ if hash_key in seen_hashes:
59
70
  continue
71
+ seen_hashes.add(hash_key)
72
+
73
+ hashes.append(
74
+ {
75
+ "username": username,
76
+ "rid": rid,
77
+ "lm_hash": lm_hash,
78
+ "nt_hash": nt_hash,
79
+ "hash_type": "NTLM",
80
+ }
81
+ )
82
+
83
+ # Parse LSA secrets (DefaultPassword, etc.)
84
+ # Format: [*] DefaultPassword
85
+ # (Unknown User):ROOT#123
86
+ lsa_pattern = r"\[\*\]\s*DefaultPassword\s*\n\s*\([^)]*\):([^\n]+)"
87
+ for match in re.finditer(lsa_pattern, content):
88
+ password = match.group(1).strip()
89
+ if password and not password.startswith("(null)"):
90
+ lsa_secrets.append(
91
+ {"secret_type": "DefaultPassword", "value": password}
92
+ )
93
+ # Also add as credential
94
+ credentials.append(
95
+ {
96
+ "domain": "",
97
+ "username": "(DefaultPassword)",
98
+ "password": password,
99
+ "credential_type": "lsa_secret",
100
+ }
101
+ )
102
+
103
+ # Parse Kerberos keys (AES, DES formats)
104
+ # Format: username:aes256-cts-hmac-sha1-96:hexkey
105
+ kerb_key_pattern = (
106
+ r"([^:\s]+):(aes\d+-cts-hmac-sha1-\d+|des-cbc-md5):([0-9a-fA-F]+)"
107
+ )
108
+ for match in re.finditer(kerb_key_pattern, content):
109
+ username, key_type, key_value = match.groups()
110
+ kerberos_keys.append(
111
+ {"username": username, "key_type": key_type, "key": key_value}
112
+ )
60
113
 
61
- hashes.append({
62
- 'username': username,
63
- 'rid': rid,
64
- 'lm_hash': lm_hash,
65
- 'nt_hash': nt_hash,
66
- 'hash_type': 'NTLM'
67
- })
68
-
69
114
  # Parse plaintext passwords with multiple format support
70
115
  # Format 1: DOMAIN\username:password
71
116
  # Format 2: DOMAIN\\username:password (escaped backslash)
@@ -73,9 +118,9 @@ def parse_secretsdump(log_path: str, target: str) -> Dict[str, Any]:
73
118
  # Format 4: [*] DOMAIN\username:password (with prefix)
74
119
 
75
120
  plaintext_patterns = [
76
- r'([^\\:\s]+)[\\]+([^:\s]+):([^\n\r]+)', # DOMAIN\user:pass
77
- r'([^@:\s]+)@([^:\s]+):([^\n\r]+)', # user@DOMAIN:pass
78
- r'\[\*\]\s*([^\\:\s]+)[\\]+([^:\s]+):([^\n\r]+)', # [*] DOMAIN\user:pass
121
+ r"([^\\:\s]+)[\\]+([^:\s]+):([^\n\r]+)", # DOMAIN\user:pass
122
+ r"([^@:\s]+)@([^:\s]+):([^\n\r]+)", # user@DOMAIN:pass
123
+ r"\[\*\]\s*([^\\:\s]+)[\\]+([^:\s]+):([^\n\r]+)", # [*] DOMAIN\user:pass
79
124
  ]
80
125
 
81
126
  for pattern in plaintext_patterns:
@@ -86,78 +131,85 @@ def parse_secretsdump(log_path: str, target: str) -> Dict[str, Any]:
86
131
  password = password.strip()
87
132
 
88
133
  # Skip null/empty passwords and hash-like values
89
- if not password or password.startswith('(null)'):
134
+ if not password or password.startswith("(null)"):
90
135
  continue
91
136
  # Skip if password looks like a hash (32+ hex chars)
92
- if re.match(r'^[0-9a-fA-F]{32,}$', password):
137
+ if re.match(r"^[0-9a-fA-F]{32,}$", password):
138
+ continue
139
+ # Skip Kerberos key formats
140
+ if re.match(r"^(aes\d+|des)-", password):
93
141
  continue
94
142
 
95
143
  # Determine domain/username based on pattern
96
- if '@' in match.group(0):
144
+ if "@" in match.group(0):
97
145
  username, domain = part1, part2
98
146
  else:
99
147
  domain, username = part1, part2
100
148
 
101
- credentials.append({
102
- 'domain': domain,
103
- 'username': username,
104
- 'password': password,
105
- 'credential_type': 'plaintext'
106
- })
107
-
108
- # Parse Kerberos keys (format: username:$krb5...)
109
- krb_pattern = r'([^:\s]+):(\$krb5[^\s]+)'
149
+ credentials.append(
150
+ {
151
+ "domain": domain,
152
+ "username": username,
153
+ "password": password,
154
+ "credential_type": "plaintext",
155
+ }
156
+ )
157
+
158
+ # Parse Kerberos tickets (format: username:$krb5...)
159
+ krb_pattern = r"([^:\s]+):(\$krb5[^\s]+)"
110
160
  for match in re.finditer(krb_pattern, content):
111
161
  username, krb_key = match.groups()
112
-
113
- tickets.append({
114
- 'username': username,
115
- 'ticket': krb_key,
116
- 'ticket_type': 'Kerberos'
117
- })
118
-
162
+
163
+ tickets.append(
164
+ {"username": username, "ticket": krb_key, "ticket_type": "Kerberos"}
165
+ )
166
+
119
167
  except FileNotFoundError:
120
168
  return {
121
- 'tool': 'secretsdump',
122
- 'target': target,
123
- 'error': 'Log file not found',
124
- 'credentials_count': 0,
125
- 'hashes_count': 0
169
+ "tool": "secretsdump",
170
+ "target": target,
171
+ "error": "Log file not found",
172
+ "credentials_count": 0,
173
+ "hashes_count": 0,
126
174
  }
127
175
  except Exception as e:
128
176
  return {
129
- 'tool': 'secretsdump',
130
- 'target': target,
131
- 'error': str(e),
132
- 'credentials_count': 0,
133
- 'hashes_count': 0
177
+ "tool": "secretsdump",
178
+ "target": target,
179
+ "error": str(e),
180
+ "credentials_count": 0,
181
+ "hashes_count": 0,
134
182
  }
135
-
183
+
136
184
  return {
137
- 'tool': 'secretsdump',
138
- 'target': target,
139
- 'credentials_count': len(credentials),
140
- 'hashes_count': len(hashes),
141
- 'tickets_count': len(tickets),
142
- 'credentials': credentials,
143
- 'hashes': hashes,
144
- 'tickets': tickets
185
+ "tool": "secretsdump",
186
+ "target": target,
187
+ "credentials_count": len(credentials),
188
+ "hashes_count": len(hashes),
189
+ "tickets_count": len(tickets),
190
+ "lsa_secrets_count": len(lsa_secrets),
191
+ "kerberos_keys_count": len(kerberos_keys),
192
+ "credentials": credentials,
193
+ "hashes": hashes,
194
+ "tickets": tickets,
195
+ "lsa_secrets": lsa_secrets,
196
+ "kerberos_keys": kerberos_keys,
145
197
  }
146
198
 
147
199
 
148
200
  def parse_getnpusers(log_path: str, target: str) -> Dict[str, Any]:
149
201
  """
150
202
  Parse GetNPUsers output for AS-REP roastable hashes.
151
-
203
+
152
204
  Output format:
153
205
  $krb5asrep$23$user@DOMAIN:hash...
154
206
  """
155
207
  hashes = []
156
-
208
+
157
209
  try:
158
- with open(log_path, 'r', encoding='utf-8') as f:
210
+ with open(log_path, "r", encoding="utf-8") as f:
159
211
  content = f.read()
160
-
212
+
161
213
  # Parse AS-REP hashes with multiple format support
162
214
  # Format 1: $krb5asrep$23$user@DOMAIN:hash (etype 23)
163
215
  # Format 2: $krb5asrep$18$user@DOMAIN:hash (etype 18)
@@ -166,8 +218,8 @@ def parse_getnpusers(log_path: str, target: str) -> Dict[str, Any]:
166
218
 
167
219
  # Full format with etype: $krb5asrep$ETYPE$user@DOMAIN:hash
168
220
  hash_patterns = [
169
- r'\$krb5asrep\$(\d+)\$([^@]+)@([^:]+):([^\s]+)', # With etype
170
- r'\$krb5asrep\$([^@$]+)@([^:]+):([^\s]+)', # Without etype
221
+ r"\$krb5asrep\$(\d+)\$([^@]+)@([^:]+):([^\s]+)", # With etype
222
+ r"\$krb5asrep\$([^@$]+)@([^:]+):([^\s]+)", # Without etype
171
223
  ]
172
224
 
173
225
  for pattern in hash_patterns:
@@ -175,57 +227,61 @@ def parse_getnpusers(log_path: str, target: str) -> Dict[str, Any]:
175
227
  groups = match.groups()
176
228
  if len(groups) == 4:
177
229
  etype, username, domain, hash_value = groups
178
- full_hash = f'$krb5asrep${etype}${username}@{domain}:{hash_value}'
230
+ full_hash = f"$krb5asrep${etype}${username}@{domain}:{hash_value}"
179
231
  elif len(groups) == 3:
180
232
  username, domain, hash_value = groups
181
- etype = '23' # Default etype
182
- full_hash = f'$krb5asrep${username}@{domain}:{hash_value}'
233
+ etype = "23" # Default etype
234
+ full_hash = f"$krb5asrep${username}@{domain}:{hash_value}"
183
235
  else:
184
236
  continue
185
237
 
186
- hashes.append({
187
- 'username': username,
188
- 'domain': domain,
189
- 'hash': full_hash,
190
- 'hash_type': 'AS-REP',
191
- 'etype': etype,
192
- 'crackable': True
193
- })
238
+ hashes.append(
239
+ {
240
+ "username": username,
241
+ "domain": domain,
242
+ "hash": full_hash,
243
+ "hash_type": "AS-REP",
244
+ "etype": etype,
245
+ "crackable": True,
246
+ }
247
+ )
194
248
 
195
249
  # Also check for simple format (username:hash)
196
250
  if not hashes:
197
- simple_pattern = r'^([^:\s]+):(\$krb5asrep[^\s]+)'
251
+ simple_pattern = r"^([^:\s]+):(\$krb5asrep[^\s]+)"
198
252
  for match in re.finditer(simple_pattern, content, re.MULTILINE):
199
253
  username, hash_value = match.groups()
200
254
 
201
- hashes.append({
202
- 'username': username,
203
- 'hash': hash_value,
204
- 'hash_type': 'AS-REP',
205
- 'crackable': True
206
- })
207
-
255
+ hashes.append(
256
+ {
257
+ "username": username,
258
+ "hash": hash_value,
259
+ "hash_type": "AS-REP",
260
+ "crackable": True,
261
+ }
262
+ )
263
+
208
264
  except FileNotFoundError:
209
265
  return {
210
- 'tool': 'GetNPUsers',
211
- 'target': target,
212
- 'error': 'Log file not found',
213
- 'hashes_count': 0
266
+ "tool": "GetNPUsers",
267
+ "target": target,
268
+ "error": "Log file not found",
269
+ "hashes_count": 0,
214
270
  }
215
271
  except Exception as e:
216
272
  return {
217
- 'tool': 'GetNPUsers',
218
- 'target': target,
219
- 'error': str(e),
220
- 'hashes_count': 0
273
+ "tool": "GetNPUsers",
274
+ "target": target,
275
+ "error": str(e),
276
+ "hashes_count": 0,
221
277
  }
222
-
278
+
223
279
  return {
224
- 'tool': 'GetNPUsers',
225
- 'target': target,
226
- 'hashes_count': len(hashes),
227
- 'hashes': hashes,
228
- 'asrep_hashes': hashes # For auto-chaining to hashcat
280
+ "tool": "GetNPUsers",
281
+ "target": target,
282
+ "hashes_count": len(hashes),
283
+ "hashes": hashes,
284
+ "asrep_hashes": hashes, # For auto-chaining to hashcat
229
285
  }
230
286
 
231
287
 
@@ -235,52 +291,49 @@ def parse_psexec(log_path: str, target: str) -> Dict[str, Any]:
235
291
  """
236
292
  output_lines = []
237
293
  success = False
238
-
294
+
239
295
  try:
240
- with open(log_path, 'r', encoding='utf-8') as f:
296
+ with open(log_path, "r", encoding="utf-8") as f:
241
297
  content = f.read()
242
-
298
+
243
299
  # Check for successful connection with multiple indicators
244
- success_indicators = [
245
- '[*] Requesting shares on',
246
- 'C:\\Windows\\system32>',
247
- 'C:\\WINDOWS\\system32>',
248
- '[*] Uploading',
249
- '[*] Opening SVCManager',
250
- 'Microsoft Windows', # Version banner
251
- '[*] Starting service',
252
- 'Process .+ created', # Process creation message
300
+ # Use simple string matching for literal strings, regex for patterns
301
+ literal_indicators = [
302
+ "[*] Requesting shares on",
303
+ "[*] Uploading",
304
+ "[*] Opening SVCManager",
305
+ "[*] Starting service",
306
+ "C:\\Windows\\system32>",
307
+ "C:\\WINDOWS\\system32>",
308
+ "Microsoft Windows",
309
+ "nt authority\\system",
310
+ "ErrorCode: 0",
253
311
  ]
254
312
 
255
- for indicator in success_indicators:
256
- if re.search(indicator, content, re.IGNORECASE):
313
+ for indicator in literal_indicators:
314
+ if indicator.lower() in content.lower():
257
315
  success = True
258
316
  break
259
-
317
+
260
318
  # Extract command output (everything after the prompt)
261
- output_lines = [line for line in content.split('\n') if line.strip()]
262
-
319
+ output_lines = [line for line in content.split("\n") if line.strip()]
320
+
263
321
  except FileNotFoundError:
264
322
  return {
265
- 'tool': 'psexec',
266
- 'target': target,
267
- 'error': 'Log file not found',
268
- 'success': False
323
+ "tool": "psexec",
324
+ "target": target,
325
+ "error": "Log file not found",
326
+ "success": False,
269
327
  }
270
328
  except Exception as e:
271
- return {
272
- 'tool': 'psexec',
273
- 'target': target,
274
- 'error': str(e),
275
- 'success': False
276
- }
277
-
329
+ return {"tool": "psexec", "target": target, "error": str(e), "success": False}
330
+
278
331
  return {
279
- 'tool': 'psexec',
280
- 'target': target,
281
- 'success': success,
282
- 'output_lines': len(output_lines),
283
- 'output': '\n'.join(output_lines)
332
+ "tool": "psexec",
333
+ "target": target,
334
+ "success": success,
335
+ "output_lines": len(output_lines),
336
+ "output": "\n".join(output_lines),
284
337
  }
285
338
 
286
339
 
@@ -291,89 +344,89 @@ def parse_smbclient(log_path: str, target: str) -> Dict[str, Any]:
291
344
  shares = []
292
345
  files = []
293
346
  success = False
294
-
347
+
295
348
  try:
296
- with open(log_path, 'r', encoding='utf-8') as f:
349
+ with open(log_path, "r", encoding="utf-8") as f:
297
350
  content = f.read()
298
-
351
+
299
352
  # Check for successful connection
300
- if 'Type Client' in content or 'smb:' in content:
353
+ if "Type Client" in content or "smb:" in content:
301
354
  success = True
302
-
355
+
303
356
  # Parse share listings
304
- share_pattern = r'^\s*([A-Z$]+)\s+(Disk|Printer|Device|IPC)\s*(.*)$'
357
+ share_pattern = r"^\s*([A-Z$]+)\s+(Disk|Printer|Device|IPC)\s*(.*)$"
305
358
  for match in re.finditer(share_pattern, content, re.MULTILINE):
306
359
  share_name, share_type, comment = match.groups()
307
-
308
- shares.append({
309
- 'name': share_name.strip(),
310
- 'type': share_type.strip(),
311
- 'comment': comment.strip()
312
- })
313
-
360
+
361
+ shares.append(
362
+ {
363
+ "name": share_name.strip(),
364
+ "type": share_type.strip(),
365
+ "comment": comment.strip(),
366
+ }
367
+ )
368
+
314
369
  # Parse file listings (basic)
315
- file_pattern = r'^\s*([^\s]+)\s+([DAH]+)\s+(\d+)\s+'
370
+ file_pattern = r"^\s*([^\s]+)\s+([DAH]+)\s+(\d+)\s+"
316
371
  for match in re.finditer(file_pattern, content, re.MULTILINE):
317
372
  filename, attributes, size = match.groups()
318
-
319
- if filename not in ['.', '..']:
320
- files.append({
321
- 'name': filename,
322
- 'attributes': attributes,
323
- 'size': int(size)
324
- })
325
-
373
+
374
+ if filename not in [".", ".."]:
375
+ files.append(
376
+ {"name": filename, "attributes": attributes, "size": int(size)}
377
+ )
378
+
326
379
  except FileNotFoundError:
327
380
  return {
328
- 'tool': 'smbclient',
329
- 'target': target,
330
- 'error': 'Log file not found',
331
- 'success': False
381
+ "tool": "smbclient",
382
+ "target": target,
383
+ "error": "Log file not found",
384
+ "success": False,
332
385
  }
333
386
  except Exception as e:
334
387
  return {
335
- 'tool': 'smbclient',
336
- 'target': target,
337
- 'error': str(e),
338
- 'success': False
388
+ "tool": "smbclient",
389
+ "target": target,
390
+ "error": str(e),
391
+ "success": False,
339
392
  }
340
-
393
+
341
394
  return {
342
- 'tool': 'smbclient',
343
- 'target': target,
344
- 'success': success,
345
- 'shares_count': len(shares),
346
- 'files_count': len(files),
347
- 'shares': shares,
348
- 'files': files
395
+ "tool": "smbclient",
396
+ "target": target,
397
+ "success": success,
398
+ "shares_count": len(shares),
399
+ "files_count": len(files),
400
+ "shares": shares,
401
+ "files": files,
349
402
  }
350
403
 
351
404
 
352
405
  def parse_impacket(log_path: str, target: str, tool: str) -> Dict[str, Any]:
353
406
  """
354
407
  Unified parser that routes to specific Impacket tool parsers.
355
-
408
+
356
409
  Args:
357
410
  log_path: Path to tool output file
358
411
  target: Target host/domain
359
412
  tool: Specific Impacket tool name
360
-
413
+
361
414
  Returns:
362
415
  Parsed results dictionary
363
416
  """
364
- tool_lower = tool.lower().replace('impacket-', '')
365
-
366
- if 'secretsdump' in tool_lower:
417
+ tool_lower = tool.lower().replace("impacket-", "")
418
+
419
+ if "secretsdump" in tool_lower:
367
420
  return parse_secretsdump(log_path, target)
368
- elif 'getnpusers' in tool_lower:
421
+ elif "getnpusers" in tool_lower:
369
422
  return parse_getnpusers(log_path, target)
370
- elif 'psexec' in tool_lower:
423
+ elif "psexec" in tool_lower:
371
424
  return parse_psexec(log_path, target)
372
- elif 'smbclient' in tool_lower:
425
+ elif "smbclient" in tool_lower:
373
426
  return parse_smbclient(log_path, target)
374
427
  else:
375
428
  return {
376
- 'tool': tool,
377
- 'target': target,
378
- 'error': f'Unknown Impacket tool: {tool}'
429
+ "tool": tool,
430
+ "target": target,
431
+ "error": f"Unknown Impacket tool: {tool}",
379
432
  }