Merge branch 'develop' into nterl0k-t1036-lolbash-your-face
@@ -0,0 +1,40 @@
|
||||
name: build
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
jobs:
|
||||
build:
|
||||
#Note that the CircleCI job used a Container. The way to do this with Github Actions
|
||||
#is to first start up a Virtual Machine, then we can by following:
|
||||
# https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idcontainer
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out the repository code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11' #Available versions here - https://github.com/actions/python-versions/releases easy to change/make a matrix/use pypy
|
||||
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
|
||||
|
||||
|
||||
- name: Install Python Dependencies and ContentCTL and Atomic Red Team
|
||||
run: |
|
||||
pip install contentctl
|
||||
git clone --depth=1 --single-branch --branch=master https://github.com/redcanaryco/atomic-red-team.git
|
||||
|
||||
- name: Running build with enrichments
|
||||
run: |
|
||||
contentctl build --enrichments
|
||||
mkdir artifacts
|
||||
mv dist/DA-ESS-ContentUpdate-latest.tar.gz artifacts/
|
||||
|
||||
- name: store_artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: content-latest
|
||||
path: |
|
||||
artifacts/DA-ESS-ContentUpdate-latest.tar.gz
|
||||
@@ -1,347 +0,0 @@
|
||||
# name: detection-testing
|
||||
# on:
|
||||
# push:
|
||||
# pull_request:
|
||||
# types: [opened, reopened]
|
||||
# schedule:
|
||||
# - cron: "44 4 * * *"
|
||||
# jobs:
|
||||
|
||||
# validate-tag-if-present:
|
||||
# runs-on: ubuntu-latest
|
||||
|
||||
# steps:
|
||||
# - name: TAGGED, Validate that the tag is in the correct format
|
||||
|
||||
# run: |
|
||||
# echo "The GITHUB_REF: $GITHUB_REF"
|
||||
# #First check to see if the release is a tag
|
||||
# if [[ $GITHUB_REF =~ refs/tags/* ]]; then
|
||||
# #Yes, this is a tag, so we need to test to make sure that the tag
|
||||
# #is in the correct format (like v1.10.20)
|
||||
# if [[ $GITHUB_REF =~ refs/tags/v[0-9]+.[0-9]+.[0-9]+ ]]; then
|
||||
# echo "PASS: Tagged release with good format"
|
||||
# exit 0
|
||||
# else
|
||||
# echo "FAIL: Tagged release with bad format"
|
||||
# exit 1
|
||||
# fi
|
||||
# else
|
||||
# echo "PASS: Not a tagged release"
|
||||
# exit 0
|
||||
# fi
|
||||
|
||||
# quit-for-dependabot:
|
||||
# runs-on: ubuntu-latest
|
||||
# if: github.actor != 'dependabot[bot]' && github.actor != 'dependabot-preview[bot]'
|
||||
# steps:
|
||||
# - name: "Placeholder"
|
||||
# run: |
|
||||
# echo "yes it ran"
|
||||
|
||||
# docker-detection-testing-setup:
|
||||
# runs-on: ubuntu-latest
|
||||
# if: "!contains(github.ref, 'refs/tags/')" #don't run on tags - future steps won't run either since they depend on this job
|
||||
# needs: [validate-tag-if-present, quit-for-dependabot]
|
||||
# steps:
|
||||
# - name: Get branch and PR required for detection testing main.py
|
||||
# id: vars
|
||||
# run: |
|
||||
# echo "::set-output name=branch::${GITHUB_REF#refs/heads/}"
|
||||
|
||||
# - name: Checkout Repo
|
||||
# uses: actions/checkout@v2
|
||||
# #with:
|
||||
# # ref: develop
|
||||
|
||||
|
||||
|
||||
# - uses: actions/setup-python@v2
|
||||
# with:
|
||||
# python-version: '3.9' #Available versions here - https://github.com/actions/python-versions/releases easy to change/make a matrix/use pypy
|
||||
# architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
|
||||
# cache: 'pip'
|
||||
|
||||
# - name: Install Python Dependencies
|
||||
# run: |
|
||||
# python -m venv .venv
|
||||
# source .venv/bin/activate
|
||||
# python -m pip install wheel
|
||||
# python -m pip install -r requirements.txt
|
||||
|
||||
# - name: Run the CI
|
||||
# run: |
|
||||
# source .venv/bin/activate
|
||||
# cd bin/docker_detection_tester
|
||||
# echo "github.event.issue.pull_request : [${{ github.event.issue.pull_request }}]"
|
||||
# echo "github.event.pull_request.number : [${{ github.event.pull_request.number }}]"
|
||||
# echo "steps.vars.outputs.branch : [${{ steps.vars.outputs.branch }}]"
|
||||
# echo "github.event.pull_request.head.ref : [${{ github.event.pull_request.head.ref }}]"
|
||||
# echo "github.event_name : [${{ github.event_name }}]"
|
||||
|
||||
|
||||
# if [[ ${{ github.event_name }} == schedule ]]; then
|
||||
# # Note that scheduled actions ONLY run on the default branch, so it won't run on all other branches!
|
||||
# echo "Running a nightly test on all detections OR a commit was made directly to develop"
|
||||
# python detection_testing_execution.py run --branch develop --mode all --mock --config_file test_config_github_actions.json
|
||||
# elif [[ ! -z "${{ github.event.pull_request.head.ref }}" && ! -z "${{ github.event.pull_request.number }}" ]]; then
|
||||
# echo "Pull request from source branch [${{ github.event.pull_request.head.ref }}] for PR number [${{ github.event.issue.number }}]"
|
||||
# python detection_testing_execution.py run --branch ${{ github.event.pull_request.head.ref }} --pr_number ${{ github.event.pull_request.number }} --mode changes --mock --config_file test_config_github_actions.json
|
||||
# else
|
||||
# echo "Push from branch [${{ steps.vars.outputs.branch }}]"
|
||||
# python detection_testing_execution.py run --branch ${{ steps.vars.outputs.branch }} --mode changes --mock --config_file test_config_github_actions.json
|
||||
# fi
|
||||
|
||||
# mv *-test-run.json replicate_test.json
|
||||
# - name: Upload Test Results Files
|
||||
# uses: actions/upload-artifact@v2
|
||||
# with:
|
||||
# name: testing-results-config
|
||||
# path: |
|
||||
# bin/docker_detection_tester/prior_config/apps/DA-ESS-ContentUpdate-latest.tar.gz
|
||||
# bin/docker_detection_tester/prior_config/config_tests_0.json
|
||||
# bin/docker_detection_tester/prior_config/config_tests_1.json
|
||||
# bin/docker_detection_tester/prior_config/config_tests_2.json
|
||||
# bin/docker_detection_tester/prior_config/config_tests_3.json
|
||||
# bin/docker_detection_tester/prior_config/config_tests_4.json
|
||||
# bin/docker_detection_tester/prior_config/config_tests_5.json
|
||||
# bin/docker_detection_tester/prior_config/config_tests_6.json
|
||||
# bin/docker_detection_tester/prior_config/config_tests_7.json
|
||||
# bin/docker_detection_tester/prior_config/config_tests_8.json
|
||||
# bin/docker_detection_tester/prior_config/config_tests_9.json
|
||||
|
||||
# - name: Upload File to Enable Replication of the Test at a Different Time or Place
|
||||
# uses: actions/upload-artifact@v2
|
||||
# with:
|
||||
# name: replicate_test
|
||||
# path: |
|
||||
# bin/docker_detection_tester/replicate_test.json
|
||||
|
||||
# docker-detection-testing-execution:
|
||||
# runs-on: ubuntu-latest
|
||||
# if: "!contains(github.ref, 'refs/tags/')" #don't run on tags - future steps won't run either since they depend on this job
|
||||
# needs: [docker-detection-testing-setup]
|
||||
# strategy:
|
||||
# matrix:
|
||||
# manifest_filename: ["config_tests_0.json",
|
||||
# "config_tests_1.json",
|
||||
# "config_tests_2.json",
|
||||
# "config_tests_3.json",
|
||||
# "config_tests_4.json",
|
||||
# "config_tests_5.json",
|
||||
# "config_tests_6.json",
|
||||
# "config_tests_7.json",
|
||||
# "config_tests_8.json",
|
||||
# "config_tests_9.json"]
|
||||
# steps:
|
||||
# - name: Get branch and PR required for detection testing main.py
|
||||
# id: vars
|
||||
# run: |
|
||||
# echo "::set-output name=branch::${GITHUB_REF#refs/heads/}"
|
||||
|
||||
# - name: Checkout Repo
|
||||
# uses: actions/checkout@v2
|
||||
# #with:
|
||||
# # ref: develop
|
||||
|
||||
# - name: Download artifacts
|
||||
# uses: actions/download-artifact@v2
|
||||
# with:
|
||||
# name: testing-results-config
|
||||
# path: bin/docker_detection_tester/prior_config
|
||||
|
||||
|
||||
# - uses: actions/setup-python@v2
|
||||
# with:
|
||||
# python-version: '3.9' #Available versions here - https://github.com/actions/python-versions/releases easy to change/make a matrix/use pypy
|
||||
# architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
|
||||
# cache: 'pip'
|
||||
|
||||
# - name: Install Python Dependencies
|
||||
# run: |
|
||||
# python -m venv .venv
|
||||
# source .venv/bin/activate
|
||||
# python -m pip install wheel
|
||||
# python -m pip install -r requirements.txt
|
||||
|
||||
# - name: Run the CI
|
||||
# run: |
|
||||
# source .venv/bin/activate
|
||||
# cd bin/docker_detection_tester
|
||||
|
||||
|
||||
# python detection_testing_execution.py run -c prior_config/${{ matrix.manifest_filename}}
|
||||
|
||||
|
||||
# - name: Upload Test Results Files
|
||||
# uses: actions/upload-artifact@v2
|
||||
# with:
|
||||
# name: ${{ matrix.manifest_filename}}.results
|
||||
# path: |
|
||||
# bin/docker_detection_tester/test_results/success.csv
|
||||
# bin/docker_detection_tester/test_results/error.csv
|
||||
# bin/docker_detection_tester/test_results/failure.csv
|
||||
# bin/docker_detection_tester/test_results/combined.csv
|
||||
# bin/docker_detection_tester/test_results/success.json
|
||||
# bin/docker_detection_tester/test_results/error.json
|
||||
# bin/docker_detection_tester/test_results/failure.json
|
||||
# bin/docker_detection_tester/test_results/combined.json
|
||||
|
||||
# bin/docker_detection_tester/test_results/summary.json
|
||||
|
||||
# docker-detection-testing-execution-merge-results:
|
||||
# runs-on: ubuntu-latest
|
||||
# if: "!contains(github.ref, 'refs/tags/')" #don't run on tags - future steps won't run either since they depend on this job
|
||||
# needs: [docker-detection-testing-setup, docker-detection-testing-execution]
|
||||
|
||||
# steps:
|
||||
# - name: Get branch and PR required for detection testing main.py
|
||||
# id: vars
|
||||
# run: |
|
||||
# echo "::set-output name=branch::${GITHUB_REF#refs/heads/}"
|
||||
|
||||
# - name: Checkout Repo
|
||||
# uses: actions/checkout@v2
|
||||
# #with:
|
||||
# # ref: develop
|
||||
|
||||
# - name: Download artifacts
|
||||
# uses: actions/download-artifact@v2
|
||||
# with:
|
||||
# name: config_tests_0.json.results
|
||||
# path: bin/docker_detection_tester/results_0
|
||||
# - name: Download artifacts
|
||||
# uses: actions/download-artifact@v2
|
||||
# with:
|
||||
# name: config_tests_1.json.results
|
||||
# path: bin/docker_detection_tester/results_1
|
||||
# - name: Download artifacts
|
||||
# uses: actions/download-artifact@v2
|
||||
# with:
|
||||
# name: config_tests_2.json.results
|
||||
# path: bin/docker_detection_tester/results_2
|
||||
# - name: Download artifacts
|
||||
# uses: actions/download-artifact@v2
|
||||
# with:
|
||||
# name: config_tests_3.json.results
|
||||
# path: bin/docker_detection_tester/results_3
|
||||
# - name: Download artifacts
|
||||
# uses: actions/download-artifact@v2
|
||||
# with:
|
||||
# name: config_tests_4.json.results
|
||||
# path: bin/docker_detection_tester/results_4
|
||||
# - name: Download artifacts
|
||||
# uses: actions/download-artifact@v2
|
||||
# with:
|
||||
# name: config_tests_5.json.results
|
||||
# path: bin/docker_detection_tester/results_5
|
||||
# - name: Download artifacts
|
||||
# uses: actions/download-artifact@v2
|
||||
# with:
|
||||
# name: config_tests_6.json.results
|
||||
# path: bin/docker_detection_tester/results_6
|
||||
# - name: Download artifacts
|
||||
# uses: actions/download-artifact@v2
|
||||
# with:
|
||||
# name: config_tests_7.json.results
|
||||
# path: bin/docker_detection_tester/results_7
|
||||
# - name: Download artifacts
|
||||
# uses: actions/download-artifact@v2
|
||||
# with:
|
||||
# name: config_tests_8.json.results
|
||||
# path: bin/docker_detection_tester/results_8
|
||||
# - name: Download artifacts
|
||||
# uses: actions/download-artifact@v2
|
||||
# with:
|
||||
# name: config_tests_9.json.results
|
||||
# path: bin/docker_detection_tester/results_9
|
||||
|
||||
# - uses: actions/setup-python@v2
|
||||
# with:
|
||||
# python-version: '3.9' #Available versions here - https://github.com/actions/python-versions/releases easy to change/make a matrix/use pypy
|
||||
# architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
|
||||
# cache: 'pip'
|
||||
|
||||
# - name: Install Python Dependencies
|
||||
# run: |
|
||||
# python -m venv .venv
|
||||
# source .venv/bin/activate
|
||||
# python -m pip install wheel
|
||||
# python -m pip install -r requirements.txt
|
||||
|
||||
# - name: Merge Detections into single File
|
||||
# run: |
|
||||
# source .venv/bin/activate
|
||||
# cd bin/docker_detection_tester
|
||||
# python summarize_json.py --files results_*/summary.json --output_filename summary_test_results.json
|
||||
|
||||
|
||||
# - name: Upload Summary Test Results JSON
|
||||
# uses: actions/upload-artifact@v2
|
||||
# if: always()
|
||||
# with:
|
||||
# name: SummaryTestResults
|
||||
# path: |
|
||||
# bin/docker_detection_tester/summary_test_results.json
|
||||
|
||||
# - name: Upload Failures Manifest on Failure
|
||||
# uses: actions/upload-artifact@v2
|
||||
# if: failure()
|
||||
# with:
|
||||
# name: DetectionFailureManifest
|
||||
# path: |
|
||||
# bin/docker_detection_tester/detection_failure_manifest.json
|
||||
|
||||
|
||||
# #Always clean these up, they make the output messy
|
||||
# - name: Clean up intermediate Files
|
||||
# uses: geekyeggo/delete-artifact@v1
|
||||
# if: always()
|
||||
# with:
|
||||
# name: |
|
||||
# config_tests_0.json.results
|
||||
# config_tests_1.json.results
|
||||
# config_tests_2.json.results
|
||||
# config_tests_3.json.results
|
||||
# config_tests_4.json.results
|
||||
# config_tests_5.json.results
|
||||
# config_tests_6.json.results
|
||||
# config_tests_7.json.results
|
||||
# config_tests_8.json.results
|
||||
# config_tests_9.json.results
|
||||
|
||||
# - name: Log in to S3 for Artifact Uploads
|
||||
# if: ${{ github.event_name == 'schedule' }}
|
||||
# uses: aws-actions/configure-aws-credentials@v1
|
||||
# with:
|
||||
# aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
# aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
|
||||
# aws-region: us-west-2
|
||||
|
||||
# - name: Upload S3 Badge and Summary Artifacts for Nightly Scheduled Run
|
||||
# if: ${{ github.event_name == 'schedule' }}
|
||||
# run: |
|
||||
# cd bin/docker_detection_tester
|
||||
# python generate_detection_coverage_badge.py --input_summary_file summary_test_results.json --output_badge_file detection_coverage.svg --badge_string "Pass Rate"
|
||||
|
||||
|
||||
# #Upload artifact (summary test results)
|
||||
# aws s3 cp summary_test_results.json s3://security-content/reporting/summary_test_results.json
|
||||
|
||||
# #Since these reside in a public bucket, no need to explicitly mark as public
|
||||
# # make the file public since it is not by default
|
||||
# #aws s3api put-object-acl --bucket security-content --key reporting/summary_test_results.json --acl public-read
|
||||
|
||||
|
||||
# #Upload artifact (test results coverage badge)
|
||||
# aws s3 cp detection_coverage.svg s3://security-content/reporting/detection_coverage.svg
|
||||
|
||||
# #Since these reside in a public bucket, no need to explicitly mark as public
|
||||
# # make the file public since it is not by default
|
||||
# #aws s3api put-object-acl --bucket security-content --key reporting/detection_coverage.svg --acl public-read
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
"""
|
||||
A simple script formatting test_results/summary.yml to display on github actions
|
||||
"""
|
||||
|
||||
import yaml
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
|
||||
def main():
|
||||
|
||||
# Define the path to the YAML file
|
||||
# yaml_file_path = 'summary.yml'
|
||||
yaml_file_path = '/home/runner/work/security_content/security_content/test_results/summary.yml'
|
||||
|
||||
# Check if the YAML file exists
|
||||
if not os.path.exists(yaml_file_path):
|
||||
print(f"Error: The file {yaml_file_path} does not exist.")
|
||||
exit(1) # Exit with an error code
|
||||
|
||||
# Load the YAML file
|
||||
with open(yaml_file_path, 'r') as file:
|
||||
data = yaml.safe_load(file)
|
||||
|
||||
# Extract total_fail value and debug print it
|
||||
total_fail = data['summary']['total_fail']
|
||||
total_detections = data['summary']['total_detections']
|
||||
print("**Download the job artifacts of this run and view complete summary in test_results/summary.yml for troubleshooting failures.**\n")
|
||||
print(" 📝 **Experimental or manual_test detections are not tested** 📝 **\n")
|
||||
print(f"Extracted total_fail: [{total_fail}]\n")
|
||||
|
||||
# Print all unit test details first
|
||||
print(" 🏗️⚒️ **Unit Test Details:**\n")
|
||||
print(f"{'Name':<80} | {'Status':<6} | {'Test Type':<10} | {'Exception':<50}")
|
||||
print(f"{'----':<80} | {'------':<6} | {'---------':<10} | {'---------':<50}")
|
||||
for detection in data['tested_detections']:
|
||||
for test in detection['tests']:
|
||||
if test['test_type'].strip() == "unit": # Check if the test type is "unit"
|
||||
name = detection['name'].strip()
|
||||
status = 'PASS' if test['success'] else 'FAIL'
|
||||
test_type = test['test_type'].strip()
|
||||
exception = test.get('exception', 'N/A') # Get exception if exists, else 'N/A'
|
||||
if status == 'FAIL':
|
||||
print(f"{name:<80} | 🔴 {status:<6} | {test_type:<10} | {exception:<50}")
|
||||
else:
|
||||
print(f"{name:<80} | 🟢 {status:<6} | {test_type:<10} | {'-':<50}")
|
||||
# Print table footer
|
||||
# print(f"{'----':<80} | {'------':<6} | {'---------':<10} | {'---------':<50}")
|
||||
|
||||
# Check if total_fail is a valid integer and greater than or equal to one
|
||||
print("\n") # Print a newline for separation
|
||||
print("**Overall Status**")
|
||||
print("-------------------------------")
|
||||
# Continue with additional prints or other logic
|
||||
if int(total_fail) >=1:
|
||||
# Print the message in bold
|
||||
print("🔴 - **CI Failure: There are failed tests.**\n\n")
|
||||
sys.exit(1)
|
||||
if int(total_fail) < 1:
|
||||
print("🟢 - **CI Success: No failed tests.**\n\n")
|
||||
sys.exit(0)
|
||||
if int(total_detections) < 1:
|
||||
print("🔵 - **CI Success: No detections to test**\n\n")
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,68 @@
|
||||
name: unit-testing
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
jobs:
|
||||
unit-testing:
|
||||
runs-on: ubuntu-latest
|
||||
if: "!contains(github.ref, 'refs/tags/')" #don't run on tags - future steps won't run either since they depend on this job
|
||||
# needs: [validate-tag-if-present, quit-for-dependabot]
|
||||
steps:
|
||||
#For fork PRs, always check out security_content and the PR target in security content!
|
||||
- name: Check out the repository code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'splunk/security_content' #this should be the TARGET repo of the PR. we hardcode it for now
|
||||
ref: ${{ github.base_ref }}
|
||||
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11' #Available versions here - https://github.com/actions/python-versions/releases easy to change/make a matrix/use pypy
|
||||
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
|
||||
|
||||
- name: Install Python Dependencies and ContentCTL
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install contentctl
|
||||
|
||||
# Running contentctl test with a few arguments, before running the command make sure you checkout into the current branch of the pull request. This step only performs unit testing on all the changes against the target-branch. In most cases this target branch will be develop
|
||||
# Make sure we check out the PR, even if it actually lives in a fork
|
||||
# Instructions for pulling a PR were taken from:
|
||||
# https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally
|
||||
- name: Run ContentCTL test for changes against target branch
|
||||
run: |
|
||||
|
||||
echo "Current Branch (Head Ref): ${{ github.head_ref }}"
|
||||
echo "Target Branch (Base Ref): ${{ github.base_ref }}"
|
||||
git pull > /dev/null 2>&1
|
||||
git fetch origin pull/${{ github.event.pull_request.number }}/head:${{ github.head_ref }}
|
||||
#We must specifically get the PR's target branch from security_content, not the one that resides in the fork PR's forked repo
|
||||
git switch ${{ github.head_ref }}
|
||||
#git checkout ${{ github.head_ref }}
|
||||
#echo "The target branch for this PR is ${{ github.base_ref }}"
|
||||
contentctl test --disable-tqdm --no-enable-integration-testing --post-test-behavior never_pause mode:changes --mode.target-branch ${{ github.base_ref }}
|
||||
echo "contentctl test - COMPLETED"
|
||||
continue-on-error: true
|
||||
|
||||
# Store test_results/summary.yml and dist/DA-ESS-ContentUpdate-latest.tar.gz to job artifact-test_summary_results.zip
|
||||
- name: store_artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test_summary_results
|
||||
path: |
|
||||
test_results/summary.yml
|
||||
dist/DA-ESS-ContentUpdate-latest.tar.gz
|
||||
continue-on-error: true
|
||||
|
||||
# Print entire result summary so that the users can view it in the Github Actions logs
|
||||
- name: Print entire test_results/summary.yml
|
||||
run: cat test_results/summary.yml
|
||||
continue-on-error: true
|
||||
|
||||
# Run a simple custom script created to pretty print results in a markdown friendly format in Github Actions Summary
|
||||
- name: Check the test_results/summary.yml for pass/fail.
|
||||
run: |
|
||||
echo "This job will fail if there are failures in unit-testing"
|
||||
python .github/workflows/format_test_results.py >> $GITHUB_STEP_SUMMARY
|
||||
echo "The Unit testing is completed. See details in the unit-testing job summary UI "
|
||||
@@ -1,54 +0,0 @@
|
||||
name: validate-and-build
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
types: [opened, reopened]
|
||||
jobs:
|
||||
|
||||
validate-and-build:
|
||||
#Note that the CircleCI job used a Container. The way to do this with Github Actions
|
||||
#is to first start up a Virtual Machine, then we can by following:
|
||||
# https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idcontainer
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out the repository code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.9' #Available versions here - https://github.com/actions/python-versions/releases easy to change/make a matrix/use pypy
|
||||
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
|
||||
|
||||
- name: Install System Packages
|
||||
run: |
|
||||
sudo apt update -qq
|
||||
sudo apt install jq -qq
|
||||
|
||||
|
||||
- name: Install Python Dependencies and ContentCTL
|
||||
run: |
|
||||
pip3 install poetry
|
||||
git submodule update --init contentctl
|
||||
cd contentctl
|
||||
git checkout main
|
||||
poetry install
|
||||
|
||||
- name: content_ctl validate
|
||||
run: |
|
||||
cd contentctl
|
||||
poetry run contentctl -p ../ validate
|
||||
|
||||
- name: contentctl generate
|
||||
run: |
|
||||
cd contentctl
|
||||
poetry run contentctl -p ../ build
|
||||
cd ..
|
||||
mkdir artifacts
|
||||
mv dist/DA-ESS-ContentUpdate-latest.tar.gz artifacts/
|
||||
|
||||
- name: store_artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: content-latest
|
||||
path: |
|
||||
artifacts/DA-ESS-ContentUpdate-latest.tar.gz
|
||||
@@ -1,5 +1,6 @@
|
||||
# Ignore example files from contentctl tool
|
||||
apps/
|
||||
dist/
|
||||
test_results/
|
||||
detections/*/.yml.example
|
||||
stories/*.yml.example
|
||||
@@ -10,7 +11,7 @@ dist/DA-ESS-ContentUpdate-*.tar.gz
|
||||
dist/DA-ESS-ContentUpdate.tar.gz
|
||||
dist/ContentPack-*.appinspect_api_results.html
|
||||
dist/ContentPack-*.appinspect_api_results.json
|
||||
|
||||
atomic-red-team/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
default:
|
||||
image: docker-hub.repo.splunkdev.net/python:3.9
|
||||
image: docker-hub.repo.splunkdev.net/python:3.11
|
||||
|
||||
variables:
|
||||
EXTRACTO_VERSION:
|
||||
@@ -8,19 +8,20 @@ variables:
|
||||
SKIP_DOWNSTREAM_TESTING:
|
||||
value: "False"
|
||||
description: "If true, downstream testing will be suppressed (useful for debugging or forcing a release in an emergency)."
|
||||
ENABLE_INTEGRATION_TESTING:
|
||||
value: "True"
|
||||
description: "Flag indicating that integration testing should be performed. Defaults to True, may be suppressed in some workflows."
|
||||
|
||||
stages:
|
||||
- validate
|
||||
- generate
|
||||
- test
|
||||
- build
|
||||
- app_inspect
|
||||
- test
|
||||
- release
|
||||
|
||||
include:
|
||||
- local: "pipeline/.validate.yml"
|
||||
- local: "pipeline/.generate.yml"
|
||||
- local: "pipeline/.build.yml"
|
||||
- local: "pipeline/.app-inspect.yml"
|
||||
- local: "pipeline/.test.yml"
|
||||
- local: "pipeline/.app_inspect.yml"
|
||||
- local: "pipeline/.release.yml"
|
||||
- local: "pipeline/.post.yml"
|
||||
|
||||
|
||||
@@ -31,4 +31,8 @@
|
||||
*
|
||||
|
||||
* Are there any detections that we're promoting from validation to production in this package? If we're adding new any detections to help understand the over-firing detections, please indicate those as well
|
||||
*
|
||||
*
|
||||
|
||||
#### Checklist
|
||||
* [ ] Trigger a full-package ESCU integration test and confirm there are no regressions (see manually triggered jobs on the most recent push pipeline)
|
||||
* [ ] Trigger a SSA/BA integration test and confirm there are no regressions (see manually triggered jobs on the most recent push pipeline)
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
[submodule "contentctl"]
|
||||
path = contentctl
|
||||
url = https://github.com/splunk/contentctl.git
|
||||
ignore = all
|
||||
@@ -1,76 +1,89 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "contentctl new_detection",
|
||||
"type": "python",
|
||||
"name": "contentctl init",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/contentctl.py",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true,
|
||||
"args": ["-p", ".", "new_content", "-t", "detection"]
|
||||
"program": "${workspaceFolder}/.venv/bin/contentctl",
|
||||
"cwd": "${workspaceFolder}/../ddd/",
|
||||
"args": [
|
||||
"init"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "contentctl validate",
|
||||
"type": "python",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/contentctl.py",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true,
|
||||
"args": ["-p", ".", "validate", "-pr", "ESCU"]
|
||||
},
|
||||
{
|
||||
"name": "contentctl generate",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/contentctl.py",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true,
|
||||
"args": ["-p", ".", "generate", "-o", "dist/escu", "-pr", "ESCU"]
|
||||
},
|
||||
{
|
||||
"name": "contentctl docgen",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/contentctl.py",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true,
|
||||
"args": ["-p", ".", "docgen", "-o", "docs"]
|
||||
},
|
||||
{
|
||||
"name": "contentctl content_changer",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/contentctl.py",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true,
|
||||
"args": ["-p", "detections", "content_changer", "-cf", "fix_kill_chain"]
|
||||
},
|
||||
{
|
||||
"name": "contentctl convert",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/contentctl.py",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true,
|
||||
"args": ["-p", ".", "convert", "-dm", "ocsf", "-dp", "dev_ssa/endpoint/ssa___windows_wmiprvse_spawn_msbuild.yml", "-o", "ssa_detections/endpoint"]
|
||||
},
|
||||
{
|
||||
"name": "Python: Current File",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"console": "integratedTerminal",
|
||||
"program": "${workspaceFolder}/.venv/bin/contentctl",
|
||||
"cwd": "${workspaceFolder}/../",
|
||||
"args": [
|
||||
"--path",
|
||||
".",
|
||||
"--output",
|
||||
"docs",
|
||||
"-v"
|
||||
"validate"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "contentctl validate enrich",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/.venv/bin/contentctl",
|
||||
"cwd": "${workspaceFolder}/../",
|
||||
"args": [
|
||||
"validate",
|
||||
"--enrichments"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "contentctl build",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/.venv/bin/contentctl",
|
||||
"cwd": "${workspaceFolder}/../",
|
||||
"args": [
|
||||
"build"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "contentctl build enrich",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/.venv/bin/contentctl",
|
||||
"cwd": "${workspaceFolder}/../",
|
||||
"args": [
|
||||
"build",
|
||||
"--enrichments"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "contentctl test",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/.venv/bin/contentctl",
|
||||
"cwd": "${workspaceFolder}/../",
|
||||
"args": [
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "contentctl --help",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/.venv/bin/contentctl",
|
||||
"cwd": "${workspaceFolder}/../",
|
||||
"args": [
|
||||
"--help"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "contentctl test detection",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/.venv/bin/contentctl",
|
||||
"cwd": "${workspaceFolder}/../",
|
||||
"args": [
|
||||
"test",
|
||||
"mode:selected",
|
||||
"--mode.files",
|
||||
"detections/endpoint/3cx_supply_chain_attack_network_indicators.yml"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
<p align="center">
|
||||
<a href="https://github.com/splunk/security_content/releases">
|
||||
<img src="https://img.shields.io/github/v/release/splunk/security_content" /></a>
|
||||
<a href="https://github.com/splunk/security_content/actions/workflows/validate-and-build.yml/badge.svg?branch=develop">
|
||||
<img src="https://github.com/splunk/security_content/actions/workflows/validate-and-build.yml/badge.svg?branch=develop" /></a>
|
||||
<a href="https://github.com/splunk/security_content/actions/workflows/build.yml/badge.svg?branch=develop">
|
||||
<img src="https://github.com/splunk/security_content/actions/workflows/build.yml/badge.svg?branch=develop" /></a>
|
||||
<a href="https://github.com/splunk/security_content">
|
||||
<img src="https://security-content.s3-us-west-2.amazonaws.com/reporting/detection_count.svg" /></a>
|
||||
<a href="https://github.com/splunk/security_content">
|
||||
|
||||
@@ -4,4 +4,4 @@ This subscription service delivers pre-packaged Security Content for use with Sp
|
||||
|
||||
Requires Splunk Enterprise Security version 4.5 or greater.
|
||||
|
||||
For more information please visit the [Splunk ES Content Update user documentation](https://docs.splunk.com/Documentation/ESSOC).
|
||||
For more information please visit the [Splunk ES Content Update user documentation](https://docs.splunk.com/Documentation/ESSOC).
|
||||
@@ -1,20 +1,13 @@
|
||||
#############
|
||||
# Automatically generated by generator.py in splunk/security_content
|
||||
# On Date: 2024-04-17T22:08:10 UTC
|
||||
# Author: Splunk Threat Research Team - Splunk
|
||||
# Contact: research@splunk.com
|
||||
#############
|
||||
## Splunk app configuration file
|
||||
|
||||
[install]
|
||||
is_configured = false
|
||||
state = enabled
|
||||
state_change_requires_restart = false
|
||||
build = 20240417220604
|
||||
build = 16367
|
||||
|
||||
[triggers]
|
||||
reload.analytic_stories = simple
|
||||
reload.usage_searches = simple
|
||||
reload.use_case_library = simple
|
||||
reload.correlationsearches = simple
|
||||
reload.analyticstories = simple
|
||||
@@ -26,15 +19,12 @@ reload.es_investigations = simple
|
||||
|
||||
[launcher]
|
||||
author = Splunk
|
||||
version = 4.30.0
|
||||
version = 4.9.0
|
||||
description = Explore the Analytic Stories included with ES Content Updates.
|
||||
|
||||
[ui]
|
||||
is_visible = true
|
||||
label = DA-ESS-ContentUpdate
|
||||
label = ES Content Updates
|
||||
|
||||
[package]
|
||||
id = DA-ESS-ContentUpdate
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
[content-version]
|
||||
version = 4.9.0
|
||||
@@ -2,5 +2,6 @@
|
||||
<view name="escu_summary" default="true"/>
|
||||
<view name="feedback"/>
|
||||
<view name="search"/>
|
||||
<view name="dashboards"/>
|
||||
<a href="http://docs.splunk.com/Documentation/ESSOC">Docs</a>
|
||||
</nav>
|
||||
@@ -22,7 +22,13 @@
|
||||
<row>
|
||||
<panel>
|
||||
<html>
|
||||
<h2 style="color:red">Explore the Analytic Stories included with Splunk Security via <a href="https://www.splunk.com/en_us/resources/videos/splunk-enterprise-security-use-case-library.html">ES Use Case Library</a> or <a href="https://splunkbase.splunk.com/app/3435/">Splunk Security Essentials</a>.</h2>
|
||||
<div style="background-color: #f8d7da; border: 1px solid #f5c6cb; border-radius: 5px; padding: 15px; margin-bottom: 20px;">
|
||||
<h2 style="color: #721c24; margin: 0;">
|
||||
<i class="icon-info-circle" style="margin-right: 10px;"></i>
|
||||
Explore Splunk Security Content using
|
||||
<a href="/app/SplunkEnterpriseSecuritySuite/ess_use_case_library" style="color: #721c24; text-decoration: underline;">Splunk Enterprise Security</a>
|
||||
</h2>
|
||||
</div>
|
||||
</html>
|
||||
</panel>
|
||||
</row>
|
||||
@@ -1,5 +1,5 @@
|
||||
[replicationSettings:refineConf]
|
||||
replicate.analytic_stories = false
|
||||
|
||||
[replicationBlacklist]
|
||||
[replicationDenylist]
|
||||
excludeESCU = apps[/\\]DA-ESS-ContentUpdate[/\\]lookups[/\\]...
|
||||
@@ -0,0 +1,638 @@
|
||||
mitre_id,technique,tactics,groups
|
||||
T1059.010,AutoHotKey & AutoIT,Execution,APT39
|
||||
T1564.012,File/Path Exclusions,Defense Evasion,no
|
||||
T1027.013,Encrypted/Encoded File,Defense Evasion,APT18|APT19|APT28|APT32|APT33|APT39|BITTER|Blue Mockingbird|Dark Caracal|Darkhotel|Elderwood|Fox Kitten|Group5|Higaisa|Inception|Lazarus Group|Leviathan|Magic Hound|Malteiro|Metador|Mofang|Molerats|Moses Staff|OilRig|Putter Panda|Sidewinder|TA2541|TA505|TeamTNT|Threat Group-3390|Transparent Tribe|Tropic Trooper|Whitefly|menuPass
|
||||
T1574.014,AppDomainManager,Defense Evasion|Persistence|Privilege Escalation,no
|
||||
T1584.008,Network Devices,Resource Development,APT28|Volt Typhoon
|
||||
T1548.006,TCC Manipulation,Defense Evasion|Privilege Escalation,no
|
||||
T1588.007,Artificial Intelligence,Resource Development,no
|
||||
T1218.015,Electron Applications,Defense Evasion,no
|
||||
T1543.005,Container Service,Persistence|Privilege Escalation,no
|
||||
T1665,Hide Infrastructure,Command And Control,APT29
|
||||
T1216.002,SyncAppvPublishingServer,Defense Evasion,no
|
||||
T1556.009,Conditional Access Policies,Credential Access|Defense Evasion|Persistence,Scattered Spider
|
||||
T1027.012,LNK Icon Smuggling,Defense Evasion,no
|
||||
T1036.009,Break Process Trees,Defense Evasion,no
|
||||
T1555.006,Cloud Secrets Management Stores,Credential Access,no
|
||||
T1016.002,Wi-Fi Discovery,Discovery,Magic Hound
|
||||
T1566.004,Spearphishing Voice,Initial Access,no
|
||||
T1598.004,Spearphishing Voice,Reconnaissance,LAPSUS$|Scattered Spider
|
||||
T1578.005,Modify Cloud Compute Configurations,Defense Evasion,no
|
||||
T1659,Content Injection,Command And Control|Initial Access,MoustachedBouncer
|
||||
T1564.011,Ignore Process Interrupts,Defense Evasion,no
|
||||
T1657,Financial Theft,Impact,Akira|Cinnamon Tempest|FIN13|Malteiro|Scattered Spider|SilverTerrier
|
||||
T1656,Impersonation,Defense Evasion,LAPSUS$|Scattered Spider
|
||||
T1567.004,Exfiltration Over Webhook,Exfiltration,no
|
||||
T1098.006,Additional Container Cluster Roles,Persistence|Privilege Escalation,no
|
||||
T1654,Log Enumeration,Discovery,APT5|Volt Typhoon
|
||||
T1548.005,Temporary Elevated Cloud Access,Defense Evasion|Privilege Escalation,no
|
||||
T1653,Power Settings,Persistence,no
|
||||
T1021.008,Direct Cloud VM Connections,Lateral Movement,no
|
||||
T1562.012,Disable or Modify Linux Audit System,Defense Evasion,no
|
||||
T1556.008,Network Provider DLL,Credential Access|Defense Evasion|Persistence,no
|
||||
T1652,Device Driver Discovery,Discovery,no
|
||||
T1027.011,Fileless Storage,Defense Evasion,APT32|Turla
|
||||
T1027.010,Command Obfuscation,Defense Evasion,APT19|APT32|Aquatic Panda|Chimera|Cobalt Group|Ember Bear|FIN6|FIN7|FIN8|Fox Kitten|GOLD SOUTHFIELD|Gamaredon Group|HEXANE|LazyScripter|Leafminer|Magic Hound|MuddyWater|Patchwork|Sandworm Team|Sidewinder|Silence|TA505|TA551|Turla|Wizard Spider
|
||||
T1562.011,Spoof Security Alerting,Defense Evasion,no
|
||||
T1552.008,Chat Messages,Credential Access,LAPSUS$
|
||||
T1651,Cloud Administration Command,Execution,APT29
|
||||
T1650,Acquire Access,Resource Development,no
|
||||
T1036.008,Masquerade File Type,Defense Evasion,Volt Typhoon
|
||||
T1567.003,Exfiltration to Text Storage Sites,Exfiltration,no
|
||||
T1583.008,Malvertising,Resource Development,Mustard Tempest
|
||||
T1021.007,Cloud Services,Lateral Movement,APT29|Scattered Spider
|
||||
T1205.002,Socket Filters,Command And Control|Defense Evasion|Persistence,no
|
||||
T1608.006,SEO Poisoning,Resource Development,Mustard Tempest
|
||||
T1027.009,Embedded Payloads,Defense Evasion,no
|
||||
T1027.008,Stripped Payloads,Defense Evasion,no
|
||||
T1556.007,Hybrid Identity,Credential Access|Defense Evasion|Persistence,APT29
|
||||
T1546.016,Installer Packages,Persistence|Privilege Escalation,no
|
||||
T1027.007,Dynamic API Resolution,Defense Evasion,Lazarus Group
|
||||
T1593.003,Code Repositories,Reconnaissance,LAPSUS$
|
||||
T1649,Steal or Forge Authentication Certificates,Credential Access,APT29
|
||||
T1070.009,Clear Persistence,Defense Evasion,no
|
||||
T1070.008,Clear Mailbox Data,Defense Evasion,no
|
||||
T1584.007,Serverless,Resource Development,no
|
||||
T1583.007,Serverless,Resource Development,no
|
||||
T1070.007,Clear Network Connection History and Configurations,Defense Evasion,Volt Typhoon
|
||||
T1556.006,Multi-Factor Authentication,Credential Access|Defense Evasion|Persistence,Scattered Spider
|
||||
T1586.003,Cloud Accounts,Resource Development,APT29
|
||||
T1585.003,Cloud Accounts,Resource Development,no
|
||||
T1648,Serverless Execution,Execution,no
|
||||
T1647,Plist File Modification,Defense Evasion,no
|
||||
T1622,Debugger Evasion,Defense Evasion|Discovery,no
|
||||
T1621,Multi-Factor Authentication Request Generation,Credential Access,APT29|LAPSUS$|Scattered Spider
|
||||
T1505.005,Terminal Services DLL,Persistence,no
|
||||
T1557.003,DHCP Spoofing,Collection|Credential Access,no
|
||||
T1059.009,Cloud API,Execution,APT29|TeamTNT
|
||||
T1595.003,Wordlist Scanning,Reconnaissance,APT41|Volatile Cedar
|
||||
T1098.005,Device Registration,Persistence|Privilege Escalation,APT29
|
||||
T1574.013,KernelCallbackTable,Defense Evasion|Persistence|Privilege Escalation,Lazarus Group
|
||||
T1556.005,Reversible Encryption,Credential Access|Defense Evasion|Persistence,no
|
||||
T1055.015,ListPlanting,Defense Evasion|Privilege Escalation,no
|
||||
T1564.010,Process Argument Spoofing,Defense Evasion,no
|
||||
T1564.009,Resource Forking,Defense Evasion,no
|
||||
T1559.003,XPC Services,Execution,no
|
||||
T1562.010,Downgrade Attack,Defense Evasion,no
|
||||
T1547.015,Login Items,Persistence|Privilege Escalation,no
|
||||
T1620,Reflective Code Loading,Defense Evasion,Lazarus Group
|
||||
T1619,Cloud Storage Object Discovery,Discovery,no
|
||||
T1218.014,MMC,Defense Evasion,no
|
||||
T1218.013,Mavinject,Defense Evasion,no
|
||||
T1614.001,System Language Discovery,Discovery,Ke3chang|Malteiro
|
||||
T1615,Group Policy Discovery,Discovery,Turla
|
||||
T1036.007,Double File Extension,Defense Evasion,Mustang Panda
|
||||
T1562.009,Safe Mode Boot,Defense Evasion,no
|
||||
T1564.008,Email Hiding Rules,Defense Evasion,FIN4|Scattered Spider
|
||||
T1505.004,IIS Components,Persistence,no
|
||||
T1027.006,HTML Smuggling,Defense Evasion,APT29
|
||||
T1213.003,Code Repositories,Collection,APT41|LAPSUS$|Scattered Spider
|
||||
T1553.006,Code Signing Policy Modification,Defense Evasion,APT39|Turla
|
||||
T1614,System Location Discovery,Discovery,SideCopy
|
||||
T1613,Container and Resource Discovery,Discovery,TeamTNT
|
||||
T1552.007,Container API,Credential Access,no
|
||||
T1612,Build Image on Host,Defense Evasion,no
|
||||
T1611,Escape to Host,Privilege Escalation,TeamTNT
|
||||
T1204.003,Malicious Image,Execution,TeamTNT
|
||||
T1053.007,Container Orchestration Job,Execution|Persistence|Privilege Escalation,no
|
||||
T1610,Deploy Container,Defense Evasion|Execution,TeamTNT
|
||||
T1609,Container Administration Command,Execution,TeamTNT
|
||||
T1608.005,Link Target,Resource Development,LuminousMoth|Silent Librarian
|
||||
T1608.004,Drive-by Target,Resource Development,APT32|Dragonfly|FIN7|LuminousMoth|Mustard Tempest|Threat Group-3390|Transparent Tribe
|
||||
T1608.003,Install Digital Certificate,Resource Development,no
|
||||
T1608.002,Upload Tool,Resource Development,Threat Group-3390
|
||||
T1608.001,Upload Malware,Resource Development,APT32|BITTER|EXOTIC LILY|Earth Lusca|FIN7|Gamaredon Group|HEXANE|Kimsuky|LazyScripter|LuminousMoth|Mustang Panda|Mustard Tempest|SideCopy|TA2541|TA505|TeamTNT|Threat Group-3390
|
||||
T1608,Stage Capabilities,Resource Development,Mustang Panda
|
||||
T1016.001,Internet Connection Discovery,Discovery,APT29|FIN13|FIN8|Gamaredon Group|HAFNIUM|HEXANE|Magic Hound|TA2541|Turla
|
||||
T1553.005,Mark-of-the-Web Bypass,Defense Evasion,APT29|TA505
|
||||
T1555.005,Password Managers,Credential Access,Fox Kitten|LAPSUS$|Threat Group-3390
|
||||
T1484.002,Trust Modification,Defense Evasion|Privilege Escalation,Scattered Spider
|
||||
T1484.001,Group Policy Modification,Defense Evasion|Privilege Escalation,Cinnamon Tempest|Indrik Spider
|
||||
T1547.014,Active Setup,Persistence|Privilege Escalation,no
|
||||
T1606.002,SAML Tokens,Credential Access,no
|
||||
T1606.001,Web Cookies,Credential Access,no
|
||||
T1606,Forge Web Credentials,Credential Access,no
|
||||
T1555.004,Windows Credential Manager,Credential Access,OilRig|Stealth Falcon|Turla|Wizard Spider
|
||||
T1059.008,Network Device CLI,Execution,no
|
||||
T1602.002,Network Device Configuration Dump,Collection,no
|
||||
T1542.005,TFTP Boot,Defense Evasion|Persistence,no
|
||||
T1542.004,ROMMONkit,Defense Evasion|Persistence,no
|
||||
T1602.001,SNMP (MIB Dump),Collection,no
|
||||
T1602,Data from Configuration Repository,Collection,no
|
||||
T1601.002,Downgrade System Image,Defense Evasion,no
|
||||
T1601.001,Patch System Image,Defense Evasion,no
|
||||
T1601,Modify System Image,Defense Evasion,no
|
||||
T1600.002,Disable Crypto Hardware,Defense Evasion,no
|
||||
T1600.001,Reduce Key Space,Defense Evasion,no
|
||||
T1600,Weaken Encryption,Defense Evasion,no
|
||||
T1556.004,Network Device Authentication,Credential Access|Defense Evasion|Persistence,no
|
||||
T1599.001,Network Address Translation Traversal,Defense Evasion,no
|
||||
T1599,Network Boundary Bridging,Defense Evasion,no
|
||||
T1020.001,Traffic Duplication,Exfiltration,no
|
||||
T1557.002,ARP Cache Poisoning,Collection|Credential Access,Cleaver|LuminousMoth
|
||||
T1588.006,Vulnerabilities,Resource Development,Sandworm Team
|
||||
T1053.006,Systemd Timers,Execution|Persistence|Privilege Escalation,no
|
||||
T1562.008,Disable or Modify Cloud Logs,Defense Evasion,APT29
|
||||
T1547.012,Print Processors,Persistence|Privilege Escalation,Earth Lusca
|
||||
T1598.003,Spearphishing Link,Reconnaissance,APT28|APT32|Dragonfly|Kimsuky|Magic Hound|Mustang Panda|Patchwork|Sandworm Team|Sidewinder|Silent Librarian|ZIRCONIUM
|
||||
T1598.002,Spearphishing Attachment,Reconnaissance,Dragonfly|SideCopy|Sidewinder
|
||||
T1598.001,Spearphishing Service,Reconnaissance,no
|
||||
T1598,Phishing for Information,Reconnaissance,APT28|Scattered Spider|ZIRCONIUM
|
||||
T1597.002,Purchase Technical Data,Reconnaissance,LAPSUS$
|
||||
T1597.001,Threat Intel Vendors,Reconnaissance,no
|
||||
T1597,Search Closed Sources,Reconnaissance,EXOTIC LILY
|
||||
T1596.005,Scan Databases,Reconnaissance,APT41
|
||||
T1596.004,CDNs,Reconnaissance,no
|
||||
T1596.003,Digital Certificates,Reconnaissance,no
|
||||
T1596.001,DNS/Passive DNS,Reconnaissance,no
|
||||
T1596.002,WHOIS,Reconnaissance,no
|
||||
T1596,Search Open Technical Databases,Reconnaissance,no
|
||||
T1595.002,Vulnerability Scanning,Reconnaissance,APT28|APT29|APT41|Aquatic Panda|Dragonfly|Earth Lusca|Magic Hound|Sandworm Team|TeamTNT|Volatile Cedar
|
||||
T1595.001,Scanning IP Blocks,Reconnaissance,TeamTNT
|
||||
T1595,Active Scanning,Reconnaissance,no
|
||||
T1594,Search Victim-Owned Websites,Reconnaissance,EXOTIC LILY|Kimsuky|Sandworm Team|Silent Librarian
|
||||
T1593.002,Search Engines,Reconnaissance,Kimsuky
|
||||
T1593.001,Social Media,Reconnaissance,EXOTIC LILY|Kimsuky
|
||||
T1593,Search Open Websites/Domains,Reconnaissance,Sandworm Team
|
||||
T1592.004,Client Configurations,Reconnaissance,HAFNIUM
|
||||
T1592.003,Firmware,Reconnaissance,no
|
||||
T1592.002,Software,Reconnaissance,Andariel|Magic Hound|Sandworm Team
|
||||
T1592.001,Hardware,Reconnaissance,no
|
||||
T1592,Gather Victim Host Information,Reconnaissance,no
|
||||
T1591.004,Identify Roles,Reconnaissance,HEXANE|LAPSUS$
|
||||
T1591.003,Identify Business Tempo,Reconnaissance,no
|
||||
T1591.001,Determine Physical Locations,Reconnaissance,Magic Hound
|
||||
T1591.002,Business Relationships,Reconnaissance,Dragonfly|LAPSUS$|Sandworm Team
|
||||
T1591,Gather Victim Org Information,Reconnaissance,Kimsuky|Lazarus Group
|
||||
T1590.006,Network Security Appliances,Reconnaissance,no
|
||||
T1590.005,IP Addresses,Reconnaissance,Andariel|HAFNIUM|Magic Hound
|
||||
T1590.004,Network Topology,Reconnaissance,FIN13
|
||||
T1590.003,Network Trust Dependencies,Reconnaissance,no
|
||||
T1590.002,DNS,Reconnaissance,no
|
||||
T1590.001,Domain Properties,Reconnaissance,Sandworm Team
|
||||
T1590,Gather Victim Network Information,Reconnaissance,HAFNIUM
|
||||
T1589.003,Employee Names,Reconnaissance,APT41|Kimsuky|Sandworm Team|Silent Librarian
|
||||
T1589.002,Email Addresses,Reconnaissance,APT32|EXOTIC LILY|HAFNIUM|HEXANE|Kimsuky|LAPSUS$|Lazarus Group|Magic Hound|Sandworm Team|Silent Librarian|TA551
|
||||
T1589.001,Credentials,Reconnaissance,APT28|APT41|Chimera|LAPSUS$|Leviathan|Magic Hound
|
||||
T1589,Gather Victim Identity Information,Reconnaissance,APT32|FIN13|HEXANE|LAPSUS$|Magic Hound
|
||||
T1588.005,Exploits,Resource Development,Kimsuky
|
||||
T1588.004,Digital Certificates,Resource Development,BlackTech|Lazarus Group|LuminousMoth|Silent Librarian
|
||||
T1588.003,Code Signing Certificates,Resource Development,BlackTech|Ember Bear|FIN8|Threat Group-3390|Wizard Spider
|
||||
T1588.002,Tool,Resource Development,APT-C-36|APT1|APT19|APT28|APT29|APT32|APT33|APT38|APT39|APT41|Aoqin Dragon|Aquatic Panda|BITTER|BRONZE BUTLER|BackdoorDiplomacy|BlackTech|Blue Mockingbird|Carbanak|Chimera|Cinnamon Tempest|Cleaver|Cobalt Group|CopyKittens|DarkHydrus|DarkVishnya|Dragonfly|Earth Lusca|Ember Bear|FIN10|FIN13|FIN5|FIN6|FIN7|FIN8|Ferocious Kitten|GALLIUM|Gorgon Group|HEXANE|Inception|IndigoZebra|Ke3chang|Kimsuky|LAPSUS$|Lazarus Group|Leafminer|LuminousMoth|Magic Hound|Metador|Moses Staff|MuddyWater|POLONIUM|Patchwork|PittyTiger|Sandworm Team|Silence|Silent Librarian|TA2541|TA505|Threat Group-3390|Thrip|Turla|Volt Typhoon|WIRTE|Whitefly|Wizard Spider|menuPass
|
||||
T1588.001,Malware,Resource Development,APT1|Andariel|Aquatic Panda|BackdoorDiplomacy|Earth Lusca|LAPSUS$|LazyScripter|LuminousMoth|Metador|TA2541|TA505|Turla
|
||||
T1588,Obtain Capabilities,Resource Development,no
|
||||
T1587.004,Exploits,Resource Development,no
|
||||
T1587.003,Digital Certificates,Resource Development,APT29|PROMETHIUM
|
||||
T1587.002,Code Signing Certificates,Resource Development,PROMETHIUM|Patchwork
|
||||
T1587.001,Malware,Resource Development,APT29|Aoqin Dragon|Cleaver|FIN13|FIN7|Indrik Spider|Ke3chang|Kimsuky|Lazarus Group|LuminousMoth|Moses Staff|Sandworm Team|TeamTNT|Turla
|
||||
T1587,Develop Capabilities,Resource Development,Kimsuky
|
||||
T1586.002,Email Accounts,Resource Development,APT28|APT29|HEXANE|IndigoZebra|Kimsuky|LAPSUS$|Leviathan|Magic Hound
|
||||
T1586.001,Social Media Accounts,Resource Development,Leviathan|Sandworm Team
|
||||
T1586,Compromise Accounts,Resource Development,no
|
||||
T1585.002,Email Accounts,Resource Development,APT1|EXOTIC LILY|HEXANE|Indrik Spider|Kimsuky|Lazarus Group|Leviathan|Magic Hound|Mustang Panda|Sandworm Team|Silent Librarian|Wizard Spider
|
||||
T1585.001,Social Media Accounts,Resource Development,APT32|CURIUM|Cleaver|EXOTIC LILY|Fox Kitten|HEXANE|Kimsuky|Lazarus Group|Leviathan|Magic Hound|Sandworm Team
|
||||
T1585,Establish Accounts,Resource Development,APT17|Fox Kitten
|
||||
T1584.006,Web Services,Resource Development,Earth Lusca|Turla
|
||||
T1584.005,Botnet,Resource Development,Axiom|Sandworm Team
|
||||
T1584.004,Server,Resource Development,APT16|Dragonfly|Earth Lusca|Indrik Spider|Lazarus Group|Sandworm Team|Turla|Volt Typhoon
|
||||
T1584.003,Virtual Private Server,Resource Development,Turla
|
||||
T1584.002,DNS Server,Resource Development,LAPSUS$
|
||||
T1584.001,Domains,Resource Development,APT1|Kimsuky|Magic Hound|Mustard Tempest|SideCopy|Transparent Tribe
|
||||
T1583.006,Web Services,Resource Development,APT17|APT28|APT29|APT32|Confucius|Earth Lusca|FIN7|HAFNIUM|IndigoZebra|Kimsuky|Lazarus Group|LazyScripter|Magic Hound|MuddyWater|POLONIUM|TA2541|Turla|ZIRCONIUM
|
||||
T1583.005,Botnet,Resource Development,no
|
||||
T1583.004,Server,Resource Development,Earth Lusca|GALLIUM|Kimsuky|Mustard Tempest|Sandworm Team
|
||||
T1583.003,Virtual Private Server,Resource Development,APT28|Axiom|Dragonfly|HAFNIUM|LAPSUS$
|
||||
T1583.002,DNS Server,Resource Development,Axiom|HEXANE
|
||||
T1584,Compromise Infrastructure,Resource Development,no
|
||||
T1583.001,Domains,Resource Development,APT1|APT28|APT32|BITTER|Dragonfly|EXOTIC LILY|Earth Lusca|FIN7|Ferocious Kitten|Gamaredon Group|HEXANE|IndigoZebra|Kimsuky|Lazarus Group|LazyScripter|Leviathan|Magic Hound|Mustang Panda|Sandworm Team|Silent Librarian|TA2541|TA505|TeamTNT|Threat Group-3390|Transparent Tribe|Winnti Group|ZIRCONIUM|menuPass
|
||||
T1583,Acquire Infrastructure,Resource Development,Sandworm Team
|
||||
T1564.007,VBA Stomping,Defense Evasion,no
|
||||
T1558.004,AS-REP Roasting,Credential Access,no
|
||||
T1580,Cloud Infrastructure Discovery,Discovery,Scattered Spider
|
||||
T1218.012,Verclsid,Defense Evasion,no
|
||||
T1205.001,Port Knocking,Command And Control|Defense Evasion|Persistence,PROMETHIUM
|
||||
T1564.006,Run Virtual Instance,Defense Evasion,no
|
||||
T1564.005,Hidden File System,Defense Evasion,Equation|Strider
|
||||
T1556.003,Pluggable Authentication Modules,Credential Access|Defense Evasion|Persistence,no
|
||||
T1574.012,COR_PROFILER,Defense Evasion|Persistence|Privilege Escalation,Blue Mockingbird
|
||||
T1562.007,Disable or Modify Cloud Firewall,Defense Evasion,no
|
||||
T1098.004,SSH Authorized Keys,Persistence|Privilege Escalation,Earth Lusca|TeamTNT
|
||||
T1480.001,Environmental Keying,Defense Evasion,APT41|Equation
|
||||
T1059.007,JavaScript,Execution,APT32|Cobalt Group|Earth Lusca|Ember Bear|Evilnum|FIN6|FIN7|Higaisa|Indrik Spider|Kimsuky|LazyScripter|Leafminer|Molerats|MoustachedBouncer|MuddyWater|Sidewinder|Silence|TA505|Turla
|
||||
T1578.004,Revert Cloud Instance,Defense Evasion,no
|
||||
T1578.003,Delete Cloud Instance,Defense Evasion,LAPSUS$
|
||||
T1578.001,Create Snapshot,Defense Evasion,no
|
||||
T1578.002,Create Cloud Instance,Defense Evasion,LAPSUS$|Scattered Spider
|
||||
T1127.001,MSBuild,Defense Evasion,no
|
||||
T1027.005,Indicator Removal from Tools,Defense Evasion,APT3|Deep Panda|GALLIUM|OilRig|Patchwork|Turla
|
||||
T1562.006,Indicator Blocking,Defense Evasion,APT41|APT5
|
||||
T1573.002,Asymmetric Cryptography,Command And Control,Cobalt Group|FIN6|FIN8|OilRig|TA2541|Tropic Trooper
|
||||
T1573.001,Symmetric Cryptography,Command And Control,APT28|APT33|BRONZE BUTLER|Darkhotel|Higaisa|Inception|Lazarus Group|MuddyWater|Mustang Panda|Stealth Falcon|Volt Typhoon|ZIRCONIUM
|
||||
T1573,Encrypted Channel,Command And Control,APT29|BITTER|Magic Hound|Tropic Trooper
|
||||
T1027.004,Compile After Delivery,Defense Evasion,Gamaredon Group|MuddyWater|Rocke
|
||||
T1574.004,Dylib Hijacking,Defense Evasion|Persistence|Privilege Escalation,no
|
||||
T1546.015,Component Object Model Hijacking,Persistence|Privilege Escalation,APT28
|
||||
T1071.004,DNS,Command And Control,APT18|APT39|APT41|Chimera|Cobalt Group|FIN7|Ke3chang|LazyScripter|OilRig|Tropic Trooper
|
||||
T1071.003,Mail Protocols,Command And Control,APT28|APT32|Kimsuky|SilverTerrier|Turla
|
||||
T1071.002,File Transfer Protocols,Command And Control,APT41|Dragonfly|Kimsuky|SilverTerrier
|
||||
T1071.001,Web Protocols,Command And Control,APT18|APT19|APT28|APT32|APT33|APT37|APT38|APT39|APT41|BITTER|BRONZE BUTLER|Chimera|Cobalt Group|Confucius|Dark Caracal|FIN13|FIN4|FIN8|Gamaredon Group|HAFNIUM|Higaisa|Inception|Ke3chang|Kimsuky|Lazarus Group|LuminousMoth|Magic Hound|Metador|MuddyWater|Mustang Panda|OilRig|Orangeworm|Rancor|Rocke|Sandworm Team|Sidewinder|SilverTerrier|Stealth Falcon|TA505|TA551|TeamTNT|Threat Group-3390|Tropic Trooper|Turla|WIRTE|Windshift|Wizard Spider
|
||||
T1572,Protocol Tunneling,Command And Control,Chimera|Cinnamon Tempest|Cobalt Group|FIN13|FIN6|Fox Kitten|Leviathan|Magic Hound|OilRig
|
||||
T1048.003,Exfiltration Over Unencrypted Non-C2 Protocol,Exfiltration,APT32|APT33|FIN6|FIN8|Lazarus Group|OilRig|Thrip|Wizard Spider
|
||||
T1048.002,Exfiltration Over Asymmetric Encrypted Non-C2 Protocol,Exfiltration,APT28
|
||||
T1048.001,Exfiltration Over Symmetric Encrypted Non-C2 Protocol,Exfiltration,no
|
||||
T1001.003,Protocol Impersonation,Command And Control,Higaisa|Lazarus Group
|
||||
T1001.002,Steganography,Command And Control,Axiom
|
||||
T1001.001,Junk Data,Command And Control,APT28
|
||||
T1132.002,Non-Standard Encoding,Command And Control,no
|
||||
T1132.001,Standard Encoding,Command And Control,APT19|APT33|BRONZE BUTLER|HAFNIUM|Lazarus Group|MuddyWater|Patchwork|Sandworm Team|TA551|Tropic Trooper
|
||||
T1090.004,Domain Fronting,Command And Control,APT29
|
||||
T1090.003,Multi-hop Proxy,Command And Control,APT28|APT29|FIN4|Inception|Leviathan
|
||||
T1090.002,External Proxy,Command And Control,APT28|APT29|APT3|APT39|FIN5|GALLIUM|Lazarus Group|MuddyWater|Silence|Tonto Team|menuPass
|
||||
T1090.001,Internal Proxy,Command And Control,APT39|FIN13|Higaisa|Lazarus Group|Strider|Turla|Volt Typhoon
|
||||
T1102.003,One-Way Communication,Command And Control,Leviathan
|
||||
T1102.002,Bidirectional Communication,Command And Control,APT12|APT28|APT37|APT39|Carbanak|FIN7|HEXANE|Kimsuky|Lazarus Group|Magic Hound|MuddyWater|POLONIUM|Sandworm Team|Turla|ZIRCONIUM
|
||||
T1102.001,Dead Drop Resolver,Command And Control,APT41|BRONZE BUTLER|Patchwork|RTM|Rocke
|
||||
T1571,Non-Standard Port,Command And Control,APT-C-36|APT32|APT33|DarkVishnya|FIN7|Lazarus Group|Magic Hound|Rocke|Sandworm Team|Silence|WIRTE
|
||||
T1074.002,Remote Data Staging,Collection,APT28|Chimera|FIN6|FIN8|Leviathan|MoustachedBouncer|Threat Group-3390|ToddyCat|menuPass
|
||||
T1074.001,Local Data Staging,Collection,APT28|APT3|APT39|APT5|BackdoorDiplomacy|Chimera|Dragonfly|FIN13|FIN5|GALLIUM|Indrik Spider|Kimsuky|Lazarus Group|Leviathan|MuddyWater|Mustang Panda|Patchwork|Sidewinder|TeamTNT|Threat Group-3390|Volt Typhoon|Wizard Spider|menuPass
|
||||
T1078.004,Cloud Accounts,Defense Evasion|Initial Access|Persistence|Privilege Escalation,APT28|APT29|APT33|APT5|Ke3chang|LAPSUS$
|
||||
T1564.004,NTFS File Attributes,Defense Evasion,APT32
|
||||
T1564.003,Hidden Window,Defense Evasion,APT19|APT28|APT3|APT32|CopyKittens|DarkHydrus|Deep Panda|Gamaredon Group|Gorgon Group|Higaisa|Kimsuky|Magic Hound|Nomadic Octopus|ToddyCat
|
||||
T1078.003,Local Accounts,Defense Evasion|Initial Access|Persistence|Privilege Escalation,APT29|APT32|FIN10|FIN7|HAFNIUM|Kimsuky|PROMETHIUM|Tropic Trooper|Turla
|
||||
T1078.002,Domain Accounts,Defense Evasion|Initial Access|Persistence|Privilege Escalation,APT3|APT5|Chimera|Cinnamon Tempest|Indrik Spider|Magic Hound|Naikon|Sandworm Team|TA505|Threat Group-1314|ToddyCat|Volt Typhoon|Wizard Spider
|
||||
T1078.001,Default Accounts,Defense Evasion|Initial Access|Persistence|Privilege Escalation,FIN13|Magic Hound
|
||||
T1564.002,Hidden Users,Defense Evasion,Dragonfly|Kimsuky
|
||||
T1574.006,Dynamic Linker Hijacking,Defense Evasion|Persistence|Privilege Escalation,APT41|Rocke
|
||||
T1574.002,DLL Side-Loading,Defense Evasion|Persistence|Privilege Escalation,APT19|APT3|APT32|APT41|BRONZE BUTLER|BlackTech|Chimera|Cinnamon Tempest|Earth Lusca|FIN13|GALLIUM|Higaisa|Lazarus Group|LuminousMoth|MuddyWater|Mustang Panda|Naikon|Patchwork|SideCopy|Sidewinder|Threat Group-3390|Tropic Trooper|menuPass
|
||||
T1574.001,DLL Search Order Hijacking,Defense Evasion|Persistence|Privilege Escalation,APT41|Aquatic Panda|BackdoorDiplomacy|Cinnamon Tempest|Evilnum|RTM|Threat Group-3390|Tonto Team|Whitefly|menuPass
|
||||
T1574.008,Path Interception by Search Order Hijacking,Defense Evasion|Persistence|Privilege Escalation,no
|
||||
T1574.007,Path Interception by PATH Environment Variable,Defense Evasion|Persistence|Privilege Escalation,no
|
||||
T1574.009,Path Interception by Unquoted Path,Defense Evasion|Persistence|Privilege Escalation,no
|
||||
T1574.011,Services Registry Permissions Weakness,Defense Evasion|Persistence|Privilege Escalation,no
|
||||
T1574.005,Executable Installer File Permissions Weakness,Defense Evasion|Persistence|Privilege Escalation,no
|
||||
T1574.010,Services File Permissions Weakness,Defense Evasion|Persistence|Privilege Escalation,no
|
||||
T1574,Hijack Execution Flow,Defense Evasion|Persistence|Privilege Escalation,no
|
||||
T1069.001,Local Groups,Discovery,Chimera|HEXANE|OilRig|Tonto Team|Turla|Volt Typhoon|admin@338
|
||||
T1570,Lateral Tool Transfer,Lateral Movement,APT32|APT41|Aoqin Dragon|Chimera|FIN10|GALLIUM|Magic Hound|Sandworm Team|Turla|Volt Typhoon|Wizard Spider
|
||||
T1568.003,DNS Calculation,Command And Control,APT12
|
||||
T1204.002,Malicious File,Execution,APT-C-36|APT12|APT19|APT28|APT29|APT30|APT32|APT33|APT37|APT38|APT39|Ajax Security Team|Andariel|Aoqin Dragon|BITTER|BRONZE BUTLER|BlackTech|CURIUM|Cobalt Group|Confucius|Dark Caracal|DarkHydrus|Darkhotel|Dragonfly|EXOTIC LILY|Earth Lusca|Elderwood|Ember Bear|FIN4|FIN6|FIN7|FIN8|Ferocious Kitten|Gallmaker|Gamaredon Group|Gorgon Group|HEXANE|Higaisa|Inception|IndigoZebra|Indrik Spider|Kimsuky|Lazarus Group|LazyScripter|Leviathan|Machete|Magic Hound|Malteiro|Mofang|Molerats|MuddyWater|Mustang Panda|Naikon|Nomadic Octopus|OilRig|PLATINUM|PROMETHIUM|Patchwork|RTM|Rancor|Sandworm Team|SideCopy|Sidewinder|Silence|TA2541|TA459|TA505|TA551|The White Company|Threat Group-3390|Tonto Team|Transparent Tribe|Tropic Trooper|WIRTE|Whitefly|Windshift|Wizard Spider|admin@338|menuPass
|
||||
T1204.001,Malicious Link,Execution,APT28|APT29|APT3|APT32|APT33|APT39|BlackTech|Cobalt Group|Confucius|EXOTIC LILY|Earth Lusca|Elderwood|Ember Bear|Evilnum|FIN4|FIN7|FIN8|Kimsuky|LazyScripter|Leviathan|LuminousMoth|Machete|Magic Hound|Mofang|Molerats|MuddyWater|Mustang Panda|Mustard Tempest|OilRig|Patchwork|Sandworm Team|Sidewinder|TA2541|TA505|Transparent Tribe|Turla|Windshift|Wizard Spider|ZIRCONIUM
|
||||
T1195.003,Compromise Hardware Supply Chain,Initial Access,no
|
||||
T1195.002,Compromise Software Supply Chain,Initial Access,APT41|Cobalt Group|Dragonfly|FIN7|GOLD SOUTHFIELD|Sandworm Team|Threat Group-3390
|
||||
T1195.001,Compromise Software Dependencies and Development Tools,Initial Access,no
|
||||
T1568.001,Fast Flux DNS,Command And Control,TA505|menuPass
|
||||
T1052.001,Exfiltration over USB,Exfiltration,Mustang Panda|Tropic Trooper
|
||||
T1569.002,Service Execution,Execution,APT32|APT38|APT39|APT41|Blue Mockingbird|Chimera|FIN6|Ke3chang|Silence|Wizard Spider
|
||||
T1569.001,Launchctl,Execution,no
|
||||
T1569,System Services,Execution,TeamTNT
|
||||
T1568.002,Domain Generation Algorithms,Command And Control,APT41|TA551
|
||||
T1568,Dynamic Resolution,Command And Control,APT29|BITTER|Gamaredon Group|TA2541|Transparent Tribe
|
||||
T1011.001,Exfiltration Over Bluetooth,Exfiltration,no
|
||||
T1567.002,Exfiltration to Cloud Storage,Exfiltration,Akira|Chimera|Cinnamon Tempest|Confucius|Earth Lusca|FIN7|HAFNIUM|HEXANE|Kimsuky|Leviathan|LuminousMoth|POLONIUM|Scattered Spider|Threat Group-3390|ToddyCat|Turla|Wizard Spider|ZIRCONIUM
|
||||
T1567.001,Exfiltration to Code Repository,Exfiltration,no
|
||||
T1059.006,Python,Execution,APT29|APT37|APT39|BRONZE BUTLER|Cinnamon Tempest|Dragonfly|Earth Lusca|Kimsuky|Machete|MuddyWater|Rocke|Tonto Team|Turla|ZIRCONIUM
|
||||
T1059.005,Visual Basic,Execution,APT-C-36|APT32|APT33|APT37|APT38|APT39|BRONZE BUTLER|Cobalt Group|Confucius|Earth Lusca|FIN13|FIN4|FIN7|Gamaredon Group|Gorgon Group|HEXANE|Higaisa|Inception|Kimsuky|Lazarus Group|LazyScripter|Leviathan|Machete|Magic Hound|Malteiro|Molerats|MuddyWater|Mustang Panda|OilRig|Patchwork|Rancor|Sandworm Team|SideCopy|Sidewinder|Silence|TA2541|TA459|TA505|Transparent Tribe|Turla|WIRTE|Windshift
|
||||
T1059.004,Unix Shell,Execution,APT41|Rocke|TeamTNT
|
||||
T1059.003,Windows Command Shell,Execution,APT1|APT18|APT28|APT3|APT32|APT37|APT38|APT41|APT5|Aquatic Panda|BRONZE BUTLER|Blue Mockingbird|Chimera|Cinnamon Tempest|Cobalt Group|Dark Caracal|Darkhotel|Dragonfly|Ember Bear|FIN10|FIN13|FIN6|FIN7|FIN8|Fox Kitten|GALLIUM|Gamaredon Group|Gorgon Group|HAFNIUM|Higaisa|Indrik Spider|Ke3chang|Kimsuky|Lazarus Group|LazyScripter|Machete|Magic Hound|Metador|MuddyWater|Mustang Panda|Nomadic Octopus|OilRig|Patchwork|Rancor|Silence|Sowbug|Suckfly|TA505|TA551|TeamTNT|Threat Group-1314|Threat Group-3390|ToddyCat|Tropic Trooper|Turla|Volt Typhoon|Wizard Spider|ZIRCONIUM|admin@338|menuPass
|
||||
T1059.002,AppleScript,Execution,no
|
||||
T1059.001,PowerShell,Execution,APT19|APT28|APT29|APT3|APT32|APT33|APT38|APT39|APT41|APT5|Aquatic Panda|BRONZE BUTLER|Blue Mockingbird|Chimera|Cinnamon Tempest|Cobalt Group|Confucius|CopyKittens|DarkHydrus|DarkVishnya|Deep Panda|Dragonfly|Earth Lusca|Ember Bear|FIN10|FIN13|FIN6|FIN7|FIN8|Fox Kitten|GALLIUM|GOLD SOUTHFIELD|Gallmaker|Gamaredon Group|Gorgon Group|HAFNIUM|HEXANE|Inception|Indrik Spider|Kimsuky|Lazarus Group|LazyScripter|Leviathan|Magic Hound|Molerats|MoustachedBouncer|MuddyWater|Mustang Panda|Nomadic Octopus|OilRig|Patchwork|Poseidon Group|Sandworm Team|Sidewinder|Silence|Stealth Falcon|TA2541|TA459|TA505|TeamTNT|Threat Group-3390|Thrip|ToddyCat|Tonto Team|Turla|Volt Typhoon|WIRTE|Wizard Spider|menuPass
|
||||
T1567,Exfiltration Over Web Service,Exfiltration,APT28|Magic Hound
|
||||
T1497.003,Time Based Evasion,Defense Evasion|Discovery,no
|
||||
T1497.002,User Activity Based Checks,Defense Evasion|Discovery,Darkhotel|FIN7
|
||||
T1497.001,System Checks,Defense Evasion|Discovery,Darkhotel|Evilnum|OilRig|Volt Typhoon
|
||||
T1498.002,Reflection Amplification,Impact,no
|
||||
T1498.001,Direct Network Flood,Impact,no
|
||||
T1566.003,Spearphishing via Service,Initial Access,APT29|Ajax Security Team|CURIUM|Dark Caracal|EXOTIC LILY|FIN6|Lazarus Group|Magic Hound|OilRig|ToddyCat|Windshift
|
||||
T1566.002,Spearphishing Link,Initial Access,APT1|APT28|APT29|APT3|APT32|APT33|APT39|BlackTech|Cobalt Group|Confucius|EXOTIC LILY|Earth Lusca|Elderwood|Ember Bear|Evilnum|FIN4|FIN7|FIN8|Kimsuky|Lazarus Group|LazyScripter|Leviathan|LuminousMoth|Machete|Magic Hound|Mofang|Molerats|MuddyWater|Mustang Panda|Mustard Tempest|OilRig|Patchwork|Sandworm Team|Sidewinder|TA2541|TA505|Transparent Tribe|Turla|Windshift|Wizard Spider|ZIRCONIUM
|
||||
T1566.001,Spearphishing Attachment,Initial Access,APT-C-36|APT1|APT12|APT19|APT28|APT29|APT30|APT32|APT33|APT37|APT38|APT39|APT41|Ajax Security Team|Andariel|BITTER|BRONZE BUTLER|BlackTech|Cobalt Group|Confucius|DarkHydrus|Darkhotel|Dragonfly|EXOTIC LILY|Elderwood|Ember Bear|FIN4|FIN6|FIN7|FIN8|Ferocious Kitten|Gallmaker|Gamaredon Group|Gorgon Group|Higaisa|Inception|IndigoZebra|Kimsuky|Lazarus Group|LazyScripter|Leviathan|Machete|Malteiro|Mofang|Molerats|MuddyWater|Mustang Panda|Naikon|Nomadic Octopus|OilRig|PLATINUM|Patchwork|RTM|Rancor|Sandworm Team|SideCopy|Sidewinder|Silence|TA2541|TA459|TA505|TA551|The White Company|Threat Group-3390|Tonto Team|Transparent Tribe|Tropic Trooper|WIRTE|Windshift|Wizard Spider|admin@338|menuPass
|
||||
T1566,Phishing,Initial Access,Axiom|GOLD SOUTHFIELD
|
||||
T1565.003,Runtime Data Manipulation,Impact,APT38
|
||||
T1565.002,Transmitted Data Manipulation,Impact,APT38
|
||||
T1565.001,Stored Data Manipulation,Impact,APT38
|
||||
T1565,Data Manipulation,Impact,FIN13
|
||||
T1564.001,Hidden Files and Directories,Defense Evasion,APT28|APT32|FIN13|HAFNIUM|Lazarus Group|LuminousMoth|Mustang Panda|Rocke|Transparent Tribe|Tropic Trooper
|
||||
T1564,Hide Artifacts,Defense Evasion,no
|
||||
T1563.002,RDP Hijacking,Lateral Movement,Axiom
|
||||
T1563.001,SSH Hijacking,Lateral Movement,no
|
||||
T1563,Remote Service Session Hijacking,Lateral Movement,no
|
||||
T1518.001,Security Software Discovery,Discovery,APT38|Aquatic Panda|Cobalt Group|Darkhotel|FIN8|Kimsuky|Malteiro|MuddyWater|Naikon|Patchwork|Rocke|SideCopy|Sidewinder|TA2541|TeamTNT|The White Company|ToddyCat|Tropic Trooper|Turla|Windshift|Wizard Spider
|
||||
T1069.003,Cloud Groups,Discovery,no
|
||||
T1069.002,Domain Groups,Discovery,Dragonfly|FIN7|Inception|Ke3chang|LAPSUS$|OilRig|ToddyCat|Turla|Volt Typhoon
|
||||
T1087.004,Cloud Account,Discovery,APT29
|
||||
T1087.003,Email Account,Discovery,Magic Hound|Sandworm Team|TA505
|
||||
T1087.002,Domain Account,Discovery,APT41|BRONZE BUTLER|Chimera|Dragonfly|FIN13|FIN6|Fox Kitten|Ke3chang|LAPSUS$|MuddyWater|OilRig|Poseidon Group|Sandworm Team|Scattered Spider|ToddyCat|Turla|Volt Typhoon|Wizard Spider|menuPass
|
||||
T1087.001,Local Account,Discovery,APT1|APT3|APT32|APT41|Chimera|Fox Kitten|Ke3chang|Moses Staff|OilRig|Poseidon Group|Threat Group-3390|Turla|admin@338
|
||||
T1553.004,Install Root Certificate,Defense Evasion,no
|
||||
T1562.004,Disable or Modify System Firewall,Defense Evasion,APT38|Carbanak|Dragonfly|Kimsuky|Lazarus Group|Magic Hound|Moses Staff|Rocke|TeamTNT|ToddyCat
|
||||
T1562.003,Impair Command History Logging,Defense Evasion,APT38
|
||||
T1562.002,Disable Windows Event Logging,Defense Evasion,Magic Hound|Threat Group-3390
|
||||
T1562.001,Disable or Modify Tools,Defense Evasion,Aquatic Panda|BRONZE BUTLER|Ember Bear|FIN6|Gamaredon Group|Gorgon Group|Indrik Spider|Kimsuky|Lazarus Group|Magic Hound|MuddyWater|Putter Panda|Rocke|TA2541|TA505|TeamTNT|Turla|Wizard Spider
|
||||
T1562,Impair Defenses,Defense Evasion,Magic Hound
|
||||
T1003.004,LSA Secrets,Credential Access,APT29|APT33|Dragonfly|Ke3chang|Leafminer|MuddyWater|OilRig|Threat Group-3390|menuPass
|
||||
T1003.005,Cached Domain Credentials,Credential Access,APT33|Leafminer|MuddyWater|OilRig
|
||||
T1561.002,Disk Structure Wipe,Impact,APT37|APT38|Lazarus Group|Sandworm Team
|
||||
T1561.001,Disk Content Wipe,Impact,Lazarus Group
|
||||
T1561,Disk Wipe,Impact,no
|
||||
T1560.003,Archive via Custom Method,Collection,CopyKittens|FIN6|Kimsuky|Lazarus Group|Mustang Panda
|
||||
T1560.002,Archive via Library,Collection,Lazarus Group|Threat Group-3390
|
||||
T1560.001,Archive via Utility,Collection,APT1|APT28|APT3|APT33|APT39|APT41|APT5|Akira|Aquatic Panda|BRONZE BUTLER|Chimera|CopyKittens|Earth Lusca|FIN13|FIN8|Fox Kitten|GALLIUM|Gallmaker|HAFNIUM|Ke3chang|Kimsuky|Magic Hound|MuddyWater|Mustang Panda|Sowbug|ToddyCat|Turla|Volt Typhoon|Wizard Spider|menuPass
|
||||
T1560,Archive Collected Data,Collection,APT28|APT32|Axiom|Dragonfly|FIN6|Ke3chang|Lazarus Group|Leviathan|LuminousMoth|Patchwork|menuPass
|
||||
T1499.004,Application or System Exploitation,Impact,no
|
||||
T1499.003,Application Exhaustion Flood,Impact,no
|
||||
T1499.002,Service Exhaustion Flood,Impact,no
|
||||
T1499.001,OS Exhaustion Flood,Impact,no
|
||||
T1491.002,External Defacement,Impact,Sandworm Team
|
||||
T1491.001,Internal Defacement,Impact,Gamaredon Group|Lazarus Group
|
||||
T1114.003,Email Forwarding Rule,Collection,Kimsuky|LAPSUS$|Silent Librarian
|
||||
T1114.002,Remote Email Collection,Collection,APT1|APT28|APT29|Chimera|Dragonfly|FIN4|HAFNIUM|Ke3chang|Kimsuky|Leafminer|Magic Hound
|
||||
T1114.001,Local Email Collection,Collection,APT1|Chimera|Magic Hound
|
||||
T1134.005,SID-History Injection,Defense Evasion|Privilege Escalation,no
|
||||
T1134.004,Parent PID Spoofing,Defense Evasion|Privilege Escalation,no
|
||||
T1134.003,Make and Impersonate Token,Defense Evasion|Privilege Escalation,FIN13
|
||||
T1134.002,Create Process with Token,Defense Evasion|Privilege Escalation,Lazarus Group|Turla
|
||||
T1134.001,Token Impersonation/Theft,Defense Evasion|Privilege Escalation,APT28|FIN8
|
||||
T1213.002,Sharepoint,Collection,APT28|Akira|Chimera|Ke3chang|LAPSUS$
|
||||
T1213.001,Confluence,Collection,LAPSUS$
|
||||
T1555.003,Credentials from Web Browsers,Credential Access,APT3|APT33|APT37|APT41|Ajax Security Team|FIN6|HEXANE|Inception|Kimsuky|LAPSUS$|Leafminer|Malteiro|Molerats|MuddyWater|OilRig|Patchwork|Sandworm Team|Stealth Falcon|TA505|ZIRCONIUM
|
||||
T1555.002,Securityd Memory,Credential Access,no
|
||||
T1555.001,Keychain,Credential Access,no
|
||||
T1559.002,Dynamic Data Exchange,Execution,APT28|APT37|BITTER|Cobalt Group|FIN7|Gallmaker|Leviathan|MuddyWater|Patchwork|Sidewinder|TA505
|
||||
T1559.001,Component Object Model,Execution,Gamaredon Group|MuddyWater
|
||||
T1559,Inter-Process Communication,Execution,no
|
||||
T1558.002,Silver Ticket,Credential Access,no
|
||||
T1558.001,Golden Ticket,Credential Access,Ke3chang
|
||||
T1558,Steal or Forge Kerberos Tickets,Credential Access,no
|
||||
T1557.001,LLMNR/NBT-NS Poisoning and SMB Relay,Collection|Credential Access,Lazarus Group|Wizard Spider
|
||||
T1557,Adversary-in-the-Middle,Collection|Credential Access,Kimsuky
|
||||
T1556.002,Password Filter DLL,Credential Access|Defense Evasion|Persistence,Strider
|
||||
T1556.001,Domain Controller Authentication,Credential Access|Defense Evasion|Persistence,Chimera
|
||||
T1556,Modify Authentication Process,Credential Access|Defense Evasion|Persistence,FIN13
|
||||
T1056.004,Credential API Hooking,Collection|Credential Access,PLATINUM
|
||||
T1056.003,Web Portal Capture,Collection|Credential Access,no
|
||||
T1056.002,GUI Input Capture,Collection|Credential Access,FIN4
|
||||
T1056.001,Keylogging,Collection|Credential Access,APT28|APT3|APT32|APT38|APT39|APT41|APT5|Ajax Security Team|Darkhotel|FIN13|FIN4|Group5|HEXANE|Ke3chang|Kimsuky|Lazarus Group|Magic Hound|OilRig|PLATINUM|Sandworm Team|Sowbug|Threat Group-3390|Tonto Team|menuPass
|
||||
T1555,Credentials from Password Stores,Credential Access,APT33|APT39|Evilnum|FIN6|HEXANE|Leafminer|Malteiro|MuddyWater|OilRig|Stealth Falcon|Volt Typhoon
|
||||
T1552.005,Cloud Instance Metadata API,Credential Access,TeamTNT
|
||||
T1003.008,/etc/passwd and /etc/shadow,Credential Access,no
|
||||
T1003.007,Proc Filesystem,Credential Access,no
|
||||
T1003.006,DCSync,Credential Access,Earth Lusca|LAPSUS$
|
||||
T1558.003,Kerberoasting,Credential Access,FIN7|Wizard Spider
|
||||
T1552.006,Group Policy Preferences,Credential Access,APT33|Wizard Spider
|
||||
T1003.003,NTDS,Credential Access,APT28|APT41|Chimera|Dragonfly|FIN13|FIN6|Fox Kitten|HAFNIUM|Ke3chang|LAPSUS$|Mustang Panda|Sandworm Team|Scattered Spider|Volt Typhoon|Wizard Spider|menuPass
|
||||
T1003.002,Security Account Manager,Credential Access,APT29|APT41|APT5|Dragonfly|FIN13|GALLIUM|Ke3chang|Threat Group-3390|Wizard Spider|menuPass
|
||||
T1003.001,LSASS Memory,Credential Access,APT1|APT28|APT3|APT32|APT33|APT39|APT41|APT5|Aquatic Panda|BRONZE BUTLER|Blue Mockingbird|Cleaver|Earth Lusca|FIN13|FIN6|FIN8|Fox Kitten|GALLIUM|HAFNIUM|Indrik Spider|Ke3chang|Kimsuky|Leafminer|Leviathan|Magic Hound|MuddyWater|OilRig|PLATINUM|Sandworm Team|Silence|Threat Group-3390|Volt Typhoon|Whitefly|Wizard Spider
|
||||
T1110.004,Credential Stuffing,Credential Access,Chimera
|
||||
T1110.003,Password Spraying,Credential Access,APT28|APT29|APT33|Chimera|HEXANE|Lazarus Group|Leafminer|Silent Librarian
|
||||
T1110.002,Password Cracking,Credential Access,APT3|APT41|Dragonfly|FIN6
|
||||
T1110.001,Password Guessing,Credential Access,APT28|APT29
|
||||
T1021.006,Windows Remote Management,Lateral Movement,Chimera|FIN13|Threat Group-3390|Wizard Spider
|
||||
T1021.005,VNC,Lateral Movement,FIN7|Fox Kitten|GCMAN|Gamaredon Group
|
||||
T1021.004,SSH,Lateral Movement,APT39|APT5|BlackTech|FIN13|FIN7|Fox Kitten|GCMAN|Lazarus Group|Leviathan|OilRig|Rocke|TeamTNT|menuPass
|
||||
T1021.003,Distributed Component Object Model,Lateral Movement,no
|
||||
T1021.002,SMB/Windows Admin Shares,Lateral Movement,APT28|APT3|APT32|APT39|APT41|Blue Mockingbird|Chimera|Cinnamon Tempest|Deep Panda|FIN13|FIN8|Fox Kitten|Ke3chang|Lazarus Group|Moses Staff|Orangeworm|Sandworm Team|Threat Group-1314|ToddyCat|Turla|Wizard Spider
|
||||
T1021.001,Remote Desktop Protocol,Lateral Movement,APT1|APT3|APT39|APT41|APT5|Axiom|Blue Mockingbird|Chimera|Cobalt Group|Dragonfly|FIN10|FIN13|FIN6|FIN7|FIN8|Fox Kitten|HEXANE|Kimsuky|Lazarus Group|Leviathan|Magic Hound|OilRig|Patchwork|Silence|Wizard Spider|menuPass
|
||||
T1554,Compromise Host Software Binary,Persistence,APT5
|
||||
T1036.006,Space after Filename,Defense Evasion,no
|
||||
T1036.005,Match Legitimate Name or Location,Defense Evasion,APT1|APT28|APT29|APT32|APT39|APT41|APT5|Aoqin Dragon|BRONZE BUTLER|BackdoorDiplomacy|Blue Mockingbird|Carbanak|Chimera|Darkhotel|Earth Lusca|FIN13|FIN7|Ferocious Kitten|Fox Kitten|Gamaredon Group|Indrik Spider|Ke3chang|Kimsuky|Lazarus Group|LuminousMoth|Machete|Magic Hound|MuddyWater|Mustang Panda|Mustard Tempest|Naikon|PROMETHIUM|Patchwork|Poseidon Group|Rocke|Sandworm Team|SideCopy|Sidewinder|Silence|Sowbug|TA2541|TeamTNT|ToddyCat|Transparent Tribe|Tropic Trooper|Volt Typhoon|WIRTE|Whitefly|admin@338|menuPass
|
||||
T1036.004,Masquerade Task or Service,Defense Evasion,APT-C-36|APT32|APT41|BITTER|BackdoorDiplomacy|Carbanak|FIN13|FIN6|FIN7|Fox Kitten|Higaisa|Kimsuky|Lazarus Group|Magic Hound|Naikon|PROMETHIUM|Wizard Spider|ZIRCONIUM
|
||||
T1036.003,Rename System Utilities,Defense Evasion,APT32|GALLIUM|Lazarus Group|menuPass
|
||||
T1036.002,Right-to-Left Override,Defense Evasion,BRONZE BUTLER|BlackTech|Ferocious Kitten|Ke3chang|Scarlet Mimic
|
||||
T1036.001,Invalid Code Signature,Defense Evasion,APT37|Windshift
|
||||
T1553.003,SIP and Trust Provider Hijacking,Defense Evasion,no
|
||||
T1553.002,Code Signing,Defense Evasion,APT41|CopyKittens|Darkhotel|Ember Bear|FIN6|FIN7|GALLIUM|Kimsuky|Lazarus Group|Leviathan|LuminousMoth|Molerats|Moses Staff|PROMETHIUM|Patchwork|Scattered Spider|Silence|Suckfly|TA505|Winnti Group|Wizard Spider|menuPass
|
||||
T1553.001,Gatekeeper Bypass,Defense Evasion,no
|
||||
T1553,Subvert Trust Controls,Defense Evasion,Axiom
|
||||
T1027.003,Steganography,Defense Evasion,APT37|Andariel|BRONZE BUTLER|Earth Lusca|Leviathan|MuddyWater|TA551|Tropic Trooper
|
||||
T1027.002,Software Packing,Defense Evasion,APT29|APT3|APT38|APT39|APT41|Aoqin Dragon|Dark Caracal|Elderwood|Ember Bear|GALLIUM|Kimsuky|MoustachedBouncer|Patchwork|Rocke|TA2541|TA505|TeamTNT|The White Company|Threat Group-3390|ZIRCONIUM
|
||||
T1027.001,Binary Padding,Defense Evasion,APT29|APT32|BRONZE BUTLER|Ember Bear|FIN7|Gamaredon Group|Higaisa|Leviathan|Moafee|Mustang Panda|Patchwork
|
||||
T1222.002,Linux and Mac File and Directory Permissions Modification,Defense Evasion,APT32|Rocke|TeamTNT
|
||||
T1222.001,Windows File and Directory Permissions Modification,Defense Evasion,Wizard Spider
|
||||
T1552.004,Private Keys,Credential Access,Rocke|Scattered Spider|TeamTNT
|
||||
T1552.003,Bash History,Credential Access,no
|
||||
T1552.002,Credentials in Registry,Credential Access,APT32
|
||||
T1552.001,Credentials In Files,Credential Access,APT3|APT33|FIN13|Fox Kitten|Kimsuky|Leafminer|MuddyWater|OilRig|Scattered Spider|TA505|TeamTNT
|
||||
T1552,Unsecured Credentials,Credential Access,no
|
||||
T1216.001,PubPrn,Defense Evasion,APT32
|
||||
T1070.006,Timestomp,Defense Evasion,APT28|APT29|APT32|APT38|APT5|Chimera|Kimsuky|Lazarus Group|Rocke
|
||||
T1070.005,Network Share Connection Removal,Defense Evasion,Threat Group-3390
|
||||
T1070.004,File Deletion,Defense Evasion,APT18|APT28|APT29|APT3|APT32|APT38|APT39|APT41|APT5|Aquatic Panda|BRONZE BUTLER|Chimera|Cobalt Group|Dragonfly|Evilnum|FIN10|FIN5|FIN6|FIN8|Gamaredon Group|Group5|Kimsuky|Lazarus Group|Magic Hound|Metador|Mustang Panda|OilRig|Patchwork|Rocke|Sandworm Team|Silence|TeamTNT|The White Company|Threat Group-3390|Tropic Trooper|Volt Typhoon|Wizard Spider|menuPass
|
||||
T1070.003,Clear Command History,Defense Evasion,APT41|APT5|Lazarus Group|Magic Hound|TeamTNT|menuPass
|
||||
T1550.004,Web Session Cookie,Defense Evasion|Lateral Movement,no
|
||||
T1550.001,Application Access Token,Defense Evasion|Lateral Movement,APT28
|
||||
T1550.003,Pass the Ticket,Defense Evasion|Lateral Movement,APT29|APT32|BRONZE BUTLER
|
||||
T1550.002,Pass the Hash,Defense Evasion|Lateral Movement,APT1|APT28|APT32|APT41|Chimera|FIN13|GALLIUM|Kimsuky|Wizard Spider
|
||||
T1550,Use Alternate Authentication Material,Defense Evasion|Lateral Movement,no
|
||||
T1548.004,Elevated Execution with Prompt,Defense Evasion|Privilege Escalation,no
|
||||
T1548.003,Sudo and Sudo Caching,Defense Evasion|Privilege Escalation,no
|
||||
T1548.002,Bypass User Account Control,Defense Evasion|Privilege Escalation,APT29|APT37|BRONZE BUTLER|Cobalt Group|Earth Lusca|Evilnum|MuddyWater|Patchwork|Threat Group-3390
|
||||
T1548.001,Setuid and Setgid,Defense Evasion|Privilege Escalation,no
|
||||
T1548,Abuse Elevation Control Mechanism,Defense Evasion|Privilege Escalation,no
|
||||
T1136.003,Cloud Account,Persistence,APT29|LAPSUS$
|
||||
T1070.002,Clear Linux or Mac System Logs,Defense Evasion,Rocke|TeamTNT
|
||||
T1070.001,Clear Windows Event Logs,Defense Evasion,APT28|APT32|APT38|APT41|Chimera|Dragonfly|FIN5|FIN8|Indrik Spider
|
||||
T1136.002,Domain Account,Persistence,GALLIUM|HAFNIUM|Wizard Spider
|
||||
T1136.001,Local Account,Persistence,APT3|APT39|APT41|APT5|Dragonfly|FIN13|Fox Kitten|Kimsuky|Leafminer|Magic Hound|TeamTNT|Wizard Spider
|
||||
T1547.010,Port Monitors,Persistence|Privilege Escalation,no
|
||||
T1547.009,Shortcut Modification,Persistence|Privilege Escalation,APT39|Gorgon Group|Lazarus Group|Leviathan
|
||||
T1547.008,LSASS Driver,Persistence|Privilege Escalation,no
|
||||
T1547.007,Re-opened Applications,Persistence|Privilege Escalation,no
|
||||
T1547.006,Kernel Modules and Extensions,Persistence|Privilege Escalation,no
|
||||
T1547.005,Security Support Provider,Persistence|Privilege Escalation,no
|
||||
T1547.004,Winlogon Helper DLL,Persistence|Privilege Escalation,Tropic Trooper|Turla|Wizard Spider
|
||||
T1547.003,Time Providers,Persistence|Privilege Escalation,no
|
||||
T1546.014,Emond,Persistence|Privilege Escalation,no
|
||||
T1546.013,PowerShell Profile,Persistence|Privilege Escalation,Turla
|
||||
T1546.012,Image File Execution Options Injection,Persistence|Privilege Escalation,no
|
||||
T1218.008,Odbcconf,Defense Evasion,Cobalt Group
|
||||
T1546.011,Application Shimming,Persistence|Privilege Escalation,FIN7
|
||||
T1547.002,Authentication Package,Persistence|Privilege Escalation,no
|
||||
T1546.010,AppInit DLLs,Persistence|Privilege Escalation,APT39
|
||||
T1546.009,AppCert DLLs,Persistence|Privilege Escalation,no
|
||||
T1218.007,Msiexec,Defense Evasion,Machete|Molerats|Rancor|TA505|ZIRCONIUM
|
||||
T1546.008,Accessibility Features,Persistence|Privilege Escalation,APT29|APT3|APT41|Axiom|Deep Panda|Fox Kitten
|
||||
T1546.007,Netsh Helper DLL,Persistence|Privilege Escalation,no
|
||||
T1546.006,LC_LOAD_DYLIB Addition,Persistence|Privilege Escalation,no
|
||||
T1546.005,Trap,Persistence|Privilege Escalation,no
|
||||
T1546.004,Unix Shell Configuration Modification,Persistence|Privilege Escalation,no
|
||||
T1546.003,Windows Management Instrumentation Event Subscription,Persistence|Privilege Escalation,APT29|APT33|Blue Mockingbird|FIN8|HEXANE|Leviathan|Metador|Mustang Panda|Rancor|Turla
|
||||
T1546.002,Screensaver,Persistence|Privilege Escalation,no
|
||||
T1546.001,Change Default File Association,Persistence|Privilege Escalation,Kimsuky
|
||||
T1547.001,Registry Run Keys / Startup Folder,Persistence|Privilege Escalation,APT18|APT19|APT28|APT29|APT3|APT32|APT33|APT37|APT39|APT41|BRONZE BUTLER|Cobalt Group|Confucius|Dark Caracal|Darkhotel|Dragonfly|FIN10|FIN13|FIN6|FIN7|Gamaredon Group|Gorgon Group|Higaisa|Inception|Ke3chang|Kimsuky|Lazarus Group|LazyScripter|Leviathan|LuminousMoth|Magic Hound|Molerats|MuddyWater|Mustang Panda|Naikon|PROMETHIUM|Patchwork|Putter Panda|RTM|Rocke|Sidewinder|Silence|TA2541|TeamTNT|Threat Group-3390|Tropic Trooper|Turla|Windshift|Wizard Spider|ZIRCONIUM
|
||||
T1218.002,Control Panel,Defense Evasion,Ember Bear
|
||||
T1218.010,Regsvr32,Defense Evasion,APT19|APT32|Blue Mockingbird|Cobalt Group|Deep Panda|Inception|Kimsuky|Leviathan|TA551|WIRTE
|
||||
T1218.009,Regsvcs/Regasm,Defense Evasion,no
|
||||
T1218.005,Mshta,Defense Evasion,APT29|APT32|Confucius|Earth Lusca|FIN7|Gamaredon Group|Inception|Kimsuky|Lazarus Group|LazyScripter|MuddyWater|Mustang Panda|SideCopy|Sidewinder|TA2541|TA551
|
||||
T1218.004,InstallUtil,Defense Evasion,Mustang Panda|menuPass
|
||||
T1218.001,Compiled HTML File,Defense Evasion,APT38|APT41|Dark Caracal|OilRig|Silence
|
||||
T1218.003,CMSTP,Defense Evasion,Cobalt Group|MuddyWater
|
||||
T1218.011,Rundll32,Defense Evasion,APT19|APT28|APT3|APT32|APT38|APT41|Blue Mockingbird|Carbanak|CopyKittens|FIN7|Gamaredon Group|HAFNIUM|Kimsuky|Lazarus Group|LazyScripter|Magic Hound|MuddyWater|Sandworm Team|TA505|TA551|Wizard Spider
|
||||
T1547,Boot or Logon Autostart Execution,Persistence|Privilege Escalation,no
|
||||
T1546,Event Triggered Execution,Persistence|Privilege Escalation,no
|
||||
T1098.003,Additional Cloud Roles,Persistence|Privilege Escalation,LAPSUS$|Scattered Spider
|
||||
T1098.002,Additional Email Delegate Permissions,Persistence|Privilege Escalation,APT28|APT29|Magic Hound
|
||||
T1098.001,Additional Cloud Credentials,Persistence|Privilege Escalation,no
|
||||
T1543.004,Launch Daemon,Persistence|Privilege Escalation,no
|
||||
T1543.003,Windows Service,Persistence|Privilege Escalation,APT19|APT3|APT32|APT38|APT41|Blue Mockingbird|Carbanak|Cinnamon Tempest|Cobalt Group|DarkVishnya|Earth Lusca|FIN7|Ke3chang|Kimsuky|Lazarus Group|PROMETHIUM|TeamTNT|Threat Group-3390|Tropic Trooper|Wizard Spider
|
||||
T1543.002,Systemd Service,Persistence|Privilege Escalation,Rocke|TeamTNT
|
||||
T1543.001,Launch Agent,Persistence|Privilege Escalation,no
|
||||
T1037.005,Startup Items,Persistence|Privilege Escalation,no
|
||||
T1037.004,RC Scripts,Persistence|Privilege Escalation,APT29
|
||||
T1055.012,Process Hollowing,Defense Evasion|Privilege Escalation,Gorgon Group|Kimsuky|Patchwork|TA2541|Threat Group-3390|menuPass
|
||||
T1055.013,Process Doppelgänging,Defense Evasion|Privilege Escalation,Leafminer
|
||||
T1055.011,Extra Window Memory Injection,Defense Evasion|Privilege Escalation,no
|
||||
T1055.014,VDSO Hijacking,Defense Evasion|Privilege Escalation,no
|
||||
T1055.009,Proc Memory,Defense Evasion|Privilege Escalation,no
|
||||
T1055.008,Ptrace System Calls,Defense Evasion|Privilege Escalation,no
|
||||
T1055.005,Thread Local Storage,Defense Evasion|Privilege Escalation,no
|
||||
T1055.004,Asynchronous Procedure Call,Defense Evasion|Privilege Escalation,FIN8
|
||||
T1055.003,Thread Execution Hijacking,Defense Evasion|Privilege Escalation,no
|
||||
T1055.002,Portable Executable Injection,Defense Evasion|Privilege Escalation,Gorgon Group|Rocke
|
||||
T1055.001,Dynamic-link Library Injection,Defense Evasion|Privilege Escalation,BackdoorDiplomacy|Lazarus Group|Leviathan|Malteiro|Putter Panda|TA505|Tropic Trooper|Turla|Wizard Spider
|
||||
T1037.003,Network Logon Script,Persistence|Privilege Escalation,no
|
||||
T1543,Create or Modify System Process,Persistence|Privilege Escalation,no
|
||||
T1037.002,Login Hook,Persistence|Privilege Escalation,no
|
||||
T1037.001,Logon Script (Windows),Persistence|Privilege Escalation,APT28|Cobalt Group
|
||||
T1542.003,Bootkit,Defense Evasion|Persistence,APT28|APT41|Lazarus Group
|
||||
T1542.002,Component Firmware,Defense Evasion|Persistence,Equation
|
||||
T1542.001,System Firmware,Defense Evasion|Persistence,no
|
||||
T1505.003,Web Shell,Persistence,APT28|APT29|APT32|APT38|APT39|APT5|BackdoorDiplomacy|Deep Panda|Dragonfly|FIN13|Fox Kitten|GALLIUM|HAFNIUM|Kimsuky|Leviathan|Magic Hound|Moses Staff|OilRig|Sandworm Team|Threat Group-3390|Tonto Team|Tropic Trooper|Volatile Cedar|Volt Typhoon
|
||||
T1505.002,Transport Agent,Persistence,no
|
||||
T1505.001,SQL Stored Procedures,Persistence,no
|
||||
T1053.003,Cron,Execution|Persistence|Privilege Escalation,APT38|APT5|Rocke
|
||||
T1053.005,Scheduled Task,Execution|Persistence|Privilege Escalation,APT-C-36|APT29|APT3|APT32|APT33|APT37|APT38|APT39|APT41|BITTER|BRONZE BUTLER|Blue Mockingbird|Chimera|Cobalt Group|Confucius|Dragonfly|FIN10|FIN13|FIN6|FIN7|FIN8|Fox Kitten|GALLIUM|Gamaredon Group|HEXANE|Higaisa|Kimsuky|Lazarus Group|LuminousMoth|Machete|Magic Hound|Molerats|MuddyWater|Mustang Panda|Naikon|OilRig|Patchwork|Rancor|Silence|Stealth Falcon|TA2541|ToddyCat|Wizard Spider|menuPass
|
||||
T1053.002,At,Execution|Persistence|Privilege Escalation,APT18|BRONZE BUTLER|Threat Group-3390
|
||||
T1542,Pre-OS Boot,Defense Evasion|Persistence,no
|
||||
T1137.001,Office Template Macros,Persistence,MuddyWater
|
||||
T1137.004,Outlook Home Page,Persistence,OilRig
|
||||
T1137.003,Outlook Forms,Persistence,no
|
||||
T1137.005,Outlook Rules,Persistence,no
|
||||
T1137.006,Add-ins,Persistence,Naikon
|
||||
T1137.002,Office Test,Persistence,APT28
|
||||
T1531,Account Access Removal,Impact,Akira|LAPSUS$
|
||||
T1539,Steal Web Session Cookie,Credential Access,Evilnum|LuminousMoth|Sandworm Team|Scattered Spider
|
||||
T1529,System Shutdown/Reboot,Impact,APT37|APT38|Lazarus Group
|
||||
T1518,Software Discovery,Discovery,BRONZE BUTLER|HEXANE|Inception|MuddyWater|Mustang Panda|SideCopy|Sidewinder|Tropic Trooper|Volt Typhoon|Windigo|Windshift|Wizard Spider
|
||||
T1547.013,XDG Autostart Entries,Persistence|Privilege Escalation,no
|
||||
T1534,Internal Spearphishing,Lateral Movement,Gamaredon Group|HEXANE|Kimsuky|Leviathan
|
||||
T1528,Steal Application Access Token,Credential Access,APT28|APT29
|
||||
T1535,Unused/Unsupported Cloud Regions,Defense Evasion,no
|
||||
T1525,Implant Internal Image,Persistence,no
|
||||
T1538,Cloud Service Dashboard,Discovery,Scattered Spider
|
||||
T1530,Data from Cloud Storage,Collection,Fox Kitten|Scattered Spider
|
||||
T1578,Modify Cloud Compute Infrastructure,Defense Evasion,no
|
||||
T1537,Transfer Data to Cloud Account,Exfiltration,no
|
||||
T1526,Cloud Service Discovery,Discovery,no
|
||||
T1505,Server Software Component,Persistence,no
|
||||
T1499,Endpoint Denial of Service,Impact,Sandworm Team
|
||||
T1497,Virtualization/Sandbox Evasion,Defense Evasion|Discovery,Darkhotel
|
||||
T1498,Network Denial of Service,Impact,APT28
|
||||
T1496,Resource Hijacking,Impact,APT41|Blue Mockingbird|Rocke|TeamTNT
|
||||
T1495,Firmware Corruption,Impact,no
|
||||
T1491,Defacement,Impact,no
|
||||
T1490,Inhibit System Recovery,Impact,Wizard Spider
|
||||
T1489,Service Stop,Impact,Indrik Spider|LAPSUS$|Lazarus Group|Wizard Spider
|
||||
T1486,Data Encrypted for Impact,Impact,APT38|APT41|Akira|FIN7|FIN8|Indrik Spider|Magic Hound|Sandworm Team|Scattered Spider|TA505
|
||||
T1485,Data Destruction,Impact,APT38|Gamaredon Group|LAPSUS$|Lazarus Group|Sandworm Team
|
||||
T1484,Domain or Tenant Policy Modification,Defense Evasion|Privilege Escalation,no
|
||||
T1482,Domain Trust Discovery,Discovery,Akira|Chimera|Earth Lusca|FIN8|Magic Hound
|
||||
T1480,Execution Guardrails,Defense Evasion,no
|
||||
T1222,File and Directory Permissions Modification,Defense Evasion,no
|
||||
T1220,XSL Script Processing,Defense Evasion,Cobalt Group|Higaisa
|
||||
T1221,Template Injection,Defense Evasion,APT28|Confucius|DarkHydrus|Dragonfly|Gamaredon Group|Inception|Tropic Trooper
|
||||
T1190,Exploit Public-Facing Application,Initial Access,APT28|APT29|APT39|APT41|APT5|Axiom|BackdoorDiplomacy|BlackTech|Blue Mockingbird|Cinnamon Tempest|Dragonfly|Earth Lusca|FIN13|FIN7|Fox Kitten|GALLIUM|GOLD SOUTHFIELD|HAFNIUM|Ke3chang|Kimsuky|Magic Hound|Moses Staff|MuddyWater|Rocke|Sandworm Team|Threat Group-3390|ToddyCat|Volatile Cedar|Volt Typhoon|menuPass
|
||||
T1213,Data from Information Repositories,Collection,APT28|FIN6|Fox Kitten|LAPSUS$|Sandworm Team|Turla
|
||||
T1202,Indirect Command Execution,Defense Evasion,Lazarus Group
|
||||
T1207,Rogue Domain Controller,Defense Evasion,no
|
||||
T1212,Exploitation for Credential Access,Credential Access,no
|
||||
T1201,Password Policy Discovery,Discovery,Chimera|OilRig|Turla
|
||||
T1197,BITS Jobs,Defense Evasion|Persistence,APT39|APT41|Leviathan|Patchwork|Wizard Spider
|
||||
T1189,Drive-by Compromise,Initial Access,APT19|APT28|APT32|APT37|APT38|Andariel|Axiom|BRONZE BUTLER|Dark Caracal|Darkhotel|Dragonfly|Earth Lusca|Elderwood|Lazarus Group|Leafminer|Leviathan|Machete|Magic Hound|Mustard Tempest|PLATINUM|PROMETHIUM|Patchwork|RTM|Threat Group-3390|Transparent Tribe|Turla|Windigo|Windshift
|
||||
T1218,System Binary Proxy Execution,Defense Evasion,Lazarus Group
|
||||
T1210,Exploitation of Remote Services,Lateral Movement,APT28|Dragonfly|Earth Lusca|FIN7|Fox Kitten|MuddyWater|Threat Group-3390|Tonto Team|Wizard Spider|menuPass
|
||||
T1203,Exploitation for Client Execution,Execution,APT12|APT28|APT29|APT3|APT32|APT33|APT37|APT41|Andariel|Aoqin Dragon|Axiom|BITTER|BRONZE BUTLER|BlackTech|Cobalt Group|Confucius|Darkhotel|Dragonfly|EXOTIC LILY|Elderwood|Ember Bear|Higaisa|Inception|Lazarus Group|Leviathan|MuddyWater|Mustang Panda|Patchwork|Sandworm Team|Sidewinder|TA459|The White Company|Threat Group-3390|Tonto Team|Transparent Tribe|Tropic Trooper|admin@338
|
||||
T1211,Exploitation for Defense Evasion,Defense Evasion,APT28
|
||||
T1216,System Script Proxy Execution,Defense Evasion,no
|
||||
T1195,Supply Chain Compromise,Initial Access,no
|
||||
T1219,Remote Access Software,Command And Control,Akira|Carbanak|Cobalt Group|DarkVishnya|Evilnum|FIN7|GOLD SOUTHFIELD|Kimsuky|MuddyWater|Mustang Panda|RTM|Sandworm Team|Scattered Spider|TeamTNT|Thrip
|
||||
T1205,Traffic Signaling,Command And Control|Defense Evasion|Persistence,no
|
||||
T1204,User Execution,Execution,LAPSUS$|Scattered Spider
|
||||
T1199,Trusted Relationship,Initial Access,APT28|APT29|GOLD SOUTHFIELD|LAPSUS$|POLONIUM|Sandworm Team|Threat Group-3390|menuPass
|
||||
T1217,Browser Information Discovery,Discovery,APT38|Chimera|Fox Kitten|Scattered Spider
|
||||
T1200,Hardware Additions,Initial Access,DarkVishnya
|
||||
T1176,Browser Extensions,Persistence,Kimsuky
|
||||
T1185,Browser Session Hijacking,Collection,no
|
||||
T1187,Forced Authentication,Credential Access,DarkHydrus|Dragonfly
|
||||
T1137,Office Application Startup,Persistence,APT32|Gamaredon Group
|
||||
T1140,Deobfuscate/Decode Files or Information,Defense Evasion,APT19|APT28|APT39|BRONZE BUTLER|Cinnamon Tempest|Darkhotel|Earth Lusca|FIN13|Gamaredon Group|Gorgon Group|Higaisa|Ke3chang|Kimsuky|Lazarus Group|Leviathan|Malteiro|Molerats|MuddyWater|OilRig|Rocke|Sandworm Team|TA505|TeamTNT|Threat Group-3390|Tropic Trooper|Turla|WIRTE|ZIRCONIUM|menuPass
|
||||
T1136,Create Account,Persistence,Indrik Spider|Scattered Spider
|
||||
T1135,Network Share Discovery,Discovery,APT1|APT32|APT38|APT39|APT41|Chimera|DarkVishnya|Dragonfly|FIN13|Sowbug|Tonto Team|Tropic Trooper|Wizard Spider
|
||||
T1134,Access Token Manipulation,Defense Evasion|Privilege Escalation,Blue Mockingbird|FIN6
|
||||
T1133,External Remote Services,Initial Access|Persistence,APT18|APT28|APT29|APT41|Akira|Chimera|Dragonfly|FIN13|FIN5|GALLIUM|GOLD SOUTHFIELD|Ke3chang|Kimsuky|LAPSUS$|Leviathan|OilRig|Sandworm Team|Scattered Spider|TeamTNT|Threat Group-3390|Wizard Spider
|
||||
T1132,Data Encoding,Command And Control,no
|
||||
T1129,Shared Modules,Execution,no
|
||||
T1127,Trusted Developer Utilities Proxy Execution,Defense Evasion,no
|
||||
T1125,Video Capture,Collection,FIN7|Silence
|
||||
T1124,System Time Discovery,Discovery,BRONZE BUTLER|Chimera|Darkhotel|Higaisa|Lazarus Group|Sidewinder|The White Company|Turla|ZIRCONIUM
|
||||
T1123,Audio Capture,Collection,APT37
|
||||
T1120,Peripheral Device Discovery,Discovery,APT28|APT37|BackdoorDiplomacy|Equation|Gamaredon Group|OilRig|TeamTNT|Turla
|
||||
T1119,Automated Collection,Collection,APT1|APT28|Chimera|Confucius|FIN5|FIN6|Gamaredon Group|Ke3chang|Mustang Panda|OilRig|Patchwork|Sidewinder|Threat Group-3390|Tropic Trooper|menuPass
|
||||
T1115,Clipboard Data,Collection,APT38|APT39
|
||||
T1114,Email Collection,Collection,Magic Hound|Silent Librarian
|
||||
T1113,Screen Capture,Collection,APT28|APT39|BRONZE BUTLER|Dark Caracal|Dragonfly|FIN7|GOLD SOUTHFIELD|Gamaredon Group|Group5|Magic Hound|MoustachedBouncer|MuddyWater|OilRig|Silence
|
||||
T1112,Modify Registry,Defense Evasion,APT19|APT32|APT38|APT41|Blue Mockingbird|Dragonfly|Earth Lusca|Ember Bear|FIN8|Gamaredon Group|Gorgon Group|Kimsuky|LuminousMoth|Magic Hound|Patchwork|Silence|TA505|Threat Group-3390|Turla|Wizard Spider
|
||||
T1111,Multi-Factor Authentication Interception,Credential Access,Chimera|Kimsuky|LAPSUS$
|
||||
T1110,Brute Force,Credential Access,APT28|APT38|APT39|DarkVishnya|Dragonfly|FIN5|Fox Kitten|HEXANE|OilRig|Turla
|
||||
T1106,Native API,Execution,APT37|APT38|BlackTech|Chimera|Gamaredon Group|Gorgon Group|Higaisa|Lazarus Group|SideCopy|Silence|TA505|ToddyCat|Tropic Trooper|Turla|menuPass
|
||||
T1105,Ingress Tool Transfer,Command And Control,APT-C-36|APT18|APT28|APT29|APT3|APT32|APT33|APT37|APT38|APT39|APT41|Ajax Security Team|Andariel|Aquatic Panda|BITTER|BRONZE BUTLER|BackdoorDiplomacy|Chimera|Cinnamon Tempest|Cobalt Group|Confucius|Darkhotel|Dragonfly|Elderwood|Ember Bear|Evilnum|FIN13|FIN7|FIN8|Fox Kitten|GALLIUM|Gamaredon Group|Gorgon Group|HAFNIUM|HEXANE|IndigoZebra|Indrik Spider|Ke3chang|Kimsuky|Lazarus Group|LazyScripter|Leviathan|LuminousMoth|Magic Hound|Metador|Molerats|Moses Staff|MuddyWater|Mustang Panda|Mustard Tempest|Nomadic Octopus|OilRig|PLATINUM|Patchwork|Rancor|Rocke|Sandworm Team|SideCopy|Sidewinder|Silence|TA2541|TA505|TA551|TeamTNT|Threat Group-3390|Tonto Team|Tropic Trooper|Turla|Volatile Cedar|WIRTE|Whitefly|Windshift|Winnti Group|Wizard Spider|ZIRCONIUM|menuPass
|
||||
T1104,Multi-Stage Channels,Command And Control,APT3|APT41|Lazarus Group|MuddyWater
|
||||
T1102,Web Service,Command And Control,APT32|EXOTIC LILY|Ember Bear|FIN6|FIN8|Fox Kitten|Gamaredon Group|Inception|LazyScripter|Mustang Panda|Rocke|TeamTNT|Turla
|
||||
T1098,Account Manipulation,Persistence|Privilege Escalation,APT3|APT41|APT5|Dragonfly|FIN13|HAFNIUM|Kimsuky|Lazarus Group|Magic Hound
|
||||
T1095,Non-Application Layer Protocol,Command And Control,APT3|BITTER|BackdoorDiplomacy|FIN6|HAFNIUM|Metador|PLATINUM|ToddyCat
|
||||
T1092,Communication Through Removable Media,Command And Control,APT28
|
||||
T1091,Replication Through Removable Media,Initial Access|Lateral Movement,APT28|Aoqin Dragon|Darkhotel|FIN7|LuminousMoth|Mustang Panda|Tropic Trooper
|
||||
T1090,Proxy,Command And Control,APT41|Blue Mockingbird|Cinnamon Tempest|CopyKittens|Earth Lusca|Fox Kitten|LAPSUS$|Magic Hound|MoustachedBouncer|POLONIUM|Sandworm Team|Turla|Volt Typhoon|Windigo
|
||||
T1087,Account Discovery,Discovery,FIN13
|
||||
T1083,File and Directory Discovery,Discovery,APT18|APT28|APT3|APT32|APT38|APT39|APT41|APT5|Aoqin Dragon|BRONZE BUTLER|Chimera|Confucius|Dark Caracal|Darkhotel|Dragonfly|FIN13|Fox Kitten|Gamaredon Group|HAFNIUM|Inception|Ke3chang|Kimsuky|Lazarus Group|Leafminer|LuminousMoth|Magic Hound|MuddyWater|Mustang Panda|Patchwork|Sandworm Team|Scattered Spider|Sidewinder|Sowbug|TeamTNT|ToddyCat|Tropic Trooper|Turla|Windigo|Winnti Group|admin@338|menuPass
|
||||
T1082,System Information Discovery,Discovery,APT18|APT19|APT3|APT32|APT37|APT38|APT41|Aquatic Panda|Blue Mockingbird|Chimera|Confucius|Darkhotel|FIN13|FIN8|Gamaredon Group|HEXANE|Higaisa|Inception|Ke3chang|Kimsuky|Lazarus Group|Magic Hound|Malteiro|Moses Staff|MuddyWater|Mustang Panda|Mustard Tempest|OilRig|Patchwork|Rocke|Sandworm Team|SideCopy|Sidewinder|Sowbug|Stealth Falcon|TA2541|TeamTNT|ToddyCat|Tropic Trooper|Turla|Volt Typhoon|Windigo|Windshift|Wizard Spider|ZIRCONIUM|admin@338
|
||||
T1080,Taint Shared Content,Lateral Movement,BRONZE BUTLER|Cinnamon Tempest|Darkhotel|Gamaredon Group
|
||||
T1078,Valid Accounts,Defense Evasion|Initial Access|Persistence|Privilege Escalation,APT18|APT28|APT29|APT33|APT39|APT41|Akira|Axiom|Carbanak|Chimera|Cinnamon Tempest|Dragonfly|FIN10|FIN4|FIN5|FIN6|FIN7|FIN8|Fox Kitten|GALLIUM|Ke3chang|LAPSUS$|Lazarus Group|Leviathan|OilRig|POLONIUM|PittyTiger|Sandworm Team|Silence|Silent Librarian|Suckfly|Threat Group-3390|Wizard Spider|menuPass
|
||||
T1074,Data Staged,Collection,Scattered Spider|Volt Typhoon|Wizard Spider
|
||||
T1072,Software Deployment Tools,Execution|Lateral Movement,APT32|Sandworm Team|Silence|Threat Group-1314
|
||||
T1071,Application Layer Protocol,Command And Control,Magic Hound|Rocke|TeamTNT
|
||||
T1070,Indicator Removal,Defense Evasion,APT5|Lazarus Group
|
||||
T1069,Permission Groups Discovery,Discovery,APT3|APT41|FIN13|TA505
|
||||
T1068,Exploitation for Privilege Escalation,Privilege Escalation,APT28|APT29|APT32|APT33|BITTER|Cobalt Group|FIN6|FIN8|LAPSUS$|MoustachedBouncer|PLATINUM|Scattered Spider|Threat Group-3390|Tonto Team|Turla|Whitefly|ZIRCONIUM
|
||||
T1059,Command and Scripting Interpreter,Execution,APT19|APT32|APT37|APT39|Dragonfly|FIN5|FIN6|FIN7|Fox Kitten|Ke3chang|OilRig|Stealth Falcon|Whitefly|Windigo
|
||||
T1057,Process Discovery,Discovery,APT1|APT28|APT3|APT37|APT38|APT5|Andariel|Chimera|Darkhotel|Deep Panda|Earth Lusca|Gamaredon Group|HAFNIUM|HEXANE|Higaisa|Inception|Ke3chang|Kimsuky|Lazarus Group|Magic Hound|Molerats|MuddyWater|Mustang Panda|OilRig|Poseidon Group|Rocke|Sidewinder|Stealth Falcon|TeamTNT|ToddyCat|Tropic Trooper|Turla|Volt Typhoon|Windshift|Winnti Group
|
||||
T1056,Input Capture,Collection|Credential Access,APT39
|
||||
T1055,Process Injection,Defense Evasion|Privilege Escalation,APT32|APT37|APT41|APT5|Cobalt Group|Kimsuky|PLATINUM|Silence|TA2541|Turla|Wizard Spider
|
||||
T1053,Scheduled Task/Job,Execution|Persistence|Privilege Escalation,Earth Lusca
|
||||
T1052,Exfiltration Over Physical Medium,Exfiltration,no
|
||||
T1049,System Network Connections Discovery,Discovery,APT1|APT3|APT32|APT38|APT41|APT5|Andariel|BackdoorDiplomacy|Chimera|Earth Lusca|FIN13|GALLIUM|HEXANE|Ke3chang|Lazarus Group|Magic Hound|MuddyWater|Mustang Panda|OilRig|Poseidon Group|Sandworm Team|TeamTNT|Threat Group-3390|ToddyCat|Tropic Trooper|Turla|Volt Typhoon|admin@338|menuPass
|
||||
T1048,Exfiltration Over Alternative Protocol,Exfiltration,TeamTNT
|
||||
T1047,Windows Management Instrumentation,Execution,APT29|APT32|APT41|Blue Mockingbird|Chimera|Cinnamon Tempest|Deep Panda|Earth Lusca|FIN13|FIN6|FIN7|FIN8|GALLIUM|Gamaredon Group|Indrik Spider|Lazarus Group|Leviathan|Magic Hound|MuddyWater|Mustang Panda|Naikon|OilRig|Sandworm Team|Stealth Falcon|TA2541|Threat Group-3390|ToddyCat|Volt Typhoon|Windshift|Wizard Spider|menuPass
|
||||
T1046,Network Service Discovery,Discovery,APT32|APT39|APT41|BackdoorDiplomacy|BlackTech|Chimera|Cobalt Group|DarkVishnya|FIN13|FIN6|Fox Kitten|Lazarus Group|Leafminer|Magic Hound|Naikon|OilRig|Rocke|Suckfly|TeamTNT|Threat Group-3390|Tropic Trooper|menuPass
|
||||
T1041,Exfiltration Over C2 Channel,Exfiltration,APT3|APT32|APT39|Chimera|Confucius|GALLIUM|Gamaredon Group|Higaisa|Ke3chang|Kimsuky|Lazarus Group|Leviathan|LuminousMoth|MuddyWater|Sandworm Team|Stealth Falcon|Wizard Spider|ZIRCONIUM
|
||||
T1040,Network Sniffing,Credential Access|Discovery,APT28|APT33|DarkVishnya|Kimsuky|Sandworm Team
|
||||
T1039,Data from Network Shared Drive,Collection,APT28|BRONZE BUTLER|Chimera|Fox Kitten|Gamaredon Group|Sowbug|menuPass
|
||||
T1037,Boot or Logon Initialization Scripts,Persistence|Privilege Escalation,APT29|Rocke
|
||||
T1036,Masquerading,Defense Evasion,APT28|APT32|BRONZE BUTLER|Dragonfly|FIN13|LazyScripter|Nomadic Octopus|OilRig|PLATINUM|Sandworm Team|TA551|TeamTNT|Windshift|ZIRCONIUM|menuPass
|
||||
T1033,System Owner/User Discovery,Discovery,APT19|APT3|APT32|APT37|APT38|APT39|APT41|Chimera|Dragonfly|Earth Lusca|FIN10|FIN7|FIN8|GALLIUM|Gamaredon Group|HAFNIUM|HEXANE|Ke3chang|Lazarus Group|LuminousMoth|Magic Hound|MuddyWater|OilRig|Patchwork|Sandworm Team|Sidewinder|Stealth Falcon|Threat Group-3390|Tropic Trooper|Volt Typhoon|Windshift|Wizard Spider|ZIRCONIUM
|
||||
T1030,Data Transfer Size Limits,Exfiltration,APT28|APT41|LuminousMoth|Threat Group-3390
|
||||
T1029,Scheduled Transfer,Exfiltration,Higaisa
|
||||
T1027,Obfuscated Files or Information,Defense Evasion,APT-C-36|APT3|APT37|APT41|BackdoorDiplomacy|BlackOasis|Earth Lusca|Ember Bear|GALLIUM|Gallmaker|Gamaredon Group|Ke3chang|Kimsuky|Mustang Panda|Rocke|Sandworm Team|Windshift
|
||||
T1025,Data from Removable Media,Collection,APT28|Gamaredon Group|Turla
|
||||
T1021,Remote Services,Lateral Movement,Wizard Spider
|
||||
T1020,Automated Exfiltration,Exfiltration,Gamaredon Group|Ke3chang|Sidewinder|Tropic Trooper
|
||||
T1018,Remote System Discovery,Discovery,APT3|APT32|APT39|Akira|BRONZE BUTLER|Chimera|Deep Panda|Dragonfly|Earth Lusca|FIN5|FIN6|FIN8|Fox Kitten|GALLIUM|HAFNIUM|HEXANE|Indrik Spider|Ke3chang|Leafminer|Magic Hound|Naikon|Rocke|Sandworm Team|Scattered Spider|Silence|Threat Group-3390|ToddyCat|Turla|Volt Typhoon|Wizard Spider|menuPass
|
||||
T1016,System Network Configuration Discovery,Discovery,APT1|APT19|APT3|APT32|APT41|Chimera|Darkhotel|Dragonfly|Earth Lusca|FIN13|GALLIUM|HAFNIUM|HEXANE|Higaisa|Ke3chang|Kimsuky|Lazarus Group|Magic Hound|Moses Staff|MuddyWater|Mustang Panda|Naikon|OilRig|SideCopy|Sidewinder|Stealth Falcon|TeamTNT|Threat Group-3390|Tropic Trooper|Turla|Volt Typhoon|Wizard Spider|ZIRCONIUM|admin@338|menuPass
|
||||
T1014,Rootkit,Defense Evasion,APT28|APT41|Rocke|TeamTNT|Winnti Group
|
||||
T1012,Query Registry,Discovery,APT32|APT39|APT41|Chimera|Dragonfly|Fox Kitten|Kimsuky|Lazarus Group|OilRig|Stealth Falcon|Threat Group-3390|Turla|Volt Typhoon|ZIRCONIUM
|
||||
T1011,Exfiltration Over Other Network Medium,Exfiltration,no
|
||||
T1010,Application Window Discovery,Discovery,HEXANE|Lazarus Group
|
||||
T1008,Fallback Channels,Command And Control,APT41|FIN7|Lazarus Group|OilRig
|
||||
T1007,System Service Discovery,Discovery,APT1|Aquatic Panda|BRONZE BUTLER|Chimera|Earth Lusca|Indrik Spider|Ke3chang|Kimsuky|OilRig|Poseidon Group|TeamTNT|Turla|admin@338
|
||||
T1006,Direct Volume Access,Defense Evasion,Scattered Spider
|
||||
T1005,Data from Local System,Collection,APT1|APT28|APT29|APT3|APT37|APT38|APT39|APT41|Andariel|Axiom|BRONZE BUTLER|CURIUM|Dark Caracal|Dragonfly|FIN13|FIN6|FIN7|Fox Kitten|GALLIUM|Gamaredon Group|HAFNIUM|Inception|Ke3chang|Kimsuky|LAPSUS$|Lazarus Group|LuminousMoth|Magic Hound|Patchwork|Sandworm Team|Stealth Falcon|Threat Group-3390|ToddyCat|Turla|Volt Typhoon|Windigo|Wizard Spider|menuPass
|
||||
T1003,OS Credential Dumping,Credential Access,APT28|APT32|APT39|Axiom|Leviathan|Poseidon Group|Sowbug|Suckfly|Tonto Team
|
||||
T1001,Data Obfuscation,Command And Control,no
|
||||
|
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
@@ -28,7 +28,7 @@ how_to_implement: 'You must have Enterprise Security 6.0 or later, if not you wi
|
||||
in a reasonable timeframe. By default, the search builds the model using the past
|
||||
30 days of data. You can modify the search window to build the model over a longer
|
||||
period of time, which may give you better results. You may also want to periodically
|
||||
re-run this search to rebuild the model with the latest data.\
|
||||
re-run this search to rebuild the model with the latest data.
|
||||
|
||||
More information on the algorithm used in the search can be found at `https://docs.splunk.com/Documentation/MLApp/4.2.0/User/Algorithms#DensityFunction`.'
|
||||
known_false_positives: none
|
||||
|
||||
@@ -28,7 +28,7 @@ how_to_implement: 'You must have Enterprise Security 6.0 or later, if not you wi
|
||||
in a reasonable timeframe. By default, the search builds the model using the past
|
||||
90 days of data. You can modify the search window to build the model over a longer
|
||||
period of time, which may give you better results. You may also want to periodically
|
||||
re-run this search to rebuild the model with the latest data.\
|
||||
re-run this search to rebuild the model with the latest data.
|
||||
|
||||
More information on the algorithm used in the search can be found at `https://docs.splunk.com/Documentation/MLApp/4.2.0/User/Algorithms#DensityFunction`.'
|
||||
known_false_positives: none
|
||||
|
||||
@@ -10,12 +10,12 @@ description: This search is used to build a Machine Learning Toolkit (MLTK) mode
|
||||
the last 90 days of data to build the model. The model created by this search is
|
||||
then used in the corresponding detection search, which identifies subsequent outliers
|
||||
in the number of RunInstances performed by a user in a small time window.
|
||||
search: '`cloudtrail` eventName=RunInstances errorCode=success `ec2_excessive_runinstances_mltk_input_filter`
|
||||
search: '`cloudtrail` eventName=RunInstances errorCode=success
|
||||
| bucket span=10m _time | stats count as instances_launched by _time src_user |
|
||||
fit DensityFunction instances_launched threshold=0.0005 into ec2_excessive_runinstances_v1'
|
||||
how_to_implement: 'You must install the AWS App for Splunk (version 5.1.0 or later)
|
||||
and Splunk Add-on for AWS (version 4.4.0 or later), then configure your CloudTrail
|
||||
inputs.\
|
||||
inputs.
|
||||
|
||||
In addition, you must have the Machine Learning Toolkit (MLTK) version >= 4.2 installed,
|
||||
along with any required dependencies. Depending on the number of users in your environment,
|
||||
@@ -24,7 +24,7 @@ how_to_implement: 'You must install the AWS App for Splunk (version 5.1.0 or lat
|
||||
timeframe. By default, the search builds the model using the past 30 days of data.
|
||||
You can modify the search window to build the model over a longer period of time,
|
||||
which may give you better results. You may also want to periodically re-run this
|
||||
search to rebuild the model with the latest data.\
|
||||
search to rebuild the model with the latest data.
|
||||
|
||||
More information on the algorithm used in the search can be found at `https://docs.splunk.com/Documentation/MLApp/4.2.0/User/Algorithms#DensityFunction`.'
|
||||
known_false_positives: none
|
||||
|
||||
@@ -11,12 +11,12 @@ description: This search is used to build a Machine Learning Toolkit (MLTK) mode
|
||||
is then used in the corresponding detection search, which identifies subsequent
|
||||
outliers in the number of TerminateInstances performed by a user in a small time
|
||||
window.
|
||||
search: '`cloudtrail` eventName=TerminateInstances errorCode=success `ec2_excessive_terminateinstances_mltk_input_filter`
|
||||
search: '`cloudtrail` eventName=TerminateInstances errorCode=success
|
||||
| bucket span=10m _time | stats count as instances_terminated by _time src_user
|
||||
| fit DensityFunction instances_terminated threshold=0.0005 into ec2_excessive_terminateinstances_v1'
|
||||
how_to_implement: 'You must install the AWS App for Splunk (version 5.1.0 or later)
|
||||
and Splunk Add-on for AWS (version 4.4.0 or later), then configure your CloudTrail
|
||||
inputs.\
|
||||
inputs.
|
||||
|
||||
In addition, you must have the Machine Learning Toolkit (MLTK) version >= 4.2 installed,
|
||||
along with any required dependencies. Depending on the number of users in your environment,
|
||||
@@ -25,7 +25,7 @@ how_to_implement: 'You must install the AWS App for Splunk (version 5.1.0 or lat
|
||||
timeframe. By default, the search builds the model using the past 30 days of data.
|
||||
You can modify the search window to build the model over a longer period of time,
|
||||
which may give you better results. You may also want to periodically re-run this
|
||||
search to rebuild the model with the latest data.\
|
||||
search to rebuild the model with the latest data.
|
||||
|
||||
More information on the algorithm used in the search can be found at `https://docs.splunk.com/Documentation/MLApp/4.2.0/User/Algorithms#DensityFunction`.'
|
||||
known_false_positives: none
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: Previously Seen Cloud Compute Instance Types - Initial
|
||||
id: 3c78025c-1ffe-4976-a640-75ef604842be
|
||||
version: 1
|
||||
date: 2020-9-03
|
||||
date: '2020-09-03'
|
||||
author: David Dorsey, Splunk
|
||||
type: Baseline
|
||||
datamodel:
|
||||
@@ -36,4 +36,4 @@ deployment:
|
||||
cron_schedule: 0 2 * * 0
|
||||
earliest_time: -90d@d
|
||||
latest_time: -1d@d
|
||||
schedule_window: auto
|
||||
schedule_window: auto
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: Previously Seen Cloud Compute Instance Types - Update
|
||||
id: 7b7ef9ab-acb9-4e07-af76-4cf1e722885c
|
||||
version: 1
|
||||
date: 2020-9-03
|
||||
date: '2020-09-03'
|
||||
author: David Dorsey, Splunk
|
||||
type: Baseline
|
||||
datamodel:
|
||||
|
||||
@@ -13,7 +13,7 @@ search: '`wineventlog_system` EventCode=7036 | rex field=Message "The (?<service
|
||||
service entered the (?<state>\w+) state" | where state="running" | stats earliest(_time)
|
||||
as firstTimeSeen, latest(_time) as lastTimeSeen by service | inputlookup previously_seen_running_windows_services
|
||||
append=t | stats min(firstTimeSeen) as firstTimeSeen, max(lastTimeSeen) as lastTimeSeen
|
||||
by service | where lastTimeSeen > relative_time(now(), "`previously_seen_windows_service_forget_window`")
|
||||
by service | where lastTimeSeen > relative_time(now(), `previously_seen_windows_services_forget_window`)
|
||||
| outputlookup previously_seen_running_windows_services'
|
||||
how_to_implement: While this search does not require you to adhere to Splunk CIM,
|
||||
you must be ingesting your Windows security-event logs for it to execute successfully.
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
name: Splunk Command and Scripting Interpreter Risky SPL MLTK Baseline
|
||||
id: 273df2f7-643a-451a-8d4d-637e39eadc87
|
||||
version: 1
|
||||
date: '2022-05-27'
|
||||
author: Abhinav Mishra, Kumar Sharad and Xiao Lin, Splunk
|
||||
type: Baseline
|
||||
datamodel:
|
||||
- Splunk_Audit
|
||||
description: 'This search supports an analyst looking for abuse or misuse of the risky commands listed here: https://docs.splunk.com/Documentation/Splunk/latest/Security/SPLsafeguards#Commands_that_trigger_the_warning
|
||||
This is accomplished by using the time spent executing one of these risky commands as a proxy for misuse/abuse of interest during investigation and/or hunting.
|
||||
The search builds a model utilizes the MLTK DensityFunction algorithm on Splunk app audit log data. The model uses the past 7 days of user history executing the above referenced commands then aggregates the total search run time for each hour as indicator of user behavior.
|
||||
The model identifies the top 0.1% of user search run time, indicating a risky use of these commands. Users can adjust this threshold 0.1% as interested however this will correlate to missed/false positive rates. This search should be scheduled to run at least every 7 days. The name of machine learning model generated is "risky_command_abuse" and should be configured to be globally shared (not private) in MLTK app as documented here:
|
||||
https://docs.splunk.com/Documentation/MLApp/5.3.1/User/Models#Sharing_models_from_other_Splunk_apps
|
||||
unless the same account of training this model will be used to perform inference using this model for anomaly
|
||||
detection.'
|
||||
search: '| tstats sum(Search_Activity.total_run_time) as run_time, count
|
||||
FROM datamodel=Splunk_Audit.Search_Activity WHERE (Search_Activity.user!="")
|
||||
AND (Search_Activity.total_run_time>1) AND (earliest=-7d@d latest=now)
|
||||
AND (Search_Activity.search IN ("*| runshellscript *", "*| collect *","*| delete *", "*| fit *", "*| outputcsv *",
|
||||
"*| outputlookup *", "*| run *", "*| script *", "*| sendalert *", "*| sendemail *", "*| tscolle*"))
|
||||
AND (Search_Activity.search_type=adhoc) AND (Search_Activity.user!=splunk-system-user)
|
||||
BY _time, Search_Activity.user span=1h
|
||||
| fit DensityFunction "run_time" dist=auto lower_threshold=0.000001 upper_threshold=0.001 show_density=true
|
||||
by Search_Activity.user into "risky_command_abuse" '
|
||||
how_to_implement: The corresponding detection of using this model is "Splunk Command and Scripting Interpreter Risky
|
||||
SPL MLTK". This detection depends on MLTK app which can be found here - https://splunkbase.splunk.com/app/2890/
|
||||
and it assumes Splunk accelerated audit data model is available. For large enterprises, training the model might
|
||||
take significant computing resources. It might require dedicated search head. The underlined machine learning
|
||||
algorithm this detection used is DensityFunction. It might need to increase its settings default values, such as
|
||||
max_fit_time, max_groups, etc. More details of achieving optimal performance and configuring DensityFunction
|
||||
parameters can be found here - https://docs.splunk.com/Documentation/MLApp/5.3.1/User/Configurefitandapply
|
||||
Users can modify earliest=-7d@d in the search to other value so that the search can collect enough data points
|
||||
to build a good baseline model. Users can also modify list of risky commands in "Search_Activity.search IN" to better
|
||||
suit users' violation policy and their usage environment.
|
||||
known_false_positives: If the run time of a search exceeds the boundaries of outlier defined by the fitted density
|
||||
function model, false positives can occur, incorrectly labeling a long running search as potentially risky.
|
||||
references:
|
||||
- https://docs.splunk.com/Documentation/Splunk/latest/Security/SPLsafeguards#Commands_that_trigger_the_warning
|
||||
tags:
|
||||
analytic_story:
|
||||
- Splunk Vulnerabilities
|
||||
asset_type: Web Server
|
||||
cis20:
|
||||
- CIS 3
|
||||
- CIS 6
|
||||
confidence: 40
|
||||
cve:
|
||||
- CVE-2022-32154
|
||||
context:
|
||||
- Source: Endpoint
|
||||
dataset:
|
||||
- https://github.com/splunk/attack_data/raw/master/datasets/attack_techniques/T1203/search_activity.txt
|
||||
impact: 50
|
||||
kill_chain_phases:
|
||||
- Actions on Objectives
|
||||
message: ML model "risky_command_abuse" training is completed.
|
||||
mitre_attack_id:
|
||||
- T1059
|
||||
nist:
|
||||
- DE.AE
|
||||
observable:
|
||||
- name: user
|
||||
type: User
|
||||
role:
|
||||
- Victim
|
||||
product:
|
||||
- Splunk Enterprise
|
||||
- Splunk Enterprise Security
|
||||
- Splunk Cloud
|
||||
required_fields:
|
||||
- _time
|
||||
- Search_Activity.search
|
||||
- Search_Activity.total_run_time
|
||||
- Search_Activity.user
|
||||
- Search_Activity.search_type
|
||||
risk_score: 20
|
||||
security_domain: audit
|
||||
detections:
|
||||
- Splunk Command and Scripting Interpreter Risky SPL MLTK
|
||||
deployment:
|
||||
scheduling:
|
||||
cron_schedule: 55 * * * *
|
||||
earliest_time: -70m@m
|
||||
latest_time: -10m@m
|
||||
schedule_window: auto
|
||||
@@ -1,97 +0,0 @@
|
||||
# Batch Detection Testing
|
||||
|
||||
The Splunk Threat Research Team produces the [Enterprise Security Content Update Splunk App](https://splunkbase.splunk.com/app/3449/), a power app that includes hundreds of curated, tested detections that you can run on your own Splunk Enterprise Server today. The splunk/security_content repo gives users an insight into our work and allows them to replicate our workflow and even author their own detections!
|
||||
|
||||
A core component of ESCU is that all of detections must be tested and validated against datasets to ensure they work correctly. In order to achieve that, the Security Content Detection Testing System was built with a few goals in mind:
|
||||
- How can we quickly test a small number of detections, and how can I easily debug those detections?
|
||||
- How can we reliably test new or modified detections every time they are committed to our repo (or every time that a PR is created) inside of GitHub Actions?
|
||||
- At the time of this writing, ESCU contains over 550 detections! How can we quickly test a large number of detections?
|
||||
- How can we run these tests on a wide variety of architectures, from develops' own machines to GitHub Actions to other Cloud Instances?
|
||||
|
||||
This architecture diagram gives insight into the workflow:
|
||||
In summary, the tool:
|
||||
|
||||
1. Downloads the latest version of the security_content Repo
|
||||
2. Lints/Sanity Checks all the Detections
|
||||
3. Builds an ESCU Splunk App
|
||||
4. Starts Docker containers (available from Docker Hub as splunk/splunk:latest), installing required Splunkbase Apps and ESCU
|
||||
5. Distributes Detection Tests Across those Containers
|
||||
6. Summarizes the Results of those Detections
|
||||
|
||||
|
||||
# Running a Basic Test
|
||||
|
||||
## Running the Tool
|
||||
The easiest way to run a tests with default, which is suitable for most use cases, is to run:
|
||||
|
||||
python detection_testing_exectuion.py run --branch BRANCH_TO_TEST [--splunkbase_username YOUR_USERNAME --splunkbase_password YOUR_PASSWORD]
|
||||
|
||||
While the test is running, you'll see helpful information printed, letting you know what step of the process is taking place and what detection is being tested.
|
||||
When you start your Splunk Server, the credentials will be printed out on the command line. You may want to use these credentials to log into the Splunk server, hosted locally, during testing for debugging or other exploration:
|
||||
|
||||
***********************
|
||||
Log into your [1] Splunk Container(s) after they boot at http://127.0.0.1:[8000-8000]
|
||||
Splunk App Username: [admin]
|
||||
Splunk App Password: [PBlZEeGvQrOF57zmUXFPOP]
|
||||
***********************
|
||||
|
||||
While you're running, you'll receive helpful progress updates each minute. They give you information about how long your test has been running, your approximate time remaining, and your approximate system load. Please note that this is total system load, not JUST load used by the detection testing:
|
||||
|
||||
***********PROGRESS UPDATE***********
|
||||
Elapsed Time : 0:27:53.358628
|
||||
Estimated Remaining Time : 0:49:18.896474
|
||||
Tests to run : 36
|
||||
Tests currently running : 1
|
||||
Tests completed : 20
|
||||
Success : 15
|
||||
Failure : 5
|
||||
Error : 0
|
||||
System Information:
|
||||
Total CPU Usage : 44% (2 CPUs)
|
||||
Total Memory Usage: 2.1GB USED / 6.8GB TOTAL
|
||||
Total Disk Usage : 21.4GB USED / 83.2GB TOTAL
|
||||
|
||||
Since you're probably running locally to test and debug your searches, there is a feature (enabled by default) called interactive_failure. If one of your detections fails, the test will pause and the offending detection will print out a message like this:
|
||||
|
||||
|
||||
|
||||
This allows you to login to your Splunk server and debug the search. All of the uploaded data for this search remains on the server. To continue, delete the data for this search, and move on to the next search, simply hit "Enter" in the command prompt.
|
||||
|
||||
When the test run is completed, you'll see some cleanup and summarization information. Finally, asimple output summarizes the test run, such as:
|
||||
|
||||
All containers completed testing!
|
||||
Removing all attack data that was downloaded during this test at: [/home/runner/work/security_content/security_content/bin/automated_detection_testing/ci/detection_testing_batch/attack_data_ogcm5da9]
|
||||
Successfully removed all attack data
|
||||
Generating test_results/success.csv...Done with [48] detections
|
||||
Generating test_results/failure.csv...Done with [9] detections
|
||||
Generating test_results/error.csv...Done with [0] detections
|
||||
Generating test_results/combined.csv...Done with [57] detections
|
||||
Settings updated. Writing results to: test_results/detection_failure_manifest.json
|
||||
Summary:
|
||||
Total Tests: 57
|
||||
Total Pass : 48
|
||||
Total Fail : 9 (0 of these were ERRORS)
|
||||
Test Execution Successful
|
||||
|
||||
Note that execution of this test will be successful if all tests complete, **even if 1 or more of the tests fail or contain errors!**
|
||||
|
||||
## Viewing Detailed Results
|
||||
A number of helpful files are generated when the tool runs and written to the `test_results/` directory. The most important files are:
|
||||
|
||||
- summary.json - A file which contains a summary of the test :
|
||||
- Successes, failures, and errors
|
||||
- The Splunk Apps (and their versions) that were installed
|
||||
- Specific Information about the Branch and Commit Hash the test was run against
|
||||
- Detailed Information about each individual test, including success/failure/error information.
|
||||
|
||||
- detection_failure_manifest.json - A file which allows you to replicate your test, testing ONLY the detections that have failed. This gives the user the chance to interactively debug these failures. This is especially useful because it is also generated by the GitHub Actions CI Pipeline - allowing you to pull a single file and debug failed tests locally in minutes! Because it contains specific application versions and the commit hash, this also lets you reproduce this test, exactly, at any point in the future.
|
||||
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Command Line Arguments
|
||||
There are a large number of configurable parameters for advanced users. To view the most common parameters, simply run
|
||||
|
||||
python detection_testing_batch.py --help
|
||||
|
||||
These commands will be described in more detail at a later time.
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
|
||||
- hosts: all
|
||||
gather_facts: False
|
||||
roles:
|
||||
- attack_replay
|
||||
@@ -1,23 +0,0 @@
|
||||
|
||||
---
|
||||
|
||||
- name: Upload replay
|
||||
copy:
|
||||
src: ../../../{{ folder_name }}/{{ out }}
|
||||
dest: /tmp/{{ out }}
|
||||
|
||||
- name: Call oneshot import
|
||||
uri:
|
||||
url: https://localhost:8089/services/data/inputs/oneshot
|
||||
validate_certs: no
|
||||
method: POST
|
||||
user: admin
|
||||
password: "{{ splunk_password }}"
|
||||
force_basic_auth: yes
|
||||
body_format: form-urlencoded
|
||||
body:
|
||||
name: /tmp/{{ out }}
|
||||
sourcetype: "{{ sourcetype }}"
|
||||
rename-source: "{{ source }}"
|
||||
index: "{{ index }}"
|
||||
status_code: 201
|
||||
@@ -1,32 +0,0 @@
|
||||
---
|
||||
|
||||
- name: Delete ESCU APP
|
||||
file:
|
||||
state: absent
|
||||
path: "/opt/splunk/etc/apps/DA-ESS-ContentUpdate"
|
||||
become: yes
|
||||
|
||||
- name: Upload ESCU APP
|
||||
copy:
|
||||
src: ../../../{{ security_content_path }}/dist/escu/
|
||||
dest: "/opt/splunk/etc/apps/DA-ESS-ContentUpdate"
|
||||
owner: splunk
|
||||
group: splunk
|
||||
become: yes
|
||||
|
||||
|
||||
- name: restart containerized splunk
|
||||
ansible.builtin.shell: /opt/splunk/bin/splunk restart
|
||||
become: yes
|
||||
- name: restart splunk
|
||||
service:
|
||||
name: splunkd
|
||||
state: restarted
|
||||
become: yes
|
||||
|
||||
- name: restart splunk
|
||||
service:
|
||||
name: splunkd
|
||||
state: restarted
|
||||
become: yes
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
- hosts: all
|
||||
gather_facts: False
|
||||
roles:
|
||||
- update_escu
|
||||
@@ -1,564 +0,0 @@
|
||||
import argparse
|
||||
import copy
|
||||
import csv
|
||||
# from ctypes.wintypes import tagRECT
|
||||
import json
|
||||
import os
|
||||
import queue
|
||||
import random
|
||||
import secrets
|
||||
import shutil
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
from collections import OrderedDict
|
||||
from datetime import datetime, timedelta
|
||||
from posixpath import basename
|
||||
from tempfile import mkdtemp
|
||||
from timeit import default_timer as timer
|
||||
from typing import Union
|
||||
from urllib.parse import urlparse
|
||||
import signal
|
||||
|
||||
|
||||
import docker
|
||||
import requests
|
||||
import requests.packages.urllib3
|
||||
from docker.client import DockerClient
|
||||
from requests import get
|
||||
|
||||
|
||||
|
||||
from modules import (container_manager, new_arguments2,
|
||||
testing_service, validate_args, utils)
|
||||
from modules.github_service import GithubService
|
||||
from modules.validate_args import validate, validate_and_write, ES_APP_NAME
|
||||
|
||||
SPLUNK_CONTAINER_APPS_DIR = "/opt/splunk/etc/apps"
|
||||
index_file_local_path = "indexes.conf.tar"
|
||||
index_file_container_path = os.path.join(SPLUNK_CONTAINER_APPS_DIR, "search")
|
||||
|
||||
# Should be the last one we copy.
|
||||
datamodel_file_local_path = "datamodels.conf.tar"
|
||||
datamodel_file_container_path = os.path.join(
|
||||
SPLUNK_CONTAINER_APPS_DIR, "Splunk_SA_CIM")
|
||||
|
||||
|
||||
authorizations_file_local_path = "authorize.conf.tar"
|
||||
authorizations_file_container_path = "/opt/splunk/etc/system/local"
|
||||
|
||||
CONTAINER_APP_DIRECTORY = "apps"
|
||||
|
||||
MAX_RECOMMENDED_CONTAINERS_BEFORE_WARNING = 2
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def copy_local_apps_to_directory(apps: dict[str, dict], splunkbase_username:tuple[str,None] = None, splunkbase_password:tuple[str,None] = None, mock:bool = False, target_directory:str = "apps") -> str:
|
||||
if mock is True:
|
||||
target_directory = os.path.join("prior_config", target_directory)
|
||||
|
||||
# Remove the apps directory or the prior config directory. If it's just an apps directory, then we don't want
|
||||
#to remove that.
|
||||
shutil.rmtree(target_directory, ignore_errors=True)
|
||||
try:
|
||||
# Make sure the directory exists. If it already did, that's okay. Don't delete anything from it
|
||||
# We want to re-use previously downloaded apps
|
||||
os.makedirs(target_directory, exist_ok = True)
|
||||
|
||||
except Exception as e:
|
||||
raise(Exception(f"Some error occured when trying to make the {target_directory}: [{str(e)}]"))
|
||||
|
||||
|
||||
for key, item in apps.items():
|
||||
|
||||
# These apps are URLs that will be passed. The apps will be downloaded and installed by the container
|
||||
# # Get the file from an http source
|
||||
splunkbase_info = True if ('app_number' in item and item['app_number'] is not None and
|
||||
'app_version' in item and item['app_version'] is not None) else False
|
||||
splunkbase_creds = True if (splunkbase_username is not None and
|
||||
splunkbase_password is not None) else False
|
||||
can_download_from_splunkbase = splunkbase_info and splunkbase_creds
|
||||
|
||||
|
||||
|
||||
#local apps can either have a local_path or an http_path
|
||||
if 'local_path' in item:
|
||||
source_path = os.path.abspath(os.path.expanduser(item['local_path']))
|
||||
base_name = os.path.basename(source_path)
|
||||
dest_path = os.path.join(target_directory, base_name)
|
||||
try:
|
||||
print(f"copying {os.path.relpath(source_path)} to {os.path.relpath(dest_path)}")
|
||||
shutil.copy(source_path, dest_path)
|
||||
item['local_path'] = dest_path
|
||||
except shutil.SameFileError as e:
|
||||
# Same file, not a real error. The copy just doesn't happen
|
||||
print("err:%s" % (str(e)))
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Error copying ESCU Package [%s] to [%s]: [%s].\n\tQuitting..." % (
|
||||
source_path, dest_path, str(e)), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
elif can_download_from_splunkbase is True:
|
||||
#Don't do anything, this will be downloaded from splunkbase
|
||||
pass
|
||||
elif splunkbase_info is True and splunkbase_creds is False and mock is True:
|
||||
#Don't need to do anything, when this actually runs the apps will be downloaded from Splunkbase
|
||||
#There is another opportunity to provide the creds then
|
||||
pass
|
||||
elif 'http_path' in item and can_download_from_splunkbase is False:
|
||||
http_path = item['http_path']
|
||||
try:
|
||||
url_parse_obj = urlparse(http_path)
|
||||
path_after_host = url_parse_obj[2].rstrip('/') #removes / at the end, if applicable
|
||||
base_name = path_after_host.rpartition('/')[-1] #just get the file name
|
||||
dest_path = os.path.join(target_directory, base_name) #write the whole path
|
||||
utils.download_file_from_http(http_path, dest_path, verbose_print=True)
|
||||
#we need to update the local path because this is used to copy it into the container later
|
||||
item['local_path'] = dest_path
|
||||
#Remove the HTTP Path, we will use the local_path instead
|
||||
except Exception as e:
|
||||
print("Error trying to download %s @ %s: [%s]. This app is required.\n\tQuitting..."%(key, http_path, str(e)),file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
elif splunkbase_info is False:
|
||||
print(f"Error - trying to install an app [{key}] that does not have 'local_path', 'http_path', "
|
||||
"or 'app_version' and 'app_number' for installing from Splunkbase.\n\tQuitting...")
|
||||
sys.exit(1)
|
||||
return target_directory
|
||||
|
||||
|
||||
def ensure_security_content(branch: str, commit_hash: Union[str,None], pr_number: Union[int, None], persist_security_content: bool) -> tuple[GithubService, bool]:
|
||||
if persist_security_content is True and os.path.exists("security_content"):
|
||||
print("****** You chose --persist_security_content and the security_content directory exists. "
|
||||
"We will not check out the repo again. Please be aware, this could cause issues if your "
|
||||
"repo is out of date or if a previous build failed to download all required tools and "\
|
||||
"libraries. If this occurs, it is suggested to change the "\
|
||||
"persist_security_content setting to false. ******")
|
||||
|
||||
github_service = GithubService(
|
||||
branch, commit_hash, persist_security_content=persist_security_content)
|
||||
|
||||
else:
|
||||
if persist_security_content is True and not os.path.exists("security_content"):
|
||||
print("Error - you chose --persist_security_content but the security_content directory does not exist!"
|
||||
" We will check it out for you.")
|
||||
persist_security_content = False
|
||||
|
||||
elif os.path.exists("security_content/"):
|
||||
print("Deleting the security_content directory")
|
||||
try:
|
||||
shutil.rmtree("security_content/", ignore_errors=True)
|
||||
print("Successfully removed security_content directory")
|
||||
except Exception as e:
|
||||
print(
|
||||
"Error - could not remove the security_content directory: [%s].\n\tQuitting..." % (str(e)))
|
||||
sys.exit(1)
|
||||
|
||||
if pr_number:
|
||||
github_service = GithubService(branch, commit_hash, pr_number)
|
||||
else:
|
||||
github_service = GithubService(branch, commit_hash)
|
||||
|
||||
return github_service, persist_security_content
|
||||
|
||||
|
||||
def generate_escu_app(persist_security_content: bool = False) -> str:
|
||||
# Go into the security content directory
|
||||
print("****GENERATING ESCU APP****")
|
||||
os.chdir("security_content")
|
||||
if persist_security_content is False:
|
||||
commands = ["python ../../../contentctl.py --path . --skip_enrichment generate --product ESCU --output dist/escu"]
|
||||
else:
|
||||
commands = ["python ../../../contentctl.py --path . --skip_enrichment generate --product ESCU --output dist/escu"]
|
||||
ret = subprocess.run("; ".join(commands),
|
||||
shell=True, capture_output=True)
|
||||
if ret.returncode != 0:
|
||||
print(f"Error generating new content0.\n\tQuitting and dumping error...\n{str(ret.stderr)}\n{str(ret.stdout)}")
|
||||
sys.exit(1)
|
||||
|
||||
ret = subprocess.run("tar -czf DA-ESS-ContentUpdate.spl -C dist/escu .",
|
||||
shell=True, capture_output=True)
|
||||
if ret.returncode != 0:
|
||||
print("Error generating new content1.\n\tQuitting and dumping error...\n[%s]" % (
|
||||
ret.stderr))
|
||||
sys.exit(1)
|
||||
|
||||
output_file_name = "DA-ESS-ContentUpdate-latest.tar.gz"
|
||||
output_file_path_from_slim_latest = os.path.join(
|
||||
"upload", output_file_name)
|
||||
output_file_path_from_security_content = os.path.join(
|
||||
"slim_packaging", output_file_path_from_slim_latest)
|
||||
output_file_path_from_root = os.path.join(
|
||||
"security_content", output_file_path_from_security_content)
|
||||
|
||||
if persist_security_content is True:
|
||||
try:
|
||||
os.remove(output_file_path_from_security_content)
|
||||
except FileNotFoundError:
|
||||
# No problem if we fail to remove it, that just means it wasn't there and we didn't need to
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Error deleting the (possibly) existing old ESCU File: [%s]" % (
|
||||
str(e)), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# There remove the latest file if it exists
|
||||
commands = ["cd slim_packaging",
|
||||
"cp -R ../dist/escu DA-ESS-ContentUpdate",
|
||||
"mkdir upload",
|
||||
"tar -czf upload/DA-ESS-ContentUpdate*.tar.gz DA-ESS-ContentUpdate",
|
||||
"cp upload/DA-ESS-ContentUpdate*.tar.gz %s" % (output_file_path_from_slim_latest)]
|
||||
|
||||
else:
|
||||
os.mkdir("slim_packaging")
|
||||
commands = ["rm -rf slim_packaging/slim_latest",
|
||||
"mkdir slim_packaging",
|
||||
"cd slim_packaging",
|
||||
"cp -R ../dist/escu DA-ESS-ContentUpdate",
|
||||
"mkdir upload",
|
||||
"tar -czf upload/DA-ESS-ContentUpdate*.tar.gz DA-ESS-ContentUpdate",
|
||||
"cp upload/DA-ESS-ContentUpdate*.tar.gz %s" % (output_file_path_from_slim_latest)]
|
||||
|
||||
ret = subprocess.run("; ".join(commands),
|
||||
shell=True, capture_output=True)
|
||||
if ret.returncode != 0:
|
||||
print("Command List:\n%s" % (commands))
|
||||
print("Error generating new ESCU Package.\n\tQuitting and dumping error...\n[%s]" % (
|
||||
ret.stderr.decode('utf-8')), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
os.chdir("../")
|
||||
|
||||
return output_file_path_from_root
|
||||
|
||||
|
||||
|
||||
def finish_mock(settings: dict, detections: list[str], output_file_template: str = "prior_config/config_tests_%d.json")->bool:
|
||||
num_containers = settings['num_containers']
|
||||
|
||||
for output_file_index in range(0, num_containers):
|
||||
fname = output_file_template % (output_file_index)
|
||||
|
||||
# Get the n'th detection for this file
|
||||
detection_tests = detections[output_file_index::num_containers]
|
||||
normalized_detection_names = []
|
||||
# Normalize the test filename to the name of the detection instead.
|
||||
# These are what we should write to the file
|
||||
for d in detection_tests:
|
||||
filename = os.path.basename(d)
|
||||
filename = filename.replace(".test.yml", ".yml")
|
||||
leading = os.path.split(d)[0]
|
||||
leading = leading.replace("tests/", "detections/")
|
||||
new_name = os.path.join(
|
||||
"security_content", leading, filename)
|
||||
normalized_detection_names.append(new_name)
|
||||
|
||||
# Generate an appropriate config file for this test
|
||||
mock_settings = copy.deepcopy(settings)
|
||||
# This may be able to support as many as 2 for GitHub Actions...
|
||||
# we will have to determine in testing.
|
||||
mock_settings['num_containers'] = 1
|
||||
|
||||
# Must be selected since we are passing in a list of detections
|
||||
mock_settings['mode'] = 'selected'
|
||||
|
||||
# Pass in the list of detections to run
|
||||
mock_settings['detections_list'] = normalized_detection_names
|
||||
|
||||
# We want to persist security content and run with the escu package that we created.
|
||||
#Note that if we haven't checked this out yet, we will check it out for you.
|
||||
mock_settings['persist_security_content'] = True
|
||||
|
||||
mock_settings['mock'] = False
|
||||
|
||||
# Make sure that it still validates after all of the changes
|
||||
|
||||
try:
|
||||
with open(fname, 'w') as outfile:
|
||||
validated_settings, b = validate_and_write(configuration=mock_settings, output_file = outfile, strip_credentials=True)
|
||||
if validated_settings is None:
|
||||
print(
|
||||
"There was an error validating the updated mock settings.\n\tQuitting...", file=sys.stderr)
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print("Error writing config file %s: [%s]\n\tQuitting..." % (
|
||||
fname, str(e)), file=sys.stderr)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def main(args: list[str]):
|
||||
#Disable insecure warnings. We make a number of HTTPS requests to Splunk
|
||||
#docker containers that we've set up. Without this line, we get an
|
||||
#insecure warning every time due to invalid cert.
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
start_datetime = datetime.now()
|
||||
|
||||
action, settings = new_arguments2.parse(args)
|
||||
if action == "configure":
|
||||
# Done, nothing else to do
|
||||
print("Configuration complete!")
|
||||
sys.exit(0)
|
||||
elif action != "run":
|
||||
print("Unsupported action: [%s]" % (action), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if settings['mock'] is False:
|
||||
# If this is a real run, then make sure Docker is installed and running and usable
|
||||
# If this is a mock, then that is not required. By only checking on a non-mock
|
||||
# run, we save ourselves the need to install docker in the CI for the manifest
|
||||
# generation step.
|
||||
try:
|
||||
docker.client.from_env()
|
||||
except Exception as e:
|
||||
print("Error, failed to get docker client. Is Docker Installed and Running?\n\t%s" % (str(e)))
|
||||
sys.exit(1)
|
||||
|
||||
credentials_needed = False
|
||||
credential_error = False
|
||||
|
||||
|
||||
|
||||
if settings['splunkbase_username'] == None or settings['splunkbase_password'] == None:
|
||||
|
||||
missing_credentials = []
|
||||
if settings['splunkbase_username'] == None:
|
||||
missing_credentials.append("--splunkbase_username")
|
||||
if settings['splunkbase_password'] == None:
|
||||
missing_credentials.append("--splunkbase_password")
|
||||
|
||||
missing_credentials_string = '\n\t'.join(missing_credentials)
|
||||
|
||||
splunkbase_only_apps = []
|
||||
for app,content in settings['apps'].items():
|
||||
if 'local_path' not in content and 'http_path' not in content:
|
||||
splunkbase_only_apps.append(app)
|
||||
if len(splunkbase_only_apps) != 0:
|
||||
print(f"Error - you have attempted to install the following apps: {splunkbase_only_apps}, "
|
||||
"but you have not provided a local_path or an http_path in the config file. Normally, "
|
||||
"we would download these from Splunkbase, but the following credentials are "
|
||||
f"missing:\n\t{missing_credentials_string}\n Please provide them on the command line "
|
||||
"or in the config file.\n\tQuitting...")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"You have listed apps to install but have "\
|
||||
f"not provided\n\t{missing_credentials_string} \nvia the command line or config file. "
|
||||
f"We will download these files from S3 rather than Splunkbase.")
|
||||
else:
|
||||
|
||||
print(f"You have listed apps to install and provided Splunkbase credentials. "\
|
||||
f"These apps will be downloaded and installed from Splunkbase!")
|
||||
|
||||
|
||||
|
||||
|
||||
FULL_DOCKER_HUB_CONTAINER_NAME = "splunk/splunk:%s" % settings['container_tag']
|
||||
|
||||
if settings['num_containers'] > MAX_RECOMMENDED_CONTAINERS_BEFORE_WARNING:
|
||||
print("You requested to run with [%d] containers which may use a very large amount of resources "
|
||||
"as they all run in parallel. The maximum suggested number of parallel containers is "
|
||||
"[%d]. We will do what you asked, but be warned!" % (settings['num_containers'], MAX_RECOMMENDED_CONTAINERS_BEFORE_WARNING))
|
||||
|
||||
# Check out security content if required
|
||||
try:
|
||||
#Make sure we fix up the persist_securiy_content argument if it is passed in error (we say it exists but it doesn't)
|
||||
github_service, settings['persist_security_content'] = ensure_security_content(
|
||||
settings['branch'], settings['commit_hash'], settings['pr_number'], settings['persist_security_content'])
|
||||
settings['commit_hash'] = github_service.commit_hash
|
||||
except Exception as e:
|
||||
print("\nFailure checking out git repository: [%s]"\
|
||||
"\n\tCommit Hash: [%s]"\
|
||||
"\n\tBranch : [%s]"\
|
||||
"\n\tPR : [%s]\n\tQuitting..."%
|
||||
(str(e),settings['commit_hash'],settings['branch'],settings['pr_number']),file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
#passes = [{'search_string': '| tstats `security_content_summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where (Processes.process_name ="7z.exe" OR Processes.process_name = "7za.exe" OR Processes.original_file_name = "7z.exe" OR Processes.original_file_name = "7za.exe") AND (Processes.process="*\\\\C$\\\\*" OR Processes.process="*\\\\Admin$\\\\*" OR Processes.process="*\\\\IPC$\\\\*") by Processes.original_file_name Processes.parent_process_name Processes.parent_process Processes.process_name Processes.process Processes.parent_process_id Processes.process_id Processes.dest Processes.user | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `7zip_commandline_to_smb_share_path_filter` | stats count | where count > 0', 'detection_name': '7zip CommandLine To SMB Share Path', 'detection_file': 'endpoint/7zip_commandline_to_smb_share_path.yml', 'success': True, 'error': False, 'diskUsage': '286720', 'runDuration': '0.922', 'scanCount': '4897'}]
|
||||
#github_service.update_and_commit_passed_tests(passes)
|
||||
#sys.exit(0)
|
||||
# Make a backup of this config containing the hash and stripped credentials.
|
||||
# This makes the test perfectly reproducible.
|
||||
reproduce_test_config, _ = validate_args.validate_and_write(settings, output_file=None, strip_credentials=True)
|
||||
if reproduce_test_config == None:
|
||||
print("Error - there was an error writing out the file to reproduce the test. This should not happen, as all "\
|
||||
"settings should have been validated by this point.\n\tQuitting...",file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
all_detection_files = github_service.get_detection_files(settings['mode'],
|
||||
settings['folders'],
|
||||
settings['types'],
|
||||
settings['detections_list'])
|
||||
|
||||
#We randomly shuffle this because there are likely patterns in searches. For example,
|
||||
#cloud/endpoint/network likely have different impacts on the system. By shuffling,
|
||||
#we spread out this load on a single computer, but also spread it in case
|
||||
#we are running on GitHub Actions against multiple machines. Hopefully, this
|
||||
#will reduce that chnaces the some machines run and complete quickly while
|
||||
#others take a long time.
|
||||
random.shuffle(all_detection_files)
|
||||
|
||||
except Exception as e:
|
||||
print("Error getting test files:\n%s"%(str(e)), file=sys.stderr)
|
||||
print("\tQuitting...", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print("***This run will test [%d] detections!***"%(len(all_detection_files)))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Check to see if we want to install ESCU and whether it was preeviously generated and we should use that file
|
||||
if ES_APP_NAME in settings['apps'] and settings['apps'][ES_APP_NAME]['local_path'] is not None:
|
||||
# Using a pregenerated ESCU, no need to build it
|
||||
pass
|
||||
|
||||
elif ES_APP_NAME not in settings['apps']:
|
||||
print(f"{ES_APP_NAME} was not found in {settings['apps'].keys()}. We assume this is an error and shut down.\n\t"
|
||||
"Quitting...", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
else:
|
||||
# Generate the ESCU package from this branch.
|
||||
source_path = generate_escu_app(settings['persist_security_content'])
|
||||
settings['apps']['SPLUNK_ES_CONTENT_UPDATE']['local_path'] = source_path
|
||||
|
||||
|
||||
# Copy all the apps, to include ESCU (whether pregenerated or just generated)
|
||||
try:
|
||||
relative_app_path = copy_local_apps_to_directory(settings['apps'],
|
||||
splunkbase_username = settings['splunkbase_username'],
|
||||
splunkbase_password = settings['splunkbase_password'],
|
||||
mock=settings['mock'], target_directory = CONTAINER_APP_DIRECTORY)
|
||||
|
||||
mounts = [{"local_path": os.path.abspath(relative_app_path),
|
||||
"container_path": "/tmp/apps", "type": "bind", "read_only": True}]
|
||||
except Exception as e:
|
||||
print(f"Error occurred when copying apps to app folder: [{str(e)}]\n\tQuitting...", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# If this is a mock run, finish it now
|
||||
if settings['mock']:
|
||||
#The function below
|
||||
if finish_mock(settings, all_detection_files):
|
||||
# mock was successful!
|
||||
print("Mock successful! Manifests generated!")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("There was an unrecoverage error during the mock.\n\tQuitting...",file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
|
||||
#Add some files that always need to be copied to to container to set up indexes and datamodels.
|
||||
files_to_copy_to_container = OrderedDict()
|
||||
files_to_copy_to_container["INDEXES"] = {
|
||||
"local_file_path": index_file_local_path, "container_file_path": index_file_container_path}
|
||||
files_to_copy_to_container["DATAMODELS"] = {
|
||||
"local_file_path": datamodel_file_local_path, "container_file_path": datamodel_file_container_path}
|
||||
files_to_copy_to_container["AUTHORIZATIONS"] = {
|
||||
"local_file_path": authorizations_file_local_path, "container_file_path": authorizations_file_container_path}
|
||||
|
||||
|
||||
|
||||
def shutdown_signal_handler_setup(sig, frame):
|
||||
|
||||
print(f"Signal {sig} received... stopping all [{settings['num_containers']}] containers and shutting down...")
|
||||
shutdown_client = docker.client.from_env()
|
||||
errorCount = 0
|
||||
for container_number in range(settings['num_containers']):
|
||||
container_name = settings['local_base_container_name']%container_number
|
||||
print(f"Shutting down {container_name}...", file=sys.stderr, end='')
|
||||
sys.stdout.flush()
|
||||
try:
|
||||
container = shutdown_client.containers.get(container_name)
|
||||
#Note that stopping does not remove any of the volumes or logs,
|
||||
#so stopping can be useful if we want to debug any container failure
|
||||
container.stop(timeout=10)
|
||||
print("done", file=sys.stderr)
|
||||
except Exception as e:
|
||||
print(f"Error trying to shut down {container_name}. It may have already shut down. Stop it youself with 'docker containter stop {container_name}", sys.stderr)
|
||||
errorCount += 1
|
||||
if errorCount == 0:
|
||||
print("All containers shut down successfully", file=sys.stderr)
|
||||
else:
|
||||
print(f"{errorCount} containers may still be running. Find out what is running with:\n\t'docker container ls'\nand shut them down with\n\t'docker container stop CONTAINER_NAME' ", file=sys.stderr)
|
||||
|
||||
print("Quitting...",file=sys.stderr)
|
||||
#We must use os._exit(1) because sys.exit(1) actually generates an exception which can be caught! And then we don't Quit!
|
||||
os._exit(1)
|
||||
|
||||
|
||||
|
||||
|
||||
#Setup requires a different teardown handler than during execution
|
||||
signal.signal(signal.SIGINT, shutdown_signal_handler_setup)
|
||||
|
||||
|
||||
try:
|
||||
cm = container_manager.ContainerManager(all_detection_files,
|
||||
FULL_DOCKER_HUB_CONTAINER_NAME,
|
||||
settings['local_base_container_name'],
|
||||
settings['num_containers'],
|
||||
settings['apps'],
|
||||
settings['branch'],
|
||||
settings['commit_hash'],
|
||||
reproduce_test_config,
|
||||
files_to_copy_to_container=files_to_copy_to_container,
|
||||
web_port_start=8000,
|
||||
management_port_start=8089,
|
||||
mounts=mounts,
|
||||
show_container_password=settings['show_splunk_app_password'],
|
||||
container_password=settings['splunk_app_password'],
|
||||
splunkbase_username=settings['splunkbase_username'],
|
||||
splunkbase_password=settings['splunkbase_password'],
|
||||
reuse_image=settings['reuse_image'],
|
||||
interactive_failure=not settings['no_interactive_failure'],
|
||||
interactive=settings['interactive'])
|
||||
except Exception as e:
|
||||
print("Error - unrecoverable error trying to set up the containers: [%s].\n\tQuitting..."%(str(e)),file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
def shutdown_signal_handler_execution(sig, frame):
|
||||
#Set that a container has failed which will gracefully stop the other containers.
|
||||
#This way we get our full cleanup routine, too!
|
||||
print("Got a signal to shut down. Shutting down all containers, please wait...", file=sys.stderr)
|
||||
cm.synchronization_object.containerFailure()
|
||||
|
||||
#Update the signal handler
|
||||
|
||||
signal.signal(signal.SIGINT, shutdown_signal_handler_execution)
|
||||
try:
|
||||
result = cm.run_test()
|
||||
except Exception as e:
|
||||
print("Error - there was an error running the tests: [%s]\n\tQuitting..."%(str(e)),file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
#github_service.update_and_commit_passed_tests(cm.synchronization_object.successes)
|
||||
|
||||
|
||||
#Return code indicates whether testing succeeded and all tests were run.
|
||||
#It does NOT indicate that all tests passed!
|
||||
if result is True:
|
||||
print("Test Execution Successful")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("Test Execution Failed - review the logs for more details")
|
||||
#Because one or more of the threads could be stuck in a certain setup loop, like
|
||||
#trying to copy files to a containers (which igonores errors), we must os._exit
|
||||
#instead of sys.exit
|
||||
os._exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1:])
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
|
||||
RAW_BADGE_SVG = '''<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="20">
|
||||
<linearGradient id="a" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="2" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
|
||||
<rect rx="3" width="60" height="20" fill="#555"/> <!-- Comment -->
|
||||
<rect rx="3" x="60" width="40" height="20" fill="#4c1"/>
|
||||
|
||||
<path fill="#4c1" d="M58 0h4v20h-4z"/>
|
||||
|
||||
<rect rx="3" width="100" height="20" fill="url(#a)"/>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="30" y="14">{}</text>
|
||||
<text x="80" y="14">{}</text>
|
||||
</g>
|
||||
</svg>'''
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(description='Use a summary.json file to generate a test coverage badge')
|
||||
parser.add_argument('-i', "--input_summary_file", type=argparse.FileType('r'), required = True,
|
||||
help='Summary file to use to generate the pass percentage badge')
|
||||
parser.add_argument('-o', "--output_badge_file", type=argparse.FileType('w'), required = True,
|
||||
help='Name of the badge to output')
|
||||
parser.add_argument('-s', "--badge_string", type=str, required = True,
|
||||
help='Name of the badge to output')
|
||||
|
||||
|
||||
|
||||
try:
|
||||
results = parser.parse_args()
|
||||
except Exception as e:
|
||||
print(f"Error parsing arguments: {str(e)}")
|
||||
exit(1)
|
||||
|
||||
try:
|
||||
summary_info = json.loads(results.input_summary_file.read())
|
||||
except Exception as e:
|
||||
print(f"Error loading {results.input_summary_file.name} JSON file: {str(e)}")
|
||||
sys.exit(1)
|
||||
|
||||
if 'summary' not in summary_info:
|
||||
print("Missing 'summary' key in {results.input_summary_file.name}")
|
||||
sys.exit(1)
|
||||
elif 'PASS_RATE' not in summary_info['summary'] or 'TESTS_PASSED' not in summary_info['summary']:
|
||||
print(f"Missing PASS_RATE in 'summary' section of {results.input_summary_file.name}")
|
||||
sys.exit(1)
|
||||
pass_percent = 100 * summary_info['summary']['PASS_RATE']
|
||||
|
||||
|
||||
try:
|
||||
results.output_badge_file.write(RAW_BADGE_SVG.format(results.badge_string, "{:2.1f}%".format(pass_percent)))
|
||||
except Exception as e:
|
||||
print(f"Error generating badge: {str(e)}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
print(f"Badge {results.output_badge_file.name} successfully generated!")
|
||||
sys.exit(0)
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
import json
|
||||
from datetime import datetime
|
||||
from datetime import timedelta
|
||||
#import fileinput
|
||||
import os
|
||||
import re
|
||||
import io
|
||||
|
||||
class DataManipulation:
|
||||
|
||||
def manipulate_timestamp(self, file_path, sourcetype, source):
|
||||
|
||||
|
||||
#print('Updating timestamps in attack_data before replaying')
|
||||
|
||||
if sourcetype == 'aws:cloudtrail':
|
||||
self.manipulate_timestamp_cloudtrail(file_path)
|
||||
|
||||
if source == 'WinEventLog:System' or source == 'WinEventLog:Security':
|
||||
self.manipulate_timestamp_windows_event_log_raw(file_path)
|
||||
|
||||
if source == 'exchange':
|
||||
self.manipulate_timestamp_exchange_logs(file_path)
|
||||
|
||||
|
||||
def manipulate_timestamp_exchange_logs(self, path):
|
||||
#path = os.path.join(os.path.dirname(__file__), '../' + file_path)
|
||||
#path = path.replace('modules/../','')
|
||||
|
||||
f = io.open(path, "r", encoding="utf-8")
|
||||
|
||||
first_line = f.readline()
|
||||
d = json.loads(first_line)
|
||||
latest_event = datetime.strptime(d["CreationTime"],"%Y-%m-%dT%H:%M:%S")
|
||||
|
||||
now = datetime.now()
|
||||
now = now.strftime("%Y-%m-%dT%H:%M:%S")
|
||||
now = datetime.strptime(now,"%Y-%m-%dT%H:%M:%S")
|
||||
|
||||
difference = now - latest_event
|
||||
f.close()
|
||||
|
||||
#Mimic the behavior of fileinput but in a threadsafe way
|
||||
#Rename the file, which fileinput does for inplace.
|
||||
#Note that path will now be the new file
|
||||
original_backup_file = f"{path}.bak"
|
||||
os.rename(path, original_backup_file)
|
||||
|
||||
with open(original_backup_file, "r") as original_file:
|
||||
with open(path, "w") as new_file:
|
||||
for line in original_file:
|
||||
d = json.loads(line)
|
||||
original_time = datetime.strptime(d["CreationTime"],"%Y-%m-%dT%H:%M:%S")
|
||||
new_time = (difference + original_time)
|
||||
|
||||
original_time = original_time.strftime("%Y-%m-%dT%H:%M:%S")
|
||||
new_time = new_time.strftime("%Y-%m-%dT%H:%M:%S")
|
||||
#There is no end character appended, no need for end=''
|
||||
new_file.write(line.replace(original_time, new_time))
|
||||
|
||||
|
||||
os.remove(original_backup_file)
|
||||
|
||||
def manipulate_timestamp_windows_event_log_raw(self, path):
|
||||
#path = os.path.join(os.path.dirname(__file__), '../' + file_path)
|
||||
#path = path.replace('modules/../','')
|
||||
|
||||
f = io.open(path, "r", encoding="utf-8")
|
||||
self.now = datetime.now()
|
||||
self.now = self.now.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
self.now = datetime.strptime(self.now,"%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
|
||||
# read raw logs
|
||||
regex = r'\d{2}/\d{2}/\d{4} \d{2}:\d{2}:\d{2} [AP]M'
|
||||
data = f.read()
|
||||
lst_matches = re.findall(regex, data)
|
||||
if len(lst_matches) > 0:
|
||||
latest_event = datetime.strptime(lst_matches[-1],"%m/%d/%Y %I:%M:%S %p")
|
||||
self.difference = self.now - latest_event
|
||||
f.close()
|
||||
|
||||
result = re.sub(regex, self.replacement_function, data)
|
||||
|
||||
with io.open(path, "w+", encoding='utf8') as f:
|
||||
f.write(result)
|
||||
else:
|
||||
f.close()
|
||||
return
|
||||
|
||||
|
||||
def replacement_function(self, match):
|
||||
try:
|
||||
event_time = datetime.strptime(match.group(),"%m/%d/%Y %I:%M:%S %p")
|
||||
new_time = self.difference + event_time
|
||||
return new_time.strftime("%m/%d/%Y %I:%M:%S %p")
|
||||
except Exception as e:
|
||||
self.logger.error("Error in timestamp replacement occured: " + str(e))
|
||||
return match.group()
|
||||
|
||||
|
||||
def manipulate_timestamp_cloudtrail(self, path):
|
||||
#path = os.path.join(os.path.dirname(__file__), '../' + file_path)
|
||||
#path = path.replace('modules/../','')
|
||||
|
||||
f = io.open(path, "r", encoding="utf-8")
|
||||
|
||||
try:
|
||||
first_line = f.readline()
|
||||
d = json.loads(first_line)
|
||||
latest_event = datetime.strptime(d["eventTime"],"%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
|
||||
now = datetime.now()
|
||||
now = now.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
now = datetime.strptime(now,"%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
except ValueError:
|
||||
first_line = f.readline()
|
||||
d = json.loads(first_line)
|
||||
latest_event = datetime.strptime(d["eventTime"],"%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
now = datetime.now()
|
||||
now = now.strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
now = datetime.strptime(now,"%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
difference = now - latest_event
|
||||
f.close()
|
||||
|
||||
|
||||
|
||||
#Mimic the behavior of fileinput but in a threadsafe way
|
||||
#Rename the file, which fileinput does for inplace.
|
||||
#Note that path will now be the new file
|
||||
original_backup_file = f"{path}.bak"
|
||||
os.rename(path, original_backup_file)
|
||||
|
||||
with open(original_backup_file, "r") as original_file:
|
||||
with open(path, "w") as new_file:
|
||||
for line in original_file:
|
||||
try:
|
||||
d = json.loads(line)
|
||||
original_time = datetime.strptime(d["eventTime"],"%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
new_time = (difference + original_time)
|
||||
|
||||
original_time = original_time.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
new_time = new_time.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
new_file.write(line.replace(original_time, new_time))
|
||||
except ValueError:
|
||||
d = json.loads(line)
|
||||
original_time = datetime.strptime(d["eventTime"],"%Y-%m-%dT%H:%M:%SZ")
|
||||
new_time = (difference + original_time)
|
||||
|
||||
original_time = original_time.strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
new_time = new_time.strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
new_file.write(line.replace(original_time, new_time))
|
||||
|
||||
|
||||
os.remove(original_backup_file)
|
||||
@@ -1,305 +0,0 @@
|
||||
from collections import OrderedDict
|
||||
from tabnanny import check
|
||||
import docker
|
||||
import datetime
|
||||
import docker.types
|
||||
import os
|
||||
import random
|
||||
|
||||
from modules import splunk_container
|
||||
import string
|
||||
from modules import test_driver
|
||||
import threading
|
||||
import time
|
||||
import timeit
|
||||
|
||||
from typing import Union
|
||||
|
||||
WEB_PORT_STRING = "8000/tcp"
|
||||
MANAGEMENT_PORT_STRING = "8089/tcp"
|
||||
|
||||
|
||||
class ContainerManager:
|
||||
def __init__(
|
||||
self,
|
||||
test_list: list[str],
|
||||
full_docker_hub_name: str,
|
||||
container_name_template: str,
|
||||
num_containers: int,
|
||||
apps: OrderedDict,
|
||||
branch:str,
|
||||
commit_hash:str,
|
||||
summarization_reproduce_failure_config:dict,
|
||||
files_to_copy_to_container: OrderedDict = OrderedDict(),
|
||||
web_port_start: int = 8000,
|
||||
management_port_start: int = 8089,
|
||||
mounts: list[dict[str, str]] = [],
|
||||
show_container_password:bool=True,
|
||||
container_password: Union[str, None] = None,
|
||||
splunkbase_username: Union[str, None] = None,
|
||||
splunkbase_password: Union[str, None] = None,
|
||||
reuse_image:bool = True,
|
||||
interactive_failure:bool=False,
|
||||
interactive:bool=False
|
||||
|
||||
):
|
||||
#Used to determine whether or not we should wait for container threads to finish when summarizing
|
||||
self.all_tests_completed = False
|
||||
|
||||
self.synchronization_object = test_driver.TestDriver(
|
||||
test_list, num_containers, summarization_reproduce_failure_config)
|
||||
|
||||
self.mounts = self.create_mounts(mounts)
|
||||
self.apps = apps
|
||||
|
||||
|
||||
if container_password is None:
|
||||
self.container_password = self.get_random_password()
|
||||
else:
|
||||
self.container_password = container_password
|
||||
|
||||
print("\n\n***********************")
|
||||
print("Log into your [%d] Splunk Container(s) after they boot at http://127.0.0.1:[%d-%d]"%(num_containers, web_port_start, web_port_start + num_containers - 1))
|
||||
print("\tSplunk App Username: [%s]"%("admin"))
|
||||
print("\tSplunk App Password: ", end='')
|
||||
if show_container_password:
|
||||
print("[%s]"%(self.container_password))
|
||||
else:
|
||||
print(" --show_splunk_app_password set to False - password not printed")
|
||||
print("***********************\n\n")
|
||||
|
||||
|
||||
self.containers = self.create_containers(
|
||||
full_docker_hub_name,
|
||||
container_name_template,
|
||||
num_containers,
|
||||
web_port_start,
|
||||
management_port_start,
|
||||
splunkbase_username,
|
||||
splunkbase_password,
|
||||
files_to_copy_to_container,
|
||||
reuse_image,
|
||||
interactive_failure,
|
||||
interactive
|
||||
)
|
||||
self.summary_thread = threading.Thread(target=self.queue_status_thread,args=())
|
||||
|
||||
#Construct the baseline from the splunk version and the apps to be installed
|
||||
self.baseline = OrderedDict()
|
||||
#Get a datetime and add it as the first entry in the baseline
|
||||
self.start_time = datetime.datetime.now()
|
||||
self.baseline['SPLUNK_VERSION'] = full_docker_hub_name
|
||||
self.baseline["branch"] = branch
|
||||
self.baseline["commit_hash"] = commit_hash
|
||||
#Added here first to preserve ordering for OrderedDict
|
||||
self.baseline['TEST_START_TIME'] = "TO BE UPDATED"
|
||||
self.baseline['TEST_FINISH_TIME'] = "TO BE UPDATED"
|
||||
self.baseline['TEST_DURATION'] = "TO BE UPDATED"
|
||||
|
||||
for key in self.apps:
|
||||
self.baseline[key] = self.apps[key]
|
||||
|
||||
|
||||
|
||||
|
||||
def run_test(self)->bool:
|
||||
self.run_status_thread()
|
||||
self.run_containers()
|
||||
self.summary_thread.join()
|
||||
|
||||
|
||||
|
||||
|
||||
for container in self.containers:
|
||||
if self.all_tests_completed == True:
|
||||
container.thread.join()
|
||||
elif self.all_tests_completed == False:
|
||||
#For some reason, we stopped early. So don't wait on the child threads to finish. Don't join,
|
||||
#these threads may be stuck in their setup loops. Continue on.
|
||||
pass
|
||||
|
||||
print(container.get_container_summary())
|
||||
print("All containers completed testing!")
|
||||
|
||||
|
||||
stop_time = datetime.datetime.now()
|
||||
x = stop_time - self.start_time
|
||||
|
||||
self.baseline['TEST_START_TIME'] = str(self.start_time)
|
||||
self.baseline['TEST_FINISH_TIME'] = str(stop_time)
|
||||
|
||||
duration = stop_time - self.start_time
|
||||
self.baseline['TEST_DURATION'] = str(duration - datetime.timedelta(microseconds=duration.microseconds))
|
||||
|
||||
return self.synchronization_object.finish(self.baseline)
|
||||
|
||||
|
||||
|
||||
|
||||
def run_containers(self) -> None:
|
||||
for container_number, container in enumerate(self.containers):
|
||||
#give a little time between container startup if there is more than one container.
|
||||
#Never wait on the first container. This gets us to testing as fast as possible
|
||||
#for the most common case (one container) and gives us some extra time and
|
||||
#reduces load when we are launching more than one container
|
||||
if (container_number != 0):
|
||||
time.sleep(10)
|
||||
container.thread.start()
|
||||
|
||||
|
||||
def run_status_thread(self) -> None:
|
||||
self.summary_thread.start()
|
||||
|
||||
|
||||
|
||||
def create_containers(
|
||||
self,
|
||||
full_docker_hub_name: str,
|
||||
container_name_template: str,
|
||||
num_containers: int,
|
||||
web_port_start: int,
|
||||
management_port_start: int,
|
||||
splunkbase_username: Union[str, None] = None,
|
||||
splunkbase_password: Union[str, None] = None,
|
||||
files_to_copy_to_container: OrderedDict = OrderedDict(),
|
||||
reuse_image:bool = True,
|
||||
interactive_failure:bool = False,
|
||||
interactive:bool = False
|
||||
) -> list[splunk_container.SplunkContainer]:
|
||||
#First make sure that the image exists and has been downloaded.
|
||||
#Note that this is intentionally not part of the time to start
|
||||
#since it can take a long time on a slow connection!
|
||||
self.setup_image(reuse_image, full_docker_hub_name)
|
||||
|
||||
new_containers = []
|
||||
for index in range(num_containers):
|
||||
container_name = container_name_template % index
|
||||
web_port_tuple = (WEB_PORT_STRING, web_port_start + index)
|
||||
management_port_tuple = (
|
||||
MANAGEMENT_PORT_STRING,
|
||||
management_port_start + index,
|
||||
)
|
||||
|
||||
new_containers.append(
|
||||
splunk_container.SplunkContainer(
|
||||
self.synchronization_object,
|
||||
full_docker_hub_name,
|
||||
container_name,
|
||||
self.apps,
|
||||
web_port_tuple,
|
||||
management_port_tuple,
|
||||
self.container_password,
|
||||
files_to_copy_to_container,
|
||||
self.mounts,
|
||||
splunkbase_username,
|
||||
splunkbase_password,
|
||||
interactive_failure=interactive_failure,
|
||||
interactive=interactive
|
||||
)
|
||||
)
|
||||
|
||||
return new_containers
|
||||
|
||||
def create_mounts(
|
||||
self, mounts: list[dict[str, str]]
|
||||
) -> list[docker.types.Mount]:
|
||||
new_mounts = []
|
||||
for mount in mounts:
|
||||
new_mounts.append(self.create_mount(mount))
|
||||
return new_mounts
|
||||
|
||||
def create_mount(self, mount: dict[str, str]) -> docker.types.Mount:
|
||||
return docker.types.Mount(
|
||||
source=os.path.abspath(mount["local_path"]),
|
||||
target=mount["container_path"],
|
||||
type=mount["type"],
|
||||
read_only=mount["read_only"],
|
||||
)
|
||||
|
||||
# taken from attack_range
|
||||
def get_random_password(
|
||||
self, password_min_length: int = 16, password_max_length: int = 26
|
||||
) -> str:
|
||||
random_source = string.ascii_letters + string.digits
|
||||
password = random.choice(string.ascii_lowercase)
|
||||
password += random.choice(string.ascii_uppercase)
|
||||
password += random.choice(string.digits)
|
||||
|
||||
for i in range(random.randrange(password_min_length, password_max_length)):
|
||||
password += random.choice(random_source)
|
||||
|
||||
password_list = list(password)
|
||||
random.SystemRandom().shuffle(password_list)
|
||||
password = "".join(password_list)
|
||||
return password
|
||||
|
||||
def queue_status_thread(self, status_interval:int=60, num_steps:int=10)->None:
|
||||
|
||||
while True:
|
||||
#This for loop lets us run the summarize print less often, but check for failure more often
|
||||
for chunk in range(0, status_interval, int(status_interval/num_steps)):
|
||||
if self.synchronization_object.checkContainerFailure():
|
||||
print("One of the containers has shut down prematurely or the test was halted. Ensuring all containers are stopped.")
|
||||
for container in self.containers:
|
||||
container.stopContainer()
|
||||
print("All containers stopped")
|
||||
self.all_tests_completed = False
|
||||
return None
|
||||
time.sleep(status_interval/num_steps)
|
||||
|
||||
at_least_one_container_has_started_running_tests = False
|
||||
for container in self.containers:
|
||||
if container.test_start_time != -1:
|
||||
at_least_one_container_has_started_running_tests = True
|
||||
break
|
||||
if self.synchronization_object.summarize(testing_currently_active = at_least_one_container_has_started_running_tests) == False:
|
||||
#There are no more tests to run, so we can return from this thread
|
||||
self.all_tests_completed = True
|
||||
return None
|
||||
|
||||
|
||||
def setup_image(self, reuse_images: bool, container_name: str) -> None:
|
||||
client = docker.client.from_env()
|
||||
if not reuse_images:
|
||||
#Check to see if the image exists. If it does, then remove it. If it does not, then do nothing
|
||||
docker_image = None
|
||||
try:
|
||||
docker_image = client.images.get(container_name)
|
||||
except Exception as e:
|
||||
#We don't need to do anything, the image did not exist on our system
|
||||
#print("Image named [%s] did not exist, so we don't need to try and remove it."%(container_name))
|
||||
pass
|
||||
if docker_image != None:
|
||||
#We found the image. Let's try to delete it
|
||||
print("Found docker image named [%s] and you have requested that we forcefully remove it"%(container_name))
|
||||
try:
|
||||
client.images.remove(image=container_name, force=True, noprune=False)
|
||||
print("Docker image named [%s] forcefully removed"%(container_name))
|
||||
except Exception as e:
|
||||
print("Error forcefully removing [%s]"%(container_name))
|
||||
raise(e)
|
||||
|
||||
#See if the image exists. If it doesn't, then pull it from Docker Hub
|
||||
try:
|
||||
docker_image = client.images.get(container_name)
|
||||
print("Docker image [%s] found, no need to download it."%(container_name))
|
||||
except Exception as e:
|
||||
#Image did not exist on the system
|
||||
docker_image = None
|
||||
|
||||
if docker_image is None:
|
||||
#We did not find the image, so pull it
|
||||
try:
|
||||
print("Downloading image [%s]. Please note "
|
||||
"that this could take a long time depending on your "
|
||||
"connection. It's around 2GB."%(container_name))
|
||||
pull_start_time = timeit.default_timer()
|
||||
client.images.pull(container_name,platform="linux/amd64")
|
||||
pull_finish_time = timeit.default_timer()
|
||||
print("Successfully pulled the docker image [%s] in %ss"%
|
||||
(container_name,
|
||||
datetime.timedelta(seconds=pull_finish_time - pull_start_time, microseconds=0) ))
|
||||
|
||||
except Exception as e:
|
||||
print("There was an error trying to pull the image [%s]: [%s]"%(container_name,str(e)))
|
||||
raise(e)
|
||||
@@ -1,572 +0,0 @@
|
||||
import csv
|
||||
import glob
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Union
|
||||
from docker import types
|
||||
import datetime
|
||||
import git
|
||||
import yaml
|
||||
from git.objects import base
|
||||
from modules import testing_service
|
||||
import pathlib
|
||||
|
||||
# Logger
|
||||
logging.basicConfig(level=os.environ.get("LOGLEVEL", "INFO"))
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SECURITY_CONTENT_URL = "https://github.com/splunk/security_content"
|
||||
|
||||
|
||||
DETECTION_ROOT_PATH = "security_content/detections"
|
||||
TEST_ROOT_PATH = "security_content/tests"
|
||||
DETECTION_FILE_EXTENSION = ".yml"
|
||||
TEST_FILE_EXTENSION = ".test.yml"
|
||||
SSA_PREFIX = "ssa___"
|
||||
|
||||
|
||||
class GithubService:
|
||||
def __init__(
|
||||
self,
|
||||
security_content_branch: str,
|
||||
commit_hash: Union[str, None],
|
||||
PR_number: Union[int, None] = None,
|
||||
persist_security_content: bool = False,
|
||||
):
|
||||
self.security_content_branch = security_content_branch
|
||||
if persist_security_content:
|
||||
print("Getting handle on existing security_content repo!")
|
||||
self.security_content_repo_obj = git.Repo("security_content")
|
||||
else:
|
||||
print("Checking out security_content repo!")
|
||||
self.security_content_repo_obj = self.clone_project(
|
||||
SECURITY_CONTENT_URL, f"security_content", f"develop"
|
||||
)
|
||||
|
||||
# Ensure that the branch name is valid
|
||||
# Get all the branch names, prefixed with "origin/"
|
||||
branch_names = [
|
||||
branch.name for branch in self.security_content_repo_obj.remote().refs
|
||||
]
|
||||
|
||||
if "origin/%s" % (security_content_branch) not in branch_names:
|
||||
raise (
|
||||
Exception(
|
||||
"Branch name [%s] not found in valid branches. Try running \n"
|
||||
"'git branch -a' to examine [%d] branches"
|
||||
% (security_content_branch, len(branch_names))
|
||||
)
|
||||
)
|
||||
|
||||
if commit_hash is not None and PR_number is not None:
|
||||
print(
|
||||
f"\n************\nWARNING - both the PR_number {PR_number} and the commit_hash {commit_hash} were provided. "
|
||||
f"You should only pass neither or one of these. We will ASSUME you want to use the PR_number, not the commit_hash. "
|
||||
f"Removing the commit_hash...\n************\n"
|
||||
)
|
||||
commit_hash = None
|
||||
|
||||
if PR_number:
|
||||
ret = subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"-C",
|
||||
"security_content/",
|
||||
"fetch",
|
||||
"origin",
|
||||
"refs/pull/%d/head:%s" % (PR_number, security_content_branch),
|
||||
],
|
||||
capture_output=True,
|
||||
)
|
||||
# ret = subprocess.call(["git", "-C", "security_content/", "fetch", "origin",
|
||||
# "refs/pull/%d/head:%s" % (PR_number, security_content_branch)])
|
||||
|
||||
if ret.returncode != 0:
|
||||
raise (
|
||||
Exception(
|
||||
"Error checking out repository: [%s]"
|
||||
% (
|
||||
ret.stdout.decode("utf-8")
|
||||
+ "\n"
|
||||
+ ret.stderr.decode("utf-8")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# No checking to see if the hash is to a commit inside of the branch - the user
|
||||
# has to do that by hand.
|
||||
|
||||
# -- ensures that we check out the appropriate branch or commit hash.
|
||||
# Without --, there can be ambiguity if a file/folder exists with the
|
||||
# same name as the branch, causing the checkout to fail with error
|
||||
if commit_hash is not None:
|
||||
print("Checking out commit hash: [%s]" % (commit_hash))
|
||||
self.security_content_repo_obj.git.checkout(commit_hash, "--")
|
||||
else:
|
||||
# Even if we have fetched a PR, we still MUST check out the branch to
|
||||
# be able to do anything with it. Otherwise we won't have the files
|
||||
print("Checking out branch: [%s]..." % (security_content_branch), end="")
|
||||
sys.stdout.flush()
|
||||
self.security_content_repo_obj.git.checkout(security_content_branch, "--")
|
||||
commit_hash = self.security_content_repo_obj.head.object.hexsha
|
||||
print("commit_hash %s" % (commit_hash))
|
||||
|
||||
self.commit_hash = commit_hash
|
||||
|
||||
def update_and_commit_passed_tests(self, results: list[dict]) -> bool:
|
||||
changed_file_paths = []
|
||||
for result in results:
|
||||
detection_obj_path = os.path.join(
|
||||
"security_content", "detections", result["detection_file"]
|
||||
)
|
||||
|
||||
test_obj_path = detection_obj_path.replace("detections", "tests", 1)
|
||||
test_obj_path = test_obj_path.replace(".yml", ".test.yml")
|
||||
|
||||
detection_obj = testing_service.load_file(detection_obj_path)
|
||||
test_obj = testing_service.load_file(test_obj_path)
|
||||
detection_obj["tags"]["automated_detection_testing"] = "passed"
|
||||
# detection_obj['tags']['automated_detection_testing_date'] = datetime.datetime.today().strftime('%Y-%m-%d-%H:%M:%S')
|
||||
|
||||
for o in test_obj["tests"]:
|
||||
if "attack_data" in o:
|
||||
datasets = []
|
||||
for dataset in o["attack_data"]:
|
||||
datasets.append(dataset["data"])
|
||||
detection_obj["tags"]["dataset"] = datasets
|
||||
with open(detection_obj_path, "w") as f:
|
||||
yaml.dump(detection_obj, f, sort_keys=False, allow_unicode=True)
|
||||
|
||||
changed_file_paths.append(detection_obj_path)
|
||||
|
||||
relpaths = [
|
||||
pathlib.Path(*pathlib.Path(p).parts[1:]).as_posix()
|
||||
for p in changed_file_paths
|
||||
]
|
||||
newpath = relpaths[0] + ".wow"
|
||||
relpaths.append(newpath)
|
||||
with open("security_content/" + newpath, "w") as d:
|
||||
d.write("fake file")
|
||||
print("status results:")
|
||||
print(
|
||||
self.security_content_repo_obj.index.diff(
|
||||
self.security_content_repo_obj.head.commit
|
||||
)
|
||||
)
|
||||
|
||||
if len(relpaths) > 0:
|
||||
print("there is at least one changed file")
|
||||
print(relpaths)
|
||||
self.security_content_repo_obj.index.add(relpaths)
|
||||
print("status results after add:")
|
||||
print(
|
||||
self.security_content_repo_obj.index.diff(
|
||||
self.security_content_repo_obj.head.commit
|
||||
)
|
||||
)
|
||||
|
||||
commit_message = (
|
||||
"The following detections passed detection testing. Their YAMLs have been updated and their datasets linked:\n - %s"
|
||||
% ("\n - ".join(relpaths))
|
||||
)
|
||||
self.security_content_repo_obj.index.commit(commit_message)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def clone_project(self, url, project, branch):
|
||||
LOGGER.info(f"Clone Security Content Project")
|
||||
repo_obj = git.Repo.clone_from(url, project, branch=branch)
|
||||
return repo_obj
|
||||
|
||||
def prune_detections(
|
||||
self,
|
||||
detection_files: list[str],
|
||||
types_to_test: list[str],
|
||||
exclude_ssa: bool = True,
|
||||
) -> list[str]:
|
||||
pruned_tests = []
|
||||
|
||||
for detection in detection_files:
|
||||
if os.path.basename(detection).startswith(SSA_PREFIX) and exclude_ssa:
|
||||
continue
|
||||
with open(detection, "r") as d:
|
||||
description: dict = yaml.safe_load(d)
|
||||
|
||||
detection_filepath_without_security_content = str(
|
||||
pathlib.Path(*pathlib.Path(detection).parts[1:])
|
||||
)
|
||||
# If no types are provided, then we will get everything
|
||||
if (
|
||||
description.get("type", None) in types_to_test
|
||||
and "production" in description.get("status", "")
|
||||
and not (
|
||||
description.get("tags", False)
|
||||
and description["tags"].get("manual_test", False)
|
||||
)
|
||||
):
|
||||
if len(description.get("tests", [])) == 0:
|
||||
print(
|
||||
Exception(
|
||||
f"Detection {detection_filepath_without_security_content} has no tests/test section defined. Detection must include at least one test."
|
||||
)
|
||||
)
|
||||
continue
|
||||
raise (
|
||||
Exception(
|
||||
f"Detection {detection_filepath_without_security_content} has no tests/test section defined. Detection must include at least one test."
|
||||
)
|
||||
)
|
||||
|
||||
pruned_tests.append(detection_filepath_without_security_content)
|
||||
|
||||
else:
|
||||
if description.get("tags", False):
|
||||
manual_test = description["tags"].get("manual_test", False)
|
||||
else:
|
||||
manual_test = False
|
||||
|
||||
print(
|
||||
f"Ignore {detection}:\n - [status:'{description.get('status', None)}'] [type:'{description.get('type', None)}'] [manual_test:'{manual_test}']"
|
||||
)
|
||||
# Don't do anything with these files
|
||||
pass
|
||||
|
||||
# if not self.ensure_paired_detection_and_test_files([], [os.path.join("security_content", p) for p in pruned_tests], exclude_ssa):
|
||||
# raise(Exception("Missing one or more test/detection files. Please see the output above."))
|
||||
|
||||
return pruned_tests
|
||||
|
||||
def ensure_paired_detection_and_test_files(
|
||||
self,
|
||||
detection_files: list[str],
|
||||
test_files: list[str],
|
||||
exclude_ssa: bool = True,
|
||||
) -> bool:
|
||||
"""
|
||||
The security_content repo contains two folders: detections and test.
|
||||
For EVERY detection in the detections folder, there must be a test.
|
||||
for EVERY test in the tests folder, there MUST be a detection.
|
||||
|
||||
If this requirement is not met, then throw an error
|
||||
"""
|
||||
|
||||
MISSING_TEMPLATE = (
|
||||
"Missing {type} file:" "\n\tEXISTS - {exists}" "\n\tMISSING - {missing}"
|
||||
)
|
||||
|
||||
no_missing_files = True
|
||||
# Check that all detection files have a test file
|
||||
for detection_file in detection_files:
|
||||
test_file = self.convert_detection_filename_into_test_filename(
|
||||
detection_file
|
||||
)
|
||||
if not os.path.exists(test_file):
|
||||
if (
|
||||
os.path.basename(detection_file).startswith(SSA_PREFIX)
|
||||
and exclude_ssa is True
|
||||
):
|
||||
print(
|
||||
MISSING_TEMPLATE.format(
|
||||
type="test", exists=detection_file, missing=test_file
|
||||
)
|
||||
)
|
||||
print(
|
||||
"\tSince exclude_ssa is TRUE, this is not an error, just a warning"
|
||||
)
|
||||
else:
|
||||
print(
|
||||
MISSING_TEMPLATE.format(
|
||||
type="test", exists=detection_file, missing=test_file
|
||||
)
|
||||
)
|
||||
no_missing_files = False
|
||||
|
||||
# Check that all test files have a detection file
|
||||
for test_file in test_files:
|
||||
detection_file = self.convert_test_filename_into_detection_filename(
|
||||
test_file
|
||||
)
|
||||
if not os.path.exists(detection_file):
|
||||
if (
|
||||
os.path.basename(test_file).startswith(SSA_PREFIX)
|
||||
and exclude_ssa is True
|
||||
):
|
||||
print(
|
||||
MISSING_TEMPLATE.format(
|
||||
type="detection", exists=test_file, missing=detection_file
|
||||
)
|
||||
)
|
||||
print(
|
||||
"\tSince exclude_ssa is TRUE, this is not an error, just a warning"
|
||||
)
|
||||
else:
|
||||
print(
|
||||
MISSING_TEMPLATE.format(
|
||||
type="detection", exists=test_file, missing=detection_file
|
||||
)
|
||||
)
|
||||
no_missing_files = False
|
||||
|
||||
return no_missing_files
|
||||
|
||||
def convert_detection_filename_into_test_filename(
|
||||
self, detection_filename: str
|
||||
) -> str:
|
||||
head, tail = os.path.split(detection_filename)
|
||||
|
||||
assert head.startswith(
|
||||
DETECTION_ROOT_PATH
|
||||
), f"Error - Expected detection filename to start with [{DETECTION_ROOT_PATH}] but instead got {detection_filename}"
|
||||
|
||||
updated_head = head.replace(DETECTION_ROOT_PATH, TEST_ROOT_PATH, 1)
|
||||
|
||||
assert tail.endswith(
|
||||
DETECTION_FILE_EXTENSION
|
||||
), f"Error - Expected detection filename to end with [{DETECTION_FILE_EXTENSION}] but instead got [{detection_filename}]"
|
||||
updated_tail = TEST_FILE_EXTENSION.join(tail.rsplit(DETECTION_FILE_EXTENSION))
|
||||
|
||||
return os.path.join(updated_head, updated_tail)
|
||||
|
||||
def convert_test_filename_into_detection_filename(self, test_filename: str) -> str:
|
||||
head, tail = os.path.split(test_filename)
|
||||
|
||||
assert head.startswith(
|
||||
TEST_ROOT_PATH
|
||||
), f"Error - Expected test filename to start with [{TEST_ROOT_PATH}] but instead got {test_filename}"
|
||||
|
||||
updated_head = head.replace(TEST_ROOT_PATH, DETECTION_ROOT_PATH, 1)
|
||||
|
||||
assert tail.endswith(
|
||||
TEST_FILE_EXTENSION
|
||||
), f"Error - Expected test filename to end with [{TEST_FILE_EXTENSION}] but instead got [{test_filename}]"
|
||||
updated_tail = DETECTION_FILE_EXTENSION.join(tail.rsplit(TEST_FILE_EXTENSION))
|
||||
|
||||
return os.path.join(updated_head, updated_tail)
|
||||
|
||||
def get_detection_files(
|
||||
self,
|
||||
mode: str,
|
||||
folders: list[str],
|
||||
types: list[str],
|
||||
detections_list: Union[list[str], None],
|
||||
) -> list[str]:
|
||||
if mode == "changes":
|
||||
tests = self.get_changed_detection_files(folders, types)
|
||||
elif mode == "selected":
|
||||
if detections_list is None:
|
||||
# It's actually valid to supply an EMPTY list of files and the test should pass.
|
||||
# This can occur when we try to test, for example, 1 detection but start 2 containers.
|
||||
# We still want this to pass testing, so we shouldn't fail there!
|
||||
print(
|
||||
"Trying to test a list of files, but None were provided",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
elif detections_list is not None:
|
||||
tests = self.get_selected_test_files(detections_list, types)
|
||||
else:
|
||||
# impossible to get here
|
||||
print(
|
||||
"Impossible to get here. Just kept to make the if/elif more self describing",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
elif mode == "all":
|
||||
tests = self.get_all_tests_and_detections(folders, types)
|
||||
elif mode == "smoketest":
|
||||
tests = self.get_everything_including_experimental_and_deprecated(types)
|
||||
else:
|
||||
print(
|
||||
"Error, unsupported mode [%s]. Mode must be one of %s", file=sys.stderr
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
return tests
|
||||
|
||||
def get_selected_test_files(
|
||||
self,
|
||||
detection_file_list: list[str],
|
||||
types_to_test: list[str] = ["Anomaly", "Hunting", "TTP"],
|
||||
) -> list[str]:
|
||||
return self.prune_detections(detection_file_list, types_to_test)
|
||||
|
||||
def get_everything_including_experimental_and_deprecated(
|
||||
self,
|
||||
types_to_test: list[str] = ["Anomaly", "Hunting", "TTP"],
|
||||
exclude_ssa: bool = True,
|
||||
) -> list[str]:
|
||||
all_detections = glob.glob(
|
||||
os.path.join(DETECTION_ROOT_PATH, "**", f"*{DETECTION_FILE_EXTENSION}"),
|
||||
recursive=True,
|
||||
)
|
||||
only_selected_detection_types: list[str] = []
|
||||
for detection in all_detections:
|
||||
if os.path.basename(detection).startswith(SSA_PREFIX) and exclude_ssa:
|
||||
continue
|
||||
with open(detection, "r") as d:
|
||||
description: dict = yaml.safe_load(d)
|
||||
|
||||
detection_filepath_without_security_content = str(
|
||||
pathlib.Path(*pathlib.Path(detection).parts[1:])
|
||||
)
|
||||
|
||||
if description.get("type", None) not in types_to_test:
|
||||
continue
|
||||
only_selected_detection_types.append(
|
||||
detection_filepath_without_security_content
|
||||
)
|
||||
|
||||
print(
|
||||
f"Number of non-ssa tests including experimental and deprecated: {len(only_selected_detection_types)}"
|
||||
)
|
||||
|
||||
return only_selected_detection_types
|
||||
|
||||
def get_all_tests_and_detections(
|
||||
self,
|
||||
folders: list[str] = ["endpoint", "cloud", "network"],
|
||||
types_to_test: list[str] = ["Anomaly", "Hunting", "TTP"],
|
||||
) -> list[str]:
|
||||
detections = []
|
||||
for folder in folders:
|
||||
detections.extend(
|
||||
self.get_all_files_in_folder(
|
||||
os.path.join(DETECTION_ROOT_PATH, folder), "*"
|
||||
)
|
||||
)
|
||||
|
||||
# Prune this down to only the subset of detections we can test
|
||||
return self.prune_detections(detections, types_to_test)
|
||||
|
||||
def get_all_files_in_folder(self, foldername: str, extension: str) -> list[str]:
|
||||
filenames = glob.glob(os.path.join(foldername, extension))
|
||||
return filenames
|
||||
|
||||
def get_changed_detection_files(
|
||||
self,
|
||||
folders=["endpoint", "cloud", "network"],
|
||||
types_to_test=["Anomaly", "Hunting", "TTP"],
|
||||
) -> list[str]:
|
||||
branch1 = self.security_content_branch
|
||||
branch2 = "develop"
|
||||
g = git.Git("security_content")
|
||||
all_changed_test_files = []
|
||||
|
||||
all_changed_detection_files = []
|
||||
if branch1 != "develop":
|
||||
if self.commit_hash is None:
|
||||
differ = g.diff("--name-status", branch2 + "..." + branch1)
|
||||
else:
|
||||
differ = g.diff("--name-status", branch2 + "..." + self.commit_hash)
|
||||
|
||||
changed_files = differ.splitlines()
|
||||
|
||||
for file_path in changed_files:
|
||||
# added or changed test files
|
||||
if file_path.startswith("A") or file_path.startswith("M"):
|
||||
# changed detections
|
||||
if "detections" in file_path and os.path.basename(
|
||||
file_path
|
||||
).endswith(".yml"):
|
||||
all_changed_detection_files.append(file_path)
|
||||
else:
|
||||
print(
|
||||
"Looking for changed detections by diffing [%s] against [%s]. They are the same branch, so none were returned."
|
||||
% (branch1, branch2),
|
||||
file=sys.stderr,
|
||||
)
|
||||
return []
|
||||
|
||||
all_changed_detection_files = [
|
||||
os.path.join("security_content", name.split("\t")[1])
|
||||
for name in all_changed_detection_files
|
||||
if len(name.split("\t")) == 2
|
||||
]
|
||||
|
||||
# Trim out any of the tests/detection that are not in the selected folders, but at least print a notice
|
||||
# to the user.
|
||||
|
||||
changed_detection_files = [
|
||||
x
|
||||
for x in all_changed_detection_files
|
||||
if (len(pathlib.Path(x).parts) > 3 and pathlib.Path(x).parts[2] in folders)
|
||||
]
|
||||
|
||||
for missing in set(changed_detection_files).symmetric_difference(
|
||||
all_changed_detection_files
|
||||
):
|
||||
print(
|
||||
"Ignoring modified detecton [%s] not in set of selected folders: %s"
|
||||
% (missing, folders)
|
||||
)
|
||||
|
||||
# Convert the test files to the detection file equivalent.
|
||||
# Note that some of these tests may be baselines and their associated
|
||||
# detection could be in experimental or not in the experimental folder
|
||||
converted_test_files = []
|
||||
# for test_filepath in changed_test_files:
|
||||
# detection_filename = str(pathlib.Path(
|
||||
# *pathlib.Path(test_filepath).parts[-2:])).replace("tests", "detections", 1)
|
||||
# converted_test_files.append(detection_filename)
|
||||
|
||||
return self.prune_detections(changed_detection_files, types_to_test)
|
||||
|
||||
# detections_to_test,_,_ = self.filter_test_types(changed_detection_files)
|
||||
# for f in detections_to_test:
|
||||
# file_path_base = os.path.splitext(f)[0].replace('detections', 'tests') + '.test'
|
||||
# file_path_new = file_path_base + '.yml'
|
||||
# if file_path_new not in changed_test_files:
|
||||
# changed_test_files.append(file_path_new)
|
||||
|
||||
# print("Total things to test (test files and detection files changed): [%d]"%(len(changed_test_files)))
|
||||
# for l in changed_test_files:
|
||||
# print(l)
|
||||
# print(len(changed_test_files))
|
||||
# import time
|
||||
# time.sleep(5)
|
||||
|
||||
def filter_test_types(self, test_files, test_types=["Anomaly", "Hunting", "TTP"]):
|
||||
files_to_test = []
|
||||
files_not_to_test = []
|
||||
error_files = []
|
||||
for filename in test_files:
|
||||
try:
|
||||
with open(os.path.join("security_content", filename), "r") as fileData:
|
||||
yaml_dict = list(yaml.safe_load_all(fileData))[0]
|
||||
if "type" not in yaml_dict.keys():
|
||||
print(
|
||||
"Failed to find 'type' in the yaml for: [%s]" % (filename)
|
||||
)
|
||||
error_files.append(filename)
|
||||
if yaml_dict["type"] in test_types:
|
||||
files_to_test.append(filename)
|
||||
else:
|
||||
files_not_to_test.append(filename)
|
||||
except Exception as e:
|
||||
print("Error on trying to scan [%s]: [%s]" % (filename, str(e)))
|
||||
error_files.append(filename)
|
||||
print(
|
||||
"***Detection Information***\n"
|
||||
"\tTotal Files : %d"
|
||||
"\tFiles to test : %d"
|
||||
"\tFiles not to test : %d"
|
||||
"\tError files : %d"
|
||||
% (
|
||||
len(test_files),
|
||||
len(files_to_test),
|
||||
len(files_not_to_test),
|
||||
len(error_files),
|
||||
)
|
||||
)
|
||||
import time
|
||||
|
||||
time.sleep(5)
|
||||
return files_to_test, files_not_to_test, error_files
|
||||
@@ -1,173 +0,0 @@
|
||||
"""
|
||||
Courtesy https://github.com/ccpgames/jsonschema-errorprinter with minor
|
||||
updates to support Python 3 (changed cStringIO to io), to print out
|
||||
multiple errors, the ability to place default values, and a few
|
||||
other small changes.
|
||||
|
||||
Licensed under the MIT License, reproduced below:
|
||||
Copyright © 2015 CCP hf.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
THERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
||||
OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
"""
|
||||
Json Schema Validation Error Pretty-printer.
|
||||
---------------------------------------------------
|
||||
|
||||
Makes a user friendly error message from a ValidationError.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import io
|
||||
import json
|
||||
import jsonschema
|
||||
import jsonschema.validators
|
||||
|
||||
# The 'default' field is really just for documentation in the
|
||||
# json schema. We would like to use it to actually fill in
|
||||
# values when they aren't supplied. This code is provided
|
||||
# by the jsonschema project itself because this behavior
|
||||
# is not part of the default jsonschema definition
|
||||
# https://python-jsonschema.readthedocs.io/en/latest/faq/
|
||||
def extend_with_default(validator_class):
|
||||
validate_properties = validator_class.VALIDATORS["properties"]
|
||||
|
||||
def set_defaults(validator, properties, instance, schema):
|
||||
for property, subschema in properties.items():
|
||||
if "default" in subschema:
|
||||
instance.setdefault(property, subschema["default"])
|
||||
|
||||
for error in validate_properties(
|
||||
validator, properties, instance, schema,
|
||||
):
|
||||
yield error
|
||||
|
||||
return jsonschema.validators.extend(
|
||||
validator_class, {"properties": set_defaults},
|
||||
)
|
||||
|
||||
|
||||
def check_json(json_object, schema, context=None) -> tuple[list[str], dict]:
|
||||
try:
|
||||
DefaultValidatingDraft7Validator = extend_with_default(
|
||||
jsonschema.Draft7Validator)
|
||||
|
||||
|
||||
validator = DefaultValidatingDraft7Validator(schema, jsonschema.FormatChecker())
|
||||
#validator = jsonschema.Draft7Validator(schema, jsonschema.FormatChecker())
|
||||
errors_formatted = []
|
||||
|
||||
for error in sorted(validator.iter_errors(json_object), key=str):
|
||||
|
||||
#validate(json_object, schema, format_checker=FormatChecker())
|
||||
# except jsonschema.ValidationError as e:
|
||||
report = generate_validation_error_report(error, json_object)
|
||||
|
||||
#note = "\n*** Note - If there is more than one error, only the first error is shown ***\n\n"
|
||||
if context:
|
||||
errors_formatted.append(
|
||||
"Schema check failed for '{}'\n{}".format(context, report))
|
||||
# return note + "Schema check failed for '{}'\n{}".format(context, report)
|
||||
else:
|
||||
errors_formatted.append(
|
||||
"Schema check failed.\n{}".format(report))
|
||||
# return note + "Schema check failed.\n{}".format(report)
|
||||
if len(errors_formatted) == 0:
|
||||
#DefaultValidatingDraft7Validator = extend_with_default(
|
||||
# jsonschema.Draft7Validator)
|
||||
#DefaultValidatingDraft7Validator(schema).validate(json_object)
|
||||
return (errors_formatted, json_object)
|
||||
else:
|
||||
return (errors_formatted, {})
|
||||
except Exception as e:
|
||||
# Some error occurred, probably related to the schema itself
|
||||
raise(Exception("Error validating the JSON Schema: %s" % (str(e))))
|
||||
|
||||
|
||||
def generate_validation_error_report(
|
||||
e,
|
||||
json_object,
|
||||
lines_before=7,
|
||||
lines_after=7
|
||||
):
|
||||
"""
|
||||
Generate a detailed report of a schema validation error.
|
||||
|
||||
'e' is a jsonschema.ValidationError exception that errored on
|
||||
'json_object'.
|
||||
|
||||
Steps to discover the location of the validation error:
|
||||
1. Traverse the json object using the 'path' in the validation exception
|
||||
and replace the offending value with a special marker.
|
||||
2. Pretty-print the json object indendented json text.
|
||||
3. Search for the special marker in the json text to find the actual
|
||||
line number of the error.
|
||||
4. Make a report by showing the error line with a context of
|
||||
'lines_before' and 'lines_after' number of lines on each side.
|
||||
"""
|
||||
|
||||
if json_object is None:
|
||||
return "'json_object' cannot be None."
|
||||
if not e.path:
|
||||
return str(e)
|
||||
marker = "3fb539deef7c4e2991f265c0a982f5ea"
|
||||
|
||||
# Find the object that is erroring, and replace it with the marker.
|
||||
ob_tmp = json_object
|
||||
for entry in list(e.path)[:-1]:
|
||||
ob_tmp = ob_tmp[entry]
|
||||
|
||||
orig, ob_tmp[e.path[-1]] = ob_tmp[e.path[-1]], marker
|
||||
|
||||
# Pretty print the object and search for the marker.
|
||||
json_error = json.dumps(json_object, indent=4)
|
||||
string_io_instance = io.StringIO(json_error)
|
||||
errline = None
|
||||
|
||||
for lineno, text in enumerate(string_io_instance):
|
||||
if marker in text:
|
||||
errline = lineno
|
||||
break
|
||||
|
||||
if errline is not None:
|
||||
# Re-create report.
|
||||
report = []
|
||||
ob_tmp[e.path[-1]] = orig
|
||||
json_error = json.dumps(json_object, indent=4)
|
||||
string_io_instance = io.StringIO(json_error)
|
||||
|
||||
for lineno, text in enumerate(string_io_instance):
|
||||
if lineno == errline:
|
||||
line_text = "{:4}: >>>".format(lineno+1)
|
||||
else:
|
||||
line_text = "{:4}: ".format(lineno+1)
|
||||
report.append(line_text + text.rstrip("\n"))
|
||||
|
||||
report = report[max(0, errline-lines_before):errline+1+lines_after]
|
||||
|
||||
s = "Error in line {}:\n".format(errline+1)
|
||||
s += "\n".join(report)
|
||||
s += '\n\tREASON:' + str(e).split('\n')[0]
|
||||
#s += "\n\n" + str(e).replace("u'", "'")
|
||||
else:
|
||||
s = str(e)
|
||||
return s
|
||||
@@ -1,234 +0,0 @@
|
||||
import argparse
|
||||
import json
|
||||
from typing import OrderedDict, Union
|
||||
from modules import validate_args
|
||||
import sys
|
||||
|
||||
DEFAULT_CONFIG_FILE = "test_config.json"
|
||||
|
||||
|
||||
def configure_action(args) -> tuple[str, dict]:
|
||||
settings = OrderedDict()
|
||||
if args.input_config_file is None:
|
||||
settings, schema = validate_args.validate({})
|
||||
else:
|
||||
settings, schema = validate_args.validate_file(args.input_config_file)
|
||||
|
||||
if settings is None:
|
||||
print("Failure while processing settings\n\tQuitting...", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
new_config = {}
|
||||
for arg in settings:
|
||||
default = settings[arg]
|
||||
default_string = str(default).replace("'", '"')
|
||||
|
||||
if 'enum' in schema['properties'][arg]:
|
||||
choice = input("%s [default: %s | choices: {%s}]: " % (
|
||||
arg, default_string, ','.join(schema['properties'][arg]['enum'])))
|
||||
else:
|
||||
choice = input("%s [default: %s]: " % (arg, default_string))
|
||||
choice = choice.strip()
|
||||
if len(choice) == 0:
|
||||
print("\tNothing entered, using default:")
|
||||
new_config[arg] = default
|
||||
formatted_print = default
|
||||
else:
|
||||
if choice.lower() in ["true", "false"] and schema['properties'][arg]['type'] == "boolean":
|
||||
new_config[arg] = json.loads(choice.lower())
|
||||
formatted_print = choice.lower()
|
||||
else:
|
||||
|
||||
if choice in ['true', 'false'] or (choice.isdigit() and schema['properties'][arg]['type'] != "integer"):
|
||||
choice = '"' + choice + '"'
|
||||
# replace all single quotes with doubles quotes to make valid json
|
||||
elif "'" in choice:
|
||||
print('''Found %d single quotes (') in input... we will convert these to double quotes (") to ensure valida json.''' % (
|
||||
choice.count("'")))
|
||||
choice = choice.replace("'", '"')
|
||||
elif '"' in choice:
|
||||
# Do nothing
|
||||
pass
|
||||
elif choice.isdigit():
|
||||
pass
|
||||
else:
|
||||
choice = '"' + choice + '"'
|
||||
|
||||
new_config[arg] = json.loads(choice)
|
||||
formatted_print = choice
|
||||
# We print out choice instead of new_config[arg] because the json.loads() messes up the quotation marks again
|
||||
print("\t{0}\n".format(formatted_print))
|
||||
|
||||
# Now parse the new config and make sure it's good
|
||||
validated_new_settings, schema = validate_args.validate_and_write(
|
||||
new_config, args.output_config_file, skip_password_accessibility_check=False)
|
||||
if validated_new_settings == None:
|
||||
print("Could not update settings.\n\tQuitting...", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
return ("configure", validated_new_settings)
|
||||
|
||||
|
||||
def update_config_with_cli_arguments(args_dict: dict) -> tuple[str, dict]:
|
||||
# First load the config file
|
||||
|
||||
settings, _ = validate_args.validate_file(args_dict['config_file'])
|
||||
if settings is None:
|
||||
print("Failure while processing settings in [%s].\n\tQuitting..." % (
|
||||
args_dict['config_file'].name), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Then update it with the values that were passed as command line arguments
|
||||
for key, value in args_dict.items():
|
||||
if key in settings:
|
||||
settings[key] = value
|
||||
|
||||
# Validate again to make sure we didn't break anything
|
||||
settings, _ = validate_args.validate(settings,skip_password_accessibility_check=False)
|
||||
if settings is None:
|
||||
print("Failure while processing updated settings from command line.\n\tQuitting...", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
return ("run", settings)
|
||||
|
||||
|
||||
def run_action(args) -> tuple[str, dict]:
|
||||
|
||||
config = update_config_with_cli_arguments(args.__dict__)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def parse(args) -> tuple[str, dict]:
|
||||
'''
|
||||
try:
|
||||
with open(DEFAULT_CONFIG_FILE, 'r') as settings_file:
|
||||
default_settings = json.load(settings_file)
|
||||
except Exception as e:
|
||||
print("Error loading settings file %s: %s"%(DEFAULT_CONFIG_FILE, str(e)), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
'''
|
||||
|
||||
import os
|
||||
# if there is no default config file, then generate one
|
||||
if not os.path.exists(DEFAULT_CONFIG_FILE):
|
||||
print("No default configuration file [%s] found. Creating one..." % (
|
||||
DEFAULT_CONFIG_FILE))
|
||||
with open(DEFAULT_CONFIG_FILE, 'w') as cfg:
|
||||
validate_args.validate_and_write({}, cfg, skip_password_accessibility_check=True)
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Use 'SOME_PROGRAM_NAME_STRING --help' to get help with the arguments")
|
||||
parser.set_defaults(func=lambda _: parser.print_help())
|
||||
|
||||
actions_parser = parser.add_subparsers(title="Action")
|
||||
|
||||
# Configure parser
|
||||
configure_parser = actions_parser.add_parser(
|
||||
"configure", help="Configure a test run")
|
||||
configure_parser.set_defaults(func=configure_action)
|
||||
configure_parser.add_argument('-i', '--input_config_file', required=False,
|
||||
type=argparse.FileType('r'), help="The config file to base the configuration off of.")
|
||||
configure_parser.add_argument('-o', '--output_config_file', required=False, default=DEFAULT_CONFIG_FILE,
|
||||
type=argparse.FileType('w'), help="The config file to write the configuration off of.")
|
||||
|
||||
# Run parser
|
||||
run_parser = actions_parser.add_parser(
|
||||
"run", help="Run a test")
|
||||
run_parser.set_defaults(func=run_action)
|
||||
run_parser.add_argument('-c', '--config_file', required=False,
|
||||
type=argparse.FileType('r'),
|
||||
default=DEFAULT_CONFIG_FILE,
|
||||
help="The config file for the test. Note that this file "
|
||||
"cannot be changed (except for credentials that can be "
|
||||
"entered on the command line).")
|
||||
|
||||
run_parser.add_argument('-user', '--splunkbase_username', required=False, type=str,
|
||||
help="Username for login to splunkbase. This is required "
|
||||
"if downloading packages from Splunkbase. While this can "
|
||||
"be stored in the config file, it is strongly recommended "
|
||||
"to enter it at runtime.")
|
||||
|
||||
run_parser.add_argument('-b', '--branch', required=False, type=str,
|
||||
help="The branch to run the tests on.")
|
||||
|
||||
run_parser.add_argument('-hash', '--commit_hash', required=False, type=str,
|
||||
help="The hash to run the tests on.")
|
||||
|
||||
run_parser.add_argument('-pr', '--pr_number', required=False, type=int,
|
||||
help="The Pull request to run the tests on.")
|
||||
|
||||
run_parser.add_argument('-m', '--mode', required=False, type=str,
|
||||
help="The mode all, changes, or selected for the testing.")
|
||||
|
||||
run_parser.add_argument('-pass', '--splunkbase_password', required=False, type=str,
|
||||
help="Password for login to splunkbase. This is required if "
|
||||
"downloading packages from Splunkbase. While this can be "
|
||||
"stored in the config file, it is strongly recommended "
|
||||
"to enter it at runtime.")
|
||||
|
||||
run_parser.add_argument('-splunkpass', '--splunk_app_password', required=False, type=str,
|
||||
help="Password for login to the splunk app. If you don't "
|
||||
"provide one here or in the config, it will be generated "
|
||||
"automatically for you.")
|
||||
|
||||
run_parser.add_argument("-show_pass", "--show_splunk_app_password", required=False,
|
||||
action="store_true",
|
||||
help="The password to login to the Splunk Server. If the config "
|
||||
"file is set to true, it will override the default False for this. True "
|
||||
"will override the default value in the config file.")
|
||||
|
||||
run_parser.add_argument("-mock", "--mock", required=False,
|
||||
action="store_true",
|
||||
help="Split into multiple configs, don't actually run the tests. If the config "
|
||||
"file is set to true, it will override the default False for this. True "
|
||||
"will override the default value in the config file.")
|
||||
|
||||
run_parser.add_argument("-n", "--num_containers", required=False, type=int,
|
||||
help="The number of Splunk containers to run or mock")
|
||||
|
||||
run_parser.add_argument("-nif", "--no_interactive_failure", required=False,
|
||||
action="store_true",
|
||||
help="After a detection fails, pause and allow the user to log into "\
|
||||
"the Splunk server to interactively debug the failure. Wait for the user "\
|
||||
"to hit enter before removing the test data and moving on to the next test.")
|
||||
|
||||
run_parser.add_argument("-i", "--interactive", required=False,
|
||||
action="store_true",
|
||||
help="After a detection runs, pause and allow the user to log into "\
|
||||
"the Splunk server to debug the detection. Wait for the user "\
|
||||
"to hit enter before removing the test data and moving on to the next test.")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
# Run the appropriate parser
|
||||
try:
|
||||
# If one of these arguments is not passed on the command line, don't overwrite its config
|
||||
# file value with None - keep the config file value
|
||||
keys = list(args.__dict__.keys())
|
||||
for key in keys:
|
||||
|
||||
# We have to do the check separately because booleans using the --store_true
|
||||
# action have an implict default=False value, even if we don't set it. We cannot
|
||||
# set their value to something else, like None
|
||||
|
||||
# Don't overwite booleans
|
||||
if args.__dict__[key] is False and key in ["show_splunk_app_password", "mock", "no_interactive_failure", "interactive"]:
|
||||
del args.__dict__[key]
|
||||
# Don't overwrite other values
|
||||
elif args.__dict__[key] is None and key in ["splunkbase_username", "branch", "commit_hash",
|
||||
"pr_number", "mode", "splunkbase_password",
|
||||
"num_containers"]:
|
||||
del args.__dict__[key]
|
||||
|
||||
action, settings = args.func(args)
|
||||
|
||||
|
||||
return action, settings
|
||||
except Exception as e:
|
||||
print("Unknown Error Validating Json Configuration - [%s]" % (str(e)))
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
parse(sys.argv[1:])
|
||||
@@ -1,507 +0,0 @@
|
||||
from collections import OrderedDict
|
||||
import datetime
|
||||
import docker
|
||||
import docker.types
|
||||
import docker.models
|
||||
import docker.models.resource
|
||||
import docker.models.containers
|
||||
import os.path
|
||||
import random
|
||||
import requests
|
||||
import shutil
|
||||
from modules import splunk_sdk
|
||||
from modules import testing_service
|
||||
from modules import test_driver
|
||||
import time
|
||||
import timeit
|
||||
from typing import Union
|
||||
import threading
|
||||
import wrapt_timeout_decorator
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
SPLUNKBASE_URL = "https://splunkbase.splunk.com/app/%d/release/%s/download"
|
||||
SPLUNK_START_ARGS = "--accept-license"
|
||||
|
||||
# Give ten minutes to start - this is probably enough time
|
||||
MAX_CONTAINER_START_TIME_SECONDS = 60 * 20
|
||||
|
||||
|
||||
class SplunkContainer:
|
||||
def __init__(
|
||||
self,
|
||||
synchronization_object: test_driver.TestDriver,
|
||||
full_docker_hub_path,
|
||||
container_name: str,
|
||||
apps: OrderedDict,
|
||||
web_port_tuple: tuple[str, int],
|
||||
management_port_tuple: tuple[str, int],
|
||||
container_password: str,
|
||||
files_to_copy_to_container: OrderedDict = OrderedDict(),
|
||||
mounts: list[docker.types.Mount] = [],
|
||||
splunkbase_username: Union[str, None] = None,
|
||||
splunkbase_password: Union[str, None] = None,
|
||||
splunk_ip: str = "127.0.0.1",
|
||||
interactive_failure: bool = False,
|
||||
interactive: bool = False,
|
||||
):
|
||||
self.interactive_failure = interactive_failure
|
||||
self.interactive = interactive
|
||||
self.synchronization_object = synchronization_object
|
||||
self.client = docker.client.from_env()
|
||||
self.full_docker_hub_path = full_docker_hub_path
|
||||
self.container_password = container_password
|
||||
|
||||
self.apps = apps
|
||||
|
||||
self.files_to_copy_to_container = files_to_copy_to_container
|
||||
self.splunk_ip = splunk_ip
|
||||
self.container_name = container_name
|
||||
self.mounts = mounts
|
||||
self.environment = self.make_environment(
|
||||
apps, container_password, splunkbase_username, splunkbase_password
|
||||
)
|
||||
self.ports = self.make_ports(web_port_tuple, management_port_tuple)
|
||||
self.web_port = web_port_tuple[1]
|
||||
self.management_port = management_port_tuple[1]
|
||||
self.container = self.make_container()
|
||||
|
||||
self.thread = threading.Thread(
|
||||
target=self.run_container,
|
||||
)
|
||||
|
||||
self.container_start_time = -1
|
||||
self.test_start_time = -1
|
||||
self.num_tests_completed = 0
|
||||
|
||||
def prepare_apps_path(
|
||||
self,
|
||||
apps: OrderedDict,
|
||||
splunkbase_username: Union[str, None] = None,
|
||||
splunkbase_password: Union[str, None] = None,
|
||||
) -> tuple[str, bool]:
|
||||
apps_to_install = []
|
||||
|
||||
# We don't require credentials unless we install at least one splunkbase app
|
||||
require_credentials = False
|
||||
|
||||
# If the username and password are supplied, then we will use splunkbase...
|
||||
# assuming that the app_name and app_number are supplied. Note that if a
|
||||
# local_path is supplied, then it should override this option!
|
||||
if splunkbase_username is not None and splunkbase_password is not None:
|
||||
use_splunkbase = True
|
||||
else:
|
||||
use_splunkbase = False
|
||||
|
||||
for app_name, app_info in self.apps.items():
|
||||
if use_splunkbase is True and "local_path" not in app_info:
|
||||
target = SPLUNKBASE_URL % (
|
||||
app_info["app_number"],
|
||||
app_info["app_version"],
|
||||
)
|
||||
apps_to_install.append(target)
|
||||
# We will require credentials since we are installing at least one splunkbase app
|
||||
require_credentials = True
|
||||
# Some paths may have a local_path and an HTTP path defined. Default to the local_path first,
|
||||
# mostly because we may have copied it before into the cache to speed up start time.
|
||||
elif "local_path" in app_info:
|
||||
app_file_name = os.path.basename(app_info["local_path"])
|
||||
app_file_container_path = os.path.join("/tmp/apps", app_file_name)
|
||||
apps_to_install.append(app_file_container_path)
|
||||
elif "http_path" in app_info:
|
||||
apps_to_install.append(app_info["http_path"])
|
||||
|
||||
else:
|
||||
if use_splunkbase is True:
|
||||
print(
|
||||
"Error, the app %s: %s could not be installed from Splunkbase because "
|
||||
"--splunkbase_username and.or --splunkbase_password were not provided."
|
||||
"\n\tQuitting..." % (app_name, app_info),
|
||||
file=sys.stderr,
|
||||
)
|
||||
else:
|
||||
print(
|
||||
"Error, the app %s: %s has no http_path or local_path.\n\tQuitting..."
|
||||
% (app_name, app_info),
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
return ",".join(apps_to_install), require_credentials
|
||||
|
||||
def make_environment(
|
||||
self,
|
||||
apps: OrderedDict,
|
||||
container_password: str,
|
||||
splunkbase_username: Union[str, None] = None,
|
||||
splunkbase_password: Union[str, None] = None,
|
||||
) -> dict:
|
||||
env = {}
|
||||
env["SPLUNK_START_ARGS"] = SPLUNK_START_ARGS
|
||||
env["SPLUNK_PASSWORD"] = container_password
|
||||
splunk_apps_url, require_credentials = self.prepare_apps_path(
|
||||
apps, splunkbase_username, splunkbase_password
|
||||
)
|
||||
|
||||
if require_credentials:
|
||||
env["SPLUNKBASE_USERNAME"] = splunkbase_username
|
||||
env["SPLUNKBASE_PASSWORD"] = splunkbase_password
|
||||
env["SPLUNK_APPS_URL"] = splunk_apps_url
|
||||
|
||||
return env
|
||||
|
||||
def make_ports(self, *ports: tuple[str, int]) -> dict[str, int]:
|
||||
port_dict = {}
|
||||
for port in ports:
|
||||
port_dict[port[0]] = port[1]
|
||||
return port_dict
|
||||
|
||||
def __str__(self) -> str:
|
||||
container_string = (
|
||||
"Container Name: %s\n\t"
|
||||
"Docker Hub Path: %s\n\t"
|
||||
"Apps: %s\n\t"
|
||||
"Ports: %s\n\t"
|
||||
"Mounts: %s\n\t"
|
||||
% (
|
||||
self.container_name,
|
||||
self.full_docker_hub_path,
|
||||
self.environment["SPLUNK_APPS_URL"],
|
||||
self.ports,
|
||||
)
|
||||
)
|
||||
|
||||
return container_string
|
||||
|
||||
def make_container(self) -> docker.models.resource.Model:
|
||||
# First, make sure that the container has been removed if it already existed
|
||||
self.removeContainer()
|
||||
|
||||
container = self.client.containers.create(
|
||||
self.full_docker_hub_path,
|
||||
ports=self.ports,
|
||||
environment=self.environment,
|
||||
name=self.container_name,
|
||||
mounts=self.mounts,
|
||||
detach=True,
|
||||
platform="linux/amd64"
|
||||
)
|
||||
|
||||
return container
|
||||
|
||||
def extract_tar_file_to_container(
|
||||
self, local_file_path: str, container_file_path: str, sleepTimeSeconds: int = 5
|
||||
) -> bool:
|
||||
# Check to make sure that the file ends in .tar. If it doesn't raise an exception
|
||||
if os.path.splitext(local_file_path)[1] != ".tar":
|
||||
raise Exception(
|
||||
"Error - Failed copy of file [%s] to container [%s]. Only "
|
||||
"files ending in .tar can be copied to the container using this function."
|
||||
% (local_file_path, self.container_name)
|
||||
)
|
||||
successful_copy = False
|
||||
api_client = docker.APIClient()
|
||||
# need to use the low level client to put a file onto a container
|
||||
while not successful_copy:
|
||||
try:
|
||||
with open(local_file_path, "rb") as fileData:
|
||||
# splunk will restart a few times will installation of apps takes place so it will reload its indexes...
|
||||
|
||||
api_client.put_archive(
|
||||
container=self.container_name,
|
||||
path=container_file_path,
|
||||
data=fileData,
|
||||
)
|
||||
successful_copy = True
|
||||
except Exception as e:
|
||||
# print("Failed copy of [%s] file to [%s] on CONTAINER [%s]: [%s]\n...we will try again"%(local_file_path, container_file_path, self.container_name, str(e)))
|
||||
time.sleep(10)
|
||||
successful_copy = False
|
||||
# print("Successfully copied [%s] to [%s] on [%s]"% (local_file_path, container_file_path, self.container_name))
|
||||
return successful_copy
|
||||
|
||||
def stopContainer(self, timeout=10) -> bool:
|
||||
try:
|
||||
container = self.client.containers.get(self.container_name)
|
||||
# Note that stopping does not remove any of the volumes or logs,
|
||||
# so stopping can be useful if we want to debug any container failure
|
||||
container.stop(timeout=10)
|
||||
self.synchronization_object.containerFailure()
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
# Container does not exist, or we could not get it. Throw and error
|
||||
print("Error stopping docker container [%s]" % (self.container_name))
|
||||
return False
|
||||
|
||||
def removeContainer(
|
||||
self, removeVolumes: bool = True, forceRemove: bool = True
|
||||
) -> bool:
|
||||
try:
|
||||
container = self.client.containers.get(self.container_name)
|
||||
except Exception as e:
|
||||
# Container does not exist, no need to try and remove it
|
||||
return True
|
||||
try:
|
||||
# container was found, so now we try to remove it
|
||||
# v also removes volumes linked to the container
|
||||
container.remove(
|
||||
v=removeVolumes, force=forceRemove
|
||||
) # remove it even if it is running. remove volumes as well
|
||||
# No need to print that the container has been removed, it is expected behavior
|
||||
return True
|
||||
except Exception as e:
|
||||
print("Could not remove Docker Container [%s]" % (self.container_name))
|
||||
raise (Exception(f"CONTAINER REMOVE ERROR: {str(e)}"))
|
||||
|
||||
def get_container_summary(self) -> str:
|
||||
current_time = timeit.default_timer()
|
||||
|
||||
# Total time the container has been running
|
||||
if self.container_start_time == -1:
|
||||
total_time_string = "NOT STARTED"
|
||||
else:
|
||||
total_time_rounded = datetime.timedelta(
|
||||
seconds=round(current_time - self.container_start_time)
|
||||
)
|
||||
total_time_string = str(total_time_rounded)
|
||||
|
||||
# Time that the container setup took
|
||||
if self.test_start_time == -1 or self.container_start_time == -1:
|
||||
setup_time_string = "NOT SET UP"
|
||||
else:
|
||||
setup_secounds_rounded = datetime.timedelta(
|
||||
seconds=round(self.test_start_time - self.container_start_time)
|
||||
)
|
||||
setup_time_string = str(setup_secounds_rounded)
|
||||
|
||||
# Time that the tests have been running
|
||||
if self.test_start_time == -1 or self.num_tests_completed == 0:
|
||||
testing_time_string = "NO TESTS COMPLETED"
|
||||
else:
|
||||
testing_seconds_rounded = datetime.timedelta(
|
||||
seconds=round(current_time - self.test_start_time)
|
||||
)
|
||||
|
||||
# Get the approximate time per test. This is a clunky way to get rid of decimal
|
||||
# seconds.... but it works
|
||||
timedelta_per_test = testing_seconds_rounded / self.num_tests_completed
|
||||
timedelta_per_test_rounded = timedelta_per_test - datetime.timedelta(
|
||||
microseconds=timedelta_per_test.microseconds
|
||||
)
|
||||
|
||||
testing_time_string = "%s (%d tests @ %s per test)" % (
|
||||
testing_seconds_rounded,
|
||||
self.num_tests_completed,
|
||||
timedelta_per_test_rounded,
|
||||
)
|
||||
|
||||
summary_str = (
|
||||
"Summary for %s\n\t"
|
||||
"Total Time : [%s]\n\t"
|
||||
"Container Start Time: [%s]\n\t"
|
||||
"Test Execution Time : [%s]\n"
|
||||
% (
|
||||
self.container_name,
|
||||
total_time_string,
|
||||
setup_time_string,
|
||||
testing_time_string,
|
||||
)
|
||||
)
|
||||
|
||||
return summary_str
|
||||
|
||||
def wait_for_splunk_ready(
|
||||
self,
|
||||
seconds_between_attempts: int = 10,
|
||||
) -> bool:
|
||||
# The smarter version of this will try to hit one of the pages,
|
||||
# probably the login page, and when that is available it means that
|
||||
# splunk is fully started and ready to go. Until then, we just
|
||||
# use a simple sleep
|
||||
|
||||
while True:
|
||||
try:
|
||||
service = splunk_sdk.client.connect(
|
||||
host=self.splunk_ip,
|
||||
port=self.management_port,
|
||||
username="admin",
|
||||
password=self.container_password,
|
||||
)
|
||||
if service.restart_required:
|
||||
# The sleep below will wait
|
||||
pass
|
||||
else:
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
# There is a good chance the server is restarting, so the SDK connection failed.
|
||||
# Or, we tried to check restart_required while the server was restarting. In the
|
||||
# calling function, we have a timeout, so it's okay if this function could get
|
||||
# stuck in an infinite loop (the caller will generate a timeout error)
|
||||
pass
|
||||
|
||||
time.sleep(seconds_between_attempts)
|
||||
|
||||
# @wrapt_timeout_decorator.timeout(MAX_CONTAINER_START_TIME_SECONDS, timeout_exception=RuntimeError)
|
||||
def setup_container(self):
|
||||
self.container.start()
|
||||
|
||||
# def shutdown_signal_handler(sig, frame):
|
||||
# shutdown_client = docker.client.from_env()
|
||||
# errorCount = 0
|
||||
|
||||
# print(f"Shutting down {self.container_name}...", file=sys.stderr)
|
||||
# try:
|
||||
# container = shutdown_client.containers.get(self.container_name)
|
||||
# #Note that stopping does not remove any of the volumes or logs,
|
||||
# #so stopping can be useful if we want to debug any container failure
|
||||
# container.stop(timeout=10)
|
||||
# print(f"{self.container_name} shut down successfully", file=sys.stderr)
|
||||
# except Exception as e:
|
||||
# print(f"Error trying to shut down {self.container_name}. It may have already shut down. Stop it youself with 'docker containter stop {self.container_name}", sys.stderr)
|
||||
|
||||
# #We must use os._exit(1) because sys.exit(1) actually generates an exception which can be caught! And then we don't Quit!
|
||||
# import os
|
||||
# os._exit(1)
|
||||
|
||||
# import signal
|
||||
# signal.signal(signal.SIGINT, shutdown_signal_handler)
|
||||
|
||||
# By default, first copy the index file then the datamodel file
|
||||
for file_description, file_dict in self.files_to_copy_to_container.items():
|
||||
self.extract_tar_file_to_container(
|
||||
file_dict["local_file_path"], file_dict["container_file_path"]
|
||||
)
|
||||
|
||||
print("Finished copying files to [%s]" % (self.container_name))
|
||||
self.wait_for_splunk_ready()
|
||||
|
||||
def successfully_finish_tests(self) -> None:
|
||||
try:
|
||||
if self.num_tests_completed == 0:
|
||||
print(
|
||||
"Container [%s] did not find any tests and will not start.\n"
|
||||
"This does not mean there was an error!" % (self.container_name)
|
||||
)
|
||||
else:
|
||||
print(
|
||||
"Container [%s] has finished running [%d] detections, time to stop the container."
|
||||
% (self.container_name, self.num_tests_completed)
|
||||
)
|
||||
|
||||
# remove the container
|
||||
self.removeContainer()
|
||||
except Exception as e:
|
||||
print("Error stopping or removing the container: [%s]" % (str(e)))
|
||||
|
||||
return None
|
||||
|
||||
def run_container(self) -> None:
|
||||
print("Starting the container [%s]" % (self.container_name))
|
||||
|
||||
# Try to get something from the queue. Check this early on
|
||||
# before launching the container because it can save us a lot of time!
|
||||
detection_to_test = self.synchronization_object.getTest()
|
||||
if detection_to_test is None:
|
||||
return self.successfully_finish_tests()
|
||||
|
||||
self.container_start_time = timeit.default_timer()
|
||||
|
||||
container_start_time = timeit.default_timer()
|
||||
|
||||
try:
|
||||
self.setup_container()
|
||||
except Exception as e:
|
||||
print(
|
||||
"There was an exception starting the container [%s]: [%s]. Shutting down container"
|
||||
% (self.container_name, str(e)),
|
||||
file=sys.stdout,
|
||||
)
|
||||
self.stopContainer()
|
||||
elapsed_rounded = round(timeit.default_timer() - container_start_time)
|
||||
time_string = datetime.timedelta(seconds=elapsed_rounded)
|
||||
print("Container [%s] FAILED in [%s]" % (self.container_name, time_string))
|
||||
return None
|
||||
|
||||
# GTive some info about how long the container took to start up
|
||||
elapsed_rounded = round(timeit.default_timer() - container_start_time)
|
||||
time_string = datetime.timedelta(seconds=elapsed_rounded)
|
||||
print("Container [%s] took [%s] to start" % (self.container_name, time_string))
|
||||
self.synchronization_object.start_barrier.wait()
|
||||
|
||||
# Sleep for a small random time so that containers drift apart and don't synchronize their testing
|
||||
time.sleep(random.randint(1, 30))
|
||||
self.test_start_time = timeit.default_timer()
|
||||
while detection_to_test is not None:
|
||||
if self.synchronization_object.checkContainerFailure():
|
||||
self.container.stop()
|
||||
print(
|
||||
"Container [%s] successfully stopped early due to failure"
|
||||
% (self.container_name)
|
||||
)
|
||||
return None
|
||||
|
||||
current_test_start_time = timeit.default_timer()
|
||||
# Sleep for a small random time so that containers drift apart and don't synchronize their testing
|
||||
# time.sleep(random.randint(1, 30))
|
||||
|
||||
# There is a detection to test
|
||||
|
||||
print("Container [%s]--->[%s]" % (self.container_name, detection_to_test))
|
||||
try:
|
||||
result = testing_service.test_detection_wrapper(
|
||||
self.container_name,
|
||||
self.splunk_ip,
|
||||
self.container_password,
|
||||
self.management_port,
|
||||
detection_to_test,
|
||||
self.synchronization_object.attack_data_root_folder,
|
||||
wait_on_failure=self.interactive_failure,
|
||||
wait_on_completion=self.interactive,
|
||||
smoketest=self.synchronization_object.summarization_reproduce_failure_config[
|
||||
"mode"
|
||||
]
|
||||
== "smoketest",
|
||||
)
|
||||
|
||||
self.synchronization_object.addResult(
|
||||
result,
|
||||
duration_string=str(
|
||||
datetime.timedelta(
|
||||
seconds=round(
|
||||
timeit.default_timer() - current_test_start_time
|
||||
)
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
# Remove the data from the test that we just ran. We MUST do this when running on CI because otherwise, we will download
|
||||
# a massive amount of data over the course of a long path and will run out of space on the relatively small CI runner drive
|
||||
shutil.rmtree(result["attack_data_directory"], ignore_errors=True)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
|
||||
print(
|
||||
"Warning - uncaught error in detection test for [%s] - this should not happen: [%s]"
|
||||
% (detection_to_test, str(e))
|
||||
)
|
||||
print(traceback.print_exc())
|
||||
|
||||
self.synchronization_object.addError(
|
||||
{"detection_file": detection_to_test, "detection_error": str(e)},
|
||||
duration_string=str(
|
||||
datetime.timedelta(
|
||||
seconds=round(
|
||||
timeit.default_timer() - current_test_start_time
|
||||
)
|
||||
)
|
||||
),
|
||||
)
|
||||
self.num_tests_completed += 1
|
||||
|
||||
# Try to get something from the queue
|
||||
detection_to_test = self.synchronization_object.getTest()
|
||||
|
||||
# We failed to get a test from the queue, so we must be done gracefully! Quit
|
||||
return self.successfully_finish_tests()
|
||||
@@ -1,310 +0,0 @@
|
||||
from os import error
|
||||
import sys
|
||||
from time import sleep
|
||||
import splunklib.client as client
|
||||
import splunklib.results as results
|
||||
import requests
|
||||
import time
|
||||
import timeit
|
||||
import datetime
|
||||
from typing import Union
|
||||
|
||||
DEFAULT_EVENT_HOST = "ATTACK_DATA_HOST"
|
||||
DEFAULT_DATA_INDEX = "main"
|
||||
FAILURE_SLEEP_INTERVAL_SECONDS = 60
|
||||
|
||||
def enable_delete_for_admin(splunk_host:str, splunk_port:int, splunk_password:str)->bool:
|
||||
try:
|
||||
service = client.connect(
|
||||
host=splunk_host,
|
||||
port=splunk_port,
|
||||
username='admin',
|
||||
password=splunk_password
|
||||
)
|
||||
except Exception as e:
|
||||
raise(Exception("Unable to connect to Splunk instance: " + str(e)))
|
||||
|
||||
|
||||
#write the following contents to /opt/splunk/etc/system/local/authorize.conf
|
||||
"[role_admin]"\
|
||||
"delete_by_keyword = enabled"\
|
||||
"grantableRoles = admin"\
|
||||
"importRoles = can_delete;user;power_user"\
|
||||
"srchIndexesAllowed = *;_*;main"\
|
||||
"srchIndexesDefault = main"\
|
||||
"srchMaxTime = 8640000"
|
||||
|
||||
#Run the following search, equivalent to running ./splunk reload auth, to get the settings to take effect
|
||||
|
||||
update_changed_auth_search = "| rest splunk_server=* /services/authentication/providers/services/_reload"
|
||||
|
||||
|
||||
try:
|
||||
job = service.jobs.create(update_changed_auth_search)
|
||||
except Exception as e:
|
||||
error_message = "Unable to enable delete: %s"%(str(e))
|
||||
return False
|
||||
|
||||
input("Waiting for you to check that delete has been enabled with: %s"%(update_changed_auth_search))
|
||||
return True
|
||||
'''
|
||||
# search and replace \\ with \\\
|
||||
# search = search.replace('\\','\\\\')
|
||||
role = service.roles['admin']
|
||||
try:
|
||||
role.grant('delete_by_keyword')
|
||||
except Exception as e:
|
||||
print("Error - failed trying to grant 'can_delete' privs to admin: [%s]"%(str(e)))
|
||||
return False
|
||||
'''
|
||||
return True
|
||||
|
||||
|
||||
|
||||
|
||||
def get_number_of_indexed_events(splunk_host, splunk_port, splunk_password, index:str, event_host:str=DEFAULT_EVENT_HOST, sourcetype:Union[str,None]=None )->int:
|
||||
|
||||
try:
|
||||
service = client.connect(
|
||||
host=splunk_host,
|
||||
port=splunk_port,
|
||||
username='admin',
|
||||
password=splunk_password
|
||||
)
|
||||
except Exception as e:
|
||||
raise(Exception("Unable to connect to Splunk instance: " + str(e)))
|
||||
|
||||
if sourcetype is not None:
|
||||
search = f'''search index="{index}" sourcetype="{sourcetype}" host="{event_host}" | stats count'''
|
||||
else:
|
||||
search = f'''search index="{index}" host="{event_host}" | stats count'''
|
||||
kwargs = {"exec_mode":"blocking"}
|
||||
try:
|
||||
job = service.jobs.create(search, **kwargs)
|
||||
|
||||
#This returns the count in string form, not as an int. For example:
|
||||
#OrderedDict([('count', '59630')])
|
||||
results_stream = job.results(output_mode='json')
|
||||
count = None
|
||||
for res in results.JSONResultsReader(results_stream):
|
||||
if 'count' in res:
|
||||
count = int(res['count'],10)
|
||||
if count is None:
|
||||
raise Exception(f"Expected the get_number_of_indexed_events search to only return 1 count, but got {len(search_results)} instead.")
|
||||
|
||||
return count
|
||||
|
||||
except Exception as e:
|
||||
raise Exception("Error trying to get the count while waiting for indexing to complete: %s"%(str(e)))
|
||||
|
||||
|
||||
|
||||
|
||||
def wait_for_indexing_to_complete(splunk_host, splunk_port, splunk_password, sourcetype:str, index:str, check_interval_seconds:int=10)->bool:
|
||||
startTime = timeit.default_timer()
|
||||
previous_count = -1
|
||||
time.sleep(check_interval_seconds)
|
||||
while True:
|
||||
new_count = get_number_of_indexed_events(splunk_host, splunk_port, splunk_password, index=index, sourcetype=sourcetype)
|
||||
#print(f"Previous Count [{previous_count}] New Count [{new_count}]")
|
||||
if previous_count == -1:
|
||||
previous_count = new_count
|
||||
else:
|
||||
if new_count == previous_count:
|
||||
stopTime = timeit.default_timer()
|
||||
return True
|
||||
else:
|
||||
previous_count = new_count
|
||||
|
||||
#If new_count is really low, then the server is taking some extra time to index the data.
|
||||
# So sleep for longer to make sure that we give time to complete (or at least process more
|
||||
# events so we don't return from this function prematurely)
|
||||
if new_count < 2:
|
||||
time.sleep(check_interval_seconds*3)
|
||||
else:
|
||||
time.sleep(check_interval_seconds)
|
||||
|
||||
|
||||
def test_baseline_search(splunk_host, splunk_port, splunk_password, search, pass_condition, baseline_name, baseline_file, earliest_time, latest_time)->dict:
|
||||
try:
|
||||
service = client.connect(
|
||||
host=splunk_host,
|
||||
port=splunk_port,
|
||||
username='admin',
|
||||
password=splunk_password
|
||||
)
|
||||
except Exception as e:
|
||||
raise(Exception("Unable to connect to Splunk instance: " + str(e)))
|
||||
|
||||
|
||||
|
||||
# search and replace \\ with \\\
|
||||
# search = search.replace('\\','\\\\')
|
||||
|
||||
if search.startswith('|'):
|
||||
updated_search = search
|
||||
else:
|
||||
updated_search = 'search ' + search
|
||||
|
||||
kwargs = {"exec_mode": "blocking",
|
||||
"dispatch.earliest_time": earliest_time,
|
||||
"dispatch.latest_time": latest_time}
|
||||
|
||||
splunk_search = updated_search + ' ' + pass_condition
|
||||
|
||||
try:
|
||||
job = service.jobs.create(splunk_search, **kwargs)
|
||||
except Exception as e:
|
||||
raise(Exception("Unable to execute baseline: " + str(e)))
|
||||
|
||||
|
||||
test_results = dict()
|
||||
test_results['diskUsage'] = job['diskUsage']
|
||||
test_results['runDuration'] = job['runDuration']
|
||||
test_results['baseline_name'] = baseline_name
|
||||
test_results['baseline_file'] = baseline_file
|
||||
test_results['scanCount'] = job['scanCount']
|
||||
|
||||
if int(job['resultCount']) != 1:
|
||||
print("Test failed for baseline: " + baseline_name)
|
||||
test_results['error'] = True
|
||||
return test_results
|
||||
else:
|
||||
print("Test successful for baseline: " + baseline_name)
|
||||
test_results['error'] = False
|
||||
return test_results
|
||||
|
||||
|
||||
|
||||
def test_detection_search(splunk_host:str, splunk_port:int, splunk_password:str, search:str, pass_condition:str,
|
||||
detection_name:str, detection_file:str, earliest_time:str, latest_time:str, attempts_remaining:int=4,
|
||||
failure_sleep_interval_seconds:int=FAILURE_SLEEP_INTERVAL_SECONDS)->dict:
|
||||
#Since this is an attempt, decrement the number of remaining attempts
|
||||
attempts_remaining -= 1
|
||||
|
||||
if search.startswith('|'):
|
||||
updated_search = search
|
||||
else:
|
||||
updated_search = 'search ' + search
|
||||
|
||||
kwargs = {"exec_mode": "blocking",
|
||||
"dispatch.earliest_time": "-1d",
|
||||
"dispatch.latest_time": "now"}
|
||||
|
||||
splunk_search = updated_search + ' ' + pass_condition
|
||||
test_results = dict()
|
||||
|
||||
#These will always be present. By default, we will say that the
|
||||
#test has failed AND there was an error (until they are set otherwise)
|
||||
test_results['search_string'] = splunk_search
|
||||
test_results['detection_name'] = detection_name
|
||||
test_results['detection_file'] = detection_file
|
||||
|
||||
test_results['success'] = False
|
||||
test_results['error'] = True
|
||||
|
||||
|
||||
try:
|
||||
service = client.connect(
|
||||
host=splunk_host,
|
||||
port=splunk_port,
|
||||
|
||||
username='admin',
|
||||
password=splunk_password
|
||||
)
|
||||
except Exception as e:
|
||||
error_message = "Unable to connect to Splunk instance: %s"%(str(e))
|
||||
print(error_message,file=sys.stderr)
|
||||
test_results['error'] = True
|
||||
test_results['detection_error'] = error_message
|
||||
return test_results
|
||||
|
||||
|
||||
# search and replace \\ with \\\
|
||||
# search = search.replace('\\','\\\\')
|
||||
|
||||
|
||||
|
||||
#print("SEARCH: %s"%(splunk_search))
|
||||
|
||||
|
||||
try:
|
||||
job = service.jobs.create(splunk_search, **kwargs)
|
||||
results_stream = job.results(output_mode='json')
|
||||
|
||||
except Exception as e:
|
||||
|
||||
error_message = "Unable to execute detection: %s"%(str(e))
|
||||
print(error_message,file=sys.stderr)
|
||||
test_results['error'] = True
|
||||
test_results['detection_error'] = error_message
|
||||
return test_results
|
||||
|
||||
test_results['diskUsage'] = job['diskUsage']
|
||||
test_results['runDuration'] = job['runDuration']
|
||||
test_results['scanCount'] = job['scanCount']
|
||||
|
||||
#If we get this far, then there was not an error
|
||||
#The search may have FAILED, but there was no error in the search
|
||||
test_results['error'] = False
|
||||
|
||||
|
||||
#Should this be 1 for a pass, or should it be greater than 0?
|
||||
if int(job['resultCount']) != 1:
|
||||
#print("Test failed for detection: " + detection_name)
|
||||
if attempts_remaining > 0:
|
||||
print(f"Execution of test failed for [{detection_name}]. Sleeping for [{failure_sleep_interval_seconds} seconds] and trying up to {attempts_remaining} more times...")
|
||||
time.sleep(failure_sleep_interval_seconds)
|
||||
return test_detection_search(splunk_host, splunk_port, splunk_password, search, pass_condition, detection_name, detection_file,
|
||||
earliest_time, latest_time, attempts_remaining=attempts_remaining,
|
||||
failure_sleep_interval_seconds=failure_sleep_interval_seconds)
|
||||
else:
|
||||
test_results['success'] = False
|
||||
return test_results
|
||||
else:
|
||||
#print("Test successful for detection: " + detection_name)
|
||||
test_results['success'] = True
|
||||
return test_results
|
||||
|
||||
|
||||
def delete_attack_data(splunk_host:str, splunk_password:str, splunk_port:int, wait_on_delete:Union[dict,None], search_string:str, detection_filename:str, indices:list[str]=[DEFAULT_DATA_INDEX], host:str=DEFAULT_EVENT_HOST)->bool:
|
||||
|
||||
if wait_on_delete:
|
||||
print(wait_on_delete['message'])
|
||||
print("FILENAME : [%s]"%(detection_filename))
|
||||
print("SEARCH :\n%s"%(search_string))
|
||||
_ = input("****************Press ENTER to Complete Test and DELETE data****************\n\n\n")
|
||||
|
||||
try:
|
||||
service = client.connect(
|
||||
host=splunk_host,
|
||||
port=splunk_port,
|
||||
|
||||
username='admin',
|
||||
password=splunk_password
|
||||
)
|
||||
except Exception as e:
|
||||
|
||||
raise(Exception("Unable to connect to Splunk instance: " + str(e)))
|
||||
|
||||
|
||||
#print(f"Deleting data for {detection_filename}: {indices}")
|
||||
for index in indices:
|
||||
while (get_number_of_indexed_events(splunk_host, splunk_port, splunk_password, index=index, event_host=host) != 0) :
|
||||
splunk_search = f'search index="{index}" host="{host}" | delete'
|
||||
kwargs = {
|
||||
"exec_mode": "blocking",
|
||||
"dispatch.earliest_time": "-1d",
|
||||
"dispatch.latest_time": "now"}
|
||||
try:
|
||||
|
||||
job = service.jobs.create(splunk_search, **kwargs)
|
||||
results_stream = job.results(output_mode='json')
|
||||
reader = results.JSONResultsReader(results_stream)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
raise(Exception(f"Trouble deleting data using the search {splunk_search}: {str(e)}"))
|
||||
|
||||
|
||||
return True
|
||||
@@ -1,357 +0,0 @@
|
||||
import copy
|
||||
import csv
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import queue
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
import timeit
|
||||
from collections import OrderedDict
|
||||
from typing import Union
|
||||
|
||||
import psutil
|
||||
import summarize_json
|
||||
|
||||
|
||||
class TestDriver:
|
||||
def __init__(self, tests:list[str], num_containers:int, summarization_reproduce_failure_config:dict):
|
||||
#Create the queue and enque all of the tests
|
||||
self.testing_queue = queue.Queue()
|
||||
for test in tests:
|
||||
self.testing_queue.put(test)
|
||||
|
||||
self.total_number_of_tests = self.testing_queue.qsize()
|
||||
#Creates a lock that will be used to synchronize access to this object
|
||||
self.lock = threading.Lock()
|
||||
self.start_time = timeit.default_timer()
|
||||
self.failures = []
|
||||
self.successes = []
|
||||
self.errors = []
|
||||
self.container_ready_time = None
|
||||
|
||||
#No containers have failed
|
||||
self.container_failure = False
|
||||
|
||||
#Just make a random folder to store attack data that we donwload
|
||||
self.attack_data_root_folder = tempfile.mkdtemp(prefix="attack_data_", dir=os.getcwd())
|
||||
print("Attack data for this run will be stored at: [%s]"%(self.attack_data_root_folder))
|
||||
|
||||
#Not used right now, but we will keep it around for a bit in case we want to use it again
|
||||
self.start_barrier = threading.Barrier(num_containers)
|
||||
|
||||
#The config that will be used for writing out the error config reproduction fiel
|
||||
self.summarization_reproduce_failure_config = copy.deepcopy(summarization_reproduce_failure_config)
|
||||
|
||||
|
||||
#According to the docs:
|
||||
# Warning the first time this function is called with interval = 0.0 or None it will return a meaningless 0.0 value which you are supposed to ignore.
|
||||
# We call this exactly once here to prime for future calls and throw away the result
|
||||
cpu_info = psutil.cpu_times_percent(percpu=False)
|
||||
|
||||
|
||||
def checkContainerFailure(self)->bool:
|
||||
|
||||
self.lock.acquire()
|
||||
|
||||
try:
|
||||
result = self.container_failure
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def containerFailure(self)->None:
|
||||
self.lock.acquire()
|
||||
try:
|
||||
self.container_failure = True
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def checkIfTestsRemain(self):
|
||||
failure = self.checkContainerFailure()
|
||||
if failure:
|
||||
#Just return None, don't continue testing if a container crashed
|
||||
#Indicate there are no tests remaining
|
||||
return False
|
||||
|
||||
try:
|
||||
#This call isn't reliable according to documentation, but can save us some time.
|
||||
#Err on the side of caution
|
||||
return not self.testing_queue.empty()
|
||||
except Exception as e:
|
||||
print("Error determinging if testing queue was empty. Return False and try to get something.",file=sys.stderr)
|
||||
return True
|
||||
|
||||
|
||||
def getTest(self)-> Union[str,None]:
|
||||
|
||||
failure = self.checkContainerFailure()
|
||||
|
||||
|
||||
|
||||
if failure:
|
||||
#Just return None, don't continue testing if a container crashed
|
||||
return None
|
||||
|
||||
try:
|
||||
return self.testing_queue.get(block=False)
|
||||
except Exception as e:
|
||||
return None
|
||||
|
||||
def addSuccess(self, result:dict, duration_string:str)->None:
|
||||
print("Test PASSED: [%s --> %s] in %s"%(result['detection_name'], result['detection_file'], duration_string))
|
||||
self.lock.acquire()
|
||||
try:
|
||||
self.successes.append(result)
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
|
||||
def addFailure(self, result:dict, duration_string:str)->None:
|
||||
print("Test FAILED: [%s --> %s] in %s"%(result['detection_name'], result['detection_file'], duration_string))
|
||||
self.lock.acquire()
|
||||
try:
|
||||
self.failures.append(result)
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def addError(self, result:dict, duration_string:str)->None:
|
||||
#Make sure that even errors have all of the required fields.
|
||||
for required_field in ['search_string', 'diskUsage','runDuration', 'detection_name', 'scanCount', 'detection_error', 'detection_file']:
|
||||
if required_field not in result:
|
||||
result[required_field] = ""
|
||||
if 'error' not in result:
|
||||
result['error'] = True
|
||||
if 'success' not in result:
|
||||
result['success'] = False
|
||||
print("Test ERROR: [%s --> %s] in %s"%(result['detection_name'], result['detection_file'], duration_string))
|
||||
self.lock.acquire()
|
||||
try:
|
||||
self.errors.append(result)
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
|
||||
def outputResultsCSV(self, field_names:list[str], output_filename:str, data:list[dict], baseline:OrderedDict)->bool:
|
||||
success = True
|
||||
|
||||
print("Generating %s..."%(output_filename), end='')
|
||||
self.lock.acquire()
|
||||
|
||||
try:
|
||||
with open(output_filename, 'w') as csvfile:
|
||||
header_writer = csv.writer(csvfile, quoting=csv.QUOTE_ALL)
|
||||
for key in baseline:
|
||||
#Very basic support for pretty pritning dicts. Doesn't handle more than 1 nested dict
|
||||
if type(baseline[key]) is OrderedDict:
|
||||
header_writer.writerow([key, "-"])
|
||||
for nestedkey in baseline[key]:
|
||||
header_writer.writerow([nestedkey, baseline[key][nestedkey]])
|
||||
#Basic support for 1 layer nested list. Doesn't handle more than 1.
|
||||
elif type(baseline[key]) is list and len(baseline[key])>0:
|
||||
header_writer.writerow([key, baseline[key][0]])
|
||||
for i in range(1,len(baseline[key])):
|
||||
header_writer.writerow(['-', baseline[key][i]])
|
||||
|
||||
else:
|
||||
header_writer.writerow([key, baseline[key]])
|
||||
header_writer.writerow(['',''])
|
||||
csv_writer = csv.DictWriter(csvfile, fieldnames=field_names)
|
||||
csv_writer.writeheader()
|
||||
for row in data:
|
||||
csv_writer.writerow(row)
|
||||
print("Done with [%d] detections"%(len(data)))
|
||||
|
||||
except Exception as e:
|
||||
print("Failure writing to CSV file for [%s]:"%(output_filename, str(e)))
|
||||
success = False
|
||||
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
return success
|
||||
|
||||
def outputResultsJSON(self, field_names:list[str], output_filename:str, data:list[dict], baseline:OrderedDict)->bool:
|
||||
success = True
|
||||
try:
|
||||
with open(output_filename, "w") as jsonFile:
|
||||
json.dump({'baseline': baseline, 'results':data}, jsonFile, indent=" ")
|
||||
except Exception as e:
|
||||
print("There was an error generating [%s]: [%s]"%(output_filename, str(e)))
|
||||
success = False
|
||||
return success
|
||||
|
||||
def outputResultsFile(self, field_names:list[str], output_filename:str, data:list[dict], baseline:OrderedDict, output_json:bool=True, output_csv:bool=True)->bool:
|
||||
success = True
|
||||
if output_csv:
|
||||
success |= self.outputResultsCSV(field_names, output_filename + ".csv", data, baseline)
|
||||
if output_json:
|
||||
success |= self.outputResultsJSON(field_names, output_filename + ".json", data, baseline)
|
||||
return success
|
||||
|
||||
|
||||
def outputResultsFiles(self, baseline:OrderedDict, fields:list[str]=['detection_name', 'detection_file','runDuration','diskUsage', 'search_string', 'error', 'success', 'scanCount', 'detection_error'])->bool:
|
||||
results_directory = "test_results"
|
||||
try:
|
||||
shutil.rmtree(results_directory,ignore_errors=True)
|
||||
os.mkdir(results_directory)
|
||||
except Exception as e:
|
||||
print("There was an error removing the results directory [%s]: [%s].\n\t We will try to continue output anyway."%(results_directory, str(e)))
|
||||
|
||||
|
||||
res = self.outputResultsFile(fields,os.path.join(results_directory, "success"), self.successes, baseline)
|
||||
res |= self.outputResultsFile(fields, os.path.join(results_directory, "failure"), self.failures, baseline)
|
||||
res |= self.outputResultsFile(fields, os.path.join(results_directory, "error"), self.errors, baseline)
|
||||
combined_data = self.successes + self.failures + self.errors
|
||||
res |= self.outputResultsFile(fields, os.path.join(results_directory, "combined"), combined_data, baseline)
|
||||
|
||||
try:
|
||||
success, test_count,pass_count,fail_count,error_count = \
|
||||
summarize_json.outputResultsJSON("summary.json", combined_data,
|
||||
baseline, output_folder=results_directory,
|
||||
summarization_reproduce_failure_config=self.summarization_reproduce_failure_config)
|
||||
summarize_json.print_summary(test_count, pass_count, fail_count, error_count)
|
||||
res |= success
|
||||
except Exception as e:
|
||||
print("Failure writing the summary file: [%s]"%str(e),file=sys.stderr)
|
||||
res = False
|
||||
|
||||
return res
|
||||
|
||||
def finish(self, baseline:OrderedDict):
|
||||
self.cleanup()
|
||||
success = True
|
||||
if self.outputResultsFiles(baseline) == False:
|
||||
print("There was an error generating one or more of the output files. "\
|
||||
"Check the logs for details.",file=sys.stderr)
|
||||
success = False
|
||||
|
||||
|
||||
if self.checkContainerFailure():
|
||||
print("One or more containers crashed or the test was HALTED early, so testing did not complete successfully. We wrote out all the results that we could")
|
||||
return False
|
||||
else:
|
||||
return success
|
||||
|
||||
|
||||
|
||||
def cleanup(self):
|
||||
self.lock.acquire()
|
||||
try:
|
||||
print("Removing all attack data that was downloaded during this test at: [%s]"%(self.attack_data_root_folder))
|
||||
shutil.rmtree(self.attack_data_root_folder)
|
||||
print("Successfully removed all attack data")
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def get_system_stats(self)->str:
|
||||
|
||||
bytes_per_GB = 1024 * 1024 * 1024
|
||||
cpu_info = psutil.cpu_times_percent(percpu=False)
|
||||
memory_info = psutil.virtual_memory()
|
||||
disk_usage_info = psutil.disk_usage('/')
|
||||
|
||||
#macOS is really weird about disk usage.... so to get free space we use TOTAL-FREE = USED instead of just USED
|
||||
corrected_used_space = disk_usage_info.total - disk_usage_info.free
|
||||
|
||||
cpu_info_string = "Total CPU Usage : %d%% (%d CPUs)"%(100 - cpu_info.idle, psutil.cpu_count(logical=False))
|
||||
memory_info_string = "Total Memory Usage: %0.1fGB USED / %0.1fGB TOTAL"%((memory_info.total - memory_info.available) / bytes_per_GB, memory_info.total / bytes_per_GB)
|
||||
disk_usage_info_string = "Total Disk Usage : %0.1fGB USED / %0.1fGB TOTAL"%(corrected_used_space / bytes_per_GB, disk_usage_info.total / bytes_per_GB)
|
||||
|
||||
return "System Information:\n\t%s\n\t%s\n\t%s"%(cpu_info_string, memory_info_string, disk_usage_info_string)
|
||||
|
||||
|
||||
def summarize(self,testing_currently_active:bool=False)->bool:
|
||||
self.lock.acquire()
|
||||
try:
|
||||
|
||||
#Get a summary of some system stats
|
||||
system_stats=self.get_system_stats()
|
||||
|
||||
current_time = timeit.default_timer()
|
||||
|
||||
|
||||
|
||||
if not testing_currently_active:
|
||||
#Testing has not started yet. We are setting up containers
|
||||
print("***********PROGRESS UPDATE***********\n"\
|
||||
"\tWaiting for container setup: %s\n\t%s\n"%(datetime.timedelta(seconds=current_time - self.start_time),system_stats))
|
||||
else:
|
||||
|
||||
if self.container_ready_time is None:
|
||||
#This is the first status update since container setup has completed. Get the current time.
|
||||
#This makes our remaining time estimates better since that estimate should not involve
|
||||
#the container setup time
|
||||
print("SETTING THE CONTAINER READY TIME!")
|
||||
|
||||
self.container_ready_time = current_time
|
||||
|
||||
numberOfCompletedTests = len(self.successes) + len(self.failures) + len(self.errors)
|
||||
remaining_tests = self.testing_queue.qsize()
|
||||
testsCurrentlyRunning = self.total_number_of_tests - remaining_tests - numberOfCompletedTests
|
||||
total_execution_time_seconds = round(current_time - self.start_time)
|
||||
|
||||
test_execution_time_seconds = current_time - self.container_ready_time
|
||||
|
||||
|
||||
if numberOfCompletedTests == 0 or test_execution_time_seconds == 0:
|
||||
estimated_seconds_to_finish_all_tests = "UNKNOWN"
|
||||
estimated_completion_time_string = "UNKNOWN"
|
||||
average_time_per_test_string = "UNKNOWN"
|
||||
else:
|
||||
average_time_per_test = test_execution_time_seconds / numberOfCompletedTests
|
||||
average_time_per_test_string = datetime.timedelta(seconds=round(test_execution_time_seconds/numberOfCompletedTests))
|
||||
#divide testsCurrentlyRunning by 2.0 because, on average, each running test will be 50% completed
|
||||
estimated_seconds_to_finish_all_tests = round(average_time_per_test * (remaining_tests + testsCurrentlyRunning/2.0))
|
||||
estimated_completion_time_string = datetime.timedelta(seconds=estimated_seconds_to_finish_all_tests)
|
||||
|
||||
|
||||
|
||||
|
||||
print(f"***********PROGRESS UPDATE***********\n"\
|
||||
f"\tElapsed Time : {datetime.timedelta(seconds=total_execution_time_seconds)}\n"\
|
||||
f"\tTest Execution Time : {datetime.timedelta(seconds=round(test_execution_time_seconds))}\n"\
|
||||
f"\tEstimated Remaining Time : {estimated_completion_time_string}\n"\
|
||||
f"\tTests to run : {remaining_tests}\n"\
|
||||
f"\tAverage Time Per Test : {average_time_per_test_string}\n",
|
||||
f"\tTests currently running : {testsCurrentlyRunning}\n"\
|
||||
f"\tTests completed : {numberOfCompletedTests}\n"\
|
||||
f"\t\tSuccess : {len(self.successes)}\n"\
|
||||
f"\t\tFailure : {len(self.failures)}\n"\
|
||||
f"\t\tError : {len(self.errors)}\n"\
|
||||
f"\t{system_stats}\n")
|
||||
|
||||
except Exception as e:
|
||||
print("Error in printing execution summary: [%s]"%(str(e)))
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
|
||||
#Return true while there are tests remaining
|
||||
completed_tests = len(self.successes) + len(self.failures) + len(self.errors)
|
||||
remaining_tests = self.total_number_of_tests - completed_tests
|
||||
return remaining_tests > 0
|
||||
|
||||
|
||||
|
||||
def addResult(self, result:dict, duration_string:str)->None:
|
||||
try:
|
||||
if result['detection_result']['error'] is True:
|
||||
self.addError(result['detection_result'], duration_string = duration_string)
|
||||
elif result['detection_result']['success'] is False:
|
||||
#This is actually a failure of the detection, not an error. Naming is confusiong
|
||||
self.addFailure(result['detection_result'], duration_string = duration_string)
|
||||
elif result['detection_result']['success'] is True:
|
||||
self.addSuccess(result['detection_result'], duration_string = duration_string)
|
||||
except Exception as e:
|
||||
#Neither a success or a failure, so add the object to the failures queue
|
||||
print('"There was an error adding the result: [%s]'%(str(e)))
|
||||
self.addError({'detection_file':"Unknown File", "detection_error":str(result)})
|
||||
|
||||
@@ -1,294 +0,0 @@
|
||||
import re
|
||||
|
||||
# import ansible_runner
|
||||
import yaml
|
||||
import uuid
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import requests
|
||||
from modules.DataManipulation import DataManipulation
|
||||
from modules import utils
|
||||
from modules import splunk_sdk
|
||||
import timeit
|
||||
from typing import Union, Tuple
|
||||
from os.path import relpath
|
||||
from tempfile import mkdtemp, mkstemp
|
||||
import datetime
|
||||
import http.client
|
||||
|
||||
|
||||
def test_detection_wrapper(
|
||||
container_name: str,
|
||||
splunk_ip: str,
|
||||
splunk_password: str,
|
||||
splunk_port: int,
|
||||
detection_file: str,
|
||||
attack_data_root_folder,
|
||||
wait_on_failure: bool = False,
|
||||
wait_on_completion: bool = False,
|
||||
smoketest: bool = False,
|
||||
) -> dict:
|
||||
one_test_start = timeit.default_timer()
|
||||
uuid_var = str(uuid.uuid4())
|
||||
result_test, indices_to_delete = test_detection(
|
||||
splunk_ip,
|
||||
splunk_port,
|
||||
container_name,
|
||||
splunk_password,
|
||||
detection_file,
|
||||
uuid_var,
|
||||
attack_data_root_folder,
|
||||
smoketest,
|
||||
)
|
||||
one_test_stop = timeit.default_timer()
|
||||
|
||||
if result_test is None:
|
||||
# We failed so early in the process that we could not produce any meaningful result
|
||||
raise (Exception("Test execution Error"))
|
||||
|
||||
# enter = input("Run some tests from [%s] on [%s] - we don't delete until you hit enter :)"%(container_name, test_file))
|
||||
# delete test data
|
||||
search_string = result_test["detection_result"]["search_string"]
|
||||
|
||||
# get pretty time info
|
||||
elapsed_search_time_string = str(
|
||||
datetime.timedelta(seconds=round(one_test_stop - one_test_start))
|
||||
)
|
||||
|
||||
# search failed if there was an error or the detection failed to produce the expected result
|
||||
# print("Elapsed search time: %s"%(elapsed_search_time_string))
|
||||
if (wait_on_failure or wait_on_completion) and (
|
||||
result_test["detection_result"]["error"]
|
||||
or not result_test["detection_result"]["success"]
|
||||
):
|
||||
wait_on_delete = {
|
||||
"message": "\n\n\n****SEARCH FAILURE : Allowing time to debug search/data****"
|
||||
}
|
||||
elif wait_on_completion:
|
||||
wait_on_delete = {
|
||||
"message": "\n\n\n****SEARCH SUCCESS : Allowing time to examine search/data****"
|
||||
}
|
||||
else:
|
||||
wait_on_delete = None
|
||||
|
||||
splunk_sdk.delete_attack_data(
|
||||
splunk_ip,
|
||||
splunk_password,
|
||||
splunk_port,
|
||||
wait_on_delete,
|
||||
search_string,
|
||||
detection_file,
|
||||
indices=indices_to_delete,
|
||||
)
|
||||
|
||||
return result_test
|
||||
|
||||
|
||||
import splunklib.client as client
|
||||
|
||||
|
||||
def get_service(splunk_ip: str, splunk_port: int, splunk_password: str):
|
||||
try:
|
||||
service = client.connect(
|
||||
host=splunk_ip, port=splunk_port, username="admin", password=splunk_password
|
||||
)
|
||||
except Exception as e:
|
||||
raise (Exception("Unable to connect to Splunk instance: " + str(e)))
|
||||
return service
|
||||
|
||||
|
||||
def test_detection(
|
||||
splunk_ip: str,
|
||||
splunk_port: int,
|
||||
container_name: str,
|
||||
splunk_password: str,
|
||||
detection_file: str,
|
||||
uuid_var,
|
||||
attack_data_root_folder,
|
||||
smoketest: bool,
|
||||
) -> Tuple[Union[dict, None], set[str]]:
|
||||
detection_file_obj = load_file(os.path.join("security_content/", detection_file))
|
||||
|
||||
if not detection_file_obj:
|
||||
print("Not detection_file_obj!")
|
||||
raise (Exception("No test file object found for [%s]" % detection_file))
|
||||
|
||||
indices_to_delete = set()
|
||||
abs_folder_path = mkdtemp(prefix="DATA_", dir=attack_data_root_folder)
|
||||
if smoketest:
|
||||
result_detection = splunk_sdk.test_detection_search(
|
||||
splunk_ip,
|
||||
splunk_port,
|
||||
splunk_password,
|
||||
detection_file_obj["search"],
|
||||
"",
|
||||
detection_file_obj["name"],
|
||||
detection_file,
|
||||
"-24h",
|
||||
"now",
|
||||
attempts_remaining=1,
|
||||
)
|
||||
|
||||
result_test = {}
|
||||
test = {"name": detection_file_obj["name"] + " Smoketest"}
|
||||
result_test["baselines_result"] = []
|
||||
else:
|
||||
# print(test_file_obj)
|
||||
|
||||
# write entry dynamodb
|
||||
# aws_service.add_detection_results_in_dynamo_db('eu-central-1', uuid_var , uuid_test, test_file_obj['tests'][0]['name'], test_file_obj['tests'][0]['file'], str(int(time.time())))
|
||||
|
||||
# epoch_time = str(int(time.time()))
|
||||
|
||||
# We want the relative path, so we convert it as required
|
||||
|
||||
tests: dict = detection_file_obj.get("tests", {})
|
||||
if len(tests) > 1:
|
||||
print(
|
||||
f"****WARNING - THIS DETECTION CONTAINS {len(tests)} TESTS BUT WE WILL ONLY RUN 1"
|
||||
)
|
||||
test = tests[0]
|
||||
|
||||
for attack_data in test["attack_data"]:
|
||||
url = attack_data["data"]
|
||||
|
||||
if "custom_index" in attack_data:
|
||||
print(
|
||||
f"Found a custom index for {detection_file}: {attack_data['custom_index']}"
|
||||
)
|
||||
data_upload_index = attack_data["custom_index"]
|
||||
else:
|
||||
data_upload_index = splunk_sdk.DEFAULT_DATA_INDEX
|
||||
|
||||
indices_to_delete.add(data_upload_index)
|
||||
|
||||
_, target_file = mkstemp(prefix="attack_data_", dir=abs_folder_path)
|
||||
|
||||
utils.download_file_from_http(url, target_file, overwrite_file=True)
|
||||
|
||||
# Update timestamps before replay
|
||||
if "update_timestamp" in attack_data:
|
||||
if attack_data["update_timestamp"] == True:
|
||||
data_manipulation = DataManipulation()
|
||||
data_manipulation.manipulate_timestamp(
|
||||
target_file, attack_data["sourcetype"], attack_data["source"]
|
||||
)
|
||||
# replay_attack_dataset(container_name, splunk_password, folder_name, "test0", attack_data['sourcetype'], attack_data['source'], attack_data['file_name'])
|
||||
|
||||
try:
|
||||
service = get_service(splunk_ip, splunk_port, splunk_password)
|
||||
test_index = service.indexes[data_upload_index]
|
||||
|
||||
with open(target_file, "rb") as target:
|
||||
test_index.submit(
|
||||
target.read(),
|
||||
sourcetype=attack_data["sourcetype"],
|
||||
source=attack_data["source"],
|
||||
host=splunk_sdk.DEFAULT_EVENT_HOST,
|
||||
)
|
||||
|
||||
except http.client.HTTPException as e:
|
||||
raise (
|
||||
Exception(
|
||||
f"Failed to submit detection file {target_file} to Splunk Server: {str(e)}"
|
||||
)
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
raise (
|
||||
Exception(
|
||||
f"Failed to submit detection file {target_file} to Splunk Server: {str(e)}"
|
||||
)
|
||||
)
|
||||
|
||||
if not splunk_sdk.wait_for_indexing_to_complete(
|
||||
splunk_ip,
|
||||
splunk_port,
|
||||
splunk_password,
|
||||
attack_data["sourcetype"],
|
||||
data_upload_index,
|
||||
):
|
||||
raise Exception("There was an error waiting for indexing to complete.")
|
||||
|
||||
# Allow some time for the data to be ingested and processed
|
||||
# print("begin sleep 30")
|
||||
# time.sleep(60)
|
||||
|
||||
# print("end sleep 30")
|
||||
|
||||
result_test = {}
|
||||
|
||||
if "baselines" in test:
|
||||
results_baselines = []
|
||||
for baseline_obj in test["baselines"]:
|
||||
baseline_file_name = baseline_obj["file"]
|
||||
baseline = load_file(
|
||||
os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
"../security_content",
|
||||
baseline_file_name,
|
||||
)
|
||||
)
|
||||
result_obj = dict()
|
||||
result_obj["baseline"] = baseline_obj["name"]
|
||||
result_obj["baseline_file"] = baseline_file_name
|
||||
print(
|
||||
"Making test_baseline_search request to: [%s:%d]"
|
||||
% (splunk_ip, splunk_port)
|
||||
)
|
||||
result = splunk_sdk.test_baseline_search(
|
||||
splunk_ip,
|
||||
splunk_port,
|
||||
splunk_password,
|
||||
baseline["search"],
|
||||
baseline_obj["pass_condition"],
|
||||
baseline["name"],
|
||||
baseline_file_name,
|
||||
baseline_obj["earliest_time"],
|
||||
baseline_obj["latest_time"],
|
||||
)
|
||||
# we don't seem to be doing anything with this loop... are we supposed to have the following line belwo?
|
||||
results_baselines.append(result)
|
||||
|
||||
result_test["baselines_result"] = results_baselines
|
||||
|
||||
result_detection = splunk_sdk.test_detection_search(
|
||||
splunk_ip,
|
||||
splunk_port,
|
||||
splunk_password,
|
||||
detection_file_obj["search"],
|
||||
test.get("pass_condition", "| stats count | where count > 0"),
|
||||
detection_file_obj["name"],
|
||||
detection_file,
|
||||
test.get("earliest_time", "-24h"),
|
||||
test.get("latest_time", "now"),
|
||||
)
|
||||
if result_detection["error"]:
|
||||
print(
|
||||
"There was an error running the search: %s"
|
||||
% (result_detection["search_string"])
|
||||
)
|
||||
|
||||
result_detection["detection_name"] = test["name"]
|
||||
result_detection["detection_file"] = detection_file
|
||||
result_test["detection_result"] = result_detection
|
||||
result_test["attack_data_directory"] = abs_folder_path
|
||||
|
||||
return result_test, indices_to_delete
|
||||
|
||||
|
||||
def load_file(file_path):
|
||||
try:
|
||||
with open(file_path, "r", encoding="utf-8") as stream:
|
||||
try:
|
||||
file = list(yaml.safe_load_all(stream))[0]
|
||||
except yaml.YAMLError as exc:
|
||||
raise (
|
||||
Exception(
|
||||
"ERROR: parsing YAML for {0}:[{1}]".format(file_path, str(exc))
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
raise (Exception("ERROR: opening {0}:[{1}]".format(file_path, str(e))))
|
||||
return file
|
||||
@@ -1,71 +0,0 @@
|
||||
Malicious events
|
||||
|
||||
| tstats count as count values(Processes.action) as action,
|
||||
values(Processes.cpu_load_percent) as cpu_load_percent,
|
||||
values(Processes.dest) as dest,
|
||||
values(Processes.mem_used) as mem_used,
|
||||
values(Processes.os) as os,
|
||||
values(Processes.parent_process) as parent_process,
|
||||
values(Processes.parent_process_exec) as parent_process_exec,
|
||||
values(Processes.parent_process_id) as parent_process_id,
|
||||
values(Processes.parent_process_guid) as parent_process_guid,
|
||||
values(Processes.parent_process_name) as parent_process_name,
|
||||
values(Processes.parent_process_path) as parent_process_path,
|
||||
values(Processes.process) as process,
|
||||
values(Processes.process_current_directory) as process_current_directory,
|
||||
values(Processes.process_exec) as process_exec,
|
||||
values(Processes.process_hash) as process_hash,
|
||||
values(Processes.process_guid) as process_guid,
|
||||
values(Processes.process_id) as process_id,
|
||||
values(Processes.process_integrity_level) as process_integrity_level,
|
||||
values(Processes.process_name) as process_name,
|
||||
values(Processes.process_path) as process_path,
|
||||
values(Processes.tag) as tag,
|
||||
values(Processes.user) as user,
|
||||
values(Processes.user_id) as user_id,
|
||||
values(Processes.vendor_product) as vendor_product,
|
||||
values(host) as host,
|
||||
values(source) as source,
|
||||
values(sourcetype) as sourcetype
|
||||
from datamodel=Endpoint.Processes
|
||||
where (Processes.process_name=reg.exe
|
||||
OR Processes.process_name=cmd.exe) Processes.process=*save* (Processes.process=*HKEY_LOCAL_MACHINE\\Security*
|
||||
OR Processes.process=*HKEY_LOCAL_MACHINE\\SAM* OR Processes.process=*HKEY_LOCAL_MACHINE\\System*
|
||||
OR Processes.process=*HKLM\\Security* OR Processes.process=*HKLM\\System* OR Processes.process=*HKLM\\SAM*)
|
||||
by Processes.user Processes.process_name Processes.process Processes.dest Processes.process_id
|
||||
|
||||
|
||||
Not mailicous events
|
||||
| tstats count as count values(Processes.action) as action,
|
||||
values(Processes.cpu_load_percent) as cpu_load_percent,
|
||||
values(Processes.dest) as dest,
|
||||
values(Processes.mem_used) as mem_used,
|
||||
values(Processes.os) as os,
|
||||
values(Processes.parent_process) as parent_process,
|
||||
values(Processes.parent_process_exec) as parent_process_exec,
|
||||
values(Processes.parent_process_id) as parent_process_id,
|
||||
values(Processes.parent_process_guid) as parent_process_guid,
|
||||
values(Processes.parent_process_name) as parent_process_name,
|
||||
values(Processes.parent_process_path) as parent_process_path,
|
||||
values(Processes.process) as process,
|
||||
values(Processes.process_current_directory) as process_current_directory,
|
||||
values(Processes.process_exec) as process_exec,
|
||||
values(Processes.process_hash) as process_hash,
|
||||
values(Processes.process_guid) as process_guid,
|
||||
values(Processes.process_id) as process_id,
|
||||
values(Processes.process_integrity_level) as process_integrity_level,
|
||||
values(Processes.process_name) as process_name,
|
||||
values(Processes.process_path) as process_path,
|
||||
values(Processes.tag) as tag,
|
||||
values(Processes.user) as user,
|
||||
values(Processes.user_id) as user_id,
|
||||
values(Processes.vendor_product) as vendor_product,
|
||||
values(host) as host,
|
||||
values(source) as source,
|
||||
values(sourcetype) as sourcetype
|
||||
from datamodel=Endpoint.Processes
|
||||
where NOT((Processes.process_name=reg.exe
|
||||
OR Processes.process_name=cmd.exe) Processes.process=*save* (Processes.process=*HKEY_LOCAL_MACHINE\\Security*
|
||||
OR Processes.process=*HKEY_LOCAL_MACHINE\\SAM* OR Processes.process=*HKEY_LOCAL_MACHINE\\System*
|
||||
OR Processes.process=*HKLM\\Security* OR Processes.process=*HKLM\\System* OR Processes.process=*HKLM\\SAM*))
|
||||
by Processes.user Processes.process_name Processes.process Processes.dest Processes.process_id
|
||||
@@ -1,25 +0,0 @@
|
||||
import os
|
||||
import requests
|
||||
|
||||
|
||||
def download_file_from_http(url:str, destination_file:str, overwrite_file:bool=False, chunk_size:int=1024*1024, verbose_print:bool=False)->None:
|
||||
if os.path.exists(destination_file) and overwrite_file is False:
|
||||
print(f"[{destination_file}] already exists...using cached version")
|
||||
return
|
||||
if verbose_print:
|
||||
print(f"downloading to [{destination_file}]...",end="")
|
||||
try:
|
||||
file_to_download = requests.get(url, stream=True)
|
||||
if file_to_download.status_code != 200:
|
||||
if verbose_print:
|
||||
print("FAILED")
|
||||
raise Exception(f"Error downloading the file {url}: Status Code {file_to_download.status_code}")
|
||||
with open(destination_file, "wb") as output:
|
||||
for piece in file_to_download.iter_content(chunk_size=chunk_size):
|
||||
output.write(piece)
|
||||
if verbose_print:
|
||||
print("Done")
|
||||
except Exception as e:
|
||||
if verbose_print:
|
||||
print("FAILED")
|
||||
raise e
|
||||
@@ -1,352 +0,0 @@
|
||||
import argparse
|
||||
import copy
|
||||
import io
|
||||
import json
|
||||
import modules.jsonschema_errorprinter as jsonschema_errorprinter
|
||||
import sys
|
||||
from typing import Union
|
||||
|
||||
|
||||
# If we want, we can easily add a description field to any of the objects here!
|
||||
ES_APP_NAME = "SPLUNK_ES_CONTENT_UPDATE"
|
||||
setup_schema = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"branch": {"type": "string", "default": "develop"},
|
||||
"commit_hash": {"type": ["string", "null"], "default": None},
|
||||
"container_tag": {"type": "string", "default": "latest"},
|
||||
"no_interactive_failure": {"type": "boolean", "default": False},
|
||||
"interactive": {"type": "boolean", "default": False},
|
||||
"detections_list": {
|
||||
"type": ["array", "null"],
|
||||
"items": {"type": "string"},
|
||||
"default": None,
|
||||
},
|
||||
"apps": {
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
"patternProperties": {
|
||||
"^.*$": {
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
"properties": {
|
||||
"app_number": {"type": ["integer", "null"]},
|
||||
"app_version": {"type": ["string", "null"]},
|
||||
"local_path": {"type": ["string", "null"]},
|
||||
"http_path": {"type": ["string", "null"]},
|
||||
},
|
||||
"anyOf": [
|
||||
{"required": ["local_path"]},
|
||||
{"required": ["http_path"]},
|
||||
{"required": ["app_number", "app_version"]},
|
||||
],
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
# The default apps below were taken from the attack_range loadout: https://github.com/splunk/attack_range/blob/develop/attack_range.conf.template
|
||||
"Splunk Add-on for CrowdStrike FDR": {
|
||||
"app_number": 5579,
|
||||
"app_version": "1.3.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-crowdstrike-fdr_140.tgz",
|
||||
},
|
||||
"ADD_ON_FOR_LINUX_SYSMON": {
|
||||
"app_number": 6176,
|
||||
"app_version": "1.0.4",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/add-on-for-linux-sysmon_104.tgz",
|
||||
},
|
||||
"SPLUNK_TA_FOR_IIS": {
|
||||
"app_number": 3185,
|
||||
"app_version": "1.2.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-microsoft-iis_120.tgz",
|
||||
},
|
||||
ES_APP_NAME: {
|
||||
"app_number": 3449,
|
||||
"app_version": None,
|
||||
"local_path": None,
|
||||
},
|
||||
"PALO_ALTO_NETWORKS_ADD_ON_FOR_SPLUNK": {
|
||||
"app_number": 2757,
|
||||
"app_version": "8.0.1",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/palo-alto-networks-add-on-for-splunk_802.tgz",
|
||||
},
|
||||
"PYTHON_FOR_SCIENTIFIC_COMPUTING_FOR_LINUX_64_BIT": {
|
||||
"app_number": 2882,
|
||||
"app_version": "4.1.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/python-for-scientific-computing-for-linux-64-bit_410.tgz",
|
||||
},
|
||||
"SPLUNK_ADD_ON_FOR_AMAZON_KINESIS_FIREHOSE": {
|
||||
"app_number": 3719,
|
||||
"app_version": "1.3.2",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-amazon-kinesis-firehose_132.tgz",
|
||||
},
|
||||
"SPLUNK_ADD_ON_FOR_MICROSOFT_OFFICE_365": {
|
||||
"app_number": 4055,
|
||||
"app_version": "4.2.1",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-microsoft-office-365_430.tgz",
|
||||
},
|
||||
"SPLUNK_ADD_ON_FOR_MICROSOFT_WINDOWS": {
|
||||
"app_number": 742,
|
||||
"app_version": "8.5.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-microsoft-windows_870.tgz",
|
||||
},
|
||||
"SPLUNK_ADD_ON_FOR_NGINX": {
|
||||
"app_number": 3258,
|
||||
"app_version": "3.2.1",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-nginx_321.tgz",
|
||||
},
|
||||
"SPLUNK_ADD_ON_FOR_STREAM_FORWARDERS": {
|
||||
"app_number": 5238,
|
||||
"app_version": "8.1.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-stream-forwarders_810.tgz",
|
||||
},
|
||||
"SPLUNK_ADD_ON_FOR_STREAM_WIRE_DATA": {
|
||||
"app_number": 5234,
|
||||
"app_version": "8.1.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-stream-wire-data_810.tgz",
|
||||
},
|
||||
"SPLUNK_ADD_ON_FOR_SYSMON": {
|
||||
"app_number": 5709,
|
||||
"app_version": "3.1.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-sysmon_310.tgz",
|
||||
},
|
||||
"SPLUNK_ADD_ON_FOR_UNIX_AND_LINUX": {
|
||||
"app_number": 833,
|
||||
"app_version": "8.8.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-unix-and-linux_890.tgz",
|
||||
},
|
||||
"SPLUNK_APP_FOR_STREAM": {
|
||||
"app_number": 1809,
|
||||
"app_version": "8.1.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-app-for-stream_810.tgz",
|
||||
},
|
||||
"SPLUNK_MACHINE_LEARNING_TOOLKIT": {
|
||||
"app_number": 2890,
|
||||
"app_version": "5.4.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-machine-learning-toolkit_540.tgz",
|
||||
},
|
||||
"SPLUNK_TA_FOR_ZEEK": {
|
||||
"app_number": 5466,
|
||||
"app_version": "1.0.5",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/ta-for-zeek_105.tgz",
|
||||
},
|
||||
"URL_TOOLBOX": {
|
||||
"app_number": 2734,
|
||||
"app_version": "1.9.2",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/url-toolbox_192.tgz",
|
||||
},
|
||||
"SPLUNK_TA_FIX_WINDOWS": {
|
||||
"app_number": 9999,
|
||||
"app_version": "1.0.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/Splunk_TA_fix_windows.tgz",
|
||||
},
|
||||
"SPLUNK_TA_MICROSOFT_CLOUD_SERVICES": {
|
||||
"app_number": 3110,
|
||||
"app_version": "4.5.2",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-microsoft-cloud-services_510.tgz",
|
||||
},
|
||||
"SPLUNK_ADD_ON_FOR_GOOGLE_CLOUD_PLATFORM": {
|
||||
"app_number": 3088,
|
||||
"app_version": "4.1.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-google-cloud-platform_410.tgz",
|
||||
},
|
||||
"SPLUNK_ADD_ON_FOR_GOOGLE_WORKSPACE": {
|
||||
"app_number": 3110,
|
||||
"app_version": "2.4.1",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-google-workspace_251.tgz",
|
||||
},
|
||||
"SPLUNK_TA_FOR_SURICATA": {
|
||||
"app_number": 2760,
|
||||
"app_version": "2.3.3",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/ta-for-suricata_234.tgz",
|
||||
},
|
||||
"SPLUNK_COMMON_INFORMATION_MODEL": {
|
||||
"app_number": 1621,
|
||||
"app_version": "5.1.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-common-information-model-cim_511.tgz",
|
||||
}
|
||||
},
|
||||
},
|
||||
"mode": {
|
||||
"type": "string",
|
||||
"enum": ["changes", "selected", "all", "smoketest"],
|
||||
"default": "changes",
|
||||
},
|
||||
"num_containers": {"type": "integer", "minimum": 1, "default": 1},
|
||||
"persist_security_content": {"type": "boolean", "default": False},
|
||||
"pr_number": {"type": ["integer", "null"], "default": None},
|
||||
"reuse_image": {"type": "boolean", "default": True},
|
||||
"show_splunk_app_password": {"type": "boolean", "default": False},
|
||||
"splunkbase_username": {"type": ["string", "null"], "default": None},
|
||||
"splunkbase_password": {"type": ["string", "null"], "default": None},
|
||||
"splunk_app_password": {"type": ["string", "null"], "default": None},
|
||||
"splunk_container_apps_directory": {
|
||||
"type": "string",
|
||||
"default": "/opt/splunk/etc/apps",
|
||||
},
|
||||
"local_base_container_name": {"type": "string", "default": "splunk_test_%d"},
|
||||
"mock": {"type": "boolean", "default": False},
|
||||
"folders": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"endpoint",
|
||||
"cloud",
|
||||
"network",
|
||||
"web",
|
||||
"application",
|
||||
"experimental",
|
||||
],
|
||||
},
|
||||
"default": ["endpoint", "cloud", "network", "web", "application"],
|
||||
},
|
||||
"types": {
|
||||
"type": "array",
|
||||
"items": {"type": "string", "enum": ["Anomaly", "Hunting", "TTP"]},
|
||||
"default": ["Anomaly", "Hunting", "TTP"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def validate_file(file: io.TextIOWrapper) -> tuple[Union[dict, None], dict]:
|
||||
try:
|
||||
settings = json.loads(file.read())
|
||||
return validate(settings)
|
||||
except Exception as e:
|
||||
raise (e)
|
||||
|
||||
|
||||
def check_dependencies(
|
||||
settings: dict, skip_password_accessibility_check: bool = True
|
||||
) -> bool:
|
||||
# Check complex mode dependencies
|
||||
error_free = True
|
||||
|
||||
# Make sure that all the mode arguments are sane
|
||||
if settings["mode"] == "selected":
|
||||
# Make sure that exactly one of the following fields is populated
|
||||
|
||||
if settings["detections_list"] == None:
|
||||
print(
|
||||
"Error - mode was 'selected' but no detections_list was supplied.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
error_free = False
|
||||
|
||||
if settings["mode"] != "selected" and settings["detections_list"] != None:
|
||||
print(
|
||||
"Error - mode was not 'selected' but detections_list was supplied.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
error_free = False
|
||||
|
||||
# Make sure that if we will be in an interactive mode, that either the user has provided the password or the password will be printed
|
||||
if skip_password_accessibility_check:
|
||||
pass
|
||||
elif (
|
||||
settings["interactive"] or not settings["no_interactive_failure"]
|
||||
) and settings["show_splunk_app_password"] is False:
|
||||
print("\n\n******************************************************\n\n")
|
||||
if settings["splunk_app_password"] is not None:
|
||||
print(
|
||||
"Warning: You have chosen an interactive mode, set show_splunk_app_password False,\n"
|
||||
"and provided a password in the config file. We will NOT print this password to\n"
|
||||
"stdout. Look in the config file for this password.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
else:
|
||||
print(
|
||||
"Warning: You have chosen an interactive mode, set show_splunk_app_password False,\n"
|
||||
"and DID NOT provide a password in the config file. We have updated show_splunk_app_password\n"
|
||||
"to True for you. Otherwise, interactive mode login would be impossible.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
settings["show_splunk_app_password"] = True
|
||||
print("\n\n******************************************************\n\n")
|
||||
|
||||
# Returns true if there are not errors
|
||||
return error_free
|
||||
|
||||
|
||||
def validate_and_write(
|
||||
configuration: dict,
|
||||
output_file: Union[io.TextIOWrapper, None] = None,
|
||||
strip_credentials: bool = False,
|
||||
skip_password_accessibility_check: bool = True,
|
||||
) -> tuple[Union[dict, None], dict]:
|
||||
closeFile = False
|
||||
if output_file is None:
|
||||
import datetime
|
||||
|
||||
now = datetime.datetime.now()
|
||||
configname = now.strftime("%Y-%m-%dT%H:%M:%S%z") + "-test-run.json"
|
||||
output_file = open(configname, "w")
|
||||
closeFile = True
|
||||
|
||||
if strip_credentials:
|
||||
configuration = copy.deepcopy(configuration)
|
||||
configuration["splunkbase_password"] = None
|
||||
configuration["splunkbase_username"] = None
|
||||
configuration["container_password"] = None
|
||||
configuration["show_splunk_app_password"] = True
|
||||
|
||||
validated_json, setup_schema = validate(
|
||||
configuration, skip_password_accessibility_check
|
||||
)
|
||||
if validated_json == None:
|
||||
print("Error in the new settings! No output file written")
|
||||
else:
|
||||
print("Settings updated. Writing results to: %s" % (output_file.name))
|
||||
try:
|
||||
output_file.write(json.dumps(validated_json, sort_keys=True, indent=4))
|
||||
except Exception as e:
|
||||
print(
|
||||
"Error writing settings to %s: [%s]" % (output_file.name, str(e)),
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
if closeFile is True:
|
||||
output_file.close()
|
||||
|
||||
return validated_json, setup_schema
|
||||
|
||||
|
||||
def validate(
|
||||
configuration: dict, skip_password_accessibility_check: bool = True
|
||||
) -> tuple[Union[dict, None], dict]:
|
||||
# v = jsonschema.Draft201909Validator(argument_schema)
|
||||
|
||||
try:
|
||||
validation_errors, validated_json = jsonschema_errorprinter.check_json(
|
||||
configuration, setup_schema
|
||||
)
|
||||
|
||||
if len(validation_errors) == 0:
|
||||
# check to make sure there were no complex errors
|
||||
no_complex_errors = check_dependencies(
|
||||
validated_json, skip_password_accessibility_check
|
||||
)
|
||||
if no_complex_errors:
|
||||
return validated_json, setup_schema
|
||||
else:
|
||||
print(
|
||||
"Validation failed due to error(s) listed above.", file=sys.stderr
|
||||
)
|
||||
return None, setup_schema
|
||||
else:
|
||||
print(
|
||||
"[%d] failures detected during validation of the configuration!"
|
||||
% (len(validation_errors)),
|
||||
file=sys.stderr,
|
||||
)
|
||||
for error in validation_errors:
|
||||
print(error, end="\n\n", file=sys.stderr)
|
||||
return None, setup_schema
|
||||
|
||||
except Exception as e:
|
||||
print(
|
||||
"There was an error validation the configuration: [%s]" % (str(e)),
|
||||
file=sys.stderr,
|
||||
)
|
||||
return None, setup_schema
|
||||
@@ -1,247 +0,0 @@
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
import argparse
|
||||
import sys
|
||||
import json
|
||||
from modules import validate_args
|
||||
import os.path
|
||||
from operator import itemgetter
|
||||
import copy
|
||||
|
||||
|
||||
def outputResultsJSON(
|
||||
output_filename: str,
|
||||
data: list[dict],
|
||||
baseline: OrderedDict,
|
||||
failure_manifest_filename="detection_failure_manifest.json",
|
||||
output_folder: str = "",
|
||||
summarization_reproduce_failure_config: dict = {},
|
||||
) -> tuple[bool, int, int, int, int]:
|
||||
success = True
|
||||
|
||||
try:
|
||||
test_count = len(data)
|
||||
# Passed
|
||||
pass_count = len([x for x in data if x["success"] == True])
|
||||
|
||||
# A failure or an error
|
||||
fail_count = len([x for x in data if x["success"] == False])
|
||||
|
||||
# An error (every error is also a failure)
|
||||
fail_and_error_count = len([x for x in data if x["error"] == True])
|
||||
|
||||
# A failure without an error
|
||||
fail_without_error_count = len(
|
||||
[x for x in data if x["success"] == False and x["error"] == False]
|
||||
)
|
||||
|
||||
# This number should always be zero...
|
||||
error_and_success_count = len(
|
||||
[x for x in data if x["success"] == True and x["error"] == True]
|
||||
)
|
||||
if error_and_success_count > 0:
|
||||
print(
|
||||
"Error - a test was successful, but also included an error. This should be impossible.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
success = False
|
||||
|
||||
if test_count != (pass_count + fail_count):
|
||||
print(
|
||||
"Error - the total tests [%d] does not equal the pass[%d]/fails[%d]"
|
||||
% (test_count, pass_count, fail_count)
|
||||
)
|
||||
success = False
|
||||
|
||||
if fail_count > 0:
|
||||
result = "FAIL for %d detections" % (fail_count)
|
||||
success = False
|
||||
else:
|
||||
result = "PASS for all %d detections" % (pass_count)
|
||||
|
||||
summary = {
|
||||
"TOTAL_TESTS": test_count,
|
||||
"TESTS_PASSED": pass_count,
|
||||
"TOTAL_FAILURES": fail_count,
|
||||
"FAIL_ONLY": fail_without_error_count,
|
||||
"PASS_RATE": calculate_pass_rate(pass_count, test_count),
|
||||
"FAIL_AND_ERROR": fail_and_error_count,
|
||||
}
|
||||
|
||||
data_sorted = sorted(
|
||||
data, key=lambda k: (-k["error"], k["success"], k["detection_file"])
|
||||
)
|
||||
with open(os.path.join(output_folder, output_filename), "w") as jsonFile:
|
||||
json.dump(
|
||||
{"summary": summary, "baseline": baseline, "results": data_sorted},
|
||||
jsonFile,
|
||||
indent=" ",
|
||||
)
|
||||
|
||||
# Generate a failure that the user can download to reproduce and test ONLY the failures locally.
|
||||
# This makes it easy to test and debug ONLY those that failed. No need to test the ones
|
||||
# that succeeded!
|
||||
|
||||
fail_list = [
|
||||
os.path.join("security_content/detections", x["detection_file"])
|
||||
for x in data_sorted
|
||||
if x["success"] == False
|
||||
]
|
||||
|
||||
if len(fail_list) > 0:
|
||||
print("FAILURES:")
|
||||
for failed_test in fail_list:
|
||||
print(f"\t{failed_test}")
|
||||
failures_test_override = copy.deepcopy(
|
||||
summarization_reproduce_failure_config
|
||||
)
|
||||
# Force all tests to be interactive, even if they don't fail (because they failed on this test)
|
||||
failures_test_override.update(
|
||||
{
|
||||
"detections_list": fail_list,
|
||||
"no_interactive_failure": False,
|
||||
"interactive": True,
|
||||
"num_containers": 1,
|
||||
"branch": baseline["branch"],
|
||||
"commit_hash": baseline["commit_hash"],
|
||||
"mode": "selected",
|
||||
"show_splunk_app_password": True,
|
||||
}
|
||||
)
|
||||
with open(
|
||||
os.path.join(output_folder, failure_manifest_filename), "w"
|
||||
) as failures:
|
||||
validate_args.validate_and_write(failures_test_override, failures)
|
||||
except Exception as e:
|
||||
print(
|
||||
"There was an error generating [%s]: [%s]" % (output_filename, str(e)),
|
||||
file=sys.stderr,
|
||||
)
|
||||
print(data)
|
||||
raise (e)
|
||||
# success = False
|
||||
# return success, False
|
||||
|
||||
# note that total failures is fail_count, fail_and_error count is JUST errors (and every error is also a failure)
|
||||
return success, test_count, pass_count, fail_count, fail_and_error_count
|
||||
|
||||
|
||||
def calculate_pass_rate(pass_count: int, test_count: int) -> float:
|
||||
if test_count == 0:
|
||||
# Assume this means 100% pass rate to avoid divide by zero
|
||||
pass_rate = 1
|
||||
else:
|
||||
pass_rate = pass_count / test_count
|
||||
return pass_rate
|
||||
|
||||
|
||||
def print_summary(
|
||||
test_count: int, pass_count: int, fail_count: int, error_count: int
|
||||
) -> None:
|
||||
print(
|
||||
"Summary:"
|
||||
f"\n\tTotal Tests: {test_count}"
|
||||
f"\n\tTotal Pass : {pass_count}"
|
||||
f"\n\tTotal Fail : {fail_count} ({error_count} of these were ERRORS))"
|
||||
f"\n\tPass Rate : {calculate_pass_rate(pass_count, test_count):.3f}"
|
||||
)
|
||||
|
||||
|
||||
def exit_with_status(
|
||||
test_pass: bool, test_count: int, pass_count: int, fail_count: int, error_count: int
|
||||
) -> None:
|
||||
if not test_pass:
|
||||
print("Result: FAIL")
|
||||
# print("DURING TESTING, THIS WILL STILL EXIT WITH AN EXIT CODE OF 0 (SUCCESS) TO ALLOW THE WORKFLOW "
|
||||
# "TO PASS AND CI/CD TO CONTINUE. THIS WILL BE CHANGED IN A FUTURE VERSION.")
|
||||
# sys.exit(0)
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("Result: PASS!")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def finish(
|
||||
test_pass: bool, test_count: int, pass_count: int, fail_count: int, error_count: int
|
||||
) -> None:
|
||||
print_summary(test_count, pass_count, fail_count, error_count)
|
||||
exit_with_status(test_pass, test_count, pass_count, fail_count, error_count)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Results Merger")
|
||||
parser.add_argument(
|
||||
"-f",
|
||||
"--files",
|
||||
type=argparse.FileType("r"),
|
||||
required=True,
|
||||
nargs="+",
|
||||
help="The json files you would like to combine into a single file",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--output_filename",
|
||||
type=str,
|
||||
required=True,
|
||||
help="The name of the output file",
|
||||
)
|
||||
parser.add_argument("--smoketest", action=argparse.BooleanOptionalAction)
|
||||
args = parser.parse_args()
|
||||
|
||||
all_data = OrderedDict()
|
||||
try:
|
||||
print("We will summarize the files: %s" % (str([f.name for f in args.files])))
|
||||
for f in args.files:
|
||||
if not f.name.endswith(".json"):
|
||||
print(
|
||||
"Error: passed in file must end in .json - you passed in [%s].\n\tQuitting..."
|
||||
% (f.name)
|
||||
)
|
||||
sys.exit(1)
|
||||
data = json.loads(f.read())
|
||||
if "baseline" in all_data:
|
||||
# everything has the same baseline, only need to do it once
|
||||
pass
|
||||
else:
|
||||
all_data["baseline"] = data["baseline"]
|
||||
if "results" in all_data:
|
||||
# this is a list of dictionaries, so add to it
|
||||
all_data["results"].extend(data["results"])
|
||||
else:
|
||||
all_data["results"] = data["results"]
|
||||
|
||||
IGNORE_MESSAGES = [
|
||||
"Model does not exist", # model not generated by baseline
|
||||
"Data model 'Identity_Management' was not found", # missing datamodel included with es
|
||||
"get_asset", # missing macro included with es
|
||||
"Failed to load model", # when running a model that has not been downloaded separately
|
||||
"UEBA", # Another missing ES asset
|
||||
]
|
||||
if args.smoketest:
|
||||
new_results = []
|
||||
for result in all_data["results"]:
|
||||
if result.get("detection_error", None):
|
||||
message = result.get("detection_error", None)
|
||||
ignore = False
|
||||
for ignore_message in IGNORE_MESSAGES:
|
||||
if ignore_message in message:
|
||||
ignore = True
|
||||
break
|
||||
if not ignore:
|
||||
new_results.append(result)
|
||||
pass
|
||||
|
||||
all_data["results"] = new_results
|
||||
|
||||
test_pass, test_count, pass_count, fail_count, error_count = outputResultsJSON(
|
||||
args.output_filename, all_data["results"], all_data["baseline"]
|
||||
)
|
||||
finish(test_pass, test_count, pass_count, fail_count, error_count)
|
||||
|
||||
except Exception as e:
|
||||
print("Error generating the summary file: [%s].\n\tQuitting..." % (str(e)))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,149 +0,0 @@
|
||||
{
|
||||
"apps": {
|
||||
"ADD_ON_FOR_LINUX_SYSMON": {
|
||||
"app_number": 6176,
|
||||
"app_version": "1.0.4",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/add-on-for-linux-sysmon_104.tgz"
|
||||
},
|
||||
"PALO_ALTO_NETWORKS_ADD_ON_FOR_SPLUNK": {
|
||||
"app_number": 2757,
|
||||
"app_version": "8.1.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/palo-alto-networks-add-on-for-splunk_810.tgz"
|
||||
},
|
||||
"SPLUNK_ADD_ON_FOR_AMAZON_KINESIS_FIREHOSE": {
|
||||
"app_number": 3719,
|
||||
"app_version": "1.3.2",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-amazon-kinesis-firehose_132.tgz"
|
||||
},
|
||||
"SPLUNK_ADD_ON_FOR_GOOGLE_CLOUD_PLATFORM": {
|
||||
"app_number": 3088,
|
||||
"app_version": "4.3.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-google-cloud-platform_430.tgz"
|
||||
},
|
||||
"SPLUNK_ADD_ON_FOR_GOOGLE_WORKSPACE": {
|
||||
"app_number": 3110,
|
||||
"app_version": "2.6.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-google-workspace_260.tgz"
|
||||
},
|
||||
"SPLUNK_ADD_ON_FOR_MICROSOFT_OFFICE_365": {
|
||||
"app_number": 4055,
|
||||
"app_version": "4.3.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-microsoft-office-365_430.tgz"
|
||||
},
|
||||
"SPLUNK_ADD_ON_FOR_MICROSOFT_WINDOWS": {
|
||||
"app_number": 742,
|
||||
"app_version": "8.8.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-microsoft-windows_880.tgz"
|
||||
},
|
||||
"SPLUNK_ADD_ON_FOR_NGINX": {
|
||||
"app_number": 3258,
|
||||
"app_version": "3.2.1",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-nginx_321.tgz"
|
||||
},
|
||||
"SPLUNK_ADD_ON_FOR_STREAM_FORWARDERS": {
|
||||
"app_number": 5238,
|
||||
"app_version": "8.1.1",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-stream-forwarders_811.tgz"
|
||||
},
|
||||
"SPLUNK_ADD_ON_FOR_STREAM_WIRE_DATA": {
|
||||
"app_number": 5234,
|
||||
"app_version": "8.1.1",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-stream-wire-data_811.tgz"
|
||||
},
|
||||
"SPLUNK_ADD_ON_FOR_SYSMON": {
|
||||
"app_number": 5709,
|
||||
"app_version": "3.1.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-sysmon_310.tgz"
|
||||
},
|
||||
"SPLUNK_ADD_ON_FOR_UNIX_AND_LINUX": {
|
||||
"app_number": 833,
|
||||
"app_version": "9.0.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-unix-and-linux_900.tgz"
|
||||
},
|
||||
"SPLUNK_COMMON_INFORMATION_MODEL": {
|
||||
"app_number": 1621,
|
||||
"app_version": "5.2.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-common-information-model-cim_520.tgz"
|
||||
},
|
||||
"SPLUNK_ES_CONTENT_UPDATE": {
|
||||
"app_number": 3449,
|
||||
"app_version": null,
|
||||
"local_path": null
|
||||
},
|
||||
"SPLUNK_MACHINE_LEARNING_TOOLKIT": {
|
||||
"app_number": 2890,
|
||||
"app_version": "5.4.1",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-machine-learning-toolkit_541.tgz"
|
||||
},
|
||||
"SPLUNK_TA_FIX_WINDOWS": {
|
||||
"app_number": 9999,
|
||||
"app_version": "1.0.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/Splunk_TA_fix_windows.tgz"
|
||||
},
|
||||
"SPLUNK_TA_FOR_IIS": {
|
||||
"app_number": 3185,
|
||||
"app_version": "1.2.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-microsoft-iis_120.tgz"
|
||||
},
|
||||
"SPLUNK_TA_FOR_SURICATA": {
|
||||
"app_number": 4242,
|
||||
"app_version": "2.3.4",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/ta-for-suricata_234.tgz"
|
||||
},
|
||||
"SPLUNK_TA_FOR_ZEEK": {
|
||||
"app_number": 5466,
|
||||
"app_version": "1.0.5",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/ta-for-zeek_105.tgz"
|
||||
},
|
||||
"SPLUNK_TA_MICROSOFT_CLOUD_SERVICES": {
|
||||
"app_number": 3110,
|
||||
"app_version": "5.2.1",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-microsoft-cloud-services_521.tgz"
|
||||
},
|
||||
"Splunk Add-on for CrowdStrike FDR": {
|
||||
"app_number": 5579,
|
||||
"app_version": "1.4.0",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-crowdstrike-fdr_140.tgz"
|
||||
},
|
||||
"URL_TOOLBOX": {
|
||||
"app_number": 2734,
|
||||
"app_version": "1.9.2",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/url-toolbox_192.tgz"
|
||||
},
|
||||
"Splunk_TA_okta_identity_cloud": {
|
||||
"app_number": 6553,
|
||||
"app_version": "2.1.0,",
|
||||
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-okta-identity-cloud_210.tgz"
|
||||
}
|
||||
},
|
||||
"branch": "BRANCH_DOES_NOT_EXIST_USE_CLI_ARGUMENT",
|
||||
"commit_hash": null,
|
||||
"container_tag": "latest",
|
||||
"detections_list": null,
|
||||
"folders": [
|
||||
"endpoint",
|
||||
"cloud",
|
||||
"network",
|
||||
"web",
|
||||
"application"
|
||||
],
|
||||
"interactive": false,
|
||||
"local_base_container_name": "splunk_test_%d",
|
||||
"mock": false,
|
||||
"mode": "changes",
|
||||
"no_interactive_failure": true,
|
||||
"num_containers": 10,
|
||||
"persist_security_content": false,
|
||||
"pr_number": null,
|
||||
"reuse_image": true,
|
||||
"show_splunk_app_password": false,
|
||||
"splunk_app_password": null,
|
||||
"splunk_container_apps_directory": "/opt/splunk/etc/apps",
|
||||
"splunkbase_password": null,
|
||||
"splunkbase_username": null,
|
||||
"types": [
|
||||
"Anomaly",
|
||||
"Hunting",
|
||||
"TTP"
|
||||
]
|
||||
}
|
||||
@@ -1,25 +1,185 @@
|
||||
build:
|
||||
#Temporary fix to support testing. The following
|
||||
#line will be reverted soon
|
||||
title: DA-ESS-ContentUpdate
|
||||
name: DA-ESS-ContentUpdate
|
||||
path_root: dist
|
||||
path: .
|
||||
app:
|
||||
uid: 3449
|
||||
title: ES Content Updates
|
||||
appid: DA-ESS-ContentUpdate
|
||||
version: 4.35.0
|
||||
description: Explore the Analytic Stories included with ES Content Updates.
|
||||
prefix: ESCU
|
||||
build: 004210
|
||||
version: 4.30.0
|
||||
label: ES Content Updates
|
||||
label: ESCU
|
||||
author_name: Splunk Threat Research Team
|
||||
author_email: research@splunk.com
|
||||
author_company: Splunk
|
||||
description: Explore the Analytic Stories included with ES Content Updates.
|
||||
splunk_app: {}
|
||||
json_objects: null
|
||||
ba_objects: null
|
||||
build_ssa:
|
||||
path_root: 'dist/ssa'
|
||||
build_api:
|
||||
path_root: 'dist/api'
|
||||
enrichments:
|
||||
attack_enrichment: false
|
||||
cve_enrichment: false
|
||||
splunk_app_enrichment: false
|
||||
enrichments: false
|
||||
build_app: true
|
||||
build_api: true
|
||||
build_ssa: false
|
||||
build_path: dist
|
||||
test_instance:
|
||||
splunk_app_username: admin
|
||||
instance_address: localhost
|
||||
hec_port: 8088
|
||||
web_ui_port: 8000
|
||||
api_port: 8089
|
||||
full_image_path: registry.hub.docker.com/splunk/splunk:latest
|
||||
container_settings:
|
||||
leave_running: true
|
||||
num_containers: 1
|
||||
mode: {}
|
||||
splunk_api_username: null
|
||||
post_test_behavior: pause_on_failure
|
||||
apps:
|
||||
# - uid: 263
|
||||
# title: Splunk Enterprise Security
|
||||
# appid: SplunkEnterpriseSecuritySuite
|
||||
# version: 7.3.1
|
||||
# description: description of app
|
||||
# hardcoded_path: ~/Downloads/splunk-enterprise-security_731.spl
|
||||
- uid: 1621
|
||||
title: Splunk Common Information Model (CIM)
|
||||
appid: Splunk_SA_CIM
|
||||
version: 5.3.2
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-common-information-model-cim_532.tgz
|
||||
- uid: 6553
|
||||
title: Splunk Add-on for Okta Identity Cloud
|
||||
appid: Splunk_TA_okta_identity_cloud
|
||||
version: 2.2.0
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-okta-identity-cloud_220.tgz
|
||||
- uid: 6652
|
||||
title: Add-on for Linux Sysmon
|
||||
appid: Splunk_TA_linux_sysmon
|
||||
version: 1.0.0
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-add-on-for-sysmon-for-linux_100.tgz
|
||||
- uid: null
|
||||
title: Splunk Fix XmlWinEventLog HEC Parsing
|
||||
appid: Splunk_FIX_XMLWINEVENTLOG_HEC_PARSING
|
||||
version: '0.1'
|
||||
description: This TA is required for replaying Windows Data into the Test Environment.
|
||||
The Default TA does not include logic for properly splitting multiple log events
|
||||
in a single file. In production environments, this logic is applied by the Universal
|
||||
Forwarder.
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/Splunk_TA_fix_windows.tgz
|
||||
- uid: 742
|
||||
title: Splunk Add-on for Microsoft Windows
|
||||
appid: SPLUNK_ADD_ON_FOR_MICROSOFT_WINDOWS
|
||||
version: 8.8.0
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-microsoft-windows_880.tgz
|
||||
- uid: 5709
|
||||
title: Splunk Add-on for Sysmon
|
||||
appid: Splunk_TA_microsoft_sysmon
|
||||
version: 4.0.1
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-sysmon_401.tgz
|
||||
- uid: 833
|
||||
title: Splunk Add-on for Unix and Linux
|
||||
appid: Splunk_TA_nix
|
||||
version: 9.0.0
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-unix-and-linux_900.tgz
|
||||
- uid: 5579
|
||||
title: Splunk Add-on for CrowdStrike FDR
|
||||
appid: Splunk_TA_CrowdStrike_FDR
|
||||
version: 1.5.0
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-crowdstrike-fdr_150.tgz
|
||||
- uid: 3185
|
||||
title: Splunk Add-on for Microsoft IIS
|
||||
appid: SPLUNK_TA_FOR_IIS
|
||||
version: 1.3.0
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-microsoft-iis_130.tgz
|
||||
- uid: 4242
|
||||
title: TA for Suricata
|
||||
appid: SPLUNK_TA_FOR_SURICATA
|
||||
version: 2.3.4
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/ta-for-suricata_234.tgz
|
||||
- uid: 5466
|
||||
title: TA for Zeek
|
||||
appid: SPLUNK_TA_FOR_ZEEK
|
||||
version: 1.0.8
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/ta-for-zeek_108.tgz
|
||||
- uid: 3258
|
||||
title: Splunk Add-on for NGINX
|
||||
appid: SPLUNK_ADD_ON_FOR_NGINX
|
||||
version: 3.2.2
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-nginx_322.tgz
|
||||
- uid: 5238
|
||||
title: Splunk Add-on for Stream Forwarders
|
||||
appid: SPLUNK_ADD_ON_FOR_STREAM_FORWARDERS
|
||||
version: 8.1.1
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-stream-forwarders_811.tgz
|
||||
- uid: 5234
|
||||
title: Splunk Add-on for Stream Wire Data
|
||||
appid: SPLUNK_ADD_ON_FOR_STREAM_WIRE_DATA
|
||||
version: 8.1.1
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-stream-wire-data_811.tgz
|
||||
- uid: 2757
|
||||
title: Palo Alto Networks Add-on for Splunk
|
||||
appid: PALO_ALTO_NETWORKS_ADD_ON_FOR_SPLUNK
|
||||
version: 8.1.1
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/palo-alto-networks-add-on-for-splunk_811.tgz
|
||||
- uid: 3865
|
||||
title: Zscaler Technical Add-On for Splunk
|
||||
appid: Zscaler_CIM
|
||||
version: 4.0.3
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/zscaler-technical-add-on-for-splunk_403.tgz
|
||||
- uid: 3719
|
||||
title: Splunk Add-on for Amazon Kinesis Firehose
|
||||
appid: SPLUNK_ADD_ON_FOR_AMAZON_KINESIS_FIREHOSE
|
||||
version: 1.3.2
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-amazon-kinesis-firehose_132.tgz
|
||||
- uid: 1876
|
||||
title: Splunk Add-on for AWS
|
||||
appid: Splunk_TA_aws
|
||||
version: 7.5.0
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-amazon-web-services-aws_750.tgz
|
||||
- uid: 3088
|
||||
title: Splunk Add-on for Google Cloud Platform
|
||||
appid: SPLUNK_ADD_ON_FOR_GOOGLE_CLOUD_PLATFORM
|
||||
version: 4.5.0
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-google-cloud-platform_450.tgz
|
||||
- uid: 5556
|
||||
title: Splunk Add-on for Google Workspace
|
||||
appid: SPLUNK_ADD_ON_FOR_GOOGLE_WORKSPACE
|
||||
version: 2.7.0
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-google-workspace_270.tgz
|
||||
- uid: 3110
|
||||
title: Splunk Add-on for Microsoft Cloud Services
|
||||
appid: SPLUNK_TA_MICROSOFT_CLOUD_SERVICES
|
||||
version: 5.2.2
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-microsoft-cloud-services_522.tgz
|
||||
- uid: 4055
|
||||
title: Splunk Add-on for Microsoft Office 365
|
||||
appid: SPLUNK_ADD_ON_FOR_MICROSOFT_OFFICE_365
|
||||
version: 4.5.1
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-microsoft-office-365_451.tgz
|
||||
- uid: 2890
|
||||
title: Splunk Machine Learning Toolkit
|
||||
appid: SPLUNK_MACHINE_LEARNING_TOOLKIT
|
||||
version: 5.4.1
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-machine-learning-toolkit_541.tgz
|
||||
- uid: 2734
|
||||
title: URL Toolbox
|
||||
appid: URL_TOOLBOX
|
||||
version: 1.9.2
|
||||
description: description of app
|
||||
hardcoded_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/url-toolbox_192.tgz
|
||||
githash: d6fac80e6d50ae06b40f91519a98489d4ce3a3fd
|
||||
|
||||
@@ -1,253 +0,0 @@
|
||||
version_control_config:
|
||||
target_branch: develop
|
||||
infrastructure_config:
|
||||
infrastructure_type: container
|
||||
full_image_path: registry.hub.docker.com/splunk/splunk:latest
|
||||
post_test_behavior: pause_on_failure
|
||||
mode: changes
|
||||
detections_list: null
|
||||
splunkbase_username: null
|
||||
splunkbase_password: null
|
||||
apps:
|
||||
- uid: 1621
|
||||
appid: Splunk_SA_CIM
|
||||
title: Splunk Common Information Model (CIM)
|
||||
description: null
|
||||
release: 5.2.0
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-common-information-model-cim_520.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
- uid: 6553
|
||||
appid: Splunk_TA_okta_identity_cloud
|
||||
title: Splunk Add-on for Okta Identity Cloud
|
||||
description: null
|
||||
release: 2.1.0
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-okta-identity-cloud_210.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
- uid: 6176
|
||||
appid: Splunk_TA_linux_sysmon
|
||||
title: Add-on for Linux Sysmon
|
||||
description: null
|
||||
release: 1.0.4
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/add-on-for-linux-sysmon_104.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
# The Following TA does NOT exist on Splunkbase. It fixes a parsing issue that occurs when raw xmlwineventlog events
|
||||
# are replayed together at a HEC endpoint. This issue does not exist when logs are sent by a Universal Forwarder
|
||||
- uid: 9999
|
||||
appid: Splunk_FIX_XMLWINEVENTLOG_HEC_PARSING
|
||||
title: Splunk Fix XmlWinEventLog HEC Parsing
|
||||
description: null
|
||||
release: 0.1
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/Splunk_TA_fix_windows.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
- uid: 742
|
||||
appid: SPLUNK_ADD_ON_FOR_MICROSOFT_WINDOWS
|
||||
title: Splunk Add-on for Microsoft Windows
|
||||
description: null
|
||||
release: 8.8.0
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-microsoft-windows_880.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
- uid: 5709
|
||||
appid: Splunk_TA_microsoft_sysmon
|
||||
title: Splunk Add-on for Sysmon
|
||||
description: null
|
||||
release: 4.0.0
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-sysmon_400.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
- uid: 833
|
||||
appid: Splunk_TA_nix
|
||||
title: Splunk Add-on for Unix and Linux
|
||||
description: null
|
||||
release: 9.0.0
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-unix-and-linux_900.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
- uid: 5579
|
||||
appid: Splunk_TA_CrowdStrike_FDR
|
||||
title: Splunk Add-on for CrowdStrike FDR
|
||||
description: null
|
||||
release: 1.5.0
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-crowdstrike-fdr_150.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
- uid: 3185
|
||||
appid: SPLUNK_TA_FOR_IIS
|
||||
title: Splunk Add-on for Microsoft IIS
|
||||
description: null
|
||||
release: 1.3.0
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-microsoft-iis_130.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
- uid: 4242
|
||||
appid: SPLUNK_TA_FOR_SURICATA
|
||||
title: TA for Suricata
|
||||
description: null
|
||||
release: 2.3.4
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/ta-for-suricata_234.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
- uid: 5466
|
||||
appid: SPLUNK_TA_FOR_ZEEK
|
||||
title: TA for Zeek
|
||||
description: null
|
||||
release: 1.0.6
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/ta-for-zeek_106.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
- uid: 3258
|
||||
appid: SPLUNK_ADD_ON_FOR_NGINX
|
||||
title: Splunk Add-on for NGINX
|
||||
description: null
|
||||
release: 3.2.2
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-nginx_322.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
- uid: 5238
|
||||
appid: SPLUNK_ADD_ON_FOR_STREAM_FORWARDERS
|
||||
title: Splunk Add-on for Stream Forwarders
|
||||
description: null
|
||||
release: 8.1.1
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-stream-forwarders_811.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
- uid: 5234
|
||||
appid: SPLUNK_ADD_ON_FOR_STREAM_WIRE_DATA
|
||||
title: Splunk Add-on for Stream Wire Data
|
||||
description: null
|
||||
release: 8.1.1
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-stream-wire-data_811.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
- uid: 2757
|
||||
appid: PALO_ALTO_NETWORKS_ADD_ON_FOR_SPLUNK
|
||||
title: Palo Alto Networks Add-on for Splunk
|
||||
description: null
|
||||
release: 8.1.1
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/palo-alto-networks-add-on-for-splunk_811.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
- uid: 3865
|
||||
appid: TA-Zscaler_CIM
|
||||
title: Zscaler Technical Add-On for Splunk
|
||||
description: null
|
||||
release: 4.0.3
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/zscaler-technical-add-on-for-splunk_403.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
- uid: 3719
|
||||
appid: SPLUNK_ADD_ON_FOR_AMAZON_KINESIS_FIREHOSE
|
||||
title: Splunk Add-on for Amazon Kinesis Firehose
|
||||
description: null
|
||||
release: 1.3.2
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-amazon-kinesis-firehose_132.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
- uid: 1876
|
||||
appid: Splunk_TA_aws
|
||||
title: Splunk Add-on for AWS
|
||||
description: null
|
||||
release: 7.5.0
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-amazon-web-services-aws_750.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
- uid: 3088
|
||||
appid: SPLUNK_ADD_ON_FOR_GOOGLE_CLOUD_PLATFORM
|
||||
title: Splunk Add-on for Google Cloud Platform
|
||||
description: null
|
||||
release: 4.4.0
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-google-cloud-platform_440.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
- uid: 5556
|
||||
appid: SPLUNK_ADD_ON_FOR_GOOGLE_WORKSPACE
|
||||
title: Splunk Add-on for Google Workspace
|
||||
description: null
|
||||
release: 2.6.3
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-google-workspace_263.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
- uid: 3110
|
||||
appid: SPLUNK_TA_MICROSOFT_CLOUD_SERVICES
|
||||
title: Splunk Add-on for Microsoft Cloud Services
|
||||
description: null
|
||||
release: 5.2.2
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-microsoft-cloud-services_522.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
- uid: 4055
|
||||
appid: SPLUNK_ADD_ON_FOR_MICROSOFT_OFFICE_365
|
||||
title: Splunk Add-on for Microsoft Office 365
|
||||
description: null
|
||||
release: 4.5.1
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-microsoft-office-365_451.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
- uid: 2890
|
||||
appid: SPLUNK_MACHINE_LEARNING_TOOLKIT
|
||||
title: Splunk Machine Learning Toolkit
|
||||
description: null
|
||||
release: 5.4.1
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-machine-learning-toolkit_541.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
- uid: 2734
|
||||
appid: URL_TOOLBOX
|
||||
title: URL Toolbox
|
||||
description: null
|
||||
release: 1.9.2
|
||||
local_path: null
|
||||
http_path: https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/url-toolbox_192.tgz
|
||||
splunkbase_path: null
|
||||
environment_path: ENVIRONMENT_PATH_NOT_SET
|
||||
force_local: false
|
||||
@@ -0,0 +1,38 @@
|
||||
name: PingID
|
||||
id: 17890675-61c1-40bd-a88e-6a8e9e246b43
|
||||
author: Patrick Bareiss, Splunk
|
||||
source: XmlWinEventLog:Security
|
||||
sourcetype: XmlWinEventLog
|
||||
supported_TA: {}
|
||||
event_names: []
|
||||
fields:
|
||||
- _time
|
||||
- actors{}.name
|
||||
- actors{}.type
|
||||
- date_hour
|
||||
- date_mday
|
||||
- date_minute
|
||||
- date_month
|
||||
- date_second
|
||||
- date_wday
|
||||
- date_year
|
||||
- date_zone
|
||||
- extracted_source
|
||||
- host
|
||||
- id
|
||||
- index
|
||||
- linecount
|
||||
- punct
|
||||
- recorded
|
||||
- resources{}.ipaddress
|
||||
- resources{}.websession
|
||||
- result.message
|
||||
- result.status
|
||||
- source
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- timeendpos
|
||||
- timestartpos
|
||||
example_log:
|
||||
'{"source":"PINGID","id":"b2eb1fef-651b-11ee-b38b-0ac7a554ed19","recorded":"2023-10-05T14:10:53.538Z","actors":[{"type":"user","name":"victim_user"}],"resources":[{"ipaddress":"174.235.80.142","websession":"webs_ijkF-T_bAC_G3w2TfvdpAEQeC545KFlqVFOsolCXdjo"}],"result":{"status":"SUCCESS","message":"Device
|
||||
Paired SMS \"Mobile 1\""}}'
|
||||
@@ -0,0 +1,34 @@
|
||||
name: Splunk
|
||||
id: d8a2c791-460b-4756-a8e5-ecade77b21e3
|
||||
author: Patrick Bareiss, Splunk
|
||||
source: splunkd_ui_access.log
|
||||
sourcetype: splunkd_ui_access
|
||||
supported_TA: {}
|
||||
event_names: []
|
||||
fields:
|
||||
- _time
|
||||
- action
|
||||
- date_hour
|
||||
- date_mday
|
||||
- date_minute
|
||||
- date_month
|
||||
- date_second
|
||||
- date_wday
|
||||
- date_year
|
||||
- date_zone
|
||||
- dest
|
||||
- host
|
||||
- index
|
||||
- info
|
||||
- linecount
|
||||
- punct
|
||||
- source
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- timeendpos
|
||||
- timestamp
|
||||
- timestartpos
|
||||
- user
|
||||
example_log:
|
||||
"Audit:[timestamp=01-25-2023 22:08:54.818, user=admin, action=search,
|
||||
info=granted REST: /search/jobs/rt_1674684525.24/events]"
|
||||
@@ -0,0 +1,27 @@
|
||||
name: Endpoint.Filesystem
|
||||
prefix: Filesystem
|
||||
fields:
|
||||
- action
|
||||
- dest
|
||||
- dest_bunit
|
||||
- dest_category
|
||||
- dest_priority
|
||||
- dest_requires_av
|
||||
- dest_should_timesync
|
||||
- dest_should_update
|
||||
- file_access_time
|
||||
- file_create_time
|
||||
- file_hash
|
||||
- file_modify_time
|
||||
- file_name
|
||||
- file_path
|
||||
- file_acl
|
||||
- file_size
|
||||
- process_guid
|
||||
- process_id
|
||||
- tag
|
||||
- user
|
||||
- user_bunit
|
||||
- user_category
|
||||
- user_priority
|
||||
- vendor_product
|
||||
@@ -0,0 +1,39 @@
|
||||
name: Endpoint.Processes
|
||||
prefix: Processes
|
||||
fields:
|
||||
- action
|
||||
- cpu_load_percent
|
||||
- dest
|
||||
- dest_bunit
|
||||
- dest_category
|
||||
- dest_is_expected
|
||||
- dest_priority
|
||||
- dest_requires_av
|
||||
- dest_should_timesync
|
||||
- dest_should_update
|
||||
- loaded_file
|
||||
- mem_used
|
||||
- original_file_name
|
||||
- os
|
||||
- parent_process
|
||||
- parent_process_exec
|
||||
- parent_process_id
|
||||
- parent_process_guid
|
||||
- parent_process_name
|
||||
- parent_process_path
|
||||
- process
|
||||
- process_current_directory
|
||||
- process_exec
|
||||
- process_hash
|
||||
- process_guid
|
||||
- process_id
|
||||
- process_integrity_level
|
||||
- process_name
|
||||
- process_path
|
||||
- tag
|
||||
- user
|
||||
- user_id
|
||||
- user_bunit
|
||||
- user_category
|
||||
- user_priority
|
||||
- vendor_product
|
||||
@@ -0,0 +1,27 @@
|
||||
name: Endpoint.Registry
|
||||
prefix: Registry
|
||||
fields:
|
||||
- action
|
||||
- dest
|
||||
- dest_bunit
|
||||
- dest_category
|
||||
- dest_priority
|
||||
- dest_requires_av
|
||||
- dest_should_timesync
|
||||
- dest_should_update
|
||||
- process_guid
|
||||
- process_id
|
||||
- registry_hive
|
||||
- registry_path
|
||||
- registry_key_name
|
||||
- registry_value_data
|
||||
- registry_value_name
|
||||
- registry_value_text
|
||||
- registry_value_type
|
||||
- status
|
||||
- tag
|
||||
- user
|
||||
- user_bunit
|
||||
- user_category
|
||||
- user_priority
|
||||
- vendor_product
|
||||
@@ -0,0 +1,35 @@
|
||||
name: Endpoint.Services
|
||||
prefix: Services
|
||||
fields:
|
||||
- description
|
||||
- dest
|
||||
- dest_bunit
|
||||
- dest_category
|
||||
- dest_is_expected
|
||||
- dest_priority
|
||||
- dest_requires_av
|
||||
- dest_should_timesync
|
||||
- dest_should_update
|
||||
- process_guid
|
||||
- process_id
|
||||
- service
|
||||
- service_dll
|
||||
- service_dll_path
|
||||
- service_dll_hash
|
||||
- service_dll_signature_exists
|
||||
- service_dll_signature_verified
|
||||
- service_exec
|
||||
- service_hash
|
||||
- service_id
|
||||
- service_name
|
||||
- service_path
|
||||
- service_signature_exists
|
||||
- service_signature_verified
|
||||
- start_mode
|
||||
- status
|
||||
- tag
|
||||
- user
|
||||
- user_bunit
|
||||
- user_category
|
||||
- user_priority
|
||||
- vendor_product
|
||||
@@ -0,0 +1,32 @@
|
||||
name: Network_Resolution.DNS
|
||||
prefix: DNS
|
||||
fields:
|
||||
- additional_answer_count
|
||||
- answer
|
||||
- answer_count
|
||||
- authority_answer_count
|
||||
- dest
|
||||
- dest_bunit
|
||||
- dest_category
|
||||
- dest_port
|
||||
- dest_priority
|
||||
- duration
|
||||
- message_type
|
||||
- name
|
||||
- query
|
||||
- query_count
|
||||
- query_type
|
||||
- record_type
|
||||
- reply_code
|
||||
- reply_code_id
|
||||
- response_time
|
||||
- src
|
||||
- src_bunit
|
||||
- src_category
|
||||
- src_port
|
||||
- src_priority
|
||||
- tag
|
||||
- transaction_id
|
||||
- transport
|
||||
- ttl
|
||||
- vendor_product
|
||||
@@ -0,0 +1,66 @@
|
||||
name: Network_Traffic.All_Traffic
|
||||
prefix: All_Traffic
|
||||
fields:
|
||||
- action
|
||||
- app
|
||||
- bytes
|
||||
- bytes_in
|
||||
- bytes_out
|
||||
- channel
|
||||
- dest
|
||||
- dest_bunit
|
||||
- dest_category
|
||||
- dest_interface
|
||||
- dest_ip
|
||||
- dest_mac
|
||||
- dest_port
|
||||
- dest_priority
|
||||
- dest_translated_ip
|
||||
- dest_translated_port
|
||||
- dest_zone
|
||||
- direction
|
||||
- duration
|
||||
- dvc
|
||||
- dvc_bunit
|
||||
- dvc_category
|
||||
- dvc_ip
|
||||
- dvc_mac
|
||||
- dvc_priority
|
||||
- dvc_zone
|
||||
- flow_id
|
||||
- icmp_code
|
||||
- icmp_type
|
||||
- packets
|
||||
- packets_in
|
||||
- packets_out
|
||||
- process_id
|
||||
- protocol
|
||||
- protocol_version
|
||||
- response_time
|
||||
- rule
|
||||
- session_id
|
||||
- src
|
||||
- src_bunit
|
||||
- src_category
|
||||
- src_interface
|
||||
- src_ip
|
||||
- src_mac
|
||||
- src_port
|
||||
- src_priority
|
||||
- src_translated_ip
|
||||
- src_translated_port
|
||||
- src_zone
|
||||
- ssid
|
||||
- tag
|
||||
- tcp_flag
|
||||
- transport
|
||||
- tos
|
||||
- ttl
|
||||
- user
|
||||
- user_bunit
|
||||
- user_category
|
||||
- user_priority
|
||||
- vendor_account
|
||||
- vendor_product
|
||||
- vlan
|
||||
- wifi
|
||||
@@ -0,0 +1,41 @@
|
||||
name: Web.Web
|
||||
prefix: Web
|
||||
fields:
|
||||
- action
|
||||
- app
|
||||
- bytes
|
||||
- bytes_in
|
||||
- bytes_out
|
||||
- cached
|
||||
- category
|
||||
- cookie
|
||||
- dest
|
||||
- dest_bunit
|
||||
- dest_category
|
||||
- dest_priority
|
||||
- dest_port
|
||||
- duration
|
||||
- http_content_type
|
||||
- http_method
|
||||
- http_referrer
|
||||
- http_referrer_domain
|
||||
- http_user_agent
|
||||
- http_user_agent_length
|
||||
- response_time
|
||||
- site
|
||||
- src
|
||||
- src_bunit
|
||||
- src_category
|
||||
- src_priority
|
||||
- status
|
||||
- tag
|
||||
- uri_path
|
||||
- uri_query
|
||||
- url
|
||||
- url_domain
|
||||
- url_length
|
||||
- user
|
||||
- user_bunit
|
||||
- user_category
|
||||
- user_priority
|
||||
- vendor_product
|
||||
@@ -0,0 +1,229 @@
|
||||
name: AWS CloudTrail
|
||||
id: aa8d90bf-8ab1-4a9f-8c1b-24a67b1cd0b0
|
||||
author: Patrick Bareiss, Splunk
|
||||
source: aws_cloudtrail
|
||||
sourcetype: aws:cloudtrail
|
||||
separator: eventName
|
||||
supported_TA:
|
||||
name: Splunk Add-on for Amazon Web Services (AWS)
|
||||
version: 7.4.1
|
||||
url: https://splunkbase.splunk.com/app/1876
|
||||
event_names:
|
||||
- event_name: AWS CloudTrail
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail.yml
|
||||
- event_name: AWS CloudTrail AssumeRoleWithSAML
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_AssumeRoleWithSAML.yml
|
||||
- event_name: AWS CloudTrail ConsoleLogin
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_ConsoleLogin.yml
|
||||
- event_name: AWS CloudTrail CopyObject
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_CopyObject.yml
|
||||
- event_name: AWS CloudTrail CreateAccessKey
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_CreateAccessKey.yml
|
||||
- event_name: AWS CloudTrail CreateKey
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_CreateKey.yml
|
||||
- event_name: AWS CloudTrail CreateLoginProfile
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_CreateLoginProfile.yml
|
||||
- event_name: AWS CloudTrail CreateNetworkAclEntry
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_CreateNetworkAclEntry.yml
|
||||
- event_name: AWS CloudTrail CreatePolicyVersion
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_CreatePolicyVersion.yml
|
||||
- event_name: AWS CloudTrail CreateSnapshot
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_CreateSnapshot.yml
|
||||
- event_name: AWS CloudTrail CreateTask
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_CreateTask.yml
|
||||
- event_name: AWS CloudTrail CreateVirtualMFADevice
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_CreateVirtualMFADevice.yml
|
||||
- event_name: AWS CloudTrail DeactivateMFADevice
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_DeactivateMFADevice.yml
|
||||
- event_name: AWS CloudTrail DeleteAccountPasswordPolicy
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_DeleteAccountPasswordPolicy.yml
|
||||
- event_name: AWS CloudTrail DeleteAlarms
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_DeleteAlarms.yml
|
||||
- event_name: AWS CloudTrail DeleteDetector
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_DeleteDetector.yml
|
||||
- event_name: AWS CloudTrail DeleteGroup
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_DeleteGroup.yml
|
||||
- event_name: AWS CloudTrail DeleteIPSet
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_DeleteIPSet.yml
|
||||
- event_name: AWS CloudTrail DeleteLogGroup
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_DeleteLogGroup.yml
|
||||
- event_name: AWS CloudTrail DeleteLogStream
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_DeleteLogStream.yml
|
||||
- event_name: AWS CloudTrail DeleteLoggingConfiguration
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_DeleteLoggingConfiguration.yml
|
||||
- event_name: AWS CloudTrail DeleteNetworkAclEntry
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_DeleteNetworkAclEntry.yml
|
||||
- event_name: AWS CloudTrail DeletePolicy
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_DeletePolicy.yml
|
||||
- event_name: AWS CloudTrail DeleteRule
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_DeleteRule.yml
|
||||
- event_name: AWS CloudTrail DeleteRuleGroup
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_DeleteRuleGroup.yml
|
||||
- event_name: AWS CloudTrail DeleteSnapshot
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_DeleteSnapshot.yml
|
||||
- event_name: AWS CloudTrail DeleteTrail
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_DeleteTrail.yml
|
||||
- event_name: AWS CloudTrail DeleteVirtualMFADevice
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_DeleteVirtualMFADevice.yml
|
||||
- event_name: AWS CloudTrail DeleteWebACL
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_DeleteWebACL.yml
|
||||
- event_name: AWS CloudTrail DescribeEventAggregates
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_DescribeEventAggregates.yml
|
||||
- event_name: AWS CloudTrail DescribeImageScanFindings
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_DescribeImageScanFindings.yml
|
||||
- event_name: AWS CloudTrail DescribeSnapshotAttribute
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_DescribeSnapshotAttribute.yml
|
||||
- event_name: AWS CloudTrail GetAccountPasswordPolicy
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_GetAccountPasswordPolicy.yml
|
||||
- event_name: AWS CloudTrail GetObject
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_GetObject.yml
|
||||
- event_name: AWS CloudTrail GetPasswordData
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_GetPasswordData.yml
|
||||
- event_name: AWS CloudTrail JobCreated
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_JobCreated.yml
|
||||
- event_name: AWS CloudTrail ModifyDBInstance
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_ModifyDBInstance.yml
|
||||
- event_name: AWS CloudTrail ModifyImageAttribute
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_ModifyImageAttribute.yml
|
||||
- event_name: AWS CloudTrail ModifySnapshotAttribute
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_ModifySnapshotAttribute.yml
|
||||
- event_name: AWS CloudTrail PutBucketAcl
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_PutBucketAcl.yml
|
||||
- event_name: AWS CloudTrail PutBucketLifecycle
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_PutBucketLifecycle.yml
|
||||
- event_name: AWS CloudTrail PutBucketReplication
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_PutBucketReplication.yml
|
||||
- event_name: AWS CloudTrail PutBucketVersioning
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_PutBucketVersioning.yml
|
||||
- event_name: AWS CloudTrail PutImage
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_PutImage.yml
|
||||
- event_name: AWS CloudTrail PutKeyPolicy
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_PutKeyPolicy.yml
|
||||
- event_name: AWS CloudTrail ReplaceNetworkAclEntry
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_ReplaceNetworkAclEntry.yml
|
||||
- event_name: AWS CloudTrail SetDefaultPolicyVersion
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_SetDefaultPolicyVersion.yml
|
||||
- event_name: AWS CloudTrail StopLogging
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_StopLogging.yml
|
||||
- event_name: AWS CloudTrail UpdateAccountPasswordPolicy
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_UpdateAccountPasswordPolicy.yml
|
||||
- event_name: AWS CloudTrail UpdateLoginProfile
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_UpdateLoginProfile.yml
|
||||
- event_name: AWS CloudTrail UpdateSAMLProvider
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_UpdateSAMLProvider.yml
|
||||
- event_name: AWS CloudTrail UpdateTrail
|
||||
data_source: data_sources/cloud/event_sources/AWS_CloudTrail_UpdateTrail.yml
|
||||
fields:
|
||||
- _time
|
||||
- action
|
||||
- app
|
||||
- awsRegion
|
||||
- aws_account_id
|
||||
- change_type
|
||||
- command
|
||||
- date_hour
|
||||
- date_mday
|
||||
- date_minute
|
||||
- date_month
|
||||
- date_second
|
||||
- date_wday
|
||||
- date_year
|
||||
- date_zone
|
||||
- dest
|
||||
- direction
|
||||
- dvc
|
||||
- errorCode
|
||||
- eventCategory
|
||||
- eventID
|
||||
- eventName
|
||||
- eventSource
|
||||
- eventTime
|
||||
- eventType
|
||||
- eventVersion
|
||||
- eventtype
|
||||
- host
|
||||
- index
|
||||
- linecount
|
||||
- managementEvent
|
||||
- msg
|
||||
- object
|
||||
- object_category
|
||||
- object_id
|
||||
- product
|
||||
- protocol
|
||||
- punct
|
||||
- readOnly
|
||||
- recipientAccountId
|
||||
- region
|
||||
- requestID
|
||||
- requestParameters.groupId
|
||||
- requestParameters.ipPermissions.items{}.fromPort
|
||||
- requestParameters.ipPermissions.items{}.ipProtocol
|
||||
- requestParameters.ipPermissions.items{}.ipRanges.items{}.cidrIp
|
||||
- requestParameters.ipPermissions.items{}.toPort
|
||||
- responseElements._return
|
||||
- responseElements.requestId
|
||||
- responseElements.securityGroupRuleSet.items{}.cidrIpv4
|
||||
- responseElements.securityGroupRuleSet.items{}.fromPort
|
||||
- responseElements.securityGroupRuleSet.items{}.groupId
|
||||
- responseElements.securityGroupRuleSet.items{}.groupOwnerId
|
||||
- responseElements.securityGroupRuleSet.items{}.ipProtocol
|
||||
- responseElements.securityGroupRuleSet.items{}.isEgress
|
||||
- responseElements.securityGroupRuleSet.items{}.securityGroupRuleId
|
||||
- responseElements.securityGroupRuleSet.items{}.toPort
|
||||
- signature
|
||||
- source
|
||||
- sourceIPAddress
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- src
|
||||
- src_ip
|
||||
- src_ip_range
|
||||
- src_port_range
|
||||
- start_time
|
||||
- status
|
||||
- tag
|
||||
- tag::eventtype
|
||||
- timeendpos
|
||||
- timestartpos
|
||||
- tlsDetails.cipherSuite
|
||||
- tlsDetails.clientProvidedHostHeader
|
||||
- tlsDetails.tlsVersion
|
||||
- user
|
||||
- userAgent
|
||||
- userIdentity.accessKeyId
|
||||
- userIdentity.accountId
|
||||
- userIdentity.arn
|
||||
- userIdentity.principalId
|
||||
- userIdentity.type
|
||||
- userIdentity.userName
|
||||
- userName
|
||||
- user_access_key
|
||||
- user_agent
|
||||
- user_arn
|
||||
- user_group_id
|
||||
- user_id
|
||||
- user_name
|
||||
- user_type
|
||||
- vendor
|
||||
- vendor_account
|
||||
- vendor_product
|
||||
- vendor_region
|
||||
example_log: '{"eventVersion": "1.09", "userIdentity": {"type": "IAMUser", "principalId":
|
||||
"AIDAAAAAAAAAAAAAAAAAA", "arn": "arn:aws:iam::111111111111:user/daftpunk_cli", "accountId":
|
||||
"111111111111", "accessKeyId": "AKIAAAAAAAAAAAAAAAAA", "userName": "daftpunk_cli"},
|
||||
"eventTime": "2024-02-21T19:19:40Z", "eventSource": "ec2.amazonaws.com", "eventName":
|
||||
"AuthorizeSecurityGroupIngress", "awsRegion": "us-west-2", "sourceIPAddress": "2.2.2.2",
|
||||
"userAgent": "aws-cli/2.13.22 Python/3.11.5 Darwin/22.5.0 source/arm64 prompt/off
|
||||
command/ec2.authorize-security-group-ingress", "requestParameters": {"groupId":
|
||||
"sg-07ffb1896dcd3713e", "ipPermissions": {"items": [{"ipProtocol": "-1", "fromPort":
|
||||
-1, "toPort": -1, "groups": {}, "ipRanges": {"items": [{"cidrIp": "0.0.0.0/0"}]},
|
||||
"ipv6Ranges": {}, "prefixListIds": {}}]}}, "responseElements": {"requestId": "4950930b-2129-423c-95b0-1b87c8fa115a",
|
||||
"_return": true, "securityGroupRuleSet": {"items": [{"groupOwnerId": "111111111111",
|
||||
"groupId": "sg-07ffb1896dcd3713e", "securityGroupRuleId": "sgr-0217c1b508cc6b76c",
|
||||
"isEgress": false, "ipProtocol": "-1", "fromPort": -1, "toPort": -1, "cidrIpv4":
|
||||
"0.0.0.0/0"}]}}, "requestID": "4950930b-2129-423c-95b0-1b87c8fa115a", "eventID":
|
||||
"bdade96f-6272-468a-b084-413b9711e92f", "readOnly": false, "eventType": "AwsApiCall",
|
||||
"managementEvent": true, "recipientAccountId": "111111111111", "eventCategory":
|
||||
"Management", "tlsDetails": {"tlsVersion": "TLSv1.3", "cipherSuite": "TLS_AES_128_GCM_SHA256",
|
||||
"clientProvidedHostHeader": "ec2.us-west-2.amazonaws.com"}}'
|
||||
@@ -0,0 +1,66 @@
|
||||
name: AWS CloudWatchLogs VPCflow
|
||||
id: 38a34fc4-e128-4478-a8f4-7835d51d5135
|
||||
author: Bhavin Patel, Splunk
|
||||
source: aws_cloudwatchlogs_vpcflow
|
||||
sourcetype: aws:cloudwatchlogs:vpcflow
|
||||
separator: eventName
|
||||
supported_TA:
|
||||
name: Splunk Add-on for Amazon Web Services (AWS)
|
||||
version: 7.4.1
|
||||
url: https://splunkbase.splunk.com/app/1876
|
||||
event_names: []
|
||||
fields:
|
||||
- _raw
|
||||
- _time
|
||||
- account_id
|
||||
- action
|
||||
- app
|
||||
- aws_account_id
|
||||
- bytes
|
||||
- date_hour
|
||||
- date_mday
|
||||
- date_minute
|
||||
- date_month
|
||||
- date_second
|
||||
- date_wday
|
||||
- date_year
|
||||
- date_zone
|
||||
- dest
|
||||
- dest_ip
|
||||
- dest_port
|
||||
- duration
|
||||
- dvc
|
||||
- end_time
|
||||
- eventtype
|
||||
- host
|
||||
- index
|
||||
- interface_id
|
||||
- linecount
|
||||
- log_status
|
||||
- packets
|
||||
- protocol
|
||||
- protocol_code
|
||||
- protocol_full_name
|
||||
- protocol_version
|
||||
- punct
|
||||
- region
|
||||
- source
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- splunk_server_group
|
||||
- src
|
||||
- src_ip
|
||||
- src_port
|
||||
- start_time
|
||||
- tag
|
||||
- tag::action
|
||||
- tag::eventtype
|
||||
- timeendpos
|
||||
- timestartpos
|
||||
- transport
|
||||
- user_id
|
||||
- vendor_account
|
||||
- vendor_product
|
||||
- version
|
||||
- vpcflow_action
|
||||
example_log: '2 123397614277 eni-0b0f9f261f45e6489 10.0.1.30 10.0.1.1 47254 22 17 2 98 1697608042 1697608070 ACCEPT OK'
|
||||
@@ -0,0 +1,122 @@
|
||||
name: AWS Security Hub
|
||||
id: b02bfbf3-294f-478e-99a1-e24b8c692d7e
|
||||
author: Patrick Bareiss, Splunk
|
||||
source: aws_securityhub_finding
|
||||
sourcetype: aws:securityhub:finding
|
||||
supported_TA:
|
||||
name: Splunk Add-on for Amazon Web Services (AWS)
|
||||
version: 7.4.1
|
||||
url: https://splunkbase.splunk.com/app/1876
|
||||
event_names: []
|
||||
fields:
|
||||
- _time
|
||||
- AwsAccountId
|
||||
- CreatedAt
|
||||
- Description
|
||||
- FirstObservedAt
|
||||
- GeneratorId
|
||||
- Id
|
||||
- LastObservedAt
|
||||
- ProductArn
|
||||
- ProductFields.aws/guardduty/service/action/actionType
|
||||
- ProductFields.aws/guardduty/service/action/awsApiCallAction/affectedResources/AWS::S3::Bucket
|
||||
- ProductFields.aws/guardduty/service/action/awsApiCallAction/api
|
||||
- ProductFields.aws/guardduty/service/action/awsApiCallAction/callerType
|
||||
- ProductFields.aws/guardduty/service/action/awsApiCallAction/remoteIpDetails/city/cityName
|
||||
- ProductFields.aws/guardduty/service/action/awsApiCallAction/remoteIpDetails/country/countryName
|
||||
- ProductFields.aws/guardduty/service/action/awsApiCallAction/remoteIpDetails/geoLocation/lat
|
||||
- ProductFields.aws/guardduty/service/action/awsApiCallAction/remoteIpDetails/geoLocation/lon
|
||||
- ProductFields.aws/guardduty/service/action/awsApiCallAction/remoteIpDetails/ipAddressV4
|
||||
- ProductFields.aws/guardduty/service/action/awsApiCallAction/remoteIpDetails/organization/asn
|
||||
- ProductFields.aws/guardduty/service/action/awsApiCallAction/remoteIpDetails/organization/asnOrg
|
||||
- ProductFields.aws/guardduty/service/action/awsApiCallAction/remoteIpDetails/organization/isp
|
||||
- ProductFields.aws/guardduty/service/action/awsApiCallAction/remoteIpDetails/organization/org
|
||||
- ProductFields.aws/guardduty/service/action/awsApiCallAction/serviceName
|
||||
- ProductFields.aws/guardduty/service/additionalInfo/sample
|
||||
- ProductFields.aws/guardduty/service/additionalInfo/unusual/hoursOfDay.0_
|
||||
- ProductFields.aws/guardduty/service/additionalInfo/unusual/userNames.0_
|
||||
- ProductFields.aws/guardduty/service/archived
|
||||
- ProductFields.aws/guardduty/service/count
|
||||
- ProductFields.aws/guardduty/service/detectorId
|
||||
- ProductFields.aws/guardduty/service/eventFirstSeen
|
||||
- ProductFields.aws/guardduty/service/eventLastSeen
|
||||
- ProductFields.aws/guardduty/service/resourceRole
|
||||
- ProductFields.aws/guardduty/service/serviceName
|
||||
- ProductFields.aws/securityhub/CompanyName
|
||||
- ProductFields.aws/securityhub/FindingId
|
||||
- ProductFields.aws/securityhub/ProductName
|
||||
- RecordState
|
||||
- Resources{}.Details.AwsEc2Instance.IamInstanceProfileArn
|
||||
- Resources{}.Details.AwsEc2Instance.ImageId
|
||||
- Resources{}.Details.AwsEc2Instance.IpV4Addresses{}
|
||||
- Resources{}.Details.AwsEc2Instance.LaunchedAt
|
||||
- Resources{}.Details.AwsEc2Instance.SubnetId
|
||||
- Resources{}.Details.AwsEc2Instance.Type
|
||||
- Resources{}.Details.AwsEc2Instance.VpcId
|
||||
- Resources{}.Details.AwsIamAccessKey.PrincipalId
|
||||
- Resources{}.Details.AwsIamAccessKey.PrincipalName
|
||||
- Resources{}.Details.AwsIamAccessKey.PrincipalType
|
||||
- Resources{}.Details.AwsS3Bucket.CreatedAt
|
||||
- Resources{}.Details.AwsS3Bucket.OwnerId
|
||||
- Resources{}.Details.AwsS3Bucket.ServerSideEncryptionConfiguration.Rules{}.ApplyServerSideEncryptionByDefault.KMSMasterKeyID
|
||||
- Resources{}.Details.AwsS3Bucket.ServerSideEncryptionConfiguration.Rules{}.ApplyServerSideEncryptionByDefault.SSEAlgorithm
|
||||
- Resources{}.Id
|
||||
- Resources{}.Partition
|
||||
- Resources{}.Region
|
||||
- Resources{}.Tags.GeneratedFindingInstaceTag1
|
||||
- Resources{}.Tags.GeneratedFindingInstaceTag2
|
||||
- Resources{}.Tags.GeneratedFindingInstaceTag3
|
||||
- Resources{}.Tags.GeneratedFindingInstaceTag4
|
||||
- Resources{}.Tags.GeneratedFindingInstaceTag5
|
||||
- Resources{}.Tags.GeneratedFindingInstaceTag6
|
||||
- Resources{}.Tags.GeneratedFindingInstaceTag7
|
||||
- Resources{}.Tags.GeneratedFindingInstaceTag8
|
||||
- Resources{}.Tags.GeneratedFindingInstaceTag9
|
||||
- Resources{}.Tags.foo
|
||||
- Resources{}.Type
|
||||
- SchemaVersion
|
||||
- Severity.Label
|
||||
- Severity.Normalized
|
||||
- Severity.Product
|
||||
- SourceUrl
|
||||
- Title
|
||||
- Types{}
|
||||
- UpdatedAt
|
||||
- Workflow.Status
|
||||
- WorkflowState
|
||||
- accesskey_extract
|
||||
- app
|
||||
- body
|
||||
- description
|
||||
- dest
|
||||
- dest_type
|
||||
- eventtype
|
||||
- host
|
||||
- id
|
||||
- index
|
||||
- instance_extract
|
||||
- linecount
|
||||
- punct
|
||||
- s3bucket_extract
|
||||
- severity
|
||||
- severity_id
|
||||
- signature
|
||||
- signature_id
|
||||
- source
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- subject
|
||||
- tag
|
||||
- tag::eventtype
|
||||
- timestamp
|
||||
- type
|
||||
- vendor_account
|
||||
- vendor_region
|
||||
example_log:
|
||||
'{"ProductArn":"arn:aws:securityhub:us-east-1::product/aws/guardduty","Types":["Software
|
||||
and Configuration Checks/Exfiltration:S3.ObjectRead.Unusual"],"SourceUrl":"https://us-east-1.console.aws.amazon.com/guardduty/home?region=us-east-1#/findings?macros=current&fId=6aba6b696aea10606e8b336f68d98819","Description":"Principal
|
||||
GeneratedFindingUserName read objects from S3 bucket GeneratedFindingS3Bucket in
|
||||
an unusual way.","SchemaVersion":"2018-10-08","GeneratorId":"arn:aws:guardduty:us-east-1:802684071507:detector/48ba636359b884eb132865311fdeb317","FirstObservedAt":"2020-09-28T22:26:15.636Z","CreatedAt":"2020-09-28T22:26:15.636Z","RecordState":"ACTIVE","Title":"Unusual
|
||||
reads of objects in S3 bucket GeneratedFindingS3Bucket.","Workflow":{"Status":"NEW"},"LastObservedAt":"2020-09-28T22:26:15.636Z","Severity":{"Normalized":20,"Label":"LOW","Product":2},"UpdatedAt":"2020-09-28T22:26:15.636Z","WorkflowState":"NEW","ProductFields":{"aws/guardduty/service/archived":"false","aws/guardduty/service/action/awsApiCallAction/remoteIpDetails/organization/asnOrg":"GeneratedFindingASNOrg","aws/guardduty/service/additionalInfo/unusual/userNames.0_":"GeneratedFindingUserName","aws/guardduty/service/action/awsApiCallAction/remoteIpDetails/organization/org":"GeneratedFindingORG","aws/guardduty/service/resourceRole":"TARGET","aws/guardduty/service/action/awsApiCallAction/remoteIpDetails/organization/isp":"GeneratedFindingISP","aws/guardduty/service/action/awsApiCallAction/remoteIpDetails/geoLocation/lat":"0","aws/guardduty/service/count":"1","aws/guardduty/service/action/awsApiCallAction/remoteIpDetails/ipAddressV4":"198.51.100.0","aws/guardduty/service/additionalInfo/sample":"true","aws/guardduty/service/action/awsApiCallAction/remoteIpDetails/country/countryName":"GeneratedFindingCountryName","aws/guardduty/service/action/awsApiCallAction/callerType":"Remote
|
||||
IP","aws/guardduty/service/action/awsApiCallAction/serviceName":"GeneratedFindingAPIServiceName","aws/guardduty/service/action/awsApiCallAction/remoteIpDetails/city/cityName":"GeneratedFindingCityName","aws/guardduty/service/action/awsApiCallAction/api":"GeneratedFindingAPIName","aws/guardduty/service/serviceName":"guardduty","aws/guardduty/service/action/awsApiCallAction/remoteIpDetails/geoLocation/lon":"0","aws/guardduty/service/detectorId":"48ba636359b884eb132865311fdeb317","aws/guardduty/service/action/awsApiCallAction/remoteIpDetails/organization/asn":"-1","aws/guardduty/service/eventFirstSeen":"2020-09-28T22:26:15.636Z","aws/guardduty/service/action/awsApiCallAction/affectedResources/AWS::S3::Bucket":"GeneratedFindingS3Bucket","aws/guardduty/service/eventLastSeen":"2020-09-28T22:26:15.636Z","aws/guardduty/service/additionalInfo/unusual/hoursOfDay.0_":"1513609200000","aws/guardduty/service/action/actionType":"AWS_API_CALL","aws/securityhub/FindingId":"arn:aws:securityhub:us-east-1::product/aws/guardduty/arn:aws:guardduty:us-east-1:802684071507:detector/48ba636359b884eb132865311fdeb317/finding/6aba6b696aea10606e8b336f68d98819","aws/securityhub/ProductName":"GuardDuty","aws/securityhub/CompanyName":"Amazon"},"AwsAccountId":"802684071507","Id":"arn:aws:guardduty:us-east-1:802684071507:detector/48ba636359b884eb132865311fdeb317/finding/6aba6b696aea10606e8b336f68d98819","Resources":[{"Partition":"aws","Type":"AwsEc2Instance","Details":{"AwsEc2Instance":{"Type":"m3.xlarge","VpcId":"GeneratedFindingVPCId","ImageId":"ami-99999999","IpV4Addresses":["10.0.0.1","198.51.100.0"],"SubnetId":"GeneratedFindingSubnetId","LaunchedAt":"2016-08-02T02:05:06Z","IamInstanceProfileArn":"arn:aws:iam::802684071507:example/instance/profile"}},"Region":"us-east-1","Id":"arn:aws:ec2:us-east-1:802684071507:instance/i-99999999","Tags":{"GeneratedFindingInstaceTag7":"GeneratedFindingInstaceTagValue7","GeneratedFindingInstaceTag8":"GeneratedFindingInstaceTagValue8","GeneratedFindingInstaceTag9":"GeneratedFindingInstaceTagValue9","GeneratedFindingInstaceTag1":"GeneratedFindingInstaceValue1","GeneratedFindingInstaceTag2":"GeneratedFindingInstaceTagValue2","GeneratedFindingInstaceTag3":"GeneratedFindingInstaceTagValue3","GeneratedFindingInstaceTag4":"GeneratedFindingInstaceTagValue4","GeneratedFindingInstaceTag5":"GeneratedFindingInstaceTagValue5","GeneratedFindingInstaceTag6":"GeneratedFindingInstaceTagValue6"}},{"Partition":"aws","Type":"AwsIamAccessKey","Details":{"AwsIamAccessKey":{"PrincipalId":"GeneratedFindingPrincipalId","PrincipalName":"GeneratedFindingUserName","PrincipalType":"IAMUser"}},"Region":"us-east-1","Id":"AWS::IAM::AccessKey:GeneratedFindingAccessKeyId"},{"Partition":"aws","Type":"AwsS3Bucket","Details":{"AwsS3Bucket":{"OwnerId":"CanonicalId
|
||||
of Owner","CreatedAt":"2017-12-18T15:58:11.551Z","ServerSideEncryptionConfiguration":{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"SSEAlgorithm","KMSMasterKeyID":"arn:aws:kms:region:123456789012:key/key-id"}}]}}},"Region":"us-east-1","Id":"arn:aws:s3:::bucketName","Tags":{"foo":"bar"}}]}'
|
||||
@@ -0,0 +1,180 @@
|
||||
name: Azure Active Directory
|
||||
id: 7c12d2b2-2679-4806-b258-c17eaffbc66d
|
||||
author: Patrick Bareiss, Splunk
|
||||
source: Azure AD
|
||||
sourcetype: azure:monitor:aad
|
||||
separator: operationName
|
||||
supported_TA:
|
||||
name: Splunk Add-on for Microsoft Cloud Services
|
||||
version: 5.2.2
|
||||
url: https://splunkbase.splunk.com/app/3110
|
||||
event_names:
|
||||
- event_name: Azure Active Directory
|
||||
data_source: data_sources/cloud/event_sources/Azure_Active_Directory.yml
|
||||
- event_name: Azure Active Directory Add app role assignment to service principal
|
||||
data_source: data_sources/cloud/event_sources/Azure_Active_Directory_Add_app_role_assignment_to_service_principal.yml
|
||||
- event_name: Azure Active Directory Add member to role
|
||||
data_source: data_sources/cloud/event_sources/Azure_Active_Directory_Add_member_to_role.yml
|
||||
- event_name: Azure Active Directory Add owner to application
|
||||
data_source: data_sources/cloud/event_sources/Azure_Active_Directory_Add_owner_to_application.yml
|
||||
- event_name: Azure Active Directory Add service principal
|
||||
data_source: data_sources/cloud/event_sources/Azure_Active_Directory_Add_service_principal.yml
|
||||
- event_name: Azure Active Directory Add unverified domain
|
||||
data_source: data_sources/cloud/event_sources/Azure_Active_Directory_Add_unverified_domain.yml
|
||||
- event_name: Azure Active Directory Consent to application
|
||||
data_source: data_sources/cloud/event_sources/Azure_Active_Directory_Consent_to_application.yml
|
||||
- event_name: Azure Active Directory Disable Strong Authentication
|
||||
data_source: data_sources/cloud/event_sources/Azure_Active_Directory_Disable_Strong_Authentication.yml
|
||||
- event_name: Azure Active Directory Enable account
|
||||
data_source: data_sources/cloud/event_sources/Azure_Active_Directory_Enable_account.yml
|
||||
- event_name: Azure Active Directory Invite external user
|
||||
data_source: data_sources/cloud/event_sources/Azure_Active_Directory_Invite_external_user.yml
|
||||
- event_name: Azure Active Directory Reset password (by admin)
|
||||
data_source: data_sources/cloud/event_sources/Azure_Active_Directory_Reset_password_(by_admin).yml
|
||||
- event_name: Azure Active Directory Set domain authentication
|
||||
data_source: data_sources/cloud/event_sources/Azure_Active_Directory_Set_domain_authentication.yml
|
||||
- event_name: Azure Active Directory Sign-in activity
|
||||
data_source: data_sources/cloud/event_sources/Azure_Active_Directory_Sign-in_activity.yml
|
||||
- event_name: Azure Active Directory Update application
|
||||
data_source: data_sources/cloud/event_sources/Azure_Active_Directory_Update_application.yml
|
||||
- event_name: Azure Active Directory Update authorization policy
|
||||
data_source: data_sources/cloud/event_sources/Azure_Active_Directory_Update_authorization_policy.yml
|
||||
- event_name: Azure Active Directory Update user
|
||||
data_source: data_sources/cloud/event_sources/Azure_Active_Directory_Update_user.yml
|
||||
- event_name: Azure Active Directory User registered security info
|
||||
data_source: data_sources/cloud/event_sources/Azure_Active_Directory_User_registered_security_info.yml
|
||||
fields:
|
||||
- _time
|
||||
- Level
|
||||
- callerIpAddress
|
||||
- category
|
||||
- correlationId
|
||||
- date_hour
|
||||
- date_mday
|
||||
- date_minute
|
||||
- date_month
|
||||
- date_second
|
||||
- date_wday
|
||||
- date_year
|
||||
- date_zone
|
||||
- durationMs
|
||||
- host
|
||||
- identity
|
||||
- index
|
||||
- linecount
|
||||
- location
|
||||
- operationName
|
||||
- operationVersion
|
||||
- properties.alternateSignInName
|
||||
- properties.appDisplayName
|
||||
- properties.appId
|
||||
- properties.appServicePrincipalId
|
||||
- properties.authenticationDetails{}.RequestSequence
|
||||
- properties.authenticationDetails{}.StatusSequence
|
||||
- properties.authenticationDetails{}.authenticationMethod
|
||||
- properties.authenticationDetails{}.authenticationMethodDetail
|
||||
- properties.authenticationDetails{}.authenticationStepDateTime
|
||||
- properties.authenticationDetails{}.authenticationStepRequirement
|
||||
- properties.authenticationDetails{}.authenticationStepResultDetail
|
||||
- properties.authenticationDetails{}.succeeded
|
||||
- properties.authenticationProcessingDetails{}.key
|
||||
- properties.authenticationProcessingDetails{}.value
|
||||
- properties.authenticationProtocol
|
||||
- properties.authenticationRequirement
|
||||
- properties.autonomousSystemNumber
|
||||
- properties.clientAppUsed
|
||||
- properties.clientCredentialType
|
||||
- properties.conditionalAccessStatus
|
||||
- properties.correlationId
|
||||
- properties.createdDateTime
|
||||
- properties.crossTenantAccessType
|
||||
- properties.deviceDetail.deviceId
|
||||
- properties.deviceDetail.operatingSystem
|
||||
- properties.flaggedForReview
|
||||
- properties.homeTenantId
|
||||
- properties.id
|
||||
- properties.incomingTokenType
|
||||
- properties.ipAddress
|
||||
- properties.isInteractive
|
||||
- properties.isTenantRestricted
|
||||
- properties.location.city
|
||||
- properties.location.countryOrRegion
|
||||
- properties.location.geoCoordinates.latitude
|
||||
- properties.location.geoCoordinates.longitude
|
||||
- properties.location.state
|
||||
- properties.originalRequestId
|
||||
- properties.processingTimeInMilliseconds
|
||||
- properties.resourceDisplayName
|
||||
- properties.resourceId
|
||||
- properties.resourceServicePrincipalId
|
||||
- properties.resourceTenantId
|
||||
- properties.riskDetail
|
||||
- properties.riskLevelAggregated
|
||||
- properties.riskLevelDuringSignIn
|
||||
- properties.riskState
|
||||
- properties.rngcStatus
|
||||
- properties.servicePrincipalId
|
||||
- properties.signInIdentifier
|
||||
- properties.ssoExtensionVersion
|
||||
- properties.status.errorCode
|
||||
- properties.status.failureReason
|
||||
- properties.tokenIssuerName
|
||||
- properties.tokenIssuerType
|
||||
- properties.uniqueTokenIdentifier
|
||||
- properties.userAgent
|
||||
- properties.userDisplayName
|
||||
- properties.userId
|
||||
- properties.userPrincipalName
|
||||
- properties.userType
|
||||
- punct
|
||||
- resourceId
|
||||
- resultDescription
|
||||
- resultSignature
|
||||
- resultType
|
||||
- source
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- tenantId
|
||||
- time
|
||||
- timeendpos
|
||||
- timestartpos
|
||||
example_log: '{"time": "2023-01-23T21:29:14.1490728Z", "resourceId": "/tenants/fc69e276-e9e8-4af9-9002-1e410d77244e/providers/Microsoft.aadiam",
|
||||
"operationName": "Sign-in activity", "operationVersion": "1.0", "category": "SignInLogs",
|
||||
"tenantId": "fc69e276-e9e8-4af9-9002-1e410d77244e", "resultType": "50126", "resultSignature":
|
||||
"None", "resultDescription": "Invalid username or password or Invalid on-premise
|
||||
username or password.", "durationMs": 0, "callerIpAddress": "35.80.10.10", "correlationId":
|
||||
"1634ad3a-1f98-4964-add5-92fc58621944", "identity": "User30", "Level": 4, "location":
|
||||
"US", "properties": {"id": "13148568-d61e-45eb-b38b-1fa63c106d00", "createdDateTime":
|
||||
"2023-01-23T21:29:14.1490728+00:00", "userDisplayName": "User30", "userPrincipalName":
|
||||
"user30@splunkresearch.com", "userId": "40b61050-e814-4ae5-8ffe-66b6f0c53998", "appId":
|
||||
"1b730954-1685-4b74-9bfd-dac224a7b894", "appDisplayName": "Azure Active Directory
|
||||
PowerShell", "ipAddress": "35.80.10.10", "status": {"errorCode": 50126, "failureReason":
|
||||
"Invalid username or password or Invalid on-premise username or password."}, "clientAppUsed":
|
||||
"Mobile Apps and Desktop clients", "userAgent": "Mozilla/5.0 (Windows NT; Windows
|
||||
NT 10.0; en-US) WindowsPowerShell/5.1.14393.5127", "deviceDetail": {"deviceId":
|
||||
"", "operatingSystem": "Windows 10"}, "location": {"city": "Boardman", "state":
|
||||
"Oregon", "countryOrRegion": "US", "geoCoordinates": {"latitude": 45.83599853515625,
|
||||
"longitude": -119.6989974975586}}, "correlationId": "1634ad3a-1f98-4964-add5-92fc58621944",
|
||||
"conditionalAccessStatus": "notApplied", "appliedConditionalAccessPolicies": [],
|
||||
"authenticationContextClassReferences": [], "originalRequestId": "13148568-d61e-45eb-b38b-1fa63c106d00",
|
||||
"isInteractive": true, "tokenIssuerName": "", "tokenIssuerType": "AzureAD", "authenticationProcessingDetails":
|
||||
[{"key": "Legacy TLS (TLS 1.0, 1.1, 3DES)", "value": "False"}, {"key": "Is CAE Token",
|
||||
"value": "False"}], "networkLocationDetails": [], "clientCredentialType": "none",
|
||||
"processingTimeInMilliseconds": 47, "riskDetail": "none", "riskLevelAggregated":
|
||||
"none", "riskLevelDuringSignIn": "none", "riskState": "none", "riskEventTypes":
|
||||
[], "riskEventTypes_v2": [], "resourceDisplayName": "Windows Azure Active Directory",
|
||||
"resourceId": "00000002-0000-0000-c000-000000000000", "resourceTenantId": "fc69e276-e9e8-4af9-9002-1e410d77244e",
|
||||
"homeTenantId": "fc69e276-e9e8-4af9-9002-1e410d77244e", "authenticationDetails":
|
||||
[{"authenticationStepDateTime": "2023-01-23T21:29:14.1490728+00:00", "authenticationMethod":
|
||||
"Password", "authenticationMethodDetail": "Password in the cloud", "succeeded":
|
||||
false, "authenticationStepResultDetail": "Invalid username or password or Invalid
|
||||
on-premise username or password.", "authenticationStepRequirement": "Primary authentication",
|
||||
"StatusSequence": 0, "RequestSequence": 1}], "authenticationRequirementPolicies":
|
||||
[], "authenticationRequirement": "singleFactorAuthentication", "alternateSignInName":
|
||||
"user30@splunkresearch.com", "signInIdentifier": "user30@splunkresearch.com", "servicePrincipalId":
|
||||
"", "userType": "Member", "flaggedForReview": false, "isTenantRestricted": false,
|
||||
"autonomousSystemNumber": 16509, "crossTenantAccessType": "none", "privateLinkDetails":
|
||||
{}, "ssoExtensionVersion": "", "uniqueTokenIdentifier": "aIUUEx7W60Wzix-mPBBtAA",
|
||||
"authenticationStrengths": [], "incomingTokenType": "none", "authenticationProtocol":
|
||||
"none", "appServicePrincipalId": null, "resourceServicePrincipalId": "4d6bd7de-c9bc-45cc-b8ec-ae315f66bf77",
|
||||
"rngcStatus": 0}}'
|
||||
@@ -0,0 +1,17 @@
|
||||
name: Azure Audit
|
||||
id: 62e2f93e-4e9c-4d38-bb2c-6d59c4565318
|
||||
author: Patrick Bareiss, Splunk
|
||||
source: mscs:azure:audit
|
||||
sourcetype: mscs:azure:audit
|
||||
separator: operationName.localizedValue
|
||||
supported_TA:
|
||||
name: Splunk Add-on for Microsoft Cloud Services
|
||||
version: 5.2.2
|
||||
url: https://splunkbase.splunk.com/app/3110
|
||||
event_names:
|
||||
- event_name: Azure Audit Create or Update an Azure Automation Runbook
|
||||
data_source: data_sources/cloud/event_sources/Azure_Audit_Create_or_Update_an_Azure_Automation_Runbook.yml
|
||||
- event_name: Azure Audit Create or Update an Azure Automation account
|
||||
data_source: data_sources/cloud/event_sources/Azure_Audit_Create_or_Update_an_Azure_Automation_account.yml
|
||||
- event_name: Azure Audit Create or Update an Azure Automation webhook
|
||||
data_source: data_sources/cloud/event_sources/Azure_Audit_Create_or_Update_an_Azure_Automation_webhook.yml
|
||||
@@ -0,0 +1,78 @@
|
||||
name: CircleCI
|
||||
id: 34ad06fc-a296-4ab5-8315-2f07714948e3
|
||||
author: Patrick Bareiss, Splunk
|
||||
source: circleci
|
||||
sourcetype: circleci
|
||||
supported_TA:
|
||||
name: App for CircleCI
|
||||
version: 0.1.1
|
||||
url: https://splunkbase.splunk.com/app/5162
|
||||
event_names: []
|
||||
fields:
|
||||
- _time
|
||||
- author_name
|
||||
- avatar_url
|
||||
- branch
|
||||
- build_num
|
||||
- build_time_millis
|
||||
- build_url
|
||||
- date_hour
|
||||
- date_mday
|
||||
- date_minute
|
||||
- date_month
|
||||
- date_second
|
||||
- date_wday
|
||||
- date_year
|
||||
- date_zone
|
||||
- eventtype
|
||||
- fail_reason
|
||||
- host
|
||||
- index
|
||||
- job_name
|
||||
- job_time
|
||||
- linecount
|
||||
- owners{}
|
||||
- project_slug
|
||||
- punct
|
||||
- queued_time
|
||||
- reponame
|
||||
- source
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- start_time
|
||||
- status
|
||||
- stop_time
|
||||
- tag
|
||||
- tag::eventtype
|
||||
- timedout
|
||||
- timeendpos
|
||||
- timestartpos
|
||||
- username
|
||||
- vcs.commit_time
|
||||
- vcs.committer_name
|
||||
- vcs.revision
|
||||
- vcs.subject
|
||||
- vcs.tag
|
||||
- vcs.type
|
||||
- vcs.url
|
||||
- workflows.job_id
|
||||
- workflows.job_name
|
||||
- workflows.upstream_job_ids{}
|
||||
- workflows.workflow_id
|
||||
- workflows.workflow_name
|
||||
- workflows.workspace_id
|
||||
example_log:
|
||||
'{"job_time": "2021-09-02T08:13:34.273Z", "stop_time": "2021-09-02T08:13:34.273Z",
|
||||
"start_time": "2021-09-02T08:10:15.829Z", "queued_time": "2021-09-02T08:10:12.764Z",
|
||||
"job_name": "Unknown", "reponame": "devsecops_poc", "build_num": 94, "build_url":
|
||||
"https://circleci.com/gh/splunk/devsecops_poc/94", "branch": "main", "status": "success",
|
||||
"project_slug": "gh/splunk/devsecops_poc", "fail_reason": null, "build_time_millis":
|
||||
198444, "timedout": false, "username": "splunk", "owners": ["P4T12ICK"], "author_name":
|
||||
"P4T12ICK", "avatar_url": "", "workflows": {"job_name": "k8s-security", "job_id":
|
||||
"aa1e394f-42c8-4809-93fc-7ba9f8fc51d2", "workflow_id": "6a1bd1c8-e3c4-4d7a-b3e4-16cc726cc0ca",
|
||||
"workspace_id": "6a1bd1c8-e3c4-4d7a-b3e4-16cc726cc0ca", "upstream_job_ids": ["7d543f1d-ae02-449d-9ce3-f710e9094c47",
|
||||
"39a8bf7a-fe22-4886-8661-9f6eec43b348", "7c1adae6-feb1-409b-b17a-c9beaab63359"],
|
||||
"upstream_concurrency_map": {}, "workflow_name": "deployment"}, "vcs": {"commit_time":
|
||||
"2021-09-02T08:05:59.000Z", "type": "github", "url": "https://github.com/splunk/devsecops_poc",
|
||||
"revision": "68d5575c64352792e6e716a1e909db5f9cb3bc2a", "tag": null, "committer_name":
|
||||
"P4T12ICK", "subject": "small change"}}'
|
||||
@@ -0,0 +1,52 @@
|
||||
name: G Suite Drive
|
||||
id: 5f79120f-a235-4468-bd0d-55203758ac22
|
||||
author: Patrick Bareiss, Splunk
|
||||
source: http:gsuite
|
||||
sourcetype: gsuite:drive:json
|
||||
supported_TA:
|
||||
name: Splunk Add-on for Google Workspace
|
||||
version: 2.6.3
|
||||
url: https://splunkbase.splunk.com/app/5556
|
||||
event_names: []
|
||||
fields:
|
||||
- _time
|
||||
- email
|
||||
- host
|
||||
- index
|
||||
- ip_address
|
||||
- linecount
|
||||
- name
|
||||
- parameters.actor_is_collaborator_account
|
||||
- parameters.billable
|
||||
- parameters.doc_id
|
||||
- parameters.doc_title
|
||||
- parameters.doc_type
|
||||
- parameters.is_encrypted
|
||||
- parameters.new_value{}
|
||||
- parameters.old_value{}
|
||||
- parameters.old_visibility
|
||||
- parameters.originating_app_id
|
||||
- parameters.owner
|
||||
- parameters.owner_is_shared_drive
|
||||
- parameters.owner_is_team_drive
|
||||
- parameters.primary_event
|
||||
- parameters.target_user
|
||||
- parameters.visibility
|
||||
- parameters.visibility_change
|
||||
- punct
|
||||
- source
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- timestamp
|
||||
- type
|
||||
- unique_id
|
||||
example_log:
|
||||
'{"type": "acl_change", "name": "change_user_access", "parameters": {"primary_event":
|
||||
true, "billable": true, "visibility_change": "none", "target_user": "alberto@internal_test_email.com",
|
||||
"old_value": ["none"], "new_value": ["can_edit"], "old_visibility": "private", "doc_id":
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "doc_type": "spreadsheet", "is_encrypted":
|
||||
false, "doc_title": "Invoice-11111 FedEx - Delivery - Dummy Detection POC", "visibility":
|
||||
"shared_internally", "originating_app_id": "000000000001", "actor_is_collaborator_account":
|
||||
false, "owner": "peter@external_test_email.com", "owner_is_shared_drive": false,
|
||||
"owner_is_team_drive": false}, "email": "peter@external_test_email.com", "unique_id":
|
||||
"123456789", "ip_address": "null", "timestamp": "2021-08-23T09:19:08.200Z"}'
|
||||
@@ -0,0 +1,108 @@
|
||||
name: G Suite Gmail
|
||||
id: 706c3978-41de-406b-b6e0-75bd01e12a5d
|
||||
author: Patrick Bareiss, Splunk
|
||||
source: http:gsuite
|
||||
sourcetype: gsuite:gmail:bigquery
|
||||
supported_TA:
|
||||
name: Splunk Add-on for Google Workspace
|
||||
version: 2.6.3
|
||||
url: https://splunkbase.splunk.com/app/5556
|
||||
event_names: []
|
||||
fields:
|
||||
- _time
|
||||
- action_type
|
||||
- attachment{}.file_extension_type
|
||||
- attachment{}.malware_family
|
||||
- attachment{}.sha256
|
||||
- connection_info.authenticated_domain{}.name
|
||||
- connection_info.authenticated_domain{}.type
|
||||
- connection_info.client_host_zone
|
||||
- connection_info.client_ip
|
||||
- connection_info.dkim_pass
|
||||
- connection_info.dmarc_pass
|
||||
- connection_info.dmarc_published_domain
|
||||
- connection_info.ip_geo_city
|
||||
- connection_info.ip_geo_country
|
||||
- connection_info.is_internal
|
||||
- connection_info.is_intra_domain
|
||||
- connection_info.smtp_in_connect_ip
|
||||
- connection_info.smtp_out_connect_ip
|
||||
- connection_info.smtp_out_remote_host
|
||||
- connection_info.smtp_reply_code
|
||||
- connection_info.smtp_response_reason
|
||||
- connection_info.smtp_tls_cipher
|
||||
- connection_info.smtp_tls_state
|
||||
- connection_info.smtp_tls_version
|
||||
- connection_info.smtp_user_agent_ip
|
||||
- connection_info.spf_pass
|
||||
- connection_info.tls_required_but_unavailable
|
||||
- description
|
||||
- destination{}.address
|
||||
- destination{}.rcpt_response
|
||||
- destination{}.selector
|
||||
- destination{}.service
|
||||
- destination{}.smime_decryption_success
|
||||
- destination{}.smime_extraction_success
|
||||
- destination{}.smime_parsing_success
|
||||
- destination{}.smime_signature_verification_success
|
||||
- eventtype
|
||||
- flattened_destinations
|
||||
- flattened_triggered_rule_info
|
||||
- host
|
||||
- index
|
||||
- is_policy_check_for_sender
|
||||
- is_spam
|
||||
- linecount
|
||||
- message_set{}.type
|
||||
- num_message_attachments
|
||||
- payload_size
|
||||
- punct
|
||||
- rfc2822_message_id
|
||||
- smime_content_type
|
||||
- smime_encrypt_message
|
||||
- smime_extraction_success
|
||||
- smime_packaging_success
|
||||
- smime_sign_message
|
||||
- smtp_relay_error
|
||||
- source
|
||||
- source.address
|
||||
- source.from_header_address
|
||||
- source.from_header_displayname
|
||||
- source.selector
|
||||
- source.service
|
||||
- sourcetype
|
||||
- spam_info
|
||||
- splunk_server
|
||||
- structured_policy_log_info
|
||||
- subject
|
||||
- tag
|
||||
- tag::eventtype
|
||||
- timestamp
|
||||
- upload_error_category
|
||||
example_log:
|
||||
'{"action_type": 10, "rfc2822_message_id": "<CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC@mail.gmail.com>",
|
||||
"subject": "New Order DHL0000001 - Dummy email for Detection Development", "payload_size":
|
||||
6733, "source": {"address": "john@external_test_email.com", "service": "gmail-for-work",
|
||||
"selector": "policy", "from_header_address": "john@external_test_email.com", "from_header_displayname":
|
||||
"john smith"}, "destination": [{"address": "peter@internal_test_email.com", "service":
|
||||
"smtp-outbound", "selector": "gmail-for-work", "smime_signature_verification_success":
|
||||
null, "smime_decryption_success": null, "smime_parsing_success": null, "smime_extraction_success":
|
||||
null, "rcpt_response": null}], "flattened_destinations": "smtp-outbound:gmail-for-work:peter@internal_test_email.com",
|
||||
"description": "", "connection_info": {"client_ip": "null", "smtp_in_connect_ip":
|
||||
null, "smtp_out_connect_ip": "null", "failed_smtp_out_connect_ip": [], "smtp_tls_state":
|
||||
1, "smtp_reply_code": 250, "tls_required_but_unavailable": false, "smtp_out_remote_host":
|
||||
"internal_test_app.com", "smtp_user_agent_ip": "null", "is_intra_domain": false,
|
||||
"dmarc_pass": null, "dmarc_published_domain": null, "client_host_zone": null, "smtp_response_reason":
|
||||
null, "ip_geo_city": null, "ip_geo_country": null, "authenticated_domain": [{"name":
|
||||
"internal_test_email.com", "type": 2}, {"name": "internal_test_email.com", "type":
|
||||
6}, {"name": "internal_test_email.com", "type": 1}], "is_internal": false, "dkim_pass":
|
||||
true, "spf_pass": true, "smtp_tls_version": "TLSv9.9", "smtp_tls_cipher": "TLS_AES"},
|
||||
"is_spam": null, "is_policy_check_for_sender": false, "num_message_attachments":
|
||||
1, "message_set": [{"type": 57}, {"type": 9}, {"type": 22}, {"type": 15}, {"type":
|
||||
48}, {"type": 27}, {"type": 10}, {"type": 50}, {"type": 51}, {"type": 46}, {"type":
|
||||
61}, {"type": 44}], "smtp_relay_error": null, "upload_error_category": null, "structured_policy_log_info":
|
||||
null, "triggered_rule_info": [], "flattened_triggered_rule_info": null, "smime_sign_message":
|
||||
null, "smime_encrypt_message": null, "smime_packaging_success": null, "smime_extraction_success":
|
||||
null, "smime_content_type": null, "link_domain": [], "attachment": [{"sha256": "1111111111111111111111111111111111111111111111111111111111111111",
|
||||
"file_extension_type": "zip", "malware_family": null}], "spam_info": null, "timestamp":
|
||||
1629378633.802384}'
|
||||
@@ -0,0 +1,205 @@
|
||||
name: GitHub
|
||||
id: 88aa4632-3c3e-43f6-a00a-998d71f558e3
|
||||
author: Patrick Bareiss, Splunk
|
||||
source: github
|
||||
sourcetype: aws:firehose:json
|
||||
supported_TA:
|
||||
name: Splunk Add-on for Github
|
||||
version: 2.2.1
|
||||
url: https://splunkbase.splunk.com/app/6254
|
||||
event_names: []
|
||||
fields:
|
||||
- _time
|
||||
- action
|
||||
- host
|
||||
- index
|
||||
- linecount
|
||||
- meta
|
||||
- punct
|
||||
- source
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- timestamp
|
||||
- workflow_run.actor.avatar_url
|
||||
- workflow_run.actor.events_url
|
||||
- workflow_run.actor.followers_url
|
||||
- workflow_run.actor.following_url
|
||||
- workflow_run.actor.gists_url
|
||||
- workflow_run.actor.gravatar_id
|
||||
- workflow_run.actor.html_url
|
||||
- workflow_run.actor.id
|
||||
- workflow_run.actor.login
|
||||
- workflow_run.actor.node_id
|
||||
- workflow_run.actor.organizations_url
|
||||
- workflow_run.actor.received_events_url
|
||||
- workflow_run.actor.repos_url
|
||||
- workflow_run.actor.site_admin
|
||||
- workflow_run.actor.starred_url
|
||||
- workflow_run.actor.subscriptions_url
|
||||
- workflow_run.actor.type
|
||||
- workflow_run.actor.url
|
||||
- workflow_run.artifacts_url
|
||||
- workflow_run.cancel_url
|
||||
- workflow_run.check_suite_id
|
||||
- workflow_run.check_suite_node_id
|
||||
- workflow_run.check_suite_url
|
||||
- workflow_run.conclusion
|
||||
- workflow_run.created_at
|
||||
- workflow_run.event
|
||||
- workflow_run.head_branch
|
||||
- workflow_run.head_commit.author.email
|
||||
- workflow_run.head_commit.author.name
|
||||
- workflow_run.head_commit.committer.email
|
||||
- workflow_run.head_commit.committer.name
|
||||
- workflow_run.head_commit.id
|
||||
- workflow_run.head_commit.message
|
||||
- workflow_run.head_commit.timestamp
|
||||
- workflow_run.head_commit.tree_id
|
||||
- workflow_run.head_repository.collaborators_url
|
||||
- workflow_run.head_repository.description
|
||||
- workflow_run.head_repository.fork
|
||||
- workflow_run.head_repository.forks_url
|
||||
- workflow_run.head_repository.full_name
|
||||
- workflow_run.head_repository.hooks_url
|
||||
- workflow_run.head_repository.html_url
|
||||
- workflow_run.head_repository.id
|
||||
- workflow_run.head_repository.keys_url
|
||||
- workflow_run.head_repository.name
|
||||
- workflow_run.head_repository.node_id
|
||||
- workflow_run.head_repository.owner.avatar_url
|
||||
- workflow_run.head_repository.owner.events_url
|
||||
- workflow_run.head_repository.owner.followers_url
|
||||
- workflow_run.head_repository.owner.following_url
|
||||
- workflow_run.head_repository.owner.gists_url
|
||||
- workflow_run.head_repository.owner.gravatar_id
|
||||
- workflow_run.head_repository.owner.html_url
|
||||
- workflow_run.head_repository.owner.id
|
||||
- workflow_run.head_repository.owner.login
|
||||
- workflow_run.head_repository.owner.node_id
|
||||
- workflow_run.head_repository.owner.organizations_url
|
||||
- workflow_run.head_repository.owner.received_events_url
|
||||
- workflow_run.head_repository.owner.repos_url
|
||||
- workflow_run.head_repository.owner.site_admin
|
||||
- workflow_run.head_repository.owner.starred_url
|
||||
- workflow_run.head_repository.owner.subscriptions_url
|
||||
- workflow_run.head_repository.owner.type
|
||||
- workflow_run.head_repository.owner.url
|
||||
- workflow_run.head_repository.private
|
||||
- workflow_run.head_repository.teams_url
|
||||
- workflow_run.head_repository.url
|
||||
- workflow_run.head_sha
|
||||
- workflow_run.html_url
|
||||
- workflow_run.id
|
||||
- workflow_run.jobs_url
|
||||
- workflow_run.logs_url
|
||||
- workflow_run.name
|
||||
- workflow_run.node_id
|
||||
- workflow_run.previous_attempt_url
|
||||
- workflow_run.pull_requests{}.base.ref
|
||||
- workflow_run.pull_requests{}.base.repo.id
|
||||
- workflow_run.pull_requests{}.base.repo.name
|
||||
- workflow_run.pull_requests{}.base.repo.url
|
||||
- workflow_run.pull_requests{}.base.sha
|
||||
- workflow_run.pull_requests{}.head.ref
|
||||
- workflow_run.pull_requests{}.head.repo.id
|
||||
- workflow_run.pull_requests{}.head.repo.name
|
||||
- workflow_run.pull_requests{}.head.repo.url
|
||||
- workflow_run.pull_requests{}.head.sha
|
||||
- workflow_run.pull_requests{}.id
|
||||
- workflow_run.pull_requests{}.number
|
||||
- workflow_run.pull_requests{}.url
|
||||
- workflow_run.repository.archive_url
|
||||
- workflow_run.repository.assignees_url
|
||||
- workflow_run.repository.blobs_url
|
||||
- workflow_run.repository.branches_url
|
||||
- workflow_run.repository.collaborators_url
|
||||
- workflow_run.repository.comments_url
|
||||
- workflow_run.repository.commits_url
|
||||
- workflow_run.repository.compare_url
|
||||
- workflow_run.repository.contents_url
|
||||
- workflow_run.repository.contributors_url
|
||||
- workflow_run.repository.deployments_url
|
||||
- workflow_run.repository.description
|
||||
- workflow_run.repository.downloads_url
|
||||
- workflow_run.repository.events_url
|
||||
- workflow_run.repository.fork
|
||||
- workflow_run.repository.forks_url
|
||||
- workflow_run.repository.full_name
|
||||
- workflow_run.repository.git_commits_url
|
||||
- workflow_run.repository.git_refs_url
|
||||
- workflow_run.repository.git_tags_url
|
||||
- workflow_run.repository.hooks_url
|
||||
- workflow_run.repository.html_url
|
||||
- workflow_run.repository.id
|
||||
- workflow_run.repository.issue_comment_url
|
||||
- workflow_run.repository.issue_events_url
|
||||
- workflow_run.repository.issues_url
|
||||
- workflow_run.repository.keys_url
|
||||
- workflow_run.repository.labels_url
|
||||
- workflow_run.repository.languages_url
|
||||
- workflow_run.repository.merges_url
|
||||
- workflow_run.repository.milestones_url
|
||||
- workflow_run.repository.name
|
||||
- workflow_run.repository.node_id
|
||||
- workflow_run.repository.notifications_url
|
||||
- workflow_run.repository.owner.avatar_url
|
||||
- workflow_run.repository.owner.events_url
|
||||
- workflow_run.repository.owner.followers_url
|
||||
- workflow_run.repository.owner.following_url
|
||||
- workflow_run.repository.owner.gists_url
|
||||
- workflow_run.repository.owner.gravatar_id
|
||||
- workflow_run.repository.owner.html_url
|
||||
- workflow_run.repository.owner.id
|
||||
- workflow_run.repository.owner.login
|
||||
- workflow_run.repository.owner.node_id
|
||||
- workflow_run.repository.owner.organizations_url
|
||||
- workflow_run.repository.owner.received_events_url
|
||||
- workflow_run.repository.owner.repos_url
|
||||
- workflow_run.repository.owner.site_admin
|
||||
- workflow_run.repository.owner.starred_url
|
||||
- workflow_run.repository.owner.subscriptions_url
|
||||
- workflow_run.repository.owner.type
|
||||
- workflow_run.repository.owner.url
|
||||
- workflow_run.repository.private
|
||||
- workflow_run.repository.pulls_url
|
||||
- workflow_run.repository.releases_url
|
||||
- workflow_run.repository.stargazers_url
|
||||
- workflow_run.repository.statuses_url
|
||||
- workflow_run.repository.subscribers_url
|
||||
- workflow_run.repository.subscription_url
|
||||
- workflow_run.repository.tags_url
|
||||
- workflow_run.repository.teams_url
|
||||
- workflow_run.repository.trees_url
|
||||
- workflow_run.repository.url
|
||||
- workflow_run.rerun_url
|
||||
- workflow_run.run_attempt
|
||||
- workflow_run.run_number
|
||||
- workflow_run.run_started_at
|
||||
- workflow_run.status
|
||||
- workflow_run.triggering_actor.avatar_url
|
||||
- workflow_run.triggering_actor.events_url
|
||||
- workflow_run.triggering_actor.followers_url
|
||||
- workflow_run.triggering_actor.following_url
|
||||
- workflow_run.triggering_actor.gists_url
|
||||
- workflow_run.triggering_actor.gravatar_id
|
||||
- workflow_run.triggering_actor.html_url
|
||||
- workflow_run.triggering_actor.id
|
||||
- workflow_run.triggering_actor.login
|
||||
- workflow_run.triggering_actor.node_id
|
||||
- workflow_run.triggering_actor.organizations_url
|
||||
- workflow_run.triggering_actor.received_events_url
|
||||
- workflow_run.triggering_actor.repos_url
|
||||
- workflow_run.triggering_actor.site_admin
|
||||
- workflow_run.triggering_actor.starred_url
|
||||
- workflow_run.triggering_actor.subscriptions_url
|
||||
- workflow_run.triggering_actor.type
|
||||
- workflow_run.triggering_actor.url
|
||||
- workflow_run.updated_at
|
||||
- workflow_run.url
|
||||
- workflow_run.workflow_id
|
||||
- workflow_run.workflow_url
|
||||
example_log:
|
||||
'{"action":"requested","workflow_run":{"id":2088708615,"name":"auto-update","node_id":"WFR_kwLOCa00Ec58fyoH","head_branch":"mac_os_detections","head_sha":"4049334910ea3d52a917ca35aed66d11c80ed966","run_number":9504,"event":"push","status":"queued","conclusion":null,"workflow_id":4692335,"check_suite_id":5918781611,"check_suite_node_id":"CS_kwDOCa00Ec8AAAABYMlwqw","url":"https://api.github.com/repos/splunk/security_content/actions/runs/2088708615","html_url":"https://github.com/splunk/security_content/actions/runs/2088708615","pull_requests":[{"url":"https://api.github.com/repos/splunk/security_content/pulls/2131","id":893091277,"number":2131,"head":{"ref":"mac_os_detections","sha":"4049334910ea3d52a917ca35aed66d11c80ed966","repo":{"id":162346001,"url":"https://api.github.com/repos/splunk/security_content","name":"security_content"}},"base":{"ref":"develop","sha":"a7d3d1dc57f9bf36fe22e470bcf518fcc2c89283","repo":{"id":162346001,"url":"https://api.github.com/repos/splunk/security_content","name":"security_content"}}}],"created_at":"2022-04-04T08:43:15Z","updated_at":"2022-04-04T08:43:15Z","actor":{"login":"jsmith","id":8362376,"node_id":"MDQ6VXNlcjgzNjIzNzY=","avatar_url":"https://avatars.githubusercontent.com/u/8362376?v=4","gravatar_id":"","url":"https://api.github.com/users/jsmith","html_url":"https://github.com/jsmith","followers_url":"https://api.github.com/users/jsmith/followers","following_url":"https://api.github.com/users/jsmith/following{/other_user}","gists_url":"https://api.github.com/users/jsmith/gists{/gist_id}","starred_url":"https://api.github.com/users/jsmith/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jsmith/subscriptions","organizations_url":"https://api.github.com/users/jsmith/orgs","repos_url":"https://api.github.com/users/jsmith/repos","events_url":"https://api.github.com/users/jsmith/events{/privacy}","received_events_url":"https://api.github.com/users/jsmith/received_events","type":"User","site_admin":false},"run_attempt":1,"run_started_at":"2022-04-04T08:43:15Z","triggering_actor":{"login":"jsmith","id":8362376,"node_id":"MDQ6VXNlcjgzNjIzNzY=","avatar_url":"https://avatars.githubusercontent.com/u/8362376?v=4","gravatar_id":"","url":"https://api.github.com/users/jsmith","html_url":"https://github.com/jsmith","followers_url":"https://api.github.com/users/jsmith/followers","following_url":"https://api.github.com/users/jsmith/following{/other_user}","gists_url":"https://api.github.com/users/jsmith/gists{/gist_id}","starred_url":"https://api.github.com/users/jsmith/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jsmith/subscriptions","organizations_url":"https://api.github.com/users/jsmith/orgs","repos_url":"https://api.github.com/users/jsmith/repos","events_url":"https://api.github.com/users/jsmith/events{/privacy}","received_events_url":"https://api.github.com/users/jsmith/received_events","type":"User","site_admin":false},"jobs_url":"https://api.github.com/repos/splunk/security_content/actions/runs/2088708615/jobs","logs_url":"https://api.github.com/repos/splunk/security_content/actions/runs/2088708615/logs","check_suite_url":"https://api.github.com/repos/splunk/security_content/check-suites/5918781611","artifacts_url":"https://api.github.com/repos/splunk/security_content/actions/runs/2088708615/artifacts","cancel_url":"https://api.github.com/repos/splunk/security_content/actions/runs/2088708615/cancel","rerun_url":"https://api.github.com/repos/splunk/security_content/actions/runs/2088708615/rerun","previous_attempt_url":null,"workflow_url":"https://api.github.com/repos/splunk/security_content/actions/workflows/4692335","head_commit":{"id":"4049334910ea3d52a917ca35aed66d11c80ed966","tree_id":"df4ddc1359be3b19f093b7a27dbf5708187743a0","message":"small
|
||||
change","timestamp":"2022-04-04T08:43:01Z","author":{"name":"jsmith","email":"jsmith@evilcorp.com"},"committer":{"name":"jsmith","email":"jsmith@evilcorp.com"}},"repository":{"id":162346001,"node_id":"MDEwOlJlcG9zaXRvcnkxNjIzNDYwMDE=","name":"security_content","full_name":"splunk/security_content","private":false,"owner":{"login":"splunk","id":651467,"node_id":"MDEyOk9yZ2FuaXphdGlvbjY1MTQ2Nw==","avatar_url":"https://avatars.githubusercontent.com/u/651467?v=4","gravatar_id":"","url":"https://api.github.com/users/splunk","html_url":"https://github.com/splunk","followers_url":"https://api.github.com/users/splunk/followers","following_url":"https://api.github.com/users/splunk/following{/other_user}","gists_url":"https://api.github.com/users/splunk/gists{/gist_id}","starred_url":"https://api.github.com/users/splunk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/splunk/subscriptions","organizations_url":"https://api.github.com/users/splunk/orgs","repos_url":"https://api.github.com/users/splunk/repos","events_url":"https://api.github.com/users/splunk/events{/privacy}","received_events_url":"https://api.github.com/users/splunk/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/splunk/security_content","description":"Splunk
|
||||
Security Content","fork":false,"url":"https://api.github.com/repos/splunk/security_content","forks_url":"https://api.github.com/repos/splunk/security_content/forks","keys_url":"https://api.github.com/repos/splunk/security_content/keys{/key_id}","collaborators_url":"https://api.github.com/repos/splunk/security_content/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/splunk/security_content/teams","hooks_url":"https://api.github.com/repos/splunk/security_content/hooks","issue_events_url":"https://api.github.com/repos/splunk/security_content/issues/events{/number}","events_url":"https://api.github.com/repos/splunk/security_content/events","assignees_url":"https://api.github.com/repos/splunk/security_content/assignees{/user}","branches_url":"https://api.github.com/repos/splunk/security_content/branches{/branch}","tags_url":"https://api.github.com/repos/splunk/security_content/tags","blobs_url":"https://api.github.com/repos/splunk/security_content/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/splunk/security_content/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/splunk/security_content/git/refs{/sha}","trees_url":"https://api.github.com/repos/splunk/security_content/git/trees{/sha}","statuses_url":"https://api.github.com/repos/splunk/security_content/statuses/{sha}","languages_url":"https://api.github.com/repos/splunk/security_content/languages","stargazers_url":"https://api.github.com/repos/splunk/security_content/stargazers","contributors_url":"https://api.github.com/repos/splunk/security_content/contributors","subscribers_url":"https://api.github.com/repos/splunk/security_content/subscribers","subscription_url":"https://api.github.com/repos/splunk/security_content/subscription","commits_url":"https://api.github.com/repos/splunk/security_content/commits{/sha}","git_commits_url":"https://api.github.com/repos/splunk/security_content/git/commits{/sha}","comments_url":"https://api.github.com/repos/splunk/security_content/comments{/number}","issue_comment_url":"https://api.github.com/repos/splunk/security_content/issues/comments{/number}","contents_url":"https://api.github.com/repos/splunk/security_content/contents/{+path}","compare_url":"https://api.github.com/repos/splunk/security_content/compare/{base}...{head}","merges_url":"https://api.github.com/repos/splunk/security_content/merges","archive_url":"https://api.github.com/repos/splunk/security_content/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/splunk/security_content/downloads","issues_url":"https://api.github.com/repos/splunk/security_content/issues{/number}","pulls_url":"https://api.github.com/repos/splunk/security_content/pulls{/number}","milestones_url":"https://api.github.com/repos/splunk/security_content/milestones{/number}","notifications_url":"https://api.github.com/repos/splunk/security_content/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/splunk/security_content/labels{/name}","releases_url":"https://api.github.com/repos/splunk/security_content/releases{/id}","deployments_url":"https://api.github.com/repos/splunk/security_content/deployments"},"head_repository":{"id":162346001,"node_id":"MDEwOlJlcG9zaXRvcnkxNjIzNDYwMDE=","name":"security_content","full_name":"splunk/security_content","private":false,"owner":{"login":"splunk","id":651467,"node_id":"MDEyOk9yZ2FuaXphdGlvbjY1MTQ2Nw==","avatar_url":"https://avatars.githubusercontent.com/u/651467?v=4","gravatar_id":"","url":"https://api.github.com/users/splunk","html_url":"https://github.com/splunk","followers_url":"https://api.github.com/users/splunk/followers","following_url":"https://api.github.com/users/splunk/following{/other_user}","gists_url":"https://api.github.com/users/splunk/gists{/gist_id}","starred_url":"https://api.github.com/users/splunk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/splunk/subscriptions","organizations_url":"https://api.github.com/users/splunk/orgs","repos_url":"https://api.github.com/users/splunk/repos","events_url":"https://api.github.com/users/splunk/events{/privacy}","received_events_url":"https://api.github.com/users/splunk/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/splunk/security_content","description":"Splunk
|
||||
Security Content","fork":false,"url":"https://api.github.com/repos/splunk/security_content","forks_url":"https://api.github.com/repos/splunk/security_content/forks","keys_url":"https://api.github.com/repos/splunk/security_content/keys{/key_id}","collaborators_url":"https://api.github.com/repos/splunk/security_content/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/splunk/security_content/teams","hooks_url":"https://api.github.com/repos/splunk/security_content/hooks","issue_events_url":"https://api.github.com/repos/splunk/security_content/issues/events{/num'
|
||||
@@ -0,0 +1,17 @@
|
||||
name: Google Workspace
|
||||
id: 9ef3a321-c641-4798-8a92-9c10c714a004
|
||||
author: Patrick Bareiss, Splunk
|
||||
source: gws:reports:admin
|
||||
sourcetype: gws:reports:admin
|
||||
separator: event.name
|
||||
supported_TA:
|
||||
name: Splunk Add-on for Google Workspace
|
||||
version: 2.6.3
|
||||
url: https://splunkbase.splunk.com/app/5556
|
||||
event_names:
|
||||
- event_name: Google Workspace
|
||||
data_source: data_sources/cloud/event_sources/Google_Workspace.yml
|
||||
- event_name: Google Workspace login_failure
|
||||
data_source: data_sources/cloud/event_sources/Google_Workspace_login_failure.yml
|
||||
- event_name: Google Workspace login_success
|
||||
data_source: data_sources/cloud/event_sources/Google_Workspace_login_success.yml
|
||||
@@ -0,0 +1,61 @@
|
||||
name: Kubernetes Audit
|
||||
id: 6c25181a-0c07-4aaf-90e6-77ab1f0e6699
|
||||
author: Patrick Bareiss, Splunk
|
||||
source: kubernetes
|
||||
sourcetype: _json
|
||||
supported_TA: {}
|
||||
event_names: []
|
||||
fields:
|
||||
- _time
|
||||
- annotations.authorization.k8s.io/decision
|
||||
- annotations.authorization.k8s.io/reason
|
||||
- apiVersion
|
||||
- auditID
|
||||
- eventtype
|
||||
- host
|
||||
- index
|
||||
- kind
|
||||
- level
|
||||
- linecount
|
||||
- objectRef.apiGroup
|
||||
- objectRef.apiVersion
|
||||
- objectRef.namespace
|
||||
- objectRef.resource
|
||||
- punct
|
||||
- requestReceivedTimestamp
|
||||
- requestURI
|
||||
- responseObject.apiVersion
|
||||
- responseObject.code
|
||||
- responseObject.details.group
|
||||
- responseObject.details.kind
|
||||
- responseObject.kind
|
||||
- responseObject.message
|
||||
- responseObject.reason
|
||||
- responseObject.status
|
||||
- responseStatus.code
|
||||
- responseStatus.details.group
|
||||
- responseStatus.details.kind
|
||||
- responseStatus.message
|
||||
- responseStatus.reason
|
||||
- responseStatus.status
|
||||
- source
|
||||
- sourceIPs{}
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- stage
|
||||
- stageTimestamp
|
||||
- tag
|
||||
- tag::eventtype
|
||||
- timestamp
|
||||
- user.groups{}
|
||||
- user.uid
|
||||
- user.username
|
||||
- userAgent
|
||||
- verb
|
||||
example_log:
|
||||
'{"kind":"Event","apiVersion":"audit.k8s.io/v1","level":"RequestResponse","auditID":"582c31ab-4906-49bb-9ff9-872f980ccb84","stage":"ResponseComplete","requestURI":"/apis/batch/v1/namespaces/test2/jobs?fieldManager=kubectl-create\u0026fieldValidation=Strict","verb":"create","user":{"username":"k8s-test-user","uid":"aws-iam-authenticator:591511147606:AROAYTOGP2RLFHNBOTP5J","groups":["system:authenticated"]},"sourceIPs":["176.95.188.101"],"userAgent":"kubectl/v1.27.2
|
||||
(darwin/arm64) kubernetes/7f6f68f","objectRef":{"resource":"jobs","namespace":"test2","apiGroup":"batch","apiVersion":"v1"},"responseStatus":{"metadata":{},"status":"Failure","message":"jobs.batch
|
||||
is forbidden: User \"k8s-test-user\" cannot create resource \"jobs\" in API group
|
||||
\"batch\" in the namespace \"test2\"","reason":"Forbidden","details":{"group":"batch","kind":"jobs"},"code":403},"responseObject":{"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"jobs.batch
|
||||
is forbidden: User \"k8s-test-user\" cannot create resource \"jobs\" in API group
|
||||
\"batch\" in the namespace \"test2\"","reason":"Forbidden","details":{"group":"batch","kind":"jobs"},"code":403},"requestReceivedTimestamp":"2023-12-07T14:44:53.358394Z","stageTimestamp":"2023-12-07T14:44:53.375985Z","annotations":{"authorization.k8s.io/decision":"forbid","authorization.k8s.io/reason":""}}'
|
||||
@@ -0,0 +1,48 @@
|
||||
name: Kubernetes Falco
|
||||
id: 23c0eeed-840a-4711-a41b-6819c1ffbba5
|
||||
author: Patrick Bareiss, Splunk
|
||||
source: kubernetes
|
||||
sourcetype: kube:container:falco
|
||||
supported_TA: {}
|
||||
event_names: []
|
||||
fields:
|
||||
- _time
|
||||
- command
|
||||
- container_id
|
||||
- container_image
|
||||
- container_image_tag
|
||||
- container_name
|
||||
- date_hour
|
||||
- date_mday
|
||||
- date_minute
|
||||
- date_month
|
||||
- date_second
|
||||
- date_wday
|
||||
- date_year
|
||||
- date_zone
|
||||
- evt_type
|
||||
- exe_flags
|
||||
- host
|
||||
- index
|
||||
- k8s_ns
|
||||
- k8s_pod_name
|
||||
- linecount
|
||||
- parent
|
||||
- proc_exepath
|
||||
- process
|
||||
- punct
|
||||
- source
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- terminal
|
||||
- timeendpos
|
||||
- timestartpos
|
||||
- user
|
||||
- user_loginuid
|
||||
- user_uid
|
||||
example_log:
|
||||
"12:18:18.691725165: Notice A shell was spawned in a container with an
|
||||
attached terminal (evt_type=execve user=root user_uid=0 user_loginuid=-1 process=bash
|
||||
proc_exepath=/usr/lib/splunk-otel-collector/agent-bundle/bin/bash parent=runc command=bash
|
||||
-il terminal=34816 exe_flags=EXE_WRITABLE container_id=7a2566e8e462 container_image=quay.io/signalfx/splunk-otel-collector
|
||||
container_image_tag=0.88.0 container_name=otel-collector k8s_ns=default k8s_pod_name=my-splunk-otel-collector-agent-9sdhr)"
|
||||
@@ -0,0 +1,123 @@
|
||||
name: O365
|
||||
id: 11c0eed5-3f3f-42e4-bf72-30f11295a686
|
||||
author: Patrick Bareiss, Splunk
|
||||
source: o365
|
||||
sourcetype: o365:management:activity
|
||||
separator: Operation
|
||||
supported_TA:
|
||||
name: Splunk Add-on for Microsoft Office 365
|
||||
version: 4.5.1
|
||||
url: https://splunkbase.splunk.com/app/4055
|
||||
event_names:
|
||||
- event_name: O365
|
||||
data_source: data_sources/cloud/event_sources/O365.yml
|
||||
- event_name: O365 Add app role assignment grant to user.
|
||||
data_source: data_sources/cloud/event_sources/O365_Add_app_role_assignment_grant_to_user..yml
|
||||
- event_name: O365 Add app role assignment to service principal.
|
||||
data_source: data_sources/cloud/event_sources/O365_Add_app_role_assignment_to_service_principal..yml
|
||||
- event_name: O365 Add member to role.
|
||||
data_source: data_sources/cloud/event_sources/O365_Add_member_to_role..yml
|
||||
- event_name: O365 Add owner to application.
|
||||
data_source: data_sources/cloud/event_sources/O365_Add_owner_to_application..yml
|
||||
- event_name: O365 Add service principal.
|
||||
data_source: data_sources/cloud/event_sources/O365_Add_service_principal..yml
|
||||
- event_name: O365 Add-MailboxPermission
|
||||
data_source: data_sources/cloud/event_sources/O365_Add-MailboxPermission.yml
|
||||
- event_name: O365 Change user license.
|
||||
data_source: data_sources/cloud/event_sources/O365_Change_user_license..yml
|
||||
- event_name: O365 Consent to application.
|
||||
data_source: data_sources/cloud/event_sources/O365_Consent_to_application..yml
|
||||
- event_name: O365 Disable Strong Authentication.
|
||||
data_source: data_sources/cloud/event_sources/O365_Disable_Strong_Authentication..yml
|
||||
- event_name: O365 MailItemsAccessed
|
||||
data_source: data_sources/cloud/event_sources/O365_MailItemsAccessed.yml
|
||||
- event_name: O365 ModifyFolderPermissions
|
||||
data_source: data_sources/cloud/event_sources/O365_ModifyFolderPermissions.yml
|
||||
- event_name: O365 Set Company Information.
|
||||
data_source: data_sources/cloud/event_sources/O365_Set_Company_Information..yml
|
||||
- event_name: O365 Set-Mailbox
|
||||
data_source: data_sources/cloud/event_sources/O365_Set-Mailbox.yml
|
||||
- event_name: O365 Update application.
|
||||
data_source: data_sources/cloud/event_sources/O365_Update_application..yml
|
||||
- event_name: O365 Update authorization policy.
|
||||
data_source: data_sources/cloud/event_sources/O365_Update_authorization_policy..yml
|
||||
- event_name: O365 Update user.
|
||||
data_source: data_sources/cloud/event_sources/O365_Update_user..yml
|
||||
- event_name: O365 UserLoggedIn
|
||||
data_source: data_sources/cloud/event_sources/O365_UserLoggedIn.yml
|
||||
- event_name: O365 UserLoginFailed
|
||||
data_source: data_sources/cloud/event_sources/O365_UserLoginFailed.yml
|
||||
fields:
|
||||
- _time
|
||||
- AppAccessContext.IssuedAtTime
|
||||
- AppAccessContext.UniqueTokenId
|
||||
- AppId
|
||||
- ClientAppId
|
||||
- ClientIP
|
||||
- CreationTime
|
||||
- ExternalAccess
|
||||
- Id
|
||||
- Name
|
||||
- ObjectId
|
||||
- Operation
|
||||
- OrganizationId
|
||||
- OrganizationName
|
||||
- OriginatingServer
|
||||
- Parameters{}.Name
|
||||
- Parameters{}.Value
|
||||
- RecordType
|
||||
- RequestId
|
||||
- ResultStatus
|
||||
- Role
|
||||
- SessionId
|
||||
- User
|
||||
- UserId
|
||||
- UserKey
|
||||
- UserType
|
||||
- Version
|
||||
- Workload
|
||||
- app
|
||||
- authentication_service
|
||||
- command
|
||||
- date_hour
|
||||
- date_mday
|
||||
- date_minute
|
||||
- date_month
|
||||
- date_second
|
||||
- date_wday
|
||||
- date_year
|
||||
- date_zone
|
||||
- dest
|
||||
- dest_name
|
||||
- dvc
|
||||
- host
|
||||
- index
|
||||
- linecount
|
||||
- object
|
||||
- punct
|
||||
- record_type
|
||||
- signature
|
||||
- source
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- src
|
||||
- src_ip
|
||||
- status
|
||||
- timeendpos
|
||||
- timestartpos
|
||||
- user
|
||||
- user_id
|
||||
- user_type
|
||||
- vendor_account
|
||||
- vendor_product
|
||||
example_log: '{"AppAccessContext": {"IssuedAtTime": "2023-10-17T19:13:05", "UniqueTokenId":
|
||||
"g7oAmNhLoU-8qJVeWeAwAA"}, "CreationTime": "2023-10-17T19:19:59", "Id": "3d26a8cd-d8f4-42f9-1898-08dbcf460e5a",
|
||||
"Operation": "New-ManagementRoleAssignment", "OrganizationId": "aeb12f6b-1ff3-4a18-9ea2-29aa57e2ae08",
|
||||
"RecordType": 1, "ResultStatus": "True", "UserKey": "1003BFFD98415B4E", "UserType":
|
||||
2, "Version": 1, "Workload": "Exchange", "ClientIP": "71.1.1.1:61528", "ObjectId":
|
||||
"splunkresearch.onmicrosoft.com\\attack-test", "UserId": "compromisedAdmin@splunkresearch.onmicrosoft.com",
|
||||
"AppId": "fb78d390-0c51-40cd-8e17-fdbfab77341b", "ClientAppId": "", "ExternalAccess":
|
||||
false, "OrganizationName": "splunkresearch.onmicrosoft.com", "OriginatingServer":
|
||||
"BYAPR18MB2408 (15.20.6863.047)", "Parameters": [{"Name": "User", "Value": "lowpriv@splunkresearch.onmicrosoft.com"},
|
||||
{"Name": "Name", "Value": "attack-test"}, {"Name": "Role", "Value": "ApplicationImpersonation"}],
|
||||
"RequestId": "53a50583-e429-63a4-c9f7-8fbb14437e8a", "SessionId": "e2a028f1-d0e1-4ddb-a5a7-ec57343457ad"}'
|
||||
@@ -0,0 +1,92 @@
|
||||
event_name: AWS CloudTrail AssumeRoleWithSAML
|
||||
fields:
|
||||
- _time
|
||||
- action
|
||||
- app
|
||||
- awsRegion
|
||||
- change_type
|
||||
- command
|
||||
- date_hour
|
||||
- date_mday
|
||||
- date_minute
|
||||
- date_month
|
||||
- date_second
|
||||
- date_wday
|
||||
- date_year
|
||||
- date_zone
|
||||
- dest
|
||||
- dvc
|
||||
- errorCode
|
||||
- eventCategory
|
||||
- eventID
|
||||
- eventName
|
||||
- eventSource
|
||||
- eventTime
|
||||
- eventType
|
||||
- eventVersion
|
||||
- eventtype
|
||||
- host
|
||||
- index
|
||||
- linecount
|
||||
- managementEvent
|
||||
- msg
|
||||
- object_category
|
||||
- product
|
||||
- punct
|
||||
- readOnly
|
||||
- recipientAccountId
|
||||
- region
|
||||
- requestID
|
||||
- requestParameters.durationSeconds
|
||||
- requestParameters.principalArn
|
||||
- requestParameters.roleArn
|
||||
- requestParameters.roleSessionName
|
||||
- requestParameters.sAMLAssertionID
|
||||
- resources{}.ARN
|
||||
- resources{}.accountId
|
||||
- resources{}.type
|
||||
- responseElements.assumedRoleUser.arn
|
||||
- responseElements.assumedRoleUser.assumedRoleId
|
||||
- responseElements.audience
|
||||
- responseElements.credentials.accessKeyId
|
||||
- responseElements.credentials.expiration
|
||||
- responseElements.credentials.sessionToken
|
||||
- responseElements.issuer
|
||||
- responseElements.nameQualifier
|
||||
- responseElements.subject
|
||||
- responseElements.subjectType
|
||||
- signature
|
||||
- source
|
||||
- sourceIPAddress
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- src
|
||||
- src_ip
|
||||
- src_user
|
||||
- src_user_id
|
||||
- src_user_type
|
||||
- start_time
|
||||
- status
|
||||
- tag
|
||||
- tag::action
|
||||
- tag::eventtype
|
||||
- temp_access_key
|
||||
- timeendpos
|
||||
- timestartpos
|
||||
- user
|
||||
- userAgent
|
||||
- userIdentity.identityProvider
|
||||
- userIdentity.principalId
|
||||
- userIdentity.type
|
||||
- userIdentity.userName
|
||||
- user_agent
|
||||
- user_arn
|
||||
- user_id
|
||||
- user_name
|
||||
- user_role
|
||||
- user_type
|
||||
- vendor
|
||||
- vendor_account
|
||||
- vendor_product
|
||||
- vendor_region
|
||||
example_log: '{"eventVersion": "1.08", "userIdentity": {"type": "SAMLUser", "principalId": "ZRu9MRAjiG9tvi1QBNfdI664G5A=:rodsoto@rodsoto.onmicrosoft.com", "userName": "rodsoto@rodsoto.onmicrosoft.com", "identityProvider": "ZRu9MRAjiG9tvi1QBNfdI664G5A="}, "eventTime": "2021-01-22T03:44:16Z", "eventSource": "sts.amazonaws.com", "eventName": "AssumeRoleWithSAML", "awsRegion": "us-east-1", "sourceIPAddress": "72.21.217.152", "userAgent": "AWS Signin, aws-internal/3 aws-sdk-java/1.11.898 Linux/4.9.230-0.1.ac.223.84.332.metal1.x86_64 OpenJDK_64-Bit_Server_VM/25.275-b01 java/1.8.0_275 kotlin/1.3.72 vendor/Oracle_Corporation", "requestParameters": {"sAMLAssertionID": "_d33ba0ad-0c88-4b83-80a6-27c08027d000", "roleSessionName": "rodsoto@rodsoto.onmicrosoft.com", "durationSeconds": 3600, "roleArn": "arn:aws:iam::111111111111:role/rodonmicrotestrole", "principalArn": "arn:aws:iam::111111111111:saml-provider/rodsotoonmicrosoft"}, "responseElements": {"subjectType": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", "issuer": "https://sts.windows.net/0e8108b1-18e9-41a4-961b-dfcddf92ef08/", "credentials": {"accessKeyId": "ASIAYTOGP2RLKJXOV7VR", "expiration": "Jan 22, 2021 3:59:16 AM", "sessionToken": "IQoJb3JpZ2luX2VjENz//////////wEaCXVzLWVhc3QtMSJGMEQCIHl/WQdLq53ULYl10fPtpeV3H7da9mOXGuIStvhdDA6kAiAdO/7rocOXAtyDV+0MJhSj/piqlqEI/BcAKm8zqBhOgiqgAwi1//////////8BEAIaDDU5MTUxMTE0NzYwNiIMAFP8GfyI/x1fKCHYKvQCznxxgKnEdcuvrr/fBLmHxEDjQ9vQxjasA8gZF8GawZ5SFH9ViKqoZh93bYkOwKqZD8cdqJIo9SYL17RGhVqxzvsjU4+QsRXTN5eGgh+LyF9EZqeCl6oCkaTJqNrXHuenzTi0yiL510LKsSckrAm8+SuwI/jQu3oE1BEcJQieAc4RjYx3II+1cUvyfRlFvR2NDfZhdWcQvAivV06KJg9c2zfCF0Agzn/YZSNvsRL5UhnKhJ2wktSvT8lR0mS3n9xR1j3iYNxVDHab180cnfkP3G6tAmxj5U29diNQrusJxKWWhr/Q9wHRdDj5dO2QUZzSymKKU/UiRJ8nyYPINaJ8XWVAPiT4QgzpMxotkvSkDLEG/brB9yEr2p7W8Y3xMGZ37qSGkuU4z9x40RU9G1XPhv27NO9aaGs/VXYTMCzWRNxnsLfNkk/DihrcaR0SclRcvs+zmfe1ZUoGnfjNYm+AIyJi4D7OrXF5/Mt46Z2y76DnULlAMJCUqYAGOpsBw4fyafV8OizX7Lebh2JRXFZsWBY1GTpyGvO5otrw++4axud44vYVi5iU5rbJxtTBm4vtJartxgXoPJFnQuat9gLrIlXhjmg+m50a5xs1Ut2UWEsWY2Duu4jA9ap7P7dYv9kIK9P2uyhsjKQhCjTpfe4I/llcupbDotYBJuvlB5n85a5kgiSriFk5zetoXQIweuYEWQZsD/AN+LE="}, "nameQualifier": "ZRu9MRAjiG9tvi1QBNfdI664G5A=", "assumedRoleUser": {"assumedRoleId": "AROAYTOGP2RLKFUVAQAIJ:rodsoto@rodsoto.onmicrosoft.com", "arn": "arn:aws:sts::111111111111:assumed-role/rodonmicrotestrole/rodsoto@rodsoto.onmicrosoft.com"}, "subject": "rodsoto@rodsoto.onmicrosoft.com", "audience": "https://signin.aws.amazon.com/saml"}, "requestID": "e19c7a7f-cd96-4642-9ee6-2360a7b01b12", "eventID": "b25b825d-9c9b-49d3-9ecd-290dbe8f2c29", "readOnly": true, "resources": [{"accountId": "111111111111", "type": "AWS::IAM::Role", "ARN": "arn:aws:iam::111111111111:role/rodonmicrotestrole"}, {"accountId": "111111111111", "type": "AWS::IAM::SAMLProvider", "ARN": "arn:aws:iam::111111111111:saml-provider/rodsotoonmicrosoft"}], "eventType": "AwsApiCall", "managementEvent": true, "eventCategory": "Management", "recipientAccountId": "111111111111"}'
|
||||
@@ -0,0 +1,80 @@
|
||||
event_name: AWS CloudTrail ConsoleLogin
|
||||
fields:
|
||||
- _time
|
||||
- action
|
||||
- additionalEventData.LoginTo
|
||||
- additionalEventData.MFAUsed
|
||||
- additionalEventData.MobileVersion
|
||||
- app
|
||||
- authentication_method
|
||||
- awsRegion
|
||||
- aws_account_id
|
||||
- command
|
||||
- date_hour
|
||||
- date_mday
|
||||
- date_minute
|
||||
- date_month
|
||||
- date_second
|
||||
- date_wday
|
||||
- date_year
|
||||
- date_zone
|
||||
- desc
|
||||
- dest
|
||||
- dvc
|
||||
- errorCode
|
||||
- errorMessage
|
||||
- eventCategory
|
||||
- eventID
|
||||
- eventName
|
||||
- eventSource
|
||||
- eventTime
|
||||
- eventType
|
||||
- eventVersion
|
||||
- eventtype
|
||||
- host
|
||||
- index
|
||||
- linecount
|
||||
- managementEvent
|
||||
- msg
|
||||
- object_category
|
||||
- product
|
||||
- punct
|
||||
- readOnly
|
||||
- reason
|
||||
- recipientAccountId
|
||||
- region
|
||||
- requestParameters
|
||||
- responseElements.ConsoleLogin
|
||||
- result
|
||||
- signature
|
||||
- source
|
||||
- sourceIPAddress
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- src
|
||||
- src_ip
|
||||
- start_time
|
||||
- status
|
||||
- tag
|
||||
- tag::action
|
||||
- tag::eventtype
|
||||
- timeendpos
|
||||
- timestartpos
|
||||
- tlsDetails.cipherSuite
|
||||
- tlsDetails.clientProvidedHostHeader
|
||||
- tlsDetails.tlsVersion
|
||||
- userAgent
|
||||
- userIdentity.accessKeyId
|
||||
- userIdentity.accountId
|
||||
- userIdentity.type
|
||||
- userIdentity.userName
|
||||
- user_access_key
|
||||
- user_agent
|
||||
- user_group_id
|
||||
- user_name
|
||||
- user_type
|
||||
- vendor
|
||||
- vendor_account
|
||||
- vendor_product
|
||||
- vendor_region
|
||||
example_log: '{"eventVersion": "1.08", "userIdentity": {"type": "IAMUser", "accountId": "140429656527", "accessKeyId": "", "userName": "HIDDEN_DUE_TO_SECURITY_REASONS"}, "eventTime": "2022-10-19T20:33:38Z", "eventSource": "signin.amazonaws.com", "eventName": "ConsoleLogin", "awsRegion": "us-east-1", "sourceIPAddress": "142.254.89.27", "userAgent": "Go-http-client/1.1", "errorMessage": "No username found in supplied account", "requestParameters": null, "responseElements": {"ConsoleLogin": "Failure"}, "additionalEventData": {"LoginTo": "https://console.aws.amazon.com", "MobileVersion": "No", "MFAUsed": "No"}, "eventID": "9fcfb8c3-3fca-48db-85d2-7b107f9d95d0", "readOnly": false, "eventType": "AwsConsoleSignIn", "managementEvent": true, "recipientAccountId": "140429656527", "eventCategory": "Management", "tlsDetails": {"tlsVersion": "TLSv1.2", "cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256", "clientProvidedHostHeader": "signin.aws.amazon.com"}}'
|
||||
@@ -0,0 +1,86 @@
|
||||
event_name: AWS CloudTrail CopyObject
|
||||
fields:
|
||||
- _time
|
||||
- additionalEventData.AuthenticationMethod
|
||||
- additionalEventData.CipherSuite
|
||||
- additionalEventData.SSEApplied
|
||||
- additionalEventData.SignatureVersion
|
||||
- additionalEventData.bytesTransferredIn
|
||||
- additionalEventData.bytesTransferredOut
|
||||
- additionalEventData.x-amz-id-2
|
||||
- app
|
||||
- awsRegion
|
||||
- aws_account_id
|
||||
- command
|
||||
- date_hour
|
||||
- date_mday
|
||||
- date_minute
|
||||
- date_month
|
||||
- date_second
|
||||
- date_wday
|
||||
- date_year
|
||||
- date_zone
|
||||
- dest
|
||||
- dvc
|
||||
- errorCode
|
||||
- eventCategory
|
||||
- eventID
|
||||
- eventName
|
||||
- eventSource
|
||||
- eventTime
|
||||
- eventType
|
||||
- eventVersion
|
||||
- host
|
||||
- index
|
||||
- linecount
|
||||
- managementEvent
|
||||
- msg
|
||||
- object_category
|
||||
- product
|
||||
- punct
|
||||
- readOnly
|
||||
- recipientAccountId
|
||||
- region
|
||||
- requestID
|
||||
- requestParameters.Host
|
||||
- requestParameters.bucketName
|
||||
- requestParameters.key
|
||||
- requestParameters.x-amz-copy-source
|
||||
- requestParameters.x-amz-server-side-encryption
|
||||
- requestParameters.x-amz-server-side-encryption-aws-kms-key-id
|
||||
- resources{}.ARN
|
||||
- resources{}.accountId
|
||||
- resources{}.type
|
||||
- responseElements.x-amz-server-side-encryption
|
||||
- responseElements.x-amz-server-side-encryption-aws-kms-key-id
|
||||
- signature
|
||||
- source
|
||||
- sourceIPAddress
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- src
|
||||
- src_ip
|
||||
- start_time
|
||||
- timeendpos
|
||||
- timestartpos
|
||||
- user
|
||||
- userAgent
|
||||
- userIdentity.accessKeyId
|
||||
- userIdentity.accountId
|
||||
- userIdentity.arn
|
||||
- userIdentity.principalId
|
||||
- userIdentity.type
|
||||
- userIdentity.userName
|
||||
- userName
|
||||
- user_access_key
|
||||
- user_agent
|
||||
- user_arn
|
||||
- user_group_id
|
||||
- user_id
|
||||
- user_name
|
||||
- user_type
|
||||
- vendor
|
||||
- vendor_account
|
||||
- vendor_product
|
||||
- vendor_region
|
||||
example_log: '{"eventVersion": "1.08", "userIdentity": {"type": "IAMUser", "principalId": "AIDAYTOGP2RLNALZHZ6KX", "arn": "arn:aws:iam::111111111111:user/patrick_cli", "accountId": "111111111111", "accessKeyId": "AKIAYTOGP2RLJ2OYSF6E", "userName": "patrick_cli"}, "eventTime": "2021-01-11T12:40:47Z", "eventSource": "s3.amazonaws.com", "eventName": "CopyObject", "awsRegion": "us-west-2", "sourceIPAddress": "95.90.199.65", "userAgent": "[aws-cli/2.0.45 Python/3.7.4 Darwin/20.2.0 exe/x86_64 command/s3.cp]", "requestParameters": {"bucketName": "patricktestbucketencrypt", "x-amz-server-side-encryption-aws-kms-key-id": "arn:aws:kms:us-west-2:111111111111:key/f2a82583-a7d3-4c92-8787-fe2baab1cee1", "Host": "patricktestbucketencrypt.s3.us-west-2.amazonaws.com", "x-amz-server-side-encryption": "aws:kms", "x-amz-copy-source": "patricktestbucketencrypt/kms_aws_events.json", "key": "kms_aws_events_encrypted.json"}, "responseElements": {"x-amz-server-side-encryption": "aws:kms", "x-amz-server-side-encryption-aws-kms-key-id": "arn:aws:kms:us-west-2:111111111111:key/f2a82583-a7d3-4c92-8787-fe2baab1cee1"}, "additionalEventData": {"SignatureVersion": "SigV4", "CipherSuite": "ECDHE-RSA-AES128-GCM-SHA256", "bytesTransferredIn": 0.0, "SSEApplied": "SSE_KMS", "AuthenticationMethod": "AuthHeader", "x-amz-id-2": "fqzX1iZV6ImDtkFxbGvziOE6fUwryRa+PhnLckfVAkLNHdbCAHNq4l/yckUd1a2HNJPL6NAS01U=", "bytesTransferredOut": 234.0}, "requestID": "6A7359F7A9414B02", "eventID": "b20d43de-175d-4443-acd7-f5f3e587ae00", "readOnly": false, "resources": [{"type": "AWS::S3::Object", "ARN": "arn:aws:s3:::patricktestbucketencrypt/kms_aws_events_encrypted.json"}, {"accountId": "111111111111", "type": "AWS::S3::Bucket", "ARN": "arn:aws:s3:::patricktestbucketencrypt"}, {"accountId": "111111111111", "type": "AWS::S3::Bucket", "ARN": "arn:aws:s3:::patricktestbucketencrypt"}, {"type": "AWS::S3::Object", "ARN": "arn:aws:s3:::patricktestbucketencrypt/kms_aws_events.json"}], "eventType": "AwsApiCall", "managementEvent": false, "recipientAccountId": "111111111111", "eventCategory": "Data"}'
|
||||
@@ -0,0 +1,80 @@
|
||||
event_name: AWS CloudTrail CreateAccessKey
|
||||
fields:
|
||||
- _time
|
||||
- action
|
||||
- app
|
||||
- awsRegion
|
||||
- aws_account_id
|
||||
- change_type
|
||||
- command
|
||||
- date_hour
|
||||
- date_mday
|
||||
- date_minute
|
||||
- date_month
|
||||
- date_second
|
||||
- date_wday
|
||||
- date_year
|
||||
- date_zone
|
||||
- dest
|
||||
- dvc
|
||||
- errorCode
|
||||
- eventCategory
|
||||
- eventID
|
||||
- eventName
|
||||
- eventSource
|
||||
- eventTime
|
||||
- eventType
|
||||
- eventVersion
|
||||
- eventtype
|
||||
- host
|
||||
- index
|
||||
- linecount
|
||||
- managementEvent
|
||||
- msg
|
||||
- object_category
|
||||
- product
|
||||
- punct
|
||||
- readOnly
|
||||
- recipientAccountId
|
||||
- region
|
||||
- requestID
|
||||
- requestParameters.userName
|
||||
- responseElements.accessKey.accessKeyId
|
||||
- responseElements.accessKey.createDate
|
||||
- responseElements.accessKey.status
|
||||
- responseElements.accessKey.userName
|
||||
- signature
|
||||
- source
|
||||
- sourceIPAddress
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- src
|
||||
- src_ip
|
||||
- src_user_name
|
||||
- start_time
|
||||
- status
|
||||
- tag
|
||||
- tag::eventtype
|
||||
- timeendpos
|
||||
- timestartpos
|
||||
- user
|
||||
- userAgent
|
||||
- userIdentity.accessKeyId
|
||||
- userIdentity.accountId
|
||||
- userIdentity.arn
|
||||
- userIdentity.principalId
|
||||
- userIdentity.type
|
||||
- userIdentity.userName
|
||||
- userName
|
||||
- user_access_key
|
||||
- user_agent
|
||||
- user_arn
|
||||
- user_group_id
|
||||
- user_id
|
||||
- user_name
|
||||
- user_type
|
||||
- vendor
|
||||
- vendor_account
|
||||
- vendor_product
|
||||
- vendor_region
|
||||
example_log: '{"eventVersion": "1.08", "userIdentity": {"type": "IAMUser", "principalId": "AIDAYTOGP2RLEHRX5YWNV", "arn": "arn:aws:iam::121521347698:user/bhavin_cli", "accountId": "121521347698", "accessKeyId": "AKIAYTOGP2RLLAA6NJUM", "userName": "bhavin_cli"}, "eventTime": "2021-03-02T21:18:24Z", "eventSource": "iam.amazonaws.com", "eventName": "CreateAccessKey", "awsRegion": "us-east-1", "sourceIPAddress": "12.25.72.12", "userAgent": "aws-cli/2.0.62 Python/3.9.0 Darwin/19.6.0 source/x86_64 command/iam.create-access-key", "requestParameters": {"userName": "AtomicRedTeam"}, "responseElements": {"accessKey": {"userName": "AtomicRedTeam", "accessKeyId": "AKIAYTOGP2RLOQ4ULYGT", "status": "Active", "createDate": "Mar 2, 2021 9:18:24 PM"}}, "requestID": "12c8773d-6c78-46bf-a8e4-f841adc8f70d", "eventID": "5772e8d5-cccc-470d-81ef-acacfe85a804", "readOnly": false, "eventType": "AwsApiCall", "managementEvent": true, "eventCategory": "Management", "recipientAccountId": "121521347698"}'
|
||||
@@ -0,0 +1,98 @@
|
||||
event_name: AWS CloudTrail CreateKey
|
||||
fields:
|
||||
- _time
|
||||
- app
|
||||
- awsRegion
|
||||
- aws_account_id
|
||||
- command
|
||||
- date_hour
|
||||
- date_mday
|
||||
- date_minute
|
||||
- date_month
|
||||
- date_second
|
||||
- date_wday
|
||||
- date_year
|
||||
- date_zone
|
||||
- dest
|
||||
- dvc
|
||||
- errorCode
|
||||
- eventCategory
|
||||
- eventID
|
||||
- eventName
|
||||
- eventSource
|
||||
- eventTime
|
||||
- eventType
|
||||
- eventVersion
|
||||
- eventtype
|
||||
- host
|
||||
- index
|
||||
- linecount
|
||||
- managementEvent
|
||||
- msg
|
||||
- object_category
|
||||
- product
|
||||
- punct
|
||||
- readOnly
|
||||
- recipientAccountId
|
||||
- region
|
||||
- requestID
|
||||
- requestParameters.bypassPolicyLockoutSafetyCheck
|
||||
- requestParameters.customerMasterKeySpec
|
||||
- requestParameters.description
|
||||
- requestParameters.keyUsage
|
||||
- requestParameters.origin
|
||||
- requestParameters.policy
|
||||
- resources{}.ARN
|
||||
- resources{}.accountId
|
||||
- resources{}.type
|
||||
- responseElements.keyMetadata.aWSAccountId
|
||||
- responseElements.keyMetadata.arn
|
||||
- responseElements.keyMetadata.creationDate
|
||||
- responseElements.keyMetadata.customerMasterKeySpec
|
||||
- responseElements.keyMetadata.description
|
||||
- responseElements.keyMetadata.enabled
|
||||
- responseElements.keyMetadata.encryptionAlgorithms{}
|
||||
- responseElements.keyMetadata.keyId
|
||||
- responseElements.keyMetadata.keyManager
|
||||
- responseElements.keyMetadata.keyState
|
||||
- responseElements.keyMetadata.keyUsage
|
||||
- responseElements.keyMetadata.origin
|
||||
- signature
|
||||
- source
|
||||
- sourceIPAddress
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- src
|
||||
- src_ip
|
||||
- start_time
|
||||
- tag
|
||||
- tag::eventtype
|
||||
- timeendpos
|
||||
- timestartpos
|
||||
- user
|
||||
- userAgent
|
||||
- userIdentity.accessKeyId
|
||||
- userIdentity.accountId
|
||||
- userIdentity.arn
|
||||
- userIdentity.principalId
|
||||
- userIdentity.sessionContext.attributes.creationDate
|
||||
- userIdentity.sessionContext.attributes.mfaAuthenticated
|
||||
- userIdentity.sessionContext.sessionIssuer.accountId
|
||||
- userIdentity.sessionContext.sessionIssuer.arn
|
||||
- userIdentity.sessionContext.sessionIssuer.principalId
|
||||
- userIdentity.sessionContext.sessionIssuer.type
|
||||
- userIdentity.sessionContext.sessionIssuer.userName
|
||||
- userIdentity.type
|
||||
- userName
|
||||
- user_access_key
|
||||
- user_agent
|
||||
- user_arn
|
||||
- user_group_id
|
||||
- user_id
|
||||
- user_name
|
||||
- user_type
|
||||
- vendor
|
||||
- vendor_account
|
||||
- vendor_product
|
||||
- vendor_region
|
||||
example_log: '{"eventVersion": "1.08", "userIdentity": {"type": "AssumedRole", "principalId": "AROAIJIESMXKGCJRCTPR6:pbareiss@splunk.local", "arn": "arn:aws:sts::111111111111:assumed-role/okta_adm_role/pbareiss@splunk.local", "accountId": "111111111111", "accessKeyId": "ASIAYTOGP2RLK74OPBDR", "sessionContext": {"sessionIssuer": {"type": "Role", "principalId": "AROAIJIESMXKGCJRCTPR6", "arn": "arn:aws:iam::111111111111:role/okta_adm_role", "accountId": "111111111111", "userName": "okta_adm_role"}, "webIdFederationData": {}, "attributes": {"mfaAuthenticated": "false", "creationDate": "2021-01-11T09:03:18Z"}}}, "eventTime": "2021-01-11T09:56:31Z", "eventSource": "kms.amazonaws.com", "eventName": "CreateKey", "awsRegion": "us-west-2", "sourceIPAddress": "95.90.199.65", "userAgent": "aws-internal/3 aws-sdk-java/1.11.893 Linux/4.9.230-0.1.ac.223.84.332.metal1.x86_64 OpenJDK_64-Bit_Server_VM/25.272-b10 java/1.8.0_272 vendor/Oracle_Corporation", "requestParameters": {"origin": "AWS_KMS", "policy": "{\n \"Id\": \"key-consolepolicy-3\",\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Sid\": \"Enable IAM User Permissions\",\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"AWS\": \"arn:aws:iam::111111111111:root\"\n },\n \"Action\": \"kms:*\",\n \"Resource\": \"*\"\n },\n {\n \"Sid\": \"Allow access for Key Administrators\",\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"AWS\": \"arn:aws:iam::111111111111:user/patrick_cli\"\n },\n \"Action\": [\n \"kms:Create*\",\n \"kms:Describe*\",\n \"kms:Enable*\",\n \"kms:List*\",\n \"kms:Put*\",\n \"kms:Update*\",\n \"kms:Revoke*\",\n \"kms:Disable*\",\n \"kms:Get*\",\n \"kms:Delete*\",\n \"kms:TagResource\",\n \"kms:UntagResource\",\n \"kms:ScheduleKeyDeletion\",\n \"kms:CancelKeyDeletion\"\n ],\n \"Resource\": \"*\"\n },\n {\n \"Sid\": \"Allow use of the key\",\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"AWS\": \"arn:aws:iam::111111111111:user/patrick_cli\"\n },\n \"Action\": [\n \"kms:Encrypt\",\n \"kms:Decrypt\",\n \"kms:ReEncrypt*\",\n \"kms:GenerateDataKey*\",\n \"kms:DescribeKey\"\n ],\n \"Resource\": \"*\"\n },\n {\n \"Sid\": \"Allow attachment of persistent resources\",\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"AWS\": \"arn:aws:iam::111111111111:user/patrick_cli\"\n },\n \"Action\": [\n \"kms:CreateGrant\",\n \"kms:ListGrants\",\n \"kms:RevokeGrant\"\n ],\n \"Resource\": \"*\",\n \"Condition\": {\n \"Bool\": {\n \"kms:GrantIsForAWSResource\": \"true\"\n }\n }\n },\n {\n \"Sid\": \"Allow use of the key\",\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"AWS\": \"*\"\n },\n \"Action\": [\n \"kms:Encrypt\"\n ],\n \"Resource\": \"*\"\n }\n ]\n}", "description": "", "customerMasterKeySpec": "SYMMETRIC_DEFAULT", "bypassPolicyLockoutSafetyCheck": false, "tags": [], "keyUsage": "ENCRYPT_DECRYPT"}, "responseElements": {"keyMetadata": {"aWSAccountId": "111111111111", "keyId": "f2a82583-a7d3-4c92-8787-fe2baab1cee1", "arn": "arn:aws:kms:us-west-2:111111111111:key/f2a82583-a7d3-4c92-8787-fe2baab1cee1", "creationDate": "Jan 11, 2021, 9:56:30 AM", "enabled": true, "description": "", "keyUsage": "ENCRYPT_DECRYPT", "keyState": "Enabled", "origin": "AWS_KMS", "keyManager": "CUSTOMER", "customerMasterKeySpec": "SYMMETRIC_DEFAULT", "encryptionAlgorithms": ["SYMMETRIC_DEFAULT"]}}, "requestID": "3356af25-a237-471f-ba5e-abb37d4a256f", "eventID": "f09518ac-5ae5-4214-80ee-4f23ccdedd4c", "readOnly": false, "resources": [{"accountId": "111111111111", "type": "AWS::KMS::Key", "ARN": "arn:aws:kms:us-west-2:111111111111:key/f2a82583-a7d3-4c92-8787-fe2baab1cee1"}], "eventType": "AwsApiCall", "managementEvent": true, "eventCategory": "Management", "recipientAccountId": "111111111111"}'
|
||||
@@ -0,0 +1,79 @@
|
||||
event_name: AWS CloudTrail CreateLoginProfile
|
||||
fields:
|
||||
- _time
|
||||
- action
|
||||
- app
|
||||
- awsRegion
|
||||
- aws_account_id
|
||||
- change_type
|
||||
- command
|
||||
- date_hour
|
||||
- date_mday
|
||||
- date_minute
|
||||
- date_month
|
||||
- date_second
|
||||
- date_wday
|
||||
- date_year
|
||||
- date_zone
|
||||
- dest
|
||||
- dvc
|
||||
- errorCode
|
||||
- eventCategory
|
||||
- eventID
|
||||
- eventName
|
||||
- eventSource
|
||||
- eventTime
|
||||
- eventType
|
||||
- eventVersion
|
||||
- eventtype
|
||||
- host
|
||||
- index
|
||||
- linecount
|
||||
- managementEvent
|
||||
- msg
|
||||
- object_category
|
||||
- product
|
||||
- punct
|
||||
- readOnly
|
||||
- recipientAccountId
|
||||
- region
|
||||
- requestID
|
||||
- requestParameters.passwordResetRequired
|
||||
- requestParameters.userName
|
||||
- responseElements.loginProfile.createDate
|
||||
- responseElements.loginProfile.passwordResetRequired
|
||||
- responseElements.loginProfile.userName
|
||||
- signature
|
||||
- source
|
||||
- sourceIPAddress
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- src
|
||||
- src_ip
|
||||
- start_time
|
||||
- status
|
||||
- tag
|
||||
- tag::eventtype
|
||||
- timeendpos
|
||||
- timestartpos
|
||||
- user
|
||||
- userAgent
|
||||
- userIdentity.accessKeyId
|
||||
- userIdentity.accountId
|
||||
- userIdentity.arn
|
||||
- userIdentity.principalId
|
||||
- userIdentity.type
|
||||
- userIdentity.userName
|
||||
- userName
|
||||
- user_access_key
|
||||
- user_agent
|
||||
- user_arn
|
||||
- user_group_id
|
||||
- user_id
|
||||
- user_name
|
||||
- user_type
|
||||
- vendor
|
||||
- vendor_account
|
||||
- vendor_product
|
||||
- vendor_region
|
||||
example_log: '{"eventVersion": "1.08", "userIdentity": {"type": "IAMUser", "principalId": "AIDAYTOGP2RLEHRX5YWNV", "arn": "arn:aws:iam::111111111111:user/bhavin_cli", "accountId": "111111111111", "accessKeyId": "AKIAYTOGP2RLLAA6NJUM", "userName": "bhavin_cli"}, "eventTime": "2021-03-05T01:02:38Z", "eventSource": "iam.amazonaws.com", "eventName": "CreateLoginProfile", "awsRegion": "us-east-1", "sourceIPAddress": "73.15.72.101", "userAgent": "aws-cli/2.0.62 Python/3.9.2 Darwin/19.6.0 source/x86_64 command/iam.create-login-profile", "requestParameters": {"userName": "AtomicRedTeam", "passwordResetRequired": false}, "responseElements": {"loginProfile": {"userName": "AtomicRedTeam", "createDate": "Mar 5, 2021 1:02:38 AM", "passwordResetRequired": false}}, "requestID": "f1b90364-8aed-4559-96cf-f5f2009bb7cb", "eventID": "ffb76906-6dd1-4219-adfe-e26b92036a1e", "readOnly": false, "eventType": "AwsApiCall", "managementEvent": true, "eventCategory": "Management", "recipientAccountId": "111111111111"}'
|
||||
@@ -0,0 +1,95 @@
|
||||
event_name: AWS CloudTrail CreateNetworkAclEntry
|
||||
fields:
|
||||
- _time
|
||||
- action
|
||||
- app
|
||||
- awsRegion
|
||||
- aws_account_id
|
||||
- change_type
|
||||
- command
|
||||
- date_hour
|
||||
- date_mday
|
||||
- date_minute
|
||||
- date_month
|
||||
- date_second
|
||||
- date_wday
|
||||
- date_year
|
||||
- date_zone
|
||||
- dest
|
||||
- direction
|
||||
- dvc
|
||||
- errorCode
|
||||
- eventCategory
|
||||
- eventID
|
||||
- eventName
|
||||
- eventSource
|
||||
- eventTime
|
||||
- eventType
|
||||
- eventVersion
|
||||
- eventtype
|
||||
- host
|
||||
- index
|
||||
- linecount
|
||||
- managementEvent
|
||||
- msg
|
||||
- object
|
||||
- object_category
|
||||
- object_id
|
||||
- product
|
||||
- protocol
|
||||
- protocol_code
|
||||
- punct
|
||||
- readOnly
|
||||
- recipientAccountId
|
||||
- region
|
||||
- requestID
|
||||
- requestParameters.aclProtocol
|
||||
- requestParameters.cidrBlock
|
||||
- requestParameters.egress
|
||||
- requestParameters.networkAclId
|
||||
- requestParameters.ruleAction
|
||||
- requestParameters.ruleNumber
|
||||
- responseElements._return
|
||||
- responseElements.requestId
|
||||
- rule_action
|
||||
- signature
|
||||
- source
|
||||
- sourceIPAddress
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- src
|
||||
- src_ip
|
||||
- src_ip_range
|
||||
- start_time
|
||||
- status
|
||||
- tag
|
||||
- tag::eventtype
|
||||
- timeendpos
|
||||
- timestartpos
|
||||
- user
|
||||
- userAgent
|
||||
- userIdentity.accessKeyId
|
||||
- userIdentity.accountId
|
||||
- userIdentity.arn
|
||||
- userIdentity.principalId
|
||||
- userIdentity.sessionContext.attributes.creationDate
|
||||
- userIdentity.sessionContext.attributes.mfaAuthenticated
|
||||
- userIdentity.sessionContext.sessionIssuer.accountId
|
||||
- userIdentity.sessionContext.sessionIssuer.arn
|
||||
- userIdentity.sessionContext.sessionIssuer.principalId
|
||||
- userIdentity.sessionContext.sessionIssuer.type
|
||||
- userIdentity.sessionContext.sessionIssuer.userName
|
||||
- userIdentity.type
|
||||
- userName
|
||||
- user_access_key
|
||||
- user_agent
|
||||
- user_arn
|
||||
- user_group_id
|
||||
- user_id
|
||||
- user_name
|
||||
- user_type
|
||||
- vendor
|
||||
- vendor_account
|
||||
- vendor_product
|
||||
- vendor_region
|
||||
example_log: '{"eventVersion": "1.08", "userIdentity": {"type": "AssumedRole", "principalId": "AROAIJIESMXKGCJRCTPR6:pbareiss@splunk.local", "arn": "arn:aws:sts::111111111111:assumed-role/okta_adm_role/pbareiss@splunk.local", "accountId": "111111111111", "accessKeyId": "ASIAYTOGP2RLF3F7BXZK", "sessionContext": {"sessionIssuer": {"type": "Role", "principalId": "AROAIJIESMXKGCJRCTPR6", "arn": "arn:aws:iam::111111111111:role/okta_adm_role", "accountId": "111111111111", "userName": "okta_adm_role"}, "webIdFederationData": {}, "attributes": {"mfaAuthenticated": "false", "creationDate": "2021-01-12T08:36:15Z"}}}, "eventTime": "2021-01-12T08:38:39Z", "eventSource": "ec2.amazonaws.com", "eventName": "CreateNetworkAclEntry", "awsRegion": "eu-central-1", "sourceIPAddress": "95.90.199.65", "userAgent": "console.ec2.amazonaws.com", "requestParameters": {"networkAclId": "acl-078ccebebcbabe175", "ruleNumber": 10, "egress": false, "ruleAction": "allow", "icmpTypeCode": {}, "portRange": {}, "aclProtocol": "-1", "cidrBlock": "0.0.0.0/0"}, "responseElements": {"requestId": "d29c9c32-3a72-48d3-b612-6ba795e9ec64", "_return": true}, "requestID": "d29c9c32-3a72-48d3-b612-6ba795e9ec64", "eventID": "6d1ce00e-4099-463c-8a4d-2af2fb2178ba", "readOnly": false, "eventType": "AwsApiCall", "managementEvent": true, "eventCategory": "Management", "recipientAccountId": "111111111111"}'
|
||||
@@ -0,0 +1,80 @@
|
||||
event_name: AWS CloudTrail CreatePolicyVersion
|
||||
fields:
|
||||
- _time
|
||||
- action
|
||||
- app
|
||||
- awsRegion
|
||||
- aws_account_id
|
||||
- change_type
|
||||
- command
|
||||
- date_hour
|
||||
- date_mday
|
||||
- date_minute
|
||||
- date_month
|
||||
- date_second
|
||||
- date_wday
|
||||
- date_year
|
||||
- date_zone
|
||||
- dest
|
||||
- dvc
|
||||
- errorCode
|
||||
- eventCategory
|
||||
- eventID
|
||||
- eventName
|
||||
- eventSource
|
||||
- eventTime
|
||||
- eventType
|
||||
- eventVersion
|
||||
- eventtype
|
||||
- host
|
||||
- index
|
||||
- linecount
|
||||
- managementEvent
|
||||
- msg
|
||||
- object_category
|
||||
- product
|
||||
- punct
|
||||
- readOnly
|
||||
- recipientAccountId
|
||||
- region
|
||||
- requestID
|
||||
- requestParameters.policyArn
|
||||
- requestParameters.policyDocument
|
||||
- requestParameters.setAsDefault
|
||||
- responseElements.policyVersion.createDate
|
||||
- responseElements.policyVersion.isDefaultVersion
|
||||
- responseElements.policyVersion.versionId
|
||||
- signature
|
||||
- source
|
||||
- sourceIPAddress
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- src
|
||||
- src_ip
|
||||
- start_time
|
||||
- status
|
||||
- tag
|
||||
- tag::eventtype
|
||||
- timeendpos
|
||||
- timestartpos
|
||||
- user
|
||||
- userAgent
|
||||
- userIdentity.accessKeyId
|
||||
- userIdentity.accountId
|
||||
- userIdentity.arn
|
||||
- userIdentity.principalId
|
||||
- userIdentity.type
|
||||
- userIdentity.userName
|
||||
- userName
|
||||
- user_access_key
|
||||
- user_agent
|
||||
- user_arn
|
||||
- user_group_id
|
||||
- user_id
|
||||
- user_name
|
||||
- user_type
|
||||
- vendor
|
||||
- vendor_account
|
||||
- vendor_product
|
||||
- vendor_region
|
||||
example_log: '{"eventVersion": "1.08", "userIdentity": {"type": "IAMUser", "principalId": "AIDAYTOGP2RLNMCDVJZAY", "arn": "arn:aws:iam::111111111111:user/rhino_escalate", "accountId": "111111111111", "accessKeyId": "AKIAYTOGP2RLHSQZPZFZ", "userName": "rhino_escalate"}, "eventTime": "2021-02-23T00:02:30Z", "eventSource": "iam.amazonaws.com", "eventName": "CreatePolicyVersion", "awsRegion": "us-east-1", "sourceIPAddress": "73.15.72.101", "userAgent": "aws-cli/2.0.62 Python/3.9.0 Darwin/19.6.0 source/x86_64 command/iam.create-policy-version", "requestParameters": {"policyArn": "arn:aws:iam::111111111111:policy/rhino_escalate", "policyDocument": "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Sid\": \"AllowEverything\",\n \"Effect\": \"Allow\",\n \"Action\": \"iam:*\",\n \"Resource\": \"*\"\n }\n ]\n }", "setAsDefault": true}, "responseElements": {"policyVersion": {"versionId": "v2", "isDefaultVersion": true, "createDate": "Feb 23, 2021 12:02:30 AM"}}, "requestID": "fa42b4b2-f34a-4673-8f9f-b25cf1f5005a", "eventID": "33149175-90fd-4cff-a43b-408e4f848c1c", "readOnly": false, "eventType": "AwsApiCall", "managementEvent": true, "eventCategory": "Management", "recipientAccountId": "111111111111"}'
|
||||
@@ -0,0 +1,89 @@
|
||||
event_name: AWS CloudTrail CreateSnapshot
|
||||
fields:
|
||||
- _time
|
||||
- app
|
||||
- awsRegion
|
||||
- aws_account_id
|
||||
- change_type
|
||||
- command
|
||||
- date_hour
|
||||
- date_mday
|
||||
- date_minute
|
||||
- date_month
|
||||
- date_second
|
||||
- date_wday
|
||||
- date_year
|
||||
- date_zone
|
||||
- dest
|
||||
- dvc
|
||||
- errorCode
|
||||
- eventCategory
|
||||
- eventID
|
||||
- eventName
|
||||
- eventSource
|
||||
- eventTime
|
||||
- eventType
|
||||
- eventVersion
|
||||
- eventtype
|
||||
- host
|
||||
- index
|
||||
- linecount
|
||||
- managementEvent
|
||||
- msg
|
||||
- object_category
|
||||
- product
|
||||
- punct
|
||||
- readOnly
|
||||
- recipientAccountId
|
||||
- region
|
||||
- requestID
|
||||
- requestParameters.tagSpecificationSet.items{}.resourceType
|
||||
- requestParameters.tagSpecificationSet.items{}.tags{}.key
|
||||
- requestParameters.tagSpecificationSet.items{}.tags{}.value
|
||||
- requestParameters.volumeId
|
||||
- responseElements.encrypted
|
||||
- responseElements.ownerId
|
||||
- responseElements.requestId
|
||||
- responseElements.snapshotId
|
||||
- responseElements.startTime
|
||||
- responseElements.status
|
||||
- responseElements.tagSet.items{}.key
|
||||
- responseElements.tagSet.items{}.value
|
||||
- responseElements.volumeId
|
||||
- responseElements.volumeSize
|
||||
- signature
|
||||
- source
|
||||
- sourceIPAddress
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- src
|
||||
- src_ip
|
||||
- start_time
|
||||
- tag
|
||||
- tag::eventtype
|
||||
- timeendpos
|
||||
- timestartpos
|
||||
- tlsDetails.cipherSuite
|
||||
- tlsDetails.clientProvidedHostHeader
|
||||
- tlsDetails.tlsVersion
|
||||
- user
|
||||
- userAgent
|
||||
- userIdentity.accessKeyId
|
||||
- userIdentity.accountId
|
||||
- userIdentity.arn
|
||||
- userIdentity.principalId
|
||||
- userIdentity.type
|
||||
- userIdentity.userName
|
||||
- userName
|
||||
- user_access_key
|
||||
- user_agent
|
||||
- user_arn
|
||||
- user_group_id
|
||||
- user_id
|
||||
- user_name
|
||||
- user_type
|
||||
- vendor
|
||||
- vendor_account
|
||||
- vendor_product
|
||||
- vendor_region
|
||||
example_log: '{"eventVersion": "1.08", "userIdentity": {"type": "IAMUser", "principalId": "AIDAYTOGP2RLCNEAQXWZV", "arn": "arn:aws:iam::111111111111:user/bhavin_console", "accountId": "111111111111", "accessKeyId": "AKIAYTOGP2RLF5EAXXXX", "userName": "bhavin_console"}, "eventTime": "2023-03-20T22:31:18Z", "eventSource": "ec2.amazonaws.com", "eventName": "CreateSnapshot", "awsRegion": "us-west-2", "sourceIPAddress": "72.135.1.1", "userAgent": "APN/1.0 HashiCorp/1.0 Terraform/1.1.2 (+https://www.terraform.io) terraform-provider-aws/3.76.1 (+https://registry.terraform.io/providers/hashicorp/aws) aws-sdk-go/1.44.157 (go1.19.3; darwin; amd64) stratus-red-team_46665bb8-dc15-4aba-a5ad-a362772b3f0d HashiCorp-terraform-exec/0.17.3", "requestParameters": {"volumeId": "vol-0363e53e12f67c9b7", "tagSpecificationSet": {"items": [{"resourceType": "snapshot", "tags": [{"key": "StratusRedTeam", "value": "true"}]}]}}, "responseElements": {"requestId": "fefed928-d461-45f0-802f-a99d94c833a8", "snapshotId": "snap-02effb3bb62786b18", "volumeId": "vol-0363e53e12f67c9b7", "status": "pending", "startTime": 1679351478226, "ownerId": "111111111111", "volumeSize": "1", "encrypted": false, "tagSet": {"items": [{"key": "StratusRedTeam", "value": "true"}]}}, "requestID": "fefed928-d461-45f0-802f-a99d94c833a8", "eventID": "2d52d141-d1e6-4d1f-a380-1461c1bf9f83", "readOnly": false, "eventType": "AwsApiCall", "managementEvent": true, "recipientAccountId": "111111111111", "eventCategory": "Management", "tlsDetails": {"tlsVersion": "TLSv1.2", "cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256", "clientProvidedHostHeader": "ec2.us-west-2.amazonaws.com"}}'
|
||||
@@ -0,0 +1,88 @@
|
||||
event_name: AWS CloudTrail CreateTask
|
||||
fields:
|
||||
- _time
|
||||
- app
|
||||
- awsRegion
|
||||
- aws_account_id
|
||||
- command
|
||||
- date_hour
|
||||
- date_mday
|
||||
- date_minute
|
||||
- date_month
|
||||
- date_second
|
||||
- date_wday
|
||||
- date_year
|
||||
- date_zone
|
||||
- dest
|
||||
- dvc
|
||||
- errorCode
|
||||
- eventCategory
|
||||
- eventID
|
||||
- eventName
|
||||
- eventSource
|
||||
- eventTime
|
||||
- eventType
|
||||
- eventVersion
|
||||
- eventtype
|
||||
- host
|
||||
- index
|
||||
- linecount
|
||||
- managementEvent
|
||||
- msg
|
||||
- object_category
|
||||
- product
|
||||
- punct
|
||||
- readOnly
|
||||
- recipientAccountId
|
||||
- region
|
||||
- requestID
|
||||
- requestParameters.cloudWatchLogGroupArn
|
||||
- requestParameters.destinationLocationArn
|
||||
- requestParameters.options.logLevel
|
||||
- requestParameters.options.verifyMode
|
||||
- requestParameters.schedule.scheduleExpression
|
||||
- requestParameters.sourceLocationArn
|
||||
- responseElements.taskArn
|
||||
- sessionCredentialFromConsole
|
||||
- signature
|
||||
- source
|
||||
- sourceIPAddress
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- src
|
||||
- src_ip
|
||||
- start_time
|
||||
- tag
|
||||
- tag::eventtype
|
||||
- timeendpos
|
||||
- timestartpos
|
||||
- tlsDetails.cipherSuite
|
||||
- tlsDetails.clientProvidedHostHeader
|
||||
- tlsDetails.tlsVersion
|
||||
- user
|
||||
- userAgent
|
||||
- userIdentity.accessKeyId
|
||||
- userIdentity.accountId
|
||||
- userIdentity.arn
|
||||
- userIdentity.principalId
|
||||
- userIdentity.sessionContext.attributes.creationDate
|
||||
- userIdentity.sessionContext.attributes.mfaAuthenticated
|
||||
- userIdentity.sessionContext.sessionIssuer.accountId
|
||||
- userIdentity.sessionContext.sessionIssuer.arn
|
||||
- userIdentity.sessionContext.sessionIssuer.principalId
|
||||
- userIdentity.sessionContext.sessionIssuer.type
|
||||
- userIdentity.sessionContext.sessionIssuer.userName
|
||||
- userIdentity.type
|
||||
- userName
|
||||
- user_access_key
|
||||
- user_agent
|
||||
- user_arn
|
||||
- user_group_id
|
||||
- user_id
|
||||
- user_name
|
||||
- user_type
|
||||
- vendor
|
||||
- vendor_account
|
||||
- vendor_product
|
||||
- vendor_region
|
||||
example_log: '{"eventVersion": "1.08", "userIdentity": {"type": "AssumedRole", "principalId": "AROAYTOGP2RLDF6WQQQQQ:abc@acme.com", "arn": "arn:aws:sts::111111111111:assumed-role/AWSReservedSSO_SPLKAdministratorAccess_d9ce1347d0a6dd3f/abc@acme.com", "accountId": "111111111111", "accessKeyId": "ASIAYTOGP2RLOB2GM111", "sessionContext": {"sessionIssuer": {"type": "Role", "principalId": "AROAYTOGP2RLDF6WQQQQQ", "arn": "arn:aws:iam::111111111111:role/aws-reserved/sso.amazonaws.com/us-west-2/AWSReservedSSO_SPLKAdministratorAccess_d9ce1347d0a6dd3f", "accountId": "111111111111", "userName": "AWSReservedSSO_SPLKAdministratorAccess_d9ce1347d0a6dd3f"}, "webIdFederationData": {}, "attributes": {"creationDate": "2023-03-14T21:53:15Z", "mfaAuthenticated": "false"}}}, "eventTime": "2023-03-14T22:05:36Z", "eventSource": "datasync.amazonaws.com", "eventName": "CreateTask", "awsRegion": "us-west-2", "sourceIPAddress": "1.1.1.1", "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36", "requestParameters": {"sourceLocationArn": "arn:aws:datasync:us-west-2:111111111111:location/loc-0921d426f7955d416", "destinationLocationArn": "arn:aws:datasync:us-west-1:111111111111:location/loc-0b94cf657c358ef06", "cloudWatchLogGroupArn": "arn:aws:logs:us-west-2:111111111111:log-group:/aws/datasync", "options": {"verifyMode": "ONLY_FILES_TRANSFERRED", "logLevel": "BASIC"}, "excludes": [], "schedule": {"scheduleExpression": "cron(6 * * * ? *)"}, "tags": [], "includes": []}, "responseElements": {"taskArn": "arn:aws:datasync:us-west-2:111111111111:task/task-0c77dc0d4b0792ce6"}, "requestID": "de5f4282-aa2b-49b8-8d1b-c3bdb11e2fba", "eventID": "def4cd05-f845-4aec-bc96-07d6ce420d16", "readOnly": false, "eventType": "AwsApiCall", "managementEvent": true, "recipientAccountId": "111111111111", "eventCategory": "Management", "tlsDetails": {"tlsVersion": "TLSv1.2", "cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256", "clientProvidedHostHeader": "datasync.us-west-2.amazonaws.com"}, "sessionCredentialFromConsole": "true"}'
|
||||
@@ -0,0 +1,78 @@
|
||||
event_name: AWS CloudTrail CreateVirtualMFADevice
|
||||
fields:
|
||||
- _time
|
||||
- action
|
||||
- app
|
||||
- awsRegion
|
||||
- aws_account_id
|
||||
- change_type
|
||||
- command
|
||||
- date_hour
|
||||
- date_mday
|
||||
- date_minute
|
||||
- date_month
|
||||
- date_second
|
||||
- date_wday
|
||||
- date_year
|
||||
- date_zone
|
||||
- dest
|
||||
- dvc
|
||||
- errorCode
|
||||
- eventCategory
|
||||
- eventID
|
||||
- eventName
|
||||
- eventSource
|
||||
- eventTime
|
||||
- eventType
|
||||
- eventVersion
|
||||
- eventtype
|
||||
- host
|
||||
- index
|
||||
- linecount
|
||||
- managementEvent
|
||||
- msg
|
||||
- object_category
|
||||
- product
|
||||
- punct
|
||||
- readOnly
|
||||
- recipientAccountId
|
||||
- region
|
||||
- requestID
|
||||
- requestParameters.path
|
||||
- requestParameters.virtualMFADeviceName
|
||||
- responseElements.virtualMFADevice.serialNumber
|
||||
- sessionCredentialFromConsole
|
||||
- signature
|
||||
- source
|
||||
- sourceIPAddress
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- src
|
||||
- src_ip
|
||||
- start_time
|
||||
- status
|
||||
- tag
|
||||
- tag::eventtype
|
||||
- timeendpos
|
||||
- timestartpos
|
||||
- user
|
||||
- userAgent
|
||||
- userIdentity.accessKeyId
|
||||
- userIdentity.accountId
|
||||
- userIdentity.arn
|
||||
- userIdentity.principalId
|
||||
- userIdentity.sessionContext.attributes.creationDate
|
||||
- userIdentity.sessionContext.attributes.mfaAuthenticated
|
||||
- userIdentity.type
|
||||
- userName
|
||||
- user_access_key
|
||||
- user_agent
|
||||
- user_arn
|
||||
- user_group_id
|
||||
- user_id
|
||||
- user_type
|
||||
- vendor
|
||||
- vendor_account
|
||||
- vendor_product
|
||||
- vendor_region
|
||||
example_log: '{"eventVersion": "1.08", "userIdentity": {"type": "Root", "principalId": "140429656527", "arn": "arn:aws:iam::140429656527:root", "accountId": "140429656527", "accessKeyId": "ASIASBMSCQHH2YXNXJBU", "sessionContext": {"sessionIssuer": {}, "webIdFederationData": {}, "attributes": {"creationDate": "2023-01-30T22:59:36Z", "mfaAuthenticated": "false"}}}, "eventTime": "2023-01-30T23:02:23Z", "eventSource": "iam.amazonaws.com", "eventName": "CreateVirtualMFADevice", "awsRegion": "us-east-1", "sourceIPAddress": "23.93.193.6", "userAgent": "AWS Internal", "requestParameters": {"path": "/", "virtualMFADeviceName": "strt_mfa_2"}, "responseElements": {"virtualMFADevice": {"serialNumber": "arn:aws:iam::140429656527:mfa/strt_mfa_2"}}, "requestID": "2fbe2074-55f8-4ec6-ad32-0b250803cf46", "eventID": "7e1c493d-c3c3-4f4a-ae4f-8cdd38970027", "readOnly": false, "eventType": "AwsApiCall", "managementEvent": true, "recipientAccountId": "140429656527", "eventCategory": "Management", "sessionCredentialFromConsole": "true"}'
|
||||
@@ -0,0 +1,78 @@
|
||||
event_name: AWS CloudTrail DeactivateMFADevice
|
||||
fields:
|
||||
- _time
|
||||
- action
|
||||
- app
|
||||
- awsRegion
|
||||
- aws_account_id
|
||||
- change_type
|
||||
- command
|
||||
- date_hour
|
||||
- date_mday
|
||||
- date_minute
|
||||
- date_month
|
||||
- date_second
|
||||
- date_wday
|
||||
- date_year
|
||||
- date_zone
|
||||
- dest
|
||||
- dvc
|
||||
- errorCode
|
||||
- eventCategory
|
||||
- eventID
|
||||
- eventName
|
||||
- eventSource
|
||||
- eventTime
|
||||
- eventType
|
||||
- eventVersion
|
||||
- eventtype
|
||||
- host
|
||||
- index
|
||||
- linecount
|
||||
- managementEvent
|
||||
- msg
|
||||
- object_category
|
||||
- product
|
||||
- punct
|
||||
- readOnly
|
||||
- recipientAccountId
|
||||
- region
|
||||
- requestID
|
||||
- requestParameters.serialNumber
|
||||
- requestParameters.userName
|
||||
- responseElements
|
||||
- signature
|
||||
- source
|
||||
- sourceIPAddress
|
||||
- sourcetype
|
||||
- splunk_server
|
||||
- src
|
||||
- src_ip
|
||||
- start_time
|
||||
- status
|
||||
- tag
|
||||
- tag::eventtype
|
||||
- timeendpos
|
||||
- timestartpos
|
||||
- user
|
||||
- userAgent
|
||||
- userIdentity.accessKeyId
|
||||
- userIdentity.accountId
|
||||
- userIdentity.arn
|
||||
- userIdentity.principalId
|
||||
- userIdentity.sessionContext.attributes.creationDate
|
||||
- userIdentity.sessionContext.attributes.mfaAuthenticated
|
||||
- userIdentity.type
|
||||
- userName
|
||||
- user_access_key
|
||||
- user_agent
|
||||
- user_arn
|
||||
- user_group_id
|
||||
- user_id
|
||||
- user_name
|
||||
- user_type
|
||||
- vendor
|
||||
- vendor_account
|
||||
- vendor_product
|
||||
- vendor_region
|
||||
example_log: '{"eventVersion": "1.08", "userIdentity": {"type": "Root", "principalId": "111111111111", "arn": "arn:aws:iam::111111111111:root", "accountId": "111111111111", "accessKeyId": "ASIASBMSCQHHWAIHMHUX", "sessionContext": {"sessionIssuer": {}, "webIdFederationData": {}, "attributes": {"creationDate": "2022-10-04T16:13:23Z", "mfaAuthenticated": "true"}}}, "eventTime": "2022-10-04T16:13:45Z", "eventSource": "iam.amazonaws.com", "eventName": "DeactivateMFADevice", "awsRegion": "us-east-1", "sourceIPAddress": "142.254.89.27", "userAgent": "Coral/Netty4", "requestParameters": {"userName": "AWS ROOT USER", "serialNumber": "arn:aws:iam::111111111111:mfa/root-account-mfa-device"}, "responseElements": null, "requestID": "d27cfb15-34b4-4c16-82bc-a55d15b4e47d", "eventID": "bfe9fd91-0b4d-470a-9c03-77839151806d", "readOnly": false, "eventType": "AwsApiCall", "managementEvent": true, "recipientAccountId": "111111111111", "eventCategory": "Management"}'
|
||||