Extending Burp with Python Defeating web application idiosyncrasies with common-sense, Python and minimal knowledge of Java GUIs
What is Burp?
Purpose of this Talk Quick tour of Burp APIs with examples to show what can be achieved Demonstrate that Web app assessment hurdles overcome with minimal coding effort
Why would you need a custom extn? 1. Decode custom encoding/serialization 2. Handle anti-tamper or signed requests 3. Provide a new “view” into an application 4. Automate a manual task with a new scanner check
Setup to run a Python Burp Extn. 1 Download Jython standalone binary 2 Tell Burp where find Jython 3 Load a Python extension Path to Jython binary goes here
The helloworld of Burp extensions from burp import IBurpExtender class BurpExtender(IBurpExtender): # required def registerExtenderCallbacks(self, callbacks): # set our extension name callbacks.setExtensionName("Hello world extension") # write a message to the Burp alerts tab callbacks.issueAlert("Hello alerts") Just writes “Hello alerts” out to alerts tab
1. Problem: Unsupported encoding Application uses an encoding not understood by Burp Examples: Serialised Java, SAP’s weird URLenc variant, SAML, Websphere Portlet Burp APIs: IMessageEditorTab to display decoded content
Solution: new encoder/decoder 1.Tell Burp about your new message editor tab class CustomDecoderTab(IMessageEditorTab): def __init__(self, extender, controller, editable):... def getTabCaption(self): return "Custom Decoder"
Solution: new decoder/encoder 2. Use setMessage to display decode def setMessage(self, content, isRequest):... if '!ut' in path: # actual decoding magic omitted content = response.read() content = xml.dom.minidom.parseString(content).toprettyxml() if content: self._txtInput.setText(content) self._currentMessage = content
Websphere portlet state decoder Source: Encoded content on URL Gets decoded in new tab
2. Problem: Signed requests Application requires signature thats generated client side. examples 1. Seen in thick client apps as anti-tamper mechanism 2. AWS API calls are signed for authentication Burp API: processHTTPMessage allows us to re-write traffic
Solution: automate request signing 1. Catch an outbound request from burp import IBurpExtender# this function catches requests and responses def processHttpMessage(self, toolFlag, messageIsRequest, currentRequest): # only process requests if not messageIsRequest: return...
Solution: automate request signing 2. Grab the request body and headers # requestInfo object allows us to easily spit body and headers requestInfo = self._helpers.analyzeRequest(currentRequest) bodyBytes = currentRequest.getRequest()[requestInfo.getBodyOffset():] bodyStr = self._helpers.bytesToString(bodyBytes) headers = requestInfo.getHeaders() newHeaders = list(headers) #it's a Java arraylist; get a python list
Solution: automate request signing 3. Append signature as HTTP Header # Do custom signing shenanigans secret = "SuperSecret123" h = hmac.new(secret, bodyStr, hashlib.sha256) newHeaders.append("Authorization: " + base64.b64encode(h.digest()))
Solution: automate request signing 4. Create and send request newMessage = self._helpers.buildHttpMessage(newHeaders, bodyStr) currentRequest.setRequest(newMessage) Here’s the new Authorization header being sent out
3. Problem: Big apps, lotsa headers Large applications may emit different headers from various locations within the app. Headers can reveal useful info. Eg. Reverse proxy may hand off from backend A to backend B. Burp APIs: processHTTPMessage and ITab to display result
Solution: View of unique headers Keep track of unique headers, filter out uninteresting headers. # insert an entry if the header is 'interesting’ if header_name.lower() not in boring_headers: # and we haven't seen this name/value pair before, log it if header not in self.headers_seen: self.headers_seen.append(header) self._log.add(LogEntry(header, …, … )
Solution: View of unique headers Create a new tab and display collected headers in the new tab. # Give the new tab a name def getTabCaption(self): return "Response Headers” # This adds all the Java UI unpleasantness def getUiComponent(self): return self._splitpane
Solution: View of unique headers List of unique headers displayed in new “Response Headers” tab Clicking item in list shows request/response
4. Problem: Automate a manual task Locate and decode F5 cookies, display as a passive scan result Burp API: doPassiveScan to trigger check code
Solution: create new check 1. doPassiveScan catches request def doPassiveScan(self, baseRequestResponse): # Returns IResponseInfo analyzedResponse = self.helpers.analyzeResponse(baseRequestResponse.getResponse()) analyzedRequest = self.helpers.analyzeRequest(baseRequestResponse) # Get Cookies from IResponseInfo Instance cookieList = analyzedResponse.getCookies()
Solution: create new check 2. Locate BIGIP cookies and decode them # Loop though list of cookies for cookie in cookieList: cookieName = cookie.getName() # Look for BIGIP Cookies if cookieName.lower().startswith("bigip"): f5CookieName = cookieName f5RawCookieValue = cookie.getValue() # Decode and check for RFC 1918 address f5info = decode(f5RawCookieValue)
Solution: create new check 3. Create Issue class to return useful info class PassiveScanIssue(IScanIssue):... def getIssueName(self): return "Encoded IP Address Discovered in F5 Cookie Value"... def getIssueDetail(self): msg = "The URL " + str(self.findingurl) + " sets the F5 load balancer cookie "
F5-BigIP Cookie Checker Source: Internal IP address retrieved from encoded cookie
Summary 1. Decode custom encoding/serialization Use IMessageEditorTab interface to display decoded content 2. Handle anti-tamper or signed requests Use processHTTPMessage to catch and rewrite requests 3. Provide a new “view” into an application Use ITab interface to display custom view 4. Automate a manual task with a new scanner check Use doPassiveScan to trigger a check