Instantly share code, notes, and snippets.
Created
February 20, 2014 16:07
-
Star
1
(1)
You must be signed in to star a gist -
Fork
1
(1)
You must be signed in to fork a gist
-
Save mwielgoszewski/012e500cb504968238c7 to your computer and use it in GitHub Desktop.
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
# -*- coding: utf-8 -*- | |
from java.awt.event import ActionListener | |
from javax.swing import JMenu, JMenuItem | |
from java.io import PrintWriter | |
from burp import IBurpExtender, IContextMenuFactory, IMessageEditorTab, IMessageEditorTabFactory, IScannerInsertionPoint, IScannerInsertionPointProvider | |
from array import array | |
from gds.gwt.GWTParser import GWTParser | |
import traceback | |
try: | |
from cStringIO import StringIO | |
except ImportError: | |
from StringIO import StringIO | |
class BurpExtender(IBurpExtender, IContextMenuFactory, IMessageEditorTabFactory, IScannerInsertionPointProvider): | |
def registerExtenderCallbacks(self, callbacks): | |
self.callbacks = callbacks | |
self.helpers = callbacks.getHelpers() | |
self.stdout = PrintWriter(callbacks.getStdout(), True) | |
self.stderr = PrintWriter(callbacks.getStderr(), True) | |
callbacks.setExtensionName("GWT Tools") | |
callbacks.registerContextMenuFactory(self) | |
callbacks.registerScannerInsertionPointProvider(self) | |
callbacks.registerMessageEditorTabFactory(self) | |
def createMenuItems(self, invocation): | |
if not invocation.getInvocationContext() in ( | |
invocation.CONTEXT_PROXY_HISTORY, | |
invocation.CONTEXT_MESSAGE_EDITOR_REQUEST, | |
invocation.CONTEXT_MESSAGE_VIEWER_REQUEST): | |
return | |
gwtToolsMenu = JMenu("GWT Tools") | |
sendAllToIntruderMenu = JMenuItem("Send GWT request to Intruder") | |
gwtToolsMenu.add(sendAllToIntruderMenu) | |
sendStrToIntruderMenu = JMenuItem("Send GWT request to Intruder (strings-only)") | |
gwtToolsMenu.add(sendStrToIntruderMenu) | |
sendAllToIntruderMenu.addActionListener( | |
SendToBurpToolActionListener( | |
self, invocation.getSelectedMessages() | |
)) | |
sendStrToIntruderMenu.addActionListener( | |
SendToBurpToolActionListener( | |
self, invocation.getSelectedMessages(), | |
fuzzEverything=False, | |
)) | |
return [gwtToolsMenu] | |
def createNewInstance(self, controller, editable): | |
return GWTEditorTab(self, controller, editable) | |
def getInsertionPoints(self, baseRequestResponse): | |
self.stdout.write("Getting insertion points for some request") | |
insertionPoints = [] | |
request = baseRequestResponse.getRequest() | |
requestInfo = self.helpers.analyzeRequest(baseRequestResponse) | |
rpc_string = request[requestInfo.getBodyOffset():].tostring() | |
try: | |
gwt = GWTParser() | |
gwt.deserialize(rpc_string) | |
for idx, item in gwt.fuzzmarked.iteritems(): | |
self.stdout.write("Creating GWT insertion point for %d: %s\n" % (idx, item)) | |
insertionPoints.append( | |
GWTInsertionPoint(self, baseRequestResponse, requestInfo, gwt, idx)) | |
except Exception as error: | |
traceback.print_exc(file=self.stderr) | |
return insertionPoints | |
def getGWTInsertionPointOffsets(self, messageInfo, fuzzEverything=True): | |
''' | |
Get the insertion point offsets for a GWT rpc string relative | |
to the (request) object passed. | |
''' | |
request = messageInfo.getRequest() | |
requestInfo = self.helpers.analyzeRequest(messageInfo) | |
rpc_string = request[requestInfo.getBodyOffset():].tostring() | |
fileobj = StringIO() | |
fileobj.write(request[:requestInfo.getBodyOffset()].tostring()) | |
# offsets in Java needs to be of type List<int[2]> | |
# Jython will coerce a list of array.array's with type 'i' | |
# to List<int> properly. | |
offsets = [] | |
gwt = GWTParser() | |
gwt.deserialize(rpc_string) | |
payload = gwt.rpc_string.rstrip('|').split('|') | |
for idx, item in enumerate(payload): | |
start = fileobj.tell() | |
fileobj.write(item) | |
# identify what items in the rpc string are suitable for | |
# fuzzing, and append an int[2] to the offsets list denoting | |
# the start and end position of a fuzzable item. | |
# | |
# e.g., ignore the version, hash, class, method and other | |
# items in the rpc request that would throw a deserialization | |
# exception if they're tampered with. | |
# | |
# also, ignore non-string parameters (if requested). | |
if idx in gwt.fuzzmarked: | |
if fuzzEverything or gwt.fuzzmarked[idx] != '%d': | |
offsets.append(array('i', (start, fileobj.tell()))) | |
fileobj.write('|') | |
assert offsets, 'Insertion point offsets cannot be null' | |
message = fileobj.getvalue() | |
assert len(message) >= offsets[-1][1], \ | |
'Last offset %d > %d' % (offsets[-1][1], len(message)) | |
return message, offsets | |
class GWTEditorTab(IMessageEditorTab): | |
TAB_CAPTION = 'GWT' | |
def __init__(self, extender, controller, editable): | |
self.extender = extender | |
self.callbacks = extender.callbacks | |
self.helpers = extender.helpers | |
self.controller = controller | |
self.editable = editable | |
self.editor = extender.callbacks.createTextEditor() | |
self.editor.setEditable(editable) | |
def getTabCaption(self): | |
return self.TAB_CAPTION | |
def getUiComponent(self): | |
return self.editor.getComponent() | |
def isEnabled(self, content, isRequest): | |
if not isRequest: | |
return False | |
requestInfo = self.helpers.analyzeRequest(content) | |
# first header is the request/response line | |
for header in requestInfo.getHeaders()[1:]: | |
name, _, value = header.partition(':') | |
if name.lower() == 'content-type': | |
value = value.lower().strip() | |
if 'text/x-gwt-rpc' in value: | |
return True | |
return False | |
def setMessage(self, content, isRequest): | |
pass | |
def getMessage(self): | |
pass | |
def isModified(self): | |
return self.editor.isTextModified() | |
def getSelectedData(self): | |
return self.editor.getSelectedText() | |
class GWTInsertionPoint(IScannerInsertionPoint): | |
def __init__(self, extender, messageInfo, requestInfo, gwt, index): | |
self.messageInfo = messageInfo | |
self.extender = extender | |
self.callbacks = extender.callbacks | |
self.helpers = extender.helpers | |
self.requestInfo = requestInfo | |
body = messageInfo.getRequest()[requestInfo.getBodyOffset():] | |
self.body = body.split('|') | |
self.index = index | |
self.value = self.body[index] | |
def buildRequest(self, payload): | |
body = list(self.body) | |
body[self.index] = payload | |
body = '|'.join(body) + '|' | |
self._current = self.helpers.buildHttpMessage( | |
self.requestInfo.getHeaders(), body) | |
return self._current | |
def getBaseValue(self): | |
return self.value | |
def getInsertionPointName(self): | |
return "GWT Parameter %d" % (self.index, ) | |
def getPayloadOffsets(self, payload): | |
index = self._current.index(payload) | |
if index == -1: | |
return | |
return index, index + len(payload) | |
def getInsertionPointType(self): | |
return IScannerInsertionPoint.INS_EXTENSION_PROVIDED | |
class SendToBurpToolActionListener(ActionListener): | |
def __init__(self, extender, messages, **kwargs): | |
self.extender = extender | |
self.callbacks = extender.callbacks | |
self.helpers = extender.callbacks.helpers | |
self.messages = messages | |
self.fuzzEverything = kwargs.get('fuzzEverything', True) | |
def actionPerformed(self, event): | |
for messageInfo in self.messages: | |
requestInfo = self.helpers.analyzeRequest(messageInfo) | |
httpService = messageInfo.getHttpService() | |
request, offsets = self.extender.getGWTInsertionPointOffsets( | |
messageInfo, fuzzEverything=self.fuzzEverything) | |
self.callbacks.sendToIntruder( | |
httpService.getHost(), | |
httpService.getPort(), | |
True if httpService.getProtocol() == 'https' else False, | |
request, | |
offsets) | |
return |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment