diff --git a/build.gradle b/build.gradle index 700fa4c..5a55d60 100644 --- a/build.gradle +++ b/build.gradle @@ -22,8 +22,6 @@ dependencies { implementation 'org.yaml:snakeyaml:2.0' implementation 'dk.brics.automaton:automaton:1.11-8' implementation 'com.github.ben-manes.caffeine:caffeine:3.1.8' - implementation 'com.google.code.gson:gson:2.11.0' - implementation 'com.squareup.okhttp3:okhttp:4.12.0' } test { @@ -36,4 +34,4 @@ jar { from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } -} +} \ No newline at end of file diff --git a/images/config-ai.png b/images/config-ai.png deleted file mode 100644 index ed5b007..0000000 Binary files a/images/config-ai.png and /dev/null differ diff --git a/images/config-setting.png b/images/config-setting.png deleted file mode 100644 index 1f1b7de..0000000 Binary files a/images/config-setting.png and /dev/null differ diff --git a/src/main/java/hae/Config.java b/src/main/java/hae/Config.java index 3411bac..173790e 100644 --- a/src/main/java/hae/Config.java +++ b/src/main/java/hae/Config.java @@ -61,11 +61,6 @@ public class Config { "gray" }; - public static String prompt = "You are a data security expert in the field of cyber security. Your task is to optimize the information provided by the user and then output it in JSON format. The user-supplied information is data that has been extracted by regular expressions. The user-supplied information is divided into two parts, the first part is RuleName which represents the name of the regular expression and the second part is MarkInfo which represents the data extracted by the regular expression. You need to find the matching or similar data in MarkInfo according to the meaning of RuleName, and output the original rows of these data in JSON format.(garbled and meaningless data rows should be removed)\n" + - "You must ensure that the extracted data is accurately expressed and correctly formatted in the JSON structure. Your output data must comply with the original MarkInfo content rows without modification, and strictly adhere to the following JSON format for return, no other text, code and formatting (e.g., line breaks, carriage returns, indentation, spaces), once the return of other irrelevant content will cause irreparable damage to the user: {\"data\":[\"data1\", \"data2\"]}."; - - public static String userTextFormat = "User Input: \r\nRuleName: %s\r\nMarkInfo: %s"; - public static Map globalRules = new HashMap<>(); public static ConcurrentHashMap>> globalDataMap = new ConcurrentHashMap<>(); diff --git a/src/main/java/hae/HaE.java b/src/main/java/hae/HaE.java index 31a85ec..4f8b3b7 100644 --- a/src/main/java/hae/HaE.java +++ b/src/main/java/hae/HaE.java @@ -12,19 +12,21 @@ import hae.instances.editor.WebSocketEditor; import hae.instances.websocket.WebSocketMessageHandler; import hae.utils.ConfigLoader; +import hae.utils.DataManager; public class HaE implements BurpExtension { @Override public void initialize(MontoyaApi api) { // 设置扩展名称 - String version = "3.4"; - api.extension().setName(String.format("HaE (%s) - Highlighter and Extractor", version)); + String version = "4.0"; + api.extension().setName("HaE - Highlighter and Extractor"); // 加载扩展后输出的项目信息 Logging logging = api.logging(); logging.logToOutput("[ HACK THE WORLD - TO DO IT ]"); logging.logToOutput("[#] Author: EvilChen && 0chencc && vaycore"); logging.logToOutput("[#] Github: https://github.com/gh0stkey/HaE"); + logging.logToOutput("[#] Version: " + version); // 配置文件加载 ConfigLoader configLoader = new ConfigLoader(api); @@ -42,6 +44,10 @@ public void initialize(MontoyaApi api) { api.userInterface().registerHttpResponseEditorProvider(new ResponseEditor(api, configLoader)); api.userInterface().registerWebSocketMessageEditorProvider(new WebSocketEditor(api, configLoader)); + // 从BurpSuite里加载数据 + DataManager dataManager = new DataManager(api); + dataManager.loadData(messageTableModel); + api.extension().registerUnloadingHandler(new ExtensionUnloadingHandler() { @Override public void extensionUnloaded() { diff --git a/src/main/java/hae/component/Config.java b/src/main/java/hae/component/Config.java index 0266236..1af3d04 100644 --- a/src/main/java/hae/component/Config.java +++ b/src/main/java/hae/component/Config.java @@ -29,7 +29,6 @@ public class Config extends JPanel { private final ConfigLoader configLoader; private final MessageTableModel messageTableModel; private final Rules rules; - private final String defaultText = "Enter a new item"; private Registration activeHandler; private Registration passiveHandler; @@ -61,22 +60,22 @@ private void initComponents() { pathTextField.setEditable(false); pathTextField.setText(configLoader.getRulesFilePath()); JButton reloadButton = new JButton("Reload"); - JButton updateButton = new JButton("Update"); + JButton reinitButton = new JButton("Reinit"); ruleInfoPanel.add(ruleLabel); ruleInfoPanel.add(pathTextField, constraints); ruleInfoPanel.add(Box.createHorizontalStrut(5)); - ruleInfoPanel.add(reloadButton); + ruleInfoPanel.add(reinitButton); ruleInfoPanel.add(Box.createHorizontalStrut(5)); - ruleInfoPanel.add(updateButton); + ruleInfoPanel.add(reloadButton); reloadButton.addActionListener(this::reloadActionPerformed); - updateButton.addActionListener(this::onlineUpdateActionPerformed); + reinitButton.addActionListener(this::reinitActionPerformed); constraints.gridx = 1; JTabbedPane configTabbedPanel = new JTabbedPane(); String[] settingMode = new String[]{"Exclude suffix", "Block host", "Exclude status"}; - JPanel settingPanel = createConfigTablePanel(settingMode, "Setting"); + JPanel settingPanel = createConfigTablePanel(settingMode); JPanel northPanel = new JPanel(new BorderLayout()); @@ -105,38 +104,6 @@ public void componentResized(ComponentEvent e) { settingPanel.add(northPanel, BorderLayout.NORTH); configTabbedPanel.add("Setting", settingPanel); - - String[] aiMode = new String[]{"Alibaba", "Moonshot"}; - JPanel aiPanel = createConfigTablePanel(aiMode, "AI+"); - JTextArea promptTextArea = new JTextArea(); - promptTextArea.setLineWrap(true); - promptTextArea.getDocument().addDocumentListener(new DocumentListener() { - @Override - public void insertUpdate(DocumentEvent e) { - onTextChange(); - } - - @Override - public void removeUpdate(DocumentEvent e) { - onTextChange(); - } - - @Override - public void changedUpdate(DocumentEvent e) { - onTextChange(); - } - - private void onTextChange() { - String promptText = promptTextArea.getText(); - configLoader.setAIPrompt(promptText); - } - }); - promptTextArea.setText(configLoader.getAIPrompt()); - JScrollPane promptScrollPane = new JScrollPane(promptTextArea); - promptScrollPane.setBorder(new TitledBorder("Prompt")); - promptScrollPane.setPreferredSize(new Dimension(0, 100)); - aiPanel.add(promptScrollPane, BorderLayout.NORTH); - configTabbedPanel.add("AI+", aiPanel); add(ruleInfoPanel, BorderLayout.NORTH); add(configTabbedPanel, BorderLayout.CENTER); } @@ -256,47 +223,8 @@ public void actionPerformed(ActionEvent e) { }; } - private TableModelListener craeteAITableModelListener(JComboBox setTypeComboBox, DefaultTableModel model) { - return new TableModelListener() { - @Override - public void tableChanged(TableModelEvent e) { - String selected = (String) setTypeComboBox.getSelectedItem(); - String values = getFirstColumnDataAsString(model); - - if (selected.equals("Alibaba")) { - if (!values.equals(configLoader.getAlibabaAIAPIKey()) && !values.isEmpty()) { - configLoader.setAlibabaAIAPIKey(values); - } - } - if (selected.equals("Moonshot")) { - if (!values.equals(configLoader.getMoonshotAIAPIKey()) && !values.isEmpty()) { - configLoader.setMoonshotAIAPIKey(values); - } - } - } - }; - } - - private ActionListener createAIActionListener(JComboBox setTypeComboBox, DefaultTableModel model) { - return new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - String selected = (String) setTypeComboBox.getSelectedItem(); - model.setRowCount(0); - - if (selected.equals("Alibaba")) { - addDataToTable(configLoader.getAlibabaAIAPIKey().replaceAll("\\|", "\r\n"), model); - } - - if (selected.equals("Moonshot")) { - addDataToTable(configLoader.getMoonshotAIAPIKey().replaceAll("\\|", "\r\n"), model); - } - } - }; - } - - private JPanel createConfigTablePanel(String[] mode, String type) { + private JPanel createConfigTablePanel(String[] mode) { GridBagConstraints constraints = new GridBagConstraints(); constraints.weightx = 1.0; constraints.fill = GridBagConstraints.HORIZONTAL; @@ -327,9 +255,9 @@ private JPanel createConfigTablePanel(String[] mode, String type) { JComboBox setTypeComboBox = new JComboBox<>(); setTypeComboBox.setModel(new DefaultComboBoxModel<>(mode)); - model.addTableModelListener(type.equals("AI+") ? craeteAITableModelListener(setTypeComboBox, model) : craeteSettingTableModelListener(setTypeComboBox, model)); + model.addTableModelListener(craeteSettingTableModelListener(setTypeComboBox, model)); - setTypeComboBox.addActionListener(type.equals("AI+") ? createAIActionListener(setTypeComboBox, model) : createSettingActionListener(setTypeComboBox, model)); + setTypeComboBox.addActionListener(createSettingActionListener(setTypeComboBox, model)); setTypeComboBox.setSelectedItem(mode[0]); @@ -346,6 +274,7 @@ private JPanel createConfigTablePanel(String[] mode, String type) { buttonPanel.add(clearButton, constraints); JTextField addTextField = new JTextField(); + String defaultText = "Enter a new item"; UIEnhancer.setTextFieldPlaceholder(addTextField, defaultText); inputPanelB.add(addTextField, BorderLayout.CENTER); @@ -390,7 +319,7 @@ public void keyPressed(KeyEvent e) { JPanel settingMainPanel = new JPanel(new BorderLayout()); settingMainPanel.setBorder(new EmptyBorder(5, 15, 10, 15)); JScrollPane settingScroller = new JScrollPane(settingPanel); - settingScroller.setBorder(new TitledBorder(type.equals("AI+") ? "API Key" : "Setting")); + settingScroller.setBorder(new TitledBorder("Setting")); settingMainPanel.add(settingScroller, BorderLayout.CENTER); return settingMainPanel; @@ -492,16 +421,17 @@ private void addActionPerformed(ActionEvent e, DefaultTableModel model, JTextFie } } - private void onlineUpdateActionPerformed(ActionEvent e) { - // 添加提示框防止用户误触导致配置更新 - int retCode = JOptionPane.showConfirmDialog(this, "Do you want to update rules?", "Info", JOptionPane.YES_NO_OPTION); - if (retCode == JOptionPane.YES_OPTION) { - configLoader.initRulesByNet(); - reloadActionPerformed(null); - } - } - private void reloadActionPerformed(ActionEvent e) { rules.reloadRuleGroup(); } + + private void reinitActionPerformed(ActionEvent e) { + int retCode = JOptionPane.showConfirmDialog(this, "Do you want to reinitialize rules? This action will overwrite your existing rules.", "Info", JOptionPane.YES_NO_OPTION); + if (retCode == JOptionPane.YES_OPTION) { + boolean ret = configLoader.initRules(); + if (ret) { + rules.reloadRuleGroup(); + } + } + } } diff --git a/src/main/java/hae/component/Main.java b/src/main/java/hae/component/Main.java index 307ac93..2bbc6b5 100644 --- a/src/main/java/hae/component/Main.java +++ b/src/main/java/hae/component/Main.java @@ -81,7 +81,6 @@ private ImageIcon getImageIcon(boolean isDark) { ImageIcon originalIcon = new ImageIcon(imageURL); Image originalImage = originalIcon.getImage(); Image scaledImage = originalImage.getScaledInstance(30, 20, Image.SCALE_FAST); - ImageIcon scaledIcon = new ImageIcon(scaledImage); - return scaledIcon; + return new ImageIcon(scaledImage); } } diff --git a/src/main/java/hae/component/board/Databoard.java b/src/main/java/hae/component/board/Databoard.java index 681a15f..a9bfc06 100644 --- a/src/main/java/hae/component/board/Databoard.java +++ b/src/main/java/hae/component/board/Databoard.java @@ -2,49 +2,35 @@ import burp.api.montoya.MontoyaApi; import hae.Config; -import hae.component.board.message.MessageEntry; import hae.component.board.message.MessageTableModel; import hae.component.board.message.MessageTableModel.MessageTable; import hae.component.board.table.Datatable; -import hae.instances.http.utils.RegularMatcher; import hae.utils.ConfigLoader; import hae.utils.UIEnhancer; -import hae.utils.project.ProjectProcessor; -import hae.utils.project.model.HaeFileContent; import hae.utils.string.StringProcessor; import javax.swing.*; -import javax.swing.border.TitledBorder; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; -import javax.swing.filechooser.FileNameExtensionFilter; -import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumnModel; import javax.swing.table.TableModel; import javax.swing.table.TableRowSorter; import java.awt.*; import java.awt.event.*; -import java.io.File; import java.util.List; import java.util.*; -import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.function.Function; import java.util.stream.Collectors; public class Databoard extends JPanel { private final MontoyaApi api; private final ConfigLoader configLoader; - private final ProjectProcessor projectProcessor; private final MessageTableModel messageTableModel; private JTextField hostTextField; private JTabbedPane dataTabbedPane; private JSplitPane splitPane; private MessageTable messageTable; - private JProgressBar progressBar; private static Boolean isMatchHost = false; private final DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel(); @@ -52,15 +38,10 @@ public class Databoard extends JPanel { private SwingWorker>, Void> handleComboBoxWorker; private SwingWorker applyHostFilterWorker; - private SwingWorker, Void> exportActionWorker; - private SwingWorker, Void> importActionWorker; - - private final String defaultText = "Please enter the host"; public Databoard(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) { this.api = api; this.configLoader = configLoader; - this.projectProcessor = new ProjectProcessor(api); this.messageTableModel = messageTableModel; initComponents(); @@ -69,25 +50,22 @@ public Databoard(MontoyaApi api, ConfigLoader configLoader, MessageTableModel me private void initComponents() { setLayout(new GridBagLayout()); ((GridBagLayout) getLayout()).columnWidths = new int[]{25, 0, 0, 0, 20, 0}; - ((GridBagLayout) getLayout()).rowHeights = new int[]{0, 65, 20, 0, 0}; + ((GridBagLayout) getLayout()).rowHeights = new int[]{0, 65, 20, 0}; ((GridBagLayout) getLayout()).columnWeights = new double[]{0.0, 0.0, 1.0, 0.0, 0.0, 1.0E-4}; - ((GridBagLayout) getLayout()).rowWeights = new double[]{0.0, 1.0, 0.0, 0.0, 1.0E-4}; + ((GridBagLayout) getLayout()).rowWeights = new double[]{0.0, 1.0, 0.0, 1.0E-4}; JLabel hostLabel = new JLabel("Host:"); JButton clearButton = new JButton("Clear"); - JButton exportButton = new JButton("Export"); - JButton importButton = new JButton("Import"); JButton actionButton = new JButton("Action"); - JPanel menuPanel = new JPanel(new GridLayout(3, 1, 0, 5)); + JPanel menuPanel = new JPanel(new GridLayout(1, 1, 0, 5)); menuPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); JPopupMenu menu = new JPopupMenu(); menuPanel.add(clearButton); - menuPanel.add(exportButton); - menuPanel.add(importButton); menu.add(menuPanel); hostTextField = new JTextField(); + String defaultText = "Please enter the host"; UIEnhancer.setTextFieldPlaceholder(hostTextField, defaultText); splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); @@ -102,10 +80,7 @@ private void initComponents() { }); clearButton.addActionListener(this::clearActionPerformed); - exportButton.addActionListener(this::exportActionPerformed); - importButton.addActionListener(this::importActionPerformed); - progressBar = new JProgressBar(); splitPane.addComponentListener(new ComponentAdapter() { @Override @@ -115,7 +90,6 @@ public void componentResized(ComponentEvent e) { }); splitPane.setVisible(false); - progressBar.setVisible(false); add(hostLabel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(8, 0, 5, 5), 0, 0)); @@ -124,12 +98,9 @@ public void componentResized(ComponentEvent e) { add(actionButton, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(8, 0, 5, 5), 0, 0)); - add(splitPane, new GridBagConstraints(1, 1, 3, 1, 0.0, 1.0, + add(splitPane, new GridBagConstraints(1, 1, 3, 2, 0.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 5, 0, 5), 0, 0)); - add(progressBar, new GridBagConstraints(1, 2, 3, 1, 1.0, 0.0, - GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, - new Insets(0, 5, 0, 5), 0, 0)); hostComboBox.setMaximumRowCount(5); add(hostComboBox, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(8, 0, 5, 5), 0, 0)); @@ -149,24 +120,6 @@ private void resizePanel() { columnModel.getColumn(5).setPreferredWidth((int) (totalWidth * 0.1)); } - private void setProgressBar(boolean status) { - setProgressBar(status, progressBar, "Loading ..."); - } - - - public static void setProgressBar(boolean status, JProgressBar progressBar, String showString) { - progressBar.setIndeterminate(status); - if (!status) { - progressBar.setMaximum(100); - progressBar.setString("OK"); - progressBar.setStringPainted(true); - progressBar.setValue(progressBar.getMaximum()); - } else { - progressBar.setString(showString); - progressBar.setStringPainted(true); - } - } - private void setAutoMatch() { hostComboBox.setSelectedItem(null); hostComboBox.addActionListener(this::handleComboBoxAction); @@ -202,8 +155,6 @@ private void handleComboBoxAction(ActionEvent e) { String selectedHost = hostComboBox.getSelectedItem().toString(); if (getHostByList().contains(selectedHost)) { - progressBar.setVisible(true); - setProgressBar(true); hostTextField.setText(selectedHost); if (handleComboBoxWorker != null && !handleComboBoxWorker.isDone()) { @@ -357,11 +308,6 @@ public boolean include(Entry entry) { return null; } - - @Override - protected void done() { - setProgressBar(false); - } }; applyHostFilterWorker.execute(); @@ -374,263 +320,6 @@ private List getHostByList() { return new ArrayList<>(); } - private void exportActionPerformed(ActionEvent e) { - String selectedHost = hostTextField.getText().trim(); - - if (selectedHost.isEmpty()) { - return; - } - - String exportDir = selectDirectory(true); - - if (exportDir.isEmpty()) { - return; - } - - if (exportActionWorker != null && !exportActionWorker.isDone()) { - exportActionWorker.cancel(true); - } - - exportActionWorker = new SwingWorker, Void>() { - @Override - protected List doInBackground() { - ConcurrentHashMap>> dataMap = Config.globalDataMap; - return exportData(selectedHost, exportDir, dataMap); - } - - @Override - protected void done() { - try { - List taskStatusList = get(); - if (!taskStatusList.isEmpty()) { - JOptionPane.showMessageDialog(Databoard.this, generateTaskStatusPane(taskStatusList), "Info", JOptionPane.INFORMATION_MESSAGE); - } - } catch (Exception ignored) { - } - } - }; - - exportActionWorker.execute(); - } - - private JScrollPane generateTaskStatusPane(List dataList) { - String[] columnNames = {"#", "Filename", "Status"}; - DefaultTableModel taskStatusTableModel = new DefaultTableModel(columnNames, 0); - JTable taskStatusTable = new JTable(taskStatusTableModel); - - for (Object[] data : dataList) { - int rowCount = taskStatusTableModel.getRowCount(); - int id = rowCount > 0 ? (Integer) taskStatusTableModel.getValueAt(rowCount - 1, 0) + 1 : 1; - Object[] rowData = new Object[data.length + 1]; - rowData[0] = id; - System.arraycopy(data, 0, rowData, 1, data.length); - taskStatusTableModel.addRow(rowData); - } - - TableRowSorter sorter = new TableRowSorter<>(taskStatusTableModel); - taskStatusTable.setRowSorter(sorter); - - JScrollPane scrollPane = new JScrollPane(taskStatusTable); - scrollPane.setBorder(new TitledBorder("Task status")); - scrollPane.setPreferredSize(new Dimension(500, 300)); - - int paneWidth = scrollPane.getPreferredSize().width; - taskStatusTable.getColumnModel().getColumn(0).setPreferredWidth((int) (paneWidth * 0.1)); - taskStatusTable.getColumnModel().getColumn(1).setPreferredWidth((int) (paneWidth * 0.7)); - taskStatusTable.getColumnModel().getColumn(2).setPreferredWidth((int) (paneWidth * 0.2)); - - return scrollPane; - } - - private List exportData(String selectedHost, String exportDir, Map>> dataMap) { - return dataMap.entrySet().stream() - .filter(entry -> selectedHost.equals("*") || StringProcessor.matchesHostPattern(entry.getKey(), selectedHost)) - .filter(entry -> !entry.getKey().contains("*")) - .map(entry -> exportEntry(entry, exportDir)) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - } - - private Object[] exportEntry(Map.Entry>> entry, String exportDir) { - String key = entry.getKey(); - Map> ruleMap = entry.getValue(); - - if (ruleMap == null || ruleMap.isEmpty()) { - return null; - } - - List messageEntryList = messageTableModel.getLogs(); - - Map entryUUIDMap = messageEntryList.stream() - .collect(Collectors.toMap( - messageEntry -> messageEntry, - messageEntry -> StringProcessor.getRandomUUID(), - (existing, replacement) -> existing - )); - - Map> httpMap = processEntries( - messageEntryList, - key, - entryUUIDMap, - this::createHttpItemMap - ); - - Map> urlMap = processEntries( - messageEntryList, - key, - entryUUIDMap, - this::creteUrlItemMap - ); - - String hostName = key.replace(":", "_"); - String filename = String.format("%s/%s-%s.hae", exportDir, StringProcessor.getCurrentTime(), hostName); - boolean createdStatus = projectProcessor.createHaeFile(filename, key, ruleMap, urlMap, httpMap); - - return new Object[]{filename, createdStatus}; - } - - - private Map> processEntries(List messageEntryList, String key, Map entryUUIDMap, Function> mapFunction) { - return messageEntryList.stream() - .filter(messageEntry -> !StringProcessor.getHostByUrl(messageEntry.getUrl()).isEmpty()) - .filter(messageEntry -> StringProcessor.getHostByUrl(messageEntry.getUrl()).equals(key)) - .collect(Collectors.toMap( - entryUUIDMap::get, - mapFunction, - (existing, replacement) -> existing - )); - } - - private Map creteUrlItemMap(MessageEntry entry) { - Map urlItemMap = new LinkedHashMap<>(); - urlItemMap.put("url", entry.getUrl()); - urlItemMap.put("method", entry.getMethod()); - urlItemMap.put("status", entry.getStatus()); - urlItemMap.put("length", entry.getLength()); - urlItemMap.put("comment", entry.getComment()); - urlItemMap.put("color", entry.getColor()); - urlItemMap.put("size", String.valueOf(entry.getRequestResponse().request().toByteArray().length())); - return urlItemMap; - } - - private Map createHttpItemMap(MessageEntry entry) { - Map httpItemMap = new LinkedHashMap<>(); - httpItemMap.put("request", entry.getRequestResponse().request().toByteArray().getBytes()); - httpItemMap.put("response", entry.getRequestResponse().response().toByteArray().getBytes()); - return httpItemMap; - } - - private void importActionPerformed(ActionEvent e) { - String exportDir = selectDirectory(false); - if (exportDir.isEmpty()) { - return; - } - - if (importActionWorker != null && !importActionWorker.isDone()) { - importActionWorker.cancel(true); - } - - importActionWorker = new SwingWorker, Void>() { - @Override - protected List doInBackground() { - List filesWithExtension = findFilesWithExtension(new File(exportDir), ".hae"); - return filesWithExtension.stream() - .map(Databoard.this::importData) - .collect(Collectors.toList()); - } - - @Override - protected void done() { - try { - List taskStatusList = get(); - if (!taskStatusList.isEmpty()) { - JOptionPane.showMessageDialog(Databoard.this, generateTaskStatusPane(taskStatusList), "Info", JOptionPane.INFORMATION_MESSAGE); - } - } catch (Exception ignored) { - } - } - }; - - importActionWorker.execute(); - } - - private Object[] importData(String filename) { - ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2); - - HaeFileContent haeFileContent = projectProcessor.readHaeFile(filename); - boolean readStatus = haeFileContent != null; - - List> tasks = new ArrayList<>(); - - if (readStatus) { - try { - String host = haeFileContent.getHost(); - haeFileContent.getDataMap().forEach((key, value) -> RegularMatcher.putDataToGlobalMap(host, key, value)); - - haeFileContent.getUrlMap().forEach((key, urlItemMap) -> { - tasks.add(() -> { - String url = urlItemMap.get("url"); - String comment = urlItemMap.get("comment"); - String color = urlItemMap.get("color"); - String length = urlItemMap.get("length"); - String method = urlItemMap.get("method"); - String status = urlItemMap.get("status"); - String path = haeFileContent.getHttpPath(); - - messageTableModel.add(null, url, method, status, length, comment, color, key, path); - return null; - }); - }); - - executor.invokeAll(tasks); - } catch (Exception e) { - api.logging().logToError("importData: " + e.getMessage()); - } finally { - executor.shutdown(); - } - } - - return new Object[]{filename, readStatus}; - } - - private List findFilesWithExtension(File directory, String extension) { - List filePaths = new ArrayList<>(); - if (directory.isDirectory()) { - File[] files = directory.listFiles(); - if (files != null) { - for (File file : files) { - if (file.isDirectory()) { - filePaths.addAll(findFilesWithExtension(file, extension)); - } else if (file.isFile() && file.getName().toLowerCase().endsWith(extension)) { - filePaths.add(file.getAbsolutePath()); - } - } - } - } else { - filePaths.add(directory.getAbsolutePath()); - } - return filePaths; - } - - private String selectDirectory(boolean forDirectories) { - JFileChooser chooser = new JFileChooser(); - chooser.setCurrentDirectory(new java.io.File(configLoader.getRulesFilePath())); - chooser.setDialogTitle(String.format("Select a Directory%s", forDirectories ? "" : " or File")); - FileNameExtensionFilter filter = new FileNameExtensionFilter(".hae Files", "hae"); - chooser.addChoosableFileFilter(filter); - chooser.setFileFilter(filter); - - chooser.setFileSelectionMode(forDirectories ? JFileChooser.DIRECTORIES_ONLY : JFileChooser.FILES_AND_DIRECTORIES); - chooser.setAcceptAllFileFilterUsed(!forDirectories); - - if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { - File selectedDirectory = chooser.getSelectedFile(); - return selectedDirectory.getAbsolutePath(); - } - - return ""; - } - private void clearActionPerformed(ActionEvent e) { int retCode = JOptionPane.showConfirmDialog(this, "Do you want to clear data?", "Info", JOptionPane.YES_NO_OPTION); @@ -638,7 +327,6 @@ private void clearActionPerformed(ActionEvent e) { if (retCode == JOptionPane.YES_OPTION && !host.isEmpty()) { dataTabbedPane.removeAll(); splitPane.setVisible(false); - progressBar.setVisible(false); Config.globalDataMap.keySet().parallelStream().forEach(key -> { if (StringProcessor.matchesHostPattern(key, host) || host.equals("*")) { diff --git a/src/main/java/hae/component/board/message/MessageEntry.java b/src/main/java/hae/component/board/message/MessageEntry.java index ec52108..ccdf720 100644 --- a/src/main/java/hae/component/board/message/MessageEntry.java +++ b/src/main/java/hae/component/board/message/MessageEntry.java @@ -11,10 +11,8 @@ public class MessageEntry { private final String status; private final String color; private final String method; - private final String hash; - private final String path; - MessageEntry(HttpRequestResponse requestResponse, String method, String url, String comment, String length, String color, String status, String hash, String path) { + MessageEntry(HttpRequestResponse requestResponse, String method, String url, String comment, String length, String color, String status) { this.requestResponse = requestResponse; this.method = method; this.url = url; @@ -22,8 +20,6 @@ public class MessageEntry { this.length = length; this.color = color; this.status = status; - this.hash = hash; - this.path = path; } public String getColor() { @@ -53,12 +49,4 @@ public String getStatus() { public HttpRequestResponse getRequestResponse() { return this.requestResponse; } - - public String getHash() { - return this.hash; - } - - public String getPath() { - return this.path; - } } \ No newline at end of file diff --git a/src/main/java/hae/component/board/message/MessageTableModel.java b/src/main/java/hae/component/board/message/MessageTableModel.java index 7b69890..9d75c8c 100644 --- a/src/main/java/hae/component/board/message/MessageTableModel.java +++ b/src/main/java/hae/component/board/message/MessageTableModel.java @@ -5,13 +5,14 @@ import burp.api.montoya.http.message.HttpRequestResponse; import burp.api.montoya.http.message.requests.HttpRequest; import burp.api.montoya.http.message.responses.HttpResponse; +import burp.api.montoya.persistence.PersistedObject; import burp.api.montoya.ui.UserInterface; import burp.api.montoya.ui.editor.HttpRequestEditor; import burp.api.montoya.ui.editor.HttpResponseEditor; import hae.Config; import hae.cache.CachePool; import hae.utils.ConfigLoader; -import hae.utils.project.FileProcessor; +import hae.utils.DataManager; import hae.utils.string.HashCalculator; import hae.utils.string.StringProcessor; @@ -97,10 +98,10 @@ private int getIndex(String color) { splitPane.setRightComponent(messageTab); } - public void add(HttpRequestResponse messageInfo, String url, String method, String status, String length, String comment, String color, String hash, String path) { + public void add(HttpRequestResponse messageInfo, String url, String method, String status, String length, String comment, String color, boolean flag) { synchronized (log) { boolean isDuplicate = false; - MessageEntry logEntry = new MessageEntry(messageInfo, method, url, comment, length, color, status, hash, path); + MessageEntry logEntry = new MessageEntry(messageInfo, method, url, comment, length, color, status); byte[] reqByteA = new byte[0]; byte[] resByteA = new byte[0]; @@ -134,6 +135,18 @@ public void add(HttpRequestResponse messageInfo, String url, String method, Stri } if (!isDuplicate) { + if (flag) { + DataManager dataManager = new DataManager(api); + // 数据存储在BurpSuite空间内 + PersistedObject persistedObject = PersistedObject.persistedObject(); + persistedObject.setHttpRequestResponse("messageInfo", messageInfo); + persistedObject.setString("comment", comment); + persistedObject.setString("color", color); + String uuidIndex = StringProcessor.getRandomUUID(); + dataManager.putData("message", uuidIndex, persistedObject); + } + + // 添加进日志 log.add(logEntry); } } @@ -177,11 +190,10 @@ public void applyHostFilter(String filterText) { filteredLog.clear(); log.forEach(entry -> { - MessageEntry finalEntry = getEntryByFile(entry); - String host = StringProcessor.getHostByUrl(finalEntry.getUrl()); + String host = StringProcessor.getHostByUrl(entry.getUrl()); if (!host.isEmpty()) { if (StringProcessor.matchesHostPattern(host, filterText) || filterText.contains("*")) { - filteredLog.add(finalEntry); + filteredLog.add(entry); } } }); @@ -189,34 +201,6 @@ public void applyHostFilter(String filterText) { fireTableDataChanged(); } - private MessageEntry getEntryByFile(MessageEntry entry) { - HttpRequestResponse requestResponse = entry.getRequestResponse(); - if (requestResponse == null) { - String url = entry.getUrl(); - String method = entry.getMethod(); - String status = entry.getStatus(); - String comment = entry.getComment(); - String color = entry.getColor(); - String path = entry.getPath(); - String hash = entry.getHash(); - int length = Integer.parseInt(entry.getLength()); - - byte[] contents = FileProcessor.readFileContent(path, hash); - - if (contents.length > length) { - byte[] response = Arrays.copyOf(contents, length); - byte[] request = Arrays.copyOfRange(contents, length, contents.length); - requestResponse = StringProcessor.createHttpRequestResponse(url, request, response); - - int index = log.indexOf(entry); - entry = new MessageEntry(requestResponse, method, url, comment, String.valueOf(length), color, status, "", ""); - log.set(index, entry); - } - } - - return entry; - } - public void applyMessageFilter(String tableName, String filterText) { filteredLog.clear(); for (MessageEntry entry : log) { diff --git a/src/main/java/hae/component/board/table/AIPower.java b/src/main/java/hae/component/board/table/AIPower.java deleted file mode 100644 index 24af1b9..0000000 --- a/src/main/java/hae/component/board/table/AIPower.java +++ /dev/null @@ -1,157 +0,0 @@ -package hae.component.board.table; - -import burp.api.montoya.MontoyaApi; -import burp.api.montoya.http.RequestOptions; -import burp.api.montoya.http.message.HttpRequestResponse; -import burp.api.montoya.http.message.requests.HttpRequest; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; -import hae.Config; -import hae.utils.ConfigLoader; -import hae.utils.http.HttpUtils; -import okhttp3.*; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class AIPower { - private final MontoyaApi api; - private final HttpUtils httpUtils; - private final ConfigLoader configLoader; - private final String apiAuth; - private final String aiModel; - private final String aiBaseUrl; - - public AIPower(MontoyaApi api, ConfigLoader configLoader, String aiModel, String aiBaseUrl, String[] apiKey) { - this.api = api; - this.configLoader = configLoader; - this.httpUtils = new HttpUtils(api, configLoader); - this.aiModel = aiModel; - this.aiBaseUrl = aiBaseUrl; - - this.apiAuth = String.format("Bearer %s", apiKey[new Random().nextInt(apiKey.length)]); - } - - // Stream Response - public String chatWithAPI(String ruleName, String data) { - OkHttpClient httpClient = new OkHttpClient(); - String fileId = uploadFileToAIService(ruleName, data); - Gson gson = new Gson(); - - if (fileId != null) { - String chatUrl = String.format("%s/chat/completions", aiBaseUrl); - String chatMessage = generateJsonData(configLoader.getAIPrompt(), fileId); - Request request = new Request.Builder() - .url(chatUrl) - .header("Authorization", apiAuth) - .post(RequestBody.create(MediaType.parse("application/json"), chatMessage)) - .build(); - - try (Response response = httpClient.newCall(request).execute()) { - if (!response.isSuccessful()) { - throw new IOException("Unexpected code " + response); - } - - BufferedReader reader = new BufferedReader(new InputStreamReader(response.body().byteStream())); - StringBuilder chatReturn = new StringBuilder(); - String line; - - while ((line = reader.readLine()) != null) { - if (line.startsWith("data: ") && !line.contains("[DONE]")) { - String jsonData = line.substring(6); - Type type = new TypeToken>() { - }.getType(); - Map map = gson.fromJson(jsonData, type); - String content = getDeltaContent(map); - if (content != null) { - chatReturn.append(content); - } - } - } - - deleteFileOnAIService(fileId); - - return chatReturn.toString(); - } catch (Exception e) { - return ""; - } - } - - return ""; - } - - private String getDeltaContent(Map map) { - List>> choices = (List>>) map.get("choices"); - if (choices != null && !choices.isEmpty()) { - Map delta = choices.get(0).get("delta"); - return delta.get("content"); - } - return null; - } - - private String uploadFileToAIService(String ruleName, String data) { - String uploadUrl = String.format("%s/files", aiBaseUrl); - String uploadParam = "file"; - String filename = "hae.txt"; - String content = String.format(Config.userTextFormat, ruleName, data); - - HttpRequest uploadFileRequest = httpUtils.generateRequestByMultipartUploadMethod(uploadUrl, uploadParam, filename, content).withAddedHeader("Authorization", apiAuth); - - HttpRequestResponse uploadFileRequestResponse = api.http().sendRequest(uploadFileRequest, RequestOptions.requestOptions().withUpstreamTLSVerification()); - String responseBody = uploadFileRequestResponse.response().bodyToString(); - Pattern pattern = Pattern.compile("\"id\":\"(.*?)\","); - Matcher matcher = pattern.matcher(responseBody); - - return matcher.find() ? matcher.group(1) : null; - } - - private void deleteFileOnAIService(String fileId) { - String deleteFileUrl = String.format("%s/files/%s", aiBaseUrl, fileId); - HttpRequest deleteFileRequest = httpUtils.generateRequestByDeleteMethod(deleteFileUrl).withAddedHeader("Authorization", apiAuth); - api.http().sendRequest(deleteFileRequest, RequestOptions.requestOptions().withUpstreamTLSVerification()); - } - - private String getFileContentOnAiService(String fileId) { - String getFileContentUrl = String.format("%s/files/%s/content", aiBaseUrl, fileId); - HttpRequest getFileContentRequest = HttpRequest.httpRequestFromUrl(getFileContentUrl).withAddedHeader("Authorization", apiAuth); - HttpRequestResponse getFileRequestResponse = api.http().sendRequest(getFileContentRequest, RequestOptions.requestOptions().withUpstreamTLSVerification()); - String responseBody = getFileRequestResponse.response().bodyToString(); - Pattern pattern = Pattern.compile("\"content\":\"(.*?)\",\"file_type\""); - Matcher matcher = pattern.matcher(responseBody); - - return matcher.find() ? matcher.group(1) : null; - } - - private String generateJsonData(String prompt, String fileId) { - Map data = new HashMap<>(); - data.put("model", aiModel); - data.put("stream", true); - data.put("messages", new Object[]{ - new HashMap() {{ - put("role", "system"); - put("content", prompt); - }}, - new HashMap() {{ - put("role", "system"); - put("content", aiModel.equals("qwen-long") ? String.format("fileid://%s", fileId) : getFileContentOnAiService(fileId)); - }}, - new HashMap() {{ - put("role", "user"); - put("content", "Start"); - }} - }); - - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - return gson.toJson(data); - } - -} diff --git a/src/main/java/hae/component/board/table/Datatable.java b/src/main/java/hae/component/board/table/Datatable.java index 0d8da66..53cc373 100644 --- a/src/main/java/hae/component/board/table/Datatable.java +++ b/src/main/java/hae/component/board/table/Datatable.java @@ -1,9 +1,6 @@ package hae.component.board.table; import burp.api.montoya.MontoyaApi; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; -import hae.component.board.Databoard; import hae.component.board.message.MessageTableModel; import hae.utils.ConfigLoader; import hae.utils.UIEnhancer; @@ -11,22 +8,17 @@ import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; -import javax.swing.event.PopupMenuEvent; -import javax.swing.event.PopupMenuListener; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumn; import javax.swing.table.TableRowSorter; import java.awt.*; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; -import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; -import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import java.util.Map; import java.util.regex.Pattern; public class Datatable extends JPanel { @@ -39,15 +31,12 @@ public class Datatable extends JPanel { private final TableRowSorter sorter; private final JCheckBox searchMode = new JCheckBox("Reverse search"); private final String tabName; - private final JProgressBar progressBar; - private final JPopupMenu aiEmpoweredMenu; private final JPanel footerPanel; public Datatable(MontoyaApi api, ConfigLoader configLoader, String tabName, List dataList) { this.api = api; this.configLoader = configLoader; this.tabName = tabName; - this.progressBar = new JProgressBar(); String[] columnNames = {"#", "Information"}; this.dataTableModel = new DefaultTableModel(columnNames, 0); @@ -56,15 +45,12 @@ public Datatable(MontoyaApi api, ConfigLoader configLoader, String tabName, List this.sorter = new TableRowSorter<>(dataTableModel); this.searchField = new JTextField(10); this.secondSearchField = new JTextField(10); - this.aiEmpoweredMenu = new JPopupMenu(); this.footerPanel = new JPanel(new BorderLayout(0, 5)); initComponents(dataList); } private void initComponents(List dataList) { - progressBar.setVisible(false); - // 设置ID排序 sorter.setComparator(0, new Comparator() { @Override @@ -142,57 +128,17 @@ public void changedUpdate(DocumentEvent e) { JButton settingsButton = new JButton("Settings"); setMenuShow(settingMenu, settingsButton); - // AI Empowered按钮 - JPanel aiEmpoweredPanel = new JPanel(new GridLayout(2, 1)); - aiEmpoweredPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); - JButton empoweredByAlibabaButton = new JButton("Alibaba - QwenLong"); - empoweredByAlibabaButton.addActionListener(e -> { - aiEmpoweredByAlibabaActionPerformed(e, tabName, getTableData(dataTable)); - }); - JButton empoweredByMoonshotButton = new JButton("Moonshot - Kimi"); - empoweredByMoonshotButton.addActionListener(e -> { - aiEmpoweredByMoonshotActionPerformed(e, tabName, getTableData(dataTable)); - }); - aiEmpoweredPanel.add(empoweredByAlibabaButton); - aiEmpoweredPanel.add(empoweredByMoonshotButton); - aiEmpoweredMenu.add(aiEmpoweredPanel); - - JButton aiEmpoweredButton = new JButton("AI Empowered"); - setMenuShow(aiEmpoweredMenu, aiEmpoweredButton); - aiEmpoweredMenu.addPopupMenuListener(new PopupMenuListener() { - @Override - public void popupMenuWillBecomeVisible(PopupMenuEvent e) { - empoweredByAlibabaButton.setEnabled(!configLoader.getAlibabaAIAPIKey().isEmpty()); - empoweredByMoonshotButton.setEnabled(!configLoader.getMoonshotAIAPIKey().isEmpty()); - } - - @Override - public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { - - } - - @Override - public void popupMenuCanceled(PopupMenuEvent e) { - - } - }); - optionsPanel.add(settingsButton); optionsPanel.add(Box.createHorizontalStrut(5)); optionsPanel.add(searchField); optionsPanel.add(Box.createHorizontalStrut(5)); optionsPanel.add(secondSearchField); - optionsPanel.add(Box.createHorizontalStrut(5)); - optionsPanel.add(aiEmpoweredButton); footerPanel.setBorder(BorderFactory.createEmptyBorder(2, 3, 5, 3)); footerPanel.add(optionsPanel, BorderLayout.CENTER); - footerPanel.add(progressBar, BorderLayout.SOUTH); add(scrollPane, BorderLayout.CENTER); add(footerPanel, BorderLayout.SOUTH); - - setProgressBar(false); } private void setMenuShow(JPopupMenu menu, JButton button) { @@ -205,9 +151,6 @@ private void setMenuShow(JPopupMenu menu, JButton button) { }); } - private void setProgressBar(boolean status) { - Databoard.setProgressBar(status, progressBar, "AI+ ..."); - } private void addRowToTable(Object[] data) { int rowCount = dataTableModel.getRowCount(); @@ -218,60 +161,6 @@ private void addRowToTable(Object[] data) { dataTableModel.addRow(rowData); } - private void aiEmpoweredByAlibabaActionPerformed(ActionEvent e, String ruleName, String data) { - AIPower aiPower = new AIPower(api, configLoader, "qwen-long", "https://dashscope.aliyuncs.com/compatible-mode/v1", configLoader.getAlibabaAIAPIKey().split("\\|")); - aiEmpoweredButtonAction(ruleName, data, aiPower); - } - - private void aiEmpoweredByMoonshotActionPerformed(ActionEvent e, String ruleName, String data) { - AIPower aiPower = new AIPower(api, configLoader, "moonshot-v1-128k", "https://api.moonshot.cn/v1", configLoader.getMoonshotAIAPIKey().split("\\|")); - aiEmpoweredButtonAction(ruleName, data, aiPower); - } - - private void aiEmpoweredButtonAction(String ruleName, String data, AIPower aiPower) { - progressBar.setVisible(true); - aiEmpoweredMenu.setVisible(true); - setProgressBar(true); - - SwingWorker worker = new SwingWorker() { - @Override - protected String doInBackground() throws Exception { - return aiPower.chatWithAPI(ruleName, data); - } - - @Override - protected void done() { - setProgressBar(false); - - try { - String chatReturn = get(); - if (!chatReturn.isEmpty()) { - Gson gson = new Gson(); - Type type = new TypeToken>() { - }.getType(); - Map> map = gson.fromJson(chatReturn, type); - - dataTableModel.setRowCount(0); - for (String item : map.get("data")) { - if (!item.isEmpty()) { - addRowToTable(new Object[]{item}); - } - } - - JOptionPane.showMessageDialog(Datatable.this, "AI+ has completed the AI empowered work.", "AI+ Info", JOptionPane.INFORMATION_MESSAGE); - } else { - JOptionPane.showMessageDialog(Datatable.this, "AI+ returns null, please check!", "AI+ Info", JOptionPane.WARNING_MESSAGE); - } - } catch (Exception ignored) { - JOptionPane.showMessageDialog(Datatable.this, "AI+ returns error, please check!", "AI+ Info", JOptionPane.ERROR_MESSAGE); - } - } - }; - worker.execute(); - - aiEmpoweredMenu.setVisible(false); - } - private void performSearch() { RowFilter firstRowFilter = applyFirstSearchFilter(); RowFilter secondRowFilter = applySecondFilter(); diff --git a/src/main/java/hae/instances/http/HttpMessageActiveHandler.java b/src/main/java/hae/instances/http/HttpMessageActiveHandler.java index df3d323..ab15c52 100644 --- a/src/main/java/hae/instances/http/HttpMessageActiveHandler.java +++ b/src/main/java/hae/instances/http/HttpMessageActiveHandler.java @@ -84,7 +84,7 @@ public ResponseReceivedAction handleHttpResponseReceived(HttpResponseReceived ht new SwingWorker() { @Override protected Void doInBackground() { - messageTableModel.add(httpRequestResponse, url, method, status, length, comment, color, "", ""); + messageTableModel.add(httpRequestResponse, url, method, status, length, comment, color, true); return null; } }.execute(); diff --git a/src/main/java/hae/instances/http/HttpMessagePassiveHandler.java b/src/main/java/hae/instances/http/HttpMessagePassiveHandler.java index 3abe297..81cbc69 100644 --- a/src/main/java/hae/instances/http/HttpMessagePassiveHandler.java +++ b/src/main/java/hae/instances/http/HttpMessagePassiveHandler.java @@ -71,7 +71,7 @@ public AuditResult passiveAudit(HttpRequestResponse httpRequestResponse) { new SwingWorker() { @Override protected Void doInBackground() { - messageTableModel.add(httpRequestResponse, url, method, status, length, comment, color, "", ""); + messageTableModel.add(httpRequestResponse, url, method, status, length, comment, color, true); return null; } }.execute(); diff --git a/src/main/java/hae/instances/http/utils/RegularMatcher.java b/src/main/java/hae/instances/http/utils/RegularMatcher.java index 70df3de..dd0ee6b 100644 --- a/src/main/java/hae/instances/http/utils/RegularMatcher.java +++ b/src/main/java/hae/instances/http/utils/RegularMatcher.java @@ -1,12 +1,15 @@ package hae.instances.http.utils; import burp.api.montoya.MontoyaApi; +import burp.api.montoya.persistence.PersistedList; +import burp.api.montoya.persistence.PersistedObject; import dk.brics.automaton.Automaton; import dk.brics.automaton.AutomatonMatcher; import dk.brics.automaton.RegExp; import dk.brics.automaton.RunAutomaton; import hae.Config; import hae.cache.CachePool; +import hae.utils.DataManager; import hae.utils.string.HashCalculator; import hae.utils.string.StringProcessor; @@ -38,7 +41,7 @@ public Map> match(String host, String type, String m // 多线程执行,一定程度上减少阻塞现象 String matchContent = ""; // 遍历获取规则 - List result = new ArrayList<>(); + List result; Map tmpMap = new HashMap<>(); boolean loaded = (Boolean) objects[0]; @@ -78,7 +81,7 @@ public Map> match(String host, String type, String m } try { - result.addAll(matchByRegex(f_regex, s_regex, matchContent, format, engine, sensitive)); + result = new ArrayList<>(matchByRegex(f_regex, s_regex, matchContent, format, engine, sensitive)); } catch (Exception e) { api.logging().logToError(String.format("[x] Error Info:\nName: %s\nRegex: %s", name, f_regex)); api.logging().logToError(e.getMessage()); @@ -98,7 +101,7 @@ public Map> match(String host, String type, String m String nameAndSize = String.format("%s (%s)", name, result.size()); finalMap.put(nameAndSize, tmpMap); - putDataToGlobalMap(host, name, result); + putDataToGlobalMap(api, host, name, result, true); } } } @@ -108,7 +111,7 @@ public Map> match(String host, String type, String m } } - public static void putDataToGlobalMap(String host, String name, List dataList) { + public static void putDataToGlobalMap(MontoyaApi api, String host, String name, List dataList, boolean flag) { // 添加到全局变量中,便于Databoard检索 if (!Objects.equals(host, "") && host != null) { Config.globalDataMap.compute(host, (existingHost, existingMap) -> { @@ -120,6 +123,18 @@ public static void putDataToGlobalMap(String host, String name, List dat return new ArrayList<>(combinedSet); }); + if (flag) { + // 数据存储在BurpSuite空间内 + DataManager dataManager = new DataManager(api); + PersistedObject persistedObject = PersistedObject.persistedObject(); + gRuleMap.forEach((kName, vList) -> { + PersistedList persistedList = PersistedList.persistedStringList(); + persistedList.addAll(vList); + persistedObject.setStringList(kName, persistedList); + }); + dataManager.putData("data", host, persistedObject); + } + return gRuleMap; }); @@ -128,7 +143,7 @@ public static void putDataToGlobalMap(String host, String name, List dat String anyHost = (splitHost.length > 2 && !StringProcessor.matchHostIsIp(onlyHost)) ? StringProcessor.replaceFirstOccurrence(onlyHost, splitHost[0], "*") : ""; - if (!Config.globalDataMap.containsKey(anyHost) && anyHost.length() > 0) { + if (!Config.globalDataMap.containsKey(anyHost) && !anyHost.isEmpty()) { // 添加通配符Host,实际数据从查询哪里将所有数据提取 Config.globalDataMap.put(anyHost, new HashMap<>()); } diff --git a/src/main/java/hae/instances/websocket/WebSocketMessageHandler.java b/src/main/java/hae/instances/websocket/WebSocketMessageHandler.java index 9597478..175666f 100644 --- a/src/main/java/hae/instances/websocket/WebSocketMessageHandler.java +++ b/src/main/java/hae/instances/websocket/WebSocketMessageHandler.java @@ -22,7 +22,7 @@ public TextMessageReceivedAction handleTextMessageReceived(InterceptedTextMessag String message = interceptedTextMessage.payload(); List> result = messageProcessor.processMessage("", message, true); - if (result != null && !result.isEmpty() && result.size() > 0) { + if (result != null && !result.isEmpty()) { interceptedTextMessage.annotations().setHighlightColor(HighlightColor.highlightColor(result.get(0).get("color"))); interceptedTextMessage.annotations().setNotes(result.get(1).get("comment")); } diff --git a/src/main/java/hae/utils/ConfigLoader.java b/src/main/java/hae/utils/ConfigLoader.java index 2435367..4cb61a6 100644 --- a/src/main/java/hae/utils/ConfigLoader.java +++ b/src/main/java/hae/utils/ConfigLoader.java @@ -1,9 +1,6 @@ package hae.utils; import burp.api.montoya.MontoyaApi; -import burp.api.montoya.http.RequestOptions; -import burp.api.montoya.http.message.HttpRequestResponse; -import burp.api.montoya.http.message.requests.HttpRequest; import hae.Config; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; @@ -46,7 +43,7 @@ public ConfigLoader(MontoyaApi api) { File rulesFilePath = new File(this.rulesFilePath); if (!(rulesFilePath.exists() && rulesFilePath.isFile())) { - initRulesByRes(); + initRules(); } Config.globalRules = getRules(); @@ -137,18 +134,6 @@ public Map getRules() { return rules; } - public String getAlibabaAIAPIKey() { - return getValueFromConfig("AlibabaAIAPIKey", ""); - } - - public String getMoonshotAIAPIKey() { - return getValueFromConfig("MoonshotAIAPIKey", ""); - } - - public String getAIPrompt() { - return getValueFromConfig("AIPrompt", Config.prompt); - } - public String getBlockHost() { return getValueFromConfig("BlockHost", Config.host); } @@ -191,18 +176,6 @@ private String getValueFromConfig(String name, String defaultValue) { return defaultValue; } - public void setAlibabaAIAPIKey(String apiKey) { - setValueToConfig("AlibabaAIAPIKey", apiKey); - } - - public void setMoonshotAIAPIKey(String apiKey) { - setValueToConfig("MoonshotAIAPIKey", apiKey); - } - - public void setAIPrompt(String prompt) { - setValueToConfig("AIPrompt", prompt); - } - public void setExcludeSuffix(String excludeSuffix) { setValueToConfig("ExcludeSuffix", excludeSuffix); } @@ -250,11 +223,12 @@ private Map loadCurrentConfig() { } } - public void initRulesByRes() { - boolean isCopySuccess = copyRulesToFile(this.rulesFilePath); - if (!isCopySuccess) { + public boolean initRules() { + boolean ret = copyRulesToFile(this.rulesFilePath); + if (!ret) { api.extension().unload(); } + return ret; } private boolean copyRulesToFile(String targetFilePath) { @@ -277,33 +251,4 @@ private boolean copyRulesToFile(String targetFilePath) { return false; } - - public void initRulesByNet() { - Thread t = new Thread() { - public void run() { - pullRules(); - } - }; - t.start(); - try { - t.join(10000); - } catch (Exception ignored) { - } - } - - private void pullRules() { - try { - String url = "https://raw.githubusercontent.com/gh0stkey/HaE/gh-pages/Rules.yml"; - HttpRequest httpRequest = HttpRequest.httpRequestFromUrl(url); - HttpRequestResponse requestResponse = api.http().sendRequest(httpRequest, RequestOptions.requestOptions().withUpstreamTLSVerification()); - String responseBody = requestResponse.response().bodyToString(); - if (responseBody.contains("rules")) { - FileOutputStream fileOutputStream = new FileOutputStream(rulesFilePath); - fileOutputStream.write(responseBody.getBytes()); - fileOutputStream.close(); - } - } catch (Exception ignored) { - api.extension().unload(); - } - } } diff --git a/src/main/java/hae/utils/DataManager.java b/src/main/java/hae/utils/DataManager.java new file mode 100644 index 0000000..732260b --- /dev/null +++ b/src/main/java/hae/utils/DataManager.java @@ -0,0 +1,85 @@ +package hae.utils; + +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.http.message.HttpRequestResponse; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; +import burp.api.montoya.persistence.PersistedList; +import burp.api.montoya.persistence.PersistedObject; +import burp.api.montoya.persistence.Persistence; +import hae.component.board.message.MessageTableModel; +import hae.instances.http.utils.RegularMatcher; + +public class DataManager { + private final MontoyaApi api; + private final Persistence persistence; + + public DataManager(MontoyaApi api) { + this.api = api; + this.persistence = api.persistence(); + } + + private void saveIndex(String indexName, String indexValue) { + PersistedList indexList = persistence.extensionData().getStringList(indexName); + + if (indexList != null && !indexList.isEmpty()) { + persistence.extensionData().deleteStringList(indexName); + } else { + indexList = PersistedList.persistedStringList(); + } + + if (!indexList.contains(indexValue)) { + indexList.add(indexValue); + } + + persistence.extensionData().setStringList(indexName, indexList); + } + + public void putData(String dataType, String dataName, PersistedObject persistedObject) { + if (persistence.extensionData().getChildObject(dataName) != null) { + persistence.extensionData().deleteChildObject(dataName); + } + persistence.extensionData().setChildObject(dataName, persistedObject); + saveIndex(dataType, dataName); + } + + public void loadData(MessageTableModel messageTableModel) { + // 1. 获取索引 + PersistedList dataIndex = persistence.extensionData().getStringList("data"); // 数据索引 + PersistedList messageIndex = persistence.extensionData().getStringList("message"); // 消息索引 + + // 2. 从索引获取数据 + loadHaEData(dataIndex); + loadMessageData(messageIndex, messageTableModel); + + } + + private void loadHaEData(PersistedList dataIndex) { + if (dataIndex != null && !dataIndex.isEmpty()) { + dataIndex.parallelStream().forEach(index -> { + PersistedObject dataObj = persistence.extensionData().getChildObject(index); + dataObj.stringListKeys().forEach(dataKey -> { + RegularMatcher.putDataToGlobalMap(api, index, dataKey, dataObj.getStringList(dataKey).stream().toList(), false); + }); + }); + } + } + + private void loadMessageData(PersistedList messageIndex, MessageTableModel messageTableModel) { + if (messageIndex != null && !messageIndex.isEmpty()) { + messageIndex.parallelStream().forEach(index -> { + PersistedObject dataObj = persistence.extensionData().getChildObject(index); + HttpRequestResponse messageInfo = dataObj.getHttpRequestResponse("messageInfo"); + String comment = dataObj.getString("comment"); + String color = dataObj.getString("color"); + HttpRequest request = messageInfo.request(); + HttpResponse response = messageInfo.response(); + String method = request.method(); + String url = request.url(); + String status = String.valueOf(response.statusCode()); + String length = String.valueOf(response.toByteArray().length()); + messageTableModel.add(messageInfo, url, method, status, length, comment, color, false); + }); + } + } +} diff --git a/src/main/java/hae/utils/UIEnhancer.java b/src/main/java/hae/utils/UIEnhancer.java index a97482a..01e0daf 100644 --- a/src/main/java/hae/utils/UIEnhancer.java +++ b/src/main/java/hae/utils/UIEnhancer.java @@ -41,6 +41,4 @@ private static void setPlaceholderText(JTextField textField) { textField.setText(placeholderText); textField.putClientProperty("isPlaceholder", true); } - - } diff --git a/src/main/java/hae/utils/http/HttpUtils.java b/src/main/java/hae/utils/http/HttpUtils.java index 2b87905..38be798 100644 --- a/src/main/java/hae/utils/http/HttpUtils.java +++ b/src/main/java/hae/utils/http/HttpUtils.java @@ -3,9 +3,7 @@ import burp.api.montoya.MontoyaApi; import burp.api.montoya.http.message.HttpRequestResponse; import burp.api.montoya.http.message.requests.HttpRequest; -import burp.api.montoya.http.message.requests.HttpTransformation; import burp.api.montoya.http.message.responses.HttpResponse; -import burp.api.montoya.utilities.RandomUtils; import hae.utils.ConfigLoader; import hae.utils.string.StringProcessor; @@ -21,25 +19,6 @@ public HttpUtils(MontoyaApi api, ConfigLoader configLoader) { this.configLoader = configLoader; } - public HttpRequest generateRequestByMultipartUploadMethod(String url, String name, String filename, String content) { - HttpRequest baseRequest = HttpRequest.httpRequestFromUrl(url).withTransformationApplied(HttpTransformation.TOGGLE_METHOD); - - String boundary = api.utilities().randomUtils().randomString(32, RandomUtils.CharacterSet.ASCII_LETTERS); - - String newBody = String.format("--%s\r\nContent-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n\r\n%s\r\n", boundary, name, filename, content) + - String.format("--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n", boundary, "purpose", "file-extract") + - "--" + boundary + "--\r\n"; - - baseRequest = baseRequest.withUpdatedHeader("Content-Type", "multipart/form-data; boundary=" + boundary).withBody(newBody); - - return baseRequest; - } - - - public HttpRequest generateRequestByDeleteMethod(String url) { - return HttpRequest.httpRequestFromUrl(url).withMethod("DELETE"); - } - public boolean verifyHttpRequestResponse(HttpRequestResponse requestResponse, String toolType) { HttpRequest request = requestResponse.request(); HttpResponse response = requestResponse.response(); diff --git a/src/main/java/hae/utils/project/FileProcessor.java b/src/main/java/hae/utils/project/FileProcessor.java deleted file mode 100644 index 7e37b6d..0000000 --- a/src/main/java/hae/utils/project/FileProcessor.java +++ /dev/null @@ -1,47 +0,0 @@ -package hae.utils.project; - -import java.io.File; -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Comparator; - -public class FileProcessor { - public static void deleteDirectoryWithContents(Path pathToBeDeleted) { - if (pathToBeDeleted != null) { - try { - Files.walk(pathToBeDeleted) - .sorted(Comparator.reverseOrder()) - .map(Path::toFile) - .forEach(File::delete); - } catch (Exception ignored) { - } - } - } - - public static byte[] readFileContent(String basePath, String fileName) { - Path filePath = Paths.get(basePath, fileName); - Path path = Paths.get(basePath); - try { - byte[] fileContent = Files.readAllBytes(filePath); - - Files.deleteIfExists(filePath); - - boolean isEmpty = isDirectoryEmpty(path); - if (isEmpty) { - Files.deleteIfExists(path); - } - - return fileContent; - } catch (Exception e) { - return new byte[0]; - } - } - - private static boolean isDirectoryEmpty(Path directory) throws Exception { - try (DirectoryStream dirStream = Files.newDirectoryStream(directory)) { - return !dirStream.iterator().hasNext(); - } - } -} diff --git a/src/main/java/hae/utils/project/ProjectProcessor.java b/src/main/java/hae/utils/project/ProjectProcessor.java deleted file mode 100644 index a3371b4..0000000 --- a/src/main/java/hae/utils/project/ProjectProcessor.java +++ /dev/null @@ -1,187 +0,0 @@ -package hae.utils.project; - -import burp.api.montoya.MontoyaApi; -import hae.utils.project.model.HaeFileContent; -import org.yaml.snakeyaml.LoaderOptions; -import org.yaml.snakeyaml.Yaml; - -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.util.*; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; -import java.util.zip.ZipOutputStream; - -public class ProjectProcessor { - private final MontoyaApi api; - - public ProjectProcessor(MontoyaApi api) { - this.api = api; - } - - public boolean createHaeFile(String haeFilePath, String host, Map> dataMap, Map> urlMap, Map> httpMap) { - ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2); - - List> tasks = new ArrayList<>(); - - ByteArrayOutputStream dataYamlStream = new ByteArrayOutputStream(); - ByteArrayOutputStream urlYamlStream = new ByteArrayOutputStream(); - Yaml yaml = new Yaml(); - - yaml.dump(dataMap, new OutputStreamWriter(dataYamlStream, StandardCharsets.UTF_8)); - yaml.dump(urlMap, new OutputStreamWriter(urlYamlStream, StandardCharsets.UTF_8)); - - try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(haeFilePath))) { - zipOut.putNextEntry(new ZipEntry("info")); - zipOut.write(host.getBytes(StandardCharsets.UTF_8)); - zipOut.closeEntry(); - - zipOut.putNextEntry(new ZipEntry("data")); - zipOut.write(dataYamlStream.toByteArray()); - zipOut.closeEntry(); - - zipOut.putNextEntry(new ZipEntry("url")); - zipOut.write(urlYamlStream.toByteArray()); - zipOut.closeEntry(); - - for (String httpHash : httpMap.keySet()) { - Map httpItem = httpMap.get(httpHash); - tasks.add(() -> { - try { - ByteArrayOutputStream httpOutStream = new ByteArrayOutputStream(); - byte[] request = (byte[]) httpItem.get("request"); - byte[] response = (byte[]) httpItem.get("response"); - - httpOutStream.write(response); - httpOutStream.write(request); - - synchronized (zipOut) { - zipOut.putNextEntry(new ZipEntry(String.format("http/%s", httpHash))); - zipOut.write(httpOutStream.toByteArray()); - zipOut.closeEntry(); - } - } catch (Exception e) { - api.logging().logToError("createHaeFile: " + e.getMessage()); - } - - return null; - }); - } - - executor.invokeAll(tasks); - } catch (Exception e) { - api.logging().logToError("createHaeFile: " + e.getMessage()); - return false; - } finally { - executor.shutdown(); - } - - return true; - } - - public HaeFileContent readHaeFile(String haeFilePath) { - ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2); - List> tasks = new ArrayList<>(); - - HaeFileContent haeFileContent = new HaeFileContent(api); - LoaderOptions loaderOptions = new LoaderOptions(); - loaderOptions.setMaxAliasesForCollections(Integer.MAX_VALUE); - loaderOptions.setCodePointLimit(Integer.MAX_VALUE); - Yaml yaml = new Yaml(loaderOptions); - Path tempDirectory = null; - - try { - if (hasValidStructure(haeFilePath)) { - tempDirectory = Files.createTempDirectory("hae"); - haeFileContent.setHttpPath(tempDirectory.toString()); - - try (ZipFile zipFile = new ZipFile(haeFilePath)) { - Enumeration entries = zipFile.entries(); - while (entries.hasMoreElements()) { - ZipEntry entry = entries.nextElement(); - String fileName = entry.getName(); - if (fileName.startsWith("http/")) { - Path filePath = tempDirectory.resolve(fileName.substring("http/".length())); - - tasks.add(() -> { - try (InputStream in = zipFile.getInputStream(entry)) { - Files.copy(in, filePath, StandardCopyOption.REPLACE_EXISTING); - } catch (IOException e) { - api.logging().logToError("readHaeFile: " + e.getMessage()); - } - - return null; - }); - } else { - try (InputStream in = zipFile.getInputStream(entry)) { - switch (fileName) { - case "info" -> - haeFileContent.setHost(new String(in.readAllBytes(), StandardCharsets.UTF_8)); - case "data" -> - haeFileContent.setDataMap(yaml.load(new InputStreamReader(in, StandardCharsets.UTF_8))); - case "url" -> - haeFileContent.setUrlMap(yaml.load(new InputStreamReader(in, StandardCharsets.UTF_8))); - } - } - } - } - - executor.invokeAll(tasks); - } - } - } catch (Exception e) { - api.logging().logToError("readHaeFile: " + e.getMessage()); - if (tempDirectory != null) { - FileProcessor.deleteDirectoryWithContents(tempDirectory); - } - haeFileContent = null; - } finally { - executor.shutdown(); - } - - return haeFileContent; - } - - private boolean hasValidStructure(String zipFilePath) { - Set requiredRootEntries = new HashSet<>(); - requiredRootEntries.add("info"); - requiredRootEntries.add("data"); - requiredRootEntries.add("url"); - - boolean hasHttpDirectoryWithFiles = false; - - try { - ZipFile zipFile = new ZipFile(zipFilePath); - Enumeration entries = zipFile.entries(); - - while (entries.hasMoreElements()) { - ZipEntry entry = entries.nextElement(); - String name = entry.getName(); - - if (!entry.isDirectory() && !name.contains("/")) { - requiredRootEntries.remove(name); - } - - if (name.startsWith("http/") && !entry.isDirectory()) { - hasHttpDirectoryWithFiles = true; - } - - if (requiredRootEntries.isEmpty() && hasHttpDirectoryWithFiles) { - break; - } - } - - zipFile.close(); - } catch (Exception ignored) { - } - - return requiredRootEntries.isEmpty() && hasHttpDirectoryWithFiles; - } -} - diff --git a/src/main/java/hae/utils/project/model/HaeFileContent.java b/src/main/java/hae/utils/project/model/HaeFileContent.java deleted file mode 100644 index 5782d70..0000000 --- a/src/main/java/hae/utils/project/model/HaeFileContent.java +++ /dev/null @@ -1,76 +0,0 @@ -package hae.utils.project.model; - -import burp.api.montoya.MontoyaApi; - -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class HaeFileContent { - private final MontoyaApi api; - private String host; - private String httpPath; - private final Map> dataMap; - private final Map> urlMap; - - public HaeFileContent(MontoyaApi api) { - this.api = api; - this.dataMap = new HashMap<>(); - this.urlMap = new HashMap<>(); - } - - public String getHost() { - return host; - } - - public Map> getDataMap() { - return dataMap; - } - - public Map> getUrlMap() { - return urlMap; - } - - public String getHttpPath() { - return httpPath; - } - - public void setHost(String host) { - this.host = host; - } - - public void setHttpPath(String path) { - this.httpPath = path; - } - - public void setDataMap(Map> dataMap) { - for (Map.Entry> entry : dataMap.entrySet()) { - List values = new ArrayList<>(); - for (Object value : entry.getValue()) { - try { - values.add(new String((byte[]) value, StandardCharsets.UTF_8)); - } catch (Exception e) { - values.add(value.toString()); - } - } - this.dataMap.put(entry.getKey(), values); - } - } - - public void setUrlMap(Map> urlMap) { - for (Map.Entry> entry : urlMap.entrySet()) { - Map newValues = new HashMap<>(); - Map values = entry.getValue(); - for (String key : values.keySet()) { - try { - newValues.put(key, new String((byte[]) values.get(key), StandardCharsets.UTF_8)); - } catch (Exception e) { - newValues.put(key, values.get(key).toString()); - } - } - this.urlMap.put(entry.getKey(), newValues); - } - } -} \ No newline at end of file diff --git a/src/main/resources/rules/Rules.yml b/src/main/resources/rules/Rules.yml index ac6edc0..e18ed1f 100644 --- a/src/main/resources/rules/Rules.yml +++ b/src/main/resources/rules/Rules.yml @@ -1,293 +1,303 @@ rules: -- group: Fingerprint - rule: - - name: Shiro - loaded: true - f_regex: (=deleteMe|rememberMe=) - s_regex: '' - format: '{0}' - color: green - scope: any header - engine: dfa - sensitive: true - - name: JSON Web Token - loaded: true - f_regex: (eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9._-]{10,}|eyJ[A-Za-z0-9_\/+-]{10,}\.[A-Za-z0-9._\/+-]{10,}) - s_regex: '' - format: '{0}' - color: green - scope: any - engine: nfa - sensitive: true - - name: Swagger UI - loaded: true - f_regex: ((swagger-ui.html)|(\"swagger\":)|(Swagger UI)|(swaggerUi)|(swaggerVersion)) - s_regex: '' - format: '{0}' - color: red - scope: response body - engine: dfa - sensitive: false - - name: Ueditor - loaded: true - f_regex: (ueditor\.(config|all)\.js) - s_regex: '' - format: '{0}' - color: green - scope: response body - engine: dfa - sensitive: false - - name: Druid - loaded: true - f_regex: (Druid Stat Index) - s_regex: '' - format: '{0}' - color: orange - scope: response body - engine: dfa - sensitive: false -- group: Maybe Vulnerability - rule: - - name: Java Deserialization - loaded: true - f_regex: (javax\.faces\.ViewState) - s_regex: '' - format: '{0}' - color: yellow - scope: response body - engine: dfa - sensitive: false - - name: Debug Logic Parameters - loaded: true - f_regex: ((access=)|(adm=)|(admin=)|(alter=)|(cfg=)|(clone=)|(config=)|(create=)|(dbg=)|(debug=)|(delete=)|(disable=)|(edit=)|(enable=)|(exec=)|(execute=)|(grant=)|(load=)|(make=)|(modify=)|(rename=)|(reset=)|(root=)|(shell=)|(test=)|(toggl=)) - s_regex: '' - format: '{0}' - color: cyan - scope: request - engine: dfa - sensitive: false - - name: URL As A Value - loaded: true - f_regex: (=(https?)(://|%3a%2f%2f)) - s_regex: '' - format: '{0}' - color: cyan - scope: any - engine: nfa - sensitive: false - - name: Upload Form - loaded: true - f_regex: (type\=\"file\") - s_regex: '' - format: '{0}' - color: yellow - scope: response body - engine: dfa - sensitive: false - - name: DoS Paramters - loaded: true - f_regex: ((size=)|(page=)|(num=)|(limit=)|(start=)|(end=)|(count=)) - s_regex: '' - format: '{0}' - color: cyan - scope: request - engine: dfa - sensitive: false -- group: Basic Information - rule: - - name: Email - loaded: true - f_regex: (([a-z0-9]+[_|\.])*[a-z0-9]+@([a-z0-9]+[-|_|\.])*[a-z0-9]+\.((?!js|css|jpg|jpeg|png|ico)[a-z]{2,5})) - s_regex: '' - format: '{0}' - color: yellow - scope: response - engine: nfa - sensitive: false - - name: Chinese IDCard - loaded: true - f_regex: '[^0-9]((\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(\d{6}(18|19|20)\d{2}(0[1-9]|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)))[^0-9]' - s_regex: '' - format: '{0}' - color: orange - scope: response body - engine: nfa - sensitive: true - - name: Chinese Mobile Number - loaded: true - f_regex: '[^\w]((?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8})[^\w]' - s_regex: '' - format: '{0}' - color: orange - scope: response body - engine: nfa - sensitive: false - - name: Internal IP Address - loaded: true - f_regex: '[^0-9]((127\.0\.0\.1)|(10\.\d{1,3}\.\d{1,3}\.\d{1,3})|(172\.((1[6-9])|(2\d)|(3[01]))\.\d{1,3}\.\d{1,3})|(192\.168\.\d{1,3}\.\d{1,3}))' - s_regex: '' - format: '{0}' - color: cyan - scope: response - engine: nfa - sensitive: true - - name: MAC Address - loaded: true - f_regex: (^([a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5})|[^a-zA-Z0-9]([a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5})) - s_regex: '' - format: '{0}' - color: green - scope: response - engine: nfa - sensitive: true -- group: Sensitive Information - rule: - - name: Cloud Key - loaded: true - f_regex: (((access)(|-|_)(key)(|-|_)(id|secret))|(LTAI[a-z0-9]{12,20})) - s_regex: '' - format: '{0}' - color: yellow - scope: any - engine: nfa - sensitive: false - - name: Windows File/Dir Path - loaded: true - f_regex: '[^\w]([a-zA-Z]:\\\\?(?:[^<>:/\\|?*]+\\\\?)*)([^<>:/\\|?*]+(?:\.[^<>:/\\|?*]+)?)' - s_regex: '' - format: '{0}' - color: green - scope: response - engine: nfa - sensitive: true - - name: Password Field - loaded: true - f_regex: ((|'|")(|[\w]{1,10})([p](ass|wd|asswd|assword))(|[\w]{1,10})(|'|")(:|=)( - |)('|")(.*?)('|")(|,)) - s_regex: '' - format: '{0}' - color: yellow - scope: response body - engine: nfa - sensitive: false - - name: Username Field - loaded: true - f_regex: ((|'|")(|[\w]{1,10})(([u](ser|name|sername))|(account)|((((create|update)((d|r)|(by|on|at)))|(creator))))(|[\w]{1,10})(|'|")(:|=)( - |)('|")(.*?)('|")(|,)) - s_regex: '' - format: '{0}' - color: green - scope: response body - engine: nfa - sensitive: false - - name: WeCom Key - loaded: true - f_regex: ((corp)(id|secret)) - s_regex: '' - format: '{0}' - color: green - scope: response body - engine: dfa - sensitive: false - - name: JDBC Connection - loaded: true - f_regex: (jdbc:[a-z:]+://[a-z0-9\.\-_:;=/@?,&]+) - s_regex: '' - format: '{0}' - color: yellow - scope: any - engine: nfa - sensitive: false - - name: Authorization Header - loaded: true - f_regex: ((basic [a-z0-9=:_\+\/-]{5,100})|(bearer [a-z0-9_.=:_\+\/-]{5,100})) - s_regex: '' - format: '{0}' - color: yellow - scope: response body - engine: nfa - sensitive: false - - name: Sensitive Field - loaded: true - f_regex: ((\[)?('|")?([\w]{0,10})((key)|(secret)|(token)|(config)|(auth)|(access)|(admin)|(ticket))([\w]{0,10})('|")?(\])?( - |)(:|=)( |)('|")(.*?)('|")(|,)) - s_regex: '' - format: '{0}' - color: yellow - scope: response - engine: nfa - sensitive: false -- group: Other - rule: - - name: Linkfinder - loaded: true - f_regex: (?:"|')(((?:[a-zA-Z]{1,10}://|//)[^"'/]{1,}\.[a-zA-Z]{2,}[^"']{0,})|((?:/|\.\./|\./)[^"'><,;|*()(%%$^/\\\[\]][^"'><,;|()]{1,})|([a-zA-Z0-9_\-/]{1,}/[a-zA-Z0-9_\-/]{1,}\.(?:[a-zA-Z]{1,4}|action)(?:[\?|#][^"|']{0,}|))|([a-zA-Z0-9_\-/]{1,}/[a-zA-Z0-9_\-/]{3,}(?:[\?|#][^"|']{0,}|))|([a-zA-Z0-9_\-]{1,}\.(?:\w)(?:[\?|#][^"|']{0,}|)))(?:"|') - s_regex: '' - format: '{0}' - color: gray - scope: response body - engine: nfa - sensitive: true - - name: Source Map - loaded: true - f_regex: (\.js\.map) - s_regex: '' - format: '{0}' - color: pink - scope: response body - engine: dfa - sensitive: false - - name: Create Script - loaded: true - f_regex: (\{[^{}]*\}\s*\[[^\s]*\]\s*\+\s*"[^\s]*\.js") - s_regex: '"?([\w].*?)"?:"(.*?)"' - format: '{0}.{1}' - color: green - scope: response body - engine: nfa - sensitive: false - - name: URL Schemes - loaded: true - f_regex: (\b(?![\w]{0,10}?https?://)(([-A-Za-z0-9]{1,20})://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|])) - s_regex: '' - format: '{0}' - color: yellow - scope: response body - engine: nfa - sensitive: false - - name: Router Push - loaded: true - f_regex: (\$router\.push) - s_regex: '' - format: '{0}' - color: magenta - scope: response body - engine: dfa - sensitive: false - - name: All URL - loaded: true - f_regex: (https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;\u4E00-\u9FFF]+[-A-Za-z0-9+&@#/%=~_|]) - s_regex: '' - format: '{0}' - color: gray - scope: response body - engine: nfa - sensitive: true - - name: Request URI - loaded: true - f_regex: ' ((?!.*\.js(\?.*)?$)(.*?[^.js$])) ' - s_regex: '' - format: '{0}' - color: gray - scope: request line - engine: nfa - sensitive: false - - name: 302 Location - loaded: true - f_regex: 'Location: (.*?)\n' - s_regex: '' - format: '{0}' - color: gray - scope: response header - engine: nfa - sensitive: false + - group: Fingerprint + rule: + - name: Shiro + loaded: true + f_regex: (=deleteMe|rememberMe=) + s_regex: '' + format: '{0}' + color: green + scope: any header + engine: dfa + sensitive: true + - name: JSON Web Token + loaded: true + f_regex: (eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9._-]{10,}|eyJ[A-Za-z0-9_\/+-]{10,}\.[A-Za-z0-9._\/+-]{10,}) + s_regex: '' + format: '{0}' + color: green + scope: any + engine: nfa + sensitive: true + - name: Swagger UI + loaded: true + f_regex: ((swagger-ui.html)|(\"swagger\":)|(Swagger UI)|(swaggerUi)|(swaggerVersion)) + s_regex: '' + format: '{0}' + color: red + scope: response body + engine: dfa + sensitive: false + - name: Ueditor + loaded: true + f_regex: (ueditor\.(config|all)\.js) + s_regex: '' + format: '{0}' + color: green + scope: response body + engine: dfa + sensitive: false + - name: Druid + loaded: true + f_regex: (Druid Stat Index) + s_regex: '' + format: '{0}' + color: orange + scope: response body + engine: dfa + sensitive: false + - group: Maybe Vulnerability + rule: + - name: Java Deserialization + loaded: true + f_regex: (javax\.faces\.ViewState) + s_regex: '' + format: '{0}' + color: yellow + scope: response body + engine: dfa + sensitive: false + - name: Debug Logic Parameters + loaded: true + f_regex: ((access=)|(adm=)|(admin=)|(alter=)|(cfg=)|(clone=)|(config=)|(create=)|(dbg=)|(debug=)|(delete=)|(disable=)|(edit=)|(enable=)|(exec=)|(execute=)|(grant=)|(load=)|(make=)|(modify=)|(rename=)|(reset=)|(root=)|(shell=)|(test=)|(toggl=)) + s_regex: '' + format: '{0}' + color: cyan + scope: request + engine: dfa + sensitive: false + - name: URL As A Value + loaded: true + f_regex: (=(https?)(://|%3a%2f%2f)) + s_regex: '' + format: '{0}' + color: cyan + scope: any + engine: nfa + sensitive: false + - name: Upload Form + loaded: true + f_regex: (type\=\"file\") + s_regex: '' + format: '{0}' + color: yellow + scope: response body + engine: dfa + sensitive: false + - name: DoS Paramters + loaded: true + f_regex: ((size=)|(page=)|(num=)|(limit=)|(start=)|(end=)|(count=)) + s_regex: '' + format: '{0}' + color: cyan + scope: request + engine: dfa + sensitive: false + - group: Basic Information + rule: + - name: Email + loaded: true + f_regex: (([a-z0-9]+[_|\.])*[a-z0-9]+@([a-z0-9]+[-|_|\.])*[a-z0-9]+\.((?!js|css|jpg|jpeg|png|ico)[a-z]{2,5})) + s_regex: '' + format: '{0}' + color: yellow + scope: response + engine: nfa + sensitive: false + - name: Chinese IDCard + loaded: true + f_regex: '[^0-9]((\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(\d{6}(18|19|20)\d{2}(0[1-9]|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)))[^0-9]' + s_regex: '' + format: '{0}' + color: orange + scope: response body + engine: nfa + sensitive: true + - name: Chinese Mobile Number + loaded: true + f_regex: '[^\w]((?:(?:\+|0{0,2})86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8})[^\w]' + s_regex: '' + format: '{0}' + color: orange + scope: response body + engine: nfa + sensitive: false + - name: Internal IP Address + loaded: true + f_regex: '[^0-9]((127\.0\.0\.1)|(10\.\d{1,3}\.\d{1,3}\.\d{1,3})|(172\.((1[6-9])|(2\d)|(3[01]))\.\d{1,3}\.\d{1,3})|(192\.168\.\d{1,3}\.\d{1,3}))' + s_regex: '' + format: '{0}' + color: cyan + scope: response + engine: nfa + sensitive: true + - name: MAC Address + loaded: true + f_regex: (^([a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5})|[^a-zA-Z0-9]([a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5})) + s_regex: '' + format: '{0}' + color: green + scope: response + engine: nfa + sensitive: true + - group: Sensitive Information + rule: + - name: Cloud Key + loaded: true + f_regex: (((access)(|-|_)(key)(|-|_)(id|secret))|(LTAI[a-z0-9]{12,20})) + s_regex: '' + format: '{0}' + color: yellow + scope: any + engine: nfa + sensitive: false + - name: Windows File/Dir Path + loaded: true + f_regex: '[^\w]([a-zA-Z]:\\\\?(?:[^<>:/\\|?*]+\\\\?)*)([^<>:/\\|?*]+(?:\.[^<>:/\\|?*]+)?)' + s_regex: '' + format: '{0}' + color: green + scope: response + engine: nfa + sensitive: true + - name: Password Field + loaded: true + f_regex: ((|\\)(|'|")(|[\w]{1,10})([p](ass|wd|asswd|assword))(|[\w]{1,10})(|\\)(|'|")(:|=|\)\.val\()( + |)(|\\)('|")([^'"]+?)(|\\)('|")(|,|\))) + s_regex: '' + format: '{0}' + color: yellow + scope: response body + engine: nfa + sensitive: false + - name: Username Field + loaded: true + f_regex: ((|\\)(|'|")(|[\w]{1,10})(([u](ser|name|sername))|(account)|((((create|update)((d|r)|(by|on|at)))|(creator))))(|[\w]{1,10})(|\\)(|'|")(:|=|\)\.val\()( + |)(|\\)('|")([^'"]+?)(|\\)('|")(|,|\))) + s_regex: '' + format: '{0}' + color: green + scope: response body + engine: nfa + sensitive: false + - name: WeCom Key + loaded: true + f_regex: ((corp)(id|secret)) + s_regex: '' + format: '{0}' + color: green + scope: response body + engine: dfa + sensitive: false + - name: JDBC Connection + loaded: true + f_regex: (jdbc:[a-z:]+://[a-z0-9\.\-_:;=/@?,&]+) + s_regex: '' + format: '{0}' + color: yellow + scope: any + engine: nfa + sensitive: false + - name: Authorization Header + loaded: true + f_regex: ((basic [a-z0-9=:_\+\/-]{5,100})|(bearer [a-z0-9_.=:_\+\/-]{5,100})) + s_regex: '' + format: '{0}' + color: yellow + scope: response body + engine: nfa + sensitive: false + - name: Sensitive Field + loaded: true + f_regex: ((\[)?('|")?([\w]{0,10})((key)|(secret)|(token)|(config)|(auth)|(access)|(admin)|(ticket))([\w]{0,10})('|")?(\])?( + |)(:|=|\)\.val\()( |)('|")([^'"]+?)('|")(|,|\))) + s_regex: '' + format: '{0}' + color: yellow + scope: response + engine: nfa + sensitive: false + - name: Mobile Number Field + loaded: true + f_regex: ((|\\)(|'|")(|[\w]{1,10})(mobile|phone|sjh|shoujihao|concat)(|[\w]{1,10})(|\\)(|'|")(:|=|\)\.val\()( + |)(|\\)('|")([^'"]+?)(|\\)('|")(|,|\))) + s_regex: '' + format: '{0}' + color: green + scope: response body + engine: nfa + sensitive: false + - group: Other + rule: + - name: Linkfinder + loaded: true + f_regex: (?:"|')(((?:[a-zA-Z]{1,10}://|//)[^"'/]{1,}\.[a-zA-Z]{2,}[^"']{0,})|((?:/|\.\./|\./)[^"'><,;|*()(%%$^/\\\[\]][^"'><,;|()]{1,})|([a-zA-Z0-9_\-/]{1,}/[a-zA-Z0-9_\-/]{1,}\.(?:[a-zA-Z]{1,4}|action)(?:[\?|#][^"|']{0,}|))|([a-zA-Z0-9_\-/]{1,}/[a-zA-Z0-9_\-/]{3,}(?:[\?|#][^"|']{0,}|))|([a-zA-Z0-9_\-]{1,}\.(?:\w)(?:[\?|#][^"|']{0,}|)))(?:"|') + s_regex: '' + format: '{0}' + color: gray + scope: response body + engine: nfa + sensitive: true + - name: Source Map + loaded: true + f_regex: (\.js\.map) + s_regex: '' + format: '{0}' + color: pink + scope: response body + engine: dfa + sensitive: false + - name: Create Script + loaded: true + f_regex: (\{[^{}]*\}\s*\[[^\s]*\]\s*\+\s*"[^\s]*\.js") + s_regex: '"?([\w].*?)"?:"(.*?)"' + format: '{0}.{1}' + color: green + scope: response body + engine: nfa + sensitive: false + - name: URL Schemes + loaded: true + f_regex: (\b(?![\w]{0,10}?https?://)(([-A-Za-z0-9]{1,20})://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|])) + s_regex: '' + format: '{0}' + color: yellow + scope: response body + engine: nfa + sensitive: false + - name: Router Push + loaded: true + f_regex: (\$router\.push) + s_regex: '' + format: '{0}' + color: magenta + scope: response body + engine: dfa + sensitive: false + - name: All URL + loaded: true + f_regex: (https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;\u4E00-\u9FFF]+[-A-Za-z0-9+&@#/%=~_|]) + s_regex: '' + format: '{0}' + color: gray + scope: response body + engine: nfa + sensitive: true + - name: Request URI + loaded: true + f_regex: ' ((?!.*\.js(\?.*)?$)(.*?[^.js$])) ' + s_regex: '' + format: '{0}' + color: gray + scope: request line + engine: nfa + sensitive: false + - name: 302 Location + loaded: true + f_regex: 'Location: (.*?)\n' + s_regex: '' + format: '{0}' + color: gray + scope: response header + engine: nfa + sensitive: false