mirror of
https://github.com/splunk/security_content
synced 2026-06-08 17:32:49 +00:00
Branch was auto-updated.
This commit is contained in:
@@ -16,8 +16,14 @@ class GithubService:
|
||||
|
||||
def __init__(self, security_content_branch):
|
||||
self.security_content_branch = security_content_branch
|
||||
self.security_content_repo_obj = self.clone_project(SECURITY_CONTENT_URL, f"security_content", f"develop")
|
||||
self.security_content_repo_obj.git.checkout(security_content_branch)
|
||||
if os.path.exists('security_content'):
|
||||
LOGGER.warning(f"Found Existing Security Content Project")
|
||||
self.created_repo = False
|
||||
else:
|
||||
self.security_content_repo_obj = self.clone_project(SECURITY_CONTENT_URL, f"security_content", f"develop")
|
||||
self.security_content_repo_obj.git.checkout(security_content_branch)
|
||||
self.created_repo = True
|
||||
|
||||
|
||||
def clone_project(self, url, project, branch):
|
||||
LOGGER.info(f"Clone Security Content Project")
|
||||
|
||||
@@ -102,6 +102,23 @@ class DSPApi:
|
||||
return response_body
|
||||
|
||||
|
||||
def get_pipelines(self):
|
||||
"""
|
||||
Returns the list of pipelines
|
||||
|
||||
@return:
|
||||
list of pipelines
|
||||
"""
|
||||
headers = {"Content-Type": "application/json", "Authorization": self.header_token}
|
||||
|
||||
response = requests.get(self.return_api_endpoint(PIPELINES_ENDPOINT), headers=headers)
|
||||
response_body = response.json()
|
||||
if response.status_code == HTTPStatus.OK:
|
||||
return response_body.get('items')
|
||||
else:
|
||||
LOGGER.error(f"Failed to get pipelines: %s", response.text)
|
||||
|
||||
|
||||
def create_pipeline(self, upl):
|
||||
"""
|
||||
POST pipelines endpoint to create a pipeline based on the valid upl
|
||||
@@ -176,12 +193,12 @@ class DSPApi:
|
||||
response_body = response.json()
|
||||
|
||||
if response.status_code == HTTPStatus.OK:
|
||||
pipeline_id = response_body.get("id")
|
||||
LOGGER.info(f"Pipeline {pipeline_id} successfully created")
|
||||
return response_body
|
||||
pipeline_id = response_body.get("activated")
|
||||
LOGGER.info(f"Pipeline {pipeline_id} successfully activated")
|
||||
else:
|
||||
LOGGER.error(f"Failed to activate pipeline {pipeline_id}")
|
||||
return
|
||||
LOGGER.error(f"Failed to activate pipeline {pipeline_id}: {response.text}")
|
||||
|
||||
return response_body
|
||||
|
||||
|
||||
def deactivate_pipeline(self, pipeline_id):
|
||||
@@ -501,4 +518,3 @@ class DSPApi:
|
||||
delete_url = f"{datasets_endpoint_api}/{index_id}"
|
||||
response = requests.delete(delete_url, headers=request_headers(self.header_token))
|
||||
return response.status_code
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ class SSADetectionTesting:
|
||||
def test_ssa_detections(self, test_obj):
|
||||
LOGGER.info('Test SSA Detection: ' + test_obj["detection_obj"]["name"])
|
||||
self.max_execution_time = MAX_EXECUTION_TIME_LIMIT
|
||||
file_path_attack_data = os.path.join(os.path.dirname(__file__), "../", test_obj["attack_data_file_path"])
|
||||
file_path_attack_data = test_obj["attack_data_file_path"]
|
||||
|
||||
test_results = self.ssa_detection_test(test_obj["detection_obj"]["search"], file_path_attack_data,
|
||||
"SSA Smoke Test " + test_obj["test_obj"]["name"])
|
||||
@@ -85,12 +85,31 @@ class SSADetectionTesting:
|
||||
return self.update_execution_time(time_in_s)
|
||||
|
||||
def ssa_detection_test_init(self):
|
||||
self.cleanup_old_pipelines()
|
||||
self.test_results["result"] = True
|
||||
self.test_results["msg"] = ""
|
||||
self.results_index = self.api.create_temp_index("mc")
|
||||
self.created_pipelines = []
|
||||
self.activated_pipelines = []
|
||||
|
||||
def cleanup_old_pipelines(self):
|
||||
pipelines = self.api.get_pipelines()
|
||||
yesterday = (time.time() - 24*3600) * 1000 # milliseconds
|
||||
for pipeline in pipelines:
|
||||
if pipeline['name'].startswith("ssa_smoke_test_pipeline_helper") and pipeline['createDate'] < yesterday:
|
||||
if pipeline['status'] == 'ACTIVATED':
|
||||
# deactivate pipeline
|
||||
resp, _ = self.api.deactivate_pipeline(pipeline['id'])
|
||||
if resp.status_code != HTTPStatus.OK:
|
||||
LOGGER.error("Error deactivating old pipeline %s: %s", pipeline['name'], resp.text)
|
||||
|
||||
# delete pipeline
|
||||
resp = self.api.delete_pipeline(pipeline['id'])
|
||||
if resp.status_code != HTTPStatus.NO_CONTENT:
|
||||
LOGGER.error("Error deleting old pipeline %s: %s", pipeline['name'], resp.text)
|
||||
else:
|
||||
LOGGER.warning("Found and deleted an old pipeline: %s", pipeline['name'])
|
||||
|
||||
def ssa_detection_test_main(self, spl, source, test_name):
|
||||
self.execution_passed = True
|
||||
|
||||
@@ -192,8 +211,7 @@ class SSADetectionTesting:
|
||||
"msg": f"Detection test failure for {test_name}"}
|
||||
except Exception as e:
|
||||
self.ssa_detection_test_teardown()
|
||||
LOGGER.error(e)
|
||||
LOGGER.error(f"Detection test failure for {test_name} (perhaps SCS problems)")
|
||||
LOGGER.exception(f"Detection test failure for {test_name} (perhaps SCS problems)")
|
||||
return {"result": False,
|
||||
"msg": f"Detection test failure for {test_name} (perhaps SCS problems)"}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ LOGGER = logging.getLogger(__name__)
|
||||
# Macros
|
||||
PULSAR_SOURCE_CONNECTION_ID_PLAYGROUND = f"29fb61f1-9342-48f5-9793-1afa008c377b"
|
||||
PULSAR_SOURCE_TOPIC_PLAYGROUND = f"persistent://ssa/egress/decorated-events-research2"
|
||||
PULSAR_SOURCE_CONNECTION_ID_STAGING = f"fd92bf9f-5d40-4c2e-bb75-bf0c3fc13980"
|
||||
PULSAR_SOURCE_CONNECTION_ID_STAGING = f"b8c81601-a7e0-4501-802c-cb2831c72b6f"
|
||||
PULSAR_SOURCE_TOPIC_STAGING = f"persistent://ssa/egress/decorated-events-research"
|
||||
|
||||
READ_SSA_ENRICHED_EVENTS = f"| from read_ssa_enriched_events()"
|
||||
@@ -110,8 +110,7 @@ def replace_ssa_macros(source, sink, spl):
|
||||
return spl
|
||||
|
||||
|
||||
def read_data(file_name):
|
||||
file_path = os.path.join(os.path.dirname(__file__), 'data', file_name)
|
||||
def read_data(file_path):
|
||||
data_manipulation = DataManipulation()
|
||||
modified_file = data_manipulation.manipulate_timestamp(file_path, 'xmlwineventlog', 'WinEventLog:Security')
|
||||
data = []
|
||||
|
||||
@@ -80,7 +80,8 @@ def main(args):
|
||||
LOGGER.info(test_result['msg'])
|
||||
LOGGER.info('-----------------------------------')
|
||||
|
||||
remove_security_content()
|
||||
if github_service.created_repo:
|
||||
remove_security_content()
|
||||
exit_code = not test_passed
|
||||
sys.exit(exit_code)
|
||||
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@ def test_detection(test, args):
|
||||
# Download data to temporal folder
|
||||
for unit in test_desc['tests']:
|
||||
detection = get_detection(unit)
|
||||
if detection['type'] == "SSA":
|
||||
if detection['type'] == 'streaming':
|
||||
log(logging.INFO, "Testing %s" % name)
|
||||
# Prepare data
|
||||
data_dir = tempfile.TemporaryDirectory(prefix="data", dir=get_path("%s" % SSML_CWD))
|
||||
|
||||
+4
-3
@@ -7,7 +7,7 @@ from modules.ssa_utils import *
|
||||
from modules.testing_utils import *
|
||||
|
||||
|
||||
DUMB_PIPELINE_INPUT = '| from read_text("/")' \
|
||||
DUMB_PIPELINE_INPUT = '| from read_text("test.spl2")' \
|
||||
'| select from_json_object(value) as input_event' \
|
||||
'| eval timestamp=parse_long(ucast(map_get(input_event, "_time"), "string", null))'
|
||||
|
||||
@@ -71,12 +71,13 @@ def extract_ssa_fields(spl2):
|
||||
fields_file = os.path.join(data_dir.name, "fields.out")
|
||||
write_validation_pipeline(spl2, pipeline_file)
|
||||
subprocess.run(["/usr/bin/java",
|
||||
"-jar", get_path("%s/humvee.jar" % SSML_CWD),
|
||||
"-jar", "humvee.jar",
|
||||
'cli', '-i',
|
||||
pipeline_file, '-o',
|
||||
fields_file,
|
||||
'-f'],
|
||||
stderr=subprocess.DEVNULL,
|
||||
#stderr=subprocess.DEVNULL,
|
||||
cwd=get_path(SSML_CWD),
|
||||
check=True)
|
||||
spl2_ssa_fields = set()
|
||||
with open(fields_file, 'r') as test_out_fh:
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
name: Rare Parent-Child Process Relationship
|
||||
id: cf090c78-bcc6-11eb-8529-0242ac130003
|
||||
version: 1
|
||||
date: '2021-05-20'
|
||||
author: Peter Gael, Splunk; Ignacio Bermudez Corrales, Splunk
|
||||
type: streaming
|
||||
datamodel: []
|
||||
description: An attacker may use LOLBAS tools spawned from vulnerable applications
|
||||
not typically used by system administrators. This search leverages the Splunk Streaming
|
||||
ML DSP plugin to find rare parent/child relationships. The list of application has
|
||||
been extracted from https://github.com/LOLBAS-Project/LOLBAS/tree/master/yml/OSBinaries
|
||||
search: '| from read_ssa_enriched_events() | eval timestamp=parse_long(ucast(map_get(input_event,
|
||||
"_time"), "string", null)) | eval parent_process=lower(ucast(map_get(input_event,
|
||||
"parent_process_name"), "string", null)), parent_process_name=mvindex(split(parent_process,
|
||||
"\\"), -1), process_name=lower(ucast(map_get(input_event, "process_name"), "string",
|
||||
null)), cmd_line=ucast(map_get(input_event, "process"), "string",
|
||||
null), dest_user_id=ucast(map_get(input_event, "dest_user_id"), "string", null),
|
||||
dest_device_id=ucast(map_get(input_event, "dest_device_id"), "string", null)
|
||||
| where parent_process_name!=null
|
||||
| select parent_process_name, process_name, cmd_line, timestamp, dest_device_id, dest_user_id
|
||||
| conditional_anomaly conditional="parent_process_name" target="process_name"
|
||||
| where (process_name="powershell.exe" OR process_name="regsvcs.exe"
|
||||
OR process_name="ftp.exe" OR process_name="dfsvc.exe" OR process_name="rasautou.exe"
|
||||
OR process_name="schtasks.exe" OR process_name="xwizard.exe" OR process_name="findstr.exe"
|
||||
OR process_name="esentutl.exe" OR process_name="cscript.exe" OR process_name="reg.exe"
|
||||
OR process_name="csc.exe" OR process_name="atbroker.exe" OR process_name="print.exe"
|
||||
OR process_name="pcwrun.exe" OR process_name="vbc.exe" OR process_name="rpcping.exe"
|
||||
OR process_name="wsreset.exe" OR process_name="ilasm.exe" OR process_name="certutil.exe"
|
||||
OR process_name="replace.exe" OR process_name="mshta.exe" OR process_name="bitsadmin.exe"
|
||||
OR process_name="wscript.exe" OR process_name="ieexec.exe" OR process_name="cmd.exe"
|
||||
OR process_name="microsoft.workflow.compiler.exe" OR process_name="runscripthelper.exe"
|
||||
OR process_name="makecab.exe" OR process_name="forfiles.exe" OR process_name="desktopimgdownldr.exe"
|
||||
OR process_name="control.exe" OR process_name="msbuild.exe" OR process_name="register-cimprovider.exe"
|
||||
OR process_name="tttracer.exe" OR process_name="ie4uinit.exe" OR process_name="sc.exe"
|
||||
OR process_name="bash.exe" OR process_name="hh.exe" OR process_name="cmstp.exe"
|
||||
OR process_name="mmc.exe" OR process_name="jsc.exe" OR process_name="scriptrunner.exe"
|
||||
OR process_name="odbcconf.exe" OR process_name="extexport.exe" OR process_name="msdt.exe"
|
||||
OR process_name="diskshadow.exe" OR process_name="extrac32.exe" OR process_name="eventvwr.exe"
|
||||
OR process_name="mavinject.exe" OR process_name="regasm.exe" OR process_name="gpscript.exe"
|
||||
OR process_name="rundll32.exe" OR process_name="regsvr32.exe" OR process_name="regedit.exe"
|
||||
OR process_name="msiexec.exe" OR process_name="gfxdownloadwrapper.exe" OR process_name="presentationhost.exe"
|
||||
OR process_name="regini.exe" OR process_name="wmic.exe" OR process_name="runonce.exe"
|
||||
OR process_name="syncappvpublishingserver.exe" OR process_name="verclsid.exe" OR
|
||||
process_name="psr.exe" OR process_name="infdefaultinstall.exe" OR process_name="explorer.exe"
|
||||
OR process_name="expand.exe" OR process_name="installutil.exe" OR process_name="netsh.exe"
|
||||
OR process_name="wab.exe" OR process_name="dnscmd.exe" OR process_name="at.exe"
|
||||
OR process_name="pcalua.exe" OR process_name="cmdkey.exe" OR process_name="msconfig.exe")
|
||||
| eval input = (-1)*log(output)
|
||||
| adaptive_threshold algorithm="gaussian" threshold=0.001 window=604800000L
|
||||
| where label AND input > mean
|
||||
| eval start_time = timestamp, end_time = timestamp, entities = mvappend(dest_device_id,
|
||||
dest_user_id), body = create_map(["process_name", process_name, "parent_process_name", parent_process_name, "input", input, "mean", mean, "variance", variance, "output", output, "cmd_line", cmd_line])
|
||||
| into write_ssa_detected_events();'
|
||||
how_to_implement: Collect endpoint data such as sysmon or 4688 events.
|
||||
known_false_positives: 'Some custom tools used by admins could be used rarely to launch
|
||||
remotely applications. This might trigger false positives at the beginning when
|
||||
it hasn''t collected yet enough data to construct the baseline.
|
||||
|
||||
'
|
||||
references: []
|
||||
tags:
|
||||
analytic_story:
|
||||
- Unusual Processes
|
||||
cis20:
|
||||
- CIS 8
|
||||
kill_chain_phases:
|
||||
- Exploitation
|
||||
mitre_attack_id:
|
||||
- T1203
|
||||
- T1059
|
||||
- T1053
|
||||
- T1072
|
||||
nist:
|
||||
- PR.PT
|
||||
- DE.CM
|
||||
product:
|
||||
- Splunk Behavioral Analytics
|
||||
required_fields:
|
||||
- process
|
||||
- process_name
|
||||
- parent_process_name
|
||||
- _time
|
||||
- dest_device_id
|
||||
- dest_user_id
|
||||
risk_severity: low
|
||||
security_domain: endpoint
|
||||
@@ -0,0 +1,11 @@
|
||||
name: Rare Parent/Child Process Relationship with LOLBAS - SSA Unit Test
|
||||
tests:
|
||||
- name: Rare Parent/Child Process Relationship with LOLBAS
|
||||
file: endpoint/ssa___rare_parent_process_relationship_lolbas.yml
|
||||
pass_condition: '@count_gt(0)'
|
||||
description: Test detection looking for LOLBAS processes spawned by other processes that are rarely seen together
|
||||
attack_data:
|
||||
- file_name: windows-sec-events.out
|
||||
data: https://raw.githubusercontent.com/splunk/attack_data/master/datasets/attack_techniques/T1003.003/atomic_red_team/windows-sec-events.out
|
||||
source: WinEventLog:Security
|
||||
|
||||
Reference in New Issue
Block a user