Skip to content

Instantly share code, notes, and snippets.

@mwielgoszewski
Created February 20, 2014 16:07
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mwielgoszewski/012e500cb504968238c7 to your computer and use it in GitHub Desktop.
Save mwielgoszewski/012e500cb504968238c7 to your computer and use it in GitHub Desktop.
# -*- 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