Skip to content

Commit

Permalink
Move AWS stuff out of burp script
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryan Gerstenkorn committed Aug 19, 2021
1 parent d57398a commit 37fc1ee
Show file tree
Hide file tree
Showing 8 changed files with 317 additions and 2 deletions.
2 changes: 1 addition & 1 deletion cdn_bypass.py → burp.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def startAPIGateway(self):
'Items': [
{
'Id': 'default',
'DomainName': self.target_host,
'DomainName': self.target_host.text,
'CustomOriginConfig': {
'HTTPPort': 123,
'HTTPSPort': 123,
Expand Down
28 changes: 28 additions & 0 deletions cdn_bypass/lambdas/request/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import random
import os


def lambda_handler(event, context):
request = event['Records'][0]['cf']['request']
headers = request['headers']

host = os.getenv('HOST')
if not host:
raise UserWarning('The environment variable HOST is not set.')

forwarded_for = os.getenv('X_FORWARDED_FOR')
if not forwarded_for:
# Set randomly
pass

headers['host'] = [{
"key": "Host",
"value": host,
}]

headers['x-forwarded-for'] = [{
"key": "X-Forwarded-For",
"value": forwarded_for,
}]

return request
198 changes: 198 additions & 0 deletions cdn_bypass/lib.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import io
import zipfile
from datetime import time
from pathlib import Path

import botocore.exceptions
import boto3.exceptions


def create_function(sess, function_name, role_arn, target):
source_code = (Path(__file__).parent / 'lambdas/request/main.py').read_text()
zip = io.BytesIO()
with zipfile.ZipFile(zip, 'w', zipfile.ZIP_DEFLATED) as zip_file:
zip_file.writestr('main.py', source_code)
zip.seek(0)

lamb = sess.client('lambda', region_name='us-east-1')
lambda_exists = False
try:
lamb.get_function(FunctionName=function_name)
lambda_exists = True
except Exception as e:
if e.response['Error']['Code'] != 'ResourceNotFoundException':
raise e

if not lambda_exists:
lamb.create_function(
FunctionName=function_name,
Runtime='python3.9',
Role=role_arn,
Handler='main.lambda_handler',
Code={
'ZipFile': zip.read(),
},
Description='Request handler for the cdn-bypass Burp plugin.',
Timeout=31,
MemorySize=128,
Publish=True,
PackageType='Zip',
Environment={
'Variables': {
'HOST': target,
}
},
)


def create_lambda_role(sess, function_name, role_name):
iam = sess.client('iam', region_name='us-east-1')
role_exists = False
try:
iam.get_role(RoleName=role_name)
role_exists = True
except botocore.exceptions.ClientError as e:
if e.response['Error']['Code'] != 'NoSuchEntity':
raise e

if role_exists:
return

resp = iam.create_role(
RoleName=role_name,
AssumeRolePolicyDocument='''
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"edgelambda.amazonaws.com",
"lambda.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
''',
Description='Execution roles for lambdas created by the cdn-bypass burp plugin.',
)
iam.put_role_policy(
RoleName=role_name,
PolicyName='basic-execution',
PolicyDocument=f'''
{{
"Version": "2012-10-17",
"Statement": [
{{
"Effect": "Allow",
"Action": "logs:CreateLogGroup",
"Resource": "arn:aws:logs:*:*:*"
}},
{{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": [
"arn:aws:logs:*:*:log-group:/aws/lambda/{function_name}:*"
]
}}
]
}}
'''
)
return resp['Role']['Arn']


def create_cloudfront_distribution(sess, target):
client = sess.client('cloudfront', region_name='us-east-1')
resp = client.create_distribution(
DistributionConfig={
'CallerReference': str(time()),
'Origins': {
'Quantity': 1,
'Items': [
{
'Id': 'default',
'DomainName': target,
'CustomOriginConfig': {
'HTTPPort': 80,
'HTTPSPort': 443,
'OriginProtocolPolicy': 'http-only',
# TODO: Add option for this | 'match-viewer' | 'https-only',
'OriginSslProtocols': {
'Quantity': 4,
'Items': [
'SSLv3',
'TLSv1',
'TLSv1.1',
'TLSv1.2',
]
},
},
'OriginShield': {
'Enabled': False,
}
},
]
},
'DefaultCacheBehavior': {
'TargetOriginId': 'default',
'ViewerProtocolPolicy': 'allow-all',
'AllowedMethods': {
'Quantity': 7,
'Items': [
'GET',
'HEAD',
'POST',
'PUT',
'PATCH',
'OPTIONS',
'DELETE',
],
'CachedMethods': {
'Quantity': 2,
'Items': [
'GET',
'HEAD',
]
}
},
'Compress': False,
'LambdaFunctionAssociations': {
'Quantity': 1,
'Items': [
{
'LambdaFunctionARN': ...,
'EventType': 'origin-request',
'IncludeBody': False,
},
# Rewrite all links?
# {
# 'LambdaFunctionARN': ...,
# 'EventType': 'origin-response',
# 'IncludeBody': True | False
# },
]
},
'CachePolicyId': '4135ea2d-6df8-44a3-9df3-4b5a84be39ad',
},
'CacheBehaviors': {
'Quantity': 0,
},
'Comment': 'cdn-bypass todo update description',
'PriceClass': 'PriceClass_100',
'Enabled': True,
'ViewerCertificate': {
'CloudFrontDefaultCertificate': True,
'MinimumProtocolVersion': 'TLSv1.2_2018',
},
'HttpVersion': 'http1.1',
'IsIPV6Enabled': False,
}
)
return resp['Distribution']['Id']
39 changes: 39 additions & 0 deletions cdn_bypass/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from __future__ import print_function

from time import time

from cdn_bypass.lib import create_function, create_lambda_role, create_cloudfront_distribution


class CdnBypass:
def __init__(self, target):
self.target = target

def deploy(self):
pass

def destroy(self):
pass


LAMBDA_ROLE_NAME = 'cdn-bypass-lambda-execution'
LAMBDA_FUNCTION_NAME = 'cdn-bypass-lambda-request'


class CloudFrontBypass(CdnBypass):
def __init__(self, sess, *args, **kwargs):
self.sess = sess
self.distribution_id = None
super(CloudFrontBypass, self).__init__(*args, **kwargs)

def deploy(self):
create_lambda_role(LAMBDA_FUNCTION_NAME, LAMBDA_ROLE_NAME)
create_function(LAMBDA_FUNCTION_NAME, LAMBDA_ROLE_NAME, self.target)
self.distribution_id = create_cloudfront_distribution(self.target)
print('CloudFront deployed')
return self.distribution_id

def destroy(self):
client = self.sess.client('cloudfront', region_name='us-east-1')
resp = client.delete_distribution(Id=self.distribution_id)
print('CloudFront destroyed')
3 changes: 3 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pytest=4.6 # Last version that supported python2
moto[lambda,iam]
docker
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
botocore==1.20.112 # Last version that supported python2
boto3==1.17.112 # Last version that supported python2
boto3==1.17.112 # Last version that supported python2
28 changes: 28 additions & 0 deletions tests/test_lib.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import os

import pytest
import boto3
from moto import mock_iam, mock_lambda

from cdn_bypass.lib import create_function, create_lambda_role


@pytest.fixture
def aws_credentials():
"""Mocked AWS Credentials for moto."""
os.environ['AWS_ACCESS_KEY_ID'] = 'testing'
os.environ['AWS_SECRET_ACCESS_KEY'] = 'testing'
os.environ['AWS_SECURITY_TOKEN'] = 'testing'
os.environ['AWS_SESSION_TOKEN'] = 'testing'


@pytest.fixture
def sess(aws_credentials):
with (mock_iam(), mock_lambda()):
yield boto3.session.Session(region_name='us-east-1')


# moto complains if you try to create a function using a role that doesn't exist, so these need to be tested together.
def test_lib(sess):
role_arn = create_lambda_role(sess, 'test-function', 'lambda-cfn')
create_function(sess, 'test-function', role_arn, 'target')
19 changes: 19 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import pytest
import boto3

from cdn_bypass.lib import create_cloudfront_distribution
from cdn_bypass.main import CloudFrontBypass


@pytest.fixture
def sess():
return boto3.session.Session(region_name='us-east-1')


@pytest.fixture
def cloudfront(sess):
return CloudFrontBypass(sess, 'example.com')


def test_create_cloudfront_distribution(cloudfront):
assert create_cloudfront_distribution(sess, 'target')

0 comments on commit 37fc1ee

Please sign in to comment.