souleyez 2.43.29__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 +22827 -10678
  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-2.43.34.dist-info}/METADATA +1 -1
  353. souleyez-2.43.34.dist-info/RECORD +443 -0
  354. {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/WHEEL +1 -1
  355. souleyez-2.43.29.dist-info/RECORD +0 -379
  356. {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/entry_points.txt +0 -0
  357. {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/licenses/LICENSE +0 -0
  358. {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/top_level.txt +0 -0
@@ -11,108 +11,110 @@ from typing import List, Dict, Optional, Tuple
11
11
 
12
12
  # CVE Database mapping CVEs to MSF modules
13
13
  CVE_DATABASE = {
14
- 'CVE-2017-0143': {
15
- 'modules': ['exploit/windows/smb/ms17_010_eternalblue'],
16
- 'affected_versions': {
17
- 'smb': ['SMBv1'],
18
- 'os': ['Windows 7', 'Windows Server 2008', 'Windows Server 2008 R2', 'Windows 8.1', 'Windows 10']
14
+ "CVE-2017-0143": {
15
+ "modules": ["exploit/windows/smb/ms17_010_eternalblue"],
16
+ "affected_versions": {
17
+ "smb": ["SMBv1"],
18
+ "os": [
19
+ "Windows 7",
20
+ "Windows Server 2008",
21
+ "Windows Server 2008 R2",
22
+ "Windows 8.1",
23
+ "Windows 10",
24
+ ],
19
25
  },
20
- 'cvss': 8.1,
21
- 'reliability': 'excellent',
22
- 'requires': [],
23
- 'impact': 'Remote Code Execution'
26
+ "cvss": 8.1,
27
+ "reliability": "excellent",
28
+ "requires": [],
29
+ "impact": "Remote Code Execution",
24
30
  },
25
- 'CVE-2019-0708': {
26
- 'modules': ['exploit/windows/rdp/cve_2019_0708_bluekeep_rce'],
27
- 'affected_versions': {
28
- 'rdp': ['7.0', '7.1', '8.0', '8.1'],
29
- 'os': ['Windows 7', 'Windows Server 2008', 'Windows Server 2008 R2']
31
+ "CVE-2019-0708": {
32
+ "modules": ["exploit/windows/rdp/cve_2019_0708_bluekeep_rce"],
33
+ "affected_versions": {
34
+ "rdp": ["7.0", "7.1", "8.0", "8.1"],
35
+ "os": ["Windows 7", "Windows Server 2008", "Windows Server 2008 R2"],
30
36
  },
31
- 'cvss': 9.8,
32
- 'reliability': 'normal',
33
- 'requires': [],
34
- 'impact': 'Remote Code Execution'
37
+ "cvss": 9.8,
38
+ "reliability": "normal",
39
+ "requires": [],
40
+ "impact": "Remote Code Execution",
35
41
  },
36
- 'CVE-2011-2523': {
37
- 'modules': ['exploit/unix/ftp/vsftpd_234_backdoor'],
38
- 'affected_versions': {
39
- 'ftp': ['vsftpd 2.3.4']
40
- },
41
- 'cvss': 10.0,
42
- 'reliability': 'excellent',
43
- 'requires': [],
44
- 'impact': 'Remote Code Execution'
42
+ "CVE-2011-2523": {
43
+ "modules": ["exploit/unix/ftp/vsftpd_234_backdoor"],
44
+ "affected_versions": {"ftp": ["vsftpd 2.3.4"]},
45
+ "cvss": 10.0,
46
+ "reliability": "excellent",
47
+ "requires": [],
48
+ "impact": "Remote Code Execution",
45
49
  },
46
- 'CVE-2014-6271': {
47
- 'modules': ['exploit/multi/http/apache_mod_cgi_bash_env_exec'],
48
- 'affected_versions': {
49
- 'bash': ['<4.3'],
50
- 'cgi': ['*']
51
- },
52
- 'cvss': 9.8,
53
- 'reliability': 'excellent',
54
- 'requires': [],
55
- 'impact': 'Remote Code Execution'
50
+ "CVE-2014-6271": {
51
+ "modules": ["exploit/multi/http/apache_mod_cgi_bash_env_exec"],
52
+ "affected_versions": {"bash": ["<4.3"], "cgi": ["*"]},
53
+ "cvss": 9.8,
54
+ "reliability": "excellent",
55
+ "requires": [],
56
+ "impact": "Remote Code Execution",
56
57
  },
57
- 'CVE-2007-2447': {
58
- 'modules': ['exploit/multi/samba/usermap_script'],
59
- 'affected_versions': {
60
- 'smb': ['Samba 3.0.20', 'Samba 3.0.21', 'Samba 3.0.22', 'Samba 3.0.23', 'Samba 3.0.24', 'Samba 3.0.25']
58
+ "CVE-2007-2447": {
59
+ "modules": ["exploit/multi/samba/usermap_script"],
60
+ "affected_versions": {
61
+ "smb": [
62
+ "Samba 3.0.20",
63
+ "Samba 3.0.21",
64
+ "Samba 3.0.22",
65
+ "Samba 3.0.23",
66
+ "Samba 3.0.24",
67
+ "Samba 3.0.25",
68
+ ]
61
69
  },
62
- 'cvss': 10.0,
63
- 'reliability': 'excellent',
64
- 'requires': [],
65
- 'impact': 'Remote Code Execution'
70
+ "cvss": 10.0,
71
+ "reliability": "excellent",
72
+ "requires": [],
73
+ "impact": "Remote Code Execution",
66
74
  },
67
- 'CVE-2008-4250': {
68
- 'modules': ['exploit/windows/smb/ms08_067_netapi'],
69
- 'affected_versions': {
70
- 'os': ['Windows 2000', 'Windows XP', 'Windows Server 2003']
75
+ "CVE-2008-4250": {
76
+ "modules": ["exploit/windows/smb/ms08_067_netapi"],
77
+ "affected_versions": {
78
+ "os": ["Windows 2000", "Windows XP", "Windows Server 2003"]
71
79
  },
72
- 'cvss': 10.0,
73
- 'reliability': 'excellent',
74
- 'requires': [],
75
- 'impact': 'Remote Code Execution'
80
+ "cvss": 10.0,
81
+ "reliability": "excellent",
82
+ "requires": [],
83
+ "impact": "Remote Code Execution",
76
84
  },
77
- 'CVE-2012-1823': {
78
- 'modules': ['exploit/multi/http/php_cgi_arg_injection'],
79
- 'affected_versions': {
80
- 'php': ['5.3.0-5.3.12', '5.4.0-5.4.2']
81
- },
82
- 'cvss': 7.5,
83
- 'reliability': 'good',
84
- 'requires': [],
85
- 'impact': 'Remote Code Execution'
85
+ "CVE-2012-1823": {
86
+ "modules": ["exploit/multi/http/php_cgi_arg_injection"],
87
+ "affected_versions": {"php": ["5.3.0-5.3.12", "5.4.0-5.4.2"]},
88
+ "cvss": 7.5,
89
+ "reliability": "good",
90
+ "requires": [],
91
+ "impact": "Remote Code Execution",
86
92
  },
87
- 'CVE-2021-4034': {
88
- 'modules': ['exploit/linux/local/cve_2021_4034_pwnkit_lpe_pkexec'],
89
- 'affected_versions': {
90
- 'polkit': ['<=0.120']
91
- },
92
- 'cvss': 7.8,
93
- 'reliability': 'excellent',
94
- 'requires': ['local_access'],
95
- 'impact': 'Privilege Escalation'
93
+ "CVE-2021-4034": {
94
+ "modules": ["exploit/linux/local/cve_2021_4034_pwnkit_lpe_pkexec"],
95
+ "affected_versions": {"polkit": ["<=0.120"]},
96
+ "cvss": 7.8,
97
+ "reliability": "excellent",
98
+ "requires": ["local_access"],
99
+ "impact": "Privilege Escalation",
96
100
  },
97
- 'CVE-2021-3156': {
98
- 'modules': ['exploit/linux/local/sudo_baron_samedit'],
99
- 'affected_versions': {
100
- 'sudo': ['1.8.2-1.8.31p2', '1.9.0-1.9.5p1']
101
- },
102
- 'cvss': 7.8,
103
- 'reliability': 'excellent',
104
- 'requires': ['local_access'],
105
- 'impact': 'Privilege Escalation'
101
+ "CVE-2021-3156": {
102
+ "modules": ["exploit/linux/local/sudo_baron_samedit"],
103
+ "affected_versions": {"sudo": ["1.8.2-1.8.31p2", "1.9.0-1.9.5p1"]},
104
+ "cvss": 7.8,
105
+ "reliability": "excellent",
106
+ "requires": ["local_access"],
107
+ "impact": "Privilege Escalation",
106
108
  },
107
- 'CVE-2017-7494': {
108
- 'modules': ['exploit/linux/samba/is_known_pipename'],
109
- 'affected_versions': {
110
- 'smb': ['Samba 3.5.0-4.6.4', 'Samba 4.5.0-4.5.9', 'Samba 4.4.0-4.4.13']
109
+ "CVE-2017-7494": {
110
+ "modules": ["exploit/linux/samba/is_known_pipename"],
111
+ "affected_versions": {
112
+ "smb": ["Samba 3.5.0-4.6.4", "Samba 4.5.0-4.5.9", "Samba 4.4.0-4.4.13"]
111
113
  },
112
- 'cvss': 7.5,
113
- 'reliability': 'good',
114
- 'requires': [],
115
- 'impact': 'Remote Code Execution'
114
+ "cvss": 7.5,
115
+ "reliability": "good",
116
+ "requires": [],
117
+ "impact": "Remote Code Execution",
116
118
  },
117
119
  }
118
120
 
@@ -124,79 +126,79 @@ class VersionMatcher:
124
126
  def parse_version(version_string: str) -> Optional[Tuple]:
125
127
  """
126
128
  Parse version string to comparable tuple (major, minor, patch).
127
-
129
+
128
130
  Args:
129
131
  version_string: Version string like "2.4.49" or "OpenSSH 7.4"
130
-
132
+
131
133
  Returns:
132
134
  Tuple of (major, minor, patch) or None if unparseable
133
135
  """
134
136
  # Extract version numbers from string
135
- version_match = re.search(r'(\d+)\.(\d+)(?:\.(\d+))?', version_string)
137
+ version_match = re.search(r"(\d+)\.(\d+)(?:\.(\d+))?", version_string)
136
138
  if not version_match:
137
139
  return None
138
-
140
+
139
141
  major = int(version_match.group(1))
140
142
  minor = int(version_match.group(2))
141
143
  patch = int(version_match.group(3)) if version_match.group(3) else 0
142
-
144
+
143
145
  return (major, minor, patch)
144
146
 
145
147
  @staticmethod
146
148
  def is_in_range(version: Tuple, range_str: str) -> bool:
147
149
  """
148
150
  Check if version is within a range string.
149
-
151
+
150
152
  Args:
151
153
  version: Parsed version tuple (major, minor, patch)
152
154
  range_str: Range like "7.0-7.6", "<=0.120", "<4.3", or exact "2.3.4"
153
-
155
+
154
156
  Returns:
155
157
  True if version is in range
156
158
  """
157
159
  # Handle <= comparisons
158
- if range_str.startswith('<='):
160
+ if range_str.startswith("<="):
159
161
  max_ver_str = range_str[2:].strip()
160
162
  max_ver = VersionMatcher.parse_version(max_ver_str)
161
163
  if max_ver:
162
164
  return version <= max_ver
163
-
165
+
164
166
  # Handle < comparisons
165
- if range_str.startswith('<'):
167
+ if range_str.startswith("<"):
166
168
  max_ver_str = range_str[1:].strip()
167
169
  max_ver = VersionMatcher.parse_version(max_ver_str)
168
170
  if max_ver:
169
171
  return version < max_ver
170
-
172
+
171
173
  # Handle range (e.g., "1.8.2-1.8.31p2")
172
- if '-' in range_str:
173
- parts = range_str.split('-')
174
+ if "-" in range_str:
175
+ parts = range_str.split("-")
174
176
  if len(parts) == 2:
175
177
  min_ver = VersionMatcher.parse_version(parts[0].strip())
176
178
  max_ver = VersionMatcher.parse_version(parts[1].strip())
177
179
  if min_ver and max_ver:
178
180
  return min_ver <= version <= max_ver
179
-
181
+
180
182
  # Handle exact match or wildcard
181
- if range_str == '*':
183
+ if range_str == "*":
182
184
  return True
183
-
185
+
184
186
  # Exact version match
185
187
  exact_ver = VersionMatcher.parse_version(range_str)
186
188
  if exact_ver:
187
189
  return version == exact_ver
188
-
190
+
189
191
  return False
190
192
 
191
193
  @staticmethod
192
194
  def is_vulnerable(service_version: str, vulnerable_ranges: List[str]) -> bool:
193
195
  """
194
196
  Check if service version falls within any vulnerable range.
195
-
197
+
196
198
  Args:
197
199
  service_version: Service version string (e.g., "OpenSSH 7.4", "2.4.49")
198
200
  vulnerable_ranges: List of version ranges
199
-
201
+
200
202
  Returns:
201
203
  True if version is vulnerable
202
204
  """
@@ -207,37 +209,40 @@ class VersionMatcher:
207
209
  if range_str.lower() in service_version.lower():
208
210
  return True
209
211
  return False
210
-
212
+
211
213
  for range_str in vulnerable_ranges:
212
214
  if VersionMatcher.is_in_range(parsed_version, range_str):
213
215
  return True
214
-
216
+
215
217
  return False
216
218
 
217
219
  @staticmethod
218
220
  def get_cves_for_version(service: str, version: str) -> List[str]:
219
221
  """
220
222
  Return CVEs affecting this specific service version.
221
-
223
+
222
224
  Args:
223
225
  service: Service name (e.g., 'ssh', 'smb', 'http')
224
226
  version: Version string
225
-
227
+
226
228
  Returns:
227
229
  List of CVE IDs
228
230
  """
229
231
  matching_cves = []
230
-
232
+
231
233
  for cve_id, cve_data in CVE_DATABASE.items():
232
- affected_versions = cve_data.get('affected_versions', {})
233
-
234
+ affected_versions = cve_data.get("affected_versions", {})
235
+
234
236
  # Check if this CVE affects this service type
235
237
  for service_type, version_ranges in affected_versions.items():
236
- if service_type.lower() in service.lower() or service.lower() in service_type.lower():
238
+ if (
239
+ service_type.lower() in service.lower()
240
+ or service.lower() in service_type.lower()
241
+ ):
237
242
  if VersionMatcher.is_vulnerable(version, version_ranges):
238
243
  matching_cves.append(cve_id)
239
244
  break
240
-
245
+
241
246
  return matching_cves
242
247
 
243
248
 
@@ -246,12 +251,13 @@ class MSFResourceGenerator:
246
251
 
247
252
  def __init__(self, output_dir: str = None):
248
253
  """Initialize generator with output directory."""
249
- self.output_dir = output_dir or os.path.join(os.getcwd(), 'msf_resources')
254
+ self.output_dir = output_dir or os.path.join(os.getcwd(), "msf_resources")
250
255
  os.makedirs(self.output_dir, exist_ok=True)
251
256
 
252
257
  def generate_header(self) -> str:
253
258
  """Generate resource script header with metadata."""
254
259
  from datetime import datetime
260
+
255
261
  return f"""# Metasploit Resource Script
256
262
  # Generated by souleyez on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
257
263
  #
@@ -260,7 +266,9 @@ class MSFResourceGenerator:
260
266
 
261
267
  """
262
268
 
263
- def generate_smb_psexec_script(self, writable_shares: List[Dict], credentials: List[Dict]) -> str:
269
+ def generate_smb_psexec_script(
270
+ self, writable_shares: List[Dict], credentials: List[Dict]
271
+ ) -> str:
264
272
  """
265
273
  Generate psexec attack script for writable SMB shares.
266
274
 
@@ -278,13 +286,17 @@ class MSFResourceGenerator:
278
286
  # Group shares by host
279
287
  shares_by_host = {}
280
288
  for share in writable_shares:
281
- host_ip = share.get('ip_address')
289
+ host_ip = share.get("ip_address")
282
290
  if host_ip not in shares_by_host:
283
291
  shares_by_host[host_ip] = []
284
292
  shares_by_host[host_ip].append(share)
285
293
 
286
294
  # Find SMB credentials
287
- smb_creds = [c for c in credentials if c.get('service', '').lower() in ['smb', 'smb2', '445', 'cifs']]
295
+ smb_creds = [
296
+ c
297
+ for c in credentials
298
+ if c.get("service", "").lower() in ["smb", "smb2", "445", "cifs"]
299
+ ]
288
300
 
289
301
  if not smb_creds:
290
302
  script += "# WARNING: No SMB credentials found!\n"
@@ -292,7 +304,7 @@ class MSFResourceGenerator:
292
304
 
293
305
  for host_ip, shares in shares_by_host.items():
294
306
  script += f"\n# Target: {host_ip}\n"
295
- script += f"# Writable shares: {', '.join([s['share_name'] for s in shares])}\n"
307
+ script += f"# Writable shares: {', '.join([s.get('share_name', 'unknown') for s in shares])}\n"
296
308
  script += "use exploit/windows/smb/psexec\n"
297
309
  script += f"set RHOSTS {host_ip}\n"
298
310
 
@@ -310,7 +322,12 @@ class MSFResourceGenerator:
310
322
 
311
323
  return script
312
324
 
313
- def generate_ssh_bruteforce_script(self, ssh_hosts: List[Dict], username_file: str = None, password_file: str = None) -> str:
325
+ def generate_ssh_bruteforce_script(
326
+ self,
327
+ ssh_hosts: List[Dict],
328
+ username_file: str = None,
329
+ password_file: str = None,
330
+ ) -> str:
314
331
  """
315
332
  Generate SSH brute force script.
316
333
 
@@ -327,48 +344,19 @@ class MSFResourceGenerator:
327
344
  script += "# WARNING: This is noisy and may trigger IDS/IPS alerts!\n"
328
345
  script += "# Ensure you have authorization before running.\n\n"
329
346
 
330
- # Default wordlists with checks for common locations
347
+ # Default wordlists - use project's self-contained wordlists
348
+ from souleyez.wordlists import resolve_wordlist_path
349
+
331
350
  if not username_file:
332
- # Try common user wordlist locations
333
- user_locations = [
334
- "/usr/share/metasploit-framework/data/wordlists/unix_users.txt",
335
- "/usr/share/wordlists/metasploit/unix_users.txt",
336
- "/usr/share/seclists/Usernames/top-usernames-shortlist.txt",
337
- ]
338
- for loc in user_locations:
339
- if os.path.exists(loc):
340
- username_file = loc
341
- break
342
- if not username_file:
343
- username_file = "/usr/share/metasploit-framework/data/wordlists/unix_users.txt"
351
+ username_file = resolve_wordlist_path("data/wordlists/usernames_common.txt")
344
352
 
345
353
  if not password_file:
346
- # Try common password wordlist locations
347
- pass_locations = [
348
- "/usr/share/wordlists/rockyou.txt", # Uncompressed
349
- "/usr/share/wordlists/fasttrack.txt",
350
- "/usr/share/metasploit-framework/data/wordlists/unix_passwords.txt",
351
- "/usr/share/seclists/Passwords/Common-Credentials/10-million-password-list-top-1000.txt",
352
- ]
353
- for loc in pass_locations:
354
- if os.path.exists(loc):
355
- password_file = loc
356
- break
357
-
358
- # If rockyou.txt.gz exists but not rockyou.txt, add a note
359
- if not password_file and os.path.exists("/usr/share/wordlists/rockyou.txt.gz"):
360
- script += "# NOTE: rockyou.txt.gz needs to be extracted first:\n"
361
- script += "# sudo gunzip /usr/share/wordlists/rockyou.txt.gz\n"
362
- script += "# Using smaller wordlist instead:\n\n"
363
- password_file = "/usr/share/metasploit-framework/data/wordlists/unix_passwords.txt"
364
- elif not password_file:
365
- # Fallback to MSF's smaller wordlist
366
- password_file = "/usr/share/metasploit-framework/data/wordlists/unix_passwords.txt"
354
+ password_file = resolve_wordlist_path("data/wordlists/passwords_brute.txt")
367
355
 
368
356
  script += "use auxiliary/scanner/ssh/ssh_login\n"
369
357
 
370
358
  # Build RHOSTS list
371
- rhosts = ' '.join([h.get('ip_address', '') for h in ssh_hosts])
359
+ rhosts = " ".join([h.get("ip_address", "") for h in ssh_hosts])
372
360
  script += f"set RHOSTS {rhosts}\n"
373
361
  script += f"set USER_FILE {username_file}\n"
374
362
  script += f"set PASS_FILE {password_file}\n"
@@ -386,7 +374,9 @@ class MSFResourceGenerator:
386
374
 
387
375
  return script
388
376
 
389
- def generate_credential_spray_script(self, credentials: List[Dict], targets: List[Dict]) -> str:
377
+ def generate_credential_spray_script(
378
+ self, credentials: List[Dict], targets: List[Dict]
379
+ ) -> str:
390
380
  """
391
381
  Generate credential spraying script across multiple services.
392
382
 
@@ -403,24 +393,24 @@ class MSFResourceGenerator:
403
393
  # Group targets by service
404
394
  services_map = {}
405
395
  for target in targets:
406
- service = target.get('service_name', 'unknown').lower()
407
- port = target.get('port', 0)
408
- ip = target.get('ip_address', '')
396
+ service = target.get("service_name", "unknown").lower()
397
+ port = target.get("port", 0)
398
+ ip = target.get("ip_address", "")
409
399
 
410
400
  if service not in services_map:
411
401
  services_map[service] = []
412
402
  services_map[service].append((ip, port))
413
403
 
414
404
  # SSH credential spray
415
- if 'ssh' in services_map:
405
+ if "ssh" in services_map:
416
406
  script += "\n# SSH Credential Spray\n"
417
407
  script += "use auxiliary/scanner/ssh/ssh_login\n"
418
- rhosts = ' '.join([ip for ip, _ in services_map['ssh']])
408
+ rhosts = " ".join([ip for ip, _ in services_map["ssh"]])
419
409
  script += f"set RHOSTS {rhosts}\n"
420
410
 
421
411
  for cred in credentials:
422
- username = cred.get('username', '')
423
- password = cred.get('password', '')
412
+ username = cred.get("username", "")
413
+ password = cred.get("password", "")
424
414
  if username and password:
425
415
  script += f"set USERNAME {username}\n"
426
416
  script += f"set PASSWORD {password}\n"
@@ -428,22 +418,22 @@ class MSFResourceGenerator:
428
418
  script += "\n"
429
419
 
430
420
  # SMB credential spray
431
- if any(s in services_map for s in ['smb', 'microsoft-ds', 'netbios-ssn']):
421
+ if any(s in services_map for s in ["smb", "microsoft-ds", "netbios-ssn"]):
432
422
  script += "\n# SMB Credential Spray\n"
433
423
  script += "use auxiliary/scanner/smb/smb_login\n"
434
424
 
435
425
  # Get all SMB-related hosts
436
426
  smb_hosts = []
437
- for key in ['smb', 'microsoft-ds', 'netbios-ssn']:
427
+ for key in ["smb", "microsoft-ds", "netbios-ssn"]:
438
428
  if key in services_map:
439
429
  smb_hosts.extend([ip for ip, _ in services_map[key]])
440
430
 
441
- rhosts = ' '.join(set(smb_hosts))
431
+ rhosts = " ".join(set(smb_hosts))
442
432
  script += f"set RHOSTS {rhosts}\n"
443
433
 
444
434
  for cred in credentials:
445
- username = cred.get('username', '')
446
- password = cred.get('password', '')
435
+ username = cred.get("username", "")
436
+ password = cred.get("password", "")
447
437
  if username and password:
448
438
  script += f"set SMBUser {username}\n"
449
439
  script += f"set SMBPass {password}\n"
@@ -468,9 +458,9 @@ class MSFResourceGenerator:
468
458
  script += "# Some exploits may crash services or cause system instability\n\n"
469
459
 
470
460
  for vuln in vulnerabilities:
471
- title = vuln.get('title', 'Unknown')
472
- host_ip = vuln.get('ip_address', 'N/A')
473
- port = vuln.get('port', 0)
461
+ title = vuln.get("title", "Unknown")
462
+ host_ip = vuln.get("ip_address", "N/A")
463
+ port = vuln.get("port", 0)
474
464
 
475
465
  script += f"\n# {title} on {host_ip}:{port}\n"
476
466
 
@@ -507,9 +497,9 @@ class MSFResourceGenerator:
507
497
  """Check if module requires payload configuration."""
508
498
  # Modules that don't need payload configuration
509
499
  no_payload_modules = [
510
- 'vsftpd_234_backdoor', # Has built-in backdoor
511
- 'usermap_script', # Uses command execution
512
- 'distcc_exec', # Direct command execution
500
+ "vsftpd_234_backdoor", # Has built-in backdoor
501
+ "usermap_script", # Uses command execution
502
+ "distcc_exec", # Direct command execution
513
503
  ]
514
504
 
515
505
  for no_payload in no_payload_modules:
@@ -524,31 +514,31 @@ class MSFResourceGenerator:
524
514
  module_lower = module.lower()
525
515
 
526
516
  # Windows exploits
527
- if 'windows' in module_lower:
528
- return 'windows/meterpreter/reverse_tcp'
517
+ if "windows" in module_lower:
518
+ return "windows/shell_reverse_tcp"
529
519
 
530
- # Linux exploits
531
- if 'linux' in module_lower or 'unix' in module_lower:
532
- return 'linux/x86/meterpreter/reverse_tcp'
520
+ # Linux/Unix exploits
521
+ if "linux" in module_lower or "unix" in module_lower:
522
+ return "cmd/unix/reverse_netcat"
533
523
 
534
- # Multi-platform or unknown
535
- return 'generic/shell_reverse_tcp'
524
+ # Multi-platform - default to unix shell (works on most targets)
525
+ return "cmd/unix/reverse_netcat"
536
526
 
537
527
  def _get_exploit_module(self, vuln: Dict) -> Optional[str]:
538
528
  """Map vulnerability to MSF exploit module."""
539
- title = vuln.get('title', '').lower()
540
- description = vuln.get('description', '').lower()
529
+ title = vuln.get("title", "").lower()
530
+ description = vuln.get("description", "").lower()
541
531
 
542
532
  # Simple keyword matching (can be expanded)
543
533
  module_map = {
544
- 'eternalblue': 'exploit/windows/smb/ms17_010_eternalblue',
545
- 'ms17-010': 'exploit/windows/smb/ms17_010_eternalblue',
546
- 'bluekeep': 'exploit/windows/rdp/cve_2019_0708_bluekeep_rce',
547
- 'cve-2019-0708': 'exploit/windows/rdp/cve_2019_0708_bluekeep_rce',
548
- 'shellshock': 'exploit/multi/http/apache_mod_cgi_bash_env_exec',
549
- 'vsftpd 2.3.4': 'exploit/unix/ftp/vsftpd_234_backdoor',
550
- 'distcc': 'exploit/unix/misc/distcc_exec',
551
- 'samba 3.0.20': 'exploit/multi/samba/usermap_script',
534
+ "eternalblue": "exploit/windows/smb/ms17_010_eternalblue",
535
+ "ms17-010": "exploit/windows/smb/ms17_010_eternalblue",
536
+ "bluekeep": "exploit/windows/rdp/cve_2019_0708_bluekeep_rce",
537
+ "cve-2019-0708": "exploit/windows/rdp/cve_2019_0708_bluekeep_rce",
538
+ "shellshock": "exploit/multi/http/apache_mod_cgi_bash_env_exec",
539
+ "vsftpd 2.3.4": "exploit/unix/ftp/vsftpd_234_backdoor",
540
+ "distcc": "exploit/unix/misc/distcc_exec",
541
+ "samba 3.0.20": "exploit/multi/samba/usermap_script",
552
542
  }
553
543
 
554
544
  for keyword, module in module_map.items():
@@ -558,31 +548,33 @@ class MSFResourceGenerator:
558
548
  return None
559
549
 
560
550
  def generate_web_exploitation_script(
561
- self,
562
- web_services: List[Dict],
563
- vulnerabilities: List[Dict] = None
551
+ self, web_services: List[Dict], vulnerabilities: List[Dict] = None
564
552
  ) -> str:
565
553
  """
566
554
  Generate script for web application exploitation.
567
-
555
+
568
556
  Args:
569
557
  web_services: List of web services (HTTP/HTTPS)
570
558
  vulnerabilities: Optional list of web vulnerabilities
571
-
559
+
572
560
  Returns:
573
561
  Resource script content
574
562
  """
575
563
  script = self.generate_header()
576
564
  script += "# Web Application Exploitation\n"
577
565
  script += "# Tests for common web vulnerabilities\n\n"
578
-
566
+
579
567
  for service in web_services:
580
- host_ip = service.get('ip_address', '')
581
- port = service.get('port', 80)
582
- proto = 'https' if service.get('service_name', '').lower() == 'https' else 'http'
583
-
568
+ host_ip = service.get("ip_address", "")
569
+ port = service.get("port", 80)
570
+ proto = (
571
+ "https"
572
+ if service.get("service_name", "").lower() == "https"
573
+ else "http"
574
+ )
575
+
584
576
  script += f"\n# Target: {proto}://{host_ip}:{port}\n"
585
-
577
+
586
578
  # Shellshock test
587
579
  script += "\n## Test for Shellshock (CVE-2014-6271)\n"
588
580
  script += "use auxiliary/scanner/http/apache_mod_cgi_bash_env_exec\n"
@@ -590,197 +582,193 @@ class MSFResourceGenerator:
590
582
  script += f"set RPORT {port}\n"
591
583
  script += f"set SSL {'true' if proto == 'https' else 'false'}\n"
592
584
  script += "run\n\n"
593
-
585
+
594
586
  # Directory traversal
595
587
  script += "## Directory Traversal Scanner\n"
596
588
  script += "use auxiliary/scanner/http/dir_traversal\n"
597
589
  script += f"set RHOSTS {host_ip}\n"
598
590
  script += f"set RPORT {port}\n"
599
591
  script += "run\n\n"
600
-
592
+
601
593
  # File upload scanner
602
594
  script += "## File Upload Scanner\n"
603
595
  script += "use auxiliary/scanner/http/http_put\n"
604
596
  script += f"set RHOSTS {host_ip}\n"
605
597
  script += f"set RPORT {port}\n"
606
598
  script += "run\n\n"
607
-
599
+
608
600
  return script
609
601
 
610
602
  def generate_post_exploitation_script(
611
- self,
612
- compromised_hosts: List[Dict],
613
- objectives: List[str] = None
603
+ self, compromised_hosts: List[Dict], objectives: List[str] = None
614
604
  ) -> str:
615
605
  """
616
606
  Generate post-exploitation script.
617
-
607
+
618
608
  Args:
619
609
  compromised_hosts: List of compromised hosts with session IDs
620
610
  objectives: List of objectives ['escalate', 'pivot', 'dump_creds', 'persist', 'exfil']
621
-
611
+
622
612
  Returns:
623
613
  Resource script content
624
614
  """
625
615
  if objectives is None:
626
- objectives = ['escalate', 'dump_creds']
627
-
616
+ objectives = ["escalate", "dump_creds"]
617
+
628
618
  script = self.generate_header()
629
619
  script += "# Post-Exploitation Activities\n"
630
620
  script += "# WARNING: Only run on authorized systems!\n\n"
631
-
621
+
632
622
  for host in compromised_hosts:
633
- session_id = host.get('session_id', 1)
634
- host_ip = host.get('ip_address', 'N/A')
635
- os_type = host.get('os', 'unknown').lower()
636
-
623
+ session_id = host.get("session_id", 1)
624
+ host_ip = host.get("ip_address", "N/A")
625
+ os_type = host.get("os", "unknown").lower()
626
+
637
627
  script += f"\n# Host: {host_ip} (Session {session_id})\n"
638
-
639
- if 'escalate' in objectives:
628
+
629
+ if "escalate" in objectives:
640
630
  script += "\n## Privilege Escalation\n"
641
- if 'windows' in os_type:
631
+ if "windows" in os_type:
642
632
  script += f"use post/multi/recon/local_exploit_suggester\n"
643
633
  script += f"set SESSION {session_id}\n"
644
634
  script += "run\n\n"
645
635
  script += f"use post/windows/gather/enum_patches\n"
646
636
  script += f"set SESSION {session_id}\n"
647
637
  script += "run\n\n"
648
- elif 'linux' in os_type:
638
+ elif "linux" in os_type:
649
639
  script += f"use post/multi/recon/local_exploit_suggester\n"
650
640
  script += f"set SESSION {session_id}\n"
651
641
  script += "run\n\n"
652
-
653
- if 'dump_creds' in objectives:
642
+
643
+ if "dump_creds" in objectives:
654
644
  script += "\n## Credential Dumping\n"
655
- if 'windows' in os_type:
645
+ if "windows" in os_type:
656
646
  script += f"use post/windows/gather/hashdump\n"
657
647
  script += f"set SESSION {session_id}\n"
658
648
  script += "run\n\n"
659
- script += f"use post/windows/gather/credentials/credential_collector\n"
649
+ script += (
650
+ f"use post/windows/gather/credentials/credential_collector\n"
651
+ )
660
652
  script += f"set SESSION {session_id}\n"
661
653
  script += "run\n\n"
662
- elif 'linux' in os_type:
654
+ elif "linux" in os_type:
663
655
  script += f"use post/linux/gather/hashdump\n"
664
656
  script += f"set SESSION {session_id}\n"
665
657
  script += "run\n\n"
666
-
667
- if 'persist' in objectives:
658
+
659
+ if "persist" in objectives:
668
660
  script += "\n## Persistence\n"
669
- if 'windows' in os_type:
661
+ if "windows" in os_type:
670
662
  script += f"use exploit/windows/local/persistence_service\n"
671
663
  script += f"set SESSION {session_id}\n"
672
664
  script += "# exploit # Uncomment to create persistence\n\n"
673
- elif 'linux' in os_type:
665
+ elif "linux" in os_type:
674
666
  script += f"use post/linux/manage/sshkey_persistence\n"
675
667
  script += f"set SESSION {session_id}\n"
676
668
  script += "# run # Uncomment to create SSH key persistence\n\n"
677
-
678
- if 'pivot' in objectives:
669
+
670
+ if "pivot" in objectives:
679
671
  script += "\n## Network Pivoting\n"
680
672
  script += f"use post/multi/manage/autoroute\n"
681
673
  script += f"set SESSION {session_id}\n"
682
674
  script += "run\n\n"
683
675
  script += "# Now you can scan internal network via this session\n\n"
684
-
676
+
685
677
  return script
686
678
 
687
679
  def generate_enumeration_script(
688
- self,
689
- services: List[Dict],
690
- enumeration_depth: str = 'standard'
680
+ self, services: List[Dict], enumeration_depth: str = "standard"
691
681
  ) -> str:
692
682
  """
693
683
  Generate comprehensive enumeration script.
694
-
684
+
695
685
  Args:
696
686
  services: List of services to enumerate
697
687
  enumeration_depth: 'light', 'standard', or 'aggressive'
698
-
688
+
699
689
  Returns:
700
690
  Resource script content
701
691
  """
702
692
  script = self.generate_header()
703
693
  script += f"# Service Enumeration - {enumeration_depth.upper()} mode\n\n"
704
-
694
+
705
695
  # Group by service type
706
696
  service_groups = {}
707
697
  for service in services:
708
- service_name = service.get('service_name', 'unknown').lower()
698
+ service_name = service.get("service_name", "unknown").lower()
709
699
  if service_name not in service_groups:
710
700
  service_groups[service_name] = []
711
701
  service_groups[service_name].append(service)
712
-
702
+
713
703
  for service_name, service_list in service_groups.items():
714
704
  script += f"\n# {service_name.upper()} Enumeration\n"
715
-
716
- rhosts = ' '.join([s.get('ip_address', '') for s in service_list])
717
- port = service_list[0].get('port', 0)
718
-
719
- if service_name == 'smb':
705
+
706
+ rhosts = " ".join([s.get("ip_address", "") for s in service_list])
707
+ port = service_list[0].get("port", 0)
708
+
709
+ if service_name == "smb":
720
710
  script += "use auxiliary/scanner/smb/smb_version\n"
721
711
  script += f"set RHOSTS {rhosts}\n"
722
712
  script += "run\n\n"
723
-
713
+
724
714
  script += "use auxiliary/scanner/smb/smb_enumshares\n"
725
715
  script += f"set RHOSTS {rhosts}\n"
726
716
  script += "run\n\n"
727
-
728
- if enumeration_depth in ['standard', 'aggressive']:
717
+
718
+ if enumeration_depth in ["standard", "aggressive"]:
729
719
  script += "use auxiliary/scanner/smb/smb_enumusers\n"
730
720
  script += f"set RHOSTS {rhosts}\n"
731
721
  script += "run\n\n"
732
-
733
- elif service_name == 'ssh':
722
+
723
+ elif service_name == "ssh":
734
724
  script += "use auxiliary/scanner/ssh/ssh_version\n"
735
725
  script += f"set RHOSTS {rhosts}\n"
736
726
  script += "run\n\n"
737
-
738
- if enumeration_depth == 'aggressive':
727
+
728
+ if enumeration_depth == "aggressive":
739
729
  script += "use auxiliary/scanner/ssh/ssh_enumusers\n"
740
730
  script += f"set RHOSTS {rhosts}\n"
741
731
  script += "run\n\n"
742
-
743
- elif service_name in ['http', 'https']:
732
+
733
+ elif service_name in ["http", "https"]:
744
734
  script += "use auxiliary/scanner/http/http_version\n"
745
735
  script += f"set RHOSTS {rhosts}\n"
746
736
  script += f"set RPORT {port}\n"
747
737
  script += f"set SSL {'true' if service_name == 'https' else 'false'}\n"
748
738
  script += "run\n\n"
749
-
739
+
750
740
  script += "use auxiliary/scanner/http/robots_txt\n"
751
741
  script += f"set RHOSTS {rhosts}\n"
752
742
  script += f"set RPORT {port}\n"
753
743
  script += "run\n\n"
754
-
744
+
755
745
  return script
756
746
 
757
747
  def generate_database_attack_script(
758
- self,
759
- db_services: List[Dict],
760
- attack_type: str = 'auth'
748
+ self, db_services: List[Dict], attack_type: str = "auth"
761
749
  ) -> str:
762
750
  """
763
751
  Generate database-specific attack script.
764
-
752
+
765
753
  Args:
766
754
  db_services: List of database services
767
755
  attack_type: 'auth', 'extract', 'exploit'
768
-
756
+
769
757
  Returns:
770
758
  Resource script content
771
759
  """
772
760
  script = self.generate_header()
773
761
  script += f"# Database Attack - {attack_type.upper()} mode\n\n"
774
-
762
+
775
763
  for db in db_services:
776
- host_ip = db.get('ip_address', '')
777
- port = db.get('port', 0)
778
- service_name = db.get('service_name', '').lower()
779
-
764
+ host_ip = db.get("ip_address", "")
765
+ port = db.get("port", 0)
766
+ service_name = db.get("service_name", "").lower()
767
+
780
768
  script += f"\n# Target: {service_name} on {host_ip}:{port}\n"
781
-
782
- if service_name in ['mysql', 'mariadb']:
783
- if attack_type == 'auth':
769
+
770
+ if service_name in ["mysql", "mariadb"]:
771
+ if attack_type == "auth":
784
772
  script += "use auxiliary/scanner/mysql/mysql_login\n"
785
773
  script += f"set RHOSTS {host_ip}\n"
786
774
  script += f"set RPORT {port}\n"
@@ -788,34 +776,34 @@ class MSFResourceGenerator:
788
776
  script += "# set USER_FILE /path/to/users.txt\n"
789
777
  script += "# set PASS_FILE /path/to/passwords.txt\n"
790
778
  script += "run\n\n"
791
- elif attack_type == 'extract':
779
+ elif attack_type == "extract":
792
780
  script += "use auxiliary/admin/mysql/mysql_enum\n"
793
781
  script += f"set RHOSTS {host_ip}\n"
794
782
  script += f"set RPORT {port}\n"
795
783
  script += "# set USERNAME root\n"
796
784
  script += "# set PASSWORD password\n"
797
785
  script += "run\n\n"
798
-
799
- elif service_name in ['postgresql', 'postgres']:
800
- if attack_type == 'auth':
786
+
787
+ elif service_name in ["postgresql", "postgres"]:
788
+ if attack_type == "auth":
801
789
  script += "use auxiliary/scanner/postgres/postgres_login\n"
802
790
  script += f"set RHOSTS {host_ip}\n"
803
791
  script += f"set RPORT {port}\n"
804
792
  script += "run\n\n"
805
-
806
- elif service_name in ['mssql', 'ms-sql-s']:
807
- if attack_type == 'auth':
793
+
794
+ elif service_name in ["mssql", "ms-sql-s"]:
795
+ if attack_type == "auth":
808
796
  script += "use auxiliary/scanner/mssql/mssql_login\n"
809
797
  script += f"set RHOSTS {host_ip}\n"
810
798
  script += f"set RPORT {port}\n"
811
799
  script += "run\n\n"
812
- elif attack_type == 'extract':
800
+ elif attack_type == "extract":
813
801
  script += "use auxiliary/admin/mssql/mssql_enum\n"
814
802
  script += f"set RHOSTS {host_ip}\n"
815
803
  script += "# set USERNAME sa\n"
816
804
  script += "# set PASSWORD password\n"
817
805
  script += "run\n\n"
818
-
806
+
819
807
  return script
820
808
 
821
809
  def save_script(self, content: str, filename: str) -> str:
@@ -831,7 +819,7 @@ class MSFResourceGenerator:
831
819
  """
832
820
  filepath = os.path.join(self.output_dir, filename)
833
821
 
834
- with open(filepath, 'w') as f:
822
+ with open(filepath, "w") as f:
835
823
  f.write(content)
836
824
 
837
825
  # Make executable
@@ -845,358 +833,360 @@ class MSFModuleSelector:
845
833
 
846
834
  # Module database with metadata
847
835
  MODULES = {
848
- 'ssh': {
849
- 'scanner': [
836
+ "ssh": {
837
+ "scanner": [
850
838
  {
851
- 'path': 'auxiliary/scanner/ssh/ssh_version',
852
- 'name': 'SSH Version Scanner',
853
- 'description': 'Detect SSH server version',
854
- 'risk': 'safe'
839
+ "path": "auxiliary/scanner/ssh/ssh_version",
840
+ "name": "SSH Version Scanner",
841
+ "description": "Detect SSH server version",
842
+ "risk": "safe",
855
843
  },
856
844
  {
857
- 'path': 'auxiliary/scanner/ssh/ssh_enumusers',
858
- 'name': 'SSH User Enumeration',
859
- 'description': 'Enumerate valid SSH usernames',
860
- 'risk': 'safe'
845
+ "path": "auxiliary/scanner/ssh/ssh_enumusers",
846
+ "name": "SSH User Enumeration",
847
+ "description": "Enumerate valid SSH usernames",
848
+ "risk": "safe",
861
849
  },
862
850
  {
863
- 'path': 'auxiliary/scanner/ssh/ssh_login',
864
- 'name': 'SSH Login Scanner',
865
- 'description': 'Brute force SSH authentication',
866
- 'risk': 'noisy'
851
+ "path": "auxiliary/scanner/ssh/ssh_login",
852
+ "name": "SSH Login Scanner",
853
+ "description": "Brute force SSH authentication",
854
+ "risk": "noisy",
867
855
  },
868
856
  ],
869
- 'exploit': []
857
+ "exploit": [],
870
858
  },
871
- 'smb': {
872
- 'scanner': [
859
+ "smb": {
860
+ "scanner": [
873
861
  {
874
- 'path': 'auxiliary/scanner/smb/smb_version',
875
- 'name': 'SMB Version Detection',
876
- 'description': 'Detect SMB version and OS',
877
- 'risk': 'safe'
862
+ "path": "auxiliary/scanner/smb/smb_version",
863
+ "name": "SMB Version Detection",
864
+ "description": "Detect SMB version and OS",
865
+ "risk": "safe",
878
866
  },
879
867
  {
880
- 'path': 'auxiliary/scanner/smb/smb_enumshares',
881
- 'name': 'SMB Share Enumeration',
882
- 'description': 'List available SMB shares',
883
- 'risk': 'safe'
868
+ "path": "auxiliary/scanner/smb/smb_enumshares",
869
+ "name": "SMB Share Enumeration",
870
+ "description": "List available SMB shares",
871
+ "risk": "safe",
884
872
  },
885
873
  {
886
- 'path': 'auxiliary/scanner/smb/smb_enumusers',
887
- 'name': 'SMB User Enumeration',
888
- 'description': 'Enumerate SMB users via RID cycling',
889
- 'risk': 'safe'
874
+ "path": "auxiliary/scanner/smb/smb_enumusers",
875
+ "name": "SMB User Enumeration",
876
+ "description": "Enumerate SMB users via RID cycling",
877
+ "risk": "safe",
890
878
  },
891
879
  {
892
- 'path': 'auxiliary/scanner/smb/smb_login',
893
- 'name': 'SMB Login Scanner',
894
- 'description': 'Brute force SMB authentication',
895
- 'risk': 'noisy'
880
+ "path": "auxiliary/scanner/smb/smb_login",
881
+ "name": "SMB Login Scanner",
882
+ "description": "Brute force SMB authentication",
883
+ "risk": "noisy",
896
884
  },
897
885
  ],
898
- 'exploit': [
886
+ "exploit": [
899
887
  {
900
- 'path': 'exploit/windows/smb/ms17_010_eternalblue',
901
- 'name': 'EternalBlue SMB RCE',
902
- 'description': 'Exploit MS17-010 (EternalBlue)',
903
- 'risk': 'dangerous',
904
- 'cve': ['CVE-2017-0143', 'CVE-2017-0144', 'CVE-2017-0145']
888
+ "path": "exploit/windows/smb/ms17_010_eternalblue",
889
+ "name": "EternalBlue SMB RCE",
890
+ "description": "Exploit MS17-010 (EternalBlue)",
891
+ "risk": "dangerous",
892
+ "cve": ["CVE-2017-0143", "CVE-2017-0144", "CVE-2017-0145"],
905
893
  },
906
894
  {
907
- 'path': 'exploit/multi/samba/usermap_script',
908
- 'name': 'Samba Usermap Script',
909
- 'description': 'Samba 3.0.20-3.0.25 username map script command execution',
910
- 'risk': 'dangerous',
911
- 'cve': ['CVE-2007-2447']
895
+ "path": "exploit/multi/samba/usermap_script",
896
+ "name": "Samba Usermap Script",
897
+ "description": "Samba 3.0.20-3.0.25 username map script command execution",
898
+ "risk": "dangerous",
899
+ "cve": ["CVE-2007-2447"],
912
900
  },
913
901
  {
914
- 'path': 'exploit/windows/smb/psexec',
915
- 'name': 'PsExec',
916
- 'description': 'Execute commands via SMB (requires creds)',
917
- 'risk': 'moderate',
918
- 'requires': 'credentials'
902
+ "path": "exploit/windows/smb/psexec",
903
+ "name": "PsExec",
904
+ "description": "Execute commands via SMB (requires creds)",
905
+ "risk": "moderate",
906
+ "requires": "credentials",
919
907
  },
920
- ]
908
+ ],
921
909
  },
922
- 'http': {
923
- 'scanner': [
910
+ "http": {
911
+ "scanner": [
924
912
  {
925
- 'path': 'auxiliary/scanner/http/dir_scanner',
926
- 'name': 'Directory Scanner',
927
- 'description': 'Brute force web directories',
928
- 'risk': 'noisy'
913
+ "path": "auxiliary/scanner/http/dir_scanner",
914
+ "name": "Directory Scanner",
915
+ "description": "Brute force web directories",
916
+ "risk": "noisy",
929
917
  },
930
918
  {
931
- 'path': 'auxiliary/scanner/http/http_version',
932
- 'name': 'HTTP Version Detection',
933
- 'description': 'Detect web server version',
934
- 'risk': 'safe'
919
+ "path": "auxiliary/scanner/http/http_version",
920
+ "name": "HTTP Version Detection",
921
+ "description": "Detect web server version",
922
+ "risk": "safe",
935
923
  },
936
924
  {
937
- 'path': 'auxiliary/scanner/http/robots_txt',
938
- 'name': 'Robots.txt Scanner',
939
- 'description': 'Check robots.txt for paths',
940
- 'risk': 'safe'
925
+ "path": "auxiliary/scanner/http/robots_txt",
926
+ "name": "Robots.txt Scanner",
927
+ "description": "Check robots.txt for paths",
928
+ "risk": "safe",
941
929
  },
942
930
  ],
943
- 'exploit': []
931
+ "exploit": [],
944
932
  },
945
- 'https': {
946
- 'scanner': [
933
+ "https": {
934
+ "scanner": [
947
935
  {
948
- 'path': 'auxiliary/scanner/http/dir_scanner',
949
- 'name': 'Directory Scanner',
950
- 'description': 'Brute force web directories',
951
- 'risk': 'noisy'
936
+ "path": "auxiliary/scanner/http/dir_scanner",
937
+ "name": "Directory Scanner",
938
+ "description": "Brute force web directories",
939
+ "risk": "noisy",
952
940
  },
953
941
  {
954
- 'path': 'auxiliary/scanner/http/http_version',
955
- 'name': 'HTTP Version Detection',
956
- 'description': 'Detect web server version',
957
- 'risk': 'safe'
942
+ "path": "auxiliary/scanner/http/http_version",
943
+ "name": "HTTP Version Detection",
944
+ "description": "Detect web server version",
945
+ "risk": "safe",
958
946
  },
959
947
  {
960
- 'path': 'auxiliary/scanner/ssl/ssl_version',
961
- 'name': 'SSL/TLS Version Scanner',
962
- 'description': 'Detect SSL/TLS version and ciphers',
963
- 'risk': 'safe'
948
+ "path": "auxiliary/scanner/ssl/ssl_version",
949
+ "name": "SSL/TLS Version Scanner",
950
+ "description": "Detect SSL/TLS version and ciphers",
951
+ "risk": "safe",
964
952
  },
965
953
  ],
966
- 'exploit': []
954
+ "exploit": [],
967
955
  },
968
- 'ftp': {
969
- 'scanner': [
956
+ "ftp": {
957
+ "scanner": [
970
958
  {
971
- 'path': 'auxiliary/scanner/ftp/ftp_version',
972
- 'name': 'FTP Version Scanner',
973
- 'description': 'Detect FTP server version',
974
- 'risk': 'safe'
959
+ "path": "auxiliary/scanner/ftp/ftp_version",
960
+ "name": "FTP Version Scanner",
961
+ "description": "Detect FTP server version",
962
+ "risk": "safe",
975
963
  },
976
964
  {
977
- 'path': 'auxiliary/scanner/ftp/anonymous',
978
- 'name': 'FTP Anonymous Login',
979
- 'description': 'Check for anonymous FTP access',
980
- 'risk': 'safe'
965
+ "path": "auxiliary/scanner/ftp/anonymous",
966
+ "name": "FTP Anonymous Login",
967
+ "description": "Check for anonymous FTP access",
968
+ "risk": "safe",
981
969
  },
982
970
  {
983
- 'path': 'auxiliary/scanner/ftp/ftp_login',
984
- 'name': 'FTP Login Scanner',
985
- 'description': 'Brute force FTP authentication',
986
- 'risk': 'noisy'
971
+ "path": "auxiliary/scanner/ftp/ftp_login",
972
+ "name": "FTP Login Scanner",
973
+ "description": "Brute force FTP authentication",
974
+ "risk": "noisy",
987
975
  },
988
976
  ],
989
- 'exploit': [
977
+ "exploit": [
990
978
  {
991
- 'path': 'exploit/unix/ftp/vsftpd_234_backdoor',
992
- 'name': 'VSFTPD 2.3.4 Backdoor',
993
- 'description': 'Exploit VSFTPD 2.3.4 backdoor',
994
- 'risk': 'dangerous',
995
- 'cve': ['CVE-2011-2523']
979
+ "path": "exploit/unix/ftp/vsftpd_234_backdoor",
980
+ "name": "VSFTPD 2.3.4 Backdoor",
981
+ "description": "Exploit VSFTPD 2.3.4 backdoor",
982
+ "risk": "dangerous",
983
+ "cve": ["CVE-2011-2523"],
996
984
  },
997
985
  {
998
- 'path': 'exploit/linux/ftp/proftp_sreplace',
999
- 'name': 'ProFTPD sreplace Buffer Overflow',
1000
- 'description': 'Exploit ProFTPD sreplace vulnerability',
1001
- 'risk': 'dangerous'
986
+ "path": "exploit/linux/ftp/proftp_sreplace",
987
+ "name": "ProFTPD sreplace Buffer Overflow",
988
+ "description": "Exploit ProFTPD sreplace vulnerability",
989
+ "risk": "dangerous",
1002
990
  },
1003
- ]
991
+ ],
1004
992
  },
1005
- 'mysql': {
1006
- 'scanner': [
993
+ "mysql": {
994
+ "scanner": [
1007
995
  {
1008
- 'path': 'auxiliary/scanner/mysql/mysql_version',
1009
- 'name': 'MySQL Version Scanner',
1010
- 'description': 'Detect MySQL server version',
1011
- 'risk': 'safe'
996
+ "path": "auxiliary/scanner/mysql/mysql_version",
997
+ "name": "MySQL Version Scanner",
998
+ "description": "Detect MySQL server version",
999
+ "risk": "safe",
1012
1000
  },
1013
1001
  {
1014
- 'path': 'auxiliary/scanner/mysql/mysql_login',
1015
- 'name': 'MySQL Login Scanner',
1016
- 'description': 'Brute force MySQL authentication',
1017
- 'risk': 'noisy'
1002
+ "path": "auxiliary/scanner/mysql/mysql_login",
1003
+ "name": "MySQL Login Scanner",
1004
+ "description": "Brute force MySQL authentication",
1005
+ "risk": "noisy",
1018
1006
  },
1019
1007
  {
1020
- 'path': 'auxiliary/admin/mysql/mysql_enum',
1021
- 'name': 'MySQL Enumeration',
1022
- 'description': 'Enumerate MySQL databases and users',
1023
- 'risk': 'safe'
1008
+ "path": "auxiliary/admin/mysql/mysql_enum",
1009
+ "name": "MySQL Enumeration",
1010
+ "description": "Enumerate MySQL databases and users",
1011
+ "risk": "safe",
1024
1012
  },
1025
1013
  {
1026
- 'path': 'auxiliary/admin/mysql/mysql_sql',
1027
- 'name': 'MySQL SQL Query',
1028
- 'description': 'Execute SQL queries on MySQL (requires creds)',
1029
- 'risk': 'moderate',
1030
- 'requires': 'credentials'
1014
+ "path": "auxiliary/admin/mysql/mysql_sql",
1015
+ "name": "MySQL SQL Query",
1016
+ "description": "Execute SQL queries on MySQL (requires creds)",
1017
+ "risk": "moderate",
1018
+ "requires": "credentials",
1031
1019
  },
1032
1020
  ],
1033
- 'exploit': []
1021
+ "exploit": [],
1034
1022
  },
1035
- 'postgresql': {
1036
- 'scanner': [
1023
+ "postgresql": {
1024
+ "scanner": [
1037
1025
  {
1038
- 'path': 'auxiliary/scanner/postgres/postgres_version',
1039
- 'name': 'PostgreSQL Version Scanner',
1040
- 'description': 'Detect PostgreSQL version',
1041
- 'risk': 'safe'
1026
+ "path": "auxiliary/scanner/postgres/postgres_version",
1027
+ "name": "PostgreSQL Version Scanner",
1028
+ "description": "Detect PostgreSQL version",
1029
+ "risk": "safe",
1042
1030
  },
1043
1031
  {
1044
- 'path': 'auxiliary/scanner/postgres/postgres_login',
1045
- 'name': 'PostgreSQL Login Scanner',
1046
- 'description': 'Brute force PostgreSQL authentication',
1047
- 'risk': 'noisy'
1032
+ "path": "auxiliary/scanner/postgres/postgres_login",
1033
+ "name": "PostgreSQL Login Scanner",
1034
+ "description": "Brute force PostgreSQL authentication",
1035
+ "risk": "noisy",
1048
1036
  },
1049
1037
  ],
1050
- 'exploit': []
1038
+ "exploit": [],
1051
1039
  },
1052
- 'mssql': {
1053
- 'scanner': [
1040
+ "mssql": {
1041
+ "scanner": [
1054
1042
  {
1055
- 'path': 'auxiliary/scanner/mssql/mssql_ping',
1056
- 'name': 'MSSQL Ping Scanner',
1057
- 'description': 'Discover MSSQL instances',
1058
- 'risk': 'safe'
1043
+ "path": "auxiliary/scanner/mssql/mssql_ping",
1044
+ "name": "MSSQL Ping Scanner",
1045
+ "description": "Discover MSSQL instances",
1046
+ "risk": "safe",
1059
1047
  },
1060
1048
  {
1061
- 'path': 'auxiliary/scanner/mssql/mssql_login',
1062
- 'name': 'MSSQL Login Scanner',
1063
- 'description': 'Brute force MSSQL authentication',
1064
- 'risk': 'noisy'
1049
+ "path": "auxiliary/scanner/mssql/mssql_login",
1050
+ "name": "MSSQL Login Scanner",
1051
+ "description": "Brute force MSSQL authentication",
1052
+ "risk": "noisy",
1065
1053
  },
1066
1054
  {
1067
- 'path': 'auxiliary/admin/mssql/mssql_enum',
1068
- 'name': 'MSSQL Enumeration',
1069
- 'description': 'Enumerate MSSQL databases and configuration',
1070
- 'risk': 'safe'
1055
+ "path": "auxiliary/admin/mssql/mssql_enum",
1056
+ "name": "MSSQL Enumeration",
1057
+ "description": "Enumerate MSSQL databases and configuration",
1058
+ "risk": "safe",
1071
1059
  },
1072
1060
  ],
1073
- 'exploit': []
1061
+ "exploit": [],
1074
1062
  },
1075
- 'rdp': {
1076
- 'scanner': [
1063
+ "rdp": {
1064
+ "scanner": [
1077
1065
  {
1078
- 'path': 'auxiliary/scanner/rdp/rdp_scanner',
1079
- 'name': 'RDP Scanner',
1080
- 'description': 'Detect RDP service',
1081
- 'risk': 'safe'
1066
+ "path": "auxiliary/scanner/rdp/rdp_scanner",
1067
+ "name": "RDP Scanner",
1068
+ "description": "Detect RDP service",
1069
+ "risk": "safe",
1082
1070
  },
1083
1071
  {
1084
- 'path': 'auxiliary/scanner/rdp/cve_2019_0708_bluekeep',
1085
- 'name': 'BlueKeep Scanner',
1086
- 'description': 'Check for CVE-2019-0708 vulnerability',
1087
- 'risk': 'safe'
1072
+ "path": "auxiliary/scanner/rdp/cve_2019_0708_bluekeep",
1073
+ "name": "BlueKeep Scanner",
1074
+ "description": "Check for CVE-2019-0708 vulnerability",
1075
+ "risk": "safe",
1088
1076
  },
1089
1077
  ],
1090
- 'exploit': [
1078
+ "exploit": [
1091
1079
  {
1092
- 'path': 'exploit/windows/rdp/cve_2019_0708_bluekeep_rce',
1093
- 'name': 'BlueKeep RDP RCE',
1094
- 'description': 'Exploit CVE-2019-0708 (BlueKeep)',
1095
- 'risk': 'dangerous',
1096
- 'cve': ['CVE-2019-0708']
1080
+ "path": "exploit/windows/rdp/cve_2019_0708_bluekeep_rce",
1081
+ "name": "BlueKeep RDP RCE",
1082
+ "description": "Exploit CVE-2019-0708 (BlueKeep)",
1083
+ "risk": "dangerous",
1084
+ "cve": ["CVE-2019-0708"],
1097
1085
  },
1098
- ]
1086
+ ],
1099
1087
  },
1100
- 'telnet': {
1101
- 'scanner': [
1088
+ "telnet": {
1089
+ "scanner": [
1102
1090
  {
1103
- 'path': 'auxiliary/scanner/telnet/telnet_version',
1104
- 'name': 'Telnet Version Scanner',
1105
- 'description': 'Detect Telnet service version',
1106
- 'risk': 'safe'
1091
+ "path": "auxiliary/scanner/telnet/telnet_version",
1092
+ "name": "Telnet Version Scanner",
1093
+ "description": "Detect Telnet service version",
1094
+ "risk": "safe",
1107
1095
  },
1108
1096
  {
1109
- 'path': 'auxiliary/scanner/telnet/telnet_login',
1110
- 'name': 'Telnet Login Scanner',
1111
- 'description': 'Brute force Telnet authentication',
1112
- 'risk': 'noisy'
1097
+ "path": "auxiliary/scanner/telnet/telnet_login",
1098
+ "name": "Telnet Login Scanner",
1099
+ "description": "Brute force Telnet authentication",
1100
+ "risk": "noisy",
1113
1101
  },
1114
1102
  ],
1115
- 'exploit': []
1103
+ "exploit": [],
1116
1104
  },
1117
- 'smtp': {
1118
- 'scanner': [
1105
+ "smtp": {
1106
+ "scanner": [
1119
1107
  {
1120
- 'path': 'auxiliary/scanner/smtp/smtp_version',
1121
- 'name': 'SMTP Version Scanner',
1122
- 'description': 'Detect SMTP server version',
1123
- 'risk': 'safe'
1108
+ "path": "auxiliary/scanner/smtp/smtp_version",
1109
+ "name": "SMTP Version Scanner",
1110
+ "description": "Detect SMTP server version",
1111
+ "risk": "safe",
1124
1112
  },
1125
1113
  {
1126
- 'path': 'auxiliary/scanner/smtp/smtp_enum',
1127
- 'name': 'SMTP User Enumeration',
1128
- 'description': 'Enumerate SMTP users via VRFY/EXPN',
1129
- 'risk': 'safe'
1114
+ "path": "auxiliary/scanner/smtp/smtp_enum",
1115
+ "name": "SMTP User Enumeration",
1116
+ "description": "Enumerate SMTP users via VRFY/EXPN",
1117
+ "risk": "safe",
1130
1118
  },
1131
1119
  ],
1132
- 'exploit': []
1120
+ "exploit": [],
1133
1121
  },
1134
- 'vnc': {
1135
- 'scanner': [
1122
+ "vnc": {
1123
+ "scanner": [
1136
1124
  {
1137
- 'path': 'auxiliary/scanner/vnc/vnc_none_auth',
1138
- 'name': 'VNC No Authentication Scanner',
1139
- 'description': 'Check for VNC without authentication',
1140
- 'risk': 'safe'
1125
+ "path": "auxiliary/scanner/vnc/vnc_none_auth",
1126
+ "name": "VNC No Authentication Scanner",
1127
+ "description": "Check for VNC without authentication",
1128
+ "risk": "safe",
1141
1129
  },
1142
1130
  {
1143
- 'path': 'auxiliary/scanner/vnc/vnc_login',
1144
- 'name': 'VNC Login Scanner',
1145
- 'description': 'Brute force VNC authentication',
1146
- 'risk': 'noisy'
1131
+ "path": "auxiliary/scanner/vnc/vnc_login",
1132
+ "name": "VNC Login Scanner",
1133
+ "description": "Brute force VNC authentication",
1134
+ "risk": "noisy",
1147
1135
  },
1148
1136
  ],
1149
- 'exploit': []
1137
+ "exploit": [],
1150
1138
  },
1151
- 'snmp': {
1152
- 'scanner': [
1139
+ "snmp": {
1140
+ "scanner": [
1153
1141
  {
1154
- 'path': 'auxiliary/scanner/snmp/snmp_enum',
1155
- 'name': 'SNMP Enumeration',
1156
- 'description': 'Enumerate SNMP information',
1157
- 'risk': 'safe'
1142
+ "path": "auxiliary/scanner/snmp/snmp_enum",
1143
+ "name": "SNMP Enumeration",
1144
+ "description": "Enumerate SNMP information",
1145
+ "risk": "safe",
1158
1146
  },
1159
1147
  {
1160
- 'path': 'auxiliary/scanner/snmp/snmp_login',
1161
- 'name': 'SNMP Community String Scanner',
1162
- 'description': 'Brute force SNMP community strings',
1163
- 'risk': 'noisy'
1148
+ "path": "auxiliary/scanner/snmp/snmp_login",
1149
+ "name": "SNMP Community String Scanner",
1150
+ "description": "Brute force SNMP community strings",
1151
+ "risk": "noisy",
1164
1152
  },
1165
1153
  ],
1166
- 'exploit': []
1154
+ "exploit": [],
1167
1155
  },
1168
- 'nfs': {
1169
- 'scanner': [
1156
+ "nfs": {
1157
+ "scanner": [
1170
1158
  {
1171
- 'path': 'auxiliary/scanner/nfs/nfsmount',
1172
- 'name': 'NFS Mount Scanner',
1173
- 'description': 'Enumerate NFS mounts',
1174
- 'risk': 'safe'
1159
+ "path": "auxiliary/scanner/nfs/nfsmount",
1160
+ "name": "NFS Mount Scanner",
1161
+ "description": "Enumerate NFS mounts",
1162
+ "risk": "safe",
1175
1163
  },
1176
1164
  ],
1177
- 'exploit': []
1165
+ "exploit": [],
1178
1166
  },
1179
- 'redis': {
1180
- 'scanner': [
1167
+ "redis": {
1168
+ "scanner": [
1181
1169
  {
1182
- 'path': 'auxiliary/scanner/redis/redis_server',
1183
- 'name': 'Redis Scanner',
1184
- 'description': 'Detect Redis service',
1185
- 'risk': 'safe'
1170
+ "path": "auxiliary/scanner/redis/redis_server",
1171
+ "name": "Redis Scanner",
1172
+ "description": "Detect Redis service",
1173
+ "risk": "safe",
1186
1174
  },
1187
1175
  ],
1188
- 'exploit': [
1176
+ "exploit": [
1189
1177
  {
1190
- 'path': 'exploit/linux/redis/redis_replication_cmd_exec',
1191
- 'name': 'Redis Replication Code Execution',
1192
- 'description': 'Exploit Redis via replication',
1193
- 'risk': 'dangerous'
1178
+ "path": "exploit/linux/redis/redis_replication_cmd_exec",
1179
+ "name": "Redis Replication Code Execution",
1180
+ "description": "Exploit Redis via replication",
1181
+ "risk": "dangerous",
1194
1182
  },
1195
- ]
1183
+ ],
1196
1184
  },
1197
1185
  }
1198
1186
 
1199
- def get_recommendations(self, service: str, version: str = None, include_risk: List[str] = None) -> List[Dict]:
1187
+ def get_recommendations(
1188
+ self, service: str, version: str = None, include_risk: List[str] = None
1189
+ ) -> List[Dict]:
1200
1190
  """
1201
1191
  Get module recommendations for a service.
1202
1192
 
@@ -1209,7 +1199,7 @@ class MSFModuleSelector:
1209
1199
  List of recommended modules
1210
1200
  """
1211
1201
  if include_risk is None:
1212
- include_risk = ['safe', 'noisy', 'moderate']
1202
+ include_risk = ["safe", "noisy", "moderate"]
1213
1203
 
1214
1204
  service_lower = service.lower()
1215
1205
  recommendations = []
@@ -1218,15 +1208,17 @@ class MSFModuleSelector:
1218
1208
  if service_lower in self.MODULES:
1219
1209
  modules = self.MODULES[service_lower]
1220
1210
 
1221
- for category in ['scanner', 'exploit']:
1211
+ for category in ["scanner", "exploit"]:
1222
1212
  for module in modules.get(category, []):
1223
- if module['risk'] in include_risk:
1224
- module['category'] = category
1213
+ if module["risk"] in include_risk:
1214
+ module["category"] = category
1225
1215
  recommendations.append(module)
1226
1216
 
1227
1217
  return recommendations
1228
1218
 
1229
- def match_vulnerability_to_exploit(self, vuln_title: str, vuln_desc: str = '', cves: List[str] = None) -> List[Dict]:
1219
+ def match_vulnerability_to_exploit(
1220
+ self, vuln_title: str, vuln_desc: str = "", cves: List[str] = None
1221
+ ) -> List[Dict]:
1230
1222
  """
1231
1223
  Match a vulnerability to potential exploit modules.
1232
1224
 
@@ -1241,16 +1233,18 @@ class MSFModuleSelector:
1241
1233
  matches = []
1242
1234
 
1243
1235
  for service, modules in self.MODULES.items():
1244
- for exploit in modules.get('exploit', []):
1236
+ for exploit in modules.get("exploit", []):
1245
1237
  # Check CVE match
1246
- if cves and 'cve' in exploit:
1247
- if any(cve in exploit['cve'] for cve in cves):
1238
+ if cves and "cve" in exploit:
1239
+ if any(cve in exploit["cve"] for cve in cves):
1248
1240
  matches.append(exploit)
1249
1241
  continue
1250
1242
 
1251
1243
  # Check keyword match
1252
- keywords = vuln_title.lower() + ' ' + vuln_desc.lower()
1253
- module_keywords = exploit['name'].lower() + ' ' + exploit['description'].lower()
1244
+ keywords = vuln_title.lower() + " " + vuln_desc.lower()
1245
+ module_keywords = (
1246
+ exploit["name"].lower() + " " + exploit["description"].lower()
1247
+ )
1254
1248
 
1255
1249
  if any(word in keywords for word in module_keywords.split()):
1256
1250
  matches.append(exploit)
@@ -1263,74 +1257,76 @@ class MSFModuleSelector:
1263
1257
  version: str = None,
1264
1258
  engagement_id: int = None,
1265
1259
  risk_levels: List[str] = None,
1266
- include_cve_matches: bool = True
1260
+ include_cve_matches: bool = True,
1267
1261
  ) -> List[Dict]:
1268
1262
  """
1269
1263
  Get intelligent recommendations based on service and version.
1270
-
1264
+
1271
1265
  Args:
1272
1266
  service: Service name (ssh, smb, http, etc.)
1273
1267
  version: Service version string
1274
1268
  engagement_id: Engagement ID for context
1275
1269
  risk_levels: Risk levels to include
1276
1270
  include_cve_matches: Include CVE-matched modules
1277
-
1271
+
1278
1272
  Returns:
1279
1273
  Prioritized list of recommendations with scores
1280
1274
  """
1281
1275
  if risk_levels is None:
1282
- risk_levels = ['safe', 'noisy', 'moderate']
1283
-
1276
+ risk_levels = ["safe", "noisy", "moderate"]
1277
+
1284
1278
  recommendations = []
1285
-
1279
+
1286
1280
  # Get basic module recommendations
1287
1281
  basic_recs = self.get_recommendations(service, version, risk_levels)
1288
-
1282
+
1289
1283
  # Add CVE matching if version provided
1290
1284
  if version and include_cve_matches:
1291
1285
  matching_cves = VersionMatcher.get_cves_for_version(service, version)
1292
-
1286
+
1293
1287
  for cve_id in matching_cves:
1294
1288
  cve_data = CVE_DATABASE.get(cve_id, {})
1295
- for module_path in cve_data.get('modules', []):
1289
+ for module_path in cve_data.get("modules", []):
1296
1290
  # Find if this module exists in our database
1297
1291
  module_info = self._find_module_by_path(module_path)
1298
1292
  if module_info:
1299
1293
  # Enhance with CVE data
1300
1294
  enhanced_module = module_info.copy()
1301
- enhanced_module['cve_match'] = cve_id
1302
- enhanced_module['cvss'] = cve_data.get('cvss')
1303
- enhanced_module['reliability'] = cve_data.get('reliability')
1304
- enhanced_module['impact'] = cve_data.get('impact')
1305
- enhanced_module['score'] = self._score_module(enhanced_module, engagement_id)
1295
+ enhanced_module["cve_match"] = cve_id
1296
+ enhanced_module["cvss"] = cve_data.get("cvss")
1297
+ enhanced_module["reliability"] = cve_data.get("reliability")
1298
+ enhanced_module["impact"] = cve_data.get("impact")
1299
+ enhanced_module["score"] = self._score_module(
1300
+ enhanced_module, engagement_id
1301
+ )
1306
1302
  recommendations.append(enhanced_module)
1307
-
1303
+
1308
1304
  # Add basic recommendations with scores
1309
1305
  for module in basic_recs:
1310
- if not any(r.get('path') == module.get('path') for r in recommendations):
1311
- module['score'] = self._score_module(module, engagement_id)
1306
+ if not any(r.get("path") == module.get("path") for r in recommendations):
1307
+ module["score"] = self._score_module(module, engagement_id)
1312
1308
  recommendations.append(module)
1313
-
1309
+
1314
1310
  # Sort by score descending
1315
- recommendations.sort(key=lambda x: x.get('score', 0), reverse=True)
1316
-
1311
+ recommendations.sort(key=lambda x: x.get("score", 0), reverse=True)
1312
+
1317
1313
  return recommendations
1318
1314
 
1319
1315
  def _find_module_by_path(self, module_path: str) -> Optional[Dict]:
1320
1316
  """Find module info by path in module database."""
1321
1317
  for service, modules in self.MODULES.items():
1322
- for category in ['scanner', 'exploit']:
1318
+ for category in ["scanner", "exploit"]:
1323
1319
  for module in modules.get(category, []):
1324
- if module.get('path') == module_path:
1320
+ if module.get("path") == module_path:
1325
1321
  result = module.copy()
1326
- result['category'] = category
1322
+ result["category"] = category
1327
1323
  return result
1328
1324
  return None
1329
1325
 
1330
1326
  def _score_module(self, module: Dict, engagement_id: int = None) -> float:
1331
1327
  """
1332
1328
  Score module (0-100) based on various factors.
1333
-
1329
+
1334
1330
  Scoring breakdown:
1335
1331
  - CVE match (30 points)
1336
1332
  - CVSS score (25 points)
@@ -1339,33 +1335,34 @@ class MSFModuleSelector:
1339
1335
  - Risk level (10 points)
1340
1336
  """
1341
1337
  score = 0.0
1342
-
1338
+
1343
1339
  # CVE match bonus
1344
- if module.get('cve_match'):
1340
+ if module.get("cve_match"):
1345
1341
  score += 30.0
1346
-
1342
+
1347
1343
  # CVSS score (normalize to 25 points)
1348
- cvss = module.get('cvss', 0)
1344
+ cvss = module.get("cvss", 0)
1349
1345
  if cvss:
1350
1346
  score += (cvss / 10.0) * 25.0
1351
-
1347
+
1352
1348
  # Reliability rating
1353
1349
  reliability_scores = {
1354
- 'excellent': 20.0,
1355
- 'good': 15.0,
1356
- 'normal': 10.0,
1357
- 'average': 5.0,
1358
- 'low': 2.0
1350
+ "excellent": 20.0,
1351
+ "good": 15.0,
1352
+ "normal": 10.0,
1353
+ "average": 5.0,
1354
+ "low": 2.0,
1359
1355
  }
1360
- reliability = module.get('reliability', 'normal')
1356
+ reliability = module.get("reliability", "normal")
1361
1357
  score += reliability_scores.get(reliability, 10.0)
1362
-
1358
+
1363
1359
  # Prerequisites (check if we have creds available)
1364
- if module.get('requires') == 'credentials':
1360
+ if module.get("requires") == "credentials":
1365
1361
  if engagement_id:
1366
1362
  # Check if we have credentials
1367
1363
  try:
1368
1364
  from souleyez.storage.credentials import CredentialsManager
1365
+
1369
1366
  cm = CredentialsManager()
1370
1367
  creds = cm.list_credentials(engagement_id)
1371
1368
  if creds:
@@ -1376,33 +1373,28 @@ class MSFModuleSelector:
1376
1373
  score += 5.0
1377
1374
  else:
1378
1375
  score += 15.0 # No prerequisites needed
1379
-
1376
+
1380
1377
  # Risk level (lower risk = higher score for safety)
1381
- risk_scores = {
1382
- 'safe': 10.0,
1383
- 'noisy': 7.0,
1384
- 'moderate': 5.0,
1385
- 'dangerous': 2.0
1386
- }
1387
- risk = module.get('risk', 'moderate')
1378
+ risk_scores = {"safe": 10.0, "noisy": 7.0, "moderate": 5.0, "dangerous": 2.0}
1379
+ risk = module.get("risk", "moderate")
1388
1380
  score += risk_scores.get(risk, 5.0)
1389
-
1381
+
1390
1382
  return score
1391
1383
 
1392
1384
  def query_live_msf_modules(self, search_term: str) -> List[Dict]:
1393
1385
  """
1394
1386
  Query actual MSF installation for modules.
1395
-
1387
+
1396
1388
  Args:
1397
1389
  search_term: Search term for msfconsole
1398
-
1390
+
1399
1391
  Returns:
1400
1392
  List of modules with metadata
1401
1393
  """
1402
1394
  console = MSFConsoleManager()
1403
1395
  if not console.is_available():
1404
1396
  return []
1405
-
1397
+
1406
1398
  try:
1407
1399
  output = console.execute_command(f"search {search_term}")
1408
1400
  modules = self._parse_msf_search_output(output)
@@ -1413,172 +1405,170 @@ class MSFModuleSelector:
1413
1405
  def _parse_msf_search_output(self, output: str) -> List[Dict]:
1414
1406
  """Parse msfconsole search output."""
1415
1407
  modules = []
1416
- lines = output.split('\n')
1417
-
1408
+ lines = output.split("\n")
1409
+
1418
1410
  for line in lines:
1419
1411
  # Skip headers and empty lines
1420
- if not line.strip() or 'Matching Modules' in line or '=====' in line:
1412
+ if not line.strip() or "Matching Modules" in line or "=====" in line:
1421
1413
  continue
1422
-
1414
+
1423
1415
  # Parse module line
1424
1416
  parts = line.strip().split(None, 3)
1425
1417
  if len(parts) >= 3:
1426
- modules.append({
1427
- 'path': parts[0],
1428
- 'disclosure_date': parts[1] if len(parts) > 1 else None,
1429
- 'rank': parts[2] if len(parts) > 2 else None,
1430
- 'name': parts[3] if len(parts) > 3 else parts[0]
1431
- })
1432
-
1418
+ modules.append(
1419
+ {
1420
+ "path": parts[0],
1421
+ "disclosure_date": parts[1] if len(parts) > 1 else None,
1422
+ "rank": parts[2] if len(parts) > 2 else None,
1423
+ "name": parts[3] if len(parts) > 3 else parts[0],
1424
+ }
1425
+ )
1426
+
1433
1427
  return modules
1434
1428
 
1435
1429
  def get_credential_powered_modules(
1436
- self,
1437
- engagement_id: int,
1438
- service_type: str = None
1430
+ self, engagement_id: int, service_type: str = None
1439
1431
  ) -> List[Dict]:
1440
1432
  """
1441
1433
  Get modules that can leverage discovered credentials.
1442
-
1434
+
1443
1435
  Args:
1444
1436
  engagement_id: Engagement ID
1445
1437
  service_type: Optional service type filter
1446
-
1438
+
1447
1439
  Returns:
1448
1440
  List of modules that can use available credentials
1449
1441
  """
1450
1442
  try:
1451
1443
  from souleyez.storage.credentials import CredentialsManager
1444
+
1452
1445
  cm = CredentialsManager()
1453
1446
  creds = cm.list_credentials(engagement_id)
1454
1447
  except:
1455
1448
  return []
1456
-
1449
+
1457
1450
  if not creds:
1458
1451
  return []
1459
-
1452
+
1460
1453
  # Group creds by service
1461
1454
  cred_map = {}
1462
1455
  for cred in creds:
1463
- service = cred.get('service', 'unknown').lower()
1456
+ service = cred.get("service", "unknown").lower()
1464
1457
  if service not in cred_map:
1465
1458
  cred_map[service] = []
1466
1459
  cred_map[service].append(cred)
1467
-
1460
+
1468
1461
  powered_modules = []
1469
-
1462
+
1470
1463
  for service, service_creds in cred_map.items():
1471
1464
  if service_type and service_type.lower() not in service:
1472
1465
  continue
1473
-
1466
+
1474
1467
  # Get modules requiring credentials
1475
1468
  modules = self.get_recommendations(
1476
- service=service,
1477
- include_risk=['safe', 'noisy', 'moderate']
1469
+ service=service, include_risk=["safe", "noisy", "moderate"]
1478
1470
  )
1479
-
1471
+
1480
1472
  for module in modules:
1481
- if module.get('requires') == 'credentials':
1482
- module['available_credentials'] = len(service_creds)
1483
- module['ready_to_run'] = True
1484
- module['score'] = 95.0 # High score since we have creds
1473
+ if module.get("requires") == "credentials":
1474
+ module["available_credentials"] = len(service_creds)
1475
+ module["ready_to_run"] = True
1476
+ module["score"] = 95.0 # High score since we have creds
1485
1477
  powered_modules.append(module)
1486
-
1478
+
1487
1479
  return powered_modules
1488
1480
 
1489
1481
 
1490
1482
  class ModuleRecommendationEngine:
1491
1483
  """Advanced recommendation engine with context-aware scoring."""
1492
-
1484
+
1493
1485
  def __init__(self):
1494
1486
  self.module_selector = MSFModuleSelector()
1495
-
1487
+
1496
1488
  def get_ranked_recommendations(
1497
- self,
1498
- host_id: int,
1499
- service_id: int,
1500
- engagement_id: int
1489
+ self, host_id: int, service_id: int, engagement_id: int
1501
1490
  ) -> List[Dict]:
1502
1491
  """
1503
1492
  Return ranked list of modules with scores and rationale.
1504
-
1493
+
1505
1494
  Args:
1506
1495
  host_id: Host ID
1507
1496
  service_id: Service ID
1508
1497
  engagement_id: Engagement ID
1509
-
1498
+
1510
1499
  Returns:
1511
1500
  List of ranked recommendations with metadata
1512
1501
  """
1513
1502
  try:
1514
1503
  from souleyez.storage.hosts import HostManager
1504
+
1515
1505
  hm = HostManager()
1516
1506
  service = hm.get_service(service_id)
1517
-
1507
+
1518
1508
  if not service:
1519
1509
  return []
1520
-
1521
- service_name = service.get('service_name', '')
1522
- service_version = service.get('service_version', '')
1523
-
1510
+
1511
+ service_name = service.get("service_name", "")
1512
+ service_version = service.get("service_version", "")
1513
+
1524
1514
  recommendations = self.module_selector.get_recommendations_for_service(
1525
1515
  service=service_name,
1526
1516
  version=service_version,
1527
1517
  engagement_id=engagement_id,
1528
- include_cve_matches=True
1518
+ include_cve_matches=True,
1529
1519
  )
1530
-
1520
+
1531
1521
  # Enhance with rationale
1532
1522
  for rec in recommendations:
1533
- rec['rationale'] = self._generate_rationale(rec)
1534
- rec['prerequisites_met'] = self._check_prerequisites(rec, engagement_id)
1535
- rec['estimated_success'] = self._estimate_success(rec)
1536
-
1523
+ rec["rationale"] = self._generate_rationale(rec)
1524
+ rec["prerequisites_met"] = self._check_prerequisites(rec, engagement_id)
1525
+ rec["estimated_success"] = self._estimate_success(rec)
1526
+
1537
1527
  return recommendations
1538
1528
  except:
1539
1529
  return []
1540
-
1530
+
1541
1531
  def _generate_rationale(self, module: Dict) -> str:
1542
1532
  """Generate human-readable rationale for recommendation."""
1543
1533
  reasons = []
1544
-
1545
- if module.get('cve_match'):
1534
+
1535
+ if module.get("cve_match"):
1546
1536
  reasons.append(f"CVE {module['cve_match']} match")
1547
-
1548
- if module.get('cvss', 0) >= 9.0:
1537
+
1538
+ if module.get("cvss", 0) >= 9.0:
1549
1539
  reasons.append("Critical CVSS score")
1550
- elif module.get('cvss', 0) >= 7.0:
1540
+ elif module.get("cvss", 0) >= 7.0:
1551
1541
  reasons.append("High CVSS score")
1552
-
1553
- if module.get('reliability') == 'excellent':
1542
+
1543
+ if module.get("reliability") == "excellent":
1554
1544
  reasons.append("Excellent reliability")
1555
-
1556
- if module.get('ready_to_run'):
1545
+
1546
+ if module.get("ready_to_run"):
1557
1547
  reasons.append("Credentials available")
1558
-
1548
+
1559
1549
  if not reasons:
1560
1550
  reasons.append("Standard recommendation")
1561
-
1551
+
1562
1552
  return " + ".join(reasons)
1563
-
1553
+
1564
1554
  def _check_prerequisites(self, module: Dict, engagement_id: int) -> bool:
1565
1555
  """Check if module prerequisites are met."""
1566
- if module.get('requires') == 'credentials':
1567
- return module.get('available_credentials', 0) > 0
1556
+ if module.get("requires") == "credentials":
1557
+ return module.get("available_credentials", 0) > 0
1568
1558
  return True
1569
-
1559
+
1570
1560
  def _estimate_success(self, module: Dict) -> str:
1571
1561
  """Estimate success probability."""
1572
- score = module.get('score', 0)
1573
-
1562
+ score = module.get("score", 0)
1563
+
1574
1564
  if score >= 80:
1575
- return 'high'
1565
+ return "high"
1576
1566
  elif score >= 60:
1577
- return 'medium'
1567
+ return "medium"
1578
1568
  elif score >= 40:
1579
- return 'low'
1569
+ return "low"
1580
1570
  else:
1581
- return 'very low'
1571
+ return "very low"
1582
1572
 
1583
1573
 
1584
1574
  class MSFConsoleManager:
@@ -1592,14 +1582,15 @@ class MSFConsoleManager:
1592
1582
  def _check_needs_no_readline(self) -> bool:
1593
1583
  """Check if --no-readline flag is needed (ARM64 Ubuntu has reline bug)."""
1594
1584
  import platform
1585
+
1595
1586
  # Only needed on ARM64 (aarch64)
1596
- if platform.machine() != 'aarch64':
1587
+ if platform.machine() != "aarch64":
1597
1588
  return False
1598
1589
  # Check if we're on Ubuntu (not Kali which works fine)
1599
1590
  try:
1600
- with open('/etc/os-release', 'r') as f:
1591
+ with open("/etc/os-release", "r") as f:
1601
1592
  content = f.read().lower()
1602
- if 'ubuntu' in content and 'kali' not in content:
1593
+ if "ubuntu" in content and "kali" not in content:
1603
1594
  return True
1604
1595
  except:
1605
1596
  pass
@@ -1610,15 +1601,15 @@ class MSFConsoleManager:
1610
1601
  import shutil
1611
1602
 
1612
1603
  # First try shutil.which (fastest)
1613
- path = shutil.which('msfconsole')
1604
+ path = shutil.which("msfconsole")
1614
1605
  if path:
1615
1606
  return path
1616
1607
 
1617
1608
  # Try common locations
1618
1609
  locations = [
1619
- '/usr/bin/msfconsole',
1620
- '/opt/metasploit-framework/bin/msfconsole',
1621
- '/usr/local/bin/msfconsole',
1610
+ "/usr/bin/msfconsole",
1611
+ "/opt/metasploit-framework/bin/msfconsole",
1612
+ "/usr/local/bin/msfconsole",
1622
1613
  ]
1623
1614
 
1624
1615
  for loc in locations:
@@ -1631,7 +1622,9 @@ class MSFConsoleManager:
1631
1622
  """Check if msfconsole is available."""
1632
1623
  return self.msf_path is not None
1633
1624
 
1634
- def launch_with_resource(self, resource_file: str, background: bool = False, use_sudo: bool = True) -> subprocess.Popen:
1625
+ def launch_with_resource(
1626
+ self, resource_file: str, background: bool = False, use_sudo: bool = True
1627
+ ) -> subprocess.Popen:
1635
1628
  """
1636
1629
  Launch msfconsole with a resource script.
1637
1630
 
@@ -1648,24 +1641,31 @@ class MSFConsoleManager:
1648
1641
 
1649
1642
  cmd = []
1650
1643
  if use_sudo:
1651
- cmd.append('sudo')
1644
+ cmd.append("sudo")
1652
1645
  cmd.append(self.msf_path)
1653
- cmd.append('-q')
1646
+ cmd.append("-q")
1654
1647
  if self._needs_no_readline:
1655
- cmd.append('--no-readline')
1656
- cmd.extend(['-r', resource_file])
1648
+ cmd.append("--no-readline")
1649
+ cmd.extend(["-r", resource_file])
1657
1650
 
1658
1651
  if background:
1659
1652
  return subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1660
1653
  else:
1661
1654
  # Interactive mode - use os.system for proper TTY handling
1662
1655
  # subprocess.run doesn't properly inherit terminal attributes
1663
- os.system(shlex.join(cmd)) # nosec B605 - intentional shell for TTY, args escaped with shlex
1656
+ os.system(
1657
+ shlex.join(cmd)
1658
+ ) # nosec B605 - intentional shell for TTY, args escaped with shlex
1664
1659
  # Reset terminal after msfconsole (ARM64 Ruby readline corrupts it)
1665
- os.system('stty sane 2>/dev/null') # nosec B605 - static command
1660
+ os.system("stty sane 2>/dev/null") # nosec B605 - static command
1666
1661
  return None
1667
1662
 
1668
- def launch_interactive(self, pre_commands: List[str] = None, workspace: str = None, use_sudo: bool = True) -> None:
1663
+ def launch_interactive(
1664
+ self,
1665
+ pre_commands: List[str] = None,
1666
+ workspace: str = None,
1667
+ use_sudo: bool = True,
1668
+ ) -> None:
1669
1669
  """
1670
1670
  Launch interactive msfconsole with optional pre-commands.
1671
1671
 
@@ -1680,16 +1680,17 @@ class MSFConsoleManager:
1680
1680
  # Build command
1681
1681
  cmd = []
1682
1682
  if use_sudo:
1683
- cmd.append('sudo')
1683
+ cmd.append("sudo")
1684
1684
  cmd.append(self.msf_path)
1685
- cmd.append('-q')
1685
+ cmd.append("-q")
1686
1686
  if self._needs_no_readline:
1687
- cmd.append('--no-readline')
1687
+ cmd.append("--no-readline")
1688
1688
 
1689
1689
  # Create temporary resource script if we have pre-commands
1690
1690
  if pre_commands or workspace:
1691
1691
  import tempfile
1692
- with tempfile.NamedTemporaryFile(mode='w', suffix='.rc', delete=False) as f:
1692
+
1693
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".rc", delete=False) as f:
1693
1694
  if workspace:
1694
1695
  f.write(f"workspace -a {workspace}\n")
1695
1696
  f.write(f"workspace {workspace}\n")
@@ -1701,18 +1702,22 @@ class MSFConsoleManager:
1701
1702
  rc_file = f.name
1702
1703
 
1703
1704
  try:
1704
- cmd.extend(['-r', rc_file])
1705
+ cmd.extend(["-r", rc_file])
1705
1706
  # Use os.system for proper TTY handling
1706
- os.system(shlex.join(cmd)) # nosec B605 - intentional shell for TTY, args escaped with shlex
1707
+ os.system(
1708
+ shlex.join(cmd)
1709
+ ) # nosec B605 - intentional shell for TTY, args escaped with shlex
1707
1710
  # Reset terminal after msfconsole (ARM64 Ruby readline corrupts it)
1708
- os.system('stty sane 2>/dev/null') # nosec B605 - static command
1711
+ os.system("stty sane 2>/dev/null") # nosec B605 - static command
1709
1712
  finally:
1710
1713
  os.unlink(rc_file)
1711
1714
  else:
1712
1715
  # Just launch msfconsole - use os.system for proper TTY handling
1713
- os.system(shlex.join(cmd)) # nosec B605 - intentional shell for TTY, args escaped with shlex
1716
+ os.system(
1717
+ shlex.join(cmd)
1718
+ ) # nosec B605 - intentional shell for TTY, args escaped with shlex
1714
1719
  # Reset terminal after msfconsole (ARM64 Ruby readline corrupts it)
1715
- os.system('stty sane 2>/dev/null') # nosec B605 - static command
1720
+ os.system("stty sane 2>/dev/null") # nosec B605 - static command
1716
1721
 
1717
1722
  def execute_command(self, command: str) -> str:
1718
1723
  """
@@ -1729,17 +1734,18 @@ class MSFConsoleManager:
1729
1734
 
1730
1735
  # Create temporary rc file
1731
1736
  import tempfile
1732
- with tempfile.NamedTemporaryFile(mode='w', suffix='.rc', delete=False) as f:
1733
- f.write(command + '\n')
1734
- f.write('exit\n')
1737
+
1738
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".rc", delete=False) as f:
1739
+ f.write(command + "\n")
1740
+ f.write("exit\n")
1735
1741
  rc_file = f.name
1736
1742
 
1737
1743
  try:
1738
1744
  result = subprocess.run(
1739
- [self.msf_path, '-q', '-r', rc_file],
1745
+ [self.msf_path, "-q", "-r", rc_file],
1740
1746
  capture_output=True,
1741
1747
  text=True,
1742
- timeout=30
1748
+ timeout=30,
1743
1749
  )
1744
1750
  return result.stdout
1745
1751
  finally: