def _fillvalue(shape): param = None # ignore some types if shape.type_name not in ['jsonvalue_string']: param = globals()['_fillvalue_%s' % shape.type_name](shape) log.debug('_fillvalue_{} with value {}'.format(shape.type_name, param)) return param
def _fillvalue_structure(shape): # Validate required fields. fillvalue_members = [] for required_member in shape.metadata.get('required', []): fillvalue_members.append(required_member) params = dict() for param in fillvalue_members: val = _fillvalue(shape.members[param]) log.debug('required: {} is filled with {}'.format(param, val)) params[param] = val return params
def cli(ctx, config, dry_run): if not ctx.config: ctx.config = config if not ctx.dry_run: ctx.dry_run = dry_run log.debug('Current context. DryRun: {} Config: {}'.format( ctx.dry_run, json.dumps(ctx.config, indent=2))) if not ctx.config.get('botocore_document_json_path', None): log.fatal('botocore_document_json_path not defined in config file')
def make_call(config, session, service, command, region=None): # Grab a region to use for the calls. This should be us-west-2 if not region: region = session.get_available_regions(service)[-1] if config.get('user_agent', None): botocore_config.user_agent = config['user_agent'] # Create a client with parameter validation off client = session.client(service, region_name=region, config=botocore_config) # Get the functions that you can call functions_list = get_boto_functions(client) # Get the service file service_file_json = get_service_json_files(config) # Get a list of params needed to make the serialization pass in botocore service_call_params = get_service_call_params(service_file_json[service]) for function in functions_list: if function[0] == command: # The service_file_json doesn't have underscores in names so let's remove them function_key = function[0].replace('_', '') # We need to pull out the parameters needed in the requestUri, ex. /{Bucket}/{Key+} -> ['Bucket', 'Key'] params = re.findall('\{(.*?)\}', service_call_params.get(function_key, '/')) params = [p.strip('+') for p in params] func_params = {} for param in params: func_params[param] = 'testparameter' try: make_api_call(service, function, region, func_params) except ClientError as e: log.debug(e) except boto3.exceptions.S3UploadFailedError as e: log.debug(e) except TypeError as e: log.debug(e) except KeyError as e: log.debug('Unknown Exception: {}.{} - {}'.format( service, function[0], e)) return
def simulate_attack(config, commands, dry_run=False): log.debug('Attack chain to be executed:') log.debug(json.dumps(commands, indent=4)) session = boto3.Session() for command in commands: service_event = command['call'].split('.') service = service_event[0] api_call = service_event[1] delay = command.get('time_delay', 0) region = command.get('region', None) log.info('Making call - {}.{}'.format(service, api_call)) if not dry_run: make_call(config, session, service, api_call, region) log.info('Sleeping {} until next call'.format(delay)) if not dry_run: time.sleep(delay)
def process_cloudtrail(arn, files): api_calls = [] log.info('EventSource, EventName, Recorded Name, Match') for file in files: f = None log.debug('Processing file: {}'.format(file)) if file.endswith('.gz'): f = gzip.open(file, 'r') else: f = open(file, 'r') try: cloudtrail = json.load(f) except Exception as e: log.error('Invalid JSON File: {} - {}'.format(file, e)) continue for record in cloudtrail['Records']: if record.get('userIdentity', {}).get('arn', '').startswith(arn): event_source = record['eventSource'].split('.')[0] event_name = record['eventName'] call = '{}.{}'.format(event_source, event_name) if call not in api_calls: session = record['userIdentity']['arn'].split('/')[-1] match = (record['eventName'].lower() == session) log.info('{}, {}, {}, {}'.format( record['eventSource'].split('.')[0], record['eventName'], session, match)) api_calls.append(call) f.close() return api_calls
def enumerate_services(config, services, dry_run=False): # Create a boto3 session to use for enumeration session = boto3.Session() authorized_calls = [] for service in services: if service == 's3control': log.info( 'Skipping {} - End-points do not seem to be working'.format( service)) continue if len(session.get_available_regions(service)) == 0: if service in [ 'budgets', 'ce', 'chime', 'cloudfront', 'iam', 'importexport', 'organizations', 'route53', 'sts', 'waf' ]: region = 'us-east-1' else: log.info( 'Skipping {} - No regions exist for this service'.format( service)) continue else: if 'us-east-1' in session.get_available_regions(service): region = 'us-east-1' else: log.info('Skipping {} - Only available in {}'.format( service, session.get_available_regions(service))) continue # Create a service client log.info('Creating {} client...'.format(service)) # Set the user-agent if specified in the config if config.get('user_agent', None): botocore_config.user_agent = config['user_agent'] # Create a client with parameter validation off client = session.client(service, region_name=region, config=botocore_config) # Get the functions that you can call functions_list = get_boto_functions(client) # Get the service file service_file_json = get_service_json_files(config) # Get a list of params needed to make the serialization pass in botocore service_call_params = get_service_call_params( service_file_json[service]) # Loop through all the functions and call them for function in functions_list: # The service_file_json doesn't have underscores in names so let's remove them function_key = function[0].replace('_', '') # Session Name Can only be 64 characters long if len(function_key) > 64: session_name = function_key[:63] log.info('Session Name {} is for {}'.format( session_name, function_key)) else: session_name = function_key # Set the session to the name of the API call we are making session = get_assume_role_session( account_number=config['account_number'], role=config['account_role'], session_id=session_name) new_client = session.client(service, region_name=region, config=botocore_config) new_functions_list = get_boto_functions(new_client) for new_func in new_functions_list: if new_func[0] == function[0]: # We need to pull out the parameters needed in the requestUri, ex. /{Bucket}/{Key+} -> ['Bucket', 'Key'] params = re.findall( '\{(.*?)\}', service_call_params.get(function_key, '/')) params = [p.strip('+') for p in params] try: func_params = {} for param in params: # Set something because we have to func_params[param] = 'testparameter' log.info('Calling {}.{} with params {} in {}'.format( service, new_func[0], func_params, region)) if not dry_run: make_api_call(service, new_func, region, func_params) except ClientError as e: if "ValidationError" in str(e): log.error(e) else: log.debug(e) except boto3.exceptions.S3UploadFailedError as e: log.debug(e) except TypeError as e: log.debug(e) except KeyError as e: log.debug('Unknown Exception: {}.{} - {}'.format( service, new_func[0], e))
def enumerate_services(config, services, dry_run=False): # read a CSV file for seen functions apis = defaultdict(list) with open('botocore_api_2_event_names.csv', 'rb') as csvfile: reader = csv.reader(csvfile) for row in reader: x = row[1].split('_') if len(x) > 1: # service_name without '-' apis[x[0]].append(row[1]) else: # event_source as a service_name without '-' apis[row[0].replace('-', '')].append(row[1]) # Create a boto3 session to use for enumeration session = boto3.Session() authorized_calls = [] for service in services: if len(session.get_available_regions(service)) == 0: log.debug('Skipping {} - No regions exist for this service'.format( service)) continue # Create a service client log.info('Creating {} client...'.format(service)) # Grab a region to use for the calls. This should be us-west-2 region = session.get_available_regions(service)[-1] # Set the user-agent if specified in the config if config.get('user_agent', None): botocore_config.user_agent = config['user_agent'] # Create a client with parameter validation off client = session.client(service, region_name=region, config=botocore_config) # Get the functions that you can call functions_list = get_boto_functions(client) # Get the service file service_file_json = get_service_json_files(config) # Get a list of params needed to make the serialization pass in botocore service_call_params = get_service_call_params( service_file_json[service]) # Loop through all the functions and call them for function in functions_list: # The service_file_json doesn't have underscores in names so let's remove them function_key = function[0].replace('_', '') ## Session Name Can only be 64 characters long srv_len = len(service) session_name = service.replace('-', '') if srv_len > 20: session_name = service[:19] srv_len = 20 func_key_limit = 64 - srv_len - 1 if len(function_key) > func_key_limit: session_name += '_' + function_key[:func_key_limit - 1] log.info('Session Name {} is for {}:{}'.format( session_name, service, function_key)) else: session_name += "_" + function_key # check session_name in the seen functions if session_name in apis[service.replace('-', '')]: log.info('found {}:{}, skipping'.format(service, session_name)) continue # Set the session to the name of the API call we are making session = get_assume_role_session( account_number=config['account_number'], role=config['account_role'], session_id=session_name) new_client = session.client(service, region_name=region, config=botocore_config) new_functions_list = get_boto_functions(new_client) for new_func in new_functions_list: if new_func[0] == function[0]: # We need to pull out the parameters needed in the requestUri, ex. /{Bucket}/{Key+} -> ['Bucket', 'Key'] params = re.findall( '\{(.*?)\}', service_call_params.get(function_key, '/')) params = [p.strip('+') for p in params] try: func_params = {} for param in params: # Set something because we have to func_params[param] = 'testparameter' log.info('Calling {}:{} with params {} in {}'.format( service, new_func[0], func_params, region)) # fill values for required members operation_name = new_client._PY_TO_OP_NAME[new_func[0]] operation_model = new_client._service_model.operation_model( operation_name) input_shape = operation_model.input_shape if service == 'sts' and operation_name == 'AssumeRole': log.info( 'skipped sts:AssumeRole for required member') elif input_shape.type_name == 'structure': # find the required members extra_params = _fillvalue(input_shape) if extra_params: func_params.update(extra_params) else: log.info('skipped this input_shape: {}'.format( input_shape)) if not dry_run: make_api_call(service, new_func, region, func_params) except ClientError as e: log.error('ClientError: {}:{} - {}'.format( service, new_func[0], e)) except EndpointConnectionError as e: log.error('EndpointConnectionError: {}:{} - {}'.format( service, new_func[0], e)) except boto3.exceptions.S3UploadFailedError as e: log.error('S3UploadFailedError: {}:{} - {}'.format( service, new_func[0], e)) except TypeError as e: log.error('TypeError: {}:{} - {}'.format( service, new_func[0], e)) except KeyError as e: log.error('Unknown Exception: {}:{} - {}'.format( service, new_func[0], e))