1231. 2025. 4. 15. 13:56
from burp import IBurpExtender, IHttpListener, ITab
from java.awt import BorderLayout
from javax.swing import JPanel, JTable, JScrollPane, JTextArea, JSplitPane, JButton
from javax.swing.table import DefaultTableModel
from java.util import Date
from java.text import SimpleDateFormat
from java.net import URL

class BurpExtender(IBurpExtender, IHttpListener, ITab):

    def registerExtenderCallbacks(self, callbacks):
        self.callbacks = callbacks
        self.helpers = callbacks.getHelpers()
        self.callbacks.setExtensionName("Dir Index Checker (Jython)")

        self._request_responses = []

        # UI 구성
        self._panel = JPanel(BorderLayout())
        self._table_model = DefaultTableModel(["URL", "Status", "Indexed", "Time"], 0)
        self._table = JTable(self._table_model)
        self._panel.add(JScrollPane(self._table), BorderLayout.CENTER)

        self._request_viewer = JTextArea()
        self._response_viewer = JTextArea()
        self._request_viewer.setEditable(False)
        self._response_viewer.setEditable(False)
        split = JSplitPane(JSplitPane.HORIZONTAL_SPLIT, JScrollPane(self._request_viewer), JScrollPane(self._response_viewer))
        split.setDividerLocation(400)
        self._panel.add(split, BorderLayout.SOUTH)

        self._table.getSelectionModel().addListSelectionListener(lambda e: self.onRowSelect())

        # 스캔 버튼
        self._button = JButton("Scan Site Map", actionPerformed=self.scanSiteMap)
        self._panel.add(self._button, BorderLayout.NORTH)

        # 탭 등록 및 리스너 등록
        callbacks.addSuiteTab(self)
        callbacks.registerHttpListener(self)

    def getTabCaption(self):
        return "Dir Index Checker"

    def getUiComponent(self):
        return self._panel

    def onRowSelect(self):
        row = self._table.getSelectedRow()
        if 0 <= row < len(self._request_responses):
            rr = self._request_responses[row]
            try:
                req = self.helpers.bytesToString(rr.getRequest())
                res = self.helpers.bytesToString(rr.getResponse())
                self._request_viewer.setText(req)
                self._response_viewer.setText(res)
            except:
                self._request_viewer.setText("[Request unavailable]")
                self._response_viewer.setText("[Response unavailable]")

    def scanSiteMap(self, event):
        self._table_model.setRowCount(0)
        self._request_responses = []

        items = self.callbacks.getSiteMap(None)
        seen_urls = set()

        for item in items:
            try:
                req_info = self.helpers.analyzeRequest(item)
                url = req_info.getUrl()

                # 중복 제거
                if url.toString() in seen_urls:
                    continue
                seen_urls.add(url.toString())

                # 디렉토리 경로만 탐지
                if not url.getPath().endswith("/"):
                    continue

                response = item.getResponse()
                if response is None:
                    continue

                res_info = self.helpers.analyzeResponse(response)
                body_offset = res_info.getBodyOffset()
                body = self.helpers.bytesToString(response)[body_offset:].lower()

                # 인덱싱 여부 판별
                indexed = "YES" if "index of" in body or "<a href=" in body else "NO"
                time_str = SimpleDateFormat("HH:mm:ss").format(Date())

                self._table_model.addRow([url.toString(), res_info.getStatusCode(), indexed, time_str])
                self._request_responses.append(item)

            except Exception as e:
                print("Error scanning URL: %s" % e)
                continue

    def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo):
        # 새 요청/응답이 도착하면 자동으로 SiteMap에 반영됨
        # 추가 구현 필요 시 자동 업데이트 로직 삽입 가능
        pass