-
-
Save irsdl/0ab8fce5368e449df64ed89c1b7323a6 to your computer and use it in GitHub Desktop.
Burp Python Scripter scripts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ***********************************************replacer_for_python_scripter | |
import re,random | |
print callbacks.getToolName(toolFlag) | |
if(messageIsRequest): | |
if (callbacks.getToolName(toolFlag) == "Proxy" or callbacks.getToolName(toolFlag) == "Intruder" or callbacks.getToolName(toolFlag) == "Repeater"): | |
requestInfo = helpers.analyzeRequest(messageInfo) | |
headers = requestInfo.getHeaders() | |
msgBody = messageInfo.getRequest()[requestInfo.getBodyOffset():] | |
msg = helpers.bytesToString(msgBody) | |
randStr = random.randint(1000000,9999999) | |
msg = re.sub(r'(name="hicEmail"\s+[^@]+)(@[^\s]+)',r'\g<1>' + str(randStr) + r'\g<2>',msg) | |
print msg | |
msgBody = helpers.stringToBytes(msg) | |
message = helpers.buildHttpMessage(headers, msgBody) | |
messageInfo.setRequest(message) | |
print "hoy hoy" | |
# ***********************************************replay before sending the request-python_scripter | |
print "hey hey - replay me before I go away!" | |
if(messageIsRequest): | |
if (callbacks.getToolName(toolFlag) == "Proxy" or callbacks.getToolName(toolFlag) == "Repeater"): | |
callbacks.makeHttpRequest(messageInfo.getHttpService(), messageInfo.getRequest()) | |
# ***********************************************request_replacer_MultipartRelated2TextXML_for_python_scripter | |
import re,random | |
import urllib | |
#### helpers start #### | |
def overwriteHeader(allheaders, strNewHeader, isCaseInsensitive): | |
isAdded = False | |
newHeaderName = strNewHeader.split(':',1)[0] | |
for id, item in enumerate(allheaders): | |
if isCaseInsensitive: | |
item = item.lower() | |
newHeaderName = newHeaderName.lower() | |
if newHeaderName in item: | |
allheaders[id] = strNewHeader | |
isAdded = True | |
if not isAdded: | |
allheaders.append(strNewHeader) | |
return allheaders | |
def findHeader(allheaders,targetHeader,isCaseInsensitive): | |
for id, item in enumerate(allheaders): | |
if isCaseInsensitive: | |
item = item.lower() | |
targetHeader = targetHeader.lower() | |
if targetHeader in item: | |
return allheaders[id] | |
return "" | |
#### helpers end #### | |
print callbacks.getToolName(toolFlag) | |
if(messageIsRequest): | |
if (callbacks.getToolName(toolFlag) == "Repeater"): | |
requestInfo = helpers.analyzeRequest(messageInfo) | |
hostname = requestInfo.getUrl().getHost() | |
relPath = urllib.unquote(requestInfo.getUrl().getPath()).decode('utf8') | |
headers = list(requestInfo.getHeaders()) #python list rather than java arraylist | |
contentType = findHeader(headers, 'content-type', 1) | |
# more granular rules for auto replace | |
if (hostname == "example.com" and ("/something" in relPath or "/foobar" in relPath) and ("multipart/related" in contentType)): | |
msgBody = messageInfo.getRequest()[requestInfo.getBodyOffset():] | |
msg = helpers.bytesToString(msgBody) | |
#randStr = random.randint(1000000,9999999) | |
#msg = re.sub(r'(name="hicEmail"\s+[^@]+)(@[^\s]+)',r'\g<1>' + str(randStr) + r'\g<2>',msg) | |
try: | |
headers = overwriteHeader(headers, 'content-type: {type}'.format(type="text/xml"), 1) | |
msg = re.search('(\<s:Envelope.*\<\/s:Envelope\>)', msg).group(0) | |
msgBody = helpers.stringToBytes(msg) | |
message = helpers.buildHttpMessage(headers, msgBody) | |
#cookieParam = helpers.buildParameter('XSRF-TOKEN', 'test', 2) # updating a cookie parameter for CSRF | |
#message = helpers.updateParameter(message, cookieParam) | |
messageInfo.setRequest(message) | |
print "request has been updated!" | |
except AttributeError: | |
# Ignore! | |
print "ignored - request was not updated!" | |
# ***********************************************response_replacer_for_python_scripter | |
import re,random | |
import urllib | |
enabled = True | |
if(not messageIsRequest and enabled): | |
if (callbacks.getToolName(toolFlag) == "Proxy"): | |
requestInfo = helpers.analyzeRequest(messageInfo) | |
hostname = requestInfo.getUrl().getHost() | |
relPath = urllib.unquote(requestInfo.getUrl().getPath()).decode('utf8') | |
# more granular rules for auto replace | |
if (hostname == "example.com" and ("/something" in relPath or "/foobar" in relPath or "somethingelse" in relPath)): | |
responseInfo = helpers.analyzeResponse(messageInfo.getResponse()) | |
headers = responseInfo.getHeaders() | |
#msgRequestBody = messageInfo.getRequest()[requestInfo.getBodyOffset():] | |
msgResponseBody = messageInfo.getResponse()[responseInfo.getBodyOffset():] | |
msg = helpers.bytesToString(msgResponseBody) | |
#randStr = random.randint(1000000,9999999) | |
msg = re.sub(r'(":)(\s*false)',r'\g<1>true',msg) | |
msg = re.sub('"type":"view"','"type":"edit"',msg) | |
print msg | |
msgBody = helpers.stringToBytes(msg) | |
message = helpers.buildHttpMessage(headers, msgBody) | |
messageInfo.setResponse(message) | |
print "response has been updated!" | |
# ***********************************************header_signature_sha1-python_scripter | |
import re | |
import random | |
import hashlib | |
import base64 | |
import datetime | |
import hmac | |
import urllib | |
import operator | |
from burp import IParameter | |
# coded by Soroush Dalili (@irsdl) for Burp Suite Python Scripter extension! | |
print "scripter starts here..." | |
xUserToken = "4ba75b0caaad1d569e12ac5bbcac2aabbaaa3e96" | |
ignorePortInURL = True | |
# We check these to prevent adding headers to unnecessary requests | |
targetHostnameCheck = "pentest.example.com" | |
targetPathCheck = "/api/" | |
targetExcludeWhenInQS = "excluded=ncc" | |
def overwriteHeader(allheaders, strNewHeader): | |
isAdded = False | |
newHeaderName = strNewHeader.split(':',1)[0] | |
for id, item in enumerate(allheaders): | |
if newHeaderName in item: | |
allheaders[id] = strNewHeader | |
isAdded = True | |
if not isAdded: | |
allheaders.append(strNewHeader) | |
return allheaders | |
def readHeader(allheaders, strTargetHeader, isCaseSensitive): | |
result = "" | |
strTargetHeader = strTargetHeader + ":" | |
if not isCaseSensitive: | |
strTargetHeader = strTargetHeader.lower() | |
for id, item in enumerate(allheaders): | |
itemtemp = item | |
if not isCaseSensitive: | |
itemtemp = itemtemp.lower() | |
if itemtemp.startswith(strTargetHeader): | |
result = item | |
break | |
return result | |
def xstr(s): | |
if s is None: | |
return '' | |
return str(s) | |
def getKey(item): | |
return item[0] | |
if(messageIsRequest): | |
if (callbacks.getToolName(toolFlag) == "Scanner" or callbacks.getToolName(toolFlag) == "Intruder" or callbacks.getToolName(toolFlag) == "Repeater" or callbacks.getToolName(toolFlag) == "Extender"): | |
requestInfo = helpers.analyzeRequest(messageInfo) | |
# relative path & QA from URL | |
url = requestInfo.getUrl().toString() | |
port = xstr(requestInfo.getUrl().getPort()) | |
hostname = requestInfo.getUrl().getHost() | |
relPath = urllib.unquote(requestInfo.getUrl().getPath()).decode('utf8') | |
QSPart = xstr(requestInfo.getUrl().getQuery()) #only Querystring - we use it to check for exclusions atm | |
allParamsArray = requestInfo.getParameters() # we need GET and POST | |
# This is to ensure we are not setting this on an unwanted request | |
if ((len(targetHostnameCheck) == 0 or hostname == targetHostnameCheck) and (len(targetPathCheck) == 0 or targetPathCheck in relPath) and (len(targetExcludeWhenInQS) == 0 or len(QSPart)==0 or targetExcludeWhenInQS not in QSPart)): | |
headers = list(requestInfo.getHeaders()) #python list rather than java arraylist | |
msgBody = messageInfo.getRequest()[requestInfo.getBodyOffset():] | |
msg = helpers.bytesToString(msgBody) | |
fullHeadersBody = helpers.bytesToString(messageInfo.getRequest()) | |
# finding all parameters / exclude file uploads / sort on key name | |
allParams = "" | |
allParamsDict = {} # we can't work with Java ArrayList easily so we convert it to a Python dictionary | |
content_type= readHeader(headers,"content-type",False).lower() | |
isMultipart = False | |
if "multipart" in content_type: | |
isMultipart = True | |
isJSON = False | |
if "application/json" in content_type: | |
isJSON = True | |
toBeHashedVar = "" | |
if isJSON or len(msg) == 0: | |
# JSON, GET | |
if len(QSPart)>0: | |
for p in allParamsArray: | |
if (p.getType() == IParameter.PARAM_BODY and not isMultipart) or p.getType() == IParameter.PARAM_URL: | |
#helpers.urlEncode did not work here and we have to use urllib.quote | |
allParams += "&" + urllib.quote(urllib.unquote(p.getName()).decode('utf8')) + "=" + urllib.quote(urllib.unquote(p.getValue()).decode('utf8')) | |
allParams = allParams[1:] | |
# In this special!!! app the colon character won't be encode to %3A but we are doing it :( | |
allParams = allParams.replace("%3A", ":") | |
headers[0] = headers[0].replace(QSPart, allParams) # replacing the querystring | |
url = url[:url.find('?')]+'?' + allParams | |
if ignorePortInURL: | |
url = url.replace(hostname+":"+port+"/",hostname+"/",1) | |
toBeHashedVar = url + msg + xUserToken | |
# we have something to hash! | |
print toBeHashedVar | |
m = hashlib.sha1() | |
m.update(toBeHashedVar) | |
headers = overwriteHeader(headers, 'x-service-request-hash: ' + m.hexdigest()) | |
message = helpers.buildHttpMessage(headers, msgBody) | |
messageInfo.setRequest(message) | |
print "scripter ends here..." | |
# ***********************************************ReqResp_replacer_for_python_scripter | |
import re,random | |
import urllib | |
enabledResponseReplace = 0 | |
enabledRequestReplace = 1 | |
#### helpers start #### | |
def overwriteHeader(allheaders, strNewHeader): | |
isAdded = False | |
newHeaderName = strNewHeader.split(':',1)[0] | |
for id, item in enumerate(allheaders): | |
if newHeaderName in item: | |
allheaders[id] = strNewHeader | |
isAdded = True | |
if not isAdded: | |
allheaders.append(strNewHeader) | |
return allheaders | |
#### helpers end #### | |
if(not messageIsRequest and enabledResponseReplace): | |
if (callbacks.getToolName(toolFlag) == "Proxy"): | |
requestInfo = helpers.analyzeRequest(messageInfo) | |
hostname = requestInfo.getUrl().getHost() | |
relPath = urllib.unquote(requestInfo.getUrl().getPath()).decode('utf8') | |
# more granular rules for auto replace | |
if (hostname == "example.com" and ("/something" in relPath or "/foobar" in relPath or "somethingelse" in relPath)): | |
responseInfo = helpers.analyzeResponse(messageInfo.getResponse()) | |
headers = responseInfo.getHeaders() | |
#msgRequestBody = messageInfo.getRequest()[requestInfo.getBodyOffset():] | |
msgResponseBody = messageInfo.getResponse()[responseInfo.getBodyOffset():] | |
msg = helpers.bytesToString(msgResponseBody) | |
#randStr = random.randint(1000000,9999999) | |
msg = re.sub(r'(":)(\s*false)',r'\g<1>true',msg) | |
msg = re.sub('"type":"view"','"type":"edit"',msg) | |
print msg | |
msgBody = helpers.stringToBytes(msg) | |
message = helpers.buildHttpMessage(headers, msgBody) | |
messageInfo.setResponse(message) | |
print "response has been updated!" | |
if(messageIsRequest and enabledRequestReplace): | |
#if (callbacks.getToolName(toolFlag) == "Proxy" or callbacks.getToolName(toolFlag) == "Intruder" or callbacks.getToolName(toolFlag) == "Repeater" or callbacks.getToolName(toolFlag) == "Target" or callbacks.getToolName(toolFlag) == "Scanner"): | |
if (callbacks.getToolName(toolFlag) == "Repeater" or callbacks.getToolName(toolFlag) == "Target"): | |
requestInfo = helpers.analyzeRequest(messageInfo) | |
hostname = requestInfo.getUrl().getHost() | |
relPath = urllib.unquote(requestInfo.getUrl().getPath()).decode('utf8') | |
# more granular rules for auto replace | |
if (hostname == "dashboard.capitatravelandevents.co.uk"): | |
headers = list(requestInfo.getHeaders()) #python list rather than java arraylist | |
msgBody = messageInfo.getRequest()[requestInfo.getBodyOffset():] | |
msg = helpers.bytesToString(msgBody) | |
#randStr = random.randint(1000000,9999999) | |
#msg = re.sub(r'(name="hicEmail"\s+[^@]+)(@[^\s]+)',r'\g<1>' + str(randStr) + r'\g<2>',msg) | |
headers = overwriteHeader(headers, 'X-XSRF-TOKEN: {csrfToken}'.format(csrfToken="test")) | |
msgBody = helpers.stringToBytes(msg) | |
message = helpers.buildHttpMessage(headers, msgBody) | |
cookieParam = helpers.buildParameter('XSRF-TOKEN', 'test', 2) # updating a cookie parameter for CSRF | |
message = helpers.updateParameter(message, cookieParam) | |
messageInfo.setRequest(message) | |
print "request has been updated!" | |
# ***********************************************debug_replace_request_replace_response | |
import re,random | |
import urllib | |
enabledResponseReplace = 0 | |
enabledRequestReplace = 1 | |
#### helpers start #### | |
def overwriteHeader(allheaders, strNewHeader): | |
isAdded = False | |
newHeaderName = strNewHeader.split(':',1)[0] | |
for id, item in enumerate(allheaders): | |
if newHeaderName in item: | |
allheaders[id] = strNewHeader | |
isAdded = True | |
if not isAdded: | |
allheaders.append(strNewHeader) | |
return allheaders | |
#### helpers end #### | |
if(not messageIsRequest and enabledResponseReplace): | |
if (callbacks.getToolName(toolFlag) == "Proxy"): | |
requestInfo = helpers.analyzeRequest(messageInfo) | |
hostname = requestInfo.getUrl().getHost() | |
relPath = urllib.unquote(requestInfo.getUrl().getPath()).decode('utf8') | |
# more granular rules for auto replace | |
if (hostname == "example.com" and ("/something" in relPath or "/foobar" in relPath or "somethingelse" in relPath)): | |
responseInfo = helpers.analyzeResponse(messageInfo.getResponse()) | |
headers = responseInfo.getHeaders() | |
#msgRequestBody = messageInfo.getRequest()[requestInfo.getBodyOffset():] | |
msgResponseBody = messageInfo.getResponse()[responseInfo.getBodyOffset():] | |
msg = helpers.bytesToString(msgResponseBody) | |
#randStr = random.randint(1000000,9999999) | |
msg = re.sub(r'(":)(\s*false)',r'\g<1>true',msg) | |
msg = re.sub('"type":"view"','"type":"edit"',msg) | |
print msg | |
msgBody = helpers.stringToBytes(msg) | |
message = helpers.buildHttpMessage(headers, msgBody) | |
messageInfo.setResponse(message) | |
print "response has been updated!" | |
if(messageIsRequest and enabledRequestReplace): | |
#if (callbacks.getToolName(toolFlag) == "Proxy" or callbacks.getToolName(toolFlag) == "Intruder" or callbacks.getToolName(toolFlag) == "Repeater" or callbacks.getToolName(toolFlag) == "Target" or callbacks.getToolName(toolFlag) == "Scanner"): | |
if (callbacks.getToolName(toolFlag) == "Repeater" or callbacks.getToolName(toolFlag) == "Target"): | |
requestInfo = helpers.analyzeRequest(messageInfo) | |
hostname = requestInfo.getUrl().getHost() | |
relPath = urllib.unquote(requestInfo.getUrl().getPath()).decode('utf8') | |
# more granular rules for auto replace | |
if (hostname == "www.target.co.uk"): | |
headers = list(requestInfo.getHeaders()) #python list rather than java arraylist | |
msgBody = messageInfo.getRequest()[requestInfo.getBodyOffset():] | |
msg = helpers.bytesToString(msgBody) | |
#randStr = random.randint(1000000,9999999) | |
#msg = re.sub(r'(name="foobar"\s+[^@]+)(@[^\s]+)',r'\g<1>' + str(randStr) + r'\g<2>',msg) | |
headers[0] = "GET /logon.asp HTTP/1.1" | |
newRequestMessageBody = "" | |
newRequestMessage = helpers.buildHttpMessage(headers, newRequestMessageBody) | |
newResponseInfo = helpers.analyzeResponse(callbacks.makeHttpRequest(messageInfo.getHttpService(), newRequestMessage).getResponse()) | |
msgNewResponseBody = newResponseInfo.getResponse()[newResponseInfo.getBodyOffset():] | |
msgNewResponseString = helpers.bytesToString(msgResponseBody) | |
tokenname_search = re.search('<input type="hidden".* ID="[a-zA-Z0-9]{11}" Name="([^"]+)"', msgNewResponseString, re.IGNORECASE) | |
tokenvalue_search = re.search('<input type="hidden".* ID="[a-zA-Z0-9]{11}".* value="([^"]+)"', msgNewResponseString, re.IGNORECASE) | |
if tokenname_search and tokenvalue_search: | |
tokenname = tokenname_search.group(1) | |
tokenvalue = tokenvalue_search.group(1) | |
msg = re.sub(r'([a-zA-Z0-9]{11})=([a-zA-Z0-9]{11})',tokenname+'='+tokenvalue,msg) | |
#headers = overwriteHeader(headers, 'X-XSRF-TOKEN: {csrfToken}'.format(csrfToken="test")) | |
msgBody = helpers.stringToBytes(msg) | |
message = helpers.buildHttpMessage(headers, msgBody) | |
#cookieParam = helpers.buildParameter('XSRF-TOKEN', 'test', 2) # updating a cookie parameter for CSRF | |
#message = helpers.updateParameter(message, cookieParam) | |
messageInfo.setRequest(message) | |
print "request has been updated!" | |
# ***********************************************autoURLEncode_QS | |
#import re,random | |
#import urllib | |
from burp import IParameter | |
enabledRequestReplace = 1 | |
def aggressive_url_encode(string): | |
return "".join("%{0:0>2}".format(format(ord(char), "x")) for char in string) | |
if(messageIsRequest and enabledRequestReplace): | |
if (callbacks.getToolName(toolFlag) == "Intruder" or callbacks.getToolName(toolFlag) == "Repeater" or callbacks.getToolName(toolFlag) == "Target" or callbacks.getToolName(toolFlag) == "Scanner" or callbacks.getToolName(toolFlag) == "Extender"): | |
#if (callbacks.getToolName(toolFlag) == "Proxy" or callbacks.getToolName(toolFlag) == "Repeater" or callbacks.getToolName(toolFlag) == "Target"): | |
requestInfo = helpers.analyzeRequest(messageInfo) | |
hostname = requestInfo.getUrl().getHost() | |
#relPath = urllib.unquote(requestInfo.getUrl().getPath()).decode('utf8') | |
# more granular rules for auto replace | |
#if (hostname == "www.target.co.uk"): | |
print "1111" | |
if (callbacks.isInScope(requestInfo.getUrl())): | |
for parameter in requestInfo.getParameters(): | |
if (parameter.getType() == IParameter.PARAM_URL): | |
messageInfo.setRequest(helpers.updateParameter(messageInfo.getRequest(), helpers.buildParameter(parameter.getName(), aggressive_url_encode(helpers.urlDecode(parameter.getValue())), IParameter.PARAM_URL))) | |
print "request has been updated!" | |
# ***********************************************header_signature-python_scripter | |
import re | |
import random | |
import hashlib | |
import base64 | |
import datetime | |
import hmac | |
import urllib | |
import operator | |
from burp import IParameter | |
# coded by Soroush Dalili (@irsdl) for Burp Suite Python Scripter extension! | |
print "scripter starts here..." | |
domain = "S2" | |
username = "foo.bar" | |
#password = "SecureXXXXSomething" # we don't use the password here! | |
apiKey = "testme" # this is from the website | |
apiSecret = "]$U{xxxxxxxxxxxxxdHSODfC(yNmb" # this is from the website | |
# usertoken is hardcoded as it needs multi step: | |
# A request to /ems/userTokens/createExternal should be made to obtain the Token | |
# The above request needs to have the password encrypted using the public key obtained from /ems/userTokens/publicKey | |
# This can be coded easily in Python as well (easier in .Net) using this: https://www.example-code.com/python/rsa_encryptModExp.asp | |
usertoken = "29c0caaa-8836-4216-bbb-e7b05ee22eee" | |
# We check these to prevent adding headers to unnecessary requests | |
targetHostnameCheck = "target.com" | |
targetPathCheck = "foo" | |
targetExcludeWhenInQS = "excluded=iamatester" | |
def overwriteHeader(allheaders, strNewHeader): | |
isAdded = False | |
newHeaderName = strNewHeader.split(':',1)[0] | |
for id, item in enumerate(allheaders): | |
if newHeaderName in item: | |
allheaders[id] = strNewHeader | |
isAdded = True | |
if not isAdded: | |
allheaders.append(strNewHeader) | |
return allheaders | |
def readHeader(allheaders, strTargetHeader, isCaseSensitive): | |
result = "" | |
strTargetHeader = strTargetHeader + ":" | |
if not isCaseSensitive: | |
strTargetHeader = strTargetHeader.lower() | |
for id, item in enumerate(allheaders): | |
itemtemp = item | |
if not isCaseSensitive: | |
itemtemp = itemtemp.lower() | |
if itemtemp.startswith(strTargetHeader): | |
result = item | |
break | |
return result | |
def xstr(s): | |
if s is None: | |
return '' | |
return str(s) | |
def getKey(item): | |
return item[0] | |
if(messageIsRequest): | |
if (callbacks.getToolName(toolFlag) == "Scanner" or callbacks.getToolName(toolFlag) == "Intruder" or callbacks.getToolName(toolFlag) == "Repeater" or callbacks.getToolName(toolFlag) == "Extender"): | |
requestInfo = helpers.analyzeRequest(messageInfo) | |
# relative path & QA from URL | |
hostname = requestInfo.getUrl().getHost() | |
relPath = urllib.unquote(requestInfo.getUrl().getPath()).decode('utf8') | |
QSPart = xstr(requestInfo.getUrl().getQuery()) #only Querystring - we use it to check for exclusions atm | |
allParamsArray = requestInfo.getParameters() # we need GET and POST | |
# This is to ensure we are not setting this on an unwanted request | |
if ((len(targetHostnameCheck) == 0 or hostname == targetHostnameCheck) and (len(targetPathCheck) == 0 or targetPathCheck in relPath) and (len(targetExcludeWhenInQS) == 0 or len(QSPart)==0 or targetExcludeWhenInQS not in QSPart)): | |
headers = list(requestInfo.getHeaders()) #python list rather than java arraylist | |
msgBody = messageInfo.getRequest()[requestInfo.getBodyOffset():] | |
msg = helpers.bytesToString(msgBody) | |
fullHeadersBody = helpers.bytesToString(messageInfo.getRequest()) | |
# finding all parameters / exclude file uploads / sort on key name | |
allParams = "" | |
allParamsDict = {} # we can't work with Java ArrayList easily so we convert it to a Python dictionary | |
content_type= readHeader(headers,"content-type",False).lower() | |
isMultipart = False | |
if "multipart" in content_type: | |
isMultipart = True | |
for p in allParamsArray: | |
# There is no signing on xml or json parameters! Therefore, we should use QueryString only if that's the case | |
if (p.getType() == IParameter.PARAM_BODY and not isMultipart) or p.getType() == IParameter.PARAM_URL: | |
allParamsDict[urllib.unquote(p.getName()).decode('utf8')] = urllib.unquote(p.getValue()).decode('utf8') | |
elif p.getType() == IParameter.PARAM_BODY or p.getType() == IParameter.PARAM_MULTIPART_ATTR: | |
allParamsDict[p.getName()] = p.getValue() | |
print sorted(allParamsDict.items(), key=lambda x: x[0].lower()) | |
matchFileParamNameObj = re.search(r'form\-data;\s*name\s*=\s*"([^"]+)";\s*filename\s*=', msg, re.M|re.I) # finding file param names | |
for key, value in sorted(allParamsDict.items(), key=lambda x: x[0].lower()): | |
excluded = False | |
if matchFileParamNameObj: | |
for fileParamName in matchFileParamNameObj.groups(): | |
if key == fileParamName or key == "filename": | |
excluded = True | |
if not excluded: | |
allParams = allParams + "&" + key + "=" + value | |
if len(allParams) > 0: | |
allParams = allParams[1:] | |
#finding file params and their values (sha512 base64 encoded) | |
filePart = "" | |
matchBoundaryObj = re.search( r'Content-Type:.*boundary=([^\r\n]+)', fullHeadersBody, re.I) | |
if matchFileParamNameObj and matchFileParamNameObj.groups(): | |
boundary = matchBoundaryObj.group(1) | |
print "boundary=" + boundary | |
matchFilenameValueObj = re.findall(r'(form\-data;[^\r\n]+filename\s*="([^"]+)"([\r]{0,1}[\n]{0,1}[^\r\n]+)+(\r\n|\r|\n){2}([\s\S]*)\r\n\-\-)'+boundary, fullHeadersBody, re.M|re.I|re.U) | |
allFilesDict = {} | |
for fileParamName in matchFilenameValueObj: | |
allFilesDict[fileParamName[1]]=base64.b64encode(hashlib.sha512(helpers.stringToBytes(fileParamName[4])).digest()) | |
for key, value in sorted(allFilesDict.items(), key=lambda x: x[0].lower()): | |
filePart = filePart + "&" + key + "=" + value | |
if len(filePart) > 0: | |
filePart = filePart[1:] | |
# creating timestampe | |
# timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]+"Z" | |
# instead of using miliseconds, I use random numbers to make it really random! | |
randStr = str(random.randint(100,999)) | |
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.")+randStr+"Z" | |
# adding/replacing timestamp in headers | |
headers = overwriteHeader(headers, 'Timestamp: ' + xstr(timestamp)) | |
# adding/replacing api-username and api-usertoken in headers | |
headers = overwriteHeader(headers, 'api-username: ' + xstr(domain) + "\\" + xstr(username)) | |
headers = overwriteHeader(headers, 'api-usertoken: ' + xstr(usertoken)) | |
# creating the message to sign - no file | |
messageToSign = '{method}\n{timestamp}\n{path}\n{querystring}\n{file}'.format(method=xstr(requestInfo.getMethod()),timestamp=xstr(timestamp),path=xstr(relPath),querystring=xstr(allParams),file=xstr(filePart)) | |
messageToSign = messageToSign.lower() | |
print messageToSign | |
# Hashing the secret token | |
secretTokenHash = base64.b64encode(hashlib.sha512(apiSecret).digest()) | |
print "secretTokenHash: "+secretTokenHash | |
# Creating the signature | |
h = hmac.new(secretTokenHash.upper(), messageToSign, hashlib.sha256) | |
signature = base64.b64encode(h.digest()) | |
print "signature: " + xstr(signature) | |
headers = overwriteHeader(headers, 'Authentication: {apikey}:{signature}'.format(apikey=xstr(apiKey),signature=xstr(signature))) | |
print headers | |
message = helpers.buildHttpMessage(headers, msgBody) | |
messageInfo.setRequest(message) | |
print "scripter ends here..." | |
# *********************************************** | |
import sys | |
# Provides introspection into the Python Scripter API. | |
apis = ('extender', 'callbacks', 'helpers', 'toolFlag', 'messageIsRequest', 'messageInfo') | |
funcs = (type, dir) | |
if messageIsRequest: | |
for api in apis: | |
print('\n{}:\n{}'.format(api, '='*len(api))) | |
for func in funcs: | |
print('\n{}:\n'.format(func.__name__)) | |
try: | |
print(func(locals()[api])) | |
except Exception as e: | |
print(func(globals()[api])) | |
# *********************************************** | |
from pyscripter_utils import CustomIssue | |
import re | |
import sys | |
# Adds custom passive audit checks. | |
# Requires pyscripter_utils.py to be loaded with Burp. | |
if not messageIsRequest: | |
if toolFlag in (callbacks.TOOL_PROXY,): | |
if callbacks.isInScope(messageInfo.getUrl()): | |
response = messageInfo.getResponse() | |
# Checks for autocomplete on text form fields. | |
results = re.findall(r'(<input [^>]*>)', response) | |
for result in results: | |
if re.search(r'''type=['"]text['"]''', result) and not re.search(r'autocomplete', result): | |
issue = CustomIssue( | |
BasePair=messageInfo, | |
IssueName='Text field with autocomplete enabled', | |
IssueDetail='The following text field has autocomplete enabled:\n\n<ul><li>' + result.replace('<', '<').replace('>', '>') + '</li></ul>', | |
Severity='Low', | |
) | |
callbacks.addScanIssue(issue) | |
# Checks for verbose headers. | |
bad_headers = ('server', 'x-powered-by', 'x-aspnet-version') | |
headers = helpers.analyzeResponse(messageInfo.getResponse()).getHeaders() | |
for header in headers: | |
name = header.split(':')[0] | |
if name.lower() in bad_headers: | |
issue = CustomIssue( | |
BasePair=messageInfo, | |
IssueName='Verbose header', | |
IssueDetail='The following HTTP response header may disclose sensitive information:\n\n<ul><li>' + header + '</li></ul>', | |
Severity='Low', | |
) | |
callbacks.addScanIssue(issue) | |
# *********************************************** | |
import re | |
import sys | |
# Extracts multiple instances of a REGEX capture group from responses. | |
pattern = r'<regex>' | |
if not messageIsRequest: | |
response = messageInfo.getResponse() | |
matches = re.findall(pattern, response) | |
for match in matches: | |
print(match) | |
# *********************************************** | |
import re | |
# Replaces the body of a response from a matched URL. | |
# Great for swapping SPA UI build definitions between user roles. | |
url_pattern = r'<regex for response URL>' | |
body = r'''<new body>''' | |
if not messageIsRequest: | |
url = messageInfo.url.toString() | |
if re.search(url_pattern, url): | |
response = messageInfo.getResponse() | |
headers = helpers.analyzeResponse(response).getHeaders() | |
new_response = helpers.buildHttpMessage(headers, helpers.stringToBytes(body)) | |
messageInfo.setResponse(new_response) | |
print('Response replaced from: {}'.format(url)) | |
# *********************************************** | |
import sys | |
import re | |
from hashlib import md5 | |
# Overwrites a previously attempted password signature to bypass client-side anti-automation logic. | |
# Not sure why anyone would do this, but they did, or this wouldn't be a thing. | |
if messageIsRequest: | |
if toolFlag in (callbacks.TOOL_INTRUDER,): | |
request = helpers.bytesToString(messageInfo.getRequest()) | |
if '&nonce=' in request: | |
nonce = re.search(r'&nonce=([^&]*)', request).group(1) | |
password = re.search(r'&password=([^&]*)', request).group(1) | |
token = md5(password+nonce).hexdigest() | |
orig_token = re.search(r'&token=([^\s]*)', request).group(1) | |
request = request.replace(orig_token, token) | |
messageInfo.setRequest(helpers.stringToBytes(request)) | |
# *********************************************** | |
# Fetches and replaces a Bearer token in the current request. | |
def get_new_token(): | |
url = '<url>' | |
username = '<username>' | |
password = '<password>' | |
import urllib2 | |
import json | |
data = { | |
'username': username, | |
'password': password, | |
} | |
req = urllib2.Request(url) | |
req.add_header('Content-Type', 'application/json') | |
response = urllib2.urlopen(req, json.dumps(data)) | |
data = json.load(response) | |
token = data.get('token', '') | |
print('New token obtained.') | |
return token | |
# only apply to repeater | |
if toolFlag == callbacks.TOOL_REPEATER: | |
# only apply to requests | |
if messageIsRequest: | |
# obtain a new token | |
new_token = get_new_token() | |
# remove any existing Authorization header | |
request = helpers.analyzeRequest(messageInfo) | |
headers = request.getHeaders() | |
for header in headers: | |
if header.startswith('Authorization'): | |
headers.remove(header) | |
break | |
# add a new Authorization header with the new token | |
headers.add('Authorization: Bearer {}'.format(new_token)) | |
body = messageInfo.getRequest()[request.getBodyOffset():] | |
new_request = helpers.buildHttpMessage(headers, body) | |
messageInfo.setRequest(new_request) | |
print('Token replaced.') | |
# *********************************************** | |
# Removes authentication information from the current request. | |
header_names = ['Cookie', 'Authorization'] | |
# only apply to target | |
if toolFlag == callbacks.TOOL_TARGET: | |
# only apply to requests | |
if messageIsRequest: | |
request = helpers.analyzeRequest(messageInfo) | |
headers = request.getHeaders() | |
for header_name in header_names: | |
for header in headers: | |
if header.startswith(header_name): | |
headers.remove(header) | |
break | |
body = messageInfo.getRequest()[request.getBodyOffset():] | |
new_request = helpers.buildHttpMessage(headers, body) | |
messageInfo.setRequest(new_request) | |
print('Headers removed: {}'.format(', '.join(header_names))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from burp import IScanIssue | |
class CustomIssue(IScanIssue): | |
def __init__(self, BasePair, Confidence='Certain', IssueBackground=None, IssueDetail=None, IssueName='Python Scripter generated issue', RemediationBackground=None, RemediationDetail=None, Severity='High'): | |
self.HttpMessages=[BasePair] # list of HTTP Messages | |
self.HttpService=BasePair.getHttpService() # HTTP Service | |
self.Url=BasePair.getUrl() # Java URL | |
self.Confidence = Confidence # "Certain", "Firm" or "Tentative" | |
self.IssueBackground = IssueBackground # String or None | |
self.IssueDetail = IssueDetail # String or None | |
self.IssueName = IssueName # String | |
self.IssueType = 134217728 # always "extension generated" | |
self.RemediationBackground = RemediationBackground # String or None | |
self.RemediationDetail = RemediationDetail # String or None | |
self.Severity = Severity # "High", "Medium", "Low", "Information" or "False positive" | |
def getHttpMessages(self): | |
return self.HttpMessages | |
def getHttpService(self): | |
return self.HttpService | |
def getUrl(self): | |
return self.Url | |
def getConfidence(self): | |
return self.Confidence | |
def getIssueBackground(self): | |
return self.IssueBackground | |
def getIssueDetail(self): | |
return self.IssueDetail | |
def getIssueName(self): | |
return self.IssueName | |
def getIssueType(self): | |
return self.IssueType | |
def getRemediationBackground(self): | |
return self.RemediationBackground | |
def getRemediationDetail(self): | |
return self.RemediationDetail | |
def getSeverity(self): | |
return self.Severity |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment