diff --git a/send_deep_security_events_to_aws_security_hub.py b/send_deep_security_events_to_aws_security_hub.py index 395a478..de655fd 100644 --- a/send_deep_security_events_to_aws_security_hub.py +++ b/send_deep_security_events_to_aws_security_hub.py @@ -17,375 +17,375 @@ import boto3 def evaluate_deep_security_event(deep_security_event): - """ - Evaluate the specified Deep Security for security importance - """ - result = False - - # Is this event related to an AWS instance? - if 'HostInstanceID' in deep_security_event: - print("Deep Security event is related to an AWS instance [{}]".format(deep_security_event['HostInstanceID'])) - - # Each Deep Security event type has different criteria for forwarding, verify those properties - if 'EventType' in deep_security_event: - if deep_security_event['EventType'] == "SystemEvent": - # Ignore, generated by Deep Security as a platform. Includes events like agent updates, communication issues, etc. - print("Ignoring system event from Deep Security") - elif deep_security_event['EventType'] == "PacketLog": - # Firewall events - if 'RepeatCount' in deep_security_event: - print("Firewall event detected, checking repetition levels") - if int(deep_security_event['RepeatCount']) >= 5: - result = True - elif deep_security_event['EventType'] == "PayloadLog": - # Intrusion prevention events - if 'Severity' in deep_security_event: - print("Intrusion prevention event detected, checking severity") - if int(deep_security_event['Severity']) >= 3: - result = True - elif deep_security_event['EventType'] == "AntiMalwareEvent": - # Anti-malware events - print("Malware event detected, checking the path of the infected file") - if 'InfectedFilePath' in deep_security_event: - if len(deep_security_event['InfectedFilePath']) > 0: - result = True - elif deep_security_event['EventType'] == "WebReputationEvent": - # Web reputation events - print("Web reputation event detected, checking the associated risk") - if 'Risk' in deep_security_event: - if int(deep_security_event['Risk']) >= 3: - result = True - elif deep_security_event['EventType'] == "IntegrityEvent": - # Integrity monitoring events - if 'Severity' in deep_security_event: - print("Integrity monitoring event detected, checking severity") - if int(deep_security_event['Severity']) >= 3: - result = True - elif deep_security_event['EventType'] == "LogInspectionEvent": - # Log inspection events - if 'OSSEC_Level' in deep_security_event: - print("Log inspection event detected, checking risk level") - if int(deep_security_event['OSSEC_Level']) >= 8: - result = True - elif deep_security_event['EventType'] == "AppControlEvent": - # Application control events - pass # TODO: Add risk evaluation for this type of event - - return result + """ + Evaluate the specified Deep Security for security importance + """ + result = False + + # Is this event related to an AWS instance? + if 'HostInstanceID' in deep_security_event: + print("Deep Security event is related to an AWS instance [{}]".format(deep_security_event['HostInstanceID'])) + + # Each Deep Security event type has different criteria for forwarding, verify those properties + if 'EventType' in deep_security_event: + if deep_security_event['EventType'] == "SystemEvent": + # Ignore, generated by Deep Security as a platform. Includes events like agent updates, communication issues, etc. + print("Ignoring system event from Deep Security") + elif deep_security_event['EventType'] == "PacketLog": + # Firewall events + if 'RepeatCount' in deep_security_event: + print("Firewall event detected, checking repetition levels") + if int(deep_security_event['RepeatCount']) >= 5: + result = True + elif deep_security_event['EventType'] == "PayloadLog": + # Intrusion prevention events + if 'Severity' in deep_security_event: + print("Intrusion prevention event detected, checking severity") + if int(deep_security_event['Severity']) >= 3: + result = True + elif deep_security_event['EventType'] == "AntiMalwareEvent": + # Anti-malware events + print("Malware event detected, checking the path of the infected file") + if 'InfectedFilePath' in deep_security_event: + if len(deep_security_event['InfectedFilePath']) > 0: + result = True + elif deep_security_event['EventType'] == "WebReputationEvent": + # Web reputation events + print("Web reputation event detected, checking the associated risk") + if 'Risk' in deep_security_event: + if int(deep_security_event['Risk']) >= 3: + result = True + elif deep_security_event['EventType'] == "IntegrityEvent": + # Integrity monitoring events + if 'Severity' in deep_security_event: + print("Integrity monitoring event detected, checking severity") + if int(deep_security_event['Severity']) >= 3: + result = True + elif deep_security_event['EventType'] == "LogInspectionEvent": + # Log inspection events + if 'OSSEC_Level' in deep_security_event: + print("Log inspection event detected, checking risk level") + if int(deep_security_event['OSSEC_Level']) >= 8: + result = True + elif deep_security_event['EventType'] == "AppControlEvent": + # Application control events + pass # TODO: Add risk evaluation for this type of event + + return result def generate_finding_title(title): - """ - Generate a consistent title for a finding in AWS Security Hub - * Setup as a function for consistency - """ - return "Trend Micro: {}".format(title) + """ + Generate a consistent title for a finding in AWS Security Hub + * Setup as a function for consistency + """ + return "Trend Micro: {}".format(title) def verify_required_properties(deep_security_event): - """ - Verify if the specified Deep Security event contains the required properties to - be convert to an Amazon Finding Format finding - """ - result = False - - required_properties = [ - 'HostOwnerID', - 'HostInstanceID', - 'TenantID', - 'EventID', - 'EventType', - 'LogDate', - 'HostAssetValue', - 'HostGroupID', - 'HostGroupName', - 'HostID', - 'Hostname', - 'HostSecurityPolicyID', - 'HostSecurityPolicyName', - ] - - for prop in required_properties: - if prop in deep_security_event: - result = True - - return result + """ + Verify if the specified Deep Security event contains the required properties to + be convert to an Amazon Finding Format finding + """ + result = False + + required_properties = [ + 'HostOwnerID', + 'HostInstanceID', + 'TenantID', + 'EventID', + 'EventType', + 'LogDate', + 'HostAssetValue', + 'HostGroupID', + 'HostGroupName', + 'HostID', + 'Hostname', + 'HostSecurityPolicyID', + 'HostSecurityPolicyName', + ] + + for prop in required_properties: + if prop in deep_security_event: + result = True + + return result def convert_deep_security_event_to_aff(deep_security_event): - """ - Convert a Deep Security to the Amazon Finding Format with all required and - any applicable option properties - """ - event_types = { - 'SystemEvent': 'system', - 'PacketLog': 'firewall', - 'PayloadLog': 'ips', - 'AntiMalwareEvent': 'antimalware', - 'WebReputationEvent': 'webreputation', - 'IntegrityEvent': 'integrity', - 'LogInspectionEvent': 'log', - 'AppControlEvent': 'applicationcontrol', - } - - deep_security_product_arns = { - "us-east-2": "arn:aws:securityhub:us-east-2:679593333241:product/trend-micro/deep-security", - "us-east-1": "arn:aws:securityhub:us-east-1:679593333241:product/trend-micro/deep-security", - "us-west-1": "arn:aws:securityhub:us-west-1:679593333241:product/trend-micro/deep-security", - "us-west-2": "arn:aws:securityhub:us-west-2:679593333241:product/trend-micro/deep-security", - "ap-south-1": "arn:aws:securityhub:ap-south-1:679593333241:product/trend-micro/deep-security", - "ap-northeast-2": "arn:aws:securityhub:ap-northeast-2:679593333241:product/trend-micro/deep-security", - "ap-southeast-1": "arn:aws:securityhub:ap-southeast-1:679593333241:product/trend-micro/deep-security", - "ap-southeast-2": "arn:aws:securityhub:ap-southeast-2:679593333241:product/trend-micro/deep-security", - "ap-northeast-1": "arn:aws:securityhub:ap-northeast-1:679593333241:product/trend-micro/deep-security", - "ca-central-1": "arn:aws:securityhub:ca-central-1:679593333241:product/trend-micro/deep-security", - "eu-central-1": "arn:aws:securityhub:eu-central-1:679593333241:product/trend-micro/deep-security", - "eu-west-1": "arn:aws:securityhub:eu-west-1:679593333241:product/trend-micro/deep-security", - "eu-west-2": "arn:aws:securityhub:eu-west-2:679593333241:product/trend-micro/deep-security", - "eu-west-3": "arn:aws:securityhub:eu-west-3:679593333241:product/trend-micro/deep-security", - "sa-east-1": "arn:aws:securityhub:sa-east-1:679593333241:product/trend-micro/deep-security", - } - - aff_format = { - "SchemaVersion": "2018-10-08", - # Id == AWS region / AWS account / EC2 instance ID / Deep Security Tenant ID / Deep Security Event ID - "Id": "{}/{}/{}/{}/{}".format(os.environ['AWS_REGION'], deep_security_event['HostOwnerID'], deep_security_event['HostInstanceID'], deep_security_event['TenantID'], deep_security_event['EventID']), - "ProductArn": deep_security_product_arns[os.environ['AWS_REGION']] if 'AWS_REGION' in os.environ else deep_security_product_arns[boto3.session.Session().region_name], - "GeneratorId": "trend-micro-deep-security-{}".format(event_types[deep_security_event['EventType']]), - "AwsAccountId": deep_security_event['HostOwnerID'], # AWS account for the event source pulled from Deep Security AWS cloud connector - "Types": [ # Specific types are added to align with event type - ], - "FirstObservedAt": deep_security_event['LogDate'], # ISO8601 formatted - "UpdatedAt": "{}Z".format(datetime.datetime.utcnow().isoformat()), # Z suffix required by API - "CreatedAt": "{}Z".format(datetime.datetime.utcnow().isoformat()), # Z suffix required by API - "Severity": { - "Product": 0, - "Normalized": 0, - }, - "Title": "", # optional - #"SourceUrl": "", # optional - "ProductFields": { - 'trend-micro:TenantName': deep_security_event['TenantName'], - 'trend-micro:TenantID': str(deep_security_event['TenantID']), - 'trend-micro:EventID': str(deep_security_event['EventID']), - 'trend-micro:HostAssetValue': str(deep_security_event['HostAssetValue']), - 'trend-micro:HostGroupID': str(deep_security_event['HostGroupID']), - 'trend-micro:HostGroupName': deep_security_event['HostGroupName'], - 'trend-micro:HostID': str(deep_security_event['HostID']), - 'trend-micro:HostInstanceID': str(deep_security_event['HostInstanceID']), - 'trend-micro:Hostname': deep_security_event['Hostname'], - 'trend-micro:HostSecurityPolicyID': str(deep_security_event['HostSecurityPolicyID']), - 'trend-micro:HostSecurityPolicyName': deep_security_event['HostSecurityPolicyName'], - }, # optional, added to - #"Malware": {}, # added when a malware event is detected - "Network": {}, # optional - "RecordState": "ACTIVE", - #"ThreatIntelIndicators": {}, # optional - "Resources": [ - { - "Type": "AwsEc2Instance", - "Id": deep_security_event['HostInstanceID'], - } - ], - } - - if 'Tags' in deep_security_event: - aff_format['ProductFields']['trend-micro:Tags'] = deep_security_event['Tags'] - if 'OriginString' in deep_security_event: - aff_format['ProductFields']['trend-micro:Origin'] = deep_security_event['OriginString'] - - # Apply custom properties based on Deep Security event type - if deep_security_event['EventType'] == "SystemEvent": - # Ignore, generated by Deep Security as a platform. Includes events like agent updates, communication issues, etc. - pass - elif deep_security_event['EventType'] == "PacketLog": - # Firewall events - aff_format['Severity']['Product'] = 0 - aff_format['Severity']['Normalized'] = int(20) # An "could result in future compromises" finding in the AFF format - aff_format['Types'].append("Unusual Behaviors/Network Flow") - aff_format['Title'] = generate_finding_title("Repeated attempted network connection on instance {}".format(deep_security_event['HostInstanceID'])) - elif deep_security_event['EventType'] == "PayloadLog": - # Intrusion prevention events - if 'Severity' in deep_security_event: - aff_format['Severity']['Product'] = int(deep_security_event['Severity']) - aff_format['Severity']['Normalized'] = int(int(deep_security_event['Severity']) * 17.5) # to match the 31-70 range in the AFF format - - # Add the finding type - aff_format['Types'].append("Software and Configuration Checks Vulnerabilities/Vulnerabilities/CVE") - aff_format['Title'] = generate_finding_title("Rule [{}] triggered".format(deep_security_event['Reason'])) - elif deep_security_event['EventType'] == "AntiMalwareEvent": - # Anti-malware events - aff_format['Malware'] = [ - { - "Name": deep_security_event['MalwareName'], - "Path": deep_security_event['InfectedFilePath'], - } - ] - aff_format['Types'].append("TPPs/Execution") - aff_format['Title'] = generate_finding_title("Malware [{}] detected".format(deep_security_event['MalwareName'])) - elif deep_security_event['EventType'] == "WebReputationEvent": - # Web reputation events - if 'Risk' in deep_security_event: - aff_format['Severity']['Product'] = int(deep_security_event['Risk']) - aff_format['Severity']['Normalized'] = int(int(deep_security_event['Risk']) * 17.5) # to match the 31-70 range in the AFF format - aff_format['Types'].append("TPPs/Execution") - aff_format['Title'] = generate_finding_title("High risk web request to IP [{}]".format(deep_security_event['TargetIP'])) - elif deep_security_event['EventType'] == "IntegrityEvent": - # Integrity monitoring events - if 'Severity' in deep_security_event: - aff_format['Severity']['Product'] = int(deep_security_event['Severity']) - aff_format['Severity']['Normalized'] = int(int(deep_security_event['Severity']) * 17.5) # to match the 31-70 range in the AFF format - aff_format['Types'].append("Unusual Behaviors/VM") - aff_format['Title'] = generate_finding_title("Unexpected change to object [{}]".format(deep_security_event['Key'])) - elif deep_security_event['EventType'] == "LogInspectionEvent": - # Log inspection events - if 'OSSEC_Level' in deep_security_event: - aff_format['Severity']['Product'] = int(deep_security_event['OSSEC_Level']) - if int(deep_security_event['OSSEC_Level']) >= 13: - aff_format['Severity']['Normalized'] = int(int(deep_security_event['OSSEC_Level']) * 6.5) # to match the 71-100 range in the AFF format - else: - aff_format['Severity']['Normalized'] = int(int(deep_security_event['OSSEC_Level']) * 5) # to match the 31-70 range in the AFF format - aff_format['Types'].append("Unusual Behaviors/VM") - aff_format['Types'].append("Unusual Behaviors/Application") - aff_format['Title'] = generate_finding_title(deep_security_event['OSSEC_Description']) - elif deep_security_event['EventType'] == "AppControlEvent": - # Application control events - aff_format['Types'].append("Unusual Behaviors/Application") - pass # TODO: Add severity normalization for this type of event - - converted_event = aff_format - print(converted_event['ProductArn']) - return converted_event + """ + Convert a Deep Security to the Amazon Finding Format with all required and + any applicable option properties + """ + event_types = { + 'SystemEvent': 'system', + 'PacketLog': 'firewall', + 'PayloadLog': 'ips', + 'AntiMalwareEvent': 'antimalware', + 'WebReputationEvent': 'webreputation', + 'IntegrityEvent': 'integrity', + 'LogInspectionEvent': 'log', + 'AppControlEvent': 'applicationcontrol', + } + + deep_security_product_arns = { + "us-east-2": "arn:aws:securityhub:us-east-2:679593333241:product/trend-micro/deep-security", + "us-east-1": "arn:aws:securityhub:us-east-1:679593333241:product/trend-micro/deep-security", + "us-west-1": "arn:aws:securityhub:us-west-1:679593333241:product/trend-micro/deep-security", + "us-west-2": "arn:aws:securityhub:us-west-2:679593333241:product/trend-micro/deep-security", + "ap-south-1": "arn:aws:securityhub:ap-south-1:679593333241:product/trend-micro/deep-security", + "ap-northeast-2": "arn:aws:securityhub:ap-northeast-2:679593333241:product/trend-micro/deep-security", + "ap-southeast-1": "arn:aws:securityhub:ap-southeast-1:679593333241:product/trend-micro/deep-security", + "ap-southeast-2": "arn:aws:securityhub:ap-southeast-2:679593333241:product/trend-micro/deep-security", + "ap-northeast-1": "arn:aws:securityhub:ap-northeast-1:679593333241:product/trend-micro/deep-security", + "ca-central-1": "arn:aws:securityhub:ca-central-1:679593333241:product/trend-micro/deep-security", + "eu-central-1": "arn:aws:securityhub:eu-central-1:679593333241:product/trend-micro/deep-security", + "eu-west-1": "arn:aws:securityhub:eu-west-1:679593333241:product/trend-micro/deep-security", + "eu-west-2": "arn:aws:securityhub:eu-west-2:679593333241:product/trend-micro/deep-security", + "eu-west-3": "arn:aws:securityhub:eu-west-3:679593333241:product/trend-micro/deep-security", + "sa-east-1": "arn:aws:securityhub:sa-east-1:679593333241:product/trend-micro/deep-security", + } + + aff_format = { + "SchemaVersion": "2018-10-08", + # Id == AWS region / AWS account / EC2 instance ID / Deep Security Tenant ID / Deep Security Event ID + "Id": "{}/{}/{}/{}/{}".format(os.environ['AWS_REGION'], deep_security_event['HostOwnerID'], deep_security_event['HostInstanceID'], deep_security_event['TenantID'], deep_security_event['EventID']), + "ProductArn": deep_security_product_arns[os.environ['AWS_REGION']] if 'AWS_REGION' in os.environ else deep_security_product_arns[boto3.session.Session().region_name], + "GeneratorId": "trend-micro-deep-security-{}".format(event_types[deep_security_event['EventType']]), + "AwsAccountId": deep_security_event['HostOwnerID'], # AWS account for the event source pulled from Deep Security AWS cloud connector + "Types": [ # Specific types are added to align with event type + ], + "FirstObservedAt": deep_security_event['LogDate'], # ISO8601 formatted + "UpdatedAt": "{}Z".format(datetime.datetime.utcnow().isoformat()), # Z suffix required by API + "CreatedAt": "{}Z".format(datetime.datetime.utcnow().isoformat()), # Z suffix required by API + "Severity": { + "Product": 0, + "Normalized": 0, + }, + "Title": "", # optional + #"SourceUrl": "", # optional + "ProductFields": { + 'trend-micro:TenantName': deep_security_event['TenantName'], + 'trend-micro:TenantID': str(deep_security_event['TenantID']), + 'trend-micro:EventID': str(deep_security_event['EventID']), + 'trend-micro:HostAssetValue': str(deep_security_event['HostAssetValue']), + 'trend-micro:HostGroupID': str(deep_security_event['HostGroupID']), + 'trend-micro:HostGroupName': deep_security_event['HostGroupName'], + 'trend-micro:HostID': str(deep_security_event['HostID']), + 'trend-micro:HostInstanceID': str(deep_security_event['HostInstanceID']), + 'trend-micro:Hostname': deep_security_event['Hostname'], + 'trend-micro:HostSecurityPolicyID': str(deep_security_event['HostSecurityPolicyID']), + 'trend-micro:HostSecurityPolicyName': deep_security_event['HostSecurityPolicyName'], + }, # optional, added to + #"Malware": {}, # added when a malware event is detected + "Network": {}, # optional + "RecordState": "ACTIVE", + #"ThreatIntelIndicators": {}, # optional + "Resources": [ + { + "Type": "AwsEc2Instance", + "Id": deep_security_event['HostInstanceID'], + } + ], + } + + if 'Tags' in deep_security_event: + aff_format['ProductFields']['trend-micro:Tags'] = deep_security_event['Tags'] + if 'OriginString' in deep_security_event: + aff_format['ProductFields']['trend-micro:Origin'] = deep_security_event['OriginString'] + + # Apply custom properties based on Deep Security event type + if deep_security_event['EventType'] == "SystemEvent": + # Ignore, generated by Deep Security as a platform. Includes events like agent updates, communication issues, etc. + pass + elif deep_security_event['EventType'] == "PacketLog": + # Firewall events + aff_format['Severity']['Product'] = 0 + aff_format['Severity']['Normalized'] = int(20) # An "could result in future compromises" finding in the AFF format + aff_format['Types'].append("Unusual Behaviors/Network Flow") + aff_format['Title'] = generate_finding_title("Repeated attempted network connection on instance {}".format(deep_security_event['HostInstanceID'])) + elif deep_security_event['EventType'] == "PayloadLog": + # Intrusion prevention events + if 'Severity' in deep_security_event: + aff_format['Severity']['Product'] = int(deep_security_event['Severity']) + aff_format['Severity']['Normalized'] = int(int(deep_security_event['Severity']) * 17.5) # to match the 31-70 range in the AFF format + + # Add the finding type + aff_format['Types'].append("Software and Configuration Checks Vulnerabilities/Vulnerabilities/CVE") + aff_format['Title'] = generate_finding_title("Rule [{}] triggered".format(deep_security_event['Reason'])) + elif deep_security_event['EventType'] == "AntiMalwareEvent": + # Anti-malware events + aff_format['Malware'] = [ + { + "Name": deep_security_event['MalwareName'], + "Path": deep_security_event['InfectedFilePath'], + } + ] + aff_format['Types'].append("TPPs/Execution") + aff_format['Title'] = generate_finding_title("Malware [{}] detected".format(deep_security_event['MalwareName'])) + elif deep_security_event['EventType'] == "WebReputationEvent": + # Web reputation events + if 'Risk' in deep_security_event: + aff_format['Severity']['Product'] = int(deep_security_event['Risk']) + aff_format['Severity']['Normalized'] = int(int(deep_security_event['Risk']) * 17.5) # to match the 31-70 range in the AFF format + aff_format['Types'].append("TPPs/Execution") + aff_format['Title'] = generate_finding_title("High risk web request to IP [{}]".format(deep_security_event['TargetIP'])) + elif deep_security_event['EventType'] == "IntegrityEvent": + # Integrity monitoring events + if 'Severity' in deep_security_event: + aff_format['Severity']['Product'] = int(deep_security_event['Severity']) + aff_format['Severity']['Normalized'] = int(int(deep_security_event['Severity']) * 17.5) # to match the 31-70 range in the AFF format + aff_format['Types'].append("Unusual Behaviors/VM") + aff_format['Title'] = generate_finding_title("Unexpected change to object [{}]".format(deep_security_event['Key'])) + elif deep_security_event['EventType'] == "LogInspectionEvent": + # Log inspection events + if 'OSSEC_Level' in deep_security_event: + aff_format['Severity']['Product'] = int(deep_security_event['OSSEC_Level']) + if int(deep_security_event['OSSEC_Level']) >= 13: + aff_format['Severity']['Normalized'] = int(int(deep_security_event['OSSEC_Level']) * 6.5) # to match the 71-100 range in the AFF format + else: + aff_format['Severity']['Normalized'] = int(int(deep_security_event['OSSEC_Level']) * 5) # to match the 31-70 range in the AFF format + aff_format['Types'].append("Unusual Behaviors/VM") + aff_format['Types'].append("Unusual Behaviors/Application") + aff_format['Title'] = generate_finding_title(deep_security_event['OSSEC_Description']) + elif deep_security_event['EventType'] == "AppControlEvent": + # Application control events + aff_format['Types'].append("Unusual Behaviors/Application") + pass # TODO: Add severity normalization for this type of event + + converted_event = aff_format + print(converted_event['ProductArn']) + return converted_event def get_role_arn_for_acct(acct_number): - """ - Get the appropriate role for the specified account number - """ - # All roles are created via the provided CloudFormation template - # this lets us use a simple pattern to get the role ARN. - # - # Each AWS account sending findings to AWS Security Hub must have - # cross-account role created that allows this function to call - # BatchImportFindings on that account's behalf, otherwise findings - # from that account are rejected as lacking the proper permissions - return "arn:aws:iam::{}:role/trend-micro-aff-forward-to-aws-security-hub".format(acct_number) + """ + Get the appropriate role for the specified account number + """ + # All roles are created via the provided CloudFormation template + # this lets us use a simple pattern to get the role ARN. + # + # Each AWS account sending findings to AWS Security Hub must have + # cross-account role created that allows this function to call + # BatchImportFindings on that account's behalf, otherwise findings + # from that account are rejected as lacking the proper permissions + return "arn:aws:iam::{}:role/trend-micro-aff-forward-to-aws-security-hub".format(acct_number) def get_security_hub_client(acct_number=None): - """ - Get an AWS Security Hub client using the appropriate credentials - for the specified AWS account number - """ - securityhub = None - - credentials = None - role_arn = get_role_arn_for_acct(acct_number) - if role_arn: - # @TODO implement master/member when available - sts = boto3.client('sts') - sts_response = None - try: - sts_response = sts.assume_role(RoleArn=role_arn, RoleSessionName="deep-security-aff-event-forward-to-aws-security-hub") - except Exception as err: - print("Could not assume role [{}]. Threw exception:\n{}".format(role_arn, err)) - if sts_response and 'Credentials' in sts_response: - credentials = sts_response['Credentials'] - - if os.environ["AWS_REGION"]: - if credentials: - securityhub = boto3.client("securityhub", region_name=os.environ["AWS_REGION"], aws_access_key_id=credentials['AccessKeyId'], aws_secret_access_key=credentials['SecretAccessKey'], aws_session_token=credentials['SessionToken']) - else: - securityhub = boto3.client("securityhub", region_name=os.environ["AWS_REGION"]) - else: - if credentials: - securityhub = boto3.client("securityhub", aws_access_key_id=credentials['AccessKeyId'], aws_secret_access_key=credentials['SecretAccessKey'], aws_session_token=credentials['SessionToken']) - else: - securityhub = boto3.client("securityhub") - - return securityhub + """ + Get an AWS Security Hub client using the appropriate credentials + for the specified AWS account number + """ + securityhub = None + + credentials = None + role_arn = get_role_arn_for_acct(acct_number) + if role_arn: + # @TODO implement master/member when available + sts = boto3.client('sts') + sts_response = None + try: + sts_response = sts.assume_role(RoleArn=role_arn, RoleSessionName="deep-security-aff-event-forward-to-aws-security-hub") + except Exception as err: + print("Could not assume role [{}]. Threw exception:\n{}".format(role_arn, err)) + if sts_response and 'Credentials' in sts_response: + credentials = sts_response['Credentials'] + + if os.environ["AWS_REGION"]: + if credentials: + securityhub = boto3.client("securityhub", region_name=os.environ["AWS_REGION"], aws_access_key_id=credentials['AccessKeyId'], aws_secret_access_key=credentials['SecretAccessKey'], aws_session_token=credentials['SessionToken']) + else: + securityhub = boto3.client("securityhub", region_name=os.environ["AWS_REGION"]) + else: + if credentials: + securityhub = boto3.client("securityhub", aws_access_key_id=credentials['AccessKeyId'], aws_secret_access_key=credentials['SecretAccessKey'], aws_session_token=credentials['SessionToken']) + else: + securityhub = boto3.client("securityhub") + + return securityhub def send_events_to_hub(aff_events, acct_number=None): - """ - Send the specified Amazon Finding Format events to the AWS Security Hub - """ - result = False - - securityhub = get_security_hub_client(acct_number) - - result = None - try: - result = securityhub.batch_import_findings(Findings=aff_events) - except Exception as err: - if 'AccessDeniedException' in err.message: - if 'not authorized to perform' in err.message: - # The function does not have sufficient permissions to call BatchImportFindings - # and most likely needs a cross-account role setup - print("Insufficient permissions to send findings to the AWS Security Hub. A cross-account role is required for any findings that are not from the current account. Please use the provided CloudFormation template to create the required role in account [{}]".format(acct_number)) - else: - print("Could not send findings to AWS Security Hub. Threw exception:\n{}".format(err)) - else: - print("Could not send findings to AWS Security Hub. Threw exception:\n{}".format(err)) - - if 'ResponseMetadata' in result and 'HTTPStatusCode' in result['ResponseMetadata']: - if result['ResponseMetadata']['HTTPStatusCode'] == 200 and 'SuccessCount' in result and 'FailedCount' in result: - print("AWS Security Hub successfully received [{}] findings".format(result['SuccessCount'])) - if not result['FailedCount'] == 0: - print("AWS Security Hub failed to process [{}] findings".format(result['FailedCount'])) - - return result + """ + Send the specified Amazon Finding Format events to the AWS Security Hub + """ + result = False + + securityhub = get_security_hub_client(acct_number) + + result = None + try: + result = securityhub.batch_import_findings(Findings=aff_events) + except Exception as err: + if 'AccessDeniedException' in err.message: + if 'not authorized to perform' in err.message: + # The function does not have sufficient permissions to call BatchImportFindings + # and most likely needs a cross-account role setup + print("Insufficient permissions to send findings to the AWS Security Hub. A cross-account role is required for any findings that are not from the current account. Please use the provided CloudFormation template to create the required role in account [{}]".format(acct_number)) + else: + print("Could not send findings to AWS Security Hub. Threw exception:\n{}".format(err)) + else: + print("Could not send findings to AWS Security Hub. Threw exception:\n{}".format(err)) + + if 'ResponseMetadata' in result and 'HTTPStatusCode' in result['ResponseMetadata']: + if result['ResponseMetadata']['HTTPStatusCode'] == 200 and 'SuccessCount' in result and 'FailedCount' in result: + print("AWS Security Hub successfully received [{}] findings".format(result['SuccessCount'])) + if not result['FailedCount'] == 0: + print("AWS Security Hub failed to process [{}] findings".format(result['FailedCount'])) + + return result def lambda_handler(event, context): - # Workflow: - # 1. Verify that this is an event from an Amazon SNS topic - # 2. Extract the Deep Security event data - # 3. Evaluate the event for security importance - # 4. Convert selected events to the Amazon Finding Format (AFF) - # 5. Send select events (in AFF) to the AWS Security Hub - total_events = 0 - forwarded_events = 0 - - if 'Records' in event: - # 1. Verify that this is an event from an Amazon SNS topic - for e in event['Records']: - if 'EventSource' in e and e['EventSource'] == 'aws:sns': - print("Amazon SNS message received") - # This is an Amazon SNS message - # 2. Extract the Deep Security event data - if 'Sns' in e: - deep_security_events = None - try: - deep_security_events = json.loads(e['Sns']['Message']) - print("Extracted Deep Security event(s) from the SNS message") - except Exception as err: - print("Could not extract the Deep Security event(s) from the SNS message. Threw exception:\n{}".format(err)) - - aff_events = [] - if deep_security_events: - print("Found {} Deep Security events...processing".format(len(deep_security_events))) - for deep_security_event in deep_security_events: - total_events += 1 - if verify_required_properties(deep_security_event): - forward_event = evaluate_deep_security_event(deep_security_event) - if forward_event: - print("Important security event detected, queuing to send to AWS Security Hub") - aff_event = convert_deep_security_event_to_aff(deep_security_event) - aff_events.append(aff_event) - forwarded_events += 1 - else: - print("Security event does not meet the criteria to send to AWS Security Hub") - else: - print("Specified event does not have the required properties to properly process it") - - if len(aff_events) > 0: - # Sort events by AWS account - aff_events_to_send = {} - for e in aff_events: - if 'AwsAccountId' in e and not e['AwsAccountId'] in aff_events_to_send: - aff_events_to_send[e['AwsAccountId']] = [] - aff_events_to_send[e['AwsAccountId']].append(e) - - # For each account, send the events - for acct_number, e in aff_events_to_send.items(): - print("Sending [{}] events to AWS SecuritY Hub for account [{}]".format(len(e), acct_number)) - send_events_to_hub(aff_events, acct_number=acct_number) - - return { - 'total_events': total_events, - 'events_forwarded': forwarded_events, - } \ No newline at end of file + # Workflow: + # 1. Verify that this is an event from an Amazon SNS topic + # 2. Extract the Deep Security event data + # 3. Evaluate the event for security importance + # 4. Convert selected events to the Amazon Finding Format (AFF) + # 5. Send select events (in AFF) to the AWS Security Hub + total_events = 0 + forwarded_events = 0 + + if 'Records' in event: + # 1. Verify that this is an event from an Amazon SNS topic + for e in event['Records']: + if 'EventSource' in e and e['EventSource'] == 'aws:sns': + print("Amazon SNS message received") + # This is an Amazon SNS message + # 2. Extract the Deep Security event data + if 'Sns' in e: + deep_security_events = None + try: + deep_security_events = json.loads(e['Sns']['Message']) + print("Extracted Deep Security event(s) from the SNS message") + except Exception as err: + print("Could not extract the Deep Security event(s) from the SNS message. Threw exception:\n{}".format(err)) + + aff_events = [] + if deep_security_events: + print("Found {} Deep Security events...processing".format(len(deep_security_events))) + for deep_security_event in deep_security_events: + total_events += 1 + if verify_required_properties(deep_security_event): + forward_event = evaluate_deep_security_event(deep_security_event) + if forward_event: + print("Important security event detected, queuing to send to AWS Security Hub") + aff_event = convert_deep_security_event_to_aff(deep_security_event) + aff_events.append(aff_event) + forwarded_events += 1 + else: + print("Security event does not meet the criteria to send to AWS Security Hub") + else: + print("Specified event does not have the required properties to properly process it") + + if len(aff_events) > 0: + # Sort events by AWS account + aff_events_to_send = {} + for e in aff_events: + if 'AwsAccountId' in e and not e['AwsAccountId'] in aff_events_to_send: + aff_events_to_send[e['AwsAccountId']] = [] + aff_events_to_send[e['AwsAccountId']].append(e) + + # For each account, send the events + for acct_number, e in aff_events_to_send.items(): + print("Sending [{}] events to AWS SecuritY Hub for account [{}]".format(len(e), acct_number)) + send_events_to_hub(aff_events, acct_number=acct_number) + + return { + 'total_events': total_events, + 'events_forwarded': forwarded_events, + } \ No newline at end of file