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
souleyez/plugins/hydra.py CHANGED
@@ -27,11 +27,11 @@ HELP = {
27
27
  "- Useful for testing credential reuse across services\n"
28
28
  "- Be cautious: aggressive brute-forcing may lock accounts\n"
29
29
  ),
30
- "usage": "souleyez jobs enqueue hydra <target> --args \"<service> -l <user> -p <pass>\"",
30
+ "usage": 'souleyez jobs enqueue hydra <target> --args "<service> -l <user> -p <pass>"',
31
31
  "examples": [
32
- "souleyez jobs enqueue hydra 192.168.1.10 --args \"ssh -l admin -P data/wordlists/top100.txt\"",
33
- "souleyez jobs enqueue hydra 192.168.1.10 --args \"ftp -L data/wordlists/all_users.txt -P data/wordlists/top100.txt\"",
34
- "souleyez jobs enqueue hydra 192.168.1.10 --args \"smb -l administrator -p password123\"",
32
+ 'souleyez jobs enqueue hydra 192.168.1.10 --args "ssh -l admin -P data/wordlists/top100.txt"',
33
+ 'souleyez jobs enqueue hydra 192.168.1.10 --args "ftp -L data/wordlists/all_users.txt -P data/wordlists/top100.txt"',
34
+ 'souleyez jobs enqueue hydra 192.168.1.10 --args "smb -l administrator -p password123"',
35
35
  "souleyez jobs enqueue hydra example.com --args \"http-post-form '/login.php:username=^USER^&password=^PASS^:F=incorrect' -L data/wordlists/all_users.txt -P data/wordlists/top100.txt\"",
36
36
  ],
37
37
  "flags": [
@@ -49,140 +49,433 @@ HELP = {
49
49
  "ssh": [
50
50
  {
51
51
  "name": "SSH Brute-Force",
52
- "args": ["ssh", "-l", "root", "-P", "data/wordlists/top100.txt", "-t", "1", "-w", "3", "-vV"],
53
- "desc": "Username(s) + password list (1 thread, 3s delay)"
52
+ "args": [
53
+ "ssh",
54
+ "-l",
55
+ "root",
56
+ "-P",
57
+ "data/wordlists/top100.txt",
58
+ "-t",
59
+ "1",
60
+ "-w",
61
+ "3",
62
+ "-vV",
63
+ ],
64
+ "desc": "Username(s) + password list (1 thread, 3s delay)",
54
65
  },
55
66
  {
56
67
  "name": "SSH Password Spray",
57
- "args": ["ssh", "-L", "users.txt", "-p", "Password123!", "-t", "1", "-w", "5", "-vV"],
58
- "desc": "One password against user list (stealthy)"
68
+ "args": [
69
+ "ssh",
70
+ "-L",
71
+ "users.txt",
72
+ "-p",
73
+ "Password123!",
74
+ "-t",
75
+ "1",
76
+ "-w",
77
+ "5",
78
+ "-vV",
79
+ ],
80
+ "desc": "One password against user list (stealthy)",
59
81
  },
60
82
  {
61
83
  "name": "SSH Quick Check",
62
- "args": ["ssh", "-L", "users.txt", "-e", "ns", "-t", "4", "-w", "1", "-vV"],
63
- "desc": "Empty + username-as-password"
64
- }
84
+ "args": [
85
+ "ssh",
86
+ "-L",
87
+ "users.txt",
88
+ "-e",
89
+ "ns",
90
+ "-t",
91
+ "4",
92
+ "-w",
93
+ "1",
94
+ "-vV",
95
+ ],
96
+ "desc": "Empty + username-as-password",
97
+ },
65
98
  ],
66
99
  "ftp": [
67
100
  {
68
101
  "name": "FTP Anonymous",
69
- "args": ["ftp", "-l", "anonymous", "-p", "anonymous@", "-t", "1", "-vV"],
70
- "desc": "Test anonymous login"
102
+ "args": [
103
+ "ftp",
104
+ "-l",
105
+ "anonymous",
106
+ "-p",
107
+ "anonymous@",
108
+ "-t",
109
+ "1",
110
+ "-vV",
111
+ ],
112
+ "desc": "Test anonymous login",
71
113
  },
72
114
  {
73
115
  "name": "FTP Brute-Force",
74
- "args": ["ftp", "-L", "users.txt", "-P", "passwords.txt", "-t", "2", "-w", "1", "-vV"],
75
- "desc": "Username(s) + password list (2 threads, 1s delay)"
116
+ "args": [
117
+ "ftp",
118
+ "-L",
119
+ "users.txt",
120
+ "-P",
121
+ "passwords.txt",
122
+ "-t",
123
+ "2",
124
+ "-w",
125
+ "1",
126
+ "-vV",
127
+ ],
128
+ "desc": "Username(s) + password list (2 threads, 1s delay)",
76
129
  },
77
130
  {
78
131
  "name": "FTP Quick Check",
79
- "args": ["ftp", "-L", "users.txt", "-e", "ns", "-t", "4", "-w", "1", "-vV"],
80
- "desc": "Empty + username-as-password"
81
- }
132
+ "args": [
133
+ "ftp",
134
+ "-L",
135
+ "users.txt",
136
+ "-e",
137
+ "ns",
138
+ "-t",
139
+ "4",
140
+ "-w",
141
+ "1",
142
+ "-vV",
143
+ ],
144
+ "desc": "Empty + username-as-password",
145
+ },
82
146
  ],
83
147
  "smb": [
84
148
  {
85
149
  "name": "SMB Brute-Force",
86
- "args": ["smb", "-l", "administrator", "-P", "data/wordlists/top100.txt", "-t", "1", "-w", "2", "-vV"],
87
- "desc": "Username(s) + password list (1 thread, 2s delay)"
150
+ "args": [
151
+ "smb",
152
+ "-l",
153
+ "administrator",
154
+ "-P",
155
+ "data/wordlists/top100.txt",
156
+ "-t",
157
+ "1",
158
+ "-w",
159
+ "2",
160
+ "-vV",
161
+ ],
162
+ "desc": "Username(s) + password list (1 thread, 2s delay)",
88
163
  },
89
164
  {
90
165
  "name": "SMB Quick Check",
91
- "args": ["smb", "-L", "users.txt", "-e", "ns", "-t", "2", "-w", "1", "-vV"],
92
- "desc": "Empty + username-as-password"
93
- }
166
+ "args": [
167
+ "smb",
168
+ "-L",
169
+ "users.txt",
170
+ "-e",
171
+ "ns",
172
+ "-t",
173
+ "2",
174
+ "-w",
175
+ "1",
176
+ "-vV",
177
+ ],
178
+ "desc": "Empty + username-as-password",
179
+ },
94
180
  ],
95
181
  "http": [
96
182
  {
97
183
  "name": "HTTP Basic Auth",
98
- "args": ["http-get", "/admin", "-l", "admin", "-P", "passwords.txt", "-t", "2", "-vV"],
99
- "desc": "Username(s) + password list (2 threads)"
184
+ "args": [
185
+ "http-get",
186
+ "/admin",
187
+ "-l",
188
+ "admin",
189
+ "-P",
190
+ "passwords.txt",
191
+ "-t",
192
+ "2",
193
+ "-vV",
194
+ ],
195
+ "desc": "Username(s) + password list (2 threads)",
100
196
  }
101
197
  ],
102
198
  "wordpress": [
103
199
  {
104
200
  "name": "WP Username Enum",
105
- "args": ["-L", "users.txt", "-p", "test", "-t", "2", "-w", "2", "-vV", "http-post-form", "/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In:F=Invalid username"],
106
- "desc": "Find valid usernames (reports success if username exists)"
201
+ "args": [
202
+ "-L",
203
+ "users.txt",
204
+ "-p",
205
+ "test",
206
+ "-t",
207
+ "2",
208
+ "-w",
209
+ "2",
210
+ "-vV",
211
+ "http-post-form",
212
+ "/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In:F=Invalid username",
213
+ ],
214
+ "desc": "Find valid usernames (reports success if username exists)",
107
215
  },
108
216
  {
109
217
  "name": "WP Password Attack",
110
- "args": ["-l", "admin", "-P", "data/wordlists/top100.txt", "-t", "2", "-w", "2", "-vV", "http-post-form", "/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In:F=is incorrect"],
111
- "desc": "Crack password for known user"
218
+ "args": [
219
+ "-l",
220
+ "admin",
221
+ "-P",
222
+ "data/wordlists/top100.txt",
223
+ "-t",
224
+ "2",
225
+ "-w",
226
+ "2",
227
+ "-vV",
228
+ "http-post-form",
229
+ "/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In:F=is incorrect",
230
+ ],
231
+ "desc": "Crack password for known user",
112
232
  },
113
233
  {
114
234
  "name": "WP Password Spray",
115
- "args": ["-L", "users.txt", "-p", "Password123!", "-t", "1", "-w", "3", "-vV", "http-post-form", "/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In:F=is incorrect"],
116
- "desc": "One password against known users"
235
+ "args": [
236
+ "-L",
237
+ "users.txt",
238
+ "-p",
239
+ "Password123!",
240
+ "-t",
241
+ "1",
242
+ "-w",
243
+ "3",
244
+ "-vV",
245
+ "http-post-form",
246
+ "/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In:F=is incorrect",
247
+ ],
248
+ "desc": "One password against known users",
117
249
  },
118
250
  {
119
251
  "name": "WP Quick Check",
120
- "args": ["-L", "users.txt", "-e", "ns", "-t", "2", "-w", "2", "-vV", "http-post-form", "/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In:F=is incorrect"],
121
- "desc": "Empty + username-as-password on known users"
122
- }
123
- ]
252
+ "args": [
253
+ "-L",
254
+ "users.txt",
255
+ "-e",
256
+ "ns",
257
+ "-t",
258
+ "2",
259
+ "-w",
260
+ "2",
261
+ "-vV",
262
+ "http-post-form",
263
+ "/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In:F=is incorrect",
264
+ ],
265
+ "desc": "Empty + username-as-password on known users",
266
+ },
267
+ ],
124
268
  },
125
269
  "presets": [
126
270
  # Flattened for backward compatibility
127
- {"name": "SSH Brute-Force", "args": ["ssh", "-l", "root", "-P", "data/wordlists/top100.txt", "-t", "4", "-vV"], "desc": "SSH brute-force with wordlist"},
128
- {"name": "FTP Anonymous", "args": ["ftp", "-l", "anonymous", "-p", "anonymous@", "-t", "1", "-vV"], "desc": "Test FTP anonymous login"},
129
- {"name": "SMB Brute-Force", "args": ["smb", "-l", "administrator", "-P", "data/wordlists/top100.txt", "-t", "2", "-vV"], "desc": "SMB brute-force with wordlist"},
271
+ {
272
+ "name": "SSH Brute-Force",
273
+ "args": [
274
+ "ssh",
275
+ "-l",
276
+ "root",
277
+ "-P",
278
+ "data/wordlists/top100.txt",
279
+ "-t",
280
+ "4",
281
+ "-vV",
282
+ ],
283
+ "desc": "SSH brute-force with wordlist",
284
+ },
285
+ {
286
+ "name": "FTP Anonymous",
287
+ "args": ["ftp", "-l", "anonymous", "-p", "anonymous@", "-t", "1", "-vV"],
288
+ "desc": "Test FTP anonymous login",
289
+ },
290
+ {
291
+ "name": "SMB Brute-Force",
292
+ "args": [
293
+ "smb",
294
+ "-l",
295
+ "administrator",
296
+ "-P",
297
+ "data/wordlists/top100.txt",
298
+ "-t",
299
+ "2",
300
+ "-vV",
301
+ ],
302
+ "desc": "SMB brute-force with wordlist",
303
+ },
130
304
  # Router Brute-Force
131
- {"name": "Router HTTP Basic", "args": ["http-get", "/", "-L", "data/wordlists/router_users.txt", "-P", "data/wordlists/router_passwords.txt", "-t", "2", "-w", "3", "-vV", "-f"], "desc": "Router web admin (HTTP Basic Auth)"},
132
- {"name": "Router SSH", "args": ["ssh", "-L", "data/wordlists/router_users.txt", "-P", "data/wordlists/router_passwords.txt", "-t", "1", "-w", "5", "-vV", "-f"], "desc": "Router SSH login"},
133
- {"name": "Router Telnet", "args": ["telnet", "-L", "data/wordlists/router_users.txt", "-P", "data/wordlists/router_passwords.txt", "-t", "2", "-w", "3", "-vV", "-f"], "desc": "Router Telnet login"},
305
+ {
306
+ "name": "Router HTTP Basic",
307
+ "args": [
308
+ "http-get",
309
+ "/",
310
+ "-L",
311
+ "data/wordlists/router_users.txt",
312
+ "-P",
313
+ "data/wordlists/router_passwords.txt",
314
+ "-t",
315
+ "2",
316
+ "-w",
317
+ "3",
318
+ "-vV",
319
+ "-f",
320
+ ],
321
+ "desc": "Router web admin (HTTP Basic Auth)",
322
+ },
323
+ {
324
+ "name": "Router SSH",
325
+ "args": [
326
+ "ssh",
327
+ "-L",
328
+ "data/wordlists/router_users.txt",
329
+ "-P",
330
+ "data/wordlists/router_passwords.txt",
331
+ "-t",
332
+ "1",
333
+ "-w",
334
+ "5",
335
+ "-vV",
336
+ "-f",
337
+ ],
338
+ "desc": "Router SSH login",
339
+ },
340
+ {
341
+ "name": "Router Telnet",
342
+ "args": [
343
+ "telnet",
344
+ "-L",
345
+ "data/wordlists/router_users.txt",
346
+ "-P",
347
+ "data/wordlists/router_passwords.txt",
348
+ "-t",
349
+ "2",
350
+ "-w",
351
+ "3",
352
+ "-vV",
353
+ "-f",
354
+ ],
355
+ "desc": "Router Telnet login",
356
+ },
134
357
  # macOS Brute-Force
135
- {"name": "macOS SSH", "args": ["ssh", "-L", "data/wordlists/macos_users.txt", "-P", "data/wordlists/top100.txt", "-t", "1", "-w", "5", "-vV", "-f"], "desc": "macOS Remote Login"},
136
- {"name": "AFP Brute", "args": ["afp", "-L", "data/wordlists/macos_users.txt", "-P", "data/wordlists/top100.txt", "-s", "548", "-t", "2", "-w", "3", "-vV", "-f"], "desc": "Apple File Sharing login"},
137
- {"name": "VNC Brute", "args": ["vnc", "-P", "data/wordlists/vnc_passwords.txt", "-s", "5900", "-t", "2", "-w", "3", "-vV", "-f"], "desc": "VNC/Screen Sharing password"},
358
+ {
359
+ "name": "macOS SSH",
360
+ "args": [
361
+ "ssh",
362
+ "-L",
363
+ "data/wordlists/macos_users.txt",
364
+ "-P",
365
+ "data/wordlists/top100.txt",
366
+ "-t",
367
+ "1",
368
+ "-w",
369
+ "5",
370
+ "-vV",
371
+ "-f",
372
+ ],
373
+ "desc": "macOS Remote Login",
374
+ },
375
+ {
376
+ "name": "AFP Brute",
377
+ "args": [
378
+ "afp",
379
+ "-L",
380
+ "data/wordlists/macos_users.txt",
381
+ "-P",
382
+ "data/wordlists/top100.txt",
383
+ "-s",
384
+ "548",
385
+ "-t",
386
+ "2",
387
+ "-w",
388
+ "3",
389
+ "-vV",
390
+ "-f",
391
+ ],
392
+ "desc": "Apple File Sharing login",
393
+ },
394
+ {
395
+ "name": "VNC Brute",
396
+ "args": [
397
+ "vnc",
398
+ "-P",
399
+ "data/wordlists/vnc_passwords.txt",
400
+ "-s",
401
+ "5900",
402
+ "-t",
403
+ "2",
404
+ "-w",
405
+ "3",
406
+ "-vV",
407
+ "-f",
408
+ ],
409
+ "desc": "VNC/Screen Sharing password",
410
+ },
138
411
  ],
139
412
  "help_sections": [
140
413
  {
141
414
  "title": "What is Hydra?",
142
415
  "color": "cyan",
143
416
  "content": [
144
- {"title": "Overview", "desc": "Hydra is one of the fastest network login crackers, supporting 50+ protocols including SSH, FTP, HTTP, SMB, RDP, and many more."},
145
- {"title": "Use Cases", "desc": "Test weak passwords and credential reuse", "tips": [
146
- "Brute-force network service logins",
147
- "Test password policies and lockout thresholds",
148
- "Validate credential reuse across services",
149
- "Check for default/weak passwords"
150
- ]}
151
- ]
417
+ {
418
+ "title": "Overview",
419
+ "desc": "Hydra is one of the fastest network login crackers, supporting 50+ protocols including SSH, FTP, HTTP, SMB, RDP, and many more.",
420
+ },
421
+ {
422
+ "title": "Use Cases",
423
+ "desc": "Test weak passwords and credential reuse",
424
+ "tips": [
425
+ "Brute-force network service logins",
426
+ "Test password policies and lockout thresholds",
427
+ "Validate credential reuse across services",
428
+ "Check for default/weak passwords",
429
+ ],
430
+ },
431
+ ],
152
432
  },
153
433
  {
154
434
  "title": "How to Use",
155
435
  "color": "green",
156
436
  "content": [
157
- {"title": "Basic Workflow", "desc": "1. Select target service (ssh, ftp, smb, http, etc.)\n 2. Choose attack mode (single user or user list)\n 3. Set low thread count to avoid lockouts\n 4. Monitor for successful credentials"},
158
- {"title": "Attack Modes", "desc": "Different credential testing strategies", "tips": [
159
- "Single user: -l user -P passwords.txt",
160
- "User list: -L users.txt -P passwords.txt",
161
- "Password spray: -L users.txt -p Password123!",
162
- "Username as password: -e s (user:user)"
163
- ]}
164
- ]
437
+ {
438
+ "title": "Basic Workflow",
439
+ "desc": "1. Select target service (ssh, ftp, smb, http, etc.)\n 2. Choose attack mode (single user or user list)\n 3. Set low thread count to avoid lockouts\n 4. Monitor for successful credentials",
440
+ },
441
+ {
442
+ "title": "Attack Modes",
443
+ "desc": "Different credential testing strategies",
444
+ "tips": [
445
+ "Single user: -l user -P passwords.txt",
446
+ "User list: -L users.txt -P passwords.txt",
447
+ "Password spray: -L users.txt -p Password123!",
448
+ "Username as password: -e s (user:user)",
449
+ ],
450
+ },
451
+ ],
165
452
  },
166
453
  {
167
454
  "title": "Tips & Best Practices",
168
455
  "color": "yellow",
169
456
  "content": [
170
- ("Best Practices:", [
171
- "Use low thread counts (-t 1-4) to avoid lockouts",
172
- "Add delays (-w 2-5) between attempts",
173
- "Try username as password first (-e s)",
174
- "Password spray (one password, many users) is stealthier",
175
- "Stop on first success (-f) to minimize impact"
176
- ]),
177
- ("Common Issues:", [
178
- "Account lockouts: Reduce -t threads and add -w delay",
179
- "Too slow: Increase threads or reduce wordlist",
180
- "Connection refused: Verify service is running",
181
- "No results: Check credentials format and service type"
182
- ])
183
- ]
184
- }
185
- ]
457
+ (
458
+ "Best Practices:",
459
+ [
460
+ "Use low thread counts (-t 1-4) to avoid lockouts",
461
+ "Add delays (-w 2-5) between attempts",
462
+ "Try username as password first (-e s)",
463
+ "Password spray (one password, many users) is stealthier",
464
+ "Stop on first success (-f) to minimize impact",
465
+ ],
466
+ ),
467
+ (
468
+ "Common Issues:",
469
+ [
470
+ "Account lockouts: Reduce -t threads and add -w delay",
471
+ "Too slow: Increase threads or reduce wordlist",
472
+ "Connection refused: Verify service is running",
473
+ "No results: Check credentials format and service type",
474
+ ],
475
+ ),
476
+ ],
477
+ },
478
+ ],
186
479
  }
187
480
 
188
481
 
@@ -205,29 +498,41 @@ class HydraPlugin(PluginBase):
205
498
  # Use ssh -o with PreferredAuthentications to test
206
499
  # If password auth is disabled, SSH will fail with specific error
207
500
  result = subprocess.run(
208
- ['ssh', '-o', 'BatchMode=yes', '-o', 'ConnectTimeout=5',
209
- '-o', 'PreferredAuthentications=password',
210
- '-o', 'StrictHostKeyChecking=no',
211
- '-o', 'UserKnownHostsFile=/dev/null',
212
- '-p', str(port), f'root@{host}', 'exit'],
501
+ [
502
+ "ssh",
503
+ "-o",
504
+ "BatchMode=yes",
505
+ "-o",
506
+ "ConnectTimeout=5",
507
+ "-o",
508
+ "PreferredAuthentications=password",
509
+ "-o",
510
+ "StrictHostKeyChecking=no",
511
+ "-o",
512
+ "UserKnownHostsFile=/dev/null",
513
+ "-p",
514
+ str(port),
515
+ f"root@{host}",
516
+ "exit",
517
+ ],
213
518
  capture_output=True,
214
519
  text=True,
215
- timeout=10
520
+ timeout=10,
216
521
  )
217
522
 
218
523
  stderr = result.stderr.lower()
219
524
 
220
525
  # Check for common error messages
221
- if 'permission denied' in stderr:
526
+ if "permission denied" in stderr:
222
527
  # Password auth is available but wrong password
223
528
  return True, "Password authentication available"
224
- elif 'no more authentication methods' in stderr:
529
+ elif "no more authentication methods" in stderr:
225
530
  return False, "Password authentication disabled (key-only)"
226
- elif 'connection refused' in stderr:
531
+ elif "connection refused" in stderr:
227
532
  return False, "SSH connection refused (service not running?)"
228
- elif 'connection timed out' in stderr or 'timed out' in stderr:
533
+ elif "connection timed out" in stderr or "timed out" in stderr:
229
534
  return False, "SSH connection timed out"
230
- elif 'could not resolve' in stderr or 'no route' in stderr:
535
+ elif "could not resolve" in stderr or "no route" in stderr:
231
536
  return False, "Cannot reach host"
232
537
  else:
233
538
  # Assume password auth is available if we can't determine otherwise
@@ -244,11 +549,17 @@ class HydraPlugin(PluginBase):
244
549
  def _is_http_service(self, args: List[str]) -> bool:
245
550
  """Check if args contain HTTP/HTTPS service."""
246
551
  http_services = {
247
- 'http-get', 'http-post', 'http-head',
248
- 'http-get-form', 'http-post-form',
249
- 'https-get', 'https-post', 'https-head',
250
- 'https-get-form', 'https-post-form',
251
- 'http-proxy'
552
+ "http-get",
553
+ "http-post",
554
+ "http-head",
555
+ "http-get-form",
556
+ "http-post-form",
557
+ "https-get",
558
+ "https-post",
559
+ "https-head",
560
+ "https-get-form",
561
+ "https-post-form",
562
+ "http-proxy",
252
563
  }
253
564
  return any(arg in http_services for arg in args)
254
565
 
@@ -266,12 +577,24 @@ class HydraPlugin(PluginBase):
266
577
  Returns:
267
578
  Modified args list with path injected
268
579
  """
269
- if not path or path == '/':
580
+ if not path or path == "/":
270
581
  return args # No path to inject
271
582
 
272
583
  new_args = []
273
- form_services = {'http-post-form', 'https-post-form', 'http-get-form', 'https-get-form'}
274
- get_services = {'http-get', 'https-get', 'http-post', 'https-post', 'http-head', 'https-head'}
584
+ form_services = {
585
+ "http-post-form",
586
+ "https-post-form",
587
+ "http-get-form",
588
+ "https-get-form",
589
+ }
590
+ get_services = {
591
+ "http-get",
592
+ "https-get",
593
+ "http-post",
594
+ "https-post",
595
+ "http-head",
596
+ "https-head",
597
+ }
275
598
 
276
599
  i = 0
277
600
  while i < len(args):
@@ -281,7 +604,7 @@ class HydraPlugin(PluginBase):
281
604
  if arg in get_services:
282
605
  new_args.append(arg)
283
606
  # Check if next arg is already a path
284
- if i + 1 < len(args) and args[i + 1].startswith('/'):
607
+ if i + 1 < len(args) and args[i + 1].startswith("/"):
285
608
  # Path already exists, don't inject
286
609
  new_args.append(args[i + 1])
287
610
  i += 2
@@ -296,13 +619,34 @@ class HydraPlugin(PluginBase):
296
619
  # Next arg should be the form string
297
620
  if i + 1 < len(args):
298
621
  form_string = args[i + 1]
299
- # Check if path already in form string (strip quotes first)
622
+ # Strip quotes for checking
300
623
  cleaned_check = form_string.lstrip("'\"")
301
- if not cleaned_check.startswith('/'):
302
- # Inject path at beginning: '/path:user=^USER^...'
303
- # Strip leading quotes and colons from form string
624
+
625
+ if cleaned_check.startswith("/"):
626
+ # Form string has absolute path (e.g., /wp-login.php:...)
627
+ # Combine URL base path with form path for subpath installs
628
+ # e.g., URL path=/blogblog/ + form=/wp-login.php -> /blogblog/wp-login.php
629
+ if path != "/" and not path.endswith("/"):
630
+ path = path + "/"
631
+
632
+ # Extract form path and rest
633
+ if ":" in cleaned_check:
634
+ form_path, rest = cleaned_check.split(":", 1)
635
+ else:
636
+ form_path = cleaned_check
637
+ rest = ""
638
+
639
+ # Combine paths: /blogblog/ + /wp-login.php -> /blogblog/wp-login.php
640
+ # Strip trailing slash from URL path and leading slash from form path
641
+ combined_path = path.rstrip("/") + form_path
642
+ form_string = (
643
+ f"{combined_path}:{rest}" if rest else combined_path
644
+ )
645
+ else:
646
+ # No absolute path - inject URL path at beginning
304
647
  cleaned = form_string.lstrip("':\"")
305
648
  form_string = f"{path}:{cleaned}"
649
+
306
650
  new_args.append(form_string)
307
651
  i += 2
308
652
  else:
@@ -332,23 +676,22 @@ class HydraPlugin(PluginBase):
332
676
 
333
677
  port = parsed.port
334
678
  if port is None:
335
- port = 443 if parsed.scheme == 'https' else 80
679
+ port = 443 if parsed.scheme == "https" else 80
336
680
 
337
- path = parsed.path or ''
681
+ path = parsed.path or ""
338
682
  if parsed.query:
339
- path += f'?{parsed.query}'
683
+ path += f"?{parsed.query}"
340
684
 
341
- is_default = (
342
- (parsed.scheme == 'http' and port == 80) or
343
- (parsed.scheme == 'https' and port == 443)
685
+ is_default = (parsed.scheme == "http" and port == 80) or (
686
+ parsed.scheme == "https" and port == 443
344
687
  )
345
688
 
346
689
  return {
347
- 'host': host,
348
- 'port': port,
349
- 'path': path,
350
- 'scheme': parsed.scheme,
351
- 'is_default_port': is_default
690
+ "host": host,
691
+ "port": port,
692
+ "path": path,
693
+ "scheme": parsed.scheme,
694
+ "is_default_port": is_default,
352
695
  }
353
696
 
354
697
  def _parse_host_port(self, target: str) -> tuple:
@@ -364,18 +707,18 @@ class HydraPlugin(PluginBase):
364
707
  import re
365
708
 
366
709
  # Check for IPv6 with port: [::1]:8080
367
- ipv6_port_match = re.match(r'^\[([^\]]+)\]:(\d+)$', target)
710
+ ipv6_port_match = re.match(r"^\[([^\]]+)\]:(\d+)$", target)
368
711
  if ipv6_port_match:
369
712
  return ipv6_port_match.group(1), int(ipv6_port_match.group(2))
370
713
 
371
714
  # Check for IPv6 without port: ::1 or [::1]
372
- if ':' in target and not target.count(':') == 1:
715
+ if ":" in target and not target.count(":") == 1:
373
716
  # Multiple colons = IPv6, strip brackets if present
374
- return target.strip('[]'), None
717
+ return target.strip("[]"), None
375
718
 
376
719
  # Check for host:port format (single colon)
377
- if ':' in target:
378
- parts = target.rsplit(':', 1)
720
+ if ":" in target:
721
+ parts = target.rsplit(":", 1)
379
722
  host = parts[0]
380
723
  try:
381
724
  port = int(parts[1])
@@ -387,11 +730,13 @@ class HydraPlugin(PluginBase):
387
730
  # No port found
388
731
  return target, None
389
732
 
390
- def build_command(self, target: str, args: List[str] = None, label: str = "", log_path: str = None):
733
+ def build_command(
734
+ self, target: str, args: List[str] = None, label: str = "", log_path: str = None
735
+ ):
391
736
  """Build Hydra command for background execution with PID tracking."""
392
737
  if not target:
393
738
  if log_path:
394
- with open(log_path, 'w') as f:
739
+ with open(log_path, "w") as f:
395
740
  f.write("ERROR: Target host is required\n")
396
741
  return None
397
742
 
@@ -401,28 +746,34 @@ class HydraPlugin(PluginBase):
401
746
  # Handle host:port format (e.g., 10.0.0.73:80)
402
747
  # Extract port and add -s flag if not already present
403
748
  extracted_port = None
404
- if not target.startswith(('http://', 'https://')):
749
+ if not target.startswith(("http://", "https://")):
405
750
  host, port = self._parse_host_port(target)
406
751
  if port is not None:
407
752
  target = host # Use just the host part
408
753
  extracted_port = port
409
754
  # Add -s PORT if not already in args
410
- if '-s' not in args:
411
- args = ['-s', str(port)] + args
755
+ if "-s" not in args:
756
+ args = ["-s", str(port)] + args
412
757
  if log_path:
413
- with open(log_path, 'a') as f:
414
- f.write(f"INFO: Extracted port {port} from target, using -s flag\n\n")
758
+ with open(log_path, "a") as f:
759
+ f.write(
760
+ f"INFO: Extracted port {port} from target, using -s flag\n\n"
761
+ )
415
762
 
416
763
  # Handle URL parsing for HTTP services
417
764
  if args and self._is_http_service(args):
418
765
  # Check for URL format (single target only)
419
- if target.startswith(('http://', 'https://')):
766
+ if target.startswith(("http://", "https://")):
420
767
  # Reject if multiple targets
421
- if ' ' in target:
768
+ if " " in target:
422
769
  if log_path:
423
- with open(log_path, 'w') as f:
424
- f.write("ERROR: URL format not supported with multiple targets\n")
425
- f.write("For multi-target attacks, use hostnames/IPs without URLs\n")
770
+ with open(log_path, "w") as f:
771
+ f.write(
772
+ "ERROR: URL format not supported with multiple targets\n"
773
+ )
774
+ f.write(
775
+ "For multi-target attacks, use hostnames/IPs without URLs\n"
776
+ )
426
777
  return None
427
778
 
428
779
  # Parse URL
@@ -430,39 +781,49 @@ class HydraPlugin(PluginBase):
430
781
  parsed = self._parse_url_for_hydra(target)
431
782
  except ValidationError as e:
432
783
  if log_path:
433
- with open(log_path, 'w') as f:
784
+ with open(log_path, "w") as f:
434
785
  f.write(f"ERROR: {e}\n")
435
786
  return None
436
787
 
437
788
  # Replace target with extracted host
438
- target = parsed['host']
789
+ target = parsed["host"]
439
790
 
440
791
  # Inject -s PORT if non-default and -s not already in args
441
- if not parsed['is_default_port']:
442
- if '-s' not in args:
443
- args = ['-s', str(parsed['port'])] + args
792
+ if not parsed["is_default_port"]:
793
+ if "-s" not in args:
794
+ args = ["-s", str(parsed["port"])] + args
444
795
  elif log_path:
445
796
  # Warn about port conflict
446
- with open(log_path, 'a') as f:
797
+ with open(log_path, "a") as f:
447
798
  f.write(f"INFO: Port conflict detected\n")
448
799
  f.write(f" URL port: {parsed['port']}\n")
449
- f.write(f" Using -s flag value (explicit flag takes precedence)\n\n")
800
+ f.write(
801
+ f" Using -s flag value (explicit flag takes precedence)\n\n"
802
+ )
450
803
 
451
804
  # Inject path into args if present
452
- if parsed['path'] and parsed['path'] != '/':
453
- args = self._inject_path_into_args(args, parsed['path'])
805
+ if parsed["path"] and parsed["path"] != "/":
806
+ args = self._inject_path_into_args(args, parsed["path"])
454
807
  if log_path:
455
- with open(log_path, 'a') as f:
456
- f.write(f"INFO: Auto-injected path from URL: {parsed['path']}\n\n")
808
+ with open(log_path, "a") as f:
809
+ f.write(
810
+ f"INFO: Auto-injected path from URL: {parsed['path']}\n\n"
811
+ )
457
812
 
458
- elif target.startswith(('http://', 'https://')):
813
+ elif target.startswith(("http://", "https://")):
459
814
  # URL provided but not HTTP service - reject
460
815
  if log_path:
461
- with open(log_path, 'w') as f:
462
- f.write("ERROR: URL format only supported for HTTP/HTTPS services\n")
816
+ with open(log_path, "w") as f:
817
+ f.write(
818
+ "ERROR: URL format only supported for HTTP/HTTPS services\n"
819
+ )
463
820
  f.write(f"Target: {target}\n")
464
- f.write("For HTTP services, use: http-get, http-post, http-post-form, etc.\n")
465
- f.write("For non-HTTP services (ssh, ftp, smb), use hostname or IP only\n")
821
+ f.write(
822
+ "For HTTP services, use: http-get, http-post, http-post-form, etc.\n"
823
+ )
824
+ f.write(
825
+ "For non-HTTP services (ssh, ftp, smb), use hostname or IP only\n"
826
+ )
466
827
  return None
467
828
 
468
829
  # Handle multiple targets (space-separated)
@@ -474,14 +835,14 @@ class HydraPlugin(PluginBase):
474
835
  validated_targets.append(validate_target(t.strip()))
475
836
  except ValidationError as e:
476
837
  if log_path:
477
- with open(log_path, 'w') as f:
838
+ with open(log_path, "w") as f:
478
839
  f.write(f"ERROR: Invalid target: {e}\n")
479
840
  return None
480
841
 
481
842
  # If multiple targets, create a temporary file and use -M flag
482
843
  import tempfile
483
844
  import os
484
-
845
+
485
846
  # Hydra syntax: hydra [OPTIONS] target service [SERVICE-OPTIONS]
486
847
  # Need to split args into: global options, service type, and service options
487
848
  global_opts = []
@@ -492,12 +853,32 @@ class HydraPlugin(PluginBase):
492
853
  while i < len(args):
493
854
  arg = args[i]
494
855
  # Service types (these go after target)
495
- if arg in ['ssh', 'ftp', 'smb', 'http-get', 'http-post', 'http-head',
496
- 'http-get-form', 'http-post-form', 'https-get', 'https-post',
497
- 'https-head', 'https-get-form', 'https-post-form']:
856
+ if arg in [
857
+ "ssh",
858
+ "ftp",
859
+ "smb",
860
+ "rdp",
861
+ "telnet",
862
+ "vnc",
863
+ "mysql",
864
+ "postgres",
865
+ "mssql",
866
+ "oracle",
867
+ "http-get",
868
+ "http-post",
869
+ "http-head",
870
+ "http-get-form",
871
+ "http-post-form",
872
+ "https-get",
873
+ "https-post",
874
+ "https-head",
875
+ "https-get-form",
876
+ "https-post-form",
877
+ "http-proxy",
878
+ ]:
498
879
  service_type = arg
499
880
  # Everything after service type is service options
500
- service_opts = args[i + 1:]
881
+ service_opts = args[i + 1 :]
501
882
  break
502
883
  else:
503
884
  # Global options (go before target)
@@ -505,12 +886,12 @@ class HydraPlugin(PluginBase):
505
886
  i += 1
506
887
 
507
888
  # Add legacy SSH algorithm support for older servers
508
- if service_type == 'ssh':
889
+ if service_type == "ssh":
509
890
  # Check if SSH supports password authentication before wasting time
510
891
  ssh_port = extracted_port or 22
511
892
  # Find -s port in args if specified
512
893
  for i, arg in enumerate(global_opts):
513
- if arg == '-s' and i + 1 < len(global_opts):
894
+ if arg == "-s" and i + 1 < len(global_opts):
514
895
  try:
515
896
  ssh_port = int(global_opts[i + 1])
516
897
  except ValueError:
@@ -523,34 +904,29 @@ class HydraPlugin(PluginBase):
523
904
 
524
905
  if not supports_password:
525
906
  if log_path:
526
- with open(log_path, 'w') as f:
907
+ with open(log_path, "w") as f:
527
908
  f.write(f"ERROR: SSH brute-force aborted: {msg}\n")
528
909
  f.write(f"Target: {check_host}:{ssh_port}\n")
529
- f.write("\nThe SSH server does not support password authentication.\n")
530
- f.write("Password brute-forcing is not possible on key-only SSH servers.\n")
910
+ f.write(
911
+ "\nThe SSH server does not support password authentication.\n"
912
+ )
913
+ f.write(
914
+ "Password brute-forcing is not possible on key-only SSH servers.\n"
915
+ )
531
916
  return None
532
917
 
533
918
  if log_path:
534
- with open(log_path, 'a') as f:
919
+ with open(log_path, "a") as f:
535
920
  f.write(f"INFO: SSH password auth check: {msg}\n\n")
536
921
 
537
- # Enable legacy host key algorithms (ssh-rsa, ssh-dss) for compatibility
538
- # with older SSH servers while still preferring modern algorithms
539
- legacy_opts = [
540
- '-m', 'ssh-option=HostKeyAlgorithms=+ssh-rsa,ssh-dss',
541
- '-m', 'ssh-option=PubkeyAcceptedAlgorithms=+ssh-rsa,ssh-dss'
542
- ]
543
- global_opts = legacy_opts + global_opts
544
- if log_path:
545
- with open(log_path, 'a') as f:
546
- f.write("INFO: Added legacy SSH algorithm support for compatibility\n\n")
547
-
548
922
  if len(validated_targets) > 1:
549
923
  # Create temp file with targets
550
- fd, temp_target_file = tempfile.mkstemp(suffix='.txt', prefix='hydra_targets_')
924
+ fd, temp_target_file = tempfile.mkstemp(
925
+ suffix=".txt", prefix="hydra_targets_"
926
+ )
551
927
  try:
552
- with os.fdopen(fd, 'w') as f:
553
- f.write('\n'.join(validated_targets))
928
+ with os.fdopen(fd, "w") as f:
929
+ f.write("\n".join(validated_targets))
554
930
 
555
931
  # Build: hydra [global_opts] -M target_file service [service_opts]
556
932
  cmd = ["hydra"] + global_opts + ["-M", temp_target_file]
@@ -559,7 +935,7 @@ class HydraPlugin(PluginBase):
559
935
  except Exception as e:
560
936
  os.unlink(temp_target_file)
561
937
  if log_path:
562
- with open(log_path, 'w') as f:
938
+ with open(log_path, "w") as f:
563
939
  f.write(f"ERROR: Failed to create target file: {e}\n")
564
940
  return None
565
941
  else:
@@ -568,12 +944,11 @@ class HydraPlugin(PluginBase):
568
944
  if service_type:
569
945
  cmd += [service_type] + service_opts
570
946
 
571
- return {
572
- 'cmd': cmd,
573
- 'timeout': 3600 # 1 hour timeout
574
- }
947
+ return {"cmd": cmd, "timeout": 3600} # 1 hour timeout
575
948
 
576
- def run(self, target: str, args: List[str] = None, label: str = "", log_path: str = None) -> int:
949
+ def run(
950
+ self, target: str, args: List[str] = None, label: str = "", log_path: str = None
951
+ ) -> int:
577
952
  """
578
953
  Execute Hydra and write output to log_path.
579
954
  """
@@ -584,12 +959,12 @@ class HydraPlugin(PluginBase):
584
959
  args = []
585
960
 
586
961
  # Handle host:port format (e.g., 10.0.0.73:80)
587
- if not target.startswith(('http://', 'https://')):
962
+ if not target.startswith(("http://", "https://")):
588
963
  host, port = self._parse_host_port(target)
589
964
  if port is not None:
590
965
  target = host
591
- if '-s' not in args:
592
- args = ['-s', str(port)] + args
966
+ if "-s" not in args:
967
+ args = ["-s", str(port)] + args
593
968
 
594
969
  # Handle multiple targets (space-separated)
595
970
  targets = target.split()
@@ -602,7 +977,7 @@ class HydraPlugin(PluginBase):
602
977
  validated_targets.append(validate_target(h))
603
978
  except ValidationError as e:
604
979
  if log_path:
605
- with open(log_path, 'w') as f:
980
+ with open(log_path, "w") as f:
606
981
  f.write(f"ERROR: Invalid target: {e}\n")
607
982
  return 1
608
983
  raise ValueError(f"Invalid target: {e}")
@@ -610,24 +985,18 @@ class HydraPlugin(PluginBase):
610
985
  # If multiple targets, create a temporary file and use -M flag
611
986
  import tempfile
612
987
  import os
613
-
614
- # Check if SSH service and add legacy algorithm support
615
- if 'ssh' in args:
616
- legacy_opts = [
617
- '-m', 'ssh-option=HostKeyAlgorithms=+ssh-rsa,ssh-dss',
618
- '-m', 'ssh-option=PubkeyAcceptedAlgorithms=+ssh-rsa,ssh-dss'
619
- ]
620
- args = legacy_opts + args
621
988
 
622
989
  if len(validated_targets) > 1:
623
990
  # Create temp file with targets
624
- fd, temp_target_file = tempfile.mkstemp(suffix='.txt', prefix='hydra_targets_')
991
+ fd, temp_target_file = tempfile.mkstemp(
992
+ suffix=".txt", prefix="hydra_targets_"
993
+ )
625
994
  try:
626
- with os.fdopen(fd, 'w') as f:
627
- f.write('\n'.join(validated_targets))
995
+ with os.fdopen(fd, "w") as f:
996
+ f.write("\n".join(validated_targets))
628
997
 
629
998
  cmd = ["hydra", "-M", temp_target_file] + args
630
- target_display = ' '.join(validated_targets)
999
+ target_display = " ".join(validated_targets)
631
1000
  except Exception as e:
632
1001
  os.unlink(temp_target_file)
633
1002
  raise e
@@ -638,21 +1007,18 @@ class HydraPlugin(PluginBase):
638
1007
  temp_target_file = None
639
1008
 
640
1009
  if log_path:
641
- with open(log_path, 'w') as f:
1010
+ with open(log_path, "w") as f:
642
1011
  f.write(f"# Hydra attack on {target_display}\n")
643
1012
  f.write(f"# Command: {' '.join(cmd)}\n")
644
1013
  f.write(f"# Started: {time.strftime('%Y-%m-%d %H:%M:%S')}\n\n")
645
1014
 
646
1015
  try:
647
1016
  result = subprocess.run(
648
- cmd,
649
- capture_output=True,
650
- text=True,
651
- timeout=3600 # 1 hour
1017
+ cmd, capture_output=True, text=True, timeout=3600 # 1 hour
652
1018
  )
653
1019
 
654
1020
  if log_path:
655
- with open(log_path, 'a') as f:
1021
+ with open(log_path, "a") as f:
656
1022
  f.write(result.stdout)
657
1023
  if result.stderr:
658
1024
  f.write(f"\n\n# Errors:\n{result.stderr}\n")
@@ -661,12 +1027,12 @@ class HydraPlugin(PluginBase):
661
1027
 
662
1028
  except subprocess.TimeoutExpired:
663
1029
  if log_path:
664
- with open(log_path, 'a') as f:
1030
+ with open(log_path, "a") as f:
665
1031
  f.write("\n\n# ERROR: Command timed out after 1 hour\n")
666
1032
  return 124
667
1033
  except Exception as e:
668
1034
  if log_path:
669
- with open(log_path, 'a') as f:
1035
+ with open(log_path, "a") as f:
670
1036
  f.write(f"\n\n# ERROR: {str(e)}\n")
671
1037
  return 1
672
1038
  finally: