카테고리 없음

1231. 2025. 3. 27. 09:55
# -*- coding: utf-8 -*-
from burp import IBurpExtender, IHttpListener, ITab
from javax.swing import (JPanel, JButton, JTextField, JLabel, JComboBox,
                         JTable, JScrollPane, JTextArea, JSplitPane,
                         BoxLayout, ListCellRenderer, JTree, JTabbedPane)
from javax.swing.table import DefaultTableModel
from javax.swing.tree import DefaultMutableTreeNode, DefaultTreeModel
from javax.swing.border import EmptyBorder, TitledBorder
from java.awt import (BorderLayout, Dimension, Insets, Color, EventQueue,
                      Font)
from java.awt.event import ItemEvent, ItemListener, MouseAdapter
import re

# MouseAdapter subclass for tree click events
class TreeClickListener(MouseAdapter):
    def __init__(self, outer):
        self.outer = outer
    def mouseClicked(self, event):
        self.outer.on_discovered_tree_click(event)

class HostSelectionListener(ItemListener):
    def __init__(self, outer):
        self.outer = outer
    def itemStateChanged(self, event):
        if event.getStateChange() == ItemEvent.SELECTED:
            self.outer.refresh_info(None)

class ColorCellRenderer(JLabel, ListCellRenderer):
    def getListCellRendererComponent(self, list, value, index, isSelected, cellHasFocus):
        self.setText(value)
        self.setOpaque(True)
        self.setBackground(Color.white if not isSelected else Color.lightGray)
        color_map = {
            "red": Color.red, "orange": Color.orange, "yellow": Color.yellow,
            "green": Color.green, "cyan": Color.cyan, "blue": Color.blue,
            "pink": Color.pink, "magenta": Color.magenta, "gray": Color.gray
        }
        self.setForeground(color_map.get(value, Color.black))
        return self

class BurpExtender(IBurpExtender, IHttpListener, ITab):

    def registerExtenderCallbacks(self, callbacks):
        self.callbacks = callbacks
        self.helpers = callbacks.getHelpers()
        callbacks.setExtensionName("7illight")
        callbacks.registerHttpListener(self)

        self.cached_keywords = []
        self.discovered_map = {}

        self.init_gui()
        callbacks.addSuiteTab(self)
        print("[+] 7illight extension with JS URL discovery loaded.")

    def init_gui(self):
        self.tabbed_panel = JTabbedPane()

        # Discovered URLs Tree
        self.disc_root_node = DefaultMutableTreeNode("Discovered JS URLs")
        self.disc_tree_model = DefaultTreeModel(self.disc_root_node)
        self.disc_tree = JTree(self.disc_tree_model)
        self.disc_tree.setRootVisible(True)
        self.disc_tree.addMouseListener(TreeClickListener(self))
        disc_scroll = JScrollPane(self.disc_tree)
        disc_scroll.setPreferredSize(Dimension(350, 500))
        disc_scroll.setBorder(TitledBorder("Discovered from JavaScript"))

        # Highlight Input (Horizontal layout)
        self.scope_selector = JComboBox(["Both", "Request", "Response"])
        self.color_selector = JComboBox(["red", "orange", "yellow", "green", "cyan", "blue", "pink", "magenta", "gray"])
        self.color_selector.setRenderer(ColorCellRenderer())
        self.keyword_field = JTextField(15)
        self.color_preview = JLabel("      ")
        self.color_preview.setOpaque(True)
        self.color_preview.setBackground(Color.red)

        def update_color_preview(event):
            color_map = {
                "red": Color.red, "orange": Color.orange, "yellow": Color.yellow,
                "green": Color.green, "cyan": Color.cyan, "blue": Color.blue,
                "pink": Color.pink, "magenta": Color.magenta, "gray": Color.gray
            }
            selected = self.color_selector.getSelectedItem()
            self.color_preview.setBackground(color_map.get(selected, Color.black))
        self.color_selector.addItemListener(update_color_preview)

        self.add_button = JButton("Add", actionPerformed=self.add_keyword)
        self.update_button = JButton("Update", actionPerformed=self.update_keyword)
        self.delete_button = JButton("Delete", actionPerformed=self.delete_keyword)

        input_row = JPanel()
        input_row.setLayout(BoxLayout(input_row, BoxLayout.X_AXIS))
        input_row.add(JLabel("Scope:"))
        input_row.add(self.scope_selector)
        input_row.add(JLabel("Color:"))
        input_row.add(self.color_selector)
        input_row.add(self.color_preview)
        input_row.add(JLabel("Keyword:"))
        input_row.add(self.keyword_field)
        input_row.add(self.add_button)
        input_row.add(self.update_button)
        input_row.add(self.delete_button)

        self.table_model = DefaultTableModel(["Scope", "Color", "Keyword"], 0)
        self.table = JTable(self.table_model)
        self.table.getSelectionModel().addListSelectionListener(lambda e: self.load_selected_row())
        keyword_scroll = JScrollPane(self.table)
        keyword_scroll.setPreferredSize(Dimension(500, 150))

        keyword_panel = JPanel(BorderLayout())
        keyword_panel.setBorder(TitledBorder("Keyword Highlighting"))
        keyword_panel.add(input_row, BorderLayout.NORTH)
        keyword_panel.add(keyword_scroll, BorderLayout.CENTER)

        # Info Panel
        self.info_area = JTextArea(10, 50)
        self.info_area.setEditable(False)
        self.info_area.setMargin(Insets(6, 6, 6, 6))
        self.info_area.setFont(Font("Monospaced", Font.PLAIN, 12))
        self.info_area.setBackground(Color(245, 245, 245))
        info_scroll = JScrollPane(self.info_area)
        info_scroll.setPreferredSize(Dimension(500, 150))

        info_panel = JPanel()
        info_panel.setLayout(BoxLayout(info_panel, BoxLayout.Y_AXIS))
        info_panel.setBorder(TitledBorder("Info (Selected URL)"))
        info_panel.add(info_scroll)

        right_panel = JPanel()
        right_panel.setLayout(BoxLayout(right_panel, BoxLayout.Y_AXIS))
        right_panel.add(keyword_panel)
        right_panel.add(info_panel)

        self.split_discovered = JSplitPane(JSplitPane.HORIZONTAL_SPLIT, disc_scroll, right_panel)
        self.split_discovered.setResizeWeight(0.35)
        self.tabbed_panel.addTab("Discovered URLs", self.split_discovered)

    def getTabCaption(self):
        return "7illight"

    def getUiComponent(self):
        return self.tabbed_panel

    def load_selected_row(self):
        row = self.table.getSelectedRow()
        if row != -1:
            self.scope_selector.setSelectedItem(self.table_model.getValueAt(row, 0))
            self.color_selector.setSelectedItem(self.table_model.getValueAt(row, 1))
            self.keyword_field.setText(self.table_model.getValueAt(row, 2))

    def add_keyword(self, event):
        scope = self.scope_selector.getSelectedItem()
        color = self.color_selector.getSelectedItem()
        keyword = self.keyword_field.getText().strip().lower()
        if keyword:
            for row in range(self.table_model.getRowCount()):
                if (self.table_model.getValueAt(row, 0) == scope and
                    self.table_model.getValueAt(row, 1) == color and
                    self.table_model.getValueAt(row, 2).lower() == keyword):
                    return
            self.table_model.addRow([scope, color, keyword])
            self.cached_keywords.append((scope, color.lower(), keyword))

    def update_keyword(self, event):
        row = self.table.getSelectedRow()
        if row != -1:
            old = (self.table_model.getValueAt(row, 0),
                   self.table_model.getValueAt(row, 1).lower(),
                   self.table_model.getValueAt(row, 2).lower())
            new_scope = self.scope_selector.getSelectedItem()
            new_color = self.color_selector.getSelectedItem()
            new_keyword = self.keyword_field.getText().strip().lower()
            self.table_model.setValueAt(new_scope, row, 0)
            self.table_model.setValueAt(new_color, row, 1)
            self.table_model.setValueAt(new_keyword, row, 2)
            self.cached_keywords = [kw for kw in self.cached_keywords if kw != old]
            self.cached_keywords.append((new_scope, new_color.lower(), new_keyword))

    def delete_keyword(self, event):
        row = self.table.getSelectedRow()
        if row != -1:
            scope = self.table_model.getValueAt(row, 0)
            color = self.table_model.getValueAt(row, 1).lower()
            keyword = self.table_model.getValueAt(row, 2).lower()
            self.cached_keywords = [kw for kw in self.cached_keywords if kw != (scope, color, keyword)]
            self.table_model.removeRow(row)

    def on_discovered_tree_click(self, event):
        sel_row = self.disc_tree.getClosestRowForLocation(event.getX(), event.getY())
        self.disc_tree.setSelectionRow(sel_row)
        node = self.disc_tree.getLastSelectedPathComponent()
        if node and node.isLeaf():
            url = self.get_full_path(node)
            content = self.discovered_map.get(url, "")
            self.info_area.setText("[+] URL: {}\n\n{}".format(url, content))

    def get_full_path(self, node):
        path = []
        while node:
            path.insert(0, str(node.getUserObject()))
            node = node.getParent()
            if node and node.getUserObject() == "Discovered JS URLs":
                break
        return "/" + "/".join(path)

    def update_discovered_tree(self):
        self.disc_root_node.removeAllChildren()
        full_urls = sorted(self.discovered_map.keys())
        for full_url in full_urls:
            parts = [p for p in full_url.strip("/").split("/") if p]
            current_node = self.disc_root_node
            for part in parts:
                match = None
                for i in range(current_node.getChildCount()):
                    child = current_node.getChildAt(i)
                    if child.getUserObject() == part:
                        match = child
                        break
                if not match:
                    match = DefaultMutableTreeNode(part)
                    self.disc_tree_model.insertNodeInto(match, current_node, current_node.getChildCount())
                current_node = match
        self.disc_tree_model.reload()
        self.disc_tree.expandRow(0)

    def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo):
        if toolFlag not in [self.callbacks.TOOL_PROXY, self.callbacks.TOOL_REPEATER]:
            return
        try:
            request_info = self.helpers.analyzeRequest(messageInfo)
            url = request_info.getUrl()
            host, path, full_url = url.getHost(), url.getPath(), str(url)

            response = messageInfo.getResponse()
            if not response:
                return
            response_info = self.helpers.analyzeResponse(response)
            headers = response_info.getHeaders()
            body = response[response_info.getBodyOffset():].tostring().decode("utf-8", "ignore")

            content_type = ""
            for h in headers:
                if h.lower().startswith("content-type:"):
                    content_type = h.lower()
                    break
            if path.endswith(".js") or "javascript" in content_type:
                found_urls = re.findall(r'["\'](/[^\'"\\s]{1,200})["\']', body)
                added = False
                for u in found_urls:
                    full = host + u
                    if full not in self.discovered_map:
                        self.discovered_map[full] = "[Extracted from {}]".format(full_url)
                        added = True
                if added:
                    EventQueue.invokeLater(self.update_discovered_tree)

            request_str = messageInfo.getRequest()[request_info.getBodyOffset():].tostring().decode("utf-8", "ignore").lower()
            response_str = body.lower()
            for scope, color, keyword in self.cached_keywords:
                if (scope == "Request" and keyword in request_str) or \
                   (scope == "Response" and keyword in response_str) or \
                   (scope == "Both" and (keyword in request_str or keyword in response_str)):
                    messageInfo.setHighlight(color)
                    break

        except Exception as e:
            print("[!] Error: {}".format(str(e)))