Skip to content

Commit

Permalink
Stuck on SAXParser import error w/ Burp
Browse files Browse the repository at this point in the history
Running this in burp produces an error related to importing SAXParser
when get_role/create_role is called.

This seems to be a long standing issue and I haven't been able to
workaround it yet.

Going to put the burp plugin on hold and work on making this a cli/
library. If we can figure out this later we'll be able to reuse
the library in the burp plugin though.
  • Loading branch information
Ryan Gerstenkorn committed Aug 19, 2021
1 parent 37fc1ee commit 4cfcd0a
Show file tree
Hide file tree
Showing 9 changed files with 219 additions and 247 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.DS_Store
*$py.class
__pycache__
venv
.idea
Empty file added cdn_bypass/__init__.py
Empty file.
3 changes: 3 additions & 0 deletions cdn_bypass/lambdas/request/main.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import random
import os

# Lambda@Edge functions cannot have environment variables, so this gets replaced at deploy time.
HOST = ''


def lambda_handler(event, context):
request = event['Records'][0]['cf']['request']
Expand Down
219 changes: 147 additions & 72 deletions cdn_bypass/lib.py
Original file line number Diff line number Diff line change
@@ -1,114 +1,177 @@
import io
import os
import re
import zipfile
from datetime import time
from pathlib import Path

import botocore.exceptions
import boto3.exceptions

from botocore.compat import XMLParseError
from xml.etree import ElementTree
from xml.etree import cElementTree


def xml_parser(**_):
class Parser(object):
def feed(self, data):
"""Feed encoded data to parser."""
try:
self.parser.Parse(data, False)
except self._error as v:
self._raiseerror(v)

@staticmethod
def close(self):
"""Finish feeding data to parser and return element structure."""
try:
self.parser.Parse(b"", True) # end of data
except self._error as v:
self._raiseerror(v)
try:
close_handler = self.target.close
except AttributeError:
pass
else:
return close_handler()
finally:
# get rid of circular references
del self.parser, self._parser
del self.target, self._target

return Parser()


# ElementTree.XMLParser = xml_parser
cElementTree.XMLParser = xml_parser

# from xml.parsers import expat
# expat._mangled_xerces_parser_name = "org.apache.xerces.parsers.SAXParser"


import xml
if "_xmlplus" in xml.__path__[0]: # PyXML sub-module
xml.__path__.reverse() # If both are available, prefer stdlib over PyXML
# import xml.parsers.expat
# xml.parsers.expat._xerces_parser_name = "org.apache.xerces.parsers.SAXParser"
# from java.net import URLClassLoader
# from java.lang import Thread
# from java.net import URL


def create_function(sess, function_name, role_arn, target):
source_code = (Path(__file__).parent / 'lambdas/request/main.py').read_text()
main = os.path.join(os.path.split(__file__)[0], 'lambdas/request/main.py')
with open(main, 'r') as f:
source_code = f.read()
# Lambda@Edge functions can't use environment variables so set this as a global.
source_code = re.sub(r'HOST\w+=.*$', 'HOST = {}'.format(target), source_code)

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:
resp = lamb.get_function(FunctionName=function_name)
latest = get_latest_lambda_version(sess, resp['Configuration']['FunctionName'])
return "{}:{}".format(resp['Configuration']['FunctionArn'], latest)
except botocore.exceptions.ClientError 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,
}
},
)
resp = lamb.create_function(
FunctionName=function_name,
Runtime='python3.8',
Role=role_arn,
Handler='main.lambda_handler',
Code={
'ZipFile': zip.read(),
},
Description='Request handler for the cdn-bypass Burp plugin.',
Timeout=30,
MemorySize=128,
Publish=False,
PackageType='Zip',
)

lamb.add_permission(
StatementId='replicator',
FunctionName=resp['FunctionName'],
Principal='replicator.lambda.amazonaws.com',
Action='lambda:GetFunction',
)
lamb.publish_version(FunctionName=resp['FunctionName'])

latest = get_latest_lambda_version(sess, resp['FunctionName'])
return "{}:{}".format(resp['FunctionArn'], latest)


# Apparently the latest version isn't always 1, even if we just created the function.
def get_latest_lambda_version(sess, lambda_name):
lamb = sess.client('lambda', region_name='us-east-1')
resp = lamb.list_versions_by_function(FunctionName=lambda_name)
versions = [v['Version'] for v in resp['Versions']]
versions.remove('$LATEST')
return max(versions)

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
resp = iam.get_role(RoleName=role_name)
return resp['Role']['Arn']
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='''
AssumeRolePolicyDocument='''{
"Version": "2012-10-17",
"Statement": [
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"edgelambda.amazonaws.com",
"lambda.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
"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}:*"
]
}}
]
}}
'''
PolicyDocument='''{{
"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/{}:*"
]
}}
]
}}'''.format(function_name)
)
return resp['Role']['Arn']


def create_cloudfront_distribution(sess, target):
def create_cloudfront_distribution(sess, target, lambda_arn):
client = sess.client('cloudfront', region_name='us-east-1')
resp = client.create_distribution(
DistributionConfig={
Expand Down Expand Up @@ -167,7 +230,7 @@ def create_cloudfront_distribution(sess, target):
'Quantity': 1,
'Items': [
{
'LambdaFunctionARN': ...,
'LambdaFunctionARN': lambda_arn,
'EventType': 'origin-request',
'IncludeBody': False,
},
Expand Down Expand Up @@ -196,3 +259,15 @@ def create_cloudfront_distribution(sess, target):
}
)
return resp['Distribution']['Id']


def delete_cloudfront_distribution(sess, distribution_id):
client = sess.client('cloudfront', region_name='us-east-1')
resp = client.get_distribution_config(Id=distribution_id)
config = resp['DistributionConfig']
config['Enabled'] = False
client.update_distribution(Id=distribution_id, DistributionConfig=config, IfMatch=resp['ETag'])
waiter = client.get_waiter('distribution_deployed')
waiter.wait(Id=distribution_id)
resp = client.get_distribution_config(Id=distribution_id)
client.delete_distribution(Id=distribution_id, IfMatch=resp['ETag'])
20 changes: 11 additions & 9 deletions cdn_bypass/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

from time import time

from cdn_bypass.lib import create_function, create_lambda_role, create_cloudfront_distribution
from cdn_bypass.lib import create_function, create_lambda_role, create_cloudfront_distribution, \
delete_cloudfront_distribution


class CdnBypass:
# import xml.parsers.expat
# xml.parsers.expat._xerces_parser_name = "org.apache.xerces.parsers.SAXParser"

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

Expand All @@ -27,13 +31,11 @@ def __init__(self, sess, *args, **kwargs):
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)
role_arn = create_lambda_role(self.sess, LAMBDA_FUNCTION_NAME, LAMBDA_ROLE_NAME)
# lambda_arn = create_function(self.sess, LAMBDA_FUNCTION_NAME, role_arn, self.target)
# self.distribution_id = create_cloudfront_distribution(self.sess, self.target, lambda_arn)
print('CloudFront deployed')
return self.distribution_id
# 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')
delete_cloudfront_distribution(self.sess, self.distribution_id)
Loading

0 comments on commit 4cfcd0a

Please sign in to comment.