Skip to content

Commit 2d163a2

Browse files
authored
Merge pull request #2 from Microsoft/AddingNewV3Entities
Adding new V3 entities
2 parents 926f79a + 4c6ab85 commit 2d163a2

File tree

6 files changed

+177
-13
lines changed

6 files changed

+177
-13
lines changed

msticpy/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
"""Version file."""
2-
VERSION = "0.0.71"
2+
VERSION = "0.0.73"

msticpy/nbtools/entityschema.py

Lines changed: 147 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ def __init__(self, src_entity=None, **kwargs):
5454
self[k] = ElevationToken(src_entity[k])
5555
elif v == GeoLocation.__name__:
5656
self[k] = GeoLocation(src_entity[k])
57+
elif v == Algorithm.__name__:
58+
self[k] = Algorithm(src_entity[k])
59+
elif isinstance(v, tuple):
60+
entity_list = []
61+
for col_entity in src_entity[k]:
62+
entity_list.append(Entity.instantiate_entity(col_entity))
63+
self[k] = entity_list
5764
else:
5865
self[k] = Entity.instantiate_entity(src_entity[k])
5966
# add AdditionalData dictionary if it's populated
@@ -193,8 +200,14 @@ def instantiate_entity(cls, raw_entity: dict):
193200
elif (raw_entity['Type'] == 'registry-value' or
194201
raw_entity['Type'] == 'registryvalue'):
195202
return RegistryValue(raw_entity)
196-
elif raw_entity['Type'] == 'host-logon-session':
203+
elif (raw_entity['Type'] == 'host-logon-session' or
204+
raw_entity['Type'] == 'hostlogonsession'):
197205
return HostLogonSession(raw_entity)
206+
elif raw_entity['Type'] == 'filehash':
207+
return FileHash(raw_entity)
208+
elif (raw_entity['Type'] == 'security-group' or
209+
raw_entity['Type'] == 'securitygroup'):
210+
return SecurityGroup(raw_entity)
198211
elif (raw_entity['Type'] == 'alerts' or
199212
raw_entity['Type'] == 'alert'):
200213
return Alert(raw_entity)
@@ -213,6 +226,7 @@ def __init__(self, src_entity=None, src_event=None, role='subject', **kwargs):
213226
214227
:param src_entity: instantiate entity using properties of src entity
215228
:param src_event: instantiate entity using properties of src event
229+
:param kwargs: key-value pair representation of entity
216230
"""
217231
# pylint: disable=locally-disabled, C0301
218232
super().__init__(src_entity=src_entity, **kwargs)
@@ -279,6 +293,34 @@ def qualified_name(self) -> str:
279293
}
280294

281295

296+
@export
297+
class SecurityGroup(Entity):
298+
"""SecurityGroup Entity class."""
299+
300+
def __init__(self, src_entity=None, **kwargs):
301+
"""
302+
Create a new instance of the entity type.
303+
304+
:param src_entity: instantiate entity using properties of src entity
305+
:param kwargs: key-value pair representation of entity
306+
"""
307+
super().__init__(src_entity=src_entity, **kwargs)
308+
309+
@property
310+
def description_str(self):
311+
"""Return Entity Description."""
312+
return self.DistinguishedName
313+
314+
_entity_schema = {
315+
# DistinguishedName (type System.String)
316+
'DistinguishedName': None,
317+
# SID (type System.String)
318+
'SID': None,
319+
# ObjectGuid (type System.String)
320+
'ObjectGuid': None,
321+
}
322+
323+
282324
@export
283325
class HostLogonSession(Entity):
284326
"""HostLogonSession Entity class."""
@@ -289,6 +331,7 @@ def __init__(self, src_entity=None, src_event=None, **kwargs):
289331
290332
:param src_entity: instantiate entity using properties of src entity
291333
:param src_event: instantiate entity using properties of src event
334+
:param kwargs: key-value pair representation of entity
292335
"""
293336
super().__init__(src_entity=src_entity, **kwargs)
294337

@@ -328,6 +371,7 @@ def __init__(self, src_entity=None, **kwargs):
328371
Create a new instance of the entity type.
329372
330373
:param src_entity: instantiate entity using properties of src entity
374+
:param kwargs: key-value pair representation of entity
331375
"""
332376
super().__init__(src_entity=src_entity, **kwargs)
333377

@@ -382,6 +426,7 @@ def __init__(self, src_entity=None, src_event=None, role='new', **kwargs):
382426
383427
:param src_entity: instantiate entity using properties of src entity
384428
:param src_event: instantiate entity using properties of src event
429+
:param kwargs: key-value pair representation of entity
385430
"""
386431
super().__init__(src_entity=src_entity, **kwargs)
387432

@@ -426,7 +471,8 @@ def description_str(self) -> str:
426471
# Sha256 (type System.String)
427472
'Sha256': None,
428473
# Sha256Ac (type System.String)
429-
'Sha256Ac': None
474+
'Sha256Ac': None,
475+
'FileHashes': (list, 'FileHash')
430476
}
431477

432478
def _add_paths(self, full_path):
@@ -442,6 +488,42 @@ def _add_paths(self, full_path):
442488
self.Directory = full_path.split(self.PathSeparator)[:-1]
443489

444490

491+
@export
492+
class FileHash(Entity):
493+
"""File Hash class."""
494+
495+
def __init__(self, src_entity=None, **kwargs):
496+
"""
497+
Create a new instance of the entity type.
498+
499+
:param src_entity: instantiate entity using properties of src entity
500+
:param kwargs: key-value pair representation of entity
501+
"""
502+
super().__init__(src_entity=src_entity, **kwargs)
503+
504+
@property
505+
def description_str(self) -> str:
506+
"""Return Entity Description."""
507+
return f'{self.Algorithm}: {self.Value}'
508+
509+
_entity_schema = {
510+
# The hash algorithm (type System.String)
511+
'Algorithm': 'Algorithm',
512+
# Value (type System.String)
513+
'Value': None,
514+
}
515+
516+
517+
@export
518+
class Algorithm(Enum):
519+
"""FileHash Algorithm Enumeration."""
520+
Unknown = 0
521+
MD5 = 1
522+
SHA1 = 2
523+
SHA256 = 3
524+
SHA256AC = 4
525+
526+
445527
@export
446528
class Host(Entity):
447529
"""Host Entity class."""
@@ -514,6 +596,7 @@ def __init__(self, src_entity=None, src_event=None, **kwargs):
514596
515597
:param src_entity: instantiate entity using properties of src entity
516598
:param src_event: instantiate entity using properties of src event
599+
:param kwargs: key-value pair representation of entity
517600
"""
518601
super().__init__(src_entity=src_entity, **kwargs)
519602

@@ -555,6 +638,7 @@ def __init__(self, src_entity=None, **kwargs):
555638
Create a new instance of the entity type.
556639
557640
:param src_entity: instantiate entity using properties of src entity
641+
:param kwargs: key-value pair representation of entity
558642
"""
559643
super().__init__(src_entity=src_entity, **kwargs)
560644

@@ -590,6 +674,7 @@ def __init__(self, src_entity=None, **kwargs):
590674
Create a new instance of the entity type.
591675
592676
:param src_entity: instantiate entity using properties of src entity
677+
:param kwargs: key-value pair representation of entity
593678
"""
594679
super().__init__(src_entity=src_entity, **kwargs)
595680

@@ -604,7 +689,9 @@ def description_str(self) -> str:
604689
# Category (type System.String)
605690
'Category': None,
606691
# File (type Microsoft.Azure.Security.Detection.AlertContracts.V3.Entities.File)
607-
'File': 'File'
692+
'File': 'File',
693+
'Files': (list, 'File'),
694+
'Processes': (list, 'Process'),
608695
}
609696

610697

@@ -617,6 +704,7 @@ def __init__(self, src_entity=None, **kwargs):
617704
Create a new instance of the entity type.
618705
619706
:param src_entity: instantiate entity using properties of src entity
707+
:param kwargs: key-value pair representation of entity
620708
"""
621709
super().__init__(src_entity=src_entity, **kwargs)
622710

@@ -654,6 +742,7 @@ def __init__(self, src_entity=None, src_event=None, role='new', **kwargs):
654742
655743
:param src_entity: instantiate entity using properties of src entity
656744
:param src_event: instantiate entity using properties of src event
745+
:param kwargs: key-value pair representation of entity
657746
"""
658747
super().__init__(src_entity=src_entity, **kwargs)
659748
# pylint: disable=locally-disabled, C0301
@@ -720,7 +809,9 @@ def description_str(self) -> str:
720809
# .V3.Entities.Process)
721810
'ParentProcess': 'Process',
722811
# Host (type Microsoft.Azure.Security.Detection.AlertContracts.V3.Entities.Host)
723-
'Host': 'Host'
812+
'Host': 'Host',
813+
# Host (type Microsoft.Azure.Security.Detection.AlertContracts.V3.Entities.HostLogonSession)
814+
'LogonSession': 'HostLogonSession',
724815
}
725816

726817

@@ -759,6 +850,7 @@ def __init__(self, src_entity=None, **kwargs):
759850
Create a new instance of the entity type.
760851
761852
:param src_entity: instantiate entity using properties of src entity
853+
:param kwargs: key-value pair representation of entity
762854
"""
763855
super().__init__(src_entity=src_entity, **kwargs)
764856

@@ -784,6 +876,7 @@ def __init__(self, src_entity=None, **kwargs):
784876
Create a new instance of the entity type.
785877
786878
:param src_entity: instantiate entity using properties of src entity
879+
:param kwargs: key-value pair representation of entity
787880
"""
788881
super().__init__(src_entity=src_entity, **kwargs)
789882

@@ -830,6 +923,7 @@ def __init__(self, src_entity=None, **kwargs):
830923
Create a new instance of the entity type.
831924
832925
:param src_entity: instantiate entity using properties of src entity
926+
:param kwargs: key-value pair representation of entity
833927
"""
834928
super().__init__(src_entity=src_entity, **kwargs)
835929

@@ -857,6 +951,7 @@ def __init__(self, src_entity=None, **kwargs):
857951
Create a new instance of the entity type.
858952
859953
:param src_entity: instantiate entity using properties of src entity
954+
:param kwargs: key-value pair representation of entity
860955
"""
861956
super().__init__(src_entity=src_entity, **kwargs)
862957

@@ -888,6 +983,54 @@ def description_str(self) -> str:
888983
'ProviderName': None
889984
}
890985

986+
987+
@export
988+
class Threatintelligence(Entity):
989+
"""Threatintelligence Entity class."""
990+
991+
def __init__(self, src_entity=None, **kwargs):
992+
"""
993+
Create a new instance of the entity type.
994+
995+
:param src_entity: instantiate entity using properties of src entity
996+
:param kwargs: key-value pair representation of entity
997+
"""
998+
super().__init__(src_entity=src_entity, **kwargs)
999+
1000+
def description_str(self) -> str:
1001+
"""Return Entity Description."""
1002+
return f'{self.DisplayName} ({self.StartTimeUtc}) {self.CompromisedEntity}'
1003+
1004+
_entity_schema = {
1005+
# String Name of the provider from whom this Threat Intelligence information was received
1006+
'ProviderName': None,
1007+
1008+
'ThreatType': None,
1009+
'ThreatName': None,
1010+
'Confidence': None,
1011+
'ReportLink': None,
1012+
'ThreatDescription': None,
1013+
}
1014+
1015+
@export
1016+
class UnknownEntity(Entity):
1017+
"""Generic Entity class."""
1018+
1019+
def __init__(self, src_entity=None, **kwargs):
1020+
"""
1021+
Create a new instance of the entity type.
1022+
1023+
:param src_entity: instantiate entity using properties of src entity
1024+
:param kwargs: key-value pair representation of entity
1025+
"""
1026+
super().__init__(src_entity=src_entity, **kwargs)
1027+
1028+
def description_str(self) -> str:
1029+
"""Return Entity Description."""
1030+
return 'OtherEntity'
1031+
1032+
_entity_schema = {}
1033+
8911034
# # test code
8921035
# if __name__ == '__main__':
8931036
# import json

msticpy/nbtools/security_alert.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import pandas as pd
1111

1212
from .._version import VERSION
13-
from .entityschema import Entity
13+
from .entityschema import Entity, UnknownEntity
1414
from .security_base import SecurityBase
1515
from .utility import export
1616

@@ -157,7 +157,7 @@ def _extract_entities(self, src_row):
157157
except TypeError:
158158
# if we didn't instantiate a known entity
159159
# just add it as it is
160-
entity = ent
160+
entity = UnknownEntity(**ent)
161161
if '$id' in ent:
162162
self._src_entities[ent['$id']] = entity
163163

msticpy/nbtools/security_alert_graph.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,13 @@ def create_alert_graph(alert: SecurityAlert):
4949
# relationships between entities and child entities
5050
# So if this entity has a property that is an entity, we add an edge to it
5151
# and prune any edge that it might have to the alert
52-
for prop, rel_entity in [(p, v) for (p, v) in entity.properties.items()
52+
if isinstance(entity, Entity):
53+
ent_props = entity.properties
54+
elif isinstance(entity, dict):
55+
ent_props = entity
56+
else:
57+
continue
58+
for prop, rel_entity in [(p, v) for (p, v) in ent_props.items()
5359
if isinstance(v, Entity)]:
5460
if rel_entity['Type'] == 'host':
5561
# don't add a new edge to the host
@@ -204,10 +210,17 @@ def _get_other_name_desc(entity):
204210
else:
205211
e_name = entity['Type']
206212

213+
if isinstance(entity, Entity):
214+
ent_props = entity.properties
215+
elif isinstance(entity, dict):
216+
ent_props = entity
217+
else:
218+
ent_props = {'unknown': None}
219+
207220
# Nasty dict comprehension to join all other items in the dictionary into a string
208221
e_properties = '\n'.join({'{}:{}'.format(k, v) for (k, v)
209-
in entity.properties.items() if (k not in ('Type', 'Name') and
210-
isinstance(v, str))})
222+
in ent_props.items() if (k not in ('Type', 'Name') and
223+
isinstance(v, str))})
211224
e_description = '{}\n{})'.format(e_name, e_properties)
212225
return e_name, e_description
213226

msticpy/nbtools/security_base.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,8 @@ def get_logon_id(self, account=None):
266266
if the account entity is not supplied, return the logon id
267267
of the first host-logon-session or account entity.
268268
"""
269-
for session in [e for e in self.entities if e['Type'] == 'host-logon-session']:
269+
for session in [e for e in self.entities if
270+
e['Type'] == 'host-logon-session' or e['Type'] == 'hostlogonsession']:
270271
if account is None or session['Account'] == account:
271272
return session['SessionId']
272273
if account is None:
@@ -342,7 +343,8 @@ def to_html(self, show_entities=False):
342343
html_doc = html_doc + entity_title + entity_html
343344
else:
344345
e_counts = Counter([ent['Type'] for ent in self.entities])
345-
e_counts_str = ', '.join([f'{e}: {c}' for e, c in e_counts.items()])
346+
e_counts_str = ', '.join(
347+
[f'{e}: {c}' for e, c in e_counts.items()])
346348
html_doc = html_doc + f'<h3>Entity counts: </h3>{e_counts_str}'
347349
return html_doc
348350

msticpy/nbtools/wsconfig.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,17 @@
1111
__version__ = '0.1'
1212
__author__ = 'Ian Hellen'
1313

14-
1514
@export
1615
class WorkspaceConfig(object):
1716
"""Workspace configuration class."""
1817

18+
# Constants
19+
TENANT_ID = "{{cookiecutter.tenant_id}}"
20+
SUBSCRIPTION_ID = "{{cookiecutter.subscription_id}}"
21+
RESOURCE_GROUP = "{{cookiecutter.resource_group}}"
22+
WORKSPACE_ID = "{{cookiecutter.workspace_id}}"
23+
WORKSPACE_NAME = "{{cookiecutter.workspace_name}}"
24+
1925
def __init__(self, config_file: str):
2026
"""
2127
Load current Azure Notebooks configuration for Log Analytics.

0 commit comments

Comments
 (0)