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/parsers/nikto_parser.py
CHANGED
|
@@ -51,56 +51,58 @@ def parse_nikto_output(output: str, target: str = "") -> Dict[str, Any]:
|
|
|
51
51
|
}
|
|
52
52
|
"""
|
|
53
53
|
result = {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
54
|
+
"target": target,
|
|
55
|
+
"target_ip": "",
|
|
56
|
+
"target_hostname": "",
|
|
57
|
+
"target_port": 80,
|
|
58
|
+
"server": "",
|
|
59
|
+
"findings": [],
|
|
60
|
+
"stats": {
|
|
61
|
+
"total": 0,
|
|
62
|
+
"by_severity": {"high": 0, "medium": 0, "low": 0, "info": 0},
|
|
63
|
+
},
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
lines = output.split(
|
|
66
|
+
lines = output.split("\n")
|
|
67
67
|
|
|
68
68
|
for line in lines:
|
|
69
69
|
line = line.strip()
|
|
70
70
|
|
|
71
71
|
# Skip empty lines and dividers
|
|
72
|
-
if not line or line.startswith(
|
|
72
|
+
if not line or line.startswith("---") or line.startswith("==="):
|
|
73
73
|
continue
|
|
74
74
|
|
|
75
75
|
# Parse target info
|
|
76
|
-
if line.startswith(
|
|
77
|
-
match = re.search(r
|
|
76
|
+
if line.startswith("+ Target IP:"):
|
|
77
|
+
match = re.search(r"\+ Target IP:\s+(.+)", line)
|
|
78
78
|
if match:
|
|
79
|
-
result[
|
|
79
|
+
result["target_ip"] = match.group(1).strip()
|
|
80
80
|
|
|
81
|
-
elif line.startswith(
|
|
82
|
-
match = re.search(r
|
|
81
|
+
elif line.startswith("+ Target Hostname:"):
|
|
82
|
+
match = re.search(r"\+ Target Hostname:\s+(.+)", line)
|
|
83
83
|
if match:
|
|
84
|
-
result[
|
|
84
|
+
result["target_hostname"] = match.group(1).strip()
|
|
85
85
|
|
|
86
|
-
elif line.startswith(
|
|
87
|
-
match = re.search(r
|
|
86
|
+
elif line.startswith("+ Target Port:"):
|
|
87
|
+
match = re.search(r"\+ Target Port:\s+(\d+)", line)
|
|
88
88
|
if match:
|
|
89
|
-
result[
|
|
89
|
+
result["target_port"] = int(match.group(1))
|
|
90
90
|
|
|
91
|
-
elif line.startswith(
|
|
92
|
-
match = re.search(r
|
|
91
|
+
elif line.startswith("+ Server:"):
|
|
92
|
+
match = re.search(r"\+ Server:\s+(.+)", line)
|
|
93
93
|
if match:
|
|
94
|
-
result[
|
|
94
|
+
result["server"] = match.group(1).strip()
|
|
95
95
|
|
|
96
96
|
# Parse findings (any line starting with + that isn't metadata)
|
|
97
|
-
elif line.startswith(
|
|
97
|
+
elif line.startswith("+"):
|
|
98
98
|
finding = _parse_finding_line(line)
|
|
99
99
|
if finding:
|
|
100
|
-
result[
|
|
101
|
-
severity = finding.get(
|
|
102
|
-
result[
|
|
103
|
-
|
|
100
|
+
result["findings"].append(finding)
|
|
101
|
+
severity = finding.get("severity", "info")
|
|
102
|
+
result["stats"]["by_severity"][severity] = (
|
|
103
|
+
result["stats"]["by_severity"].get(severity, 0) + 1
|
|
104
|
+
)
|
|
105
|
+
result["stats"]["total"] += 1
|
|
104
106
|
|
|
105
107
|
return result
|
|
106
108
|
|
|
@@ -116,54 +118,54 @@ def _parse_finding_line(line: str) -> Dict[str, Any]:
|
|
|
116
118
|
+ Server: Apache/2.4.41 (Ubuntu) <- Skip these
|
|
117
119
|
"""
|
|
118
120
|
# Skip metadata lines
|
|
119
|
-
if line.startswith(
|
|
121
|
+
if line.startswith("+ Server:") or line.startswith("+ Start Time:"):
|
|
120
122
|
return None
|
|
121
|
-
if line.startswith(
|
|
123
|
+
if line.startswith("+ Target"):
|
|
122
124
|
return None
|
|
123
|
-
if line.startswith(
|
|
125
|
+
if line.startswith("+ End Time:"):
|
|
124
126
|
return None
|
|
125
|
-
if line.startswith(
|
|
127
|
+
if line.startswith("+ No CGI"):
|
|
126
128
|
return None
|
|
127
|
-
if
|
|
129
|
+
if "host(s) tested" in line:
|
|
128
130
|
return None
|
|
129
|
-
if
|
|
131
|
+
if "items checked:" in line:
|
|
130
132
|
return None
|
|
131
133
|
|
|
132
134
|
try:
|
|
133
135
|
# Remove leading +
|
|
134
|
-
content = line.lstrip(
|
|
136
|
+
content = line.lstrip("+ ").strip()
|
|
135
137
|
|
|
136
138
|
osvdb = None
|
|
137
|
-
path =
|
|
138
|
-
description =
|
|
139
|
+
path = ""
|
|
140
|
+
description = ""
|
|
139
141
|
|
|
140
142
|
# Check for OSVDB reference
|
|
141
|
-
osvdb_match = re.match(r
|
|
143
|
+
osvdb_match = re.match(r"(OSVDB-\d+):\s*(.+)", content)
|
|
142
144
|
if osvdb_match:
|
|
143
145
|
osvdb = osvdb_match.group(1)
|
|
144
146
|
content = osvdb_match.group(2)
|
|
145
147
|
|
|
146
148
|
# Extract path and description
|
|
147
149
|
# Format: /path/: Description or /path: Description
|
|
148
|
-
path_match = re.match(r
|
|
150
|
+
path_match = re.match(r"(/[^:]*):?\s*(.+)", content)
|
|
149
151
|
if path_match:
|
|
150
|
-
path = path_match.group(1).rstrip(
|
|
152
|
+
path = path_match.group(1).rstrip(":").strip()
|
|
151
153
|
description = path_match.group(2).strip()
|
|
152
154
|
else:
|
|
153
155
|
description = content
|
|
154
156
|
|
|
155
157
|
# Skip if no meaningful content
|
|
156
|
-
if not description or description.startswith(
|
|
158
|
+
if not description or description.startswith("Target"):
|
|
157
159
|
return None
|
|
158
160
|
|
|
159
161
|
# Determine severity based on keywords
|
|
160
162
|
severity = _determine_severity(description, osvdb)
|
|
161
163
|
|
|
162
164
|
return {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
"osvdb": osvdb,
|
|
166
|
+
"path": path,
|
|
167
|
+
"description": description,
|
|
168
|
+
"severity": severity,
|
|
167
169
|
}
|
|
168
170
|
|
|
169
171
|
except Exception:
|
|
@@ -178,52 +180,331 @@ def _determine_severity(description: str, osvdb: str = None) -> str:
|
|
|
178
180
|
|
|
179
181
|
# High severity indicators
|
|
180
182
|
high_keywords = [
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
183
|
+
"remote code execution",
|
|
184
|
+
"rce",
|
|
185
|
+
"command execution",
|
|
186
|
+
"shell",
|
|
187
|
+
"sql injection",
|
|
188
|
+
"file inclusion",
|
|
189
|
+
"lfi",
|
|
190
|
+
"rfi",
|
|
191
|
+
"arbitrary file",
|
|
192
|
+
"password",
|
|
193
|
+
"credentials",
|
|
194
|
+
"authentication bypass",
|
|
195
|
+
"backdoor",
|
|
185
196
|
]
|
|
186
197
|
for keyword in high_keywords:
|
|
187
198
|
if keyword in desc_lower:
|
|
188
|
-
return
|
|
199
|
+
return "high"
|
|
189
200
|
|
|
190
201
|
# Medium severity indicators
|
|
191
202
|
medium_keywords = [
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
203
|
+
"directory indexing",
|
|
204
|
+
"directory listing",
|
|
205
|
+
"backup file",
|
|
206
|
+
"config file",
|
|
207
|
+
"sensitive",
|
|
208
|
+
"exposure",
|
|
209
|
+
"disclosure",
|
|
210
|
+
"xss",
|
|
211
|
+
"cross-site",
|
|
212
|
+
"injection",
|
|
213
|
+
"traversal",
|
|
214
|
+
"default",
|
|
215
|
+
"admin",
|
|
216
|
+
".bak",
|
|
217
|
+
".old",
|
|
218
|
+
".swp",
|
|
196
219
|
]
|
|
197
220
|
for keyword in medium_keywords:
|
|
198
221
|
if keyword in desc_lower:
|
|
199
|
-
return
|
|
222
|
+
return "medium"
|
|
200
223
|
|
|
201
224
|
# Low severity indicators
|
|
202
225
|
low_keywords = [
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
226
|
+
"header",
|
|
227
|
+
"x-frame",
|
|
228
|
+
"x-content-type",
|
|
229
|
+
"x-xss",
|
|
230
|
+
"cookie",
|
|
231
|
+
"httponly",
|
|
232
|
+
"secure flag",
|
|
233
|
+
"hsts",
|
|
234
|
+
"content-security-policy",
|
|
235
|
+
"csp",
|
|
236
|
+
"clickjacking",
|
|
206
237
|
]
|
|
207
238
|
for keyword in low_keywords:
|
|
208
239
|
if keyword in desc_lower:
|
|
209
|
-
return
|
|
240
|
+
return "low"
|
|
210
241
|
|
|
211
242
|
# OSVDB references often indicate real issues
|
|
212
243
|
if osvdb:
|
|
213
|
-
return
|
|
244
|
+
return "medium"
|
|
214
245
|
|
|
215
|
-
return
|
|
246
|
+
return "info"
|
|
216
247
|
|
|
217
248
|
|
|
218
|
-
def get_findings_by_severity(
|
|
249
|
+
def get_findings_by_severity(
|
|
250
|
+
parsed: Dict[str, Any], severity: str
|
|
251
|
+
) -> List[Dict[str, Any]]:
|
|
219
252
|
"""
|
|
220
253
|
Filter findings by severity level.
|
|
221
254
|
"""
|
|
222
|
-
return [f for f in parsed.get(
|
|
255
|
+
return [f for f in parsed.get("findings", []) if f.get("severity") == severity]
|
|
223
256
|
|
|
224
257
|
|
|
225
258
|
def get_high_value_findings(parsed: Dict[str, Any]) -> List[Dict[str, Any]]:
|
|
226
259
|
"""
|
|
227
260
|
Get findings that are high or medium severity.
|
|
228
261
|
"""
|
|
229
|
-
return [
|
|
262
|
+
return [
|
|
263
|
+
f for f in parsed.get("findings", []) if f.get("severity") in ("high", "medium")
|
|
264
|
+
]
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def generate_next_steps(
|
|
268
|
+
parsed: Dict[str, Any], target: str = ""
|
|
269
|
+
) -> List[Dict[str, Any]]:
|
|
270
|
+
"""
|
|
271
|
+
Generate suggested next steps based on nikto findings.
|
|
272
|
+
|
|
273
|
+
Translates cryptic OSVDB references and generic findings into
|
|
274
|
+
actionable exploitation steps.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
parsed: Output from parse_nikto_output()
|
|
278
|
+
target: Target URL for command examples
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
List of next step dicts with title, commands, reason
|
|
282
|
+
"""
|
|
283
|
+
next_steps = []
|
|
284
|
+
findings = parsed.get("findings", [])
|
|
285
|
+
base_url = target.rstrip("/") or f"http://{parsed.get('target_hostname', 'target')}"
|
|
286
|
+
|
|
287
|
+
if not findings:
|
|
288
|
+
return next_steps
|
|
289
|
+
|
|
290
|
+
# Categorize findings
|
|
291
|
+
dir_indexing = []
|
|
292
|
+
backup_files = []
|
|
293
|
+
config_exposure = []
|
|
294
|
+
outdated_server = []
|
|
295
|
+
missing_headers = []
|
|
296
|
+
cgi_vulns = []
|
|
297
|
+
file_inclusion = []
|
|
298
|
+
injection_vulns = []
|
|
299
|
+
auth_issues = []
|
|
300
|
+
|
|
301
|
+
server = parsed.get("server", "")
|
|
302
|
+
|
|
303
|
+
for finding in findings:
|
|
304
|
+
desc = finding.get("description", "").lower()
|
|
305
|
+
path = finding.get("path", "")
|
|
306
|
+
osvdb = finding.get("osvdb", "")
|
|
307
|
+
severity = finding.get("severity", "info")
|
|
308
|
+
|
|
309
|
+
# Directory indexing
|
|
310
|
+
if "directory indexing" in desc or "directory listing" in desc:
|
|
311
|
+
dir_indexing.append({"path": path, "osvdb": osvdb})
|
|
312
|
+
# Backup files
|
|
313
|
+
elif any(
|
|
314
|
+
kw in desc
|
|
315
|
+
for kw in ["backup", ".bak", ".old", ".orig", ".save", "configuration file"]
|
|
316
|
+
):
|
|
317
|
+
backup_files.append({"path": path, "desc": finding.get("description", "")})
|
|
318
|
+
# Config files
|
|
319
|
+
elif any(
|
|
320
|
+
kw in desc
|
|
321
|
+
for kw in ["config", "phpinfo", ".htaccess", ".htpasswd", "web.config"]
|
|
322
|
+
):
|
|
323
|
+
config_exposure.append(
|
|
324
|
+
{"path": path, "desc": finding.get("description", "")}
|
|
325
|
+
)
|
|
326
|
+
# Outdated server
|
|
327
|
+
elif "outdated" in desc or "appears to be" in desc:
|
|
328
|
+
outdated_server.append(
|
|
329
|
+
{"desc": finding.get("description", ""), "server": server}
|
|
330
|
+
)
|
|
331
|
+
# Missing security headers
|
|
332
|
+
elif any(
|
|
333
|
+
kw in desc
|
|
334
|
+
for kw in [
|
|
335
|
+
"x-frame",
|
|
336
|
+
"x-xss",
|
|
337
|
+
"x-content-type",
|
|
338
|
+
"httponly",
|
|
339
|
+
"secure flag",
|
|
340
|
+
"hsts",
|
|
341
|
+
"csp",
|
|
342
|
+
]
|
|
343
|
+
):
|
|
344
|
+
missing_headers.append({"desc": finding.get("description", "")})
|
|
345
|
+
# CGI vulnerabilities
|
|
346
|
+
elif "/cgi-bin/" in path or ".cgi" in path or "cgi" in desc:
|
|
347
|
+
cgi_vulns.append(
|
|
348
|
+
{"path": path, "desc": finding.get("description", ""), "osvdb": osvdb}
|
|
349
|
+
)
|
|
350
|
+
# File inclusion
|
|
351
|
+
elif any(
|
|
352
|
+
kw in desc
|
|
353
|
+
for kw in ["file inclusion", "lfi", "rfi", "traversal", "arbitrary file"]
|
|
354
|
+
):
|
|
355
|
+
file_inclusion.append(
|
|
356
|
+
{"path": path, "desc": finding.get("description", ""), "osvdb": osvdb}
|
|
357
|
+
)
|
|
358
|
+
# Injection
|
|
359
|
+
elif any(kw in desc for kw in ["injection", "xss", "sql", "command execution"]):
|
|
360
|
+
injection_vulns.append(
|
|
361
|
+
{"path": path, "desc": finding.get("description", ""), "osvdb": osvdb}
|
|
362
|
+
)
|
|
363
|
+
# Auth issues
|
|
364
|
+
elif any(
|
|
365
|
+
kw in desc
|
|
366
|
+
for kw in ["authentication", "bypass", "default", "password", "credential"]
|
|
367
|
+
):
|
|
368
|
+
auth_issues.append({"path": path, "desc": finding.get("description", "")})
|
|
369
|
+
|
|
370
|
+
# Directory indexing - browse for sensitive files
|
|
371
|
+
if dir_indexing:
|
|
372
|
+
paths = [d["path"] for d in dir_indexing[:3]]
|
|
373
|
+
next_steps.append(
|
|
374
|
+
{
|
|
375
|
+
"title": "Browse indexed directories for sensitive files",
|
|
376
|
+
"commands": [f'curl -s "{base_url}{p}"' for p in paths]
|
|
377
|
+
+ [f"# Look for: passwords, configs, source code, database files"],
|
|
378
|
+
"reason": f"Found {len(dir_indexing)} directory with indexing enabled - may expose sensitive files",
|
|
379
|
+
}
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
# Backup files - download and analyze
|
|
383
|
+
if backup_files:
|
|
384
|
+
next_steps.append(
|
|
385
|
+
{
|
|
386
|
+
"title": "Download and review backup files",
|
|
387
|
+
"commands": [
|
|
388
|
+
f'curl -s "{base_url}{b["path"]}" -o backup_file'
|
|
389
|
+
for b in backup_files[:3]
|
|
390
|
+
],
|
|
391
|
+
"reason": f"Found {len(backup_files)} backup file(s) - may contain source code, credentials",
|
|
392
|
+
}
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
# Config exposure - extract credentials
|
|
396
|
+
if config_exposure:
|
|
397
|
+
next_steps.append(
|
|
398
|
+
{
|
|
399
|
+
"title": "Extract credentials from exposed configs",
|
|
400
|
+
"commands": [
|
|
401
|
+
f'curl -s "{base_url}{c["path"]}"' for c in config_exposure[:3]
|
|
402
|
+
],
|
|
403
|
+
"reason": f"Found {len(config_exposure)} config file(s) - check for database passwords, API keys",
|
|
404
|
+
}
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
# Outdated server - searchsploit
|
|
408
|
+
if outdated_server or server:
|
|
409
|
+
server_info = (
|
|
410
|
+
server or outdated_server[0].get("desc", "") if outdated_server else ""
|
|
411
|
+
)
|
|
412
|
+
if server_info:
|
|
413
|
+
next_steps.append(
|
|
414
|
+
{
|
|
415
|
+
"title": f"Search for exploits for {server_info[:40]}",
|
|
416
|
+
"commands": [
|
|
417
|
+
f'searchsploit "{server_info}"',
|
|
418
|
+
f'# Or: searchsploit -w "{server_info}" for web links',
|
|
419
|
+
],
|
|
420
|
+
"reason": f"Server version detected - check for known CVEs and public exploits",
|
|
421
|
+
}
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
# CGI vulnerabilities
|
|
425
|
+
if cgi_vulns:
|
|
426
|
+
cgi_path = cgi_vulns[0]["path"]
|
|
427
|
+
full_url = f"{base_url}{cgi_path}"
|
|
428
|
+
next_steps.append(
|
|
429
|
+
{
|
|
430
|
+
"title": "Test CGI scripts for command injection",
|
|
431
|
+
"commands": [
|
|
432
|
+
f'curl "{full_url}?cmd=id"',
|
|
433
|
+
f'curl "{full_url}?file=/etc/passwd"',
|
|
434
|
+
f"curl -A '() {{ :; }}; echo; /bin/id' \"{full_url}\"",
|
|
435
|
+
],
|
|
436
|
+
"reason": f"Nikto found {len(cgi_vulns)} CGI issue(s) - test for injection and Shellshock",
|
|
437
|
+
}
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
# File inclusion
|
|
441
|
+
if file_inclusion:
|
|
442
|
+
lfi_path = file_inclusion[0]["path"]
|
|
443
|
+
next_steps.append(
|
|
444
|
+
{
|
|
445
|
+
"title": "Exploit file inclusion vulnerability",
|
|
446
|
+
"commands": [
|
|
447
|
+
f'curl "{base_url}{lfi_path}?file=../../../etc/passwd"',
|
|
448
|
+
f'curl "{base_url}{lfi_path}?page=php://filter/convert.base64-encode/resource=index.php"',
|
|
449
|
+
],
|
|
450
|
+
"reason": f"File inclusion detected - test path traversal and PHP wrappers",
|
|
451
|
+
}
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
# Injection vulnerabilities
|
|
455
|
+
if injection_vulns:
|
|
456
|
+
inj = injection_vulns[0]
|
|
457
|
+
next_steps.append(
|
|
458
|
+
{
|
|
459
|
+
"title": "Test injection vulnerability",
|
|
460
|
+
"commands": [
|
|
461
|
+
f'sqlmap -u "{base_url}{inj["path"]}" --batch --risk=3 --level=5',
|
|
462
|
+
],
|
|
463
|
+
"reason": f'Injection point found at {inj["path"]} - run automated testing',
|
|
464
|
+
}
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
# Auth issues
|
|
468
|
+
if auth_issues:
|
|
469
|
+
next_steps.append(
|
|
470
|
+
{
|
|
471
|
+
"title": "Test authentication bypass",
|
|
472
|
+
"commands": [
|
|
473
|
+
f"# Try: admin/admin, admin/password, root/root",
|
|
474
|
+
f'hydra -L data/wordlists/usernames_common.txt -P data/wordlists/top20_quick.txt {base_url.replace("http://", "").replace("https://", "").split("/")[0]} http-get /',
|
|
475
|
+
],
|
|
476
|
+
"reason": f"Authentication issue detected - test default credentials",
|
|
477
|
+
}
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
# Missing headers - only include if high severity findings exist
|
|
481
|
+
# (headers alone are low value, but good to mention for completeness)
|
|
482
|
+
high_severity = [f for f in findings if f.get("severity") in ("high", "medium")]
|
|
483
|
+
if missing_headers and not high_severity:
|
|
484
|
+
next_steps.append(
|
|
485
|
+
{
|
|
486
|
+
"title": "Report missing security headers",
|
|
487
|
+
"commands": [
|
|
488
|
+
f'curl -I "{base_url}" | grep -i "x-frame\\|x-xss\\|x-content\\|strict-transport"',
|
|
489
|
+
],
|
|
490
|
+
"reason": f"{len(missing_headers)} security header(s) missing - low risk but worth documenting",
|
|
491
|
+
}
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
# If OSVDB references exist, suggest looking them up
|
|
495
|
+
osvdb_refs = [f.get("osvdb") for f in findings if f.get("osvdb")]
|
|
496
|
+
if osvdb_refs:
|
|
497
|
+
unique_osvdb = list(set(osvdb_refs))[:3]
|
|
498
|
+
next_steps.append(
|
|
499
|
+
{
|
|
500
|
+
"title": "Look up OSVDB references",
|
|
501
|
+
"commands": [
|
|
502
|
+
f"# OSVDB is deprecated, use CVE/NVD instead",
|
|
503
|
+
f"# Search: https://cve.mitre.org/cve/search_cve_list.html",
|
|
504
|
+
f"searchsploit --cve <CVE-XXXX-XXXXX>",
|
|
505
|
+
],
|
|
506
|
+
"reason": f'Found {len(unique_osvdb)} OSVDB reference(s): {", ".join(unique_osvdb)} - cross-reference with CVE database',
|
|
507
|
+
}
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
return next_steps
|