howler-sentinel-plugin 0.2.0.dev170__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.
- howler_sentinel_plugin-0.2.0.dev170.dist-info/METADATA +23 -0
- howler_sentinel_plugin-0.2.0.dev170.dist-info/RECORD +21 -0
- howler_sentinel_plugin-0.2.0.dev170.dist-info/WHEEL +4 -0
- howler_sentinel_plugin-0.2.0.dev170.dist-info/entry_points.txt +3 -0
- howler_sentinel_plugin-0.2.0.dev170.dist-info/licenses/LICENSE +21 -0
- sentinel/__init__.py +0 -0
- sentinel/actions/azure_emit_hash.py +124 -0
- sentinel/actions/send_to_sentinel.py +141 -0
- sentinel/actions/update_defender_xdr_alert.py +194 -0
- sentinel/actions/update_defender_xdr_incident.py +194 -0
- sentinel/config.py +70 -0
- sentinel/manifest.yml +13 -0
- sentinel/mapping/sentinel_incident.py +240 -0
- sentinel/mapping/xdr_alert.py +415 -0
- sentinel/mapping/xdr_alert_evidence.py +782 -0
- sentinel/odm/__init__.py +0 -0
- sentinel/odm/hit.py +33 -0
- sentinel/odm/models/sentinel.py +15 -0
- sentinel/routes/__init__.py +0 -0
- sentinel/routes/ingest.py +260 -0
- sentinel/utils/tenant_utils.py +56 -0
|
@@ -0,0 +1,782 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
from uuid import uuid4
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def default_unknown_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
6
|
+
"""Default function to handle unknown evidence types."""
|
|
7
|
+
return {}
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class XDRAlertEvidence:
|
|
11
|
+
"""Parse graph evidence into howler evidence format."""
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
def amazon_resource_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
15
|
+
"""Convert Amazon resource evidence to howler evidence format."""
|
|
16
|
+
return {
|
|
17
|
+
"aws": {
|
|
18
|
+
"account": {
|
|
19
|
+
"id": evidence.get("amazonAccountId"),
|
|
20
|
+
},
|
|
21
|
+
"resource": {
|
|
22
|
+
"id": evidence.get("amazonResourceId"),
|
|
23
|
+
"type": evidence.get("resourceType"),
|
|
24
|
+
"name": evidence.get("resourceName"),
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def analyzed_message_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
31
|
+
"""Convert analyzed message evidence to howler evidence format."""
|
|
32
|
+
sender = [
|
|
33
|
+
evidence.get("p1Sender", {}).get("emailAddress", "Unknown"),
|
|
34
|
+
evidence.get("p2Sender", {}).get("emailAddress", "Unknown"),
|
|
35
|
+
]
|
|
36
|
+
if not sender:
|
|
37
|
+
sender = []
|
|
38
|
+
return {
|
|
39
|
+
"email": {
|
|
40
|
+
"direction": evidence.get("antiSpamDirection"),
|
|
41
|
+
"local_id": evidence.get("networkMessageId"),
|
|
42
|
+
"to": evidence.get("recipientEmailAddress"),
|
|
43
|
+
"sender": ", ".join(sender),
|
|
44
|
+
"subject": evidence.get("subject"),
|
|
45
|
+
},
|
|
46
|
+
"host": {"ip": [evidence.get("senderIp")]},
|
|
47
|
+
"url": {
|
|
48
|
+
"full": ", ".join(evidence.get("urls", [])),
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def azure_resource_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
54
|
+
"""Convert Azure resource evidence to howler evidence format."""
|
|
55
|
+
return {
|
|
56
|
+
"azure": {
|
|
57
|
+
"resource": [
|
|
58
|
+
{
|
|
59
|
+
"resource_id": evidence.get("resourceId"),
|
|
60
|
+
"resource_type": evidence.get("resourceType"),
|
|
61
|
+
"resource_name": evidence.get("resourceName"),
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@staticmethod
|
|
68
|
+
def blob_container_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
69
|
+
"""Convert blob container evidence to howler evidence format."""
|
|
70
|
+
resource = evidence.get("storageResource", {})
|
|
71
|
+
_hashes = evidence.get("fileHashes", {})
|
|
72
|
+
if not resource:
|
|
73
|
+
resource = {}
|
|
74
|
+
if not _hashes:
|
|
75
|
+
_hashes = {}
|
|
76
|
+
return {
|
|
77
|
+
"Azure": {
|
|
78
|
+
"resource": [
|
|
79
|
+
{
|
|
80
|
+
"resource_id": resource.get("resourceId"),
|
|
81
|
+
"resource_type": resource.get("resourceType"),
|
|
82
|
+
"resource_name": resource.get("resourceName"),
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
"file": {
|
|
87
|
+
"created": evidence.get("createdDateTime"),
|
|
88
|
+
"hash": {
|
|
89
|
+
"sha1": _hashes.get("sha1"),
|
|
90
|
+
"sha256": _hashes.get("sha256"),
|
|
91
|
+
"md5": _hashes.get("md5"),
|
|
92
|
+
},
|
|
93
|
+
"name": evidence.get("name"),
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@staticmethod
|
|
98
|
+
def blob_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
99
|
+
"""Convert blob evidence to howler evidence format."""
|
|
100
|
+
container_evidence = evidence.get("blobContainer", {})
|
|
101
|
+
resource = container_evidence.get("storageResource", {})
|
|
102
|
+
_hashes = evidence.get("fileHashes", {})
|
|
103
|
+
if not container_evidence:
|
|
104
|
+
container_evidence = {}
|
|
105
|
+
if not resource:
|
|
106
|
+
resource = {}
|
|
107
|
+
if not _hashes:
|
|
108
|
+
_hashes = {}
|
|
109
|
+
return {
|
|
110
|
+
"Azure": {
|
|
111
|
+
"resource": [
|
|
112
|
+
{
|
|
113
|
+
"resource_id": resource.get("resourceId"),
|
|
114
|
+
"resource_type": resource.get("resourceType"),
|
|
115
|
+
"resource_name": resource.get("resourceName"),
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
},
|
|
119
|
+
"file": {
|
|
120
|
+
"created": evidence.get("createdDateTime"),
|
|
121
|
+
"hash": {
|
|
122
|
+
"sha1": _hashes.get("sha1"),
|
|
123
|
+
"sha256": _hashes.get("sha256"),
|
|
124
|
+
"md5": _hashes.get("md5"),
|
|
125
|
+
},
|
|
126
|
+
"name": evidence.get("name"),
|
|
127
|
+
},
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@staticmethod
|
|
131
|
+
def cloud_application_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
132
|
+
"""Convert cloud application evidence to howler evidence format."""
|
|
133
|
+
return {
|
|
134
|
+
"cloud": {
|
|
135
|
+
"project": {
|
|
136
|
+
"id": str(evidence.get("appId")),
|
|
137
|
+
"name": evidence.get("displayName"),
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
@staticmethod
|
|
143
|
+
def cloud_logon_session_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
144
|
+
"""Convert cloud logon session evidence to howler evidence format."""
|
|
145
|
+
account = evidence.get("account", {})
|
|
146
|
+
user = account.get("userAccount", {})
|
|
147
|
+
if not account:
|
|
148
|
+
account = {}
|
|
149
|
+
if not user:
|
|
150
|
+
user = {}
|
|
151
|
+
return {
|
|
152
|
+
"host": {
|
|
153
|
+
"name": evidence.get("deviceName"),
|
|
154
|
+
"os": {
|
|
155
|
+
"full": evidence.get("operatingSystem"),
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
"network": {
|
|
159
|
+
"protocol": evidence.get("protocol"),
|
|
160
|
+
},
|
|
161
|
+
"user": {
|
|
162
|
+
"name": user.get("accountName"),
|
|
163
|
+
"full_name": user.get("userPrincipalName"),
|
|
164
|
+
"id": user.get("azureAdUserId"),
|
|
165
|
+
"domain": user.get("domainName"),
|
|
166
|
+
},
|
|
167
|
+
"user_agent": {
|
|
168
|
+
"original": evidence.get("userAgent"),
|
|
169
|
+
},
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
@staticmethod
|
|
173
|
+
def container_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
174
|
+
"""Convert container evidence to howler evidence format."""
|
|
175
|
+
image = evidence.get("image", {})
|
|
176
|
+
pod = evidence.get("pod", {})
|
|
177
|
+
containers = pod.get("containers", [])
|
|
178
|
+
if not image:
|
|
179
|
+
image = {}
|
|
180
|
+
if not pod:
|
|
181
|
+
pod = {}
|
|
182
|
+
if not containers:
|
|
183
|
+
containers = []
|
|
184
|
+
return {
|
|
185
|
+
"container": {
|
|
186
|
+
"id": evidence.get("containerId"),
|
|
187
|
+
"image": evidence.get("name"),
|
|
188
|
+
},
|
|
189
|
+
"image": {"name": image.get("digestImage")},
|
|
190
|
+
"kubernetes": {
|
|
191
|
+
"pod": {
|
|
192
|
+
"containers": [
|
|
193
|
+
{
|
|
194
|
+
"name": container.get("name"),
|
|
195
|
+
"image": container.get("image"),
|
|
196
|
+
"id": container.get("containerId"),
|
|
197
|
+
"digest": container.get("digestImage"),
|
|
198
|
+
}
|
|
199
|
+
for container in containers
|
|
200
|
+
],
|
|
201
|
+
"controller": pod.get("controller"),
|
|
202
|
+
"ephemeral_containers": pod.get("ephemeralContainers"),
|
|
203
|
+
"init_containers": pod.get("initContainers"),
|
|
204
|
+
"labels": pod.get("labels"),
|
|
205
|
+
"name": pod.get("name"),
|
|
206
|
+
"namespace": pod.get("namespace"),
|
|
207
|
+
"pod_ip": pod.get("podIp"),
|
|
208
|
+
"service_account": {
|
|
209
|
+
"name": pod.get("serviceAccountName"),
|
|
210
|
+
"namespace": pod.get("namespace"),
|
|
211
|
+
},
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
@staticmethod
|
|
217
|
+
def container_image_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
218
|
+
"""Convert container image evidence to howler evidence format."""
|
|
219
|
+
return {
|
|
220
|
+
"image": {"name": evidence.get("digestImage")},
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
@staticmethod
|
|
224
|
+
def container_registry_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
225
|
+
"""Convert container registry evidence to howler evidence format."""
|
|
226
|
+
return {
|
|
227
|
+
"container": {
|
|
228
|
+
"registry": evidence.get("registry"),
|
|
229
|
+
},
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
@staticmethod
|
|
233
|
+
def device_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
234
|
+
"""Convert device evidence to howler evidence format."""
|
|
235
|
+
hostname = evidence.get("hostName")
|
|
236
|
+
if not hostname:
|
|
237
|
+
hostname = evidence.get("deviceDnsName")
|
|
238
|
+
return {
|
|
239
|
+
"host": {
|
|
240
|
+
"id": evidence.get("mdeDeviceId"),
|
|
241
|
+
"name": hostname,
|
|
242
|
+
"domain": [evidence.get("ntDomain", "unknown")],
|
|
243
|
+
"os": {
|
|
244
|
+
"platform": evidence.get("osPlatform"),
|
|
245
|
+
"version": str(evidence.get("osBuild")),
|
|
246
|
+
},
|
|
247
|
+
"logged_on_users": [
|
|
248
|
+
{
|
|
249
|
+
"name": user.get("accountName"),
|
|
250
|
+
"domain": user.get("domainName"),
|
|
251
|
+
}
|
|
252
|
+
for user in evidence.get("loggedOnUsers", [])
|
|
253
|
+
],
|
|
254
|
+
"risk_score": evidence.get("riskScore"),
|
|
255
|
+
"status_av": evidence.get("defenderAvStatus"),
|
|
256
|
+
"status_health": evidence.get("healthStatus"),
|
|
257
|
+
"status_onboarding": evidence.get("onboardingStatus"),
|
|
258
|
+
},
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
@staticmethod
|
|
262
|
+
def file_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
263
|
+
"""Convert file evidence to howler evidence format."""
|
|
264
|
+
file_details = evidence.get("fileDetails", {})
|
|
265
|
+
if not file_details:
|
|
266
|
+
file_details = {}
|
|
267
|
+
return {
|
|
268
|
+
"file": {
|
|
269
|
+
"created": evidence.get("createdDateTime"),
|
|
270
|
+
"hash": {
|
|
271
|
+
"sha1": file_details.get("sha1"),
|
|
272
|
+
"sha256": file_details.get("sha256"),
|
|
273
|
+
"md5": file_details.get("md5"),
|
|
274
|
+
},
|
|
275
|
+
"name": file_details.get("fileName"),
|
|
276
|
+
"path": file_details.get("filePath"),
|
|
277
|
+
"size": file_details.get("fileSize"),
|
|
278
|
+
"code_signature": {
|
|
279
|
+
"signing_id": file_details.get("signer"),
|
|
280
|
+
"team_id": file_details.get("issuer"),
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
@staticmethod
|
|
286
|
+
def google_cloud_resource_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
287
|
+
"""Convert Google Cloud resource evidence to howler evidence format."""
|
|
288
|
+
return {
|
|
289
|
+
"gcp": {
|
|
290
|
+
"zone": evidence.get("location"),
|
|
291
|
+
"zone_type": evidence.get("locationType"),
|
|
292
|
+
"project_id": evidence.get("projectId"),
|
|
293
|
+
"project_number": evidence.get("projectNumber"),
|
|
294
|
+
"resource_id": evidence.get("resourceName"),
|
|
295
|
+
"resource_type": evidence.get("resourceType"),
|
|
296
|
+
},
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
@staticmethod
|
|
300
|
+
def iot_device_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
301
|
+
"""Convert IoT device evidence to howler evidence format."""
|
|
302
|
+
return {
|
|
303
|
+
"iot": {
|
|
304
|
+
"hub": evidence.get("iotHub"),
|
|
305
|
+
"id": evidence.get("deviceId"),
|
|
306
|
+
"name": evidence.get("deviceName"),
|
|
307
|
+
"owners": evidence.get("owners"),
|
|
308
|
+
"ioTSecurityAgentId": evidence.get("securityAgentId"),
|
|
309
|
+
"deviceType": evidence.get("deviceType"),
|
|
310
|
+
"source": evidence.get("source"),
|
|
311
|
+
"source_ref": evidence.get("sourceRef"),
|
|
312
|
+
"manufacturer": evidence.get("manufacturer"),
|
|
313
|
+
"model": evidence.get("model"),
|
|
314
|
+
"serial": evidence.get("serialNumber"),
|
|
315
|
+
"site": evidence.get("site"),
|
|
316
|
+
"zone": evidence.get("zone"),
|
|
317
|
+
"sensor": evidence.get("sensor"),
|
|
318
|
+
"importance": evidence.get("importance"),
|
|
319
|
+
"purdue_layer": evidence.get("purdueLayer"),
|
|
320
|
+
"is_programming": evidence.get("isProgramming"),
|
|
321
|
+
"is_authorized": evidence.get("isAuthorized"),
|
|
322
|
+
"is_scanner": evidence.get("isScanner"),
|
|
323
|
+
"device_link": evidence.get("devicePageLink"),
|
|
324
|
+
"device_subtype": evidence.get("deviceSubtype"),
|
|
325
|
+
},
|
|
326
|
+
"host": {
|
|
327
|
+
"ip": evidence.get("ipAddress"),
|
|
328
|
+
"mac": evidence.get("macAddress"),
|
|
329
|
+
"os": {
|
|
330
|
+
"full": evidence.get("operatingSystem"),
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
@staticmethod
|
|
336
|
+
def ip_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
337
|
+
"""Convert IP evidence to howler evidence format."""
|
|
338
|
+
return {
|
|
339
|
+
"host": {
|
|
340
|
+
"ip": [evidence.get("ipAddress")],
|
|
341
|
+
"geo": {
|
|
342
|
+
"country_iso_code": evidence.get("countryCode"),
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
@staticmethod
|
|
348
|
+
def kubernetes_cluster_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
349
|
+
"""Convert Kubernetes cluster evidence to howler evidence format."""
|
|
350
|
+
return {
|
|
351
|
+
"kubernetes": {
|
|
352
|
+
"namespace": {
|
|
353
|
+
"cluster": {
|
|
354
|
+
"cloud_resource": evidence.get("cloudResource"),
|
|
355
|
+
"distribution": evidence.get("distribution"),
|
|
356
|
+
"name": evidence.get("name"),
|
|
357
|
+
"platform": evidence.get("platform"),
|
|
358
|
+
"version": evidence.get("version"),
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
@staticmethod
|
|
365
|
+
def kubernetes_controller_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
366
|
+
"""Convert Kubernetes controller evidence to howler evidence format."""
|
|
367
|
+
return {
|
|
368
|
+
"kubernetes": {
|
|
369
|
+
"pod": {
|
|
370
|
+
"controller": {
|
|
371
|
+
"labels": evidence.get("labels"),
|
|
372
|
+
"name": evidence.get("name"),
|
|
373
|
+
"namespace": evidence.get("namespace"),
|
|
374
|
+
"controller_type": evidence.get("type"),
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
@staticmethod
|
|
381
|
+
def kubernetes_namespace_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
382
|
+
"""Convert Kubernetes namespace evidence to howler evidence format."""
|
|
383
|
+
return {
|
|
384
|
+
"kubernetes": {
|
|
385
|
+
"namespace": {
|
|
386
|
+
"cluster": {
|
|
387
|
+
"cloud_resource": evidence.get("cloudResource"),
|
|
388
|
+
"distribution": evidence.get("distribution"),
|
|
389
|
+
"name": evidence.get("name"),
|
|
390
|
+
"platform": evidence.get("platform"),
|
|
391
|
+
"version": evidence.get("version"),
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
"labels": evidence.get("labels"),
|
|
395
|
+
"name": evidence.get("name"),
|
|
396
|
+
},
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
@staticmethod
|
|
400
|
+
def kubernetes_pod_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
401
|
+
"""Convert Kubernetes pod evidence to howler evidence format."""
|
|
402
|
+
return {
|
|
403
|
+
"kubernetes": {
|
|
404
|
+
"controller": {
|
|
405
|
+
"labels": evidence.get("labels"),
|
|
406
|
+
"name": evidence.get("name"),
|
|
407
|
+
"namespace": evidence.get("namespace"),
|
|
408
|
+
"controller_type": evidence.get("type"),
|
|
409
|
+
"ephemeral_containers": [
|
|
410
|
+
{
|
|
411
|
+
"id": container.get("containerId"),
|
|
412
|
+
"args": container.get("args"),
|
|
413
|
+
"command": container.get("command"),
|
|
414
|
+
"name": container.get("name"),
|
|
415
|
+
}
|
|
416
|
+
for container in evidence.get("ephemeralContainers", [])
|
|
417
|
+
],
|
|
418
|
+
"init_containers": [
|
|
419
|
+
{
|
|
420
|
+
"id": container.get("containerId"),
|
|
421
|
+
"args": container.get("args"),
|
|
422
|
+
"command": container.get("command"),
|
|
423
|
+
"name": container.get("name"),
|
|
424
|
+
}
|
|
425
|
+
for container in evidence.get("ephemeralContainers", [])
|
|
426
|
+
],
|
|
427
|
+
"pod_ip": evidence.get("podIp"),
|
|
428
|
+
"service_account": {
|
|
429
|
+
"name": evidence.get("serviceAccountName"),
|
|
430
|
+
"namespace": evidence.get("namespace"),
|
|
431
|
+
},
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
@staticmethod
|
|
437
|
+
def kubernetes_secret_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
438
|
+
"""Convert Kubernetes secret evidence to howler evidence format."""
|
|
439
|
+
return {
|
|
440
|
+
"kubernetes": {
|
|
441
|
+
"secret": {
|
|
442
|
+
"name": evidence.get("name"),
|
|
443
|
+
"namespace": evidence.get("namespace"),
|
|
444
|
+
"secret_type": evidence.get("secretType"),
|
|
445
|
+
}
|
|
446
|
+
},
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
@staticmethod
|
|
450
|
+
def kubernetes_service_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
451
|
+
"""Convert Kubernetes service evidence to howler evidence format."""
|
|
452
|
+
return {
|
|
453
|
+
"kubernetes": {
|
|
454
|
+
"service": {
|
|
455
|
+
"cluster_ip": evidence.get("clusterIp"),
|
|
456
|
+
"external_ips": [x.get("ipAddress") for x in evidence.get("externalIps", [])],
|
|
457
|
+
"labels": evidence.get("labels"),
|
|
458
|
+
"name": evidence.get("name"),
|
|
459
|
+
"namespace": evidence.get("namespace"),
|
|
460
|
+
"selector": evidence.get("selector"),
|
|
461
|
+
"service_ports": [
|
|
462
|
+
{
|
|
463
|
+
"app_protocol": port.get("appProtocol"),
|
|
464
|
+
"name": port.get("name"),
|
|
465
|
+
"node_port": port.get("nodePort"),
|
|
466
|
+
"port": port.get("port"),
|
|
467
|
+
"protocol": port.get("protocol"),
|
|
468
|
+
"target_port": port.get("targetPort"),
|
|
469
|
+
}
|
|
470
|
+
for port in evidence.get("servicePorts", [])
|
|
471
|
+
],
|
|
472
|
+
"service_type": evidence.get("serviceType"),
|
|
473
|
+
}
|
|
474
|
+
},
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
@staticmethod
|
|
478
|
+
def kubernetes_service_account_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
479
|
+
"""Convert Kubernetes service account evidence to howler evidence format."""
|
|
480
|
+
return {
|
|
481
|
+
"kubernetes": {
|
|
482
|
+
"pod": {
|
|
483
|
+
"service_account": {
|
|
484
|
+
"name": evidence.get("name"),
|
|
485
|
+
"namespace": evidence.get("namespace"),
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
},
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
@staticmethod
|
|
492
|
+
def mail_cluster_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
493
|
+
"""Convert mail cluster evidence to howler evidence format."""
|
|
494
|
+
return {
|
|
495
|
+
"metadata": {
|
|
496
|
+
"id": str(uuid4()),
|
|
497
|
+
"kind": "evidence",
|
|
498
|
+
"provider": "Alert",
|
|
499
|
+
"rawdata": evidence,
|
|
500
|
+
},
|
|
501
|
+
"mailcluster": {
|
|
502
|
+
"cluster_by": evidence.get("clusterBy"),
|
|
503
|
+
"cluster_by_value": evidence.get("clusterByValue"),
|
|
504
|
+
"email_count": evidence.get("emailCount"),
|
|
505
|
+
"network_message_ids": evidence.get("networkMessageIds"),
|
|
506
|
+
"query": evidence.get("query"),
|
|
507
|
+
"urn": evidence.get("urn"),
|
|
508
|
+
},
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
@staticmethod
|
|
512
|
+
def mailbox_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
513
|
+
"""Convert mailbox evidence to howler evidence format."""
|
|
514
|
+
user = evidence.get("userAccount", {})
|
|
515
|
+
if not user:
|
|
516
|
+
user = {}
|
|
517
|
+
return {
|
|
518
|
+
"metadata": {
|
|
519
|
+
"kind": "evidence",
|
|
520
|
+
"provider": "Alert",
|
|
521
|
+
"rawdata": evidence,
|
|
522
|
+
},
|
|
523
|
+
"email": {
|
|
524
|
+
"id": str(uuid4()),
|
|
525
|
+
"to": evidence.get("primaryAddress"),
|
|
526
|
+
},
|
|
527
|
+
"user": {
|
|
528
|
+
"id": user.get("azureAdUserId"),
|
|
529
|
+
"name": user.get("accountName"),
|
|
530
|
+
"full_name": user.get("userPrincipalName"),
|
|
531
|
+
"domain": user.get("domain"),
|
|
532
|
+
},
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
@staticmethod
|
|
536
|
+
def nic_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
537
|
+
"""Convert NIC evidence to howler evidence format."""
|
|
538
|
+
return {
|
|
539
|
+
"host": {
|
|
540
|
+
"ip": evidence.get("ipAddress"),
|
|
541
|
+
"mac": evidence.get("macAddress"),
|
|
542
|
+
},
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
@staticmethod
|
|
546
|
+
def oauth_application_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
547
|
+
"""Convert OAuth application evidence to howler evidence format."""
|
|
548
|
+
return {
|
|
549
|
+
"metadata": {
|
|
550
|
+
"id": str(uuid4()),
|
|
551
|
+
"kind": "evidence",
|
|
552
|
+
"provider": "Alert",
|
|
553
|
+
"rawdata": evidence,
|
|
554
|
+
},
|
|
555
|
+
"oauth": {
|
|
556
|
+
"app_id": evidence.get("appId"),
|
|
557
|
+
"display_name": evidence.get("displayName"),
|
|
558
|
+
"object_id": evidence.get("objectId"),
|
|
559
|
+
"publisher": evidence.get("publisher"),
|
|
560
|
+
},
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
@staticmethod
|
|
564
|
+
def process_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
565
|
+
"""Convert process evidence to howler evidence format."""
|
|
566
|
+
image_file = evidence.get("imageFile", {})
|
|
567
|
+
parent_image_file = evidence.get("parentImageFile", {})
|
|
568
|
+
if not image_file:
|
|
569
|
+
image_file = {}
|
|
570
|
+
if not parent_image_file:
|
|
571
|
+
parent_image_file = {}
|
|
572
|
+
executable = f"{image_file.get('filePath')}\\{image_file.get('fileName')}"
|
|
573
|
+
return {
|
|
574
|
+
"metadata": {
|
|
575
|
+
"kind": "evidence",
|
|
576
|
+
"provider": "Alert",
|
|
577
|
+
"rawdata": evidence,
|
|
578
|
+
"status_remediation": evidence.get("detectionStatus"),
|
|
579
|
+
},
|
|
580
|
+
"process": {
|
|
581
|
+
"start": evidence.get("processCreationDateTime"),
|
|
582
|
+
"pid": evidence.get("processId"),
|
|
583
|
+
"name": image_file.get("fileName"),
|
|
584
|
+
"hash": {
|
|
585
|
+
"sha1": image_file.get("sha1"),
|
|
586
|
+
"sha256": image_file.get("sha256"),
|
|
587
|
+
"md5": image_file.get("md5"),
|
|
588
|
+
},
|
|
589
|
+
"executable": executable,
|
|
590
|
+
"code_signature": {
|
|
591
|
+
"team_id": image_file.get("issuer"),
|
|
592
|
+
"signing_id": image_file.get("signer"),
|
|
593
|
+
},
|
|
594
|
+
"parent": {
|
|
595
|
+
"start": evidence.get("parentProcessCreationDateTime"),
|
|
596
|
+
"pid": evidence.get("parentProcessId"),
|
|
597
|
+
"name": parent_image_file.get("fileName"),
|
|
598
|
+
"hash": {
|
|
599
|
+
"sha1": parent_image_file.get("sha1"),
|
|
600
|
+
"sha256": parent_image_file.get("sha256"),
|
|
601
|
+
"md5": parent_image_file.get("md5"),
|
|
602
|
+
},
|
|
603
|
+
"executable": parent_image_file.get("filePath"),
|
|
604
|
+
"code_signature": {
|
|
605
|
+
"team_id": parent_image_file.get("issuer"),
|
|
606
|
+
"signing_id": parent_image_file.get("signer"),
|
|
607
|
+
},
|
|
608
|
+
},
|
|
609
|
+
},
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
@staticmethod
|
|
613
|
+
def registry_key_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
614
|
+
"""Convert registry key evidence to howler evidence format."""
|
|
615
|
+
return {
|
|
616
|
+
"metadata": {
|
|
617
|
+
"kind": "evidence",
|
|
618
|
+
"provider": "Alert",
|
|
619
|
+
"rawdata": evidence,
|
|
620
|
+
},
|
|
621
|
+
"key": evidence.get("registryKey"),
|
|
622
|
+
"hive": evidence.get("registryHive"),
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
@staticmethod
|
|
626
|
+
def registry_value_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
627
|
+
"""Convert registry value evidence to howler evidence format."""
|
|
628
|
+
return {
|
|
629
|
+
"metadata": {
|
|
630
|
+
"kind": "evidence",
|
|
631
|
+
"provider": "Alert",
|
|
632
|
+
"rawdata": evidence,
|
|
633
|
+
},
|
|
634
|
+
"registry": {
|
|
635
|
+
"hive": evidence.get("registryHive"),
|
|
636
|
+
"key": evidence.get("registryKey"),
|
|
637
|
+
"value": evidence.get("registryValue"),
|
|
638
|
+
},
|
|
639
|
+
"host": {
|
|
640
|
+
"id": evidence.get("mdeDeviceId"),
|
|
641
|
+
},
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
@staticmethod
|
|
645
|
+
def security_group_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
646
|
+
"""Convert security group evidence to howler evidence format."""
|
|
647
|
+
return {
|
|
648
|
+
"metadata": {
|
|
649
|
+
"kind": "evidence",
|
|
650
|
+
"provider": "Alert",
|
|
651
|
+
"rawdata": evidence,
|
|
652
|
+
},
|
|
653
|
+
"group": {
|
|
654
|
+
"id": evidence.get("securityGroupId"),
|
|
655
|
+
"name": evidence.get("displayName"),
|
|
656
|
+
},
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
@staticmethod
|
|
660
|
+
def teams_message_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
661
|
+
"""Convert Teams message evidence to howler evidence format."""
|
|
662
|
+
return {
|
|
663
|
+
"metadata": {
|
|
664
|
+
"kind": "evidence",
|
|
665
|
+
"provider": "Alert",
|
|
666
|
+
"rawdata": evidence,
|
|
667
|
+
"roles": evidence.get("roles"),
|
|
668
|
+
"tags": evidence.get("tags"),
|
|
669
|
+
"verdict": evidence.get("verdict"),
|
|
670
|
+
"status_remediation": evidence.get("remediationStatus"),
|
|
671
|
+
"status_remediation_details": evidence.get("remediationStatusDetails"),
|
|
672
|
+
},
|
|
673
|
+
"teams": {
|
|
674
|
+
"campaign_id": evidence.get("campaignId"),
|
|
675
|
+
"channel_id": evidence.get("channelId"),
|
|
676
|
+
"delivery_action": evidence.get("deliveryAction"),
|
|
677
|
+
"delivery_location": evidence.get("deliveryLocation"),
|
|
678
|
+
"detailed_roles": evidence.get("detailedRoles"),
|
|
679
|
+
"files": [
|
|
680
|
+
{
|
|
681
|
+
"path": file.get("fileDetails", {}).get("filePath"),
|
|
682
|
+
"name": file.get("fileDetails", {}).get("fileName"),
|
|
683
|
+
"hash": {
|
|
684
|
+
"sha1": file.get("fileDetails", {}).get("sha1"),
|
|
685
|
+
"sha256": file.get("fileDetails", {}).get("sha256"),
|
|
686
|
+
"md5": file.get("fileDetails", {}).get("md5"),
|
|
687
|
+
},
|
|
688
|
+
"code_signature": {
|
|
689
|
+
"signing_id": file.get("fileDetails", {}).get("signer"),
|
|
690
|
+
"team_id": file.get("fileDetails", {}).get("issuer"),
|
|
691
|
+
},
|
|
692
|
+
"size": file.get("fileDetails", {}).get("fileSize"),
|
|
693
|
+
}
|
|
694
|
+
for file in evidence.get("files", [])
|
|
695
|
+
],
|
|
696
|
+
"group_id": evidence.get("groupId"),
|
|
697
|
+
"is_external": evidence.get("isExternal"),
|
|
698
|
+
"is_owned": evidence.get("isOwned"),
|
|
699
|
+
"last_modified": evidence.get("lastModified"),
|
|
700
|
+
"message_direction": evidence.get("messageDirection"),
|
|
701
|
+
"message_id": evidence.get("messageId"),
|
|
702
|
+
"owning_tenant_id": evidence.get("owningTenantId"),
|
|
703
|
+
"parent_message_id": evidence.get("parentMessageId"),
|
|
704
|
+
"received": evidence.get("received"),
|
|
705
|
+
"recipients": evidence.get("recipients"),
|
|
706
|
+
"sender_from_address": evidence.get("senderFromAddress"),
|
|
707
|
+
"sender_ip": evidence.get("senderIp"),
|
|
708
|
+
"source_add_name": evidence.get("sourceAddName"),
|
|
709
|
+
"source_id": evidence.get("sourceId"),
|
|
710
|
+
"subject": evidence.get("subject"),
|
|
711
|
+
"suspicious_recipients": evidence.get("suspiciousRecipients"),
|
|
712
|
+
"thread_id": evidence.get("threadId"),
|
|
713
|
+
"thread_type": evidence.get("threadType"),
|
|
714
|
+
"urls": [
|
|
715
|
+
{
|
|
716
|
+
"full": url.get("url"),
|
|
717
|
+
}
|
|
718
|
+
for url in evidence.get("urls", [])
|
|
719
|
+
],
|
|
720
|
+
},
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
@staticmethod
|
|
724
|
+
def url_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
725
|
+
"""Convert URL evidence to howler evidence format."""
|
|
726
|
+
return {
|
|
727
|
+
"url": {
|
|
728
|
+
"full": evidence.get("url"),
|
|
729
|
+
},
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
@staticmethod
|
|
733
|
+
def user_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
734
|
+
"""Convert user evidence to howler evidence format."""
|
|
735
|
+
user = evidence.get("userAccount", {})
|
|
736
|
+
if not user:
|
|
737
|
+
user = {}
|
|
738
|
+
return {
|
|
739
|
+
"user": {
|
|
740
|
+
"name": user.get("accountName"),
|
|
741
|
+
"full_name": user.get("userPrincipalName"),
|
|
742
|
+
"id": user.get("azureAdUserId"),
|
|
743
|
+
"domain": user.get("domainName"),
|
|
744
|
+
},
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
evidence_function_map = {
|
|
749
|
+
"amazonResourceEvidence": XDRAlertEvidence.amazon_resource_evidence,
|
|
750
|
+
"analyzedMessageEvidence": XDRAlertEvidence.analyzed_message_evidence,
|
|
751
|
+
"azureResourceEvidence": XDRAlertEvidence.azure_resource_evidence,
|
|
752
|
+
"blobContainerEvidence": XDRAlertEvidence.blob_container_evidence,
|
|
753
|
+
"blobEvidence": XDRAlertEvidence.blob_evidence,
|
|
754
|
+
"cloudApplicationEvidence": XDRAlertEvidence.cloud_application_evidence,
|
|
755
|
+
"cloudLogonSessionEvidence": XDRAlertEvidence.cloud_logon_session_evidence,
|
|
756
|
+
"containerEvidence": XDRAlertEvidence.container_evidence,
|
|
757
|
+
"containerImageEvidence": XDRAlertEvidence.container_image_evidence,
|
|
758
|
+
"containerRegistryEvidence": XDRAlertEvidence.container_registry_evidence,
|
|
759
|
+
"deviceEvidence": XDRAlertEvidence.device_evidence,
|
|
760
|
+
"fileEvidence": XDRAlertEvidence.file_evidence,
|
|
761
|
+
"googleCloudResourceEvidence": XDRAlertEvidence.google_cloud_resource_evidence,
|
|
762
|
+
"iotDeviceEvidence": XDRAlertEvidence.iot_device_evidence,
|
|
763
|
+
"ipEvidence": XDRAlertEvidence.ip_evidence,
|
|
764
|
+
"kubernetesClusterEvidence": XDRAlertEvidence.kubernetes_cluster_evidence,
|
|
765
|
+
"kubernetesControllerEvidence": XDRAlertEvidence.kubernetes_controller_evidence,
|
|
766
|
+
"kubernetesNamespaceEvidence": XDRAlertEvidence.kubernetes_namespace_evidence,
|
|
767
|
+
"kubernetesPodEvidence": XDRAlertEvidence.kubernetes_pod_evidence,
|
|
768
|
+
"kubernetesSecretEvidence": XDRAlertEvidence.kubernetes_secret_evidence,
|
|
769
|
+
"kubernetesServiceEvidence": XDRAlertEvidence.kubernetes_service_evidence,
|
|
770
|
+
"kubernetesServiceAccountEvidence": XDRAlertEvidence.kubernetes_service_account_evidence,
|
|
771
|
+
"mailClusterEvidence": XDRAlertEvidence.mail_cluster_evidence,
|
|
772
|
+
"mailboxEvidence": XDRAlertEvidence.mailbox_evidence,
|
|
773
|
+
"nicEvidence": XDRAlertEvidence.nic_evidence,
|
|
774
|
+
"oauthApplicationEvidence": XDRAlertEvidence.oauth_application_evidence,
|
|
775
|
+
"processEvidence": XDRAlertEvidence.process_evidence,
|
|
776
|
+
"registryKeyEvidence": XDRAlertEvidence.registry_key_evidence,
|
|
777
|
+
"registryValueEvidence": XDRAlertEvidence.registry_value_evidence,
|
|
778
|
+
"securityGroupEvidence": XDRAlertEvidence.security_group_evidence,
|
|
779
|
+
"teamsMessageEvidence": XDRAlertEvidence.teams_message_evidence,
|
|
780
|
+
"urlEvidence": XDRAlertEvidence.url_evidence,
|
|
781
|
+
"userEvidence": XDRAlertEvidence.user_evidence,
|
|
782
|
+
}
|