예제 #1
0
class RequestResponseWidget(QObject):
    def __init__(self,
                 framework,
                 tabwidget,
                 searchControlPlaceholder,
                 parent=None):
        QObject.__init__(self, parent)

        self.framework = framework
        QObject.connect(self, SIGNAL('destroyed(QObject*)'), self._destroyed)

        self.standardPageFactory = StandardPageFactory(self.framework, None,
                                                       self)
        self.headlessPageFactory = HeadlessPageFactory(self.framework, None,
                                                       self)

        self.qlock = QMutex()

        self.scintillaWidgets = set(
        )  # store scintilla widget reference to handle zoom in/zoom out

        self.contentExtractor = self.framework.getContentExtractor()
        self.htmlExtractor = self.contentExtractor.getExtractor('html')
        self.hexDumper = HexDump()

        self.contentTypeMapping = {
            # TODO: complete
            'json': 'javascript',
            'javascript': 'javascript',
            'text/x-js': 'javascript',
            'html': 'html',
            'text/xml': 'xml',
            'text/html': 'html',
            'text/xhtml': 'html',
            'text/css': 'css',
            'text/plain': 'text',
        }
        self.lexerMapping = {
            'text': None,
            'javascript': Qsci.QsciLexerJavaScript,
            'html': Qsci.QsciLexerHTML,
            'xml': Qsci.QsciLexerXML,
            'css': Qsci.QsciLexerCSS,
        }

        self.setup_ui(tabwidget, searchControlPlaceholder)

        self.Data = None
        self.cursor = None
        self.requestResponse = None
        self.framework.subscribe_database_events(self.db_attach,
                                                 self.db_detach)

        self.framework.subscribe_zoom_in(self.zoom_in_scintilla)
        self.framework.subscribe_zoom_out(self.zoom_out_scintilla)

    def _destroyed(self):
        self.framework.unsubscribe_zoom_in(self.zoom_in_scintilla)
        self.framework.unsubscribe_zoom_out(self.zoom_out_scintilla)

    def db_attach(self):
        self.Data = self.framework.getDB()
        self.cursor = self.Data.allocate_thread_cursor()
        self.clear()

    def db_detach(self):
        self.close_cursor()
        self.Data = None
        self.clear()

    def close_cursor(self):
        if self.cursor:
            self.cursor.close()
            self.Data.release_thread_cursor(self.cursor)
            self.cursor = None

    def setup_ui(self, tabwidget, searchControlPlaceholder):

        self.tabwidget = tabwidget
        self.searchControlPlaceholder = searchControlPlaceholder

        self.networkAccessManager = self.framework.getNetworkAccessManager()

        if self.searchControlPlaceholder is not None:
            self.searchLayout = self.searchControlPlaceholder.layout()
            if not self.searchLayout or 0 == self.searchLayout:
                self.searchLayout = QVBoxLayout(self.searchControlPlaceholder)
            self.searchLayout.addWidget(
                self.makeSearchWidget(self.searchControlPlaceholder))
            self.searchLayout.addWidget(
                self.makeConfirmedUpdateWidget(self.searchControlPlaceholder))
            self.searchLayout.setSpacing(0)
            self.searchLayout.setContentsMargins(-1, 0, -1, 0)
            self.searchControlPlaceholder.updateGeometry()

        self.requestView = QWidget(tabwidget)
        self.requestView.setObjectName(tabwidget.objectName() + 'Request')
        self.tabwidget.addTab(self.requestView, 'Request')

        self.responseView = QWidget(tabwidget)
        self.responseView.setObjectName(tabwidget.objectName() + 'Response')
        self.tabwidget.addTab(self.responseView, 'Response')

        self.hexBody = QWidget(tabwidget)
        self.hexBody.setObjectName(tabwidget.objectName() + 'HexBody')
        self.hexBodyIndex = self.tabwidget.addTab(self.hexBody, 'Hex Body')

        self.scriptsView = QWidget(tabwidget)
        self.scriptsView.setObjectName(tabwidget.objectName() + 'Scripts')
        self.scriptsTabIndex = self.tabwidget.addTab(self.scriptsView,
                                                     'Scripts')

        self.commentsView = QWidget(tabwidget)
        self.commentsView.setObjectName(tabwidget.objectName() + 'Comments')
        self.tabwidget.addTab(self.commentsView, 'Comments')

        self.linksView = QWidget(tabwidget)
        self.linksView.setObjectName(tabwidget.objectName() + 'Links')
        self.tabwidget.addTab(self.linksView, 'Links')

        self.formsView = QWidget(tabwidget)
        self.formsView.setObjectName(tabwidget.objectName() + 'Forms')
        self.tabwidget.addTab(self.formsView, 'Forms')

        self.renderView = QWidget(tabwidget)
        self.renderView.setObjectName(tabwidget.objectName() + 'Render')
        self.renderTabIndex = self.tabwidget.addTab(self.renderView, 'Render')
        self.tabwidget.currentChanged.connect(self.handle_tab_currentChanged)

        self.generatedSourceView = QWidget(tabwidget)
        self.generatedSourceView.setObjectName(tabwidget.objectName() +
                                               'GeneratedSource')
        self.generatedSourceTabIndex = self.tabwidget.addTab(
            self.generatedSourceView, 'Generated Source')

        self.notesView = QWidget(tabwidget)
        self.notesView.setObjectName(tabwidget.objectName() + 'Notes')
        self.notesTabIndex = self.tabwidget.addTab(self.notesView, 'Notes')

        self.tab_item_widgets = []

        self.vlayout0 = QVBoxLayout(self.requestView)
        self.requestScintilla = Qsci.QsciScintilla(self.requestView)
        self.setScintillaProperties(self.requestScintilla)
        self.vlayout0.addWidget(self.requestScintilla)
        self.tab_item_widgets.append(self.requestScintilla)

        self.vlayout1 = QVBoxLayout(self.responseView)
        self.responseScintilla = Qsci.QsciScintilla(self.responseView)
        self.responseScintilla.setMarginLineNumbers(1, True)
        self.setScintillaProperties(self.responseScintilla)
        self.vlayout1.addWidget(self.responseScintilla)
        self.tab_item_widgets.append(self.responseScintilla)

        self.vlayout2a = QVBoxLayout(self.hexBody)
        self.hexBodyScintilla = Qsci.QsciScintilla(self.hexBody)
        self.hexBodyScintilla.setFont(self.framework.get_monospace_font())
        self.vlayout2a.addWidget(self.hexBodyScintilla)
        self.tab_item_widgets.append(self.hexBodyScintilla)

        self.vlayout2 = QVBoxLayout(self.scriptsView)
        self.scriptsScintilla = Qsci.QsciScintilla(self.scriptsView)
        #        self.scriptsScintilla.setMarginLineNumbers(1, True)
        self.setScintillaProperties(self.scriptsScintilla, 'javascript')
        self.vlayout2.addWidget(self.scriptsScintilla)
        self.tab_item_widgets.append(self.scriptsScintilla)

        self.vlayout3 = QVBoxLayout(self.commentsView)
        self.commentsScintilla = Qsci.QsciScintilla(self.commentsView)
        #        self.commentsScintilla.setMarginLineNumbers(1, True)
        self.setScintillaProperties(self.commentsScintilla, 'html')
        self.vlayout3.addWidget(self.commentsScintilla)
        self.tab_item_widgets.append(self.commentsScintilla)

        self.vlayout4 = QVBoxLayout(self.linksView)
        self.linksScintilla = Qsci.QsciScintilla(self.linksView)
        self.setScintillaProperties(self.linksScintilla)
        self.vlayout4.addWidget(self.linksScintilla)
        self.tab_item_widgets.append(self.linksScintilla)

        self.vlayout5 = QVBoxLayout(self.formsView)
        self.formsScintilla = Qsci.QsciScintilla(self.formsView)
        self.setScintillaProperties(self.formsScintilla, 'html')
        self.vlayout5.addWidget(self.formsScintilla)
        self.tab_item_widgets.append(self.formsScintilla)

        self.vlayout6 = QVBoxLayout(self.renderView)
        self.renderWebView = RenderingWebView(self.framework,
                                              self.standardPageFactory,
                                              self.renderView)
        self.renderWebView.page().setNetworkAccessManager(
            self.networkAccessManager)
        self.renderWebView.loadFinished.connect(
            self.render_handle_loadFinished)
        self.vlayout6.addWidget(self.renderWebView)
        self.tab_item_widgets.append(self.renderWebView)

        self.vlayout7 = QVBoxLayout(self.generatedSourceView)
        self.generatedSourceScintilla = Qsci.QsciScintilla(
            self.generatedSourceView)
        self.generatedSourceWebView = RenderingWebView(
            self.framework, self.headlessPageFactory, self.generatedSourceView)
        self.generatedSourceWebView.page().setNetworkAccessManager(
            self.networkAccessManager)
        self.generatedSourceWebView.loadFinished.connect(
            self.generatedSource_handle_loadFinished)
        self.generatedSourceWebView.setVisible(False)
        self.generatedSourceScintilla.setMarginLineNumbers(1, True)
        self.setScintillaProperties(self.generatedSourceScintilla, 'html')
        self.vlayout7.addWidget(self.generatedSourceWebView)
        self.vlayout7.addWidget(self.generatedSourceScintilla)
        self.tab_item_widgets.append(self.generatedSourceScintilla)

        self.vlayout8 = QVBoxLayout(self.notesView)
        self.notesTextEdit = QTextEdit(self.notesView)
        self.vlayout8.addWidget(self.notesTextEdit)
        self.tab_item_widgets.append(self.notesTextEdit)

        self.clear()

    def setScintillaProperties(self, scintillaWidget, contentType='text'):
        scintillaWidget.setFont(self.framework.get_font())
        scintillaWidget.setWrapMode(1)
        scintillaWidget.zoomTo(self.framework.get_zoom_size())
        # TOOD: set based on line numbers (size is in pixels)
        scintillaWidget.setMarginWidth(1, '1000')
        self.attachLexer(scintillaWidget, contentType)
        self.scintillaWidgets.add(scintillaWidget)

    def zoom_in_scintilla(self):
        for scintillaWidget in self.scintillaWidgets:
            scintillaWidget.zoomIn()

    def zoom_out_scintilla(self):
        for scintillaWidget in self.scintillaWidgets:
            scintillaWidget.zoomOut()

    def makeSearchWidget(self, parentWidget, tooltip='Search the value'):
        # TODO: these should be store in a class variable list to so that they can be cleared...
        self.searchWidget = QWidget(parentWidget)
        self.searchWidget.setContentsMargins(-1, 0, -1, 0)
        self.search_hlayout = QHBoxLayout(self.searchWidget)
        self.search_label = QLabel(self.searchWidget)
        self.search_label.setText('Search: ')
        self.searchLineEdit = QLineEdit(self.searchWidget)
        self.searchLineEdit.setToolTip(tooltip)
        self.search_hlayout.addWidget(self.search_label)
        self.search_hlayout.addWidget(self.searchLineEdit)
        # always supports regex search
        self.searchReCheckBox = QCheckBox(self.searchWidget)
        self.searchReCheckBox.setText('RE')
        self.searchReCheckBox.setToolTip('Use Regular Expression Syntax')
        self.search_hlayout.addWidget(self.searchReCheckBox)
        self.searchFindButton = QPushButton()
        self.searchFindButton.setText('Find')
        QObject.connect(self.searchFindButton, SIGNAL('clicked()'),
                        self.run_search_find)
        QObject.connect(self.searchLineEdit, SIGNAL('returnPressed()'),
                        self.run_search_find)
        self.search_hlayout.addWidget(self.searchFindButton)
        return self.searchWidget

    def run_search_find(self):
        targetWidget = self.tab_item_widgets[self.tabwidget.currentIndex()]
        if isinstance(targetWidget, Qsci.QsciScintilla):
            self.searchScintilla(targetWidget)
        elif isinstance(targetWidget, QtWebKit.QWebView):
            self.searchWebView(targetWidget)
        else:
            self.searchTextEdit(targetWidget)

    def confirmedButtonStateChanged(self, state):
        # self.emit(SIGNAL('confirmedButtonSet(int)'), state)
        if hasattr(self, 'confirmedCheckBox'):
            self.confirmedCheckBox.setChecked(state)

    def makeConfirmedUpdateWidget(self, parentWidget):
        self.confirmedUpdateWidget = QWidget(parentWidget)
        self.confirmedUpdateWidget.setContentsMargins(-1, 0, -1, 0)
        self.confirmed_hlayout = QHBoxLayout(self.confirmedUpdateWidget)
        self.confirmedCheckBox = QCheckBox(parentWidget)
        self.confirmedCheckBox.setText('Confirmed Vulnerable')
        QObject.connect(self.confirmedCheckBox, SIGNAL('stateChanged(int)'),
                        self.confirmedButtonStateChanged)
        self.quickNotesLabel = QLabel(parentWidget)
        self.quickNotesLabel.setText('Quick Notes: ')
        self.quickNotesEdit = QLineEdit(parentWidget)
        self.confirmed_horizontalSpacer = QSpacerItem(40, 20,
                                                      QSizePolicy.Expanding,
                                                      QSizePolicy.Minimum)
        self.updateButton = QPushButton(parentWidget)
        self.updateButton.setText('Update')
        QObject.connect(self.updateButton, SIGNAL('clicked()'),
                        self.handle_updateButton_clicked)
        self.confirmed_hlayout.addWidget(self.confirmedCheckBox)
        self.confirmed_hlayout.addItem(self.confirmed_horizontalSpacer)
        self.confirmed_hlayout.addWidget(self.quickNotesLabel)
        self.confirmed_hlayout.addWidget(self.quickNotesEdit)
        self.confirmed_hlayout.addWidget(self.updateButton)
        return self.confirmedUpdateWidget

    def handle_updateButton_clicked(self):
        if self.responseId is not None:
            quickNotes = str(self.quickNotesEdit.text()).strip()
            notes = str(self.notesTextEdit.toPlainText())
            confirmed = str(self.confirmedCheckBox.isChecked())
            if len(quickNotes) > 0:
                notes = quickNotes + '\n' + notes
            self.Data.update_responses(self.cursor, notes, '', confirmed,
                                       self.responseId)
            self.quickNotesEdit.setText('')
            self.notesTextEdit.setText(notes)
            # update request response state
            self.requestResponse.confirmed = confirmed
            self.requestResponse.notes = notes
            # TODO: update in datamodel

    def searchTextEdit(self, targetWidget):
        # TODO: simulate regex searching
        searchText = self.searchLineEdit.text()
        return targetWidget.find(searchText)

    def searchScintilla(self, targetWidget):
        searchText = self.searchLineEdit.text()
        line, index = targetWidget.getCursorPosition()
        return targetWidget.findFirst(searchText,
                                      self.searchReCheckBox.isChecked(), False,
                                      False, True, True, line, index)

    def searchWebView(self, targetWidget):
        is_re = self.searchReCheckBox.isChecked()
        searchText = self.searchLineEdit.text()
        # TODO: simulate regex search
        targetWidget.findText(
            '', QtWebKit.QWebPage.FindWrapsAroundDocument
            | QtWebKit.QWebPage.HighlightAllOccurrences)
        return targetWidget.findText(
            searchText, QtWebKit.QWebPage.FindWrapsAroundDocument
            | QtWebKit.QWebPage.HighlightAllOccurrences)

    def viewItemSelected(self, index):
        if index and index.isValid():
            obj = index.internalPointer()
            self.fill(obj.Id)

    def clear(self):
        # clear
        self.responseId = None
        self.requestResponse = None
        self.requestScintilla.setText('')
        self.responseScintilla.setText('')
        self.hexBodyScintilla.setText('')
        self.scriptsScintilla.setText('')
        self.commentsScintilla.setText('')
        self.linksScintilla.setText('')
        self.formsScintilla.setText('')
        self.renderWebView.setHtml('')
        self.generatedSourceWebView.setHtml('')
        self.generatedSourceScintilla.setText('')
        self.contentResults = None
        self.notesTextEdit.setPlainText('')
        self.confirmedButtonStateChanged(Qt.Unchecked)

    def set_search_info(self, searchText, isRE):
        self.searchLineEdit.setText(searchText)
        self.searchReCheckBox.setChecked(isRE)

    def fill(self, Id):
        if self.requestResponse and self.requestResponse.Id == Id:
            # already filled
            return

        if self.qlock.tryLock():
            try:
                self.fill_internal(Id)
            finally:
                self.qlock.unlock()

    def fill_internal(self, Id):

        self.clear()

        if not Id:
            return

        self.responseId = Id
        self.requestResponse = self.framework.get_request_response(Id)
        rr = self.requestResponse

        confirmedState = Qt.Unchecked
        if rr.confirmed and rr.confirmed.lower() in ['y', '1', 'true']:
            confirmedState = Qt.Checked
        self.confirmedButtonStateChanged(confirmedState)

        self.requestScintilla.setText(rr.rawRequest)

        self.attachLexer(self.responseScintilla, rr.responseContentType,
                         rr.responseBody)
        self.responseScintilla.setText(
            ContentHelper.convertBytesToDisplayText(rr.rawResponse))
        self.hexBodyScintilla.setText(self.hexDumper.dump(rr.responseBody))
        self.contentResults = self.generateExtractorResults(
            rr.responseHeaders, rr.responseBody, rr.responseUrl, rr.charset)
        self.notesTextEdit.setText(rr.notes)
        self.handle_tab_currentChanged(self.tabwidget.currentIndex())

    def generateExtractorResults(self, headers, body, url, charset):
        rr = self.requestResponse
        scriptsIO, commentsIO, linksIO, formsIO = StringIO(), StringIO(
        ), StringIO(), StringIO()
        try:
            results = rr.results
            if 'html' == rr.baseType:
                # Create content for parsing HTML
                self.htmlExtractor.process(body, url, charset, results)

                self.tabwidget.setTabText(self.scriptsTabIndex, 'Scripts')
                for script in results.scripts:
                    scriptsIO.write('%s\n\n' % self.flat_str(script))

                self.attachLexer(self.commentsScintilla, 'html')
                for comment in results.comments:
                    commentsIO.write('%s\n\n' % self.flat_str(comment))

                for link in results.links:
                    linksIO.write('%s\n' % self.flat_str(link))

                for form in results.forms:
                    formsIO.write('%s\n' % self.flat_str(form))

                for input in results.other_inputs:
                    formsIO.write('%s\n' % self.flat_str(input))

            elif 'javascript' == rr.baseType:

                self.tabwidget.setTabText(self.scriptsTabIndex, 'Strings')
                for script_string in results.strings:
                    scriptsIO.write('%s\n' % self.flat_str(script_string))

                self.attachLexer(self.commentsScintilla, 'javascript')
                for comment in results.comments:
                    commentsIO.write('%s\n' % self.flat_str(comment))

                for link in results.links:
                    linksIO.write('%s\n' % self.flat_str(link))

                for link in results.relative_links:
                    linksIO.write('%s\n' % self.flat_str(link))

        except Exception as e:
            # TODO: log
            self.framework.report_exception(e)

        self.scriptsScintilla.setText(scriptsIO.getvalue())
        self.commentsScintilla.setText(commentsIO.getvalue())
        self.linksScintilla.setText(linksIO.getvalue())
        self.formsScintilla.setText(formsIO.getvalue())

    def flat_str(self, u):
        if bytes == type(u):
            try:
                s = u.decode('utf-8')
            except UnicodeDecodeError:
                s = repr(u)[2:-1].replace('\\r', '').replace('\\n',
                                                             '\n').replace(
                                                                 '\\t', '\t')

            return s
        else:
            # may be object type implementing str
            s = str(u)
            return s

    def attachLexer(self, scintillaWidget, contentType, data=''):
        lexer = self.getLexer(contentType, data)
        if lexer:
            lexerInstance = lexer(scintillaWidget)
            lexerInstance.setFont(self.framework.get_font())
            scintillaWidget.setLexer(lexerInstance)
        else:
            scintillaWidget.setLexer(None)

    def handle_tab_currentChanged(self, index):
        if index == self.renderTabIndex:
            return self.doRenderApply()
        elif index == self.generatedSourceTabIndex:
            return self.doGeneratedSourceApply()
        return False

    def doRenderApply(self):
        rr = self.requestResponse
        if rr and rr.responseUrl:
            self.renderWebView.fill_from_response(rr.responseUrl,
                                                  rr.responseHeaders,
                                                  rr.responseBody,
                                                  rr.responseContentType)
            return True
        return False

    def doGeneratedSourceApply(self):
        rr = self.requestResponse
        if rr and rr.responseUrl and 'html' == rr.baseType:
            self.generatedSourceWebView.fill_from_response(
                rr.responseUrl, rr.responseHeaders, rr.responseBody,
                rr.responseContentType)
            return True
        return False

    def generatedSource_handle_loadFinished(self):
        self.set_generated_source(self.generatedSourceWebView)

    def render_handle_loadFinished(self):
        self.set_generated_source(self.renderWebView)

    def set_generated_source(self, webview):
        # TODO: consider merging frames sources?
        # TODO: consider other optimizations
        if self.requestResponse:
            rr = self.requestResponse
            xhtml = webview.page().mainFrame().documentElement().toOuterXml()
            self.generatedSourceScintilla.setText(xhtml)
            body_bytes = xhtml.encode('utf-8')
            self.generateExtractorResults(rr.responseHeaders, body_bytes,
                                          rr.responseUrl, rr.charset)

    def getLexer(self, contentType, data):
        lexerContentType = self.inferContentType(contentType, data)
        return self.lexerMapping[lexerContentType]

    def inferContentType(self, contentType, data):
        # TODO: scan data for additional info
        # XXX: data -> bytes
        for comp in list(self.contentTypeMapping.keys()):
            if comp in contentType:
                return self.contentTypeMapping[comp]
        return 'text'

    def set_search(self, tabname, searchText):
        if tabname == 'request':
            self.tabwidget.setCurrentIndex(0)
        elif tabname == 'response':
            self.tabwidget.setCurrentIndex(1)
        self.searchLineEdit.setText(searchText)
        self.requestScintilla.findFirst(searchText, False, True, False, True)
        self.responseScintilla.findFirst(searchText, False, True, False, True)
예제 #2
0
파일: TesterTab.py 프로젝트: Averroes/raft
class TesterTab(QObject):
    class ClickJackingInteractor:
        def __init__(self, parent):
            self.parent = parent

        def log(self, logtype, logmessage):
            self.parent.clickjacking_console_log(logtype, logmessage)

        def confirm(self, frame, msg):
            return self.parent.clickjacking_browser_confirm(frame, msg)

    DEFAULT_FRAMED_URL = "http://attacker.example.com/framed.html"
    DEFAULT_CSRF_URL = "http://attacker.example.com/csrf.html"

    def __init__(self, framework, mainWindow):
        QObject.__init__(self, mainWindow)
        self.framework = framework
        QObject.connect(self, SIGNAL("destroyed(QObject*)"), self._destroyed)
        self.mainWindow = mainWindow
        self.cjInteractor = TesterTab.ClickJackingInteractor(self)
        self.cjTester = ClickjackingTester(self.framework)

        self.scintillaWidgets = set()  # store scintilla widget reference to handle zoom in/zoom out

        #        self.networkAccessManager = StandardNetworkAccessManager(self.framework, self.framework.get_global_cookie_jar())
        self.pageFactory = TesterPageFactory(self.framework, self.cjInteractor, None, self)

        self.framework.subscribe_populate_tester_csrf(self.tester_populate_csrf)
        self.framework.subscribe_populate_tester_click_jacking(self.tester_populate_click_jacking)

        self.Data = None
        self.cursor = None
        self.framework.subscribe_database_events(self.db_attach, self.db_detach)

        self.setScintillaProperties(self.mainWindow.csrfGenEdit)
        self.mainWindow.testerRegenBtn.clicked.connect(self.regen_csrf)
        self.mainWindow.csrfOpenBtn.clicked.connect(self.handle_csrfOpenBtn_clicked)

        self.setScintillaProperties(self.mainWindow.testerClickjackingEditHtml)
        self.mainWindow.testerClickjackingSimulateButton.clicked.connect(
            self.handle_testerClickjackingSimulateButton_clicked
        )
        self.mainWindow.testerClickjackingOpenInBrowserButton.clicked.connect(
            self.handle_testerClickjackingOpenInBrowserButton_clicked
        )
        self.mainWindow.testerClickjackingGenerateButton.clicked.connect(
            self.handle_testerClickjackingGenerateButton_clicked
        )
        self.mainWindow.testerClickjackingEnableJavascript.clicked.connect(
            self.handle_testerClickjackingEnableJavascript_clicked
        )

        self.clickjackingRenderWebView = RenderingWebView(
            self.framework, self.pageFactory, self.mainWindow.testerClickjackingEmbeddedBrowserPlaceholder
        )
        self.clickjackingRenderWebView.loadFinished.connect(self.handle_clickjackingRenderWebView_loadFinished)
        self.clickjackingRenderWebView.urlChanged.connect(self.handle_clickjackingRenderWebView_urlChanged)
        self.clickjackingRenderWebView.titleChanged.connect(self.handle_clickjackingRenderWebView_titleChanged)

        self.framework.subscribe_zoom_in(self.zoom_in_scintilla)
        self.framework.subscribe_zoom_out(self.zoom_out_scintilla)

    def _destroyed(self):
        self.framework.unsubscribe_zoom_in(self.zoom_in_scintilla)
        self.framework.unsubscribe_zoom_out(self.zoom_out_scintilla)

    def db_attach(self):
        self.Data = self.framework.getDB()
        self.cursor = self.Data.allocate_thread_cursor()

    def db_detach(self):
        self.close_cursor()
        self.Data = None

    def close_cursor(self):
        if self.cursor and self.Data:
            self.cursor.close()
            self.Data.release_thread_cursor(self.cursor)
            self.cursor = None

    def tester_populate_csrf(self, response_id):

        row = self.Data.read_responses_by_id(self.cursor, response_id)

        if not row:
            return

        responseItems = interface.data_row_to_response_items(row)

        url = responseItems[ResponsesTable.URL]
        # Are reqHeaders necessary?
        reqHeaders = str(responseItems[ResponsesTable.REQ_HEADERS], "utf-8", "ignore")
        reqData = str(responseItems[ResponsesTable.REQ_DATA], "utf-8", "ignore")  # TODO: consider replacement

        data = reqHeaders + "\n" + reqData

        # Check to ensure that either a GET or a POST is being used and pass that along to the function
        # check = re.compile("^(GET|POST)", re.I)
        # result = check.match(reqHeaders)
        # if not result:
        #    return()

        GET = re.compile("^GET", re.I)
        POST = re.compile("^POST", re.I)

        if GET.match(reqHeaders):
            htmlresult = CSRFTester.generate_csrf_html(url, reqData, "get")
        elif POST.match(reqHeaders):
            htmlresult = CSRFTester.generate_csrf_html(url, reqData, "post")
        else:
            return ()

        # htmlresult = CSRFTester.generate_csrf_html(url, reqData)

        self.mainWindow.testerCSRFURLEdit.setText(url)
        self.mainWindow.csrfGenEdit.setText(htmlresult)
        self.mainWindow.csrfReqEdit.setPlainText(data)

    def regen_csrf(self):
        # Regenerate CSRF playload based on selection
        if self.mainWindow.testerImgGen.isChecked():
            url = self.mainWindow.testerCSRFURLEdit.text()
            htmlresult = CSRFTester.generate_csrf_img(url, self.mainWindow.csrfGenEdit.text())
            self.mainWindow.csrfGenEdit.setText(htmlresult)

    def handle_csrfOpenBtn_clicked(self):
        url = self.DEFAULT_CSRF_URL  # TODO: exposed this (?)
        body = self.mainWindow.csrfGenEdit.text()
        content_type = "text/html"
        self.framework.open_content_in_browser(url, body, content_type)

    def tester_populate_click_jacking(self, response_id):
        row = self.Data.read_responses_by_id(self.cursor, response_id)
        if not row:
            return
        responseItems = interface.data_row_to_response_items(row)
        url = responseItems[ResponsesTable.URL]
        self.setup_clickjacking_url(url)

    def setup_clickjacking_url(self, url):
        self._clickjacking_simulation_running = False
        self.mainWindow.testerClickjackingUrlEdit.setText("")
        self.mainWindow.testerClickjackingConsoleLogTextEdit.setText("")
        self.clickjackingRenderWebView.setHtml("", QUrl("about:blank"))

        htmlcontent = self.cjTester.make_default_frame_html(url)
        self.mainWindow.testerClickjackingTargetURL.setText(url)
        self.mainWindow.testerClickjackingEditHtml.setText(htmlcontent)

    def handle_testerClickjackingSimulateButton_clicked(self):
        self.mainWindow.testerClickjackingConsoleLogTextEdit.setText("Starting Clickjacking Simulation")
        self.clickjackingRenderWebView.page().set_javascript_enabled(
            self.mainWindow.testerClickjackingEnableJavascript.isChecked()
        )
        self._clickjacking_simulation_running = True
        url = self.mainWindow.testerClickjackingUrlEdit.text()
        # TODO: better way than to force unique URL to reload content ?
        if "?" in url:
            url = url[0 : url.find("?") + 1] + uuid.uuid4().hex
        else:
            url = url + "?" + uuid.uuid4().hex
        headers = ""
        body = self.mainWindow.testerClickjackingEditHtml.text()
        content_type = "text/html"
        self.clickjackingRenderWebView.fill_from_response(url, headers, body, content_type)

    def handle_testerClickjackingGenerateButton_clicked(self):
        entry = self.mainWindow.testerClickjackingTargetURL.text()
        url = QUrl.fromUserInput(entry).toEncoded().data().decode("utf-8")
        self.setup_clickjacking_url(url)

    def handle_testerClickjackingOpenInBrowserButton_clicked(self):
        url = self.clickjackingRenderWebView.url().toEncoded().data().decode("utf-8")
        body = self.mainWindow.testerClickjackingEditHtml.text()
        content_type = "text/html"
        self.framework.open_content_in_browser(url, body, content_type)

    def handle_clickjackingRenderWebView_loadFinished(self):
        self.clickjacking_console_log("info", "Page load finished")
        if not self._clickjacking_simulation_running:
            self.mainWindow.testerClickjackingConsoleLogTextEdit.setPlainText("")

    def handle_clickjackingRenderWebView_urlChanged(self):
        url = self.clickjackingRenderWebView.url().toEncoded().data().decode("utf-8")
        if "about:blank" == url and not self._clickjacking_simulation_running:
            self.mainWindow.testerClickjackingUrlEdit.setText(self.DEFAULT_FRAMED_URL)
        else:
            self.mainWindow.testerClickjackingUrlEdit.setText(url)

    def handle_testerClickjackingEnableJavascript_clicked(self):
        self.clickjackingRenderWebView.page().set_javascript_enabled(
            self.mainWindow.testerClickjackingEnableJavascript.isChecked()
        )

    def handle_clickjackingRenderWebView_titleChanged(self, title):
        self.clickjacking_console_log("info", "Page title changed to [%s]" % (title))

    def clickjacking_browser_confirm(self, frame, msg):
        if not self._clickjacking_simulation_running:
            return True
        if "Clickjacking: Navigate Away" == msg:  # TODO: create a common location for this string
            if self.mainWindow.testerClickjackingIgnoreNavigationRequests.isChecked():
                self.clickjacking_console_log(
                    "info", "Simulating canceled navigation response to confirmation: %s" % (msg)
                )
                return False
        return True

    def clickjacking_console_log(self, logtype, logmessage):
        msg = None
        if "javaScriptConsoleMessage" == logtype:
            (lineNumber, sourceID, message) = logmessage
            msg = "console log from [%s / %s]: %s" % (lineNumber, sourceID, message)
        elif "info":
            msg = logmessage

        if msg:
            curtext = self.mainWindow.testerClickjackingConsoleLogTextEdit.toPlainText()
            curtext += "\n" + msg
            self.mainWindow.testerClickjackingConsoleLogTextEdit.setPlainText(curtext)

    # TODO: refactor into common module in framework
    def setScintillaProperties(self, scintillaWidget, contentType="html"):
        scintillaWidget.setFont(self.framework.get_font())
        scintillaWidget.setWrapMode(1)
        scintillaWidget.zoomTo(self.framework.get_zoom_size())
        # TOOD: set based on line numbers (size is in pixels)
        scintillaWidget.setMarginWidth(1, "1000")
        lexerInstance = Qsci.QsciLexerHTML(scintillaWidget)
        lexerInstance.setFont(self.framework.get_font())
        scintillaWidget.setLexer(lexerInstance)
        self.scintillaWidgets.add(scintillaWidget)

    def zoom_in_scintilla(self):
        for scintillaWidget in self.scintillaWidgets:
            scintillaWidget.zoomIn()

    def zoom_out_scintilla(self):
        for scintillaWidget in self.scintillaWidgets:
            scintillaWidget.zoomOut()
예제 #3
0
class RequestResponseWidget(QObject):
    def __init__(self, framework, tabwidget, searchControlPlaceholder, parent = None):
        QObject.__init__(self, parent)

        self.framework = framework
        QObject.connect(self, SIGNAL('destroyed(QObject*)'), self._destroyed)

        self.standardPageFactory = StandardPageFactory(self.framework, None, self)
        self.headlessPageFactory = HeadlessPageFactory(self.framework, None, self)

        self.qlock = QMutex()

        self.scintillaWidgets = set() # store scintilla widget reference to handle zoom in/zoom out

        self.contentExtractor = self.framework.getContentExtractor()
        self.htmlExtractor = self.contentExtractor.getExtractor('html')
        self.hexDumper = HexDump()

        self.contentTypeMapping = {
            # TODO: complete
            'json' : 'javascript',
            'javascript': 'javascript',
            'text/x-js' : 'javascript',
            'html' : 'html',
            'text/xml' : 'xml',
            'text/html' : 'html',
            'text/xhtml' : 'html',
            'text/css' : 'css',
            'text/plain' : 'text',
            }
        self.lexerMapping = {
            'text' : None,
            'javascript' : Qsci.QsciLexerJavaScript,
            'html' : Qsci.QsciLexerHTML,
            'xml' : Qsci.QsciLexerXML,
            'css' : Qsci.QsciLexerCSS,
            }

        self.setup_ui(tabwidget, searchControlPlaceholder)

        self.Data = None
        self.cursor = None
        self.requestResponse = None
        self.framework.subscribe_database_events(self.db_attach, self.db_detach)

        self.framework.subscribe_zoom_in(self.zoom_in_scintilla)
        self.framework.subscribe_zoom_out(self.zoom_out_scintilla)

    def _destroyed(self):
        self.framework.unsubscribe_zoom_in(self.zoom_in_scintilla)
        self.framework.unsubscribe_zoom_out(self.zoom_out_scintilla)

    def db_attach(self):
        self.Data = self.framework.getDB()
        self.cursor = self.Data.allocate_thread_cursor()
        self.clear()

    def db_detach(self):
        self.close_cursor()
        self.Data = None
        self.clear()

    def close_cursor(self):
        if self.cursor:
            self.cursor.close()
            self.Data.release_thread_cursor(self.cursor)
            self.cursor = None

    def setup_ui(self, tabwidget, searchControlPlaceholder):

        self.tabwidget = tabwidget
        self.searchControlPlaceholder = searchControlPlaceholder

        self.networkAccessManager = self.framework.getNetworkAccessManager()

        if self.searchControlPlaceholder is not None:
            self.searchLayout = self.searchControlPlaceholder.layout()
            if not self.searchLayout or 0 == self.searchLayout:
                self.searchLayout = QVBoxLayout(self.searchControlPlaceholder)
            self.searchLayout.addWidget(self.makeSearchWidget(self.searchControlPlaceholder))
            self.searchLayout.addWidget(self.makeConfirmedUpdateWidget(self.searchControlPlaceholder))
            self.searchLayout.setSpacing(0)
            self.searchLayout.setContentsMargins(-1, 0, -1, 0)
            self.searchControlPlaceholder.updateGeometry()

        self.requestView = QWidget(tabwidget)
        self.requestView.setObjectName(tabwidget.objectName()+'Request')
        self.tabwidget.addTab(self.requestView, 'Request')

        self.responseView = QWidget(tabwidget)
        self.responseView.setObjectName(tabwidget.objectName()+'Response')
        self.tabwidget.addTab(self.responseView, 'Response')

        self.hexBody = QWidget(tabwidget)
        self.hexBody.setObjectName(tabwidget.objectName()+'HexBody')
        self.hexBodyIndex = self.tabwidget.addTab(self.hexBody, 'Hex Body')

        self.scriptsView = QWidget(tabwidget)
        self.scriptsView.setObjectName(tabwidget.objectName()+'Scripts')
        self.scriptsTabIndex = self.tabwidget.addTab(self.scriptsView, 'Scripts')

        self.commentsView = QWidget(tabwidget)
        self.commentsView.setObjectName(tabwidget.objectName()+'Comments')
        self.tabwidget.addTab(self.commentsView, 'Comments')

        self.linksView = QWidget(tabwidget)
        self.linksView.setObjectName(tabwidget.objectName()+'Links')
        self.tabwidget.addTab(self.linksView, 'Links')

        self.formsView = QWidget(tabwidget)
        self.formsView.setObjectName(tabwidget.objectName()+'Forms')
        self.tabwidget.addTab(self.formsView, 'Forms')

        self.renderView = QWidget(tabwidget)
        self.renderView.setObjectName(tabwidget.objectName()+'Render')
        self.renderTabIndex = self.tabwidget.addTab(self.renderView, 'Render')
        self.tabwidget.currentChanged.connect(self.handle_tab_currentChanged)

        self.generatedSourceView = QWidget(tabwidget)
        self.generatedSourceView.setObjectName(tabwidget.objectName()+'GeneratedSource')
        self.generatedSourceTabIndex = self.tabwidget.addTab(self.generatedSourceView, 'Generated Source')

        self.notesView = QWidget(tabwidget)
        self.notesView.setObjectName(tabwidget.objectName()+'Notes')
        self.notesTabIndex = self.tabwidget.addTab(self.notesView, 'Notes')

        self.tab_item_widgets = []

        self.vlayout0 = QVBoxLayout(self.requestView)
        self.requestScintilla = Qsci.QsciScintilla(self.requestView)
        self.setScintillaProperties(self.requestScintilla)
        self.vlayout0.addWidget(self.requestScintilla)
        self.tab_item_widgets.append(self.requestScintilla)

        self.vlayout1 = QVBoxLayout(self.responseView)
        self.responseScintilla = Qsci.QsciScintilla(self.responseView)
        self.responseScintilla.setMarginLineNumbers(1, True)
        self.setScintillaProperties(self.responseScintilla)
        self.vlayout1.addWidget(self.responseScintilla)
        self.tab_item_widgets.append(self.responseScintilla)

        self.vlayout2a = QVBoxLayout(self.hexBody)
        self.hexBodyScintilla = Qsci.QsciScintilla(self.hexBody)
        self.hexBodyScintilla.setFont(self.framework.get_monospace_font())
        self.vlayout2a.addWidget(self.hexBodyScintilla)
        self.tab_item_widgets.append(self.hexBodyScintilla)

        self.vlayout2 = QVBoxLayout(self.scriptsView)
        self.scriptsScintilla = Qsci.QsciScintilla(self.scriptsView)
#        self.scriptsScintilla.setMarginLineNumbers(1, True)
        self.setScintillaProperties(self.scriptsScintilla, 'javascript')
        self.vlayout2.addWidget(self.scriptsScintilla)
        self.tab_item_widgets.append(self.scriptsScintilla)

        self.vlayout3 = QVBoxLayout(self.commentsView)
        self.commentsScintilla = Qsci.QsciScintilla(self.commentsView)
#        self.commentsScintilla.setMarginLineNumbers(1, True)
        self.setScintillaProperties(self.commentsScintilla, 'html')
        self.vlayout3.addWidget(self.commentsScintilla)
        self.tab_item_widgets.append(self.commentsScintilla)

        self.vlayout4 = QVBoxLayout(self.linksView)
        self.linksScintilla = Qsci.QsciScintilla(self.linksView)
        self.setScintillaProperties(self.linksScintilla)
        self.vlayout4.addWidget(self.linksScintilla)
        self.tab_item_widgets.append(self.linksScintilla)

        self.vlayout5 = QVBoxLayout(self.formsView)
        self.formsScintilla = Qsci.QsciScintilla(self.formsView)
        self.setScintillaProperties(self.formsScintilla, 'html')
        self.vlayout5.addWidget(self.formsScintilla)
        self.tab_item_widgets.append(self.formsScintilla)

        self.vlayout6 = QVBoxLayout(self.renderView)
        self.renderWebView = RenderingWebView(self.framework, self.standardPageFactory, self.renderView)
        self.renderWebView.page().setNetworkAccessManager(self.networkAccessManager)
        self.renderWebView.loadFinished.connect(self.render_handle_loadFinished)
        self.vlayout6.addWidget(self.renderWebView)
        self.tab_item_widgets.append(self.renderWebView)

        self.vlayout7 = QVBoxLayout(self.generatedSourceView)
        self.generatedSourceScintilla = Qsci.QsciScintilla(self.generatedSourceView)
        self.generatedSourceWebView = RenderingWebView(self.framework, self.headlessPageFactory, self.generatedSourceView)
        self.generatedSourceWebView.page().setNetworkAccessManager(self.networkAccessManager)
        self.generatedSourceWebView.loadFinished.connect(self.generatedSource_handle_loadFinished)
        self.generatedSourceWebView.setVisible(False)
        self.generatedSourceScintilla.setMarginLineNumbers(1, True)
        self.setScintillaProperties(self.generatedSourceScintilla, 'html')
        self.vlayout7.addWidget(self.generatedSourceWebView)
        self.vlayout7.addWidget(self.generatedSourceScintilla)
        self.tab_item_widgets.append(self.generatedSourceScintilla)

        self.vlayout8 = QVBoxLayout(self.notesView)
        self.notesTextEdit = QTextEdit(self.notesView)
        self.vlayout8.addWidget(self.notesTextEdit)
        self.tab_item_widgets.append(self.notesTextEdit)

        self.clear()

    def setScintillaProperties(self, scintillaWidget, contentType = 'text'):
        scintillaWidget.setFont(self.framework.get_font())
        scintillaWidget.setWrapMode(1)
        scintillaWidget.zoomTo(self.framework.get_zoom_size())
        # TOOD: set based on line numbers (size is in pixels)
        scintillaWidget.setMarginWidth(1, '1000')
        self.attachLexer(scintillaWidget, contentType)
        self.scintillaWidgets.add(scintillaWidget)

    def zoom_in_scintilla(self):
        for scintillaWidget in self.scintillaWidgets:
            scintillaWidget.zoomIn()

    def zoom_out_scintilla(self):
        for scintillaWidget in self.scintillaWidgets:
            scintillaWidget.zoomOut()

    def makeSearchWidget(self, parentWidget, tooltip = 'Search the value'):
        # TODO: these should be store in a class variable list to so that they can be cleared...
        self.searchWidget = QWidget(parentWidget)
        self.searchWidget.setContentsMargins(-1, 0, -1, 0)
        self.search_hlayout = QHBoxLayout(self.searchWidget)
        self.search_label = QLabel(self.searchWidget)
        self.search_label.setText('Search: ')
        self.searchLineEdit = QLineEdit(self.searchWidget)
        self.searchLineEdit.setToolTip(tooltip)
        self.search_hlayout.addWidget(self.search_label)
        self.search_hlayout.addWidget(self.searchLineEdit)
        # always supports regex search
        self.searchReCheckBox = QCheckBox(self.searchWidget)
        self.searchReCheckBox.setText('RE')
        self.searchReCheckBox.setToolTip('Use Regular Expression Syntax')
        self.search_hlayout.addWidget(self.searchReCheckBox)
        self.searchFindButton = QPushButton()
        self.searchFindButton.setText('Find')
        QObject.connect(self.searchFindButton, SIGNAL('clicked()'), self.run_search_find)
        QObject.connect(self.searchLineEdit, SIGNAL('returnPressed()'), self.run_search_find)
        self.search_hlayout.addWidget(self.searchFindButton)
        return self.searchWidget

    def run_search_find(self):
        targetWidget = self.tab_item_widgets[self.tabwidget.currentIndex()]
        if isinstance(targetWidget, Qsci.QsciScintilla):
            self.searchScintilla(targetWidget)
        elif isinstance(targetWidget,  QtWebKit.QWebView):
            self.searchWebView(targetWidget)
        else:
            self.searchTextEdit(targetWidget)

    def confirmedButtonStateChanged(self, state):
        # self.emit(SIGNAL('confirmedButtonSet(int)'), state)
        if hasattr(self, 'confirmedCheckBox'):
            self.confirmedCheckBox.setChecked(state)

    def makeConfirmedUpdateWidget(self, parentWidget):
        self.confirmedUpdateWidget = QWidget(parentWidget)
        self.confirmedUpdateWidget.setContentsMargins(-1, 0, -1, 0)
        self.confirmed_hlayout = QHBoxLayout(self.confirmedUpdateWidget)
        self.confirmedCheckBox = QCheckBox(parentWidget)
        self.confirmedCheckBox.setText('Confirmed Vulnerable')
        QObject.connect(self.confirmedCheckBox, SIGNAL('stateChanged(int)'), self.confirmedButtonStateChanged)
        self.quickNotesLabel = QLabel(parentWidget)
        self.quickNotesLabel.setText('Quick Notes: ')
        self.quickNotesEdit = QLineEdit(parentWidget)
        self.confirmed_horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
        self.updateButton = QPushButton(parentWidget)
        self.updateButton.setText('Update')
        QObject.connect(self.updateButton, SIGNAL('clicked()'), self.handle_updateButton_clicked)
        self.confirmed_hlayout.addWidget(self.confirmedCheckBox)
        self.confirmed_hlayout.addItem(self.confirmed_horizontalSpacer)
        self.confirmed_hlayout.addWidget(self.quickNotesLabel)
        self.confirmed_hlayout.addWidget(self.quickNotesEdit)
        self.confirmed_hlayout.addWidget(self.updateButton)
        return self.confirmedUpdateWidget

    def handle_updateButton_clicked(self):
        if self.responseId is not None:
            quickNotes = str(self.quickNotesEdit.text()).strip()
            notes = str(self.notesTextEdit.toPlainText())
            confirmed = str(self.confirmedCheckBox.isChecked())
            if len(quickNotes) > 0:
                notes = quickNotes + '\n' + notes
            self.Data.update_responses(self.cursor, notes, '', confirmed, self.responseId)
            self.quickNotesEdit.setText('')
            self.notesTextEdit.setText(notes)
            # update request response state
            self.requestResponse.confirmed = confirmed
            self.requestResponse.notes = notes
            # TODO: update in datamodel

    def searchTextEdit(self, targetWidget):
        # TODO: simulate regex searching
        searchText = self.searchLineEdit.text()
        return targetWidget.find(searchText)

    def searchScintilla(self, targetWidget):
        searchText = self.searchLineEdit.text()
        line, index = targetWidget.getCursorPosition()
        return targetWidget.findFirst(searchText, self.searchReCheckBox.isChecked(), False, False, True, True, line, index)

    def searchWebView(self, targetWidget):
        is_re = self.searchReCheckBox.isChecked()
        searchText = self.searchLineEdit.text()
        # TODO: simulate regex search
        targetWidget.findText('', QtWebKit.QWebPage.FindWrapsAroundDocument|QtWebKit.QWebPage.HighlightAllOccurrences)
        return targetWidget.findText(searchText, QtWebKit.QWebPage.FindWrapsAroundDocument|QtWebKit.QWebPage.HighlightAllOccurrences)

    def viewItemSelected(self, index):
        if index and index.isValid():
            obj = index.internalPointer()
            self.fill(obj.Id)

    def clear(self):
        # clear
        self.responseId = None
        self.requestResponse = None
        self.requestScintilla.setText('')
        self.responseScintilla.setText('')
        self.hexBodyScintilla.setText('')
        self.scriptsScintilla.setText('')
        self.commentsScintilla.setText('')
        self.linksScintilla.setText('')
        self.formsScintilla.setText('')
        self.renderWebView.setHtml('')
        self.generatedSourceWebView.setHtml('')
        self.generatedSourceScintilla.setText('')
        self.contentResults = None
        self.notesTextEdit.setPlainText('')
        self.confirmedButtonStateChanged(Qt.Unchecked)

    def set_search_info(self, searchText, isRE):
        self.searchLineEdit.setText(searchText)
        self.searchReCheckBox.setChecked(isRE)

    def fill(self, Id):
        if self.requestResponse and self.requestResponse.Id == Id:
            # already filled
            return

        if self.qlock.tryLock():
            try:
                self.fill_internal(Id)
            finally:
                self.qlock.unlock()

    def fill_internal(self, Id):

        self.clear()

        if not Id:
            return

        self.responseId = Id
        self.requestResponse = self.framework.get_request_response(Id)
        rr = self.requestResponse

        confirmedState = Qt.Unchecked
        if rr.confirmed and rr.confirmed.lower() in ['y', '1', 'true']:
            confirmedState = Qt.Checked
        self.confirmedButtonStateChanged(confirmedState)

        self.requestScintilla.setText(rr.rawRequest)

        self.attachLexer(self.responseScintilla, rr.responseContentType, rr.responseBody)
        self.responseScintilla.setText(ContentHelper.convertBytesToDisplayText(rr.rawResponse))
        self.hexBodyScintilla.setText(self.hexDumper.dump(rr.responseBody))
        self.contentResults = self.generateExtractorResults(rr.responseHeaders, rr.responseBody, rr.responseUrl, rr.charset)
        self.notesTextEdit.setText(rr.notes)
        self.handle_tab_currentChanged(self.tabwidget.currentIndex())

    def generateExtractorResults(self, headers, body, url, charset):
        rr = self.requestResponse
        scriptsIO, commentsIO, linksIO, formsIO = StringIO(), StringIO(), StringIO(), StringIO()
        try:
            results = rr.results
            if 'html' == rr.baseType:
                # Create content for parsing HTML
                self.htmlExtractor.process(body, url, charset, results)

                self.tabwidget.setTabText(self.scriptsTabIndex, 'Scripts')
                for script in results.scripts:
                    scriptsIO.write('%s\n\n' % self.flat_str(script))

                self.attachLexer(self.commentsScintilla, 'html')
                for comment in results.comments:
                    commentsIO.write('%s\n\n' % self.flat_str(comment))

                for link in results.links:
                    linksIO.write('%s\n' % self.flat_str(link))

                for form in results.forms:
                    formsIO.write('%s\n' % self.flat_str(form))

                for input in results.other_inputs:
                    formsIO.write('%s\n' % self.flat_str(input))

            elif 'javascript' == rr.baseType:

                self.tabwidget.setTabText(self.scriptsTabIndex, 'Strings')
                for script_string in results.strings:
                    scriptsIO.write('%s\n' % self.flat_str(script_string))

                self.attachLexer(self.commentsScintilla, 'javascript')
                for comment in results.comments:
                    commentsIO.write('%s\n' % self.flat_str(comment))

                for link in results.links:
                    linksIO.write('%s\n' % self.flat_str(link))

                for link in results.relative_links:
                    linksIO.write('%s\n' % self.flat_str(link))

        except Exception as e:
            # TODO: log 
            self.framework.report_exception(e)

        self.scriptsScintilla.setText(scriptsIO.getvalue())
        self.commentsScintilla.setText(commentsIO.getvalue())
        self.linksScintilla.setText(linksIO.getvalue())
        self.formsScintilla.setText(formsIO.getvalue())

    def flat_str(self, u):
        if bytes == type(u):
            try:
                s = u.decode('utf-8')
            except UnicodeDecodeError:
                s = repr(u)[2:-1].replace('\\r', '').replace('\\n', '\n').replace('\\t', '\t')

            return s
        else:
            # may be object type implementing str
            s = str(u)
            return s

    def attachLexer(self, scintillaWidget, contentType, data = ''):
        lexer = self.getLexer(contentType, data)
        if lexer:
            lexerInstance = lexer(scintillaWidget)
            lexerInstance.setFont(self.framework.get_font())
            scintillaWidget.setLexer(lexerInstance)
        else:
            scintillaWidget.setLexer(None)

    def handle_tab_currentChanged(self, index):
        if index == self.renderTabIndex:
            return self.doRenderApply()
        elif index == self.generatedSourceTabIndex:
            return self.doGeneratedSourceApply()
        return False
        
    def doRenderApply(self):
        rr = self.requestResponse
        if rr and rr.responseUrl:
            self.renderWebView.fill_from_response(rr.responseUrl, rr.responseHeaders, rr.responseBody, rr.responseContentType)
            return True
        return False

    def doGeneratedSourceApply(self):
        rr = self.requestResponse
        if rr and rr.responseUrl and 'html' == rr.baseType:
            self.generatedSourceWebView.fill_from_response(rr.responseUrl, rr.responseHeaders, rr.responseBody, rr.responseContentType)
            return True
        return False

    def generatedSource_handle_loadFinished(self):
        self.set_generated_source(self.generatedSourceWebView)

    def render_handle_loadFinished(self):
        self.set_generated_source(self.renderWebView)

    def set_generated_source(self, webview):
        # TODO: consider merging frames sources?
        # TODO: consider other optimizations
        if self.requestResponse:
            rr = self.requestResponse
            xhtml = webview.page().mainFrame().documentElement().toOuterXml()
            self.generatedSourceScintilla.setText(xhtml)
            body_bytes = xhtml.encode('utf-8')
            self.generateExtractorResults(rr.responseHeaders, body_bytes, rr.responseUrl, rr.charset)

    def getLexer(self, contentType, data):
        lexerContentType = self.inferContentType(contentType, data)
        return self.lexerMapping[lexerContentType]
        
    def inferContentType(self, contentType, data):
        # TODO: scan data for additional info
        # XXX: data -> bytes
        for comp in list(self.contentTypeMapping.keys()):
            if comp in contentType:
                return self.contentTypeMapping[comp]
        return 'text'
            
    def set_search(self, tabname, searchText):
        if tabname == 'request':
            self.tabwidget.setCurrentIndex(0)
        elif tabname=='response':
            self.tabwidget.setCurrentIndex(1)
        self.searchLineEdit.setText(searchText)
        self.requestScintilla.findFirst(searchText, False, True, False, True)
        self.responseScintilla.findFirst(searchText, False, True, False, True)
예제 #4
0
class MiniResponseRenderWidget(QObject):
    def __init__(self, framework, tabWidget, showRequest, parent = None):
        QObject.__init__(self, parent)
        self.framework = framework
        QObject.connect(self, SIGNAL('destroyed(QObject*)'), self._destroyed)
        self.tabWidget = tabWidget
        self.showRequest = showRequest

        if self.showRequest:
            self.reqReqEdit_Tab = QWidget(self.tabWidget)
            self.tabWidget.addTab(self.reqReqEdit_Tab, 'Request')
            # TODO: must this hard-coded ?
            self.render_tab_index = 2
        else:
            self.render_tab_index = 1

        self.reqResEdit_Tab = QWidget(self.tabWidget)
        self.tabWidget.addTab(self.reqResEdit_Tab, 'Response')

        self.reqRenderView_Tab = QWidget(self.tabWidget)
        self.tabWidget.addTab(self.reqRenderView_Tab, 'Render')

        # TODO: a common utility method should be used to all scintilla stuff
        if self.showRequest:
            self.reqReqEdit_Layout = QVBoxLayout(self.reqReqEdit_Tab)
            self.reqReqEdit = Qsci.QsciScintilla(self.reqReqEdit_Tab)
            self.reqReqEdit.zoomTo(self.framework.get_zoom_size())
            self.reqReqEdit.setMarginLineNumbers(1, True)
            self.reqReqEdit.setMarginWidth(1, '1000')
            self.reqReqEdit.setWrapMode(1)
            self.reqReqEdit.setWrapVisualFlags(2, 1, 0)
            self.reqReqEdit_Layout.addWidget(self.reqReqEdit)

        self.reqResEdit_Layout = QVBoxLayout(self.reqResEdit_Tab)
        self.reqResEdit = Qsci.QsciScintilla(self.reqResEdit_Tab)
        self.reqResEdit.zoomTo(self.framework.get_zoom_size())
        self.reqResEdit.setMarginLineNumbers(1, True)
        self.reqResEdit.setMarginWidth(1, '1000')
        self.reqResEdit.setWrapMode(1)
        self.reqResEdit.setWrapVisualFlags(2, 1, 0)
        self.reqResEdit_Layout.addWidget(self.reqResEdit)

        self.reqRenderView_Layout = QVBoxLayout(self.reqRenderView_Tab)
        self.requesterPageFactory = StandardPageFactory(self.framework, None, self)
        self.reqRenderView = RenderingWebView(self.framework, self.requesterPageFactory, self.tabWidget)
        self.reqRenderView_Layout.addWidget(self.reqRenderView)

        self.request_url = None

        self.tabWidget.currentChanged.connect(self.do_render_apply)

        self.framework.subscribe_zoom_in(self.zoom_in_scintilla)
        self.framework.subscribe_zoom_out(self.zoom_out_scintilla)

    def _destroyed(self):
        self.framework.unsubscribe_zoom_in(self.zoom_in_scintilla)
        self.framework.unsubscribe_zoom_out(self.zoom_out_scintilla)

    def fill_from_response(self, url, headers, body, content_type = ''):
        self.reqRenderView.fill_from_response(url, headers, body, content_type)

    def populate_response_content(self, url, req_headers, req_body, res_headers, res_body, res_content_type = ''):
            
        self.request_url = url
        self.request_headers = req_headers
        self.request_body = req_body
        self.response_headers = res_headers
        self.response_body = res_body
        self.response_content_type = res_content_type

        if self.showRequest:
            self.reqReqEdit.setText(ContentHelper.getCombinedText(self.request_headers, self.request_body, ''))

        # TODO: should support different lexers based on content type
        lexerInstance = Qsci.QsciLexerHTML(self.reqResEdit)
        lexerInstance.setFont(self.framework.get_font())
        self.reqResEdit.setLexer(lexerInstance)
        # TODO: should verify trailing newlines?
        self.reqResEdit.setText(ContentHelper.getCombinedText(self.response_headers, self.response_body, self.response_content_type))

        self.do_render_apply(self.tabWidget.currentIndex())

    def do_render_apply(self, index):
        if self.render_tab_index == index:
            if self.request_url:
                self.fill_from_response(self.request_url, self.response_headers, self.response_body, self.response_content_type)

    def clear_response_render(self):
        if self.showRequest:
            self.reqReqEdit.setText('')
        self.reqResEdit.setText('')
        self.reqRenderView.setHtml('', QUrl('about:blank'))
        self.request_url = ''
        self.request_headers = b''
        self.request_body = b''
        self.response_headers = b''
        self.response_body = b''
        self.response_content_type = ''
        

    def zoom_in_scintilla(self):
        if self.showRequest:
            self.reqReqEdit.zoomIn()
        self.reqResEdit.zoomIn()

    def zoom_out_scintilla(self):
        if self.showRequest:
            self.reqReqEdit.zoomOut()
        self.reqResEdit.zoomOut()
예제 #5
0
class TesterTab(QObject):
    class ClickJackingInteractor():
        def __init__(self, parent):
            self.parent = parent

        def log(self, logtype, logmessage):
            self.parent.clickjacking_console_log(logtype, logmessage)

        def confirm(self, frame, msg):
            return self.parent.clickjacking_browser_confirm(frame, msg)

    DEFAULT_FRAMED_URL = 'http://attacker.example.com/framed.html'
    DEFAULT_CSRF_URL = 'http://attacker.example.com/csrf.html'

    def __init__(self, framework, mainWindow):
        QObject.__init__(self, mainWindow)
        self.framework = framework
        QObject.connect(self, SIGNAL('destroyed(QObject*)'), self._destroyed)
        self.mainWindow = mainWindow
        self.cjInteractor = TesterTab.ClickJackingInteractor(self)
        self.cjTester = ClickjackingTester(self.framework)

        self.scintillaWidgets = set(
        )  # store scintilla widget reference to handle zoom in/zoom out

        #        self.networkAccessManager = StandardNetworkAccessManager(self.framework, self.framework.get_global_cookie_jar())
        self.pageFactory = TesterPageFactory(self.framework, self.cjInteractor,
                                             None, self)

        self.framework.subscribe_populate_tester_csrf(
            self.tester_populate_csrf)
        self.framework.subscribe_populate_tester_click_jacking(
            self.tester_populate_click_jacking)

        self.Data = None
        self.cursor = None
        self.framework.subscribe_database_events(self.db_attach,
                                                 self.db_detach)

        self.setScintillaProperties(self.mainWindow.csrfGenEdit)
        self.mainWindow.testerRegenBtn.clicked.connect(self.regen_csrf)
        self.mainWindow.csrfOpenBtn.clicked.connect(
            self.handle_csrfOpenBtn_clicked)

        self.setScintillaProperties(self.mainWindow.testerClickjackingEditHtml)
        self.mainWindow.testerClickjackingSimulateButton.clicked.connect(
            self.handle_testerClickjackingSimulateButton_clicked)
        self.mainWindow.testerClickjackingOpenInBrowserButton.clicked.connect(
            self.handle_testerClickjackingOpenInBrowserButton_clicked)
        self.mainWindow.testerClickjackingGenerateButton.clicked.connect(
            self.handle_testerClickjackingGenerateButton_clicked)
        self.mainWindow.testerClickjackingEnableJavascript.clicked.connect(
            self.handle_testerClickjackingEnableJavascript_clicked)

        self.clickjackingRenderWebView = RenderingWebView(
            self.framework, self.pageFactory,
            self.mainWindow.testerClickjackingEmbeddedBrowserPlaceholder)
        self.clickjackingRenderWebView.loadFinished.connect(
            self.handle_clickjackingRenderWebView_loadFinished)
        self.clickjackingRenderWebView.urlChanged.connect(
            self.handle_clickjackingRenderWebView_urlChanged)
        self.clickjackingRenderWebView.titleChanged.connect(
            self.handle_clickjackingRenderWebView_titleChanged)

        self.framework.subscribe_zoom_in(self.zoom_in_scintilla)
        self.framework.subscribe_zoom_out(self.zoom_out_scintilla)

    def _destroyed(self):
        self.framework.unsubscribe_zoom_in(self.zoom_in_scintilla)
        self.framework.unsubscribe_zoom_out(self.zoom_out_scintilla)

    def db_attach(self):
        self.Data = self.framework.getDB()
        self.cursor = self.Data.allocate_thread_cursor()

    def db_detach(self):
        self.close_cursor()
        self.Data = None

    def close_cursor(self):
        if self.cursor and self.Data:
            self.cursor.close()
            self.Data.release_thread_cursor(self.cursor)
            self.cursor = None

    def tester_populate_csrf(self, response_id):

        row = self.Data.read_responses_by_id(self.cursor, response_id)

        if not row:
            return

        responseItems = interface.data_row_to_response_items(row)

        url = responseItems[ResponsesTable.URL]
        # Are reqHeaders necessary?
        reqHeaders = str(responseItems[ResponsesTable.REQ_HEADERS], 'utf-8',
                         'ignore')
        reqData = str(responseItems[ResponsesTable.REQ_DATA], 'utf-8',
                      'ignore')  # TODO: consider replacement

        data = reqHeaders + "\n" + reqData

        # Check to ensure that either a GET or a POST is being used and pass that along to the function
        # check = re.compile("^(GET|POST)", re.I)
        # result = check.match(reqHeaders)
        # if not result:
        #    return()

        GET = re.compile("^GET", re.I)
        POST = re.compile("^POST", re.I)

        if GET.match(reqHeaders):
            htmlresult = CSRFTester.generate_csrf_html(url, reqData, "get")
        elif POST.match(reqHeaders):
            htmlresult = CSRFTester.generate_csrf_html(url, reqData, "post")
        else:
            return ()

        # htmlresult = CSRFTester.generate_csrf_html(url, reqData)

        self.mainWindow.testerCSRFURLEdit.setText(url)
        self.mainWindow.csrfGenEdit.setText(htmlresult)
        self.mainWindow.csrfReqEdit.setPlainText(data)

    def regen_csrf(self):
        # Regenerate CSRF playload based on selection
        if self.mainWindow.testerImgGen.isChecked():
            url = self.mainWindow.testerCSRFURLEdit.text()
            htmlresult = CSRFTester.generate_csrf_img(
                url, self.mainWindow.csrfGenEdit.text())
            self.mainWindow.csrfGenEdit.setText(htmlresult)

    def handle_csrfOpenBtn_clicked(self):
        url = self.DEFAULT_CSRF_URL  # TODO: exposed this (?)
        body = self.mainWindow.csrfGenEdit.text()
        content_type = 'text/html'
        self.framework.open_content_in_browser(url, body, content_type)

    def tester_populate_click_jacking(self, response_id):
        row = self.Data.read_responses_by_id(self.cursor, response_id)
        if not row:
            return
        responseItems = interface.data_row_to_response_items(row)
        url = responseItems[ResponsesTable.URL]
        self.setup_clickjacking_url(url)

    def setup_clickjacking_url(self, url):
        self._clickjacking_simulation_running = False
        self.mainWindow.testerClickjackingUrlEdit.setText('')
        self.mainWindow.testerClickjackingConsoleLogTextEdit.setText('')
        self.clickjackingRenderWebView.setHtml('', QUrl('about:blank'))

        htmlcontent = self.cjTester.make_default_frame_html(url)
        self.mainWindow.testerClickjackingTargetURL.setText(url)
        self.mainWindow.testerClickjackingEditHtml.setText(htmlcontent)

    def handle_testerClickjackingSimulateButton_clicked(self):
        self.mainWindow.testerClickjackingConsoleLogTextEdit.setText(
            'Starting Clickjacking Simulation')
        self.clickjackingRenderWebView.page().set_javascript_enabled(
            self.mainWindow.testerClickjackingEnableJavascript.isChecked())
        self._clickjacking_simulation_running = True
        url = self.mainWindow.testerClickjackingUrlEdit.text()
        # TODO: better way than to force unique URL to reload content ?
        if '?' in url:
            url = url[0:url.find('?') + 1] + uuid.uuid4().hex
        else:
            url = url + '?' + uuid.uuid4().hex
        headers = ''
        body = self.mainWindow.testerClickjackingEditHtml.text()
        content_type = 'text/html'
        self.clickjackingRenderWebView.fill_from_response(
            url, headers, body, content_type)

    def handle_testerClickjackingGenerateButton_clicked(self):
        entry = self.mainWindow.testerClickjackingTargetURL.text()
        url = QUrl.fromUserInput(entry).toEncoded().data().decode('utf-8')
        self.setup_clickjacking_url(url)

    def handle_testerClickjackingOpenInBrowserButton_clicked(self):
        url = self.clickjackingRenderWebView.url().toEncoded().data().decode(
            'utf-8')
        body = self.mainWindow.testerClickjackingEditHtml.text()
        content_type = 'text/html'
        self.framework.open_content_in_browser(url, body, content_type)

    def handle_clickjackingRenderWebView_loadFinished(self):
        self.clickjacking_console_log('info', 'Page load finished')
        if not self._clickjacking_simulation_running:
            self.mainWindow.testerClickjackingConsoleLogTextEdit.setPlainText(
                '')

    def handle_clickjackingRenderWebView_urlChanged(self):
        url = self.clickjackingRenderWebView.url().toEncoded().data().decode(
            'utf-8')
        if 'about:blank' == url and not self._clickjacking_simulation_running:
            self.mainWindow.testerClickjackingUrlEdit.setText(
                self.DEFAULT_FRAMED_URL)
        else:
            self.mainWindow.testerClickjackingUrlEdit.setText(url)

    def handle_testerClickjackingEnableJavascript_clicked(self):
        self.clickjackingRenderWebView.page().set_javascript_enabled(
            self.mainWindow.testerClickjackingEnableJavascript.isChecked())

    def handle_clickjackingRenderWebView_titleChanged(self, title):
        self.clickjacking_console_log('info',
                                      'Page title changed to [%s]' % (title))

    def clickjacking_browser_confirm(self, frame, msg):
        if not self._clickjacking_simulation_running:
            return True
        if 'Clickjacking: Navigate Away' == msg:  # TODO: create a common location for this string
            if self.mainWindow.testerClickjackingIgnoreNavigationRequests.isChecked(
            ):
                self.clickjacking_console_log(
                    'info',
                    'Simulating canceled navigation response to confirmation: %s'
                    % (msg))
                return False
        return True

    def clickjacking_console_log(self, logtype, logmessage):
        msg = None
        if 'javaScriptConsoleMessage' == logtype:
            (lineNumber, sourceID, message) = logmessage
            msg = 'console log from [%s / %s]: %s' % (lineNumber, sourceID,
                                                      message)
        elif 'info':
            msg = logmessage

        if msg:
            curtext = self.mainWindow.testerClickjackingConsoleLogTextEdit.toPlainText(
            )
            curtext += '\n' + msg
            self.mainWindow.testerClickjackingConsoleLogTextEdit.setPlainText(
                curtext)

    # TODO: refactor into common module in framework
    def setScintillaProperties(self, scintillaWidget, contentType='html'):
        scintillaWidget.setFont(self.framework.get_font())
        scintillaWidget.setWrapMode(1)
        scintillaWidget.zoomTo(self.framework.get_zoom_size())
        # TOOD: set based on line numbers (size is in pixels)
        scintillaWidget.setMarginWidth(1, '1000')
        lexerInstance = Qsci.QsciLexerHTML(scintillaWidget)
        lexerInstance.setFont(self.framework.get_font())
        scintillaWidget.setLexer(lexerInstance)
        self.scintillaWidgets.add(scintillaWidget)

    def zoom_in_scintilla(self):
        for scintillaWidget in self.scintillaWidgets:
            scintillaWidget.zoomIn()

    def zoom_out_scintilla(self):
        for scintillaWidget in self.scintillaWidgets:
            scintillaWidget.zoomOut()