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
|
@@ -60,8 +60,12 @@ class SentinelSIEMClient(SIEMClient):
|
|
|
60
60
|
self._token_expiry: Optional[datetime] = None
|
|
61
61
|
|
|
62
62
|
# Azure endpoints
|
|
63
|
-
self.login_url =
|
|
64
|
-
|
|
63
|
+
self.login_url = (
|
|
64
|
+
f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token"
|
|
65
|
+
)
|
|
66
|
+
self.log_analytics_url = (
|
|
67
|
+
f"https://api.loganalytics.io/v1/workspaces/{workspace_id}"
|
|
68
|
+
)
|
|
65
69
|
self.management_base = "https://management.azure.com"
|
|
66
70
|
self.sentinel_base = (
|
|
67
71
|
f"{self.management_base}/subscriptions/{subscription_id}"
|
|
@@ -71,7 +75,7 @@ class SentinelSIEMClient(SIEMClient):
|
|
|
71
75
|
)
|
|
72
76
|
|
|
73
77
|
@classmethod
|
|
74
|
-
def from_config(cls, config: Dict[str, Any]) ->
|
|
78
|
+
def from_config(cls, config: Dict[str, Any]) -> "SentinelSIEMClient":
|
|
75
79
|
"""Create client from configuration dictionary.
|
|
76
80
|
|
|
77
81
|
Args:
|
|
@@ -81,21 +85,23 @@ class SentinelSIEMClient(SIEMClient):
|
|
|
81
85
|
SentinelSIEMClient instance
|
|
82
86
|
"""
|
|
83
87
|
return cls(
|
|
84
|
-
tenant_id=config.get(
|
|
85
|
-
client_id=config.get(
|
|
86
|
-
client_secret=config.get(
|
|
87
|
-
subscription_id=config.get(
|
|
88
|
-
resource_group=config.get(
|
|
89
|
-
workspace_name=config.get(
|
|
90
|
-
workspace_id=config.get(
|
|
88
|
+
tenant_id=config.get("tenant_id", ""),
|
|
89
|
+
client_id=config.get("client_id", ""),
|
|
90
|
+
client_secret=config.get("client_secret", ""),
|
|
91
|
+
subscription_id=config.get("subscription_id", ""),
|
|
92
|
+
resource_group=config.get("resource_group", ""),
|
|
93
|
+
workspace_name=config.get("workspace_name", ""),
|
|
94
|
+
workspace_id=config.get("workspace_id", ""),
|
|
91
95
|
)
|
|
92
96
|
|
|
93
97
|
@property
|
|
94
98
|
def siem_type(self) -> str:
|
|
95
99
|
"""Return the SIEM type identifier."""
|
|
96
|
-
return
|
|
100
|
+
return "sentinel"
|
|
97
101
|
|
|
98
|
-
def _get_access_token(
|
|
102
|
+
def _get_access_token(
|
|
103
|
+
self, scope: str = "https://api.loganalytics.io/.default"
|
|
104
|
+
) -> str:
|
|
99
105
|
"""Get Azure AD access token.
|
|
100
106
|
|
|
101
107
|
Args:
|
|
@@ -111,22 +117,18 @@ class SentinelSIEMClient(SIEMClient):
|
|
|
111
117
|
|
|
112
118
|
# Request new token
|
|
113
119
|
data = {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
120
|
+
"grant_type": "client_credentials",
|
|
121
|
+
"client_id": self.client_id,
|
|
122
|
+
"client_secret": self.client_secret,
|
|
123
|
+
"scope": scope,
|
|
118
124
|
}
|
|
119
125
|
|
|
120
|
-
response = requests.post(
|
|
121
|
-
self.login_url,
|
|
122
|
-
data=data,
|
|
123
|
-
timeout=30
|
|
124
|
-
)
|
|
126
|
+
response = requests.post(self.login_url, data=data, timeout=30)
|
|
125
127
|
response.raise_for_status()
|
|
126
128
|
|
|
127
129
|
token_data = response.json()
|
|
128
|
-
self._access_token = token_data[
|
|
129
|
-
expires_in = token_data.get(
|
|
130
|
+
self._access_token = token_data["access_token"]
|
|
131
|
+
expires_in = token_data.get("expires_in", 3600)
|
|
130
132
|
self._token_expiry = datetime.now() + timedelta(seconds=expires_in - 60)
|
|
131
133
|
|
|
132
134
|
return self._access_token
|
|
@@ -143,28 +145,28 @@ class SentinelSIEMClient(SIEMClient):
|
|
|
143
145
|
token = self._get_access_token("https://api.loganalytics.io/.default")
|
|
144
146
|
|
|
145
147
|
headers = {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
+
"Authorization": f"Bearer {token}",
|
|
149
|
+
"Content-Type": "application/json",
|
|
148
150
|
}
|
|
149
151
|
|
|
150
152
|
response = requests.post(
|
|
151
153
|
f"{self.log_analytics_url}/query",
|
|
152
154
|
headers=headers,
|
|
153
|
-
json={
|
|
154
|
-
timeout=60
|
|
155
|
+
json={"query": kql},
|
|
156
|
+
timeout=60,
|
|
155
157
|
)
|
|
156
158
|
response.raise_for_status()
|
|
157
159
|
|
|
158
160
|
data = response.json()
|
|
159
|
-
tables = data.get(
|
|
161
|
+
tables = data.get("tables", [])
|
|
160
162
|
|
|
161
163
|
if not tables:
|
|
162
164
|
return []
|
|
163
165
|
|
|
164
166
|
# Convert first table to list of dicts
|
|
165
167
|
table = tables[0]
|
|
166
|
-
columns = [col[
|
|
167
|
-
rows = table.get(
|
|
168
|
+
columns = [col["name"] for col in table.get("columns", [])]
|
|
169
|
+
rows = table.get("rows", [])
|
|
168
170
|
|
|
169
171
|
return [dict(zip(columns, row)) for row in rows]
|
|
170
172
|
|
|
@@ -187,18 +189,14 @@ class SentinelSIEMClient(SIEMClient):
|
|
|
187
189
|
token = self._get_access_token("https://management.azure.com/.default")
|
|
188
190
|
|
|
189
191
|
headers = {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
+
"Authorization": f"Bearer {token}",
|
|
193
|
+
"Content-Type": "application/json",
|
|
192
194
|
}
|
|
193
195
|
|
|
194
196
|
url = f"{self.sentinel_base}{endpoint}?api-version=2023-02-01"
|
|
195
197
|
|
|
196
198
|
return requests.request(
|
|
197
|
-
method=method,
|
|
198
|
-
url=url,
|
|
199
|
-
headers=headers,
|
|
200
|
-
json=json_data,
|
|
201
|
-
timeout=60
|
|
199
|
+
method=method, url=url, headers=headers, json=json_data, timeout=60
|
|
202
200
|
)
|
|
203
201
|
|
|
204
202
|
def test_connection(self) -> SIEMConnectionStatus:
|
|
@@ -214,32 +212,30 @@ class SentinelSIEMClient(SIEMClient):
|
|
|
214
212
|
if result:
|
|
215
213
|
return SIEMConnectionStatus(
|
|
216
214
|
connected=True,
|
|
217
|
-
version=
|
|
218
|
-
siem_type=
|
|
215
|
+
version="Azure Sentinel",
|
|
216
|
+
siem_type="sentinel",
|
|
219
217
|
details={
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
218
|
+
"workspace_id": self.workspace_id,
|
|
219
|
+
"workspace_name": self.workspace_name,
|
|
220
|
+
"subscription": self.subscription_id,
|
|
221
|
+
},
|
|
224
222
|
)
|
|
225
223
|
else:
|
|
226
224
|
return SIEMConnectionStatus(
|
|
227
225
|
connected=False,
|
|
228
|
-
error=
|
|
229
|
-
siem_type=
|
|
226
|
+
error="Query returned no results",
|
|
227
|
+
siem_type="sentinel",
|
|
230
228
|
)
|
|
231
229
|
|
|
232
230
|
except requests.exceptions.ConnectionError as e:
|
|
233
231
|
return SIEMConnectionStatus(
|
|
234
232
|
connected=False,
|
|
235
233
|
error=f"Connection failed: {str(e)}",
|
|
236
|
-
siem_type=
|
|
234
|
+
siem_type="sentinel",
|
|
237
235
|
)
|
|
238
236
|
except Exception as e:
|
|
239
237
|
return SIEMConnectionStatus(
|
|
240
|
-
connected=False,
|
|
241
|
-
error=str(e),
|
|
242
|
-
siem_type='sentinel'
|
|
238
|
+
connected=False, error=str(e), siem_type="sentinel"
|
|
243
239
|
)
|
|
244
240
|
|
|
245
241
|
def get_alerts(
|
|
@@ -250,7 +246,7 @@ class SentinelSIEMClient(SIEMClient):
|
|
|
250
246
|
dest_ip: Optional[str] = None,
|
|
251
247
|
rule_ids: Optional[List[str]] = None,
|
|
252
248
|
search_text: Optional[str] = None,
|
|
253
|
-
limit: int = 100
|
|
249
|
+
limit: int = 100,
|
|
254
250
|
) -> List[SIEMAlert]:
|
|
255
251
|
"""Query alerts from Microsoft Sentinel.
|
|
256
252
|
|
|
@@ -280,21 +276,23 @@ class SentinelSIEMClient(SIEMClient):
|
|
|
280
276
|
|
|
281
277
|
# Rule ID filter
|
|
282
278
|
if rule_ids:
|
|
283
|
-
rule_filter =
|
|
284
|
-
kql_parts.append(f
|
|
279
|
+
rule_filter = " or ".join(f'AlertName == "{r}"' for r in rule_ids)
|
|
280
|
+
kql_parts.append(f"| where {rule_filter}")
|
|
285
281
|
|
|
286
282
|
# Search text
|
|
287
283
|
if search_text:
|
|
288
284
|
kql_parts.append(f'| where * contains "{search_text}"')
|
|
289
285
|
|
|
290
286
|
# Limit and project
|
|
291
|
-
kql_parts.extend(
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
287
|
+
kql_parts.extend(
|
|
288
|
+
[
|
|
289
|
+
f"| take {limit}",
|
|
290
|
+
"| project TimeGenerated, AlertName, AlertSeverity, Description, "
|
|
291
|
+
"Tactics, Entities, ExtendedProperties, ProviderName, SystemAlertId",
|
|
292
|
+
]
|
|
293
|
+
)
|
|
296
294
|
|
|
297
|
-
kql =
|
|
295
|
+
kql = "\n".join(kql_parts)
|
|
298
296
|
|
|
299
297
|
try:
|
|
300
298
|
results = self._log_analytics_query(kql)
|
|
@@ -314,26 +312,24 @@ class SentinelSIEMClient(SIEMClient):
|
|
|
314
312
|
import json
|
|
315
313
|
|
|
316
314
|
# Parse timestamp
|
|
317
|
-
timestamp_str = raw_alert.get(
|
|
315
|
+
timestamp_str = raw_alert.get("TimeGenerated", "")
|
|
318
316
|
try:
|
|
319
|
-
timestamp = datetime.fromisoformat(
|
|
320
|
-
timestamp_str.replace('Z', '+00:00')
|
|
321
|
-
)
|
|
317
|
+
timestamp = datetime.fromisoformat(timestamp_str.replace("Z", "+00:00"))
|
|
322
318
|
except (ValueError, AttributeError):
|
|
323
319
|
timestamp = datetime.now()
|
|
324
320
|
|
|
325
321
|
# Map severity
|
|
326
|
-
severity = self._map_severity(raw_alert.get(
|
|
322
|
+
severity = self._map_severity(raw_alert.get("AlertSeverity", ""))
|
|
327
323
|
|
|
328
324
|
# Parse entities for IPs
|
|
329
325
|
source_ip = None
|
|
330
326
|
dest_ip = None
|
|
331
|
-
entities_str = raw_alert.get(
|
|
327
|
+
entities_str = raw_alert.get("Entities", "[]")
|
|
332
328
|
try:
|
|
333
329
|
entities = json.loads(entities_str) if entities_str else []
|
|
334
330
|
for entity in entities:
|
|
335
|
-
if entity.get(
|
|
336
|
-
addr = entity.get(
|
|
331
|
+
if entity.get("Type") == "ip":
|
|
332
|
+
addr = entity.get("Address", "")
|
|
337
333
|
if not source_ip:
|
|
338
334
|
source_ip = addr
|
|
339
335
|
elif not dest_ip:
|
|
@@ -342,23 +338,27 @@ class SentinelSIEMClient(SIEMClient):
|
|
|
342
338
|
pass
|
|
343
339
|
|
|
344
340
|
# Parse tactics
|
|
345
|
-
tactics_str = raw_alert.get(
|
|
341
|
+
tactics_str = raw_alert.get("Tactics", "")
|
|
346
342
|
mitre_tactics = []
|
|
347
343
|
if tactics_str:
|
|
348
344
|
try:
|
|
349
|
-
mitre_tactics =
|
|
345
|
+
mitre_tactics = (
|
|
346
|
+
json.loads(tactics_str)
|
|
347
|
+
if tactics_str.startswith("[")
|
|
348
|
+
else [tactics_str]
|
|
349
|
+
)
|
|
350
350
|
except json.JSONDecodeError:
|
|
351
351
|
mitre_tactics = [tactics_str] if tactics_str else []
|
|
352
352
|
|
|
353
353
|
return SIEMAlert(
|
|
354
|
-
id=raw_alert.get(
|
|
354
|
+
id=raw_alert.get("SystemAlertId", ""),
|
|
355
355
|
timestamp=timestamp,
|
|
356
|
-
rule_id=raw_alert.get(
|
|
357
|
-
rule_name=raw_alert.get(
|
|
356
|
+
rule_id=raw_alert.get("AlertName", ""),
|
|
357
|
+
rule_name=raw_alert.get("AlertName", ""),
|
|
358
358
|
severity=severity,
|
|
359
359
|
source_ip=source_ip,
|
|
360
360
|
dest_ip=dest_ip,
|
|
361
|
-
description=raw_alert.get(
|
|
361
|
+
description=raw_alert.get("Description", ""),
|
|
362
362
|
raw_data=raw_alert,
|
|
363
363
|
mitre_tactics=mitre_tactics,
|
|
364
364
|
mitre_techniques=[],
|
|
@@ -367,18 +367,16 @@ class SentinelSIEMClient(SIEMClient):
|
|
|
367
367
|
def _map_severity(self, severity: str) -> str:
|
|
368
368
|
"""Map Sentinel severity to normalized severity."""
|
|
369
369
|
severity_lower = str(severity).lower()
|
|
370
|
-
if severity_lower ==
|
|
371
|
-
return
|
|
372
|
-
elif severity_lower ==
|
|
373
|
-
return
|
|
374
|
-
elif severity_lower ==
|
|
375
|
-
return
|
|
376
|
-
return
|
|
370
|
+
if severity_lower == "high":
|
|
371
|
+
return "critical"
|
|
372
|
+
elif severity_lower == "medium":
|
|
373
|
+
return "high"
|
|
374
|
+
elif severity_lower == "low":
|
|
375
|
+
return "medium"
|
|
376
|
+
return "low"
|
|
377
377
|
|
|
378
378
|
def get_rules(
|
|
379
|
-
self,
|
|
380
|
-
rule_ids: Optional[List[str]] = None,
|
|
381
|
-
enabled_only: bool = True
|
|
379
|
+
self, rule_ids: Optional[List[str]] = None, enabled_only: bool = True
|
|
382
380
|
) -> List[SIEMRule]:
|
|
383
381
|
"""Get analytics rules from Microsoft Sentinel.
|
|
384
382
|
|
|
@@ -396,30 +394,30 @@ class SentinelSIEMClient(SIEMClient):
|
|
|
396
394
|
return []
|
|
397
395
|
|
|
398
396
|
data = response.json()
|
|
399
|
-
raw_rules = data.get(
|
|
397
|
+
raw_rules = data.get("value", [])
|
|
400
398
|
|
|
401
399
|
rules = []
|
|
402
400
|
for raw_rule in raw_rules:
|
|
403
|
-
properties = raw_rule.get(
|
|
401
|
+
properties = raw_rule.get("properties", {})
|
|
404
402
|
|
|
405
403
|
# Filter by rule_ids if provided
|
|
406
|
-
rule_id = raw_rule.get(
|
|
404
|
+
rule_id = raw_rule.get("name", "")
|
|
407
405
|
if rule_ids and rule_id not in rule_ids:
|
|
408
406
|
continue
|
|
409
407
|
|
|
410
408
|
# Filter disabled if requested
|
|
411
|
-
enabled = properties.get(
|
|
409
|
+
enabled = properties.get("enabled", True)
|
|
412
410
|
if enabled_only and not enabled:
|
|
413
411
|
continue
|
|
414
412
|
|
|
415
413
|
# Extract MITRE tactics
|
|
416
|
-
mitre_tactics = properties.get(
|
|
414
|
+
mitre_tactics = properties.get("tactics", [])
|
|
417
415
|
|
|
418
416
|
rule = SIEMRule(
|
|
419
417
|
id=rule_id,
|
|
420
|
-
name=properties.get(
|
|
421
|
-
description=properties.get(
|
|
422
|
-
severity=self._map_severity(properties.get(
|
|
418
|
+
name=properties.get("displayName", ""),
|
|
419
|
+
description=properties.get("description", ""),
|
|
420
|
+
severity=self._map_severity(properties.get("severity", "")),
|
|
423
421
|
enabled=enabled,
|
|
424
422
|
mitre_tactics=mitre_tactics,
|
|
425
423
|
mitre_techniques=[],
|
|
@@ -443,25 +441,25 @@ class SentinelSIEMClient(SIEMClient):
|
|
|
443
441
|
"""
|
|
444
442
|
# Sentinel content hub rule templates
|
|
445
443
|
recommendations_map = {
|
|
446
|
-
|
|
444
|
+
"nmap": [
|
|
447
445
|
{
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
446
|
+
"rule_id": "port-scan-detection",
|
|
447
|
+
"rule_name": "Potential Port Scan Detection",
|
|
448
|
+
"kql": 'AzureFirewall | where Action == "Deny" | summarize count() by SourceIP | where count_ > 100',
|
|
451
449
|
},
|
|
452
450
|
],
|
|
453
|
-
|
|
451
|
+
"hydra": [
|
|
454
452
|
{
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
453
|
+
"rule_id": "brute-force-detection",
|
|
454
|
+
"rule_name": "Brute Force Attack Detection",
|
|
455
|
+
"kql": "SigninLogs | where ResultType != 0 | summarize count() by IPAddress | where count_ > 10",
|
|
458
456
|
},
|
|
459
457
|
],
|
|
460
|
-
|
|
458
|
+
"sqlmap": [
|
|
461
459
|
{
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
460
|
+
"rule_id": "sql-injection-detection",
|
|
461
|
+
"rule_name": "SQL Injection Attack Detection",
|
|
462
|
+
"kql": 'AzureDiagnostics | where Category == "SQLSecurityAuditEvents" | where action_name_s == "BATCH COMPLETED"',
|
|
465
463
|
},
|
|
466
464
|
],
|
|
467
465
|
}
|
|
@@ -471,13 +469,13 @@ class SentinelSIEMClient(SIEMClient):
|
|
|
471
469
|
|
|
472
470
|
return [
|
|
473
471
|
{
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
472
|
+
"rule_id": r["rule_id"],
|
|
473
|
+
"rule_name": r["rule_name"],
|
|
474
|
+
"description": f"Sentinel analytics rule for {attack_type}",
|
|
475
|
+
"severity": "high",
|
|
476
|
+
"enabled": True,
|
|
477
|
+
"siem_type": "sentinel",
|
|
478
|
+
"kql_query": r.get("kql", ""),
|
|
481
479
|
}
|
|
482
480
|
for r in recommendations
|
|
483
481
|
]
|