def cloudtrail_mfa_policy_abuse_attempt(rec): """ author: Scott Piper of Summit Route in collaboration with Duo Security description: Alert on potential attacks performed by users that are supposed to have MFA enforced. May indicate leaked access keys and an attempt to abuse a flawed MFA enforcement policy. playbook: (a) Identify whether or not the attempt was a mistake. (b) Begin IR: Roll access keys, investigate past CloudTrail logs for other actions performed, investigate how the keys were leaked in the first place. """ # Depending on the practices you follow with AWS today, you may wish to simply alert # on any errors at all in AWS, possibly with some focus on IAM actions, # and possibly with some exemptions to ignore things like failed login attempts. # Another option would be to just alert on any AccessDenied's when MFA is not used. # This rule attempts to reduce the false positives. # Get the value for whether the user is MFA authenticated. # If this doesn't exist, assume not MFA authenticated. try: mfa_authenticated = rec['userIdentity']['sessionContext'][ 'attributes']['mfaAuthenticated'] except KeyError: mfa_authenticated = 'false' # If the user is MFA authenticated, then any issues are not due to just a compromised # access key, so ignore it. if mfa_authenticated == 'true': return False # If the user tries to remove their MFA device without being MFA authenticated, # it could be an attacker trying to take advantage of an issue with an older AWS policy. if rec['eventName'] == 'DeactivateMFADevice': return True # Similarly, the attacker could try some other IAM actions under the assumption that the user # is an admin with the flawed policy. There are a lot of actions they could try, which should # be blocked by your policy anyway now, but these should detect most of the actions an attacker # would try. iam_actions = { 'CreateUser', 'CreateAccessKey', 'DetachUserPolicy', 'DetachGroupPolicy', 'RemoveUserFromGroup', 'DeleteUserPolicy', 'PutGroupPolicy', 'PutUserPolicy' } if in_set(rec['eventName'], iam_actions): return True # If the user tries to create or enable an MFA device, but they are unable to, it could mean # they are attempting to exploit a race condition where they wait for the user to one day # swap MFA devices. # This will have an errorCode of: # - 'AccessDenied' # - 'EntityAlreadyExists': Can't create another MFA device with the same name. # - 'LimitExceeded': Can't enable a second MFA device for the same user. if ('errorCode' in rec and in_set( rec['eventName'], {'CreateVirtualMFADevice', 'EnableMFADevice'})): return True return False
def cloudtrail_critical_api_calls(rec): """ author: airbnb_csirt description: Alert on AWS API calls that stop or delete security/infrastructure logs. Additionally, alert on AWS API calls that delete critical resources (VPCs, Subnets, DB's, ...) reference: https://medium.com/@robwitoff/ proactive-cloud-security-w-aws-organizations-d58695bcae16#.tx2e6iju0 playbook: (a) identify the AWS account in the log (b) identify what resource(s) are impacted by the API call (c) determine if the intent is valid, malicious or accidental """ critical_events = { # VPC Flow Logs (~netflow) 'DeleteFlowLogs', # Critical, large resources 'DeleteSubnet', 'DeleteVpc', 'DeleteDBCluster', 'DeleteCluster', # CloudTrail 'DeleteTrail', 'UpdateTrail', 'StopLogging', # AWS Config 'DeleteDeliveryChannel', 'StopConfigurationRecorder', # CloudWatch 'DeleteRule', 'DisableRule' } return in_set(rec['eventName'], critical_events)
def test_in_set(): """Helpers - In Set""" # basic example test_list = ['this', 'is', 'a9', 'test'] data = 'test' result = base.in_set(data, test_list) assert_equal(result, True) # with globbing host_patterns = {'myhost*', 'yourhost*', 'ahost*'} myhost = 'myhost1232312321' yourhost = 'yourhost134931' ahost = 'ahost12321-test' result = all(base.in_set(host, host_patterns) for host in (myhost, yourhost, ahost)) assert_equal(result, True)
def cloudtrail_put_object_acl_public(rec): """ author: @mimeframe description: Identifies a change to an S3 object ACL that grants access to AllUsers (anyone on the internet) or AuthenticatedUsers (any user with an AWS account). reference: http://amzn.to/2yfRxzp playbook: (a) Verify if the object should be publicly accessible (b) If not, modify the object ACL """ public_acls = { 'http://acs.amazonaws.com/groups/global/AuthenticatedUsers', 'http://acs.amazonaws.com/groups/global/AllUsers' } # s3 buckets that are expected to have public objects public_buckets = { 'example-bucket-to-ignore' } request_params = rec['detail']['requestParameters'] return ( rec['detail']['eventName'] == 'PutObjectAcl' and # note: substring is used because it can exist as: # "http://acs.amazonaws.com/groups/global/AllUsers" or # "uri=http://acs.amazonaws.com/groups/global/AllUsers" data_has_value_from_substring_list(request_params, public_acls) and not in_set(request_params.get('bucketName'), public_buckets) )
def sample_cloudtrail_rule(rec): """Non Lambda/Kinesis service AssumedRole""" whitelist_services = { 'lambda.amazonaws.com', 'kinesis.amazonaws.com' } return ( rec['eventName'] == 'AssumeRole' and rec['awsRegion'] == 'us-east-1' and not in_set(rec['userIdentity']['invokedBy'], whitelist_services) )
def github_disable_required_pull_request_reviews(rec): """ author: @mimeframe description: Setting 'Require pull request reviews before merging' was disabled. When enabled, all commits must be made to a non-protected branch and submitted via a pull request with at least one approved review and no changes requested before it can be merged into master. repro_steps: (a) Visit /<org>/<repo>/settings/branches/<branch> (b) Uncheck 'Require pull request reviews before merging' (c) Click 'Save Changes' reference: https://help.github.com/articles/enabling-required-reviews-for-pull-requests/ """ actor_ignorelist = {} return (rec['action'] == 'protected_branch.dismissal_restricted_users_teams' and rec['data'].get('authorized_actors_only') is True and not in_set(rec['actor'], actor_ignorelist))