Skip to content

Commit

Permalink
Merge pull request #559 from 0xdabbad00/muting_improvements
Browse files Browse the repository at this point in the history
Muting improvements; Custom auditors
  • Loading branch information
0xdabbad00 authored Sep 26, 2019
2 parents e81c49b + 288d27e commit c6fd62a
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 17 deletions.
7 changes: 7 additions & 0 deletions config/audit_config_override.yaml.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,10 @@
# S3_PUBLIC_POLICY_GETOBJECT_ONLY:
# # This is an array of regexes, and must match the entire string
# ignore_resources: [".*demo"]

# Example 3: Use a custom auditor to add your own auditing functions
# CUSTOM_GUARDDUTY_OFF:
# title: "[Custom] GuardDuty is not enabled"
# description: GuardDuty is an AWS threat detection service that detects compromised access keys, EC2 instances, and more. It should be enabled in all regions.
# severity: Medium
# group: GuardDuty
69 changes: 69 additions & 0 deletions config/custom_auditor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from __future__ import print_function
from shared.query import query_aws, get_parameter_file
from shared.common import Finding

# To use custom auditing, you must copy this file to ./private_commands/custom_auditory.py
# and uncomment and modify the functions below.

__description__ = "Custom auditing functions"

# def custom_audit_guardduty(findings, region):
# """
# A custom auditor must be named a method that starts with "custom_audit_"
# You can create your own private auditors without needing to fork the project.
# You can mute the real ones, such as GUARDDUTY_OFF, and use your own.
# Note that you must add your own ID's and other info to audit_config_override.yaml
# """

# #
# # Custom logic
# #
# if region.name not in ['ap-southeast-1']:
# return

# #
# # The rest of this function is the same as real one
# #
# detector_list_json = query_aws(
# region.account, "guardduty-list-detectors", region
# )
# if not detector_list_json:
# # GuardDuty must not exist in this region (or the collect data is old)
# return
# is_enabled = False
# for detector in detector_list_json["DetectorIds"]:
# detector_json = get_parameter_file(
# region, "guardduty", "get-detector", detector
# )
# if detector_json["Status"] == "ENABLED":
# is_enabled = True
# if not is_enabled:
# findings.add(Finding(region, "CUSTOM_GUARDDUTY_OFF", None, None))


# def custom_filter(finding, conf):
# """
# Return True if the finding should be filtered, or false to allow it.
# """

# # This filters the GUARDDUTY_OFF rule, so that it only alerts if GuardDuty
# # is off in ap-southeast-1. This is useful if you have SCPs or similar to
# # disable other regions and therefore only care about issues in ap-southeast-1.
# if finding.issue_id == 'GUARDDUTY_OFF' and finding.region.name not in ['ap-southeast-1']:
# return True

# # Accessible data includes:
# # - finding.issue_id: Ex. "GUARDDUTY_OFF"
# # - finding.region.name: Ex. "ap-southeast-1"
# # - finding.region.account.name: The account name: Ex. "prod"
# # - finding.region.account.local_id: The Accoutn ID: Ex. "000000000000"
# # - finding.resource_id: Ex. the S3 bucket name
# # - finding.resource_details: The dictionary associated with the finding
# # The resource_details are finding type specific.
# # - conf["severity"]
# # - conf["title"]
# # - conf["description"]

# # Note that you could also modify the title, severity, etc.

# return False
83 changes: 66 additions & 17 deletions shared/audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import pyjq
import traceback
import re
import pkgutil
import importlib
import inspect

from policyuniverse.policy import Policy

Expand All @@ -21,6 +24,9 @@
from shared.nodes import Account, Region, get_name
from shared.iam_audit import find_admins_in_account

# Global
custom_filter = None


class Findings(object):
findings = None
Expand Down Expand Up @@ -49,6 +55,9 @@ def finding_is_filtered(finding, conf):
if re.search(ignore_regex, finding.resource_id):
return True

if custom_filter and custom_filter(finding, conf):
return True

return False


Expand All @@ -64,6 +73,13 @@ def load_audit_config():
# Over-write the values from audit_config
if audit_override:
for finding_id in audit_override:
if finding_id not in audit_config:
audit_config[finding_id] = {
"title": "Unknown",
"description": "Unknown",
"severity": "High",
"group": "unknown",
}
for k in audit_override[finding_id]:
audit_config[finding_id][k] = audit_override[finding_id][k]
return audit_config
Expand Down Expand Up @@ -169,23 +185,19 @@ def audit_s3_block_policy(findings, region):


def audit_guardduty(findings, region):
for region_json in get_regions(region.account):
region = Region(region.account, region_json)
detector_list_json = query_aws(
region.account, "guardduty-list-detectors", region
detector_list_json = query_aws(region.account, "guardduty-list-detectors", region)
if not detector_list_json:
# GuardDuty must not exist in this region (or the collect data is old)
return
is_enabled = False
for detector in detector_list_json["DetectorIds"]:
detector_json = get_parameter_file(
region, "guardduty", "get-detector", detector
)
if not detector_list_json:
# GuardDuty must not exist in this region (or the collect data is old)
continue
is_enabled = False
for detector in detector_list_json["DetectorIds"]:
detector_json = get_parameter_file(
region, "guardduty", "get-detector", detector
)
if detector_json["Status"] == "ENABLED":
is_enabled = True
if not is_enabled:
findings.add(Finding(region, "GUARDDUTY_OFF", None, None))
if detector_json["Status"] == "ENABLED":
is_enabled = True
if not is_enabled:
findings.add(Finding(region, "GUARDDUTY_OFF", None, None))


def audit_iam(findings, region):
Expand Down Expand Up @@ -976,11 +988,26 @@ def audit_lightsail(findings, region):
def audit(accounts):
findings = Findings()

custom_auditor = None
commands_path = "private_commands"
for importer, command_name, _ in pkgutil.iter_modules([commands_path]):
if "custom_auditor" != command_name:
continue

full_package_name = "%s.%s" % (commands_path, command_name)
custom_auditor = importlib.import_module(full_package_name)

for name, method in inspect.getmembers(custom_auditor, inspect.isfunction):
if name.startswith("custom_filter"):
global custom_filter
custom_filter = method

for account in accounts:
account = Account(None, account)

for region_json in get_regions(account):
region = Region(account, region_json)

try:
if region.name == "us-east-1":
audit_s3_buckets(findings, region)
Expand All @@ -992,7 +1019,7 @@ def audit(accounts):
audit_route53(findings, region)
audit_cloudfront(findings, region)
audit_s3_block_policy(findings, region)
audit_guardduty(findings, region)
audit_guardduty(findings, region)
audit_ebs_snapshots(findings, region)
audit_rds_snapshots(findings, region)
audit_rds(findings, region)
Expand Down Expand Up @@ -1020,4 +1047,26 @@ def audit(accounts):
},
)
)

# Run custom auditor if it exists
try:
if custom_auditor is not None:
for name, method in inspect.getmembers(
custom_auditor, inspect.isfunction
):
if name.startswith("custom_audit_"):
method(findings, region)
except Exception as e:
findings.add(
Finding(
region,
"EXCEPTION",
str(e),
resource_details={
"exception": str(e),
"traceback": str(traceback.format_exc()),
},
)
)

return findings

0 comments on commit c6fd62a

Please sign in to comment.