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.
- 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 +22827 -10678
- 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-2.43.34.dist-info}/METADATA +1 -1
- souleyez-2.43.34.dist-info/RECORD +443 -0
- {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/WHEEL +1 -1
- souleyez-2.43.29.dist-info/RECORD +0 -379
- {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/entry_points.txt +0 -0
- {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/licenses/LICENSE +0 -0
- {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Katana handler.
|
|
4
|
+
|
|
5
|
+
Consolidates parsing and display logic for katana web crawling jobs.
|
|
6
|
+
"""
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
10
|
+
from urllib.parse import urlparse
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
|
|
14
|
+
from souleyez.engine.job_status import (
|
|
15
|
+
STATUS_DONE,
|
|
16
|
+
STATUS_ERROR,
|
|
17
|
+
STATUS_NO_RESULTS,
|
|
18
|
+
STATUS_WARNING,
|
|
19
|
+
)
|
|
20
|
+
from souleyez.handlers.base import BaseToolHandler
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class KatanaHandler(BaseToolHandler):
|
|
26
|
+
"""Handler for Katana web crawling jobs."""
|
|
27
|
+
|
|
28
|
+
tool_name = "katana"
|
|
29
|
+
display_name = "Katana"
|
|
30
|
+
|
|
31
|
+
# All handlers enabled
|
|
32
|
+
has_error_handler = True
|
|
33
|
+
has_warning_handler = True
|
|
34
|
+
has_no_results_handler = True
|
|
35
|
+
has_done_handler = True
|
|
36
|
+
|
|
37
|
+
def parse_job(
|
|
38
|
+
self,
|
|
39
|
+
engagement_id: int,
|
|
40
|
+
log_path: str,
|
|
41
|
+
job: Dict[str, Any],
|
|
42
|
+
host_manager: Optional[Any] = None,
|
|
43
|
+
findings_manager: Optional[Any] = None,
|
|
44
|
+
credentials_manager: Optional[Any] = None,
|
|
45
|
+
) -> Dict[str, Any]:
|
|
46
|
+
"""
|
|
47
|
+
Parse katana crawl results.
|
|
48
|
+
|
|
49
|
+
Extracts discovered URLs, parameters, forms, and JS endpoints.
|
|
50
|
+
"""
|
|
51
|
+
try:
|
|
52
|
+
from souleyez.parsers.katana_parser import (
|
|
53
|
+
parse_katana_output,
|
|
54
|
+
extract_injectable_urls,
|
|
55
|
+
)
|
|
56
|
+
from souleyez.engine.result_handler import detect_tool_error
|
|
57
|
+
|
|
58
|
+
# Import managers if not provided
|
|
59
|
+
if host_manager is None:
|
|
60
|
+
from souleyez.storage.hosts import HostManager
|
|
61
|
+
|
|
62
|
+
host_manager = HostManager()
|
|
63
|
+
|
|
64
|
+
target = job.get("target", "")
|
|
65
|
+
parsed = parse_katana_output(log_path)
|
|
66
|
+
|
|
67
|
+
if parsed.get("error"):
|
|
68
|
+
return {
|
|
69
|
+
"tool": "katana",
|
|
70
|
+
"status": STATUS_ERROR,
|
|
71
|
+
"target": target,
|
|
72
|
+
"error": parsed["error"],
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
# Extract host for tracking
|
|
76
|
+
parsed_url = urlparse(target)
|
|
77
|
+
target_host = parsed_url.hostname or target
|
|
78
|
+
|
|
79
|
+
host_id = None
|
|
80
|
+
if target_host:
|
|
81
|
+
result = host_manager.add_or_update_host(
|
|
82
|
+
engagement_id, {"ip": target_host, "status": "up"}
|
|
83
|
+
)
|
|
84
|
+
if isinstance(result, dict):
|
|
85
|
+
host_id = result.get("id")
|
|
86
|
+
else:
|
|
87
|
+
host_id = result
|
|
88
|
+
|
|
89
|
+
# Store web paths discovered by katana
|
|
90
|
+
paths_added = 0
|
|
91
|
+
if host_id and parsed.get("urls"):
|
|
92
|
+
try:
|
|
93
|
+
from souleyez.storage.web_paths import WebPathsManager
|
|
94
|
+
|
|
95
|
+
wpm = WebPathsManager()
|
|
96
|
+
|
|
97
|
+
# Convert URLs to path format
|
|
98
|
+
web_paths = []
|
|
99
|
+
for url in parsed["urls"]:
|
|
100
|
+
url_parsed = urlparse(url)
|
|
101
|
+
path = url_parsed.path or "/"
|
|
102
|
+
if url_parsed.query:
|
|
103
|
+
path += "?" + url_parsed.query
|
|
104
|
+
|
|
105
|
+
web_paths.append(
|
|
106
|
+
{
|
|
107
|
+
"path": path,
|
|
108
|
+
"url": url,
|
|
109
|
+
"status_code": 200, # Katana only returns accessible URLs
|
|
110
|
+
"method": "GET",
|
|
111
|
+
"source": "katana",
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
if web_paths:
|
|
116
|
+
paths_added = wpm.bulk_add_web_paths(host_id, web_paths)
|
|
117
|
+
except Exception as e:
|
|
118
|
+
logger.warning(f"Failed to store web paths: {e}")
|
|
119
|
+
|
|
120
|
+
# Check for katana errors in log
|
|
121
|
+
katana_error = None
|
|
122
|
+
try:
|
|
123
|
+
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
124
|
+
log_content = f.read()
|
|
125
|
+
katana_error = detect_tool_error(log_content, "katana")
|
|
126
|
+
except Exception:
|
|
127
|
+
pass
|
|
128
|
+
|
|
129
|
+
# Get injectable URLs for chaining (excludes LFI-only params)
|
|
130
|
+
injectable_urls = extract_injectable_urls(parsed)
|
|
131
|
+
|
|
132
|
+
# Get LFI candidate URLs for LFI-specific scanning
|
|
133
|
+
from souleyez.parsers.katana_parser import extract_lfi_urls
|
|
134
|
+
|
|
135
|
+
lfi_urls = extract_lfi_urls(parsed)
|
|
136
|
+
|
|
137
|
+
# Determine status
|
|
138
|
+
urls_list = parsed.get("urls", [])
|
|
139
|
+
urls_with_params_list = parsed.get("urls_with_params", [])
|
|
140
|
+
lfi_candidate_list = parsed.get("lfi_candidate_urls", [])
|
|
141
|
+
sqli_candidate_list = parsed.get("sqli_candidate_urls", [])
|
|
142
|
+
forms_list = parsed.get("forms_found", [])
|
|
143
|
+
js_list = parsed.get("js_endpoints", [])
|
|
144
|
+
lfi_params_list = parsed.get("lfi_params_found", [])
|
|
145
|
+
|
|
146
|
+
if katana_error:
|
|
147
|
+
status = STATUS_ERROR
|
|
148
|
+
elif len(urls_list) > 0:
|
|
149
|
+
status = STATUS_DONE
|
|
150
|
+
else:
|
|
151
|
+
status = STATUS_NO_RESULTS
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
"tool": "katana",
|
|
155
|
+
"status": status,
|
|
156
|
+
"target": target,
|
|
157
|
+
"target_host": target_host,
|
|
158
|
+
# Counts for display
|
|
159
|
+
"urls_found": len(urls_list),
|
|
160
|
+
"urls_with_params_count": len(urls_with_params_list),
|
|
161
|
+
"lfi_candidate_count": len(lfi_candidate_list),
|
|
162
|
+
"sqli_candidate_count": len(sqli_candidate_list),
|
|
163
|
+
"forms_found_count": len(forms_list),
|
|
164
|
+
"js_endpoints_count": len(js_list),
|
|
165
|
+
"unique_parameters": parsed.get("unique_parameters", []),
|
|
166
|
+
"lfi_params_found": lfi_params_list,
|
|
167
|
+
"paths_added": paths_added,
|
|
168
|
+
# Lists for chaining (chain rules expect these)
|
|
169
|
+
"urls_with_params": urls_with_params_list, # For has:urls_with_params
|
|
170
|
+
"lfi_candidate_urls": lfi_candidate_list, # For has:lfi_candidate_urls (LFI scan)
|
|
171
|
+
"sqli_candidate_urls": sqli_candidate_list, # For has:sqli_candidate_urls (SQLMap)
|
|
172
|
+
"forms_found": forms_list, # For has:forms_found
|
|
173
|
+
"injectable_urls": injectable_urls, # SQLi candidates only (excludes LFI-only)
|
|
174
|
+
"crawled_urls": urls_list,
|
|
175
|
+
"parameterized_urls": urls_with_params_list,
|
|
176
|
+
"post_endpoints": forms_list,
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
except Exception as e:
|
|
180
|
+
logger.exception(f"Failed to parse katana results: {e}")
|
|
181
|
+
return {
|
|
182
|
+
"tool": "katana",
|
|
183
|
+
"status": STATUS_ERROR,
|
|
184
|
+
"target": job.get("target", ""),
|
|
185
|
+
"error": str(e),
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
def display_done(
|
|
189
|
+
self,
|
|
190
|
+
job: Dict[str, Any],
|
|
191
|
+
log_path: str,
|
|
192
|
+
show_all: bool = False,
|
|
193
|
+
show_passwords: bool = False,
|
|
194
|
+
) -> None:
|
|
195
|
+
"""Display successful katana crawl results."""
|
|
196
|
+
from souleyez.parsers.katana_parser import parse_katana_output
|
|
197
|
+
|
|
198
|
+
target = job.get("target", "Unknown")
|
|
199
|
+
parse_result = job.get("parse_result", {})
|
|
200
|
+
|
|
201
|
+
# Re-parse for full details if needed
|
|
202
|
+
parsed = parse_katana_output(log_path)
|
|
203
|
+
|
|
204
|
+
click.echo(click.style("=" * 70, fg="green"))
|
|
205
|
+
click.echo(click.style("KATANA WEB CRAWLER RESULTS", bold=True, fg="green"))
|
|
206
|
+
click.echo(click.style("=" * 70, fg="green"))
|
|
207
|
+
click.echo()
|
|
208
|
+
click.echo(f" Target: {target}")
|
|
209
|
+
click.echo()
|
|
210
|
+
|
|
211
|
+
# Summary stats
|
|
212
|
+
urls_found = len(parsed.get("urls", []))
|
|
213
|
+
urls_with_params = parsed.get("urls_with_params", [])
|
|
214
|
+
forms_found = parsed.get("forms_found", [])
|
|
215
|
+
js_endpoints = parsed.get("js_endpoints", [])
|
|
216
|
+
unique_params = parsed.get("unique_parameters", [])
|
|
217
|
+
|
|
218
|
+
click.echo(click.style(" SUMMARY", bold=True))
|
|
219
|
+
click.echo(f" Total URLs discovered: {urls_found}")
|
|
220
|
+
click.echo(f" URLs with parameters: {len(urls_with_params)}")
|
|
221
|
+
click.echo(f" POST endpoints (forms): {len(forms_found)}")
|
|
222
|
+
click.echo(f" JavaScript endpoints: {len(js_endpoints)}")
|
|
223
|
+
click.echo(f" Unique parameters: {len(unique_params)}")
|
|
224
|
+
click.echo()
|
|
225
|
+
|
|
226
|
+
# All discovered URLs
|
|
227
|
+
all_urls = parsed.get("urls", [])
|
|
228
|
+
if all_urls:
|
|
229
|
+
click.echo(click.style(" DISCOVERED URLs", bold=True, fg="cyan"))
|
|
230
|
+
display_limit = None if show_all else 15
|
|
231
|
+
for url in all_urls[:display_limit]:
|
|
232
|
+
click.echo(f" {url}")
|
|
233
|
+
if not show_all and len(all_urls) > 15:
|
|
234
|
+
click.echo(
|
|
235
|
+
f" ... and {len(all_urls) - 15} more (use 'a' to show all)"
|
|
236
|
+
)
|
|
237
|
+
click.echo()
|
|
238
|
+
|
|
239
|
+
# URLs with parameters (most important for SQLi)
|
|
240
|
+
if urls_with_params:
|
|
241
|
+
click.echo(
|
|
242
|
+
click.style(
|
|
243
|
+
" URLS WITH PARAMETERS (SQLi Candidates)", bold=True, fg="yellow"
|
|
244
|
+
)
|
|
245
|
+
)
|
|
246
|
+
display_limit = None if show_all else 10
|
|
247
|
+
for i, url in enumerate(urls_with_params[:display_limit]):
|
|
248
|
+
click.echo(f" {url}")
|
|
249
|
+
if not show_all and len(urls_with_params) > 10:
|
|
250
|
+
click.echo(
|
|
251
|
+
f" ... and {len(urls_with_params) - 10} more (use 'a' to show all)"
|
|
252
|
+
)
|
|
253
|
+
click.echo()
|
|
254
|
+
|
|
255
|
+
# POST endpoints
|
|
256
|
+
if forms_found:
|
|
257
|
+
click.echo(
|
|
258
|
+
click.style(" POST ENDPOINTS (Form Submissions)", bold=True, fg="cyan")
|
|
259
|
+
)
|
|
260
|
+
display_limit = None if show_all else 5
|
|
261
|
+
for url in forms_found[:display_limit]:
|
|
262
|
+
click.echo(f" [POST] {url}")
|
|
263
|
+
if not show_all and len(forms_found) > 5:
|
|
264
|
+
click.echo(f" ... and {len(forms_found) - 5} more")
|
|
265
|
+
click.echo()
|
|
266
|
+
|
|
267
|
+
# JavaScript endpoints
|
|
268
|
+
if js_endpoints:
|
|
269
|
+
click.echo(click.style(" JAVASCRIPT ENDPOINTS", bold=True, fg="magenta"))
|
|
270
|
+
display_limit = None if show_all else 5
|
|
271
|
+
for url in js_endpoints[:display_limit]:
|
|
272
|
+
click.echo(f" {url}")
|
|
273
|
+
if not show_all and len(js_endpoints) > 5:
|
|
274
|
+
click.echo(f" ... and {len(js_endpoints) - 5} more")
|
|
275
|
+
click.echo()
|
|
276
|
+
|
|
277
|
+
# Unique parameters found
|
|
278
|
+
if unique_params:
|
|
279
|
+
click.echo(click.style(" UNIQUE PARAMETERS FOUND", bold=True))
|
|
280
|
+
params_str = ", ".join(unique_params[:20])
|
|
281
|
+
if len(unique_params) > 20:
|
|
282
|
+
params_str += f", ... (+{len(unique_params) - 20} more)"
|
|
283
|
+
click.echo(f" {params_str}")
|
|
284
|
+
click.echo()
|
|
285
|
+
|
|
286
|
+
click.echo(click.style("=" * 70, fg="green"))
|
|
287
|
+
click.echo()
|
|
288
|
+
|
|
289
|
+
def display_error(
|
|
290
|
+
self,
|
|
291
|
+
job: Dict[str, Any],
|
|
292
|
+
log_path: str,
|
|
293
|
+
log_content: Optional[str] = None,
|
|
294
|
+
) -> None:
|
|
295
|
+
"""Display katana error results."""
|
|
296
|
+
target = job.get("target", "Unknown")
|
|
297
|
+
parse_result = job.get("parse_result", {})
|
|
298
|
+
|
|
299
|
+
click.echo(click.style("=" * 70, fg="red"))
|
|
300
|
+
click.echo(click.style("KATANA CRAWL ERROR", bold=True, fg="red"))
|
|
301
|
+
click.echo(click.style("=" * 70, fg="red"))
|
|
302
|
+
click.echo()
|
|
303
|
+
click.echo(f" Target: {target}")
|
|
304
|
+
click.echo()
|
|
305
|
+
|
|
306
|
+
# Try to identify common errors
|
|
307
|
+
if log_content is None:
|
|
308
|
+
try:
|
|
309
|
+
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
310
|
+
log_content = f.read()
|
|
311
|
+
except Exception:
|
|
312
|
+
log_content = ""
|
|
313
|
+
|
|
314
|
+
error_identified = False
|
|
315
|
+
|
|
316
|
+
if (
|
|
317
|
+
"no such file or directory" in log_content.lower()
|
|
318
|
+
or "failed to run command" in log_content.lower()
|
|
319
|
+
):
|
|
320
|
+
click.echo(click.style(" Issue: Katana not installed", fg="red"))
|
|
321
|
+
click.echo(" Katana binary was not found on this system.")
|
|
322
|
+
click.echo()
|
|
323
|
+
click.echo(" To install:")
|
|
324
|
+
click.echo(" Kali Linux: sudo apt install katana chromium")
|
|
325
|
+
click.echo(
|
|
326
|
+
" Ubuntu: go install github.com/projectdiscovery/katana/cmd/katana@latest"
|
|
327
|
+
)
|
|
328
|
+
click.echo()
|
|
329
|
+
click.echo(" Also ensure Chromium is installed for headless mode:")
|
|
330
|
+
click.echo(" sudo apt install chromium")
|
|
331
|
+
error_identified = True
|
|
332
|
+
elif "connection refused" in log_content.lower():
|
|
333
|
+
click.echo(click.style(" Issue: Connection refused", fg="red"))
|
|
334
|
+
click.echo(" The target is not responding on the specified port.")
|
|
335
|
+
error_identified = True
|
|
336
|
+
elif "timeout" in log_content.lower():
|
|
337
|
+
click.echo(click.style(" Issue: Connection timeout", fg="red"))
|
|
338
|
+
click.echo(" The target took too long to respond.")
|
|
339
|
+
error_identified = True
|
|
340
|
+
elif "chromium" in log_content.lower() or "headless" in log_content.lower():
|
|
341
|
+
click.echo(click.style(" Issue: Headless browser error", fg="red"))
|
|
342
|
+
click.echo(" Chromium may not be installed or configured correctly.")
|
|
343
|
+
click.echo(" Try: sudo apt install chromium")
|
|
344
|
+
click.echo(' Or run with --args "-no-headless" to skip browser mode.')
|
|
345
|
+
error_identified = True
|
|
346
|
+
elif "certificate" in log_content.lower() or "ssl" in log_content.lower():
|
|
347
|
+
click.echo(click.style(" Issue: SSL/TLS certificate error", fg="red"))
|
|
348
|
+
click.echo(" The target has an invalid or self-signed certificate.")
|
|
349
|
+
error_identified = True
|
|
350
|
+
|
|
351
|
+
if not error_identified:
|
|
352
|
+
error_msg = parse_result.get("error", "Unknown error occurred")
|
|
353
|
+
click.echo(f" Error: {error_msg}")
|
|
354
|
+
|
|
355
|
+
click.echo()
|
|
356
|
+
click.echo(" Tips:")
|
|
357
|
+
click.echo(" - Verify the target URL is accessible")
|
|
358
|
+
click.echo(" - Check if Chromium is installed for headless mode")
|
|
359
|
+
click.echo(" - Try with -no-headless flag if browser issues persist")
|
|
360
|
+
click.echo()
|
|
361
|
+
click.echo(click.style("=" * 70, fg="red"))
|
|
362
|
+
click.echo()
|
|
363
|
+
|
|
364
|
+
def display_no_results(
|
|
365
|
+
self,
|
|
366
|
+
job: Dict[str, Any],
|
|
367
|
+
log_path: str,
|
|
368
|
+
) -> None:
|
|
369
|
+
"""Display no results message with tips."""
|
|
370
|
+
target = job.get("target", "Unknown")
|
|
371
|
+
|
|
372
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
373
|
+
click.echo(click.style("KATANA CRAWL - NO RESULTS", bold=True, fg="yellow"))
|
|
374
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
375
|
+
click.echo()
|
|
376
|
+
click.echo(f" Target: {target}")
|
|
377
|
+
click.echo()
|
|
378
|
+
click.echo(" No URLs were discovered during the crawl.")
|
|
379
|
+
click.echo()
|
|
380
|
+
click.echo(" Tips:")
|
|
381
|
+
click.echo(" - Verify the target URL is accessible and returns content")
|
|
382
|
+
click.echo(" - The page may require authentication")
|
|
383
|
+
click.echo(" - Try increasing crawl depth with -d 5")
|
|
384
|
+
click.echo(" - Ensure headless mode is working (check Chromium install)")
|
|
385
|
+
click.echo(" - The target may be blocking automated crawlers")
|
|
386
|
+
click.echo()
|
|
387
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
388
|
+
click.echo()
|
|
389
|
+
|
|
390
|
+
def display_warning(
|
|
391
|
+
self,
|
|
392
|
+
job: Dict[str, Any],
|
|
393
|
+
log_path: str,
|
|
394
|
+
log_content: Optional[str] = None,
|
|
395
|
+
) -> None:
|
|
396
|
+
"""Display warning status results."""
|
|
397
|
+
target = job.get("target", "Unknown")
|
|
398
|
+
parse_result = job.get("parse_result", {})
|
|
399
|
+
|
|
400
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
401
|
+
click.echo(click.style("KATANA CRAWL WARNING", bold=True, fg="yellow"))
|
|
402
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
403
|
+
click.echo()
|
|
404
|
+
click.echo(f" Target: {target}")
|
|
405
|
+
click.echo()
|
|
406
|
+
|
|
407
|
+
summary = parse_result.get("summary", "Crawl completed with warnings")
|
|
408
|
+
click.echo(f" {summary}")
|
|
409
|
+
click.echo()
|
|
410
|
+
|
|
411
|
+
# Still show any results we got
|
|
412
|
+
urls_found = parse_result.get("urls_found", 0)
|
|
413
|
+
urls_with_params = len(parse_result.get("urls_with_params", []))
|
|
414
|
+
if urls_found > 0:
|
|
415
|
+
click.echo(f" Partial results: {urls_found} URLs discovered")
|
|
416
|
+
if urls_with_params > 0:
|
|
417
|
+
click.echo(f" URLs with parameters: {urls_with_params}")
|
|
418
|
+
|
|
419
|
+
click.echo()
|
|
420
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
421
|
+
click.echo()
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
# Register handler - this makes it discoverable by the registry
|
|
425
|
+
handler = KatanaHandler()
|