Esempio n. 1
0
    def registerExtenderCallbacks(self, callbacks):
        '''
        This method is invoked on startup.
        '''
        self._callbacks = callbacks

        if self.opt.file:
            if os.path.isfile(self.opt.file):
                self.restoreState(self.opt.file)
                self.issueAlert('Restored state from %s' % (self.opt.file,))
            else:
                self.issueAlert('Could not restore state from %s:'
                                'file does not exist' % (self.opt.file,))

        for module, _ in self._menus.options():
            if self._menus.getbool(module) is True:
                for menu in _get_menus(module):
                    menu(self)

        for component, _ in self._components.options():
            if self._components.getbool(component) is True:
                _get_plugins(component)

        if not self.opt.disable_reloading:
            self._monitor_item(self.config)
            self.monitor = PluginMonitorThread(self)
            self.monitor.start()

        self.issueAlert('Burp extender ready...')

        return
    def registerExtenderCallbacks(self, callbacks):
        '''
        This method is invoked on startup.
        '''
        self._callbacks = callbacks

        if self.opt.file:
            if os.path.isfile(self.opt.file):
                self.restoreState(self.opt.file)
                self.issueAlert('Restored state from %s' % (self.opt.file,))
            else:
                self.issueAlert('Could not restore state from %s:'
                                'file does not exist' % (self.opt.file,))

        for module, _ in self.menus.options():
            if self.menus.getbool(module) is True:
                for MenuItemHandler in _get_menus(module):
                    MenuItemHandler(self)

        for component, _ in self._components.options():
            if self._components.getbool(component) is True:
                _get_plugins(component)

        if not self.opt.disable_reloading:
            self.monitor = PluginMonitorThread(self)
            self.monitor.start()

        self.issueAlert('Burp extender ready...')

        return
Esempio n. 3
0
    def registerExtenderCallbacks(self, callbacks):
        '''
        This method is invoked on startup.
        '''
        self._callbacks = callbacks

        if self.opt.file:
            if os.path.isfile(self.opt.file):
                self.restoreState(self.opt.file)
                self.issueAlert('restored state from %s' % (self.opt.file,))
            else:
                self.issueAlert('could not restore state from %s:'
                                'file does not exist' % (self.opt.file,))

        if self.opt.interactive:
            ConsoleMenu(_burp=self)

        if not self.opt.disable_reloading:
            self.monitor = PluginMonitorThread(self)
            self.monitor.start()

        self.issueAlert('burp extender ready...')

        return
Esempio n. 4
0
    def registerExtenderCallbacks(self, callbacks):
        '''
        This method is invoked on startup.
        '''
        self._callbacks = callbacks

        if self.opt.file:
            if os.path.isfile(self.opt.file):
                self.restoreState(self.opt.file)
                self.issueAlert('Restored state from %s' % (self.opt.file, ))
            else:
                self.issueAlert('Could not restore state from %s:'
                                'file does not exist' % (self.opt.file, ))

        for module, _ in self._menus.options():
            if self._menus.getbool(module) is True:
                for menu in _get_menus(module):
                    menu(self)

        for component, _ in self._components.options():
            if self._components.getbool(component) is True:
                _get_plugins(component)

        if not self.opt.disable_reloading:
            self._monitor_item(self.config)
            self.monitor = PluginMonitorThread(self)
            self.monitor.start()

        try:
            self.setExtensionName('jython-burp-api')
        except Exception:
            pass

        self.issueAlert('Burp extender ready...')

        return
class BurpExtender(IBurpExtender, ComponentManager):

    _components = ConfigSection('components', '')
    menus = ConfigSection('menus', '')

    def __init__(self):
        ComponentManager.__init__(self)
        self.log = logging.getLogger(self.__class__.__name__)
        self.monitoring = []


    def __repr__(self):
        return '<BurpExtender at %#x>' % (id(self),)


    def componentActivated(self, component):
        component.burp = self
        component.config = self.config
        component.log = self.log


    def setCommandLineArgs(self, args):
        '''
        This method is invoked immediately after the implementation's
        constructor to pass any command-line arguments that were passed
        to Burp Suite on startup.

        The following command-line options have been made available:

        -i, --interactive   Run Burp in interactive mode (Jython Console)
        -f <FILE>           Restore from burp state file upon startup
        -d, --debug         Set log level to DEBUG
        -v, --verbose       Set log level to INFO
        -C, --config        Specify an alternate config (default: burp.ini)
        --disable-reloading Disable monitoring of plugins for changes
        -h
        '''
        from optparse import OptionParser
        parser = OptionParser()

        parser.add_option('-i', '--interactive',
                          action='store_true',
                          help='Run Burp in interactive mode (Jython Console)')

        parser.add_option('-f', '--file', metavar='FILE',
                          help='Restore Burp state from FILE on startup')

        parser.add_option('-d', '--debug',
                          action='store_true',
                          help='Set log level to DEBUG')

        parser.add_option('-v', '--verbose',
                          action='store_true',
                          help='Set log level to INFO')

        parser.add_option('-P', '--python-path',
                          default='',
                          help='Set PYTHONPATH used by Jython')

        parser.add_option('-C', '--config',
                          default='burp.ini',
                          help='Specify alternate jython-burp config file')

        parser.add_option('--disable-reloading',
                          action='store_true',
                          help='Disable hot-reloading when a file is changed')

        opt, args = parser.parse_args(list(args))

        if opt.debug:
            logging.basicConfig(
                filename='jython-burp.log',
                format='%(asctime)-15s - %(levelname)s - %(message)s',
                level=logging.DEBUG)

        elif opt.verbose:
            logging.basicConfig(
                filename='jython-burp.log',
                format='%(asctime)-15s - %(levelname)s - %(message)s',
                level=logging.INFO)

        self.config = Configuration(opt.config)

        if opt.interactive:
            from java.util import Properties

            pre_properties = System.getProperties()
            pre_properties['python.console'] = 'org.python.util.ReadlineConsole'

            post_properties = Properties()

            if opt.python_path:
                post_properties['python.path'] = opt.python_path

            PythonInterpreter.initialize(
                pre_properties, post_properties, sys.argv[1:])

            self.console = JLineConsole()
            self.console.exec('import __builtin__ as __builtins__')
            self.console.exec('from gds.burp import HttpRequest, HttpResponse')
            self.console.set('Burp', self)

            sys.stderr.write('Launching interactive session...\n')
            ConsoleThread(self.console).start()

        self.opt, self.args = opt, args

        return


    def applicationClosing(self):
        '''
        This method is invoked immediately before Burp Suite exits.
        '''
        self.log.debug('Shutting down Burp')
        return


    def registerExtenderCallbacks(self, callbacks):
        '''
        This method is invoked on startup.
        '''
        self._callbacks = callbacks

        if self.opt.file:
            if os.path.isfile(self.opt.file):
                self.restoreState(self.opt.file)
                self.issueAlert('Restored state from %s' % (self.opt.file,))
            else:
                self.issueAlert('Could not restore state from %s:'
                                'file does not exist' % (self.opt.file,))

        for module, _ in self.menus.options():
            if self.menus.getbool(module) is True:
                for MenuItemHandler in _get_menus(module):
                    MenuItemHandler(self)

        for component, _ in self._components.options():
            if self._components.getbool(component) is True:
                _get_plugins(component)

        if not self.opt.disable_reloading:
            self.monitor = PluginMonitorThread(self)
            self.monitor.start()

        self.issueAlert('Burp extender ready...')

        return


    def _check_cb(self):
        if hasattr(self, '_callbacks'):
            return getattr(self, '_callbacks')


    def _check_and_callback(self, method, *args):
        cb = self._check_cb()

        if not hasattr(cb, method.__name__):
            raise Exception("%s not available in your version of Burp" % (
                            method.__name__,))

        return getattr(cb, method.__name__)(*args)


    cb = property(_check_cb)


    @callback
    def makeHttpRequest(self, host, port, useHttps, request):
        return


    @callback
    def sendToRepeater(self, host, port, useHttps, request, tabCaption):
        return


    @callback
    def sendToIntruder(self, host, port, useHttps, request, *args):
        return


    @callback
    def sendToSpider(self, url):
        return


    @callback
    def doActiveScan(self, host, port, useHttps, request, *args):
        return


    @callback
    def doPassiveScan(self, host, port, useHttps, request, response):
        return


    @callback
    def getScanIssues(self, urlPrefix):
        return


    def registerMenuItem(self, menuItemCaption, menuItemHandler):
        '''
        This method can be used to register a new menu item which
        will appear on the various context menus that are used
        throughout Burp Suite to handle user-driven actions.

        :param menuItemCaption: The caption to be displayed on the
        menu item.
        :param menuItemHandler: The handler to be invoked when the
        user clicks on the menu item.
        '''
        # don't monitor objects initialized in the interpreter
        if menuItemHandler.__module__ != '__main__':
            _module = menuItemHandler.__module__
            _filename = inspect.getsourcefile(menuItemHandler.__class__)
            _class = menuItemHandler.__class__.__name__

            self.monitoring.append({
                'filename': _filename,
                'class': _class,
                'module': _module,
                'type': 'IMenuItemHandler',
                'instance': weakref.ref(menuItemHandler),
                })

        self._check_and_callback(
            self.registerMenuItem, menuItemCaption, menuItemHandler)

        return


    def newScanIssue(self, issue):
        '''
        This method is invoked whenever Burp Scanner discovers a new,
        unique issue, and can be used to perform customised reporting
        or logging of issues.

        Plugins should implement the :meth:`~INewScanIssueHandler.newScanIssue`
        method of the :class:`INewScanIssueHandler` interface to act upon
        new scan issues as they are identified.

        :param issue: Details of the new scan issue.
        '''
        return NewScanIssueDispatcher(self).newScanIssue(issue)


    def processHttpMessage(self, toolName, messageIsRequest, messageInfo):
        '''
        This method is invoked whenever any of Burp's tools makes an HTTP
        request or receives a response. It allows extensions to intercept
        and modify the HTTP traffic of all Burp tools. For each request,
        the method is invoked after the request has been fully processed
        by the invoking tool and is about to be made on the network. For
        each response, the method is invoked after the response has been
        received from the network and before any processing is performed
        by the invoking tool.

        Plugins should implement the :meth:`processRequest` and/or
        :meth:`processResponse` methods of one or more interfaces in
        :module:`gds.burp.api`.

        A plugin may implement more than one interface, and implement both
        `processRequest` and `processResponse` methods. This allows plugins
        to only hook certain tools in specific scenarios, such as "only
        hook requests sent via Intruder or Scanner, and only hook responses
        received via Proxy tool.

        An example is provided below that only modifies requests as they
        are made via Repeater and Intruder.

        .. code-block:: python
            class MyPlugin(Component):

                implements(IIntruderRequestHandler, IRepeaterRequestHandler)

                def processRequest(self, request):
                    # replace all occurrences of 'somestring' in HTTP
                    # request with 'anotherstring'.
                    request.raw = request.raw.replace('somestring',
                                                      'anotherstring')

        '''
        return PluginDispatcher(self).processHttpMessage(
            toolName, messageIsRequest, messageInfo)


    def getProxyHistory(self, *args):
        '''
        This method returns a generator of all items in the proxy history.

        :params *args: Optional strings to match against url.
        '''
        if args:
            matchers = [re.compile(arg) for arg in args]
            for request in self._check_and_callback(self.getProxyHistory):
                for matcher in matchers:
                    if matcher.search(request.getUrl().toString()):
                        yield HttpRequest(request, _burp=self)
        else:
            for request in self._check_and_callback(self.getProxyHistory):
                yield HttpRequest(request, _burp=self)


    @callback
    def addToSiteMap(self, item):
        return


    def getSiteMap(self, *urlPrefixes):
        '''
        This method returns a generator of details of items in the site map.

        :params *urlPrefixes: Optional URL prefixes, in order to extract
        a specific subset of the site map. The method performs a simple
        case-sensitive text match, returning all site map items whose URL
        begins with the specified prefix. If this parameter is null,
        the entire site map is returned.
        '''
        for urlPrefix in urlPrefixes:
            for item in self._check_and_callback(self.getSiteMap, urlPrefix):
                yield HttpRequest(item, _burp=self)


    @callback
    def excludeFromScope(self, url):
        return


    @callback
    def includeInScope(self, url):
        return


    @callback
    def isInScope(self, url):
        return


    @callback
    def issueAlert(self, message):
        '''
        This method can be used to display a specified message in
        the Burp Suite alerts tab.

        :param message: The alert message to display.
        '''
        return


    def restoreState(self, filename):
        '''
        This method can be used to restore Burp's state from a
        specified saved state file.

        :param filename: The filename containing Burp's saved state.
        '''
        return self._check_and_callback(self.restoreState, File(filename))


    def saveState(self, filename):
        '''
        This method can be used to save Burp's state to a specified
        file. This method blocks until the save operation is completed,
        and must not be called from the event thread.

        :param filename: The filename to save Burp's state in.
        '''
        return self._check_and_callback(self.saveState, File(filename))


    @callback
    def loadConfig(self, config):
        '''
        This method causes Burp to load a new configuration from a
        dictionary of key/value pairs provided. Any settings not
        specified in the dict will be restored to their default values.
        To selectively update only some settings and leave the rest
        unchanged, you should first call saveConfig to obtain Burp's
        current configuration, modify the relevant items in the dict,
        and then call loadConfig with the same dict.

        :param config: A dict of key/value pairs to use as Burp's new
        configuration.
        '''
        return


    def saveConfig(self):
        '''
        This method causes Burp to return its current configuration
        as a dictionary of key/value pairs.
        '''
        return dict(self._check_and_callback(self.saveConfig))


    @callback
    def setProxyInterceptionEnabled(self, enabled):
        '''
        This method sets the interception mode for Burp Proxy.

        :param enabled: Indicates whether interception of proxy messages
        should be enabled.
        '''
        return


    def getBurpVersion(self):
        '''
        This method retrieves information about the version of Burp
        in which the extension is running. It can be used by extensions
        to dynamically adjust their behavior depending on the
        functionality and APIs supported by the current version.
        '''
        return list(self._check_and_callback(self.getBurpVersion))


    def exitSuite(self, promptUser=False):
        '''
        This method can be used to shut down Burp programmatically,
        with an optional prompt to the user. If the method returns,
        the user cancelled the shutdown prompt.

        :param promptUser: Indicates whether to prompt the user to
        confirm the shutdown (default is False: no prompt).
        '''
        if promptUser is True:
            return self._check_and_callback(self.exitSuite, True)

        return self._check_and_callback(self.exitSuite, False)
Esempio n. 6
0
class BurpExtender(IBurpExtender, ComponentManager):

    _components = ConfigSection('components', '')
    _menus = ConfigSection('menus', '')

    def __init__(self):
        ComponentManager.__init__(self)
        self.log = logging.getLogger(self.__class__.__name__)
        self.monitoring = {}

    def __repr__(self):
        return '<BurpExtender at %#x>' % (id(self), )

    def __iter__(self):
        for request in self.getProxyHistory():
            yield request

    def _monitor_item(self, obj):
        # don't monitor objects initialized in the interpreter

        if obj.__module__ == '__main__':
            return

        mod = obj.__module__
        cls = obj.__class__.__name__

        # Monitor the actual configuration file rather than the
        # module the Configuration class is defined in

        if isinstance(obj, Configuration):
            filename = obj.filename

        elif isinstance(obj, (Component, IMenuItemHandler)):
            filename = inspect.getsourcefile(obj.__class__)

        elif isinstance(obj, type):
            filename = inspect.getsourcefile(obj)

        monitoring = self.monitoring.setdefault(filename, [])

        monitoring.append({
            'class': cls,
            'instance': weakref.ref(obj),
            'module': mod,
            })

        return

    def componentActivated(self, component):
        self.log.debug('Activating component: %r', component)
        component.burp = self
        component.config = self.config
        component.log = self.log

        return

    def applicationClosing(self):
        '''
        This method is invoked immediately before Burp Suite exits.
        '''
        self.log.debug('Shutting down Burp')
        return

    def registerExtenderCallbacks(self, callbacks):
        '''
        This method is invoked on startup.
        '''
        self._callbacks = callbacks

        try:
            self.setExtensionName(self.getExtensionName())
        except Exception:
            pass

        try:
            log_filename = self.loadExtensionSetting(*settings.LOG_FILENAME)
            log_format = self.loadExtensionSetting(*settings.LOG_FORMAT)
            log_level = self.loadExtensionSetting(*settings.LOG_LEVEL)

            self.log.setLevel(log_level)

            fileHandler = logging.FileHandler(
                    log_filename, encoding='utf-8', delay=True)

            streamHandler = logging.StreamHandler()

            formatter = logging.Formatter(fmt=log_format)

            fileHandler.setFormatter(formatter)
            streamHandler.setFormatter(formatter)

            self.log.addHandler(fileHandler)
            self.log.addHandler(streamHandler)

            self._handler = fileHandler
        except Exception:
            self.log.exception('Could not load extension logging settings')

        try:
            config = self.loadExtensionSetting(*settings.CONFIG_FILENAME)
            self.config = Configuration(os.path.abspath(config))
        except Exception:
            self.log.exception('Could not load extension config settings')

        try:
            from gds.burp.listeners import PluginListener, \
                    SaveConfigurationOnUnload, \
                    ScannerListener

            SaveConfigurationOnUnload(self)
            PluginListener(self)
            ScannerListener(self)
        except Exception:
            self.log.exception('Could not load extension listener')

        try:
            from gds.burp.ui import ConsoleTab
            self._console_tab = ConsoleTab(self)
            self.console = self._console_tab.interpreter
        except Exception as e:
            self.log.exception('Could not load console tab')

        for module, _ in self._menus.options():
            if self._menus.getbool(module) is True:
                for menu in _get_menus(module):
                    menu(self)

        for component, _ in self._components.options():
            if self._components.getbool(component) is True:
                _get_plugins(component)

        self._monitor_item(self.config)
        self.monitor = PluginMonitorThread(self)
        self.monitor.start()

        self.issueAlert('Burp extender ready...')
        return

    def _check_cb(self):
        if hasattr(self, '_callbacks'):
            return getattr(self, '_callbacks')

    def _check_and_callback(self, method, *args):
        cb = self._check_cb()

        if not hasattr(cb, method.__name__):
            raise Exception("%s() not available in your version of Burp" % (
                            method.__name__, ))

        try:
            return getattr(cb, method.__name__)(*args)
        except AbstractMethodError:
            raise Exception("%s() not available in your version of Burp" % (
                            method.__name__, ))

    cb = property(_check_cb)

    @callback
    def makeHttpRequest(self, host, port, useHttps, request):
        return

    @callback
    def sendToRepeater(self, host, port, useHttps, request, tabCaption):
        return

    @callback
    def sendToIntruder(self, host, port, useHttps, request, *args):
        return

    def sendToSpider(self, url):
        if not self.isInScope(url):
            self.includeInScope(url)

        self._check_and_callback(self.sendToSpider, URL(str(url)))
        return

    @callback
    def doActiveScan(self, host, port, useHttps, request, *args):
        return

    @callback
    def doPassiveScan(self, host, port, useHttps, request, response):
        return

    @callback
    def getScanIssues(self, urlPrefix):
        return

    def registerMenuItem(self, menuItemCaption, menuItemHandler):
        '''
        This method can be used to register a new menu item which
        will appear on the various context menus that are used
        throughout Burp Suite to handle user-driven actions.

        :param menuItemCaption: The caption to be displayed on the
        menu item.
        :param menuItemHandler: The handler to be invoked when the
        user clicks on the menu item.
        '''
        self._monitor_item(menuItemHandler)

        self._check_and_callback(
            self.registerMenuItem, menuItemCaption, menuItemHandler)

        return

    def newScanIssue(self, issue):
        '''
        This method is invoked whenever Burp Scanner discovers a new,
        unique issue, and can be used to perform customised reporting
        or logging of issues.

        Plugins should implement the :meth:`~INewScanIssueHandler.newScanIssue`
        method of the :class:`INewScanIssueHandler` interface to act upon
        new scan issues as they are identified.

        :param issue: Details of the new scan issue.
        '''
        return NewScanIssueDispatcher(self).newScanIssue(issue)

    def processHttpMessage(self, toolName, messageIsRequest, messageInfo):
        '''
        This method is invoked whenever any of Burp's tools makes an HTTP
        request or receives a response. It allows extensions to intercept
        and modify the HTTP traffic of all Burp tools. For each request,
        the method is invoked after the request has been fully processed
        by the invoking tool and is about to be made on the network. For
        each response, the method is invoked after the response has been
        received from the network and before any processing is performed
        by the invoking tool.

        Plugins should implement the :meth:`processRequest` and/or
        :meth:`processResponse` methods of one or more interfaces in
        :module:`gds.burp.api`.

        A plugin may implement more than one interface, and implement both
        `processRequest` and `processResponse` methods. This allows plugins
        to only hook certain tools in specific scenarios, such as "only
        hook requests sent via Intruder or Scanner, and only hook responses
        received via Proxy tool.

        An example is provided below that only modifies requests as they
        are made via Repeater and Intruder.

        .. code-block:: python
            class MyPlugin(Component):

                implements(IIntruderRequestHandler, IRepeaterRequestHandler)

                def processRequest(self, request):
                    # replace all occurrences of 'somestring' in HTTP
                    # request with 'anotherstring'.
                    request.raw = request.raw.replace('somestring',
                                                      'anotherstring')

        '''
        return PluginDispatcher(self).processHttpMessage(
            toolName, messageIsRequest, messageInfo)

    def getProxyHistory(self, *args):
        '''
        This method returns a generator of all items in the proxy history.

        :params *args: Optional strings to match against url.
        '''
        if args:
            matchers = [re.compile(arg) for arg in args]
            for request in self._check_and_callback(self.getProxyHistory):
                for matcher in matchers:
                    if matcher.search(request.getUrl().toString()):
                        yield HttpRequest(request, _burp=self)
                        break
        else:
            for request in self._check_and_callback(self.getProxyHistory):
                yield HttpRequest(request, _burp=self)

    history = property(lambda burp: list(burp.getProxyHistory()))

    @callback
    def addToSiteMap(self, item):
        return

    def getSiteMap(self, *urlPrefixes):
        '''
        This method returns a generator of details of items in the site map.

        :params *urlPrefixes: Optional URL prefixes, in order to extract
        a specific subset of the site map. The method performs a simple
        case-sensitive text match, returning all site map items whose URL
        begins with the specified prefix. If this parameter is null,
        the entire site map is returned.
        '''
        for urlPrefix in urlPrefixes or ('http', ):
            for item in self._check_and_callback(self.getSiteMap, urlPrefix):
                yield HttpRequest(item, _burp=self)

    def excludeFromScope(self, url):
        self._check_and_callback(self.excludeFromScope, URL(str(url)))
        return

    def includeInScope(self, url):
        self._check_and_callback(self.includeInScope, URL(str(url)))
        return

    def isInScope(self, url):
        return self._check_and_callback(self.isInScope, URL(str(url)))

    @callback
    def issueAlert(self, message):
        '''
        This method can be used to display a specified message in
        the Burp Suite alerts tab.

        :param message: The alert message to display.
        '''
        return

    def restoreState(self, filename):
        '''
        This method can be used to restore Burp's state from a
        specified saved state file.

        :param filename: The filename containing Burp's saved state.
        '''
        return self._check_and_callback(self.restoreState, File(filename))

    def saveState(self, filename):
        '''
        This method can be used to save Burp's state to a specified
        file. This method blocks until the save operation is completed,
        and must not be called from the event thread.

        :param filename: The filename to save Burp's state in.
        '''
        return self._check_and_callback(self.saveState, File(filename))

    @callback
    def loadConfig(self, config):
        '''
        This method causes Burp to load a new configuration from a
        dictionary of key/value pairs provided. Any settings not
        specified in the dict will be restored to their default values.
        To selectively update only some settings and leave the rest
        unchanged, you should first call saveConfig to obtain Burp's
        current configuration, modify the relevant items in the dict,
        and then call loadConfig with the same dict.

        :param config: A dict of key/value pairs to use as Burp's new
        configuration.
        '''
        return

    def saveConfig(self):
        '''
        This method causes Burp to return its current configuration
        as a dictionary of key/value pairs.
        '''
        return dict(self._check_and_callback(self.saveConfig))

    @callback
    def setProxyInterceptionEnabled(self, enabled):
        '''
        This method sets the interception mode for Burp Proxy.

        :param enabled: Indicates whether interception of proxy messages
        should be enabled.
        '''
        return

    def getBurpVersion(self):
        '''
        This method retrieves information about the version of Burp
        in which the extension is running. It can be used by extensions
        to dynamically adjust their behavior depending on the
        functionality and APIs supported by the current version.
        '''
        return list(self._check_and_callback(self.getBurpVersion))

    version = property(getBurpVersion)

    def exitSuite(self, promptUser=False):
        '''
        This method can be used to shut down Burp programmatically,
        with an optional prompt to the user. If the method returns,
        the user cancelled the shutdown prompt.

        :param promptUser: Indicates whether to prompt the user to
        confirm the shutdown (default is False: no prompt).
        '''
        if promptUser is True:
            return self._check_and_callback(self.exitSuite, True)

        return self._check_and_callback(self.exitSuite, False)

    @callback
    def addScanIssue(self, issue):
        '''
        This method is used to register a new Scanner issue.
        
        Note: Wherever possible, extensions should implement custom
        Scanner checks using IScannerCheck and report issues via those
        checks, so as to integrate with Burp's user-driven workflow,
        and ensure proper consolidation of duplicate reported issues.
        This method is only designed for tasks outside of the normal
        testing workflow, such as importing results from other scanning
        tools.

        :param issue: An object created by the extension that implements
        the IScanIssue interface.
        '''
        return

    @callback
    def addSuiteTab(self, tab):
        return

    @callback
    def applyMarkers(self, request, requestMarkers=None, responseMarkers=None):
        return

    @callback
    def createMessageEditor(self, controller, editable):
        return

    @callback
    def createTextEditor(self):
        return

    @callback
    def customizeUiComponent(self, component):
        return

    @callback
    def getHelpers(self):
        return

    helpers = property(lambda burp: burp.getHelpers())

    @callback
    def getStderr(self):
        return

    stderr = property(lambda burp: burp.getStderr())

    @callback
    def getStdout(self):
        return

    stdout = property(lambda burp: burp.getStdout())

    @callback
    def getToolName(self, toolFlag):
        return

    @callback
    def registerContextMenuFactory(self, factory):
        return
    
    @callback
    def registerExtensionStateListener(self, listener):
        return

    @callback
    def registerHttpListener(self, listener):
        return

    @callback
    def registerIntruderPayloadGeneratorFactory(self, factory):
        return

    @callback
    def registerIntruderPayloadProcessor(self, processor):
        return

    @callback
    def registerMessageEditorTabFactory(self, factory):
        return

    @callback
    def registerProxyListener(self, listener):
        return

    @callback
    def registerScannerCheck(self, check):
        return

    @callback
    def registerScannerInsertionPointProvider(self, provider):
        return

    @callback
    def registerScannerListener(self, listener):
        return

    @callback
    def registerSessionHandlingAction(self, action):
        return

    @callback
    def removeSuiteTab(self, tab):
        return

    @callback
    def saveBuffersToTempFiles(self, request):
        return

    @callback
    def saveToTempFile(self, buffer):
        return

    @callback
    def setExtensionName(self, name):
        return

    def getExtensionName(self):
        return self.loadExtensionSetting(*settings.EXTENSION_NAME)

    def loadExtensionSetting(self, name, default=None):
        if name.startswith('jython.'):
            settings = self._check_and_callback(self.loadExtensionSetting,
                    'settings')
            if settings:
                settings = json.loads(settings)
                return settings.get(name, default)
            return default

        value = self._check_and_callback(self.loadExtensionSetting, name)
        if not value and default is not None:
            return default
        return value

    def saveExtensionSetting(self, name, value):
        if name.startswith('jython.'):
            settings = self._check_and_callback(self.loadExtensionSetting,
                    'settings')

            if settings:
                settings = json.loads(settings)
            else:
                settings = {}

            settings[name] = value
            self._check_and_callback(self.saveExtensionSetting,
                    'settings', json.dumps(settings))
            return

        self._check_and_callback(self.saveExtensionSetting, name, value)
        return
Esempio n. 7
0
    def registerExtenderCallbacks(self, callbacks):
        '''
        This method is invoked on startup.
        '''
        self._callbacks = callbacks

        try:
            self.setExtensionName(self.getExtensionName())
        except Exception:
            pass

        try:
            log_filename = self.loadExtensionSetting(*settings.LOG_FILENAME)
            log_format = self.loadExtensionSetting(*settings.LOG_FORMAT)
            log_level = self.loadExtensionSetting(*settings.LOG_LEVEL)

            self.log.setLevel(log_level)

            fileHandler = logging.FileHandler(
                    log_filename, encoding='utf-8', delay=True)

            streamHandler = logging.StreamHandler()

            formatter = logging.Formatter(fmt=log_format)

            fileHandler.setFormatter(formatter)
            streamHandler.setFormatter(formatter)

            self.log.addHandler(fileHandler)
            self.log.addHandler(streamHandler)

            self._handler = fileHandler
        except Exception:
            self.log.exception('Could not load extension logging settings')

        try:
            config = self.loadExtensionSetting(*settings.CONFIG_FILENAME)
            self.config = Configuration(os.path.abspath(config))
        except Exception:
            self.log.exception('Could not load extension config settings')

        try:
            from gds.burp.listeners import PluginListener, \
                    SaveConfigurationOnUnload, \
                    ScannerListener

            SaveConfigurationOnUnload(self)
            PluginListener(self)
            ScannerListener(self)
        except Exception:
            self.log.exception('Could not load extension listener')

        try:
            from gds.burp.ui import ConsoleTab
            self._console_tab = ConsoleTab(self)
            self.console = self._console_tab.interpreter
        except Exception as e:
            self.log.exception('Could not load console tab')

        for module, _ in self._menus.options():
            if self._menus.getbool(module) is True:
                for menu in _get_menus(module):
                    menu(self)

        for component, _ in self._components.options():
            if self._components.getbool(component) is True:
                _get_plugins(component)

        self._monitor_item(self.config)
        self.monitor = PluginMonitorThread(self)
        self.monitor.start()

        self.issueAlert('Burp extender ready...')
        return
Esempio n. 8
0
class BurpExtender(IBurpExtender):
    def __init__(self):
        self.monitoring = []

    def __repr__(self):
        return '<BurpExtender %#x>' % (id(self),)


    def setCommandLineArgs(self, args):
        '''
        This method is invoked immediately after the implementation's
        constructor to pass any command-line arguments that were passed
        to Burp Suite on startup.

        The following command-line options have been made available:

        -i, --interactive   Run Burp in interactive mode (Jython Console)
        -f <FILE>           Restore from burp state file upon startup
        -h
        '''
        from optparse import OptionParser
        parser = OptionParser()

        parser.add_option('-i', '--interactive',
                          action='store_true',
                          help='Run Burp in interactive mode (Jython Console)')

        parser.add_option('-f', '--file', metavar='FILE',
                          help='Restore Burp state from FILE on startup')

        parser.add_option('-P', '--python-path',
                          default='',
                          help='Set PYTHONPATH used by Jython')

        parser.add_option('--disable-reloading',
                          action='store_true',
                          help='Disable hot-reloading when a file is changed')

        opt, args = parser.parse_args(list(args))

        if opt.interactive:
            from java.util import Properties

            pre_properties = System.getProperties()
            pre_properties['python.console'] = 'org.python.util.ReadlineConsole'

            post_properties = Properties()

            if opt.python_path:
                post_properties['python.path'] = opt.python_path

            PythonInterpreter.initialize(pre_properties, post_properties, sys.argv[1:])

            self.console = JLineConsole()
            self.console.exec('import __builtin__ as __builtins__')
            self.console.exec('from gds.burp import HttpRequest, HttpResponse')
            self.console.set('Burp', self)

            sys.stderr.write('Launching interactive session...\n')
            ConsoleThread(self.console).start()

        self.opt, self.args = opt, args

        return


    def applicationClosing(self):
        '''
        This method is invoked immediately before Burp Suite exits.
        '''
        return


    def registerExtenderCallbacks(self, callbacks):
        '''
        This method is invoked on startup.
        '''
        self._callbacks = callbacks

        if self.opt.file:
            if os.path.isfile(self.opt.file):
                self.restoreState(self.opt.file)
                self.issueAlert('restored state from %s' % (self.opt.file,))
            else:
                self.issueAlert('could not restore state from %s:'
                                'file does not exist' % (self.opt.file,))

        if self.opt.interactive:
            ConsoleMenu(_burp=self)

        if not self.opt.disable_reloading:
            self.monitor = PluginMonitorThread(self)
            self.monitor.start()

        self.issueAlert('burp extender ready...')

        return


    def _check_cb(self):
        if hasattr(self, '_callbacks'):
            return getattr(self, '_callbacks')


    def _check_and_callback(self, method, *args):
        cb = self._check_cb()

        if not hasattr(cb, method.__name__):
            raise Exception("%s not available in your version of Burp" % (
                            method.__name__,))

        return getattr(cb, method.__name__)(*args)


    cb = property(_check_cb)


    @callback
    def makeHttpRequest(self, host, port, useHttps, request):
        return


    @callback
    def sendToRepeater(self, host, port, useHttps, request, tabCaption):
        return


    @callback
    def sendToIntruder(self, host, port, useHttps, request, *args):
        return


    @callback
    def sendToSpider(self, url):
        return


    @callback
    def doActiveScan(self, host, port, useHttps, request, *args):
        return


    @callback
    def doPassiveScan(self, host, port, useHttps, request, response):
        return


    @callback
    def getScanIssues(self, urlPrefix):
        return


    def registerMenuItem(self, menuItemCaption, menuItemHandler):
        '''
        This method can be used to register a new menu item which
        will appear on the various context menus that are used
        throughout Burp Suite to handle user-driven actions.

        :param menuItemCaption: The caption to be displayed on the
        menu item.
        :param menuItemHandler: The handler to be invoked when the
        user clicks on the menu item.
        '''
        # don't monitor objects initialized in the interpreter
        if menuItemHandler.__module__ != '__main__':
            _module = menuItemHandler.__module__
            _filename = sys.modules[_module].__file__
            _class = menuItemHandler.__class__.__name__

            self.monitoring.append({
                'filename': _filename.replace('$py.class', '.py'),
                'class': _class,
                'module': _module,
                'type': 'IMenuItemHandler',
                'instance': weakref.ref(menuItemHandler),
                })

        self._check_and_callback(
            self.registerMenuItem, menuItemCaption, menuItemHandler)

        return


    def getProxyHistory(self, *args):
        '''
        This method returns a generator of all items in the proxy history.

        :params *args: Optional strings to match against url.
        '''
        if args:
            matchers = [re.compile(arg) for arg in args]
            for request in self._check_and_callback(self.getProxyHistory):
                for matcher in matchers:
                    if matcher.search(request.getUrl().toString()):
                        yield HttpRequest(request, _burp=self)
        else:
            for request in self._check_and_callback(self.getProxyHistory):
                yield HttpRequest(request, _burp=self)


    @callback
    def addToSiteMap(self, item):
        return


    def getSiteMap(self, *urlPrefixes):
        '''
        This method returns a generator of details of items in the site map.

        :params *urlPrefixes: Optional URL prefixes, in order to extract
        a specific subset of the site map. The method performs a simple
        case-sensitive text match, returning all site map items whose URL
        begins with the specified prefix. If this parameter is null,
        the entire site map is returned.
        '''
        for urlPrefix in urlPrefixes:
            for item in self._check_and_callback(self.getSiteMap, urlPrefix):
                yield HttpRequest(item, _burp=self)


    @callback
    def excludeFromScope(self, url):
        return


    @callback
    def includeInScope(self, url):
        return


    @callback
    def isInScope(self, url):
        return


    @callback
    def issueAlert(self, message):
        '''
        This method can be used to display a specified message in
        the Burp Suite alerts tab.

        :param message: The alert message to display.
        '''
        return


    def restoreState(self, filename):
        '''
        This method can be used to restore Burp's state from a
        specified saved state file.

        :param filename: The filename containing Burp's saved state.
        '''
        return self._check_and_callback(self.restoreState, File(filename))


    def saveState(self, filename):
        '''
        This method can be used to save Burp's state to a specified
        file. This method blocks until the save operation is completed,
        and must not be called from the event thread.

        :param filename: The filename to save Burp's state in.
        '''
        return self._check_and_callback(self.saveState, File(filename))


    @callback
    def loadConfig(self, config):
        '''
        This method causes Burp to load a new configuration from a
        dictionary of key/value pairs provided. Any settings not
        specified in the dict will be restored to their default values.
        To selectively update only some settings and leave the rest
        unchanged, you should first call saveConfig to obtain Burp's
        current configuration, modify the relevant items in the dict,
        and then call loadConfig with the same dict.

        :param config: A dict of key/value pairs to use as Burp's new
        configuration.
        '''
        return


    def saveConfig(self):
        '''
        This method causes Burp to return its current configuration
        as a dictionary of key/value pairs.
        '''
        return dict(self._check_and_callback(self.saveConfig))


    @callback
    def setProxyInterceptionEnabled(self, enabled):
        '''
        This method sets the interception mode for Burp Proxy.

        :param enabled: Indicates whether interception of proxy messages
        should be enabled.
        '''
        return


    def getBurpVersion(self):
        '''
        This method retrieves information about the version of Burp
        in which the extension is running. It can be used by extensions
        to dynamically adjust their behavior depending on the
        functionality and APIs supported by the current version.
        '''
        return list(self._check_and_callback(self.getBurpVersion))


    def exitSuite(self, promptUser=False):
        '''
        This method can be used to shut down Burp programmatically,
        with an optional prompt to the user. If the method returns,
        the user cancelled the shutdown prompt.

        :param promptUser: Indicates whether to prompt the user to
        confirm the shutdown (default is False: no prompt).
        '''
        if promptUser is True:
            return self._check_and_callback(self.exitSuite, True)

        return self._check_and_callback(self.exitSuite, False)
Esempio n. 9
0
class BurpExtender(IBurpExtender, ComponentManager):

    _components = ConfigSection('components', '')
    _menus = ConfigSection('menus', '')

    def __init__(self):
        ComponentManager.__init__(self)
        self.log = logging.getLogger(self.__class__.__name__)
        self.monitoring = {}

    def __repr__(self):
        return '<BurpExtender at %#x>' % (id(self),)

    def _monitor_item(self, obj):
        # don't monitor objects initialized in the interpreter

        if obj.__module__ == '__main__':
            return

        mod = obj.__module__
        cls = obj.__class__.__name__

        # Monitor the actual configuration file rather than the
        # module the Configuration class is defined in

        if isinstance(obj, Configuration):
            filename = obj.filename

        elif isinstance(obj, (Component, IMenuItemHandler)):
            filename = inspect.getsourcefile(obj.__class__)

        elif isinstance(obj, type):
            filename = inspect.getsourcefile(obj)

        monitoring = self.monitoring.setdefault(filename, [])

        monitoring.append({
            'class': cls,
            'instance': weakref.ref(obj),
            'module': mod,
            })

        return

    def componentActivated(self, component):
        self.log.debug('Activating component: %r', component)
        component.burp = self
        component.config = self.config
        component.log = self.log

        return

    def setCommandLineArgs(self, args):
        '''
        This method is invoked immediately after the implementation's
        constructor to pass any command-line arguments that were passed
        to Burp Suite on startup.

        The following command-line options have been made available:

        -i, --interactive   Run Burp in interactive mode (Jython Console)
        -f <FILE>           Restore from burp state file upon startup
        -d, --debug         Set log level to DEBUG
        -v, --verbose       Set log level to INFO
        -C, --config        Specify an alternate config (default: burp.ini)
        --disable-reloading Disable monitoring of plugins for changes
        -h
        '''
        from optparse import OptionParser
        parser = OptionParser()

        parser.add_option('-i', '--interactive',
                          action='store_true',
                          help='Run Burp in interactive mode (Jython Console)')

        parser.add_option('-f', '--file', metavar='FILE',
                          help='Restore Burp state from FILE on startup')

        parser.add_option('-d', '--debug',
                          action='store_true',
                          help='Set log level to DEBUG')

        parser.add_option('-v', '--verbose',
                          action='store_true',
                          help='Set log level to INFO')

        parser.add_option('-P', '--python-path',
                          default='',
                          help='Set PYTHONPATH used by Jython')

        parser.add_option('-C', '--config',
                          default='burp.ini',
                          help='Specify alternate jython-burp config file')

        parser.add_option('--disable-reloading',
                          action='store_true',
                          help='Disable hot-reloading when a file is changed')

        opt, args = parser.parse_args(list(args))

        if opt.debug:
            logging.basicConfig(
                filename='jython-burp.log',
                format='%(asctime)-15s - %(levelname)s - %(message)s',
                level=logging.DEBUG)

        elif opt.verbose:
            logging.basicConfig(
                filename='jython-burp.log',
                format='%(asctime)-15s - %(levelname)s - %(message)s',
                level=logging.INFO)

        self.config = Configuration(os.path.abspath(opt.config))

        if opt.interactive:
            from java.util import Properties

            pre_properties = System.getProperties()
            pre_properties['python.console'] = 'org.python.util.ReadlineConsole'

            post_properties = Properties()

            if opt.python_path:
                post_properties['python.path'] = opt.python_path

            PythonInterpreter.initialize(
                pre_properties, post_properties, sys.argv[1:])

            self.console = JLineConsole()
            self.console.exec('import __builtin__ as __builtins__')
            self.console.exec('from gds.burp import HttpRequest, HttpResponse')
            self.console.set('Burp', self)

            sys.stderr.write('Launching interactive session...\n')
            ConsoleThread(self.console).start()

        self.opt, self.args = opt, args

        return

    def applicationClosing(self):
        '''
        This method is invoked immediately before Burp Suite exits.
        '''
        self.log.debug('Shutting down Burp')
        return

    def registerExtenderCallbacks(self, callbacks):
        '''
        This method is invoked on startup.
        '''
        self._callbacks = callbacks

        if self.opt.file:
            if os.path.isfile(self.opt.file):
                self.restoreState(self.opt.file)
                self.issueAlert('Restored state from %s' % (self.opt.file,))
            else:
                self.issueAlert('Could not restore state from %s:'
                                'file does not exist' % (self.opt.file,))

        for module, _ in self._menus.options():
            if self._menus.getbool(module) is True:
                for menu in _get_menus(module):
                    menu(self)

        for component, _ in self._components.options():
            if self._components.getbool(component) is True:
                _get_plugins(component)

        if not self.opt.disable_reloading:
            self._monitor_item(self.config)
            self.monitor = PluginMonitorThread(self)
            self.monitor.start()

        self.issueAlert('Burp extender ready...')

        return

    def _check_cb(self):
        if hasattr(self, '_callbacks'):
            return getattr(self, '_callbacks')

    def _check_and_callback(self, method, *args):
        cb = self._check_cb()

        if not hasattr(cb, method.__name__):
            raise Exception("%s not available in your version of Burp" % (
                            method.__name__,))

        return getattr(cb, method.__name__)(*args)


    cb = property(_check_cb)

    @callback
    def makeHttpRequest(self, host, port, useHttps, request):
        return

    @callback
    def sendToRepeater(self, host, port, useHttps, request, tabCaption):
        return

    @callback
    def sendToIntruder(self, host, port, useHttps, request, *args):
        return

    @callback
    def sendToSpider(self, url):
        return

    @callback
    def doActiveScan(self, host, port, useHttps, request, *args):
        return

    @callback
    def doPassiveScan(self, host, port, useHttps, request, response):
        return

    @callback
    def getScanIssues(self, urlPrefix):
        return


    def registerMenuItem(self, menuItemCaption, menuItemHandler):
        '''
        This method can be used to register a new menu item which
        will appear on the various context menus that are used
        throughout Burp Suite to handle user-driven actions.

        :param menuItemCaption: The caption to be displayed on the
        menu item.
        :param menuItemHandler: The handler to be invoked when the
        user clicks on the menu item.
        '''
        self._monitor_item(menuItemHandler)

        self._check_and_callback(
            self.registerMenuItem, menuItemCaption, menuItemHandler)

        return


    def newScanIssue(self, issue):
        '''
        This method is invoked whenever Burp Scanner discovers a new,
        unique issue, and can be used to perform customised reporting
        or logging of issues.

        Plugins should implement the :meth:`~INewScanIssueHandler.newScanIssue`
        method of the :class:`INewScanIssueHandler` interface to act upon
        new scan issues as they are identified.

        :param issue: Details of the new scan issue.
        '''
        return NewScanIssueDispatcher(self).newScanIssue(issue)


    def processHttpMessage(self, toolName, messageIsRequest, messageInfo):
        '''
        This method is invoked whenever any of Burp's tools makes an HTTP
        request or receives a response. It allows extensions to intercept
        and modify the HTTP traffic of all Burp tools. For each request,
        the method is invoked after the request has been fully processed
        by the invoking tool and is about to be made on the network. For
        each response, the method is invoked after the response has been
        received from the network and before any processing is performed
        by the invoking tool.

        Plugins should implement the :meth:`processRequest` and/or
        :meth:`processResponse` methods of one or more interfaces in
        :module:`gds.burp.api`.

        A plugin may implement more than one interface, and implement both
        `processRequest` and `processResponse` methods. This allows plugins
        to only hook certain tools in specific scenarios, such as "only
        hook requests sent via Intruder or Scanner, and only hook responses
        received via Proxy tool.

        An example is provided below that only modifies requests as they
        are made via Repeater and Intruder.

        .. code-block:: python
            class MyPlugin(Component):

                implements(IIntruderRequestHandler, IRepeaterRequestHandler)

                def processRequest(self, request):
                    # replace all occurrences of 'somestring' in HTTP
                    # request with 'anotherstring'.
                    request.raw = request.raw.replace('somestring',
                                                      'anotherstring')

        '''
        return PluginDispatcher(self).processHttpMessage(
            toolName, messageIsRequest, messageInfo)


    def getProxyHistory(self, *args):
        '''
        This method returns a generator of all items in the proxy history.

        :params *args: Optional strings to match against url.
        '''
        if args:
            matchers = [re.compile(arg) for arg in args]
            for request in self._check_and_callback(self.getProxyHistory):
                for matcher in matchers:
                    if matcher.search(request.getUrl().toString()):
                        yield HttpRequest(request, _burp=self)
                        break
        else:
            for request in self._check_and_callback(self.getProxyHistory):
                yield HttpRequest(request, _burp=self)

    @callback
    def addToSiteMap(self, item):
        return

    def getSiteMap(self, *urlPrefixes):
        '''
        This method returns a generator of details of items in the site map.

        :params *urlPrefixes: Optional URL prefixes, in order to extract
        a specific subset of the site map. The method performs a simple
        case-sensitive text match, returning all site map items whose URL
        begins with the specified prefix. If this parameter is null,
        the entire site map is returned.
        '''
        for urlPrefix in urlPrefixes or ('http',):
            for item in self._check_and_callback(self.getSiteMap, urlPrefix):
                yield HttpRequest(item, _burp=self)

    @callback
    def excludeFromScope(self, url):
        return

    @callback
    def includeInScope(self, url):
        return

    @callback
    def isInScope(self, url):
        return

    @callback
    def issueAlert(self, message):
        '''
        This method can be used to display a specified message in
        the Burp Suite alerts tab.

        :param message: The alert message to display.
        '''
        return

    def restoreState(self, filename):
        '''
        This method can be used to restore Burp's state from a
        specified saved state file.

        :param filename: The filename containing Burp's saved state.
        '''
        return self._check_and_callback(self.restoreState, File(filename))

    def saveState(self, filename):
        '''
        This method can be used to save Burp's state to a specified
        file. This method blocks until the save operation is completed,
        and must not be called from the event thread.

        :param filename: The filename to save Burp's state in.
        '''
        return self._check_and_callback(self.saveState, File(filename))

    @callback
    def loadConfig(self, config):
        '''
        This method causes Burp to load a new configuration from a
        dictionary of key/value pairs provided. Any settings not
        specified in the dict will be restored to their default values.
        To selectively update only some settings and leave the rest
        unchanged, you should first call saveConfig to obtain Burp's
        current configuration, modify the relevant items in the dict,
        and then call loadConfig with the same dict.

        :param config: A dict of key/value pairs to use as Burp's new
        configuration.
        '''
        return

    def saveConfig(self):
        '''
        This method causes Burp to return its current configuration
        as a dictionary of key/value pairs.
        '''
        return dict(self._check_and_callback(self.saveConfig))

    @callback
    def setProxyInterceptionEnabled(self, enabled):
        '''
        This method sets the interception mode for Burp Proxy.

        :param enabled: Indicates whether interception of proxy messages
        should be enabled.
        '''
        return

    def getBurpVersion(self):
        '''
        This method retrieves information about the version of Burp
        in which the extension is running. It can be used by extensions
        to dynamically adjust their behavior depending on the
        functionality and APIs supported by the current version.
        '''
        return list(self._check_and_callback(self.getBurpVersion))

    def exitSuite(self, promptUser=False):
        '''
        This method can be used to shut down Burp programmatically,
        with an optional prompt to the user. If the method returns,
        the user cancelled the shutdown prompt.

        :param promptUser: Indicates whether to prompt the user to
        confirm the shutdown (default is False: no prompt).
        '''
        if promptUser is True:
            return self._check_and_callback(self.exitSuite, True)

        return self._check_and_callback(self.exitSuite, False)