souleyez 2.43.29__py3-none-any.whl → 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +9564 -2881
- 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 +564 -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 +409 -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 +417 -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 +913 -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 +219 -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 +237 -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 +23034 -10679
- 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.29.dist-info → souleyez-3.0.0.dist-info}/METADATA +2 -2
- souleyez-3.0.0.dist-info/RECORD +443 -0
- {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/WHEEL +1 -1
- souleyez-2.43.29.dist-info/RECORD +0 -379
- {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/entry_points.txt +0 -0
- {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/licenses/LICENSE +0 -0
- {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/top_level.txt +0 -0
souleyez/parsers/john_parser.py
CHANGED
|
@@ -12,208 +12,220 @@ from typing import Dict, List, Tuple
|
|
|
12
12
|
def parse_john_output(output: str, hash_file: str = None) -> Dict:
|
|
13
13
|
"""
|
|
14
14
|
Parse John the Ripper output and extract cracked passwords.
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
Args:
|
|
17
17
|
output: John's stdout/stderr output
|
|
18
18
|
hash_file: Path to the hash file (to run --show if needed)
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
Returns:
|
|
21
21
|
Dictionary with cracked credentials
|
|
22
22
|
"""
|
|
23
23
|
results = {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
"cracked": [],
|
|
25
|
+
"total_loaded": 0,
|
|
26
|
+
"total_cracked": 0,
|
|
27
|
+
"session_status": "unknown",
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
# Parse loaded hashes
|
|
31
|
-
loaded_match = re.search(r
|
|
31
|
+
loaded_match = re.search(r"Loaded (\d+) password hash", output)
|
|
32
32
|
if loaded_match:
|
|
33
|
-
results[
|
|
34
|
-
|
|
33
|
+
results["total_loaded"] = int(loaded_match.group(1))
|
|
34
|
+
|
|
35
35
|
# Parse cracked passwords from live output with multiple format support
|
|
36
36
|
# Format 1: "password (username)"
|
|
37
37
|
# Format 2: "password (username)"
|
|
38
38
|
# Format 3: "username:password"
|
|
39
39
|
# Format 4: "password (username) [hash_type]"
|
|
40
|
-
for line in output.split(
|
|
40
|
+
for line in output.split("\n"):
|
|
41
41
|
line = line.strip()
|
|
42
|
-
if not line or line.startswith(
|
|
42
|
+
if not line or line.startswith("#") or line.startswith("["):
|
|
43
43
|
continue
|
|
44
44
|
|
|
45
45
|
# Try format: password (username) with optional hash type
|
|
46
|
-
match = re.match(r
|
|
46
|
+
match = re.match(r"^(\S+)\s+\(([^)]+)\)(?:\s+\[.+\])?\s*$", line)
|
|
47
47
|
if match:
|
|
48
48
|
password = match.group(1)
|
|
49
49
|
username = match.group(2)
|
|
50
|
-
results[
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
'source': 'john_live'
|
|
54
|
-
})
|
|
50
|
+
results["cracked"].append(
|
|
51
|
+
{"username": username, "password": password, "source": "john_live"}
|
|
52
|
+
)
|
|
55
53
|
continue
|
|
56
54
|
|
|
57
55
|
# Try format: username:password (from --show output)
|
|
58
|
-
if
|
|
59
|
-
parts = line.split(
|
|
56
|
+
if ":" in line and not line.startswith("Loaded"):
|
|
57
|
+
parts = line.split(":")
|
|
60
58
|
if len(parts) >= 2 and len(parts[0]) > 0 and len(parts[-1]) > 0:
|
|
61
59
|
# Skip if it looks like a hash (32+ hex chars)
|
|
62
|
-
if not re.match(r
|
|
60
|
+
if not re.match(r"^[0-9a-fA-F]{32,}$", parts[-1]):
|
|
63
61
|
username = parts[0]
|
|
64
62
|
password = parts[-1]
|
|
65
|
-
results[
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
results["cracked"].append(
|
|
64
|
+
{
|
|
65
|
+
"username": username,
|
|
66
|
+
"password": password,
|
|
67
|
+
"source": "john_live",
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
70
|
|
|
71
71
|
# Check session status with multiple format support
|
|
72
|
-
if any(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
if any(
|
|
73
|
+
x in output
|
|
74
|
+
for x in ["Session completed", "session completed", "Proceeding with next"]
|
|
75
|
+
):
|
|
76
|
+
results["session_status"] = "completed"
|
|
77
|
+
elif any(
|
|
78
|
+
x in output for x in ["Session aborted", "session aborted", "Interrupted"]
|
|
79
|
+
):
|
|
80
|
+
results["session_status"] = "aborted"
|
|
81
|
+
elif "No password hashes left to crack" in output:
|
|
82
|
+
results["session_status"] = "completed"
|
|
78
83
|
|
|
79
84
|
# Parse summary line with multiple formats
|
|
80
85
|
# Format 1: "2g 0:00:00:01 DONE..."
|
|
81
86
|
# Format 2: "2g 0:00:00:01 100% DONE..."
|
|
82
87
|
# Format 3: "Session completed, 2g"
|
|
83
88
|
summary_patterns = [
|
|
84
|
-
r
|
|
85
|
-
r
|
|
86
|
-
r
|
|
89
|
+
r"(\d+)g\s+[\d:]+\s+(?:\d+%\s+)?(DONE|Session)",
|
|
90
|
+
r"Session completed[,\s]+(\d+)g",
|
|
91
|
+
r"(\d+)\s+password hashes? cracked",
|
|
87
92
|
]
|
|
88
93
|
for pattern in summary_patterns:
|
|
89
94
|
summary_match = re.search(pattern, output, re.IGNORECASE)
|
|
90
95
|
if summary_match:
|
|
91
|
-
results[
|
|
96
|
+
results["total_cracked"] = int(summary_match.group(1))
|
|
92
97
|
break
|
|
93
|
-
|
|
98
|
+
|
|
94
99
|
# If hash_file provided, also parse john.pot or run --show
|
|
95
100
|
if hash_file and os.path.isfile(hash_file):
|
|
96
101
|
pot_results = parse_john_pot(hash_file)
|
|
97
102
|
# Merge with live results (pot is authoritative)
|
|
98
103
|
if pot_results:
|
|
99
|
-
results[
|
|
104
|
+
results["cracked"].extend(pot_results)
|
|
100
105
|
# Deduplicate by username
|
|
101
106
|
seen = set()
|
|
102
107
|
unique_creds = []
|
|
103
|
-
for cred in results[
|
|
104
|
-
if cred[
|
|
105
|
-
seen.add(cred[
|
|
108
|
+
for cred in results["cracked"]:
|
|
109
|
+
if cred["username"] not in seen:
|
|
110
|
+
seen.add(cred["username"])
|
|
106
111
|
unique_creds.append(cred)
|
|
107
|
-
results[
|
|
108
|
-
results[
|
|
109
|
-
|
|
112
|
+
results["cracked"] = unique_creds
|
|
113
|
+
results["total_cracked"] = len(unique_creds)
|
|
114
|
+
|
|
110
115
|
return results
|
|
111
116
|
|
|
112
117
|
|
|
113
118
|
def parse_john_pot(hash_file: str = None) -> List[Dict]:
|
|
114
119
|
"""
|
|
115
120
|
Parse John's potfile for cracked passwords.
|
|
116
|
-
|
|
121
|
+
|
|
117
122
|
Args:
|
|
118
123
|
hash_file: If provided, run 'john --show hashfile' to get results
|
|
119
|
-
|
|
124
|
+
|
|
120
125
|
Returns:
|
|
121
126
|
List of cracked credentials
|
|
122
127
|
"""
|
|
123
128
|
cracked = []
|
|
124
|
-
|
|
129
|
+
|
|
125
130
|
# Try running john --show on the hash file
|
|
126
131
|
if hash_file and os.path.isfile(hash_file):
|
|
127
132
|
try:
|
|
128
133
|
import subprocess
|
|
134
|
+
|
|
129
135
|
result = subprocess.run(
|
|
130
|
-
[
|
|
136
|
+
["john", "--show", hash_file],
|
|
131
137
|
capture_output=True,
|
|
132
138
|
text=True,
|
|
133
|
-
timeout=10
|
|
139
|
+
timeout=10,
|
|
134
140
|
)
|
|
135
|
-
|
|
141
|
+
|
|
136
142
|
# Parse --show output
|
|
137
143
|
# Format: "username:password" or "username:hash:password"
|
|
138
|
-
for line in result.stdout.split(
|
|
144
|
+
for line in result.stdout.split("\n"):
|
|
139
145
|
line = line.strip()
|
|
140
|
-
if
|
|
141
|
-
parts = line.split(
|
|
146
|
+
if ":" in line and not line.startswith("#"):
|
|
147
|
+
parts = line.split(":")
|
|
142
148
|
if len(parts) >= 2:
|
|
143
149
|
username = parts[0]
|
|
144
150
|
# Password is the last part
|
|
145
151
|
password = parts[-1]
|
|
146
152
|
if username and password:
|
|
147
|
-
cracked.append(
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
153
|
+
cracked.append(
|
|
154
|
+
{
|
|
155
|
+
"username": username,
|
|
156
|
+
"password": password,
|
|
157
|
+
"source": "john_pot",
|
|
158
|
+
}
|
|
159
|
+
)
|
|
152
160
|
except Exception:
|
|
153
161
|
pass
|
|
154
|
-
|
|
162
|
+
|
|
155
163
|
# Also try reading ~/.john/john.pot directly
|
|
156
|
-
pot_file = os.path.expanduser(
|
|
164
|
+
pot_file = os.path.expanduser("~/.john/john.pot")
|
|
157
165
|
if os.path.isfile(pot_file):
|
|
158
166
|
try:
|
|
159
|
-
with open(pot_file,
|
|
167
|
+
with open(pot_file, "r") as f:
|
|
160
168
|
for line in f:
|
|
161
169
|
line = line.strip()
|
|
162
|
-
if
|
|
170
|
+
if ":" in line:
|
|
163
171
|
# Potfile format: hash:password
|
|
164
|
-
parts = line.split(
|
|
172
|
+
parts = line.split(":")
|
|
165
173
|
if len(parts) >= 2:
|
|
166
174
|
# Extract password (last part)
|
|
167
175
|
password = parts[-1]
|
|
168
176
|
# Try to find username from hash if available
|
|
169
177
|
# This is basic - john.pot doesn't always have username
|
|
170
|
-
cracked.append(
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
178
|
+
cracked.append(
|
|
179
|
+
{
|
|
180
|
+
"username": "unknown",
|
|
181
|
+
"password": password,
|
|
182
|
+
"hash": parts[0],
|
|
183
|
+
"source": "john_pot_file",
|
|
184
|
+
}
|
|
185
|
+
)
|
|
176
186
|
except Exception:
|
|
177
187
|
pass
|
|
178
|
-
|
|
188
|
+
|
|
179
189
|
return cracked
|
|
180
190
|
|
|
181
191
|
|
|
182
|
-
def extract_credentials_to_db(
|
|
192
|
+
def extract_credentials_to_db(
|
|
193
|
+
output: str, hash_file: str = None, engagement_id: int = None
|
|
194
|
+
):
|
|
183
195
|
"""
|
|
184
196
|
Extract cracked credentials and store them in the database.
|
|
185
|
-
|
|
197
|
+
|
|
186
198
|
Args:
|
|
187
199
|
output: John's output
|
|
188
200
|
hash_file: Path to hash file
|
|
189
201
|
engagement_id: Current engagement ID
|
|
190
202
|
"""
|
|
191
203
|
from souleyez.storage.credentials import CredentialManager
|
|
192
|
-
|
|
204
|
+
|
|
193
205
|
results = parse_john_output(output, hash_file)
|
|
194
|
-
|
|
195
|
-
if not results[
|
|
206
|
+
|
|
207
|
+
if not results["cracked"]:
|
|
196
208
|
return 0
|
|
197
|
-
|
|
209
|
+
|
|
198
210
|
cred_mgr = CredentialManager()
|
|
199
211
|
added = 0
|
|
200
|
-
|
|
201
|
-
for cred in results[
|
|
202
|
-
username = cred.get(
|
|
203
|
-
password = cred.get(
|
|
204
|
-
|
|
205
|
-
if username and password and username !=
|
|
212
|
+
|
|
213
|
+
for cred in results["cracked"]:
|
|
214
|
+
username = cred.get("username")
|
|
215
|
+
password = cred.get("password")
|
|
216
|
+
|
|
217
|
+
if username and password and username != "unknown":
|
|
206
218
|
try:
|
|
207
219
|
# Add to credentials database
|
|
208
220
|
cred_mgr.add_credential(
|
|
209
221
|
username=username,
|
|
210
222
|
password=password,
|
|
211
|
-
service=
|
|
212
|
-
host=
|
|
213
|
-
engagement_id=engagement_id
|
|
223
|
+
service="cracked", # Mark as cracked hash
|
|
224
|
+
host="", # No specific host
|
|
225
|
+
engagement_id=engagement_id,
|
|
214
226
|
)
|
|
215
227
|
added += 1
|
|
216
228
|
except Exception:
|
|
217
229
|
pass # Credential might already exist
|
|
218
|
-
|
|
230
|
+
|
|
219
231
|
return added
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
souleyez.parsers.katana_parser - Parse Katana JSONL output
|
|
4
|
+
|
|
5
|
+
Katana is a web crawler from ProjectDiscovery that discovers endpoints,
|
|
6
|
+
parameters, and JavaScript-rendered routes.
|
|
7
|
+
"""
|
|
8
|
+
import json
|
|
9
|
+
from typing import Dict, Any, List, Set
|
|
10
|
+
from urllib.parse import urlparse, parse_qs
|
|
11
|
+
|
|
12
|
+
# LFI-suspicious parameter names - these typically include files, not query databases
|
|
13
|
+
LFI_PARAM_NAMES = {
|
|
14
|
+
# Direct file inclusion params
|
|
15
|
+
"page",
|
|
16
|
+
"file",
|
|
17
|
+
"include",
|
|
18
|
+
"inc",
|
|
19
|
+
"path",
|
|
20
|
+
"filepath",
|
|
21
|
+
"filename",
|
|
22
|
+
"template",
|
|
23
|
+
"tmpl",
|
|
24
|
+
"tpl",
|
|
25
|
+
"view",
|
|
26
|
+
"layout",
|
|
27
|
+
"content",
|
|
28
|
+
# Document/resource params
|
|
29
|
+
"doc",
|
|
30
|
+
"document",
|
|
31
|
+
"pdf",
|
|
32
|
+
"folder",
|
|
33
|
+
"root",
|
|
34
|
+
"directory",
|
|
35
|
+
"dir",
|
|
36
|
+
# Language/locale (often load language files)
|
|
37
|
+
"lang",
|
|
38
|
+
"language",
|
|
39
|
+
"locale",
|
|
40
|
+
"loc",
|
|
41
|
+
# Style/theme (often load CSS/template files)
|
|
42
|
+
"style",
|
|
43
|
+
"stylesheet",
|
|
44
|
+
"css",
|
|
45
|
+
"theme",
|
|
46
|
+
"skin",
|
|
47
|
+
# Config/module loading
|
|
48
|
+
"config",
|
|
49
|
+
"conf",
|
|
50
|
+
"cfg",
|
|
51
|
+
"module",
|
|
52
|
+
"mod",
|
|
53
|
+
"plugin",
|
|
54
|
+
# Read/load operations
|
|
55
|
+
"read",
|
|
56
|
+
"load",
|
|
57
|
+
"fetch",
|
|
58
|
+
"get",
|
|
59
|
+
"show",
|
|
60
|
+
"display",
|
|
61
|
+
"render",
|
|
62
|
+
# PHP-specific
|
|
63
|
+
"pg",
|
|
64
|
+
"p",
|
|
65
|
+
"cont",
|
|
66
|
+
"controller",
|
|
67
|
+
"action",
|
|
68
|
+
"act",
|
|
69
|
+
# Common variations
|
|
70
|
+
"pagename",
|
|
71
|
+
"page_name",
|
|
72
|
+
"pageid",
|
|
73
|
+
"site",
|
|
74
|
+
"section",
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def parse_katana_output(log_path: str) -> Dict[str, Any]:
|
|
79
|
+
"""
|
|
80
|
+
Parse Katana JSONL output (one JSON object per line).
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
log_path: Path to katana output file
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Dict containing:
|
|
87
|
+
- urls: List of all discovered URLs
|
|
88
|
+
- urls_with_params: List of URLs containing query parameters
|
|
89
|
+
- lfi_candidate_urls: URLs with LFI-suspicious params (page, file, include, etc.)
|
|
90
|
+
- sqli_candidate_urls: URLs with non-LFI params (id, q, search, etc.)
|
|
91
|
+
- forms_found: List of POST endpoint URLs
|
|
92
|
+
- js_endpoints: List of JavaScript-discovered endpoints
|
|
93
|
+
- unique_parameters: Set of unique parameter names found
|
|
94
|
+
- lfi_params_found: Set of LFI parameter names found
|
|
95
|
+
- methods: Dict of method counts (GET, POST, etc.)
|
|
96
|
+
"""
|
|
97
|
+
urls: List[str] = []
|
|
98
|
+
urls_with_params: List[str] = []
|
|
99
|
+
lfi_candidate_urls: List[str] = [] # URLs with LFI-suspicious params only
|
|
100
|
+
sqli_candidate_urls: List[str] = [] # URLs with non-LFI params
|
|
101
|
+
forms_found: List[str] = []
|
|
102
|
+
js_endpoints: List[str] = []
|
|
103
|
+
unique_parameters: Set[str] = set()
|
|
104
|
+
lfi_params_found: Set[str] = set() # Track which LFI params we found
|
|
105
|
+
methods: Dict[str, int] = {"GET": 0, "POST": 0, "PUT": 0, "DELETE": 0, "OTHER": 0}
|
|
106
|
+
status_codes: Dict[int, int] = {}
|
|
107
|
+
sources: Dict[str, int] = {}
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
with open(log_path, "r", encoding="utf-8") as f:
|
|
111
|
+
for line in f:
|
|
112
|
+
# Skip comment lines and metadata
|
|
113
|
+
if line.startswith("#") or line.startswith("==="):
|
|
114
|
+
continue
|
|
115
|
+
|
|
116
|
+
line = line.strip()
|
|
117
|
+
if not line:
|
|
118
|
+
continue
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
result = json.loads(line)
|
|
122
|
+
|
|
123
|
+
# Katana output format can vary by version
|
|
124
|
+
# Try multiple field locations
|
|
125
|
+
url = None
|
|
126
|
+
method = "GET"
|
|
127
|
+
status_code = None
|
|
128
|
+
source = None
|
|
129
|
+
|
|
130
|
+
# Format 1: request.endpoint structure
|
|
131
|
+
if "request" in result:
|
|
132
|
+
req = result["request"]
|
|
133
|
+
url = req.get("endpoint") or req.get("url")
|
|
134
|
+
method = req.get("method", "GET").upper()
|
|
135
|
+
if "response" in result:
|
|
136
|
+
status_code = result["response"].get("status_code")
|
|
137
|
+
|
|
138
|
+
# Format 2: Direct fields
|
|
139
|
+
if not url:
|
|
140
|
+
url = result.get("endpoint") or result.get("url")
|
|
141
|
+
method = result.get("method", "GET").upper()
|
|
142
|
+
status_code = result.get("status_code") or result.get("status")
|
|
143
|
+
|
|
144
|
+
# Get source (how endpoint was discovered)
|
|
145
|
+
source = result.get("source") or result.get("tag") or "unknown"
|
|
146
|
+
|
|
147
|
+
if not url:
|
|
148
|
+
continue
|
|
149
|
+
|
|
150
|
+
# Track all URLs
|
|
151
|
+
if url not in urls:
|
|
152
|
+
urls.append(url)
|
|
153
|
+
|
|
154
|
+
# Track methods
|
|
155
|
+
if method in methods:
|
|
156
|
+
methods[method] += 1
|
|
157
|
+
else:
|
|
158
|
+
methods["OTHER"] += 1
|
|
159
|
+
|
|
160
|
+
# Track status codes
|
|
161
|
+
if status_code:
|
|
162
|
+
status_codes[status_code] = status_codes.get(status_code, 0) + 1
|
|
163
|
+
|
|
164
|
+
# Track sources
|
|
165
|
+
sources[source] = sources.get(source, 0) + 1
|
|
166
|
+
|
|
167
|
+
# Check for query parameters
|
|
168
|
+
parsed = urlparse(url)
|
|
169
|
+
if parsed.query:
|
|
170
|
+
if url not in urls_with_params:
|
|
171
|
+
urls_with_params.append(url)
|
|
172
|
+
|
|
173
|
+
# Extract parameter names and categorize
|
|
174
|
+
# Use keep_blank_values=True to detect params like ?q= (empty value)
|
|
175
|
+
params = parse_qs(parsed.query, keep_blank_values=True)
|
|
176
|
+
param_names = set(params.keys())
|
|
177
|
+
unique_parameters.update(param_names)
|
|
178
|
+
|
|
179
|
+
# Check if params are LFI-suspicious
|
|
180
|
+
lfi_params_in_url = param_names & LFI_PARAM_NAMES
|
|
181
|
+
non_lfi_params = param_names - LFI_PARAM_NAMES
|
|
182
|
+
|
|
183
|
+
if lfi_params_in_url:
|
|
184
|
+
lfi_params_found.update(lfi_params_in_url)
|
|
185
|
+
|
|
186
|
+
# Categorize URL based on params
|
|
187
|
+
if lfi_params_in_url and not non_lfi_params:
|
|
188
|
+
# ALL params are LFI-suspicious → LFI candidate only
|
|
189
|
+
if url not in lfi_candidate_urls:
|
|
190
|
+
lfi_candidate_urls.append(url)
|
|
191
|
+
elif non_lfi_params:
|
|
192
|
+
# Has non-LFI params → SQLi candidate
|
|
193
|
+
if url not in sqli_candidate_urls:
|
|
194
|
+
sqli_candidate_urls.append(url)
|
|
195
|
+
# Also add to LFI if it has LFI params (mixed)
|
|
196
|
+
if lfi_params_in_url and url not in lfi_candidate_urls:
|
|
197
|
+
lfi_candidate_urls.append(url)
|
|
198
|
+
|
|
199
|
+
# Track POST endpoints as forms
|
|
200
|
+
if method == "POST":
|
|
201
|
+
if url not in forms_found:
|
|
202
|
+
forms_found.append(url)
|
|
203
|
+
|
|
204
|
+
# Track JavaScript-discovered endpoints
|
|
205
|
+
if source in ("js", "script", "javascript", "jscrawl"):
|
|
206
|
+
if url not in js_endpoints:
|
|
207
|
+
js_endpoints.append(url)
|
|
208
|
+
|
|
209
|
+
except json.JSONDecodeError:
|
|
210
|
+
# Skip non-JSON lines (like metadata headers)
|
|
211
|
+
continue
|
|
212
|
+
|
|
213
|
+
except FileNotFoundError:
|
|
214
|
+
return {
|
|
215
|
+
"urls": [],
|
|
216
|
+
"urls_with_params": [],
|
|
217
|
+
"lfi_candidate_urls": [],
|
|
218
|
+
"sqli_candidate_urls": [],
|
|
219
|
+
"forms_found": [],
|
|
220
|
+
"js_endpoints": [],
|
|
221
|
+
"unique_parameters": [],
|
|
222
|
+
"lfi_params_found": [],
|
|
223
|
+
"methods": methods,
|
|
224
|
+
"status_codes": {},
|
|
225
|
+
"sources": {},
|
|
226
|
+
"error": f"Log file not found: {log_path}",
|
|
227
|
+
}
|
|
228
|
+
except Exception as e:
|
|
229
|
+
return {
|
|
230
|
+
"urls": [],
|
|
231
|
+
"urls_with_params": [],
|
|
232
|
+
"lfi_candidate_urls": [],
|
|
233
|
+
"sqli_candidate_urls": [],
|
|
234
|
+
"forms_found": [],
|
|
235
|
+
"js_endpoints": [],
|
|
236
|
+
"unique_parameters": [],
|
|
237
|
+
"lfi_params_found": [],
|
|
238
|
+
"methods": methods,
|
|
239
|
+
"status_codes": {},
|
|
240
|
+
"sources": {},
|
|
241
|
+
"error": str(e),
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
"urls": urls,
|
|
246
|
+
"urls_with_params": urls_with_params,
|
|
247
|
+
"lfi_candidate_urls": lfi_candidate_urls,
|
|
248
|
+
"sqli_candidate_urls": sqli_candidate_urls,
|
|
249
|
+
"forms_found": forms_found,
|
|
250
|
+
"js_endpoints": js_endpoints,
|
|
251
|
+
"unique_parameters": sorted(list(unique_parameters)),
|
|
252
|
+
"lfi_params_found": sorted(list(lfi_params_found)),
|
|
253
|
+
"methods": methods,
|
|
254
|
+
"status_codes": status_codes,
|
|
255
|
+
"sources": sources,
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def extract_injectable_urls(parsed_data: Dict[str, Any]) -> List[str]:
|
|
260
|
+
"""
|
|
261
|
+
Extract URLs that are good candidates for SQL injection testing.
|
|
262
|
+
|
|
263
|
+
Only returns URLs with non-LFI parameters. URLs with only LFI-suspicious
|
|
264
|
+
params (page, file, include, etc.) are excluded since SQLMap won't find
|
|
265
|
+
LFI vulnerabilities.
|
|
266
|
+
|
|
267
|
+
Prioritizes:
|
|
268
|
+
1. URLs with non-LFI query parameters (sqli_candidate_urls)
|
|
269
|
+
2. POST form endpoints
|
|
270
|
+
3. JavaScript-discovered API endpoints
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
parsed_data: Output from parse_katana_output()
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
List of URLs suitable for SQLMap/SQL injection testing
|
|
277
|
+
"""
|
|
278
|
+
injectable = []
|
|
279
|
+
|
|
280
|
+
# SQLi candidate URLs (have non-LFI params)
|
|
281
|
+
for url in parsed_data.get("sqli_candidate_urls", []):
|
|
282
|
+
if url not in injectable:
|
|
283
|
+
injectable.append(url)
|
|
284
|
+
|
|
285
|
+
# POST forms are also injectable, but skip LFI-only forms
|
|
286
|
+
lfi_candidates = parsed_data.get("lfi_candidate_urls", [])
|
|
287
|
+
sqli_candidates = parsed_data.get("sqli_candidate_urls", [])
|
|
288
|
+
for url in parsed_data.get("forms_found", []):
|
|
289
|
+
if url not in injectable:
|
|
290
|
+
# Skip forms that only have LFI params (no SQLi potential)
|
|
291
|
+
if url in lfi_candidates and url not in sqli_candidates:
|
|
292
|
+
continue
|
|
293
|
+
injectable.append(url)
|
|
294
|
+
|
|
295
|
+
# JS endpoints might have hidden params
|
|
296
|
+
for url in parsed_data.get("js_endpoints", []):
|
|
297
|
+
if url not in injectable:
|
|
298
|
+
injectable.append(url)
|
|
299
|
+
|
|
300
|
+
return injectable
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def extract_lfi_urls(parsed_data: Dict[str, Any]) -> List[str]:
|
|
304
|
+
"""
|
|
305
|
+
Extract URLs that are good candidates for LFI (Local File Inclusion) testing.
|
|
306
|
+
|
|
307
|
+
Returns URLs with LFI-suspicious parameters like page, file, include, path,
|
|
308
|
+
template, etc. These should be tested with LFI payloads, not SQLMap.
|
|
309
|
+
|
|
310
|
+
Args:
|
|
311
|
+
parsed_data: Output from parse_katana_output()
|
|
312
|
+
|
|
313
|
+
Returns:
|
|
314
|
+
List of URLs suitable for LFI testing
|
|
315
|
+
"""
|
|
316
|
+
return parsed_data.get("lfi_candidate_urls", [])
|