Пример #1
0
class BurpExtender(IBurpExtender, IProxyListener, ITab):
    def getTabCaption(self):  ### ITab
        return NAME

    def getUiComponent(self):  ### ITab
        return self.settings

    def registerExtenderCallbacks(self, this_callbacks):  ### IBurpExtender
        global callbacks, helpers
        global extension_enable
        global remove_csrf_headers, remove_csrf_params, change_method_to_post
        global change_ct_to_json, change_ct_to_plain, change_to_get

        callbacks = this_callbacks
        helpers = callbacks.getHelpers()
        callbacks.setExtensionName(NAME)

        self.settings = JPanel(GridBagLayout())
        c = GridBagConstraints()

        self.extension_enable_box = JCheckBox('Enable extension',
                                              extension_enable)
        self.extension_enable_box.setFont(Font("Serif", Font.BOLD, 20))
        self.extension_enable_box.setForeground(Color(0, 0, 153))
        c.insets = Insets(5, 5, 5, 5)
        c.gridx = 0
        c.gridy = 0
        c.gridwidth = 1
        c.weightx = 1
        c.fill = GridBagConstraints.NONE
        c.anchor = GridBagConstraints.WEST
        self.settings.add(self.extension_enable_box, c)

        self.remove_csrf_headers_box = JCheckBox('Remove CSRF headers',
                                                 remove_csrf_params)
        self.remove_csrf_headers_box.setFont(Font("Serif", Font.BOLD, 20))
        self.remove_csrf_headers_box.setForeground(Color(0, 0, 153))
        c.insets = Insets(40, 5, 5, 5)
        c.gridx = 0
        c.gridy = 1
        self.settings.add(self.remove_csrf_headers_box, c)

        remove_csrf_headers_box_lbl = JLabel(
            'Check to remove headers with CSRF tokens from all requests.')
        c.insets = Insets(5, 5, 5, 5)
        c.gridx = 0
        c.gridy = 2
        self.settings.add(remove_csrf_headers_box_lbl, c)

        self.remove_csrf_params_box = JCheckBox('Remove CSRF parameters',
                                                remove_csrf_params)
        self.remove_csrf_params_box.setFont(Font("Serif", Font.BOLD, 20))
        self.remove_csrf_params_box.setForeground(Color(0, 0, 153))
        c.gridx = 0
        c.gridy = 3
        self.settings.add(self.remove_csrf_params_box, c)

        remove_csrf_params_box_lbl = JLabel(
            'Check to remove URL/body parameters with CSRF tokens from all requests. URL-encoded, multipart, JSON parameters are supported.'
        )
        c.gridx = 0
        c.gridy = 4
        self.settings.add(remove_csrf_params_box_lbl, c)

        self.change_method_to_post_box = JCheckBox(
            'Change HTTP method to POST', change_method_to_post)
        self.change_method_to_post_box.setFont(Font("Serif", Font.BOLD, 20))
        self.change_method_to_post_box.setForeground(Color(0, 0, 153))
        c.gridx = 0
        c.gridy = 5
        self.settings.add(self.change_method_to_post_box, c)

        change_method_to_post_lbl = JLabel(
            'Check to convert PUT/DELETE/PATCH method to POST in all requests.'
        )
        c.gridx = 0
        c.gridy = 6
        self.settings.add(change_method_to_post_lbl, c)

        self.change_ct_to_json_box = JCheckBox('Change media type to json',
                                               change_ct_to_json)
        self.change_ct_to_json_box.setFont(Font("Serif", Font.BOLD, 20))
        self.change_ct_to_json_box.setForeground(Color(0, 0, 153))
        c.gridx = 0
        c.gridy = 7
        self.settings.add(self.change_ct_to_json_box, c)

        change_ct_to_json_lbl = JLabel(
            'Check to convert body to json and set Content-Type to application/json in url-encoded requests.'
        )
        c.gridx = 0
        c.gridy = 8
        self.settings.add(change_ct_to_json_lbl, c)

        self.change_ct_to_plain_box = JCheckBox(
            'Change Content-Type to text/plain', change_ct_to_plain)
        self.change_ct_to_plain_box.setFont(Font("Serif", Font.BOLD, 20))
        self.change_ct_to_plain_box.setForeground(Color(0, 0, 153))
        c.gridx = 0
        c.gridy = 9
        self.settings.add(self.change_ct_to_plain_box, c)

        change_ct_to_plain_lbl = JLabel(
            'Check to set Content-Type to text/plain in request with non-simple media type. Simple media types - application/application/x-www-form-urlencoded, text/plain, multipart/form-data.'
        )
        c.gridx = 0
        c.gridy = 10
        self.settings.add(change_ct_to_plain_lbl, c)

        self.change_to_get_box = JCheckBox('Change to GET', change_to_get)
        self.change_to_get_box.setFont(Font("Serif", Font.BOLD, 20))
        self.change_to_get_box.setForeground(Color(0, 0, 153))
        c.gridx = 0
        c.gridy = 11
        self.settings.add(self.change_to_get_box, c)

        change_to_get_lbl = JLabel(
            'Check to convert POST/PUT/DELETE/PATCH url-encoded requests to GET.'
        )
        c.gridx = 0
        c.gridy = 12
        self.settings.add(change_to_get_lbl, c)

        callbacks.customizeUiComponent(self.settings)
        callbacks.addSuiteTab(self)

        callbacks.registerProxyListener(self)

        print "Successfully loaded %s v%s" % (NAME, VERSION)

    def processProxyMessage(self, messageIsRequest,
                            message):  ### IProxyListener
        extension_enable = self.extension_enable_box.isSelected()
        if not extension_enable:
            return  # Do nothing

        remove_csrf_headers = self.remove_csrf_headers_box.isSelected()
        remove_csrf_params = self.remove_csrf_params_box.isSelected()
        change_method_to_post = self.change_method_to_post_box.isSelected()
        change_ct_to_json = self.change_ct_to_json_box.isSelected()
        change_ct_to_plain = self.change_ct_to_plain_box.isSelected()
        change_to_get = self.change_to_get_box.isSelected()

        request_response = message.getMessageInfo()
        request_info = helpers.analyzeRequest(request_response)
        request_method = request_info.getMethod()

        if not messageIsRequest or request_method not in [
                'POST', 'PUT', 'DELETE', 'PATCH'
        ]:
            return  # Do nothing

        http_service = request_response.getHttpService()
        request = request_response.getRequest()
        headers = request_info.getHeaders()
        parameters = request_info.getParameters()

        new_headers = headers
        if remove_csrf_headers:
            new_headers = filter_headers(headers)  # Remove CSRF headers

        if change_ct_to_plain and request_info.getContentType() not in [
                IRequestInfo.CONTENT_TYPE_URL_ENCODED,
                IRequestInfo.CONTENT_TYPE_MULTIPART
        ]:
            for i in range(len(new_headers)):
                if new_headers[i].lower().startswith(
                        'content-type'):  # Change CT to text/plain
                    new_headers[i] = 'Content-Type: text/plain'

        if remove_csrf_params:
            for parameter in parameters:  # Remove CSRF parameters from request's body or URL
                for csrf_param in csrf_params_names:
                    if parameter.getType() != IParameter.PARAM_COOKIE and \
                            csrf_param.lower() in parameter.getName().lower():
                        if request_info.getContentType(
                        ) == IRequestInfo.CONTENT_TYPE_MULTIPART:
                            start = parameter.getNameStart()
                            end = parameter.getNameEnd()

                            request = request[:start] + helpers.stringToBytes(
                                "REPLACEMENT") + request[end:]
                        elif parameter.getType() == IParameter.PARAM_JSON:
                            start = parameter.getNameStart() - 1
                            end = parameter.getValueEnd() + 1
                            request = request[:start] + request[end:]

                            offset = helpers.analyzeRequest(
                                http_service, request).getBodyOffset()
                            body = request[offset:]
                            body = re.sub(",\s*,", ",", body)
                            body = re.sub("{\s*,", "{", body)
                            body = re.sub(",\s*}", "}", body)

                            request = helpers.buildHttpMessage(headers, body)
                        elif parameter.getType() in [
                                IParameter.PARAM_URL, IParameter.PARAM_BODY
                        ]:
                            request = helpers.removeParameter(
                                request, parameter)

        offset = helpers.analyzeRequest(http_service, request).getBodyOffset()
        body = request[offset:]

        if change_ct_to_json and request_info.getContentType(
        ) == IRequestInfo.CONTENT_TYPE_URL_ENCODED:
            for i in range(len(new_headers)):
                if new_headers[i].lower().startswith(
                        'content-type'):  # Change to JSON from URL-encoded
                    new_headers[i] = 'Content-Type: application/json'

            body = safe_bytes_to_string(body)
            d = dict((k, v if len(v) > 1 else v[0])
                     for k, v in parse_qs(body).iteritems())
            body = dumps(d)

        if change_method_to_post or (change_to_get and not change_ct_to_json and \
                                request_info.getContentType() == IRequestInfo.CONTENT_TYPE_URL_ENCODED):
            for i in range(len(new_headers)):
                if new_headers[i].startswith("PUT") or new_headers[i].startswith("DELETE") \
                        or new_headers[i].startswith("PATCH"):
                    new_headers[i] = new_headers[i].replace(
                        request_method, 'POST', 1)
                    break

        new_request = helpers.buildHttpMessage(
            new_headers, body)  # Create new request with valid Content-Length

        if change_to_get and not change_ct_to_json and request_info.getContentType(
        ) == IRequestInfo.CONTENT_TYPE_URL_ENCODED:
            new_request = helpers.toggleRequestMethod(
                new_request)  # Change any URL-encoded request to GET

        message.setInterceptAction(
            IInterceptedProxyMessage.ACTION_FOLLOW_RULES_AND_REHOOK)
        message.getMessageInfo().setRequest(new_request)
        message.getMessageInfo().setHighlight('red')
Пример #2
0
class BurpExtender(IBurpExtender, IExtensionStateListener, IHttpListener, ITab,
                   FocusListener, ActionListener, MouseAdapter):
    _version = "0.2"
    _name = "PyRules"
    _varsStorage = _name + "_vars"
    _scriptStorage = _name + "_script"

    _enabled = 0
    _vars = {}

    def registerExtenderCallbacks(self, callbacks):
        print "Load:" + self._name + " " + self._version

        self.callbacks = callbacks
        self.helpers = callbacks.helpers

        #Create Tab layout
        self.jVarsPane = JTextPane()
        self.jVarsPane.setFont(Font('Monospaced', Font.PLAIN, 11))
        self.jVarsPane.addFocusListener(self)

        self.jMenuPanel = JPanel()
        self.jLeftUpPanel = JPanel()

        self.jEnable = JCheckBox()
        self.jEnable.setFont(Font('Monospaced', Font.BOLD, 11))
        self.jEnable.setForeground(Color(0, 0, 204))
        self.jEnable.setText(self._name)
        self.jEnable.addActionListener(self)

        self.jDocs = JLabel()
        self.jDocs.setFont(Font('Monospaced', Font.PLAIN, 11))
        self.jDocs.setForeground(Color(51, 102, 255))
        self.jDocs.setText(Strings.docs_titel)
        self.jDocs.setToolTipText(Strings.docs_tooltip)
        self.jDocs.addMouseListener(self)

        self.jConsoleText = JTextArea()
        self.jConsoleText.setFont(Font('Monospaced', Font.PLAIN, 10))
        self.jConsoleText.setBackground(Color(244, 246, 247))
        self.jConsoleText.setEditable(0)
        self.jConsoleText.setWrapStyleWord(1)
        self.jConsoleText.setRows(10)
        self.jScrollConsolePane = JScrollPane()
        self.jScrollConsolePane.setViewportView(self.jConsoleText)
        #set initial text
        self.jConsoleText.setText(Strings.console_disable)

        self.jMenuPanelLayout = GroupLayout(self.jMenuPanel)
        self.jMenuPanel.setLayout(self.jMenuPanelLayout)
        self.jMenuPanelLayout.setHorizontalGroup(
            self.jMenuPanelLayout.createParallelGroup(
                GroupLayout.Alignment.LEADING).addGroup(
                    self.jMenuPanelLayout.createSequentialGroup().addComponent(
                        self.jEnable).addPreferredGap(
                            LayoutStyle.ComponentPlacement.RELATED, 205,
                            32767).addComponent(self.jDocs)))

        self.jMenuPanelLayout.setVerticalGroup(
            self.jMenuPanelLayout.createParallelGroup(
                GroupLayout.Alignment.LEADING).addGroup(
                    self.jMenuPanelLayout.createSequentialGroup().addGroup(
                        self.jMenuPanelLayout.createParallelGroup(
                            GroupLayout.Alignment.BASELINE).addComponent(
                                self.jEnable).addComponent(self.jDocs)).addGap(
                                    0, 7, 32767)))

        self.jConsolePane = JPanel()
        self.jConsoleLayout = GroupLayout(self.jConsolePane)
        self.jConsolePane.setLayout(self.jConsoleLayout)
        self.jConsoleLayout.setHorizontalGroup(
            self.jConsoleLayout.createParallelGroup(
                GroupLayout.Alignment.LEADING).addComponent(
                    self.jScrollConsolePane))
        self.jConsoleLayout.setVerticalGroup(
            self.jConsoleLayout.createParallelGroup(
                GroupLayout.Alignment.LEADING).addGroup(
                    GroupLayout.Alignment.TRAILING,
                    self.jConsoleLayout.createSequentialGroup().addComponent(
                        self.jScrollConsolePane, GroupLayout.DEFAULT_SIZE, 154,
                        32767).addContainerGap()))
        self.jLeftUpPanelLayout = GroupLayout(self.jLeftUpPanel)
        self.jLeftUpPanel.setLayout(self.jLeftUpPanelLayout)
        self.jLeftUpPanelLayout.setHorizontalGroup(
            self.jLeftUpPanelLayout.createParallelGroup(
                GroupLayout.Alignment.LEADING).addComponent(
                    self.jConsolePane, GroupLayout.DEFAULT_SIZE,
                    GroupLayout.DEFAULT_SIZE,
                    32767).addComponent(self.jMenuPanel,
                                        GroupLayout.DEFAULT_SIZE,
                                        GroupLayout.DEFAULT_SIZE,
                                        GroupLayout.PREFERRED_SIZE))
        self.jLeftUpPanelLayout.setVerticalGroup(
            self.jLeftUpPanelLayout.
            createParallelGroup(GroupLayout.Alignment.LEADING).addGroup(
                GroupLayout.Alignment.TRAILING,
                self.jLeftUpPanelLayout.createSequentialGroup().addComponent(
                    self.jMenuPanel, GroupLayout.PREFERRED_SIZE,
                    GroupLayout.DEFAULT_SIZE,
                    GroupLayout.PREFERRED_SIZE).addPreferredGap(
                        LayoutStyle.ComponentPlacement.RELATED).addComponent(
                            self.jConsolePane, GroupLayout.DEFAULT_SIZE,
                            GroupLayout.DEFAULT_SIZE, 32767)))

        self.jScrollpaneLeftDown = JScrollPane()
        self.jScrollpaneLeftDown.setViewportView(self.jVarsPane)

        self.jSplitPaneLeft = JSplitPane(JSplitPane.VERTICAL_SPLIT,
                                         self.jLeftUpPanel,
                                         self.jScrollpaneLeftDown)
        self.jSplitPaneLeft.setDividerLocation(300)

        self.jScriptPane = JTextPane()
        self.jScriptPane.setFont(Font('Monospaced', Font.PLAIN, 11))
        self.jScriptPane.addMouseListener(self)

        self.JScrollPaneRight = JScrollPane()
        self.JScrollPaneRight.setViewportView(self.jScriptPane)

        self.jSplitPane = JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
                                     self.jSplitPaneLeft,
                                     self.JScrollPaneRight)
        self.jSplitPane.setDividerLocation(400)

        #Load saved saved settings
        ##Load vars
        vars = callbacks.loadExtensionSetting(self._varsStorage)
        if vars:
            vars = base64.b64decode(vars)
        else:
            # try to load the example
            try:
                with open("examples/Simple-CSRF-vars.py") as fvars:
                    vars = fvars.read()
            # load the default text
            except:
                vars = Strings.vars

        ## initiate the persistant variables
        locals_ = {}
        try:
            exec(vars, {}, locals_)
        except Exception as e:
            print e
        self._vars = locals_

        ## update the vars screen
        self.jVarsPane.document.insertString(self.jVarsPane.document.length,
                                             vars, SimpleAttributeSet())

        ##Load script
        script = callbacks.loadExtensionSetting(self._scriptStorage)
        if script:
            script = base64.b64decode(script)
        else:
            # try to load the example
            try:
                with open("examples/Simple-CSRF-script.py") as fscript:
                    script = fscript.read()
            # load the default text
            except:
                script = Strings.script

        ## compile the rules
        self._script = script
        self._code = ''

        try:
            self._code = compile(script, '<string>', 'exec')
        except Exception as e:
            print(
                '{}\nReload extension after you correct the error.'.format(e))

        ## update the rules screen
        self.jScriptPane.document.insertString(
            self.jScriptPane.document.length, script, SimpleAttributeSet())

        #Register Extension
        callbacks.customizeUiComponent(self.getUiComponent())
        callbacks.addSuiteTab(self)
        callbacks.registerExtensionStateListener(self)
        callbacks.registerHttpListener(self)

        self.jScriptPane.requestFocus()

    def getUiComponent(self):
        return self.jSplitPane

    def getTabCaption(self):
        return self._name

    def actionPerformed(self, event):
        #Check box was clicked
        if self.jEnable == event.getSource():
            if self._enabled == 1:
                self._enabled = 0
                # console content shows help
                self.jConsoleText.setText(Strings.console_disable)
            else:
                self._enabled = 1
                # console content displays the current persistent variable state
                self.jConsoleText.setText(Strings.console_state)
                self.jConsoleText.append(pformat(self._vars))
                self.jConsoleText.append(Strings.extra_line)
                self.jConsoleText.append(Strings.console_log)
        return

    def mouseClicked(self, event):
        if event.source == self.jDocs:
            uri = URI.create("https://github.com/DanNegrea/PyRules")
            if uri and Desktop.isDesktopSupported() and Desktop.getDesktop(
            ).isSupported(Desktop.Action.BROWSE):
                Desktop.getDesktop().browse(uri)
        return

    def focusGained(self, event):

        if self.jConsolePane == event.getSource():
            pass
            #print "Status pane gained focus" #debug
        return

    def focusLost(self, event):
        #Reinitialize the persistent values
        if self.jVarsPane == event.getSource():
            # get the text from the pane
            end = self.jVarsPane.document.length
            vars = self.jVarsPane.document.getText(0, end)

            # compute the new values
            locals_ = {}
            exec(vars, {}, locals_)
            self._vars = locals_

            # display the new result in console
            self.jConsoleText.append(Strings.console_state)
            self.jConsoleText.append(pformat(self._vars))
            self.jConsoleText.append(Strings.extra_line)
            self.jConsoleText.append(Strings.console_log)

            # scroll to bottom
            verticalScrollBar = self.jScrollConsolePane.getVerticalScrollBar()
            verticalScrollBar.setValue(verticalScrollBar.getMaximum())
        return

    def extensionUnloaded(self):
        try:
            #Save the latestest vars and script text
            ## save vars
            end = self.jVarsPane.document.length
            vars = self.jVarsPane.document.getText(0, end)
            vars = base64.b64encode(vars)
            self.callbacks.saveExtensionSetting(self._varsStorage, vars)
            ## save script/rules
            end = self.jScriptPane.document.length
            script = self.jScriptPane.document.getText(0, end)
            script = base64.b64encode(script)
            self.callbacks.saveExtensionSetting(self._scriptStorage, script)
        except Exception:
            traceback.print_exc(file=self.callbacks.getStderr())
        print "Unloaded"  #debug
        return

    def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo):
        if self._enabled == 0:
            return

        try:
            locals_ = {
                'extender': self,
                'callbacks': self.callbacks,
                'helpers': self.helpers,
                'toolFlag': toolFlag,
                'messageIsRequest': messageIsRequest,
                'messageInfo': messageInfo,
                'log': self.log
            }
            # add the _vars as gloval variables
            locals_ = dict(locals_, **self._vars)

            # execute the script/rules
            try:
                exec(self.getCode, {}, locals_)
            # catch exit() call inside the rule
            except SystemExit:
                pass

            # update the persistant variables by searching the local variables with the same name
            for key in self._vars:
                # assumption self._vars dictionary is smaller than locals_
                if key in locals_:
                    self._vars[key] = locals_[key]
        except Exception:
            traceback.print_exc(file=self.callbacks.getStderr())
        return

    #Returns the compiled script
    @property
    def getCode(self):
        end = self.jScriptPane.document.length
        script = self.jScriptPane.document.getText(0, end)

        # if the script hasn't changed return the already compile text
        if script == self._script:
            return self._code
        self._script = script

        # compile, store and return the result
        self._code = compile(script, '<string>', 'exec')
        return self._code

    #Log the information into the console screen
    def log(self, obj):
        # if string just append. else use pformat from pprint
        if isinstance(obj, str):
            self.jConsoleText.append(obj + "\n")
        else:
            self.jConsoleText.append(pformat(obj) + "\n")
        # scroll to bottom
        verticalScrollBar = self.jScrollConsolePane.getVerticalScrollBar()
        verticalScrollBar.setValue(verticalScrollBar.getMaximum())
        return