contentctl 5.0.0a3__py3-none-any.whl → 5.0.1__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.
- contentctl/actions/new_content.py +12 -10
- contentctl/objects/abstract_security_content_objects/detection_abstract.py +11 -15
- contentctl/objects/abstract_security_content_objects/security_content_object_abstract.py +53 -10
- contentctl/objects/baseline.py +12 -11
- contentctl/objects/constants.py +3 -0
- contentctl/objects/investigation.py +14 -7
- contentctl/objects/rba.py +55 -4
- contentctl/objects/story.py +11 -7
- contentctl/output/templates/analyticstories_detections.j2 +1 -1
- contentctl/output/templates/analyticstories_stories.j2 +1 -1
- contentctl/output/templates/es_investigations_investigations.j2 +1 -1
- contentctl/output/templates/es_investigations_stories.j2 +1 -1
- contentctl/output/templates/savedsearches_baselines.j2 +2 -2
- contentctl/output/templates/savedsearches_detections.j2 +2 -8
- contentctl/output/templates/savedsearches_investigations.j2 +2 -2
- contentctl/output/templates/transforms.j2 +0 -2
- contentctl/templates/stories/cobalt_strike.yml +1 -0
- {contentctl-5.0.0a3.dist-info → contentctl-5.0.1.dist-info}/METADATA +1 -1
- {contentctl-5.0.0a3.dist-info → contentctl-5.0.1.dist-info}/RECORD +22 -22
- {contentctl-5.0.0a3.dist-info → contentctl-5.0.1.dist-info}/LICENSE.md +0 -0
- {contentctl-5.0.0a3.dist-info → contentctl-5.0.1.dist-info}/WHEEL +0 -0
- {contentctl-5.0.0a3.dist-info → contentctl-5.0.1.dist-info}/entry_points.txt +0 -0
|
@@ -32,6 +32,14 @@ class NewContent:
|
|
|
32
32
|
},
|
|
33
33
|
]
|
|
34
34
|
|
|
35
|
+
DEFAULT_RBA = {
|
|
36
|
+
"message": "Risk Message goes here",
|
|
37
|
+
"risk_objects": [{"field": "dest", "type": "system", "score": 10}],
|
|
38
|
+
"threat_objects": [
|
|
39
|
+
{"field": "parent_process_name", "type": "parent_process_name"}
|
|
40
|
+
],
|
|
41
|
+
}
|
|
42
|
+
|
|
35
43
|
def buildDetection(self) -> tuple[dict[str, Any], str]:
|
|
36
44
|
questions = NewContentQuestions.get_questions_detection()
|
|
37
45
|
answers: dict[str, str] = questionary.prompt(
|
|
@@ -42,8 +50,8 @@ class NewContent:
|
|
|
42
50
|
raise ValueError("User didn't answer one or more questions!")
|
|
43
51
|
|
|
44
52
|
data_source_field = (
|
|
45
|
-
answers["
|
|
46
|
-
if len(answers["
|
|
53
|
+
answers["data_sources"]
|
|
54
|
+
if len(answers["data_sources"]) > 0
|
|
47
55
|
else [f"{NewContent.UPDATE_PREFIX} zero or more data_sources"]
|
|
48
56
|
)
|
|
49
57
|
file_name = (
|
|
@@ -83,19 +91,12 @@ class NewContent:
|
|
|
83
91
|
f"{NewContent.UPDATE_PREFIX} zero or more http references to provide more information about your search"
|
|
84
92
|
],
|
|
85
93
|
"drilldown_searches": NewContent.DEFAULT_DRILLDOWN_DEF,
|
|
86
|
-
"rba":
|
|
87
|
-
"message": "Risk Messages goes here",
|
|
88
|
-
"risk_objects": [],
|
|
89
|
-
"threat_objects": [],
|
|
90
|
-
},
|
|
94
|
+
"rba": NewContent.DEFAULT_RBA,
|
|
91
95
|
"tags": {
|
|
92
96
|
"analytic_story": [
|
|
93
97
|
f"{NewContent.UPDATE_PREFIX} by providing zero or more analytic stories"
|
|
94
98
|
],
|
|
95
99
|
"asset_type": f"{NewContent.UPDATE_PREFIX} by providing and asset type from {list(AssetType._value2member_map_)}",
|
|
96
|
-
"confidence": f"{NewContent.UPDATE_PREFIX} by providing a value between 1-100",
|
|
97
|
-
"impact": f"{NewContent.UPDATE_PREFIX} by providing a value between 1-100",
|
|
98
|
-
"message": f"{NewContent.UPDATE_PREFIX} by providing a risk message. Fields in your search results can be referenced using $fieldName$",
|
|
99
100
|
"mitre_attack_id": mitre_attack_ids,
|
|
100
101
|
"product": [
|
|
101
102
|
"Splunk Enterprise",
|
|
@@ -139,6 +140,7 @@ class NewContent:
|
|
|
139
140
|
del answers["story_name"]
|
|
140
141
|
answers["id"] = str(uuid.uuid4())
|
|
141
142
|
answers["version"] = 1
|
|
143
|
+
answers["status"] = "production"
|
|
142
144
|
answers["date"] = datetime.today().strftime("%Y-%m-%d")
|
|
143
145
|
answers["author"] = answers["story_author"]
|
|
144
146
|
del answers["story_author"]
|
|
@@ -383,21 +383,17 @@ class Detection_Abstract(SecurityContentObject):
|
|
|
383
383
|
@computed_field
|
|
384
384
|
@property
|
|
385
385
|
def risk(self) -> list[dict[str, Any]]:
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
threat_object["threat_object_field"] = entity.field
|
|
398
|
-
threat_object["threat_object_type"] = entity.type
|
|
399
|
-
risk_objects.append(threat_object)
|
|
400
|
-
return risk_objects
|
|
386
|
+
if self.rba is None:
|
|
387
|
+
raise Exception(
|
|
388
|
+
f"Attempting to serialize rba section of [{self.name}], however RBA section is None"
|
|
389
|
+
)
|
|
390
|
+
"""
|
|
391
|
+
action.risk.param._risk
|
|
392
|
+
of the conf file only contains a list of dicts. We do not eant to
|
|
393
|
+
include the message here, so we do not return it.
|
|
394
|
+
"""
|
|
395
|
+
rba_dict = self.rba.model_dump()
|
|
396
|
+
return rba_dict["risk_objects"] + rba_dict["threat_objects"]
|
|
401
397
|
|
|
402
398
|
@computed_field
|
|
403
399
|
@property
|
|
@@ -1,31 +1,39 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Self
|
|
3
4
|
|
|
4
5
|
if TYPE_CHECKING:
|
|
6
|
+
from contentctl.input.director import DirectorOutputDto
|
|
5
7
|
from contentctl.objects.deployment import Deployment
|
|
6
8
|
from contentctl.objects.security_content_object import SecurityContentObject
|
|
7
|
-
from contentctl.input.director import DirectorOutputDto
|
|
8
9
|
|
|
9
|
-
from contentctl.objects.enums import AnalyticsType
|
|
10
|
-
from contentctl.objects.constants import CONTENTCTL_MAX_STANZA_LENGTH
|
|
11
10
|
import abc
|
|
12
|
-
import uuid
|
|
13
11
|
import datetime
|
|
12
|
+
import pathlib
|
|
14
13
|
import pprint
|
|
14
|
+
import uuid
|
|
15
|
+
from functools import cached_property
|
|
16
|
+
from typing import List, Optional, Tuple, Union
|
|
17
|
+
|
|
15
18
|
from pydantic import (
|
|
16
19
|
BaseModel,
|
|
17
|
-
|
|
20
|
+
ConfigDict,
|
|
18
21
|
Field,
|
|
19
|
-
ValidationInfo,
|
|
20
22
|
FilePath,
|
|
21
23
|
HttpUrl,
|
|
22
24
|
NonNegativeInt,
|
|
23
|
-
|
|
25
|
+
ValidationInfo,
|
|
26
|
+
computed_field,
|
|
27
|
+
field_validator,
|
|
24
28
|
model_serializer,
|
|
25
29
|
)
|
|
26
|
-
from typing import Tuple, Optional, List, Union
|
|
27
|
-
import pathlib
|
|
28
30
|
|
|
31
|
+
from contentctl.objects.constants import (
|
|
32
|
+
CONTENTCTL_MAX_STANZA_LENGTH,
|
|
33
|
+
DEPRECATED_TEMPLATE,
|
|
34
|
+
EXPERIMENTAL_TEMPLATE,
|
|
35
|
+
)
|
|
36
|
+
from contentctl.objects.enums import AnalyticsType, DetectionStatus
|
|
29
37
|
|
|
30
38
|
NO_FILE_NAME = "NO_FILE_NAME"
|
|
31
39
|
|
|
@@ -44,6 +52,41 @@ class SecurityContentObject_Abstract(BaseModel, abc.ABC):
|
|
|
44
52
|
def model_post_init(self, __context: Any) -> None:
|
|
45
53
|
self.ensureFileNameMatchesSearchName()
|
|
46
54
|
|
|
55
|
+
@computed_field
|
|
56
|
+
@cached_property
|
|
57
|
+
def status_aware_description(self) -> str:
|
|
58
|
+
"""We need to be able to write out a description that includes information
|
|
59
|
+
about whether or not a detection has been deprecated or not. This is important
|
|
60
|
+
for providing information to the user as well as powering the deprecation
|
|
61
|
+
assistant dashboad(s). Make sure this information is output correctly, if
|
|
62
|
+
appropriate.
|
|
63
|
+
Otherwise, if a detection is not deprecated or experimental, just return th
|
|
64
|
+
unmodified description.
|
|
65
|
+
|
|
66
|
+
Raises:
|
|
67
|
+
NotImplementedError: This content type does not support status_aware_description.
|
|
68
|
+
This is because the object does not define a status field
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
str: description, which may or may not be prefixed with the deprecation/experimental message
|
|
72
|
+
"""
|
|
73
|
+
status = getattr(self, "status", None)
|
|
74
|
+
|
|
75
|
+
if not isinstance(status, DetectionStatus):
|
|
76
|
+
raise NotImplementedError(
|
|
77
|
+
f"Detection status is not implemented for [{self.name}] of type '{type(self).__name__}'"
|
|
78
|
+
)
|
|
79
|
+
if status == DetectionStatus.experimental:
|
|
80
|
+
return EXPERIMENTAL_TEMPLATE.format(
|
|
81
|
+
content_type=type(self).__name__, description=self.description
|
|
82
|
+
)
|
|
83
|
+
elif status == DetectionStatus.deprecated:
|
|
84
|
+
return DEPRECATED_TEMPLATE.format(
|
|
85
|
+
content_type=type(self).__name__, description=self.description
|
|
86
|
+
)
|
|
87
|
+
else:
|
|
88
|
+
return self.description
|
|
89
|
+
|
|
47
90
|
@model_serializer
|
|
48
91
|
def serialize_model(self):
|
|
49
92
|
return {
|
contentctl/objects/baseline.py
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Annotated, Any, List, Literal
|
|
3
4
|
|
|
4
5
|
if TYPE_CHECKING:
|
|
5
6
|
from contentctl.input.director import DirectorOutputDto
|
|
6
7
|
|
|
7
8
|
from pydantic import (
|
|
8
|
-
field_validator,
|
|
9
|
-
ValidationInfo,
|
|
10
9
|
Field,
|
|
11
|
-
|
|
10
|
+
ValidationInfo,
|
|
12
11
|
computed_field,
|
|
12
|
+
field_validator,
|
|
13
|
+
model_serializer,
|
|
13
14
|
)
|
|
14
|
-
from contentctl.objects.deployment import Deployment
|
|
15
|
-
from contentctl.objects.security_content_object import SecurityContentObject
|
|
16
|
-
from contentctl.objects.enums import DataModel
|
|
17
|
-
from contentctl.objects.baseline_tags import BaselineTags
|
|
18
15
|
|
|
16
|
+
from contentctl.objects.baseline_tags import BaselineTags
|
|
19
17
|
from contentctl.objects.config import CustomApp
|
|
20
|
-
|
|
21
|
-
from contentctl.objects.lookup import Lookup
|
|
22
18
|
from contentctl.objects.constants import (
|
|
23
|
-
CONTENTCTL_MAX_SEARCH_NAME_LENGTH,
|
|
24
19
|
CONTENTCTL_BASELINE_STANZA_NAME_FORMAT_TEMPLATE,
|
|
20
|
+
CONTENTCTL_MAX_SEARCH_NAME_LENGTH,
|
|
25
21
|
)
|
|
22
|
+
from contentctl.objects.deployment import Deployment
|
|
23
|
+
from contentctl.objects.enums import DataModel, DetectionStatus
|
|
24
|
+
from contentctl.objects.lookup import Lookup
|
|
25
|
+
from contentctl.objects.security_content_object import SecurityContentObject
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
class Baseline(SecurityContentObject):
|
|
@@ -35,6 +35,7 @@ class Baseline(SecurityContentObject):
|
|
|
35
35
|
lookups: list[Lookup] = Field([], validate_default=True)
|
|
36
36
|
# enrichment
|
|
37
37
|
deployment: Deployment = Field({})
|
|
38
|
+
status: Literal[DetectionStatus.production, DetectionStatus.deprecated]
|
|
38
39
|
|
|
39
40
|
@field_validator("lookups", mode="before")
|
|
40
41
|
@classmethod
|
contentctl/objects/constants.py
CHANGED
|
@@ -144,3 +144,6 @@ CONTENTCTL_MAX_SEARCH_NAME_LENGTH = CONTENTCTL_MAX_STANZA_LENGTH - len(
|
|
|
144
144
|
app_label="ESCU", detection_name=""
|
|
145
145
|
)
|
|
146
146
|
)
|
|
147
|
+
|
|
148
|
+
DEPRECATED_TEMPLATE = "**WARNING**, this {content_type} has been marked **DEPRECATED** by the Splunk Threat Research Team. This means that it will no longer be maintained or supported. If you have any questions feel free to email us at: research@splunk.com. {description}"
|
|
149
|
+
EXPERIMENTAL_TEMPLATE = "**WARNING**, this {content_type} is marked **EXPERIMENTAL** by the Splunk Threat Research Team. This means that the {content_type} has been manually tested but we do not have the associated attack data to perform automated testing or cannot share this attack dataset due to its sensitive nature. If you have any questions feel free to email us at: research@splunk.com. {description}"
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
|
|
2
3
|
import re
|
|
3
|
-
from typing import List,
|
|
4
|
-
|
|
5
|
-
from
|
|
6
|
-
|
|
7
|
-
from contentctl.objects.
|
|
4
|
+
from typing import Any, List, Literal
|
|
5
|
+
|
|
6
|
+
from pydantic import ConfigDict, Field, computed_field, model_serializer
|
|
7
|
+
|
|
8
|
+
from contentctl.objects.config import CustomApp
|
|
8
9
|
from contentctl.objects.constants import (
|
|
9
10
|
CONTENTCTL_MAX_SEARCH_NAME_LENGTH,
|
|
10
|
-
CONTENTCTL_RESPONSE_TASK_NAME_FORMAT_TEMPLATE,
|
|
11
11
|
CONTENTCTL_MAX_STANZA_LENGTH,
|
|
12
|
+
CONTENTCTL_RESPONSE_TASK_NAME_FORMAT_TEMPLATE,
|
|
12
13
|
)
|
|
13
|
-
from contentctl.objects.
|
|
14
|
+
from contentctl.objects.enums import DataModel, DetectionStatus
|
|
15
|
+
from contentctl.objects.investigation_tags import InvestigationTags
|
|
16
|
+
from contentctl.objects.security_content_object import SecurityContentObject
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
class Investigation(SecurityContentObject):
|
|
@@ -21,6 +24,7 @@ class Investigation(SecurityContentObject):
|
|
|
21
24
|
how_to_implement: str = Field(...)
|
|
22
25
|
known_false_positives: str = Field(...)
|
|
23
26
|
tags: InvestigationTags
|
|
27
|
+
status: Literal[DetectionStatus.production, DetectionStatus.deprecated]
|
|
24
28
|
|
|
25
29
|
# enrichment
|
|
26
30
|
@computed_field
|
|
@@ -99,3 +103,6 @@ class Investigation(SecurityContentObject):
|
|
|
99
103
|
# back to itself
|
|
100
104
|
for story in self.tags.analytic_story:
|
|
101
105
|
story.investigations.append(self)
|
|
106
|
+
# back to itself
|
|
107
|
+
for story in self.tags.analytic_story:
|
|
108
|
+
story.investigations.append(self)
|
contentctl/objects/rba.py
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
3
|
from abc import ABC
|
|
4
|
-
from
|
|
5
|
-
from
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from typing import Annotated, Set
|
|
6
6
|
|
|
7
|
+
from pydantic import BaseModel, Field, computed_field, model_serializer
|
|
8
|
+
|
|
9
|
+
from contentctl.objects.enums import RiskSeverity
|
|
7
10
|
|
|
8
11
|
RiskScoreValue_Type = Annotated[int, Field(ge=1, le=100)]
|
|
9
12
|
|
|
@@ -51,6 +54,28 @@ class RiskObject(BaseModel):
|
|
|
51
54
|
def __hash__(self):
|
|
52
55
|
return hash((self.field, self.type, self.score))
|
|
53
56
|
|
|
57
|
+
def __lt__(self, other: RiskObject) -> bool:
|
|
58
|
+
if (
|
|
59
|
+
f"{self.field}{self.type}{self.score}"
|
|
60
|
+
< f"{other.field}{other.type}{other.score}"
|
|
61
|
+
):
|
|
62
|
+
return True
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
@model_serializer
|
|
66
|
+
def serialize_risk_object(self) -> dict[str, str | int]:
|
|
67
|
+
"""
|
|
68
|
+
We define this explicitly for two reasons, even though the automatic
|
|
69
|
+
serialization works correctly. First we want to enforce a specific
|
|
70
|
+
field order for reasons of readability. Second, some of the fields
|
|
71
|
+
actually have different names than they do in the object.
|
|
72
|
+
"""
|
|
73
|
+
return {
|
|
74
|
+
"risk_object_field": self.field,
|
|
75
|
+
"risk_object_type": self.type,
|
|
76
|
+
"risk_score": self.score,
|
|
77
|
+
}
|
|
78
|
+
|
|
54
79
|
|
|
55
80
|
class ThreatObject(BaseModel):
|
|
56
81
|
field: str
|
|
@@ -59,6 +84,24 @@ class ThreatObject(BaseModel):
|
|
|
59
84
|
def __hash__(self):
|
|
60
85
|
return hash((self.field, self.type))
|
|
61
86
|
|
|
87
|
+
def __lt__(self, other: ThreatObject) -> bool:
|
|
88
|
+
if f"{self.field}{self.type}" < f"{other.field}{other.type}":
|
|
89
|
+
return True
|
|
90
|
+
return False
|
|
91
|
+
|
|
92
|
+
@model_serializer
|
|
93
|
+
def serialize_threat_object(self) -> dict[str, str]:
|
|
94
|
+
"""
|
|
95
|
+
We define this explicitly for two reasons, even though the automatic
|
|
96
|
+
serialization works correctly. First we want to enforce a specific
|
|
97
|
+
field order for reasons of readability. Second, some of the fields
|
|
98
|
+
actually have different names than they do in the object.
|
|
99
|
+
"""
|
|
100
|
+
return {
|
|
101
|
+
"threat_object_field": self.field,
|
|
102
|
+
"threat_object_type": self.type,
|
|
103
|
+
}
|
|
104
|
+
|
|
62
105
|
|
|
63
106
|
class RBAObject(BaseModel, ABC):
|
|
64
107
|
message: str
|
|
@@ -94,3 +137,11 @@ class RBAObject(BaseModel, ABC):
|
|
|
94
137
|
raise Exception(
|
|
95
138
|
f"Error getting severity - risk_score must be between 0-100, but was actually {self.risk_score}"
|
|
96
139
|
)
|
|
140
|
+
|
|
141
|
+
@model_serializer
|
|
142
|
+
def serialize_rba(self) -> dict[str, str | list[dict[str, str | int]]]:
|
|
143
|
+
return {
|
|
144
|
+
"message": self.message,
|
|
145
|
+
"risk_objects": [obj.model_dump() for obj in sorted(self.risk_objects)],
|
|
146
|
+
"threat_objects": [obj.model_dump() for obj in sorted(self.threat_objects)],
|
|
147
|
+
}
|
contentctl/objects/story.py
CHANGED
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from contentctl.objects.story_tags import StoryTags
|
|
4
|
-
from pydantic import Field, model_serializer, computed_field, model_validator
|
|
2
|
+
|
|
5
3
|
import re
|
|
4
|
+
from typing import TYPE_CHECKING, List, Literal
|
|
5
|
+
|
|
6
|
+
from pydantic import Field, computed_field, model_serializer, model_validator
|
|
7
|
+
|
|
8
|
+
from contentctl.objects.story_tags import StoryTags
|
|
6
9
|
|
|
7
10
|
if TYPE_CHECKING:
|
|
8
|
-
from contentctl.objects.detection import Detection
|
|
9
|
-
from contentctl.objects.investigation import Investigation
|
|
10
11
|
from contentctl.objects.baseline import Baseline
|
|
11
|
-
from contentctl.objects.data_source import DataSource
|
|
12
12
|
from contentctl.objects.config import CustomApp
|
|
13
|
+
from contentctl.objects.data_source import DataSource
|
|
14
|
+
from contentctl.objects.detection import Detection
|
|
15
|
+
from contentctl.objects.investigation import Investigation
|
|
13
16
|
|
|
17
|
+
from contentctl.objects.enums import DetectionStatus
|
|
14
18
|
from contentctl.objects.security_content_object import SecurityContentObject
|
|
15
19
|
|
|
16
20
|
|
|
17
21
|
class Story(SecurityContentObject):
|
|
18
22
|
narrative: str = Field(...)
|
|
19
23
|
tags: StoryTags = Field(...)
|
|
20
|
-
|
|
24
|
+
status: Literal[DetectionStatus.production, DetectionStatus.deprecated]
|
|
21
25
|
# These are updated when detection and investigation objects are created.
|
|
22
26
|
# Specifically in the model_post_init functions
|
|
23
27
|
detections: List[Detection] = []
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
type = detection
|
|
8
8
|
asset_type = {{ detection.tags.asset_type }}
|
|
9
9
|
confidence = medium
|
|
10
|
-
explanation = {{
|
|
10
|
+
explanation = {{ detection.status_aware_description | escapeNewlines() }}
|
|
11
11
|
{% if detection.how_to_implement is defined %}
|
|
12
12
|
how_to_implement = {{ detection.how_to_implement | escapeNewlines() }}
|
|
13
13
|
{% else %}
|
|
@@ -11,7 +11,7 @@ references = {{ story.getReferencesListForJson() | tojson }}
|
|
|
11
11
|
maintainers = [{"company": "{{ story.author_company }}", "email": "{{ story.author_email }}", "name": "{{ story.author_name }}"}]
|
|
12
12
|
spec_version = 3
|
|
13
13
|
searches = {{ story.storyAndInvestigationNamesWithApp(app) | tojson }}
|
|
14
|
-
description = {{ story.
|
|
14
|
+
description = {{ story.status_aware_description | escapeNewlines() }}
|
|
15
15
|
{% if story.narrative is defined %}
|
|
16
16
|
narrative = {{ story.narrative | escapeNewlines() }}
|
|
17
17
|
{% endif %}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
{% for response_task in objects %}
|
|
3
3
|
[panel://workbench_panel_{{ response_task.lowercase_name }}___response_task]
|
|
4
4
|
label = {{ response_task.name }}
|
|
5
|
-
description = {{ response_task.
|
|
5
|
+
description = {{ response_task.status_aware_description | escapeNewlines() }}
|
|
6
6
|
disabled = 0
|
|
7
7
|
tokens = {\
|
|
8
8
|
{% for token in response_task.inputs %}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
{% for story in objects %}
|
|
3
3
|
[panel_group://workbench_panel_group_{{ story.lowercase_name}}]
|
|
4
4
|
label = {{ story.name }}
|
|
5
|
-
description = {{ story.
|
|
5
|
+
description = {{ story.status_aware_description | escapeNewlines() }}
|
|
6
6
|
disabled = 0
|
|
7
7
|
|
|
8
8
|
{% if story.workbench_panels is defined %}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
action.escu = 0
|
|
9
9
|
action.escu.enabled = 1
|
|
10
10
|
action.escu.search_type = support
|
|
11
|
-
description = {{ detection.
|
|
11
|
+
description = {{ detection.status_aware_description | escapeNewlines() }}
|
|
12
12
|
action.escu.creation_date = {{ detection.date }}
|
|
13
13
|
action.escu.modification_date = {{ detection.date }}
|
|
14
14
|
{% if detection.tags.analytic_story is defined %}
|
|
@@ -29,7 +29,7 @@ action.escu.providing_technologies = {{ detection.providing_technologies | tojso
|
|
|
29
29
|
{% else %}
|
|
30
30
|
action.escu.providing_technologies = []
|
|
31
31
|
{% endif %}
|
|
32
|
-
action.escu.eli5 = {{ detection.
|
|
32
|
+
action.escu.eli5 = {{ detection.status_aware_description | escapeNewlines() }}
|
|
33
33
|
{% if detection.how_to_implement is defined %}
|
|
34
34
|
action.escu.how_to_implement = {{ detection.how_to_implement | escapeNewlines() }}
|
|
35
35
|
{% else %}
|
|
@@ -5,16 +5,10 @@
|
|
|
5
5
|
[{{ detection.get_conf_stanza_name(app) }}]
|
|
6
6
|
action.escu = 0
|
|
7
7
|
action.escu.enabled = 1
|
|
8
|
-
{
|
|
9
|
-
description = **WARNING**, this detection has been marked **DEPRECATED** by the Splunk Threat Research Team. This means that it will no longer be maintained or supported. If you have any questions feel free to email us at: research@splunk.com. {{ detection.description | escapeNewlines() }}
|
|
10
|
-
{% elif detection.status == "experimental" %}
|
|
11
|
-
description = **WARNING**, this detection is marked **EXPERIMENTAL** by the Splunk Threat Research Team. This means that the detection has been manually tested but we do not have the associated attack data to perform automated testing or cannot share this attack dataset due to its sensitive nature. If you have any questions feel free to email us at: research@splunk.com. {{ detection.description | escapeNewlines() }}
|
|
12
|
-
{% else %}
|
|
13
|
-
description = {{ detection.description | escapeNewlines() }}
|
|
14
|
-
{% endif %}
|
|
8
|
+
description = {{ detection.status_aware_description | escapeNewlines() }}
|
|
15
9
|
action.escu.mappings = {{ detection.mappings | tojson }}
|
|
16
10
|
action.escu.data_models = {{ detection.datamodel | tojson }}
|
|
17
|
-
action.escu.eli5 = {{ detection.
|
|
11
|
+
action.escu.eli5 = {{ detection.status_aware_description | escapeNewlines() }}
|
|
18
12
|
{% if detection.how_to_implement %}
|
|
19
13
|
action.escu.how_to_implement = {{ detection.how_to_implement | escapeNewlines() }}
|
|
20
14
|
{% else %}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
action.escu = 0
|
|
10
10
|
action.escu.enabled = 1
|
|
11
11
|
action.escu.search_type = investigative
|
|
12
|
-
description = {{ detection.
|
|
12
|
+
description = {{ detection.status_aware_description | escapeNewlines() }}
|
|
13
13
|
action.escu.creation_date = {{ detection.date }}
|
|
14
14
|
action.escu.modification_date = {{ detection.date }}
|
|
15
15
|
{% if detection.tags.analytic_story is defined %}
|
|
@@ -21,7 +21,7 @@ action.escu.earliest_time_offset = 3600
|
|
|
21
21
|
action.escu.latest_time_offset = 86400
|
|
22
22
|
action.escu.providing_technologies = []
|
|
23
23
|
action.escu.data_models = {{ detection.datamodel | tojson }}
|
|
24
|
-
action.escu.eli5 = {{ detection.
|
|
24
|
+
action.escu.eli5 = {{ detection.status_aware_description | escapeNewlines() }}
|
|
25
25
|
action.escu.how_to_implement = none
|
|
26
26
|
action.escu.known_false_positives = None at this time
|
|
27
27
|
disabled = true
|
|
@@ -13,9 +13,7 @@ default_match = {{ lookup.default_match | lower }}
|
|
|
13
13
|
{% if lookup.case_sensitive_match is defined and lookup.case_sensitive_match != None %}
|
|
14
14
|
case_sensitive_match = {{ lookup.case_sensitive_match | lower }}
|
|
15
15
|
{% endif %}
|
|
16
|
-
{% if lookup.description is defined and lookup.description != None %}
|
|
17
16
|
# description = {{ lookup.description | escapeNewlines() }}
|
|
18
|
-
{% endif %}
|
|
19
17
|
{% if lookup.match_type | length > 0 %}
|
|
20
18
|
match_type = {{ lookup.match_type_to_conf_format }}
|
|
21
19
|
{% endif %}
|
|
@@ -3,6 +3,7 @@ id: bcfd17e8-5461-400a-80a2-3b7d1459220c
|
|
|
3
3
|
version: 1
|
|
4
4
|
date: '2021-02-16'
|
|
5
5
|
author: Michael Haag, Splunk
|
|
6
|
+
status: production
|
|
6
7
|
description: Cobalt Strike is threat emulation software. Red teams and penetration
|
|
7
8
|
testers use Cobalt Strike to demonstrate the risk of a breach and evaluate mature
|
|
8
9
|
security programs. Most recently, Cobalt Strike has become the choice tool by threat
|
|
@@ -15,7 +15,7 @@ contentctl/actions/detection_testing/views/DetectionTestingViewWeb.py,sha256=CXV
|
|
|
15
15
|
contentctl/actions/doc_gen.py,sha256=P2-RYsJoW-QuhAkSpOQespDLJBC-4Cq3-XGTmadK8Ys,936
|
|
16
16
|
contentctl/actions/initialize.py,sha256=KaWSbrTaJA4vNSpKc_rwdlaaERnWw_hPlWwsPOA6Gy8,3191
|
|
17
17
|
contentctl/actions/inspect.py,sha256=rXnrhDt59-n0Jqh_UZ0tDzpKqiOvkGzlvSypXoarKjU,18322
|
|
18
|
-
contentctl/actions/new_content.py,sha256=
|
|
18
|
+
contentctl/actions/new_content.py,sha256=xs0QvHzlrf0g-EgdUJTkdDdFaA-uEGmzMTixDt6NcTY,8212
|
|
19
19
|
contentctl/actions/release_notes.py,sha256=_Rdljg0tPSAFlw34LJ7dUsHLiH8tJTQ6B95X6MvxURo,17023
|
|
20
20
|
contentctl/actions/reporting.py,sha256=GF32i7sHdc47bw-VWSW-nZ1QBaUl6Ni1JjV5_SOyiAU,1660
|
|
21
21
|
contentctl/actions/test.py,sha256=GTtvHi1yB5yDm1jPMyuc4WxczOq-O7f2N8LpTmMaWgU,6014
|
|
@@ -32,17 +32,17 @@ contentctl/helper/utils.py,sha256=rigwZzCwWzn11sKTVWDkYEtLmRSf0yBbJ671OSRQnOM,19
|
|
|
32
32
|
contentctl/input/director.py,sha256=asK4yUlSVdv0QDUzCrTEXQUm0j9hbzVu55o_-wD-eWc,11560
|
|
33
33
|
contentctl/input/new_content_questions.py,sha256=z2C4Mg7-EyxtiF2z9m4SnSbi6QO4CUPB3wg__JeMXIQ,4067
|
|
34
34
|
contentctl/input/yml_reader.py,sha256=ymmAqsWsf9Oj56waDOhCh_E4SomkSCmu4dAx7iURFt8,2050
|
|
35
|
-
contentctl/objects/abstract_security_content_objects/detection_abstract.py,sha256=
|
|
36
|
-
contentctl/objects/abstract_security_content_objects/security_content_object_abstract.py,sha256=
|
|
35
|
+
contentctl/objects/abstract_security_content_objects/detection_abstract.py,sha256=9uUb3BkH9d5vc9qn7RURhki9HZZJSGBTYfnqMImsiY4,43814
|
|
36
|
+
contentctl/objects/abstract_security_content_objects/security_content_object_abstract.py,sha256=P1v5n8hM4XzJOjNZbJ5m3y4Bu-cQYaddLPdbE6K-4Xw,12164
|
|
37
37
|
contentctl/objects/alert_action.py,sha256=iEvdEOT4TrTXT0z4rQ_W5v79hPJpPhFPSzo7TuHDxwA,1376
|
|
38
38
|
contentctl/objects/annotated_types.py,sha256=eAMm1Nm3_C5pwfCxhzL5ynDRsC_eK614bFuwUFxPVLw,261
|
|
39
39
|
contentctl/objects/atomic.py,sha256=5nl-JhZnymadi8B8ZEJ8l80DnpvjG-OlRxUjVKR6ffY,7341
|
|
40
40
|
contentctl/objects/base_test.py,sha256=JG6qlr7xe9P71n3CzKOro8_bsmDQGYDfTG9YooHQSIE,1105
|
|
41
41
|
contentctl/objects/base_test_result.py,sha256=TYYzTPKWqp9rHTebWoid50uxAp_iALZouril4sFwIcA,5197
|
|
42
|
-
contentctl/objects/baseline.py,sha256=
|
|
42
|
+
contentctl/objects/baseline.py,sha256=grzM56KCpROjMnJQIan-fG0LCYfRGA2GHui4FwBwb8A,3172
|
|
43
43
|
contentctl/objects/baseline_tags.py,sha256=Eomy8y3HV-E6Lym5B5ZZTtsmQJYi6Jd4y8GZpTWGYgQ,1643
|
|
44
44
|
contentctl/objects/config.py,sha256=5nK3EkUea-6qUvSzqAxl5UyoLRlldKsh9K8glaBup3I,48603
|
|
45
|
-
contentctl/objects/constants.py,sha256=
|
|
45
|
+
contentctl/objects/constants.py,sha256=P4K07PX4_pL_n5jmBVn1BxtoFpjvhT8MuHOquhnEkdA,5465
|
|
46
46
|
contentctl/objects/correlation_search.py,sha256=ab6v-0nbzujhTMpwaXynQiInWpRO1zB5KR4eZLCav_M,45234
|
|
47
47
|
contentctl/objects/dashboard.py,sha256=qMSP76hkJo7PVsWr19hQW4eYoUqGTcRejaOEjlcA_DY,4198
|
|
48
48
|
contentctl/objects/data_source.py,sha256=4u4JaA-Q5xa0gS61yarJuowgy4TrAYE98O2G8o9CQzA,1454
|
|
@@ -62,7 +62,7 @@ contentctl/objects/enums.py,sha256=HdaSQgEQ_T38BIlVYk1xdqMm05YyhQb0720nzBorXQw,1
|
|
|
62
62
|
contentctl/objects/errors.py,sha256=xX_FDUaJbJiOWgjgrzjtYW5QsD41UZ2KWqH-yGkHaCU,5554
|
|
63
63
|
contentctl/objects/integration_test.py,sha256=TYjKyH4YinUnYXOse5BQGCa4-ez_5mtoMwvh1JJcb0o,1254
|
|
64
64
|
contentctl/objects/integration_test_result.py,sha256=_uUSgqgjFhEZM8UwOJI6Q9K-ekIrbKU6OPdqHZycl-s,279
|
|
65
|
-
contentctl/objects/investigation.py,sha256=
|
|
65
|
+
contentctl/objects/investigation.py,sha256=kmvQ0grCd1YVEW5wVF4-_Rx87PnOxPiNoN_ufTLc3ZM,3659
|
|
66
66
|
contentctl/objects/investigation_tags.py,sha256=qDGNusrWDvCX_GcBEzag2MydSV0LIhGxoXZGgxDXfHA,1317
|
|
67
67
|
contentctl/objects/lookup.py,sha256=mzOPhMDyoNZKLAj8zf6Wg6i9FJKMu3qHWinATtH75I8,13015
|
|
68
68
|
contentctl/objects/macro.py,sha256=usbxyOPIRIJoDmvawfP2DxtFNf71GaDwffxiZsRkP5A,3594
|
|
@@ -73,13 +73,13 @@ contentctl/objects/notable_action.py,sha256=sW5XlpGznMHqyBmGXtXrl22hWLiCoKkfGCas
|
|
|
73
73
|
contentctl/objects/notable_event.py,sha256=2aOtmfnsdInTtN_fHAGIKmBTBritjHbS_Nc-pqL-GbY,689
|
|
74
74
|
contentctl/objects/playbook.py,sha256=mgYbWsD3OW86u11MbIFKvmyFueSoMJ1WBJm_rNrFvAo,2425
|
|
75
75
|
contentctl/objects/playbook_tags.py,sha256=O5obkQyb82YdJEii8ZJEQtrHtLOSnAvAkT1qIgpCK2s,1547
|
|
76
|
-
contentctl/objects/rba.py,sha256=
|
|
76
|
+
contentctl/objects/rba.py,sha256=VudoJAqADAEflDqVDWlOIk8epRfg_rqvfCsyavjvcaU,4737
|
|
77
77
|
contentctl/objects/risk_analysis_action.py,sha256=v-TQktXEEzbGzmTtqwEykXoSKdGnIlK_JojnqvvAE1s,4370
|
|
78
78
|
contentctl/objects/risk_event.py,sha256=JQUmXriiwi5FetqVnhM0hf5cUp6LzLSNPuoecC2JKK0,12593
|
|
79
79
|
contentctl/objects/risk_object.py,sha256=5iUKW_UwQLjjLWiD_vlE78uwH9bkaMNCHRNmKM25W1Q,905
|
|
80
80
|
contentctl/objects/savedsearches_conf.py,sha256=Dn_Pxd9i3RT6DwNh6JrgmfxjsO3q15xzMksYr3wIGwQ,8624
|
|
81
81
|
contentctl/objects/security_content_object.py,sha256=iDnhq81P7m6Qkmc_Yi-wOyFm9gZUYnPy1GJxxyCtonA,245
|
|
82
|
-
contentctl/objects/story.py,sha256=
|
|
82
|
+
contentctl/objects/story.py,sha256=jalNgK4yYGRr_yVkzuO_YHIrPhpWHF0Q79PkTJhcLzY,5048
|
|
83
83
|
contentctl/objects/story_tags.py,sha256=SLwgkckLxBdtgJro0LnYgj5TFHZEgMiaqDI9q6OfNE0,2364
|
|
84
84
|
contentctl/objects/test_attack_data.py,sha256=7p-kOJguTZtG9y5th5U3qfPFvpiAWLST_OBw8dwWl_4,488
|
|
85
85
|
contentctl/objects/test_group.py,sha256=r-dXyddok4yslv8SIjwOpqylbN1rdjsRi-HIijvpWD0,2602
|
|
@@ -98,9 +98,9 @@ contentctl/output/doc_md_output.py,sha256=wlgbzBD2hUbQNIW2zv5sdrq2UdAKhOZJUYSObn
|
|
|
98
98
|
contentctl/output/jinja_writer.py,sha256=5PbFrc8KuLWrlNIHDvMTyvJ18u_mtjd5Led6-9sn2Eo,1204
|
|
99
99
|
contentctl/output/json_writer.py,sha256=waw73wOmalSrUFcr2K1CWR-xz5oW8il10zDAn56mtMg,1041
|
|
100
100
|
contentctl/output/svg_output.py,sha256=5s9fjmKullMV6cCCGwP7_xvQwg9EZLOKRKMw_IyO6hY,2988
|
|
101
|
-
contentctl/output/templates/analyticstories_detections.j2,sha256=
|
|
101
|
+
contentctl/output/templates/analyticstories_detections.j2,sha256=6ZiQO8np6KkX8skVoIB0BN9_s8SBW3qeo8IBA8r8GQk,923
|
|
102
102
|
contentctl/output/templates/analyticstories_investigations.j2,sha256=kqy9lR6W3avqETCM2tSZ8WWOlfiyOtFv6G5N4SZWSaQ,527
|
|
103
|
-
contentctl/output/templates/analyticstories_stories.j2,sha256=
|
|
103
|
+
contentctl/output/templates/analyticstories_stories.j2,sha256=MxkmwsgW1oge2YJhbgAzXVcTplSr5JjKIDxX4SBZV0E,676
|
|
104
104
|
contentctl/output/templates/app.conf.j2,sha256=UL80Px4IUGPD-DgcAiUrS4emHBIY7DxleSNyNXCH5tQ,623
|
|
105
105
|
contentctl/output/templates/app.manifest.j2,sha256=Q1803mcfgNvUs8s4e1zD1J3_mxfPYVtLkD8fhCO6d-I,1103
|
|
106
106
|
contentctl/output/templates/collections.j2,sha256=w2hkY7Yfm7AmY1O_7DP-znLS_whgKX79VbnW7QlvrNU,151
|
|
@@ -115,16 +115,16 @@ contentctl/output/templates/doc_playbooks.j2,sha256=CWsnm8F097oYT8anW3CE7JaX1haA
|
|
|
115
115
|
contentctl/output/templates/doc_playbooks_page.j2,sha256=2d5UNDSOxyMtxKGxGHzJ2Ny_UrqTq267NO1h-lmNduc,679
|
|
116
116
|
contentctl/output/templates/doc_stories.j2,sha256=0J3dAbfSZz-Ma1-C9B6vYPKGwrxoZryYoudy3wUIT4s,1827
|
|
117
117
|
contentctl/output/templates/doc_story_page.j2,sha256=jrf-As8GbqLarRoiDipfM9ZUVRl_bhdNsy-XaCrBaXE,874
|
|
118
|
-
contentctl/output/templates/es_investigations_investigations.j2,sha256=
|
|
119
|
-
contentctl/output/templates/es_investigations_stories.j2,sha256=
|
|
118
|
+
contentctl/output/templates/es_investigations_investigations.j2,sha256=vIWiQ6hNsb_-w8ChlWWbQXxGIx5jjWaQ6VoeBJxpHJk,1026
|
|
119
|
+
contentctl/output/templates/es_investigations_stories.j2,sha256=E-OvO7EFdqO3HKyfAY1jBS6VMsDVKuiVqg8SrDVXwls,401
|
|
120
120
|
contentctl/output/templates/header.j2,sha256=3usV7jm1q6J-QNnQrZzII9cN0XEGQjg_eVKrEQwfOG0,201
|
|
121
121
|
contentctl/output/templates/macros.j2,sha256=SLcQQ5X7TZS8j-2qP06BTXqdIcnwoYqTAaBLX2Dge7Y,390
|
|
122
122
|
contentctl/output/templates/panel.j2,sha256=Cw_W6p-14n6UivVfpS75KKJiJ2VpdGsSBceYsUYe9gk,221
|
|
123
|
-
contentctl/output/templates/savedsearches_baselines.j2,sha256=
|
|
124
|
-
contentctl/output/templates/savedsearches_detections.j2,sha256=
|
|
125
|
-
contentctl/output/templates/savedsearches_investigations.j2,sha256=
|
|
123
|
+
contentctl/output/templates/savedsearches_baselines.j2,sha256=WHZB4e0vmeym8832VxRmuUfDJ-YRYt6emcYaJrghI58,1709
|
|
124
|
+
contentctl/output/templates/savedsearches_detections.j2,sha256=B7_b8aBjEKAV7mJm0DxUS_HyCt63bQZtpacCQfDgDqc,6033
|
|
125
|
+
contentctl/output/templates/savedsearches_investigations.j2,sha256=KH2r8SgyAMiettSHypSbA2-1XmQ_8_8xzk3BkbZ1Re4,1196
|
|
126
126
|
contentctl/output/templates/server.conf.j2,sha256=sPZUkiuJNGm9R8rpjfRKyuAvmmQb0C4w9Q6hpmvmPeU,127
|
|
127
|
-
contentctl/output/templates/transforms.j2,sha256=
|
|
127
|
+
contentctl/output/templates/transforms.j2,sha256=mM9vIIGqxlhLk1W8sHoUgppfK1FO3ESQD1WCVAewhBw,1386
|
|
128
128
|
contentctl/output/templates/workflow_actions.j2,sha256=DFoZVnCa8dMRHjW2AdpoydBC0THgiH_W-Nx7WI4-uR4,925
|
|
129
129
|
contentctl/output/yml_writer.py,sha256=gGgbamHWunHKjj47TcqB04k0xliX6w3H7iajZtUZRSU,2124
|
|
130
130
|
contentctl/templates/README.md,sha256=GoRmywUqwnjaehY_GLmGqxsFXCLP9plpDYwB6W6nVPs,428
|
|
@@ -160,9 +160,9 @@ contentctl/templates/detections/network/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeu
|
|
|
160
160
|
contentctl/templates/detections/web/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
161
161
|
contentctl/templates/macros/security_content_ctime.yml,sha256=Gg1YNllHVsX_YB716H1SJLWzxXZEfuJlnsgB2fuyoHU,159
|
|
162
162
|
contentctl/templates/macros/security_content_summariesonly.yml,sha256=9BYUxAl2E4Nwh8K19F3AJS8Ka7ceO6ZDBjFiO3l3LY0,162
|
|
163
|
-
contentctl/templates/stories/cobalt_strike.yml,sha256=
|
|
164
|
-
contentctl-5.0.
|
|
165
|
-
contentctl-5.0.
|
|
166
|
-
contentctl-5.0.
|
|
167
|
-
contentctl-5.0.
|
|
168
|
-
contentctl-5.0.
|
|
163
|
+
contentctl/templates/stories/cobalt_strike.yml,sha256=uj8idtDNOAIqpZ9p8usQg6mop1CQkJ5TlB4Q7CJdTIE,3082
|
|
164
|
+
contentctl-5.0.1.dist-info/LICENSE.md,sha256=hQWUayRk-pAiOZbZnuy8djmoZkjKBx8MrCFpW-JiOgo,11344
|
|
165
|
+
contentctl-5.0.1.dist-info/METADATA,sha256=C2v7yiaeE_BPSiTz4hGC9S6Wa2CbaOhsfmGRIXa0lOI,21539
|
|
166
|
+
contentctl-5.0.1.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
|
167
|
+
contentctl-5.0.1.dist-info/entry_points.txt,sha256=5bjZ2NkbQfSwK47uOnA77yCtjgXhvgxnmCQiynRF_-U,57
|
|
168
|
+
contentctl-5.0.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|