souleyez 2.43.26__py3-none-any.whl → 2.43.34__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- souleyez/__init__.py +1 -2
- souleyez/ai/__init__.py +21 -15
- souleyez/ai/action_mapper.py +249 -150
- souleyez/ai/chain_advisor.py +116 -100
- souleyez/ai/claude_provider.py +29 -28
- souleyez/ai/context_builder.py +80 -62
- souleyez/ai/executor.py +158 -117
- souleyez/ai/feedback_handler.py +136 -121
- souleyez/ai/llm_factory.py +27 -20
- souleyez/ai/llm_provider.py +4 -2
- souleyez/ai/ollama_provider.py +6 -9
- souleyez/ai/ollama_service.py +44 -37
- souleyez/ai/path_scorer.py +91 -76
- souleyez/ai/recommender.py +176 -144
- souleyez/ai/report_context.py +74 -73
- souleyez/ai/report_service.py +84 -66
- souleyez/ai/result_parser.py +222 -229
- souleyez/ai/safety.py +67 -44
- souleyez/auth/__init__.py +23 -22
- souleyez/auth/audit.py +36 -26
- souleyez/auth/engagement_access.py +65 -48
- souleyez/auth/permissions.py +14 -3
- souleyez/auth/session_manager.py +54 -37
- souleyez/auth/user_manager.py +109 -64
- souleyez/commands/audit.py +40 -43
- souleyez/commands/auth.py +35 -15
- souleyez/commands/deliverables.py +55 -50
- souleyez/commands/engagement.py +47 -28
- souleyez/commands/license.py +32 -23
- souleyez/commands/screenshots.py +36 -32
- souleyez/commands/user.py +82 -36
- souleyez/config.py +52 -44
- souleyez/core/credential_tester.py +87 -81
- souleyez/core/cve_mappings.py +179 -192
- souleyez/core/cve_matcher.py +162 -148
- souleyez/core/msf_auto_mapper.py +100 -83
- souleyez/core/msf_chain_engine.py +294 -256
- souleyez/core/msf_database.py +153 -70
- souleyez/core/msf_integration.py +679 -673
- souleyez/core/msf_rpc_client.py +40 -42
- souleyez/core/msf_rpc_manager.py +77 -79
- souleyez/core/msf_sync_manager.py +241 -181
- souleyez/core/network_utils.py +22 -15
- souleyez/core/parser_handler.py +34 -25
- souleyez/core/pending_chains.py +114 -63
- souleyez/core/templates.py +158 -107
- souleyez/core/tool_chaining.py +9526 -2879
- souleyez/core/version_utils.py +79 -94
- souleyez/core/vuln_correlation.py +136 -89
- souleyez/core/web_utils.py +33 -32
- souleyez/data/wordlists/ad_users.txt +378 -0
- souleyez/data/wordlists/api_endpoints_large.txt +769 -0
- souleyez/data/wordlists/home_dir_sensitive.txt +39 -0
- souleyez/data/wordlists/lfi_payloads.txt +82 -0
- souleyez/data/wordlists/passwords_brute.txt +1548 -0
- souleyez/data/wordlists/passwords_crack.txt +2479 -0
- souleyez/data/wordlists/passwords_spray.txt +386 -0
- souleyez/data/wordlists/subdomains_large.txt +5057 -0
- souleyez/data/wordlists/usernames_common.txt +694 -0
- souleyez/data/wordlists/web_dirs_large.txt +4769 -0
- souleyez/detection/__init__.py +1 -1
- souleyez/detection/attack_signatures.py +12 -17
- souleyez/detection/mitre_mappings.py +61 -55
- souleyez/detection/validator.py +97 -86
- souleyez/devtools.py +23 -10
- souleyez/docs/README.md +4 -4
- souleyez/docs/api-reference/cli-commands.md +2 -2
- souleyez/docs/developer-guide/adding-new-tools.md +562 -0
- souleyez/docs/user-guide/auto-chaining.md +30 -8
- souleyez/docs/user-guide/getting-started.md +1 -1
- souleyez/docs/user-guide/installation.md +26 -3
- souleyez/docs/user-guide/metasploit-integration.md +2 -2
- souleyez/docs/user-guide/rbac.md +1 -1
- souleyez/docs/user-guide/scope-management.md +1 -1
- souleyez/docs/user-guide/siem-integration.md +1 -1
- souleyez/docs/user-guide/tools-reference.md +1 -8
- souleyez/docs/user-guide/worker-management.md +1 -1
- souleyez/engine/background.py +1239 -535
- souleyez/engine/base.py +4 -1
- souleyez/engine/job_status.py +17 -49
- souleyez/engine/log_sanitizer.py +103 -77
- souleyez/engine/manager.py +38 -7
- souleyez/engine/result_handler.py +2200 -1550
- souleyez/engine/worker_manager.py +50 -41
- souleyez/export/evidence_bundle.py +72 -62
- souleyez/feature_flags/features.py +16 -20
- souleyez/feature_flags.py +5 -9
- souleyez/handlers/__init__.py +11 -0
- souleyez/handlers/base.py +188 -0
- souleyez/handlers/bash_handler.py +277 -0
- souleyez/handlers/bloodhound_handler.py +243 -0
- souleyez/handlers/certipy_handler.py +311 -0
- souleyez/handlers/crackmapexec_handler.py +486 -0
- souleyez/handlers/dnsrecon_handler.py +344 -0
- souleyez/handlers/enum4linux_handler.py +400 -0
- souleyez/handlers/evil_winrm_handler.py +493 -0
- souleyez/handlers/ffuf_handler.py +815 -0
- souleyez/handlers/gobuster_handler.py +1114 -0
- souleyez/handlers/gpp_extract_handler.py +334 -0
- souleyez/handlers/hashcat_handler.py +444 -0
- souleyez/handlers/hydra_handler.py +563 -0
- souleyez/handlers/impacket_getuserspns_handler.py +343 -0
- souleyez/handlers/impacket_psexec_handler.py +222 -0
- souleyez/handlers/impacket_secretsdump_handler.py +426 -0
- souleyez/handlers/john_handler.py +286 -0
- souleyez/handlers/katana_handler.py +425 -0
- souleyez/handlers/kerbrute_handler.py +298 -0
- souleyez/handlers/ldapsearch_handler.py +636 -0
- souleyez/handlers/lfi_extract_handler.py +464 -0
- souleyez/handlers/msf_auxiliary_handler.py +408 -0
- souleyez/handlers/msf_exploit_handler.py +380 -0
- souleyez/handlers/nikto_handler.py +413 -0
- souleyez/handlers/nmap_handler.py +821 -0
- souleyez/handlers/nuclei_handler.py +359 -0
- souleyez/handlers/nxc_handler.py +371 -0
- souleyez/handlers/rdp_sec_check_handler.py +353 -0
- souleyez/handlers/registry.py +292 -0
- souleyez/handlers/responder_handler.py +232 -0
- souleyez/handlers/service_explorer_handler.py +434 -0
- souleyez/handlers/smbclient_handler.py +344 -0
- souleyez/handlers/smbmap_handler.py +510 -0
- souleyez/handlers/smbpasswd_handler.py +296 -0
- souleyez/handlers/sqlmap_handler.py +1116 -0
- souleyez/handlers/theharvester_handler.py +601 -0
- souleyez/handlers/web_login_test_handler.py +327 -0
- souleyez/handlers/whois_handler.py +277 -0
- souleyez/handlers/wpscan_handler.py +554 -0
- souleyez/history.py +32 -16
- souleyez/importers/msf_importer.py +106 -75
- souleyez/importers/smart_importer.py +208 -147
- souleyez/integrations/siem/__init__.py +10 -10
- souleyez/integrations/siem/base.py +17 -18
- souleyez/integrations/siem/elastic.py +108 -122
- souleyez/integrations/siem/factory.py +207 -80
- souleyez/integrations/siem/googlesecops.py +146 -154
- souleyez/integrations/siem/rule_mappings/__init__.py +1 -1
- souleyez/integrations/siem/rule_mappings/wazuh_rules.py +8 -5
- souleyez/integrations/siem/sentinel.py +107 -109
- souleyez/integrations/siem/splunk.py +246 -212
- souleyez/integrations/siem/wazuh.py +65 -71
- souleyez/integrations/wazuh/__init__.py +5 -5
- souleyez/integrations/wazuh/client.py +70 -93
- souleyez/integrations/wazuh/config.py +85 -57
- souleyez/integrations/wazuh/host_mapper.py +28 -36
- souleyez/integrations/wazuh/sync.py +78 -68
- souleyez/intelligence/__init__.py +4 -5
- souleyez/intelligence/correlation_analyzer.py +309 -295
- souleyez/intelligence/exploit_knowledge.py +661 -623
- souleyez/intelligence/exploit_suggestions.py +159 -139
- souleyez/intelligence/gap_analyzer.py +132 -97
- souleyez/intelligence/gap_detector.py +251 -214
- souleyez/intelligence/sensitive_tables.py +266 -129
- souleyez/intelligence/service_parser.py +137 -123
- souleyez/intelligence/surface_analyzer.py +407 -268
- souleyez/intelligence/target_parser.py +159 -162
- souleyez/licensing/__init__.py +6 -6
- souleyez/licensing/validator.py +17 -19
- souleyez/log_config.py +79 -54
- souleyez/main.py +1505 -687
- souleyez/migrations/fix_job_counter.py +16 -14
- souleyez/parsers/bloodhound_parser.py +41 -39
- souleyez/parsers/crackmapexec_parser.py +178 -111
- souleyez/parsers/dalfox_parser.py +72 -77
- souleyez/parsers/dnsrecon_parser.py +103 -91
- souleyez/parsers/enum4linux_parser.py +183 -153
- souleyez/parsers/ffuf_parser.py +29 -25
- souleyez/parsers/gobuster_parser.py +301 -41
- souleyez/parsers/hashcat_parser.py +324 -79
- souleyez/parsers/http_fingerprint_parser.py +350 -103
- souleyez/parsers/hydra_parser.py +131 -111
- souleyez/parsers/impacket_parser.py +231 -178
- souleyez/parsers/john_parser.py +98 -86
- souleyez/parsers/katana_parser.py +316 -0
- souleyez/parsers/msf_parser.py +943 -498
- souleyez/parsers/nikto_parser.py +346 -65
- souleyez/parsers/nmap_parser.py +262 -174
- souleyez/parsers/nuclei_parser.py +40 -44
- souleyez/parsers/responder_parser.py +26 -26
- souleyez/parsers/searchsploit_parser.py +74 -74
- souleyez/parsers/service_explorer_parser.py +279 -0
- souleyez/parsers/smbmap_parser.py +180 -124
- souleyez/parsers/sqlmap_parser.py +434 -308
- souleyez/parsers/theharvester_parser.py +75 -57
- souleyez/parsers/whois_parser.py +135 -94
- souleyez/parsers/wpscan_parser.py +278 -190
- souleyez/plugins/afp.py +44 -36
- souleyez/plugins/afp_brute.py +114 -46
- souleyez/plugins/ard.py +48 -37
- souleyez/plugins/bloodhound.py +95 -61
- souleyez/plugins/certipy.py +303 -0
- souleyez/plugins/crackmapexec.py +186 -85
- souleyez/plugins/dalfox.py +120 -59
- souleyez/plugins/dns_hijack.py +146 -41
- souleyez/plugins/dnsrecon.py +97 -61
- souleyez/plugins/enum4linux.py +91 -66
- souleyez/plugins/evil_winrm.py +291 -0
- souleyez/plugins/ffuf.py +166 -90
- souleyez/plugins/firmware_extract.py +133 -29
- souleyez/plugins/gobuster.py +387 -190
- souleyez/plugins/gpp_extract.py +393 -0
- souleyez/plugins/hashcat.py +100 -73
- souleyez/plugins/http_fingerprint.py +854 -267
- souleyez/plugins/hydra.py +566 -200
- souleyez/plugins/impacket_getnpusers.py +117 -69
- souleyez/plugins/impacket_psexec.py +84 -64
- souleyez/plugins/impacket_secretsdump.py +103 -69
- souleyez/plugins/impacket_smbclient.py +89 -75
- souleyez/plugins/john.py +86 -69
- souleyez/plugins/katana.py +313 -0
- souleyez/plugins/kerbrute.py +237 -0
- souleyez/plugins/lfi_extract.py +541 -0
- souleyez/plugins/macos_ssh.py +117 -48
- souleyez/plugins/mdns.py +35 -30
- souleyez/plugins/msf_auxiliary.py +253 -130
- souleyez/plugins/msf_exploit.py +239 -161
- souleyez/plugins/nikto.py +134 -78
- souleyez/plugins/nmap.py +275 -91
- souleyez/plugins/nuclei.py +180 -89
- souleyez/plugins/nxc.py +285 -0
- souleyez/plugins/plugin_base.py +35 -36
- souleyez/plugins/plugin_template.py +13 -5
- souleyez/plugins/rdp_sec_check.py +130 -0
- souleyez/plugins/responder.py +112 -71
- souleyez/plugins/router_http_brute.py +76 -65
- souleyez/plugins/router_ssh_brute.py +118 -41
- souleyez/plugins/router_telnet_brute.py +124 -42
- souleyez/plugins/routersploit.py +91 -59
- souleyez/plugins/routersploit_exploit.py +77 -55
- souleyez/plugins/searchsploit.py +91 -77
- souleyez/plugins/service_explorer.py +1160 -0
- souleyez/plugins/smbmap.py +122 -72
- souleyez/plugins/smbpasswd.py +215 -0
- souleyez/plugins/sqlmap.py +301 -113
- souleyez/plugins/theharvester.py +127 -75
- souleyez/plugins/tr069.py +79 -57
- souleyez/plugins/upnp.py +65 -47
- souleyez/plugins/upnp_abuse.py +73 -55
- souleyez/plugins/vnc_access.py +129 -42
- souleyez/plugins/vnc_brute.py +109 -38
- souleyez/plugins/web_login_test.py +417 -0
- souleyez/plugins/whois.py +77 -58
- souleyez/plugins/wpscan.py +173 -69
- souleyez/reporting/__init__.py +2 -1
- souleyez/reporting/attack_chain.py +411 -346
- souleyez/reporting/charts.py +436 -501
- souleyez/reporting/compliance_mappings.py +334 -201
- souleyez/reporting/detection_report.py +126 -125
- souleyez/reporting/formatters.py +828 -591
- souleyez/reporting/generator.py +386 -302
- souleyez/reporting/metrics.py +72 -75
- souleyez/scanner.py +35 -29
- souleyez/security/__init__.py +37 -11
- souleyez/security/scope_validator.py +175 -106
- souleyez/security/validation.py +223 -149
- souleyez/security.py +22 -6
- souleyez/storage/credentials.py +247 -186
- souleyez/storage/crypto.py +296 -129
- souleyez/storage/database.py +73 -50
- souleyez/storage/db.py +58 -36
- souleyez/storage/deliverable_evidence.py +177 -128
- souleyez/storage/deliverable_exporter.py +282 -246
- souleyez/storage/deliverable_templates.py +134 -116
- souleyez/storage/deliverables.py +135 -130
- souleyez/storage/engagements.py +109 -56
- souleyez/storage/evidence.py +181 -152
- souleyez/storage/execution_log.py +31 -17
- souleyez/storage/exploit_attempts.py +93 -57
- souleyez/storage/exploits.py +67 -36
- souleyez/storage/findings.py +48 -61
- souleyez/storage/hosts.py +176 -144
- souleyez/storage/migrate_to_engagements.py +43 -19
- souleyez/storage/migrations/_001_add_credential_enhancements.py +22 -12
- souleyez/storage/migrations/_002_add_status_tracking.py +10 -7
- souleyez/storage/migrations/_003_add_execution_log.py +14 -8
- souleyez/storage/migrations/_005_screenshots.py +13 -5
- souleyez/storage/migrations/_006_deliverables.py +13 -5
- souleyez/storage/migrations/_007_deliverable_templates.py +12 -7
- souleyez/storage/migrations/_008_add_nuclei_table.py +10 -4
- souleyez/storage/migrations/_010_evidence_linking.py +17 -10
- souleyez/storage/migrations/_011_timeline_tracking.py +20 -13
- souleyez/storage/migrations/_012_team_collaboration.py +34 -21
- souleyez/storage/migrations/_013_add_host_tags.py +12 -6
- souleyez/storage/migrations/_014_exploit_attempts.py +22 -10
- souleyez/storage/migrations/_015_add_mac_os_fields.py +15 -7
- souleyez/storage/migrations/_016_add_domain_field.py +10 -4
- souleyez/storage/migrations/_017_msf_sessions.py +16 -8
- souleyez/storage/migrations/_018_add_osint_target.py +10 -6
- souleyez/storage/migrations/_019_add_engagement_type.py +10 -6
- souleyez/storage/migrations/_020_add_rbac.py +36 -15
- souleyez/storage/migrations/_021_wazuh_integration.py +20 -8
- souleyez/storage/migrations/_022_wazuh_indexer_columns.py +6 -4
- souleyez/storage/migrations/_023_fix_detection_results_fk.py +16 -6
- souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +26 -10
- souleyez/storage/migrations/_025_multi_siem_support.py +3 -5
- souleyez/storage/migrations/_026_add_engagement_scope.py +31 -12
- souleyez/storage/migrations/_027_multi_siem_persistence.py +32 -15
- souleyez/storage/migrations/__init__.py +26 -26
- souleyez/storage/migrations/migration_manager.py +19 -19
- souleyez/storage/msf_sessions.py +100 -65
- souleyez/storage/osint.py +17 -24
- souleyez/storage/recommendation_engine.py +269 -235
- souleyez/storage/screenshots.py +33 -32
- souleyez/storage/smb_shares.py +136 -92
- souleyez/storage/sqlmap_data.py +183 -128
- souleyez/storage/team_collaboration.py +135 -141
- souleyez/storage/timeline_tracker.py +122 -94
- souleyez/storage/wazuh_vulns.py +64 -66
- souleyez/storage/web_paths.py +33 -37
- souleyez/testing/credential_tester.py +221 -205
- souleyez/ui/__init__.py +1 -1
- souleyez/ui/ai_quotes.py +12 -12
- souleyez/ui/attack_surface.py +2439 -1516
- souleyez/ui/chain_rules_view.py +914 -382
- souleyez/ui/correlation_view.py +312 -230
- souleyez/ui/dashboard.py +2382 -1130
- souleyez/ui/deliverables_view.py +148 -62
- souleyez/ui/design_system.py +13 -13
- souleyez/ui/errors.py +49 -49
- souleyez/ui/evidence_linking_view.py +284 -179
- souleyez/ui/evidence_vault.py +393 -285
- souleyez/ui/exploit_suggestions_view.py +555 -349
- souleyez/ui/export_view.py +100 -66
- souleyez/ui/gap_analysis_view.py +315 -171
- souleyez/ui/help_system.py +105 -97
- souleyez/ui/intelligence_view.py +436 -293
- souleyez/ui/interactive.py +23434 -10286
- souleyez/ui/interactive_selector.py +75 -68
- souleyez/ui/log_formatter.py +47 -39
- souleyez/ui/menu_components.py +22 -13
- souleyez/ui/msf_auxiliary_menu.py +184 -133
- souleyez/ui/pending_chains_view.py +336 -172
- souleyez/ui/progress_indicators.py +5 -3
- souleyez/ui/recommendations_view.py +195 -137
- souleyez/ui/rule_builder.py +343 -225
- souleyez/ui/setup_wizard.py +678 -284
- souleyez/ui/shortcuts.py +217 -165
- souleyez/ui/splunk_gap_analysis_view.py +452 -270
- souleyez/ui/splunk_vulns_view.py +139 -86
- souleyez/ui/team_dashboard.py +498 -335
- souleyez/ui/template_selector.py +196 -105
- souleyez/ui/terminal.py +6 -6
- souleyez/ui/timeline_view.py +198 -127
- souleyez/ui/tool_setup.py +264 -164
- souleyez/ui/tutorial.py +202 -72
- souleyez/ui/tutorial_state.py +40 -40
- souleyez/ui/wazuh_vulns_view.py +235 -141
- souleyez/ui/wordlist_browser.py +260 -107
- souleyez/ui.py +464 -312
- souleyez/utils/tool_checker.py +427 -367
- souleyez/utils.py +33 -29
- souleyez/wordlists.py +134 -167
- {souleyez-2.43.26.dist-info → souleyez-2.43.34.dist-info}/METADATA +1 -1
- souleyez-2.43.34.dist-info/RECORD +443 -0
- {souleyez-2.43.26.dist-info → souleyez-2.43.34.dist-info}/WHEEL +1 -1
- souleyez-2.43.26.dist-info/RECORD +0 -379
- {souleyez-2.43.26.dist-info → souleyez-2.43.34.dist-info}/entry_points.txt +0 -0
- {souleyez-2.43.26.dist-info → souleyez-2.43.34.dist-info}/licenses/LICENSE +0 -0
- {souleyez-2.43.26.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":
|
|
30
|
+
"usage": 'souleyez jobs enqueue hydra <target> --args "<service> -l <user> -p <pass>"',
|
|
31
31
|
"examples": [
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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": [
|
|
53
|
-
|
|
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": [
|
|
58
|
-
|
|
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": [
|
|
63
|
-
|
|
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": [
|
|
70
|
-
|
|
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": [
|
|
75
|
-
|
|
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": [
|
|
80
|
-
|
|
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": [
|
|
87
|
-
|
|
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": [
|
|
92
|
-
|
|
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": [
|
|
99
|
-
|
|
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": [
|
|
106
|
-
|
|
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": [
|
|
111
|
-
|
|
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": [
|
|
116
|
-
|
|
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": [
|
|
121
|
-
|
|
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
|
-
{
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
{
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
{
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
{
|
|
145
|
-
|
|
146
|
-
"
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
"
|
|
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
|
-
{
|
|
158
|
-
|
|
159
|
-
"
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
"
|
|
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
|
-
(
|
|
171
|
-
"
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
"
|
|
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
|
-
[
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
|
526
|
+
if "permission denied" in stderr:
|
|
222
527
|
# Password auth is available but wrong password
|
|
223
528
|
return True, "Password authentication available"
|
|
224
|
-
elif
|
|
529
|
+
elif "no more authentication methods" in stderr:
|
|
225
530
|
return False, "Password authentication disabled (key-only)"
|
|
226
|
-
elif
|
|
531
|
+
elif "connection refused" in stderr:
|
|
227
532
|
return False, "SSH connection refused (service not running?)"
|
|
228
|
-
elif
|
|
533
|
+
elif "connection timed out" in stderr or "timed out" in stderr:
|
|
229
534
|
return False, "SSH connection timed out"
|
|
230
|
-
elif
|
|
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
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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 = {
|
|
274
|
-
|
|
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
|
-
#
|
|
622
|
+
# Strip quotes for checking
|
|
300
623
|
cleaned_check = form_string.lstrip("'\"")
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
#
|
|
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 ==
|
|
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
|
|
683
|
+
path += f"?{parsed.query}"
|
|
340
684
|
|
|
341
|
-
is_default = (
|
|
342
|
-
|
|
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
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
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
|
|
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
|
|
715
|
+
if ":" in target and not target.count(":") == 1:
|
|
373
716
|
# Multiple colons = IPv6, strip brackets if present
|
|
374
|
-
return target.strip(
|
|
717
|
+
return target.strip("[]"), None
|
|
375
718
|
|
|
376
719
|
# Check for host:port format (single colon)
|
|
377
|
-
if
|
|
378
|
-
parts = target.rsplit(
|
|
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(
|
|
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,
|
|
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((
|
|
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
|
|
411
|
-
args = [
|
|
755
|
+
if "-s" not in args:
|
|
756
|
+
args = ["-s", str(port)] + args
|
|
412
757
|
if log_path:
|
|
413
|
-
with open(log_path,
|
|
414
|
-
f.write(
|
|
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((
|
|
766
|
+
if target.startswith(("http://", "https://")):
|
|
420
767
|
# Reject if multiple targets
|
|
421
|
-
if
|
|
768
|
+
if " " in target:
|
|
422
769
|
if log_path:
|
|
423
|
-
with open(log_path,
|
|
424
|
-
f.write(
|
|
425
|
-
|
|
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,
|
|
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[
|
|
789
|
+
target = parsed["host"]
|
|
439
790
|
|
|
440
791
|
# Inject -s PORT if non-default and -s not already in args
|
|
441
|
-
if not parsed[
|
|
442
|
-
if
|
|
443
|
-
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,
|
|
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(
|
|
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[
|
|
453
|
-
args = self._inject_path_into_args(args, parsed[
|
|
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,
|
|
456
|
-
f.write(
|
|
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((
|
|
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,
|
|
462
|
-
f.write(
|
|
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(
|
|
465
|
-
|
|
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,
|
|
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 [
|
|
496
|
-
|
|
497
|
-
|
|
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 ==
|
|
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 ==
|
|
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,
|
|
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(
|
|
530
|
-
|
|
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,
|
|
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(
|
|
924
|
+
fd, temp_target_file = tempfile.mkstemp(
|
|
925
|
+
suffix=".txt", prefix="hydra_targets_"
|
|
926
|
+
)
|
|
551
927
|
try:
|
|
552
|
-
with os.fdopen(fd,
|
|
553
|
-
f.write(
|
|
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,
|
|
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(
|
|
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((
|
|
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
|
|
592
|
-
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,
|
|
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(
|
|
991
|
+
fd, temp_target_file = tempfile.mkstemp(
|
|
992
|
+
suffix=".txt", prefix="hydra_targets_"
|
|
993
|
+
)
|
|
625
994
|
try:
|
|
626
|
-
with os.fdopen(fd,
|
|
627
|
-
f.write(
|
|
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 =
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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:
|