Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa35b0a625 | ||
|
|
8ef98d20a9 | ||
|
|
e556abb6f7 | ||
|
|
471aab5ea1 | ||
|
|
76b475bd91 | ||
|
|
6014089594 | ||
|
|
910658f2e0 | ||
|
|
8692b0a494 | ||
|
|
5419d4a679 | ||
|
|
ae8cb2fd25 | ||
|
|
5b6bdbe5b6 | ||
|
|
ddb08e9a6e | ||
|
|
6a2f289d57 | ||
|
|
84746a7089 | ||
|
|
68f0bce619 | ||
|
|
4f0401347c | ||
|
|
a7e0a2a6ce | ||
|
|
b7c5a8363d |
4
.github/ISSUE_TEMPLATE/问题反馈.md
vendored
4
.github/ISSUE_TEMPLATE/问题反馈.md
vendored
@@ -14,7 +14,9 @@ HaE 版本:
|
|||||||
有无自定义规则:
|
有无自定义规则:
|
||||||
BurpSuite 版本:
|
BurpSuite 版本:
|
||||||
操作系统版本:
|
操作系统版本:
|
||||||
有无仔细阅读README:
|
是否阅读README:
|
||||||
|
是否知晓注意事项:
|
||||||
|
是否查阅历史ISSUE:
|
||||||
```
|
```
|
||||||
|
|
||||||
## 问题详情
|
## 问题详情
|
||||||
|
|||||||
@@ -12,6 +12,15 @@
|
|||||||
|
|
||||||
> 随着现代化Web应用采用前后端分离的开发模式,日常漏洞挖掘的过程中,捕获的HTTP请求流量也相应增加。若想全面评估一个Web应用,会花费大量时间在无用的报文上。**HaE的出现旨在解决这类情况**,借助HaE,您能够**有效减少**测试时间,将更多精力集中在**有价值且有意义**的报文上,从而**提高漏洞挖掘效率**。
|
> 随着现代化Web应用采用前后端分离的开发模式,日常漏洞挖掘的过程中,捕获的HTTP请求流量也相应增加。若想全面评估一个Web应用,会花费大量时间在无用的报文上。**HaE的出现旨在解决这类情况**,借助HaE,您能够**有效减少**测试时间,将更多精力集中在**有价值且有意义**的报文上,从而**提高漏洞挖掘效率**。
|
||||||
|
|
||||||
|
GitHub项目地址:https://github.com/gh0stkey/HaE
|
||||||
|
|
||||||
|
GitCode项目地址:https://gitcode.com/gh0stkey/HaE
|
||||||
|
|
||||||
|
**所获荣誉**:
|
||||||
|
|
||||||
|
1. [入选2022年KCon兵器谱](https://mp.weixin.qq.com/s/JohMsl1WD29LHCHuLf8mVQ)
|
||||||
|
2. [入选GitCode G-Star项目](https://gitcode.com/gh0stkey/HaE)
|
||||||
|
|
||||||
**注意事项**:
|
**注意事项**:
|
||||||
|
|
||||||
1. HaE 3.3版本开启了AI+新功能,该功能目前仅支持阿里的`Qwen-Long`模型(支持超长文本)和月之暗面的`moonshot-v1-128k`模型(支持短文本),请配置和使用时注意。
|
1. HaE 3.3版本开启了AI+新功能,该功能目前仅支持阿里的`Qwen-Long`模型(支持超长文本)和月之暗面的`moonshot-v1-128k`模型(支持短文本),请配置和使用时注意。
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ dependencies {
|
|||||||
implementation 'org.yaml:snakeyaml:2.0'
|
implementation 'org.yaml:snakeyaml:2.0'
|
||||||
implementation 'dk.brics.automaton:automaton:1.11-8'
|
implementation 'dk.brics.automaton:automaton:1.11-8'
|
||||||
implementation 'com.github.ben-manes.caffeine:caffeine:3.1.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 {
|
test {
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 157 KiB After Width: | Height: | Size: 167 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 320 KiB After Width: | Height: | Size: 187 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 331 KiB After Width: | Height: | Size: 318 KiB |
@@ -10,6 +10,12 @@ public class Config {
|
|||||||
|
|
||||||
public static String host = "gh0st.cn";
|
public static String host = "gh0st.cn";
|
||||||
|
|
||||||
|
public static String status = "404";
|
||||||
|
|
||||||
|
public static String size = "0";
|
||||||
|
|
||||||
|
public static String boundary = "\n\t\n";
|
||||||
|
|
||||||
public static String[] scope = new String[]{
|
public static String[] scope = new String[]{
|
||||||
"any",
|
"any",
|
||||||
"any header",
|
"any header",
|
||||||
@@ -26,6 +32,8 @@ public class Config {
|
|||||||
|
|
||||||
public static String scopeOptions = "Suite|Target|Proxy|Scanner|Intruder|Repeater|Logger|Sequencer|Decoder|Comparer|Extensions|Organizer|Recorded login replayer";
|
public static String scopeOptions = "Suite|Target|Proxy|Scanner|Intruder|Repeater|Logger|Sequencer|Decoder|Comparer|Extensions|Organizer|Recorded login replayer";
|
||||||
|
|
||||||
|
public static String modeStatus = "true";
|
||||||
|
|
||||||
public static String[] ruleFields = {
|
public static String[] ruleFields = {
|
||||||
"Loaded", "Name", "F-Regex", "S-Regex", "Format", "Color", "Scope", "Engine", "Sensitive"
|
"Loaded", "Name", "F-Regex", "S-Regex", "Format", "Color", "Scope", "Engine", "Sensitive"
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import hae.component.board.message.MessageTableModel;
|
|||||||
import hae.instances.editor.RequestEditor;
|
import hae.instances.editor.RequestEditor;
|
||||||
import hae.instances.editor.ResponseEditor;
|
import hae.instances.editor.ResponseEditor;
|
||||||
import hae.instances.editor.WebSocketEditor;
|
import hae.instances.editor.WebSocketEditor;
|
||||||
import hae.instances.http.HttpMessageHandler;
|
|
||||||
import hae.instances.websocket.WebSocketMessageHandler;
|
import hae.instances.websocket.WebSocketMessageHandler;
|
||||||
import hae.utils.ConfigLoader;
|
import hae.utils.ConfigLoader;
|
||||||
|
|
||||||
@@ -18,7 +17,7 @@ public class HaE implements BurpExtension {
|
|||||||
@Override
|
@Override
|
||||||
public void initialize(MontoyaApi api) {
|
public void initialize(MontoyaApi api) {
|
||||||
// 设置扩展名称
|
// 设置扩展名称
|
||||||
String version = "3.3";
|
String version = "3.4";
|
||||||
api.extension().setName(String.format("HaE (%s) - Highlighter and Extractor", version));
|
api.extension().setName(String.format("HaE (%s) - Highlighter and Extractor", version));
|
||||||
|
|
||||||
// 加载扩展后输出的项目信息
|
// 加载扩展后输出的项目信息
|
||||||
@@ -30,14 +29,11 @@ public class HaE implements BurpExtension {
|
|||||||
// 配置文件加载
|
// 配置文件加载
|
||||||
ConfigLoader configLoader = new ConfigLoader(api);
|
ConfigLoader configLoader = new ConfigLoader(api);
|
||||||
|
|
||||||
MessageTableModel messageTableModel = new MessageTableModel(api);
|
MessageTableModel messageTableModel = new MessageTableModel(api, configLoader);
|
||||||
|
|
||||||
// 注册Tab页(用于查询数据)
|
// 注册Tab页(用于查询数据)
|
||||||
api.userInterface().registerSuiteTab("HaE", new Main(api, configLoader, messageTableModel));
|
api.userInterface().registerSuiteTab("HaE", new Main(api, configLoader, messageTableModel));
|
||||||
|
|
||||||
// 注册HTTP处理器
|
|
||||||
api.http().registerHttpHandler(new HttpMessageHandler(api, configLoader, messageTableModel));
|
|
||||||
|
|
||||||
// 注册WebSocket处理器
|
// 注册WebSocket处理器
|
||||||
api.proxy().registerWebSocketCreationHandler(proxyWebSocketCreation -> proxyWebSocketCreation.proxyWebSocket().registerProxyMessageHandler(new WebSocketMessageHandler(api)));
|
api.proxy().registerWebSocketCreationHandler(proxyWebSocketCreation -> proxyWebSocketCreation.proxyWebSocket().registerProxyMessageHandler(new WebSocketMessageHandler(api)));
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
package hae.component.config;
|
package hae.component;
|
||||||
|
|
||||||
import burp.api.montoya.MontoyaApi;
|
import burp.api.montoya.MontoyaApi;
|
||||||
|
import burp.api.montoya.core.Registration;
|
||||||
|
import hae.component.board.message.MessageTableModel;
|
||||||
import hae.component.rule.Rules;
|
import hae.component.rule.Rules;
|
||||||
|
import hae.instances.http.HttpMessageActiveHandler;
|
||||||
|
import hae.instances.http.HttpMessagePassiveHandler;
|
||||||
import hae.utils.ConfigLoader;
|
import hae.utils.ConfigLoader;
|
||||||
import hae.utils.UIEnhancer;
|
import hae.utils.UIEnhancer;
|
||||||
|
|
||||||
@@ -16,28 +20,28 @@ import javax.swing.table.DefaultTableModel;
|
|||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.datatransfer.Clipboard;
|
import java.awt.datatransfer.Clipboard;
|
||||||
import java.awt.datatransfer.DataFlavor;
|
import java.awt.datatransfer.DataFlavor;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.*;
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.KeyAdapter;
|
|
||||||
import java.awt.event.KeyEvent;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class Config extends JPanel {
|
public class Config extends JPanel {
|
||||||
private final MontoyaApi api;
|
private final MontoyaApi api;
|
||||||
private final ConfigLoader configLoader;
|
private final ConfigLoader configLoader;
|
||||||
|
private final MessageTableModel messageTableModel;
|
||||||
private final Rules rules;
|
private final Rules rules;
|
||||||
private JTextField addTextField;
|
|
||||||
private final String defaultText = "Enter a new item";
|
private final String defaultText = "Enter a new item";
|
||||||
private final GridBagConstraints constraints = new GridBagConstraints();
|
|
||||||
|
|
||||||
public Config(MontoyaApi api, ConfigLoader configLoader, Rules rules) {
|
private Registration activeHandler;
|
||||||
|
private Registration passiveHandler;
|
||||||
|
|
||||||
|
public Config(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel, Rules rules) {
|
||||||
this.api = api;
|
this.api = api;
|
||||||
this.configLoader = configLoader;
|
this.configLoader = configLoader;
|
||||||
|
this.messageTableModel = messageTableModel;
|
||||||
this.rules = rules;
|
this.rules = rules;
|
||||||
|
|
||||||
constraints.weightx = 1.0;
|
this.activeHandler = api.http().registerHttpHandler(new HttpMessageActiveHandler(api, configLoader, messageTableModel));
|
||||||
constraints.fill = GridBagConstraints.HORIZONTAL;
|
this.passiveHandler = api.scanner().registerScanCheck(new HttpMessagePassiveHandler(api, configLoader, messageTableModel));
|
||||||
|
|
||||||
initComponents();
|
initComponents();
|
||||||
}
|
}
|
||||||
@@ -45,6 +49,10 @@ public class Config extends JPanel {
|
|||||||
private void initComponents() {
|
private void initComponents() {
|
||||||
setLayout(new BorderLayout());
|
setLayout(new BorderLayout());
|
||||||
|
|
||||||
|
GridBagConstraints constraints = new GridBagConstraints();
|
||||||
|
constraints.weightx = 1.0;
|
||||||
|
constraints.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
|
||||||
JPanel ruleInfoPanel = new JPanel(new GridBagLayout());
|
JPanel ruleInfoPanel = new JPanel(new GridBagLayout());
|
||||||
ruleInfoPanel.setBorder(new EmptyBorder(10, 15, 5, 15));
|
ruleInfoPanel.setBorder(new EmptyBorder(10, 15, 5, 15));
|
||||||
|
|
||||||
@@ -67,12 +75,35 @@ public class Config extends JPanel {
|
|||||||
constraints.gridx = 1;
|
constraints.gridx = 1;
|
||||||
JTabbedPane configTabbedPanel = new JTabbedPane();
|
JTabbedPane configTabbedPanel = new JTabbedPane();
|
||||||
|
|
||||||
String[] settingMode = new String[]{"Exclude suffix", "Block host"};
|
String[] settingMode = new String[]{"Exclude suffix", "Block host", "Exclude status"};
|
||||||
JPanel settingPanel = createConfigTablePanel(settingMode, "Setting");
|
JPanel settingPanel = createConfigTablePanel(settingMode, "Setting");
|
||||||
|
|
||||||
|
JPanel northPanel = new JPanel(new BorderLayout());
|
||||||
|
|
||||||
|
JPanel modePanel = getModePanel();
|
||||||
|
JScrollPane modeScrollPane = new JScrollPane(modePanel);
|
||||||
|
modeScrollPane.setBorder(new TitledBorder("Mode"));
|
||||||
|
|
||||||
|
JTextField limitPanel = getLimitPanel();
|
||||||
|
JScrollPane limitScrollPane = new JScrollPane(limitPanel);
|
||||||
|
limitScrollPane.setBorder(new TitledBorder("Limit Size (MB)"));
|
||||||
|
|
||||||
|
JSplitPane northTopPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, modeScrollPane, limitScrollPane);
|
||||||
|
northTopPanel.addComponentListener(new ComponentAdapter() {
|
||||||
|
@Override
|
||||||
|
public void componentResized(ComponentEvent e) {
|
||||||
|
northTopPanel.setDividerLocation(0.5);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
JPanel scopePanel = getScopePanel();
|
JPanel scopePanel = getScopePanel();
|
||||||
JScrollPane scopeScrollPane = new JScrollPane(scopePanel);
|
JScrollPane scopeScrollPane = new JScrollPane(scopePanel);
|
||||||
scopeScrollPane.setBorder(new TitledBorder("Scope"));
|
scopeScrollPane.setBorder(new TitledBorder("Scope"));
|
||||||
settingPanel.add(scopeScrollPane, BorderLayout.NORTH);
|
|
||||||
|
northPanel.add(scopeScrollPane, BorderLayout.SOUTH);
|
||||||
|
northPanel.add(northTopPanel, BorderLayout.NORTH);
|
||||||
|
settingPanel.add(northPanel, BorderLayout.NORTH);
|
||||||
|
|
||||||
configTabbedPanel.add("Setting", settingPanel);
|
configTabbedPanel.add("Setting", settingPanel);
|
||||||
|
|
||||||
String[] aiMode = new String[]{"Alibaba", "Moonshot"};
|
String[] aiMode = new String[]{"Alibaba", "Moonshot"};
|
||||||
@@ -113,23 +144,67 @@ public class Config extends JPanel {
|
|||||||
private JPanel getScopePanel() {
|
private JPanel getScopePanel() {
|
||||||
JPanel scopePanel = new JPanel();
|
JPanel scopePanel = new JPanel();
|
||||||
scopePanel.setLayout(new BoxLayout(scopePanel, BoxLayout.X_AXIS));
|
scopePanel.setLayout(new BoxLayout(scopePanel, BoxLayout.X_AXIS));
|
||||||
|
scopePanel.setBorder(new EmptyBorder(3, 0, 6, 0));
|
||||||
|
|
||||||
String[] scopeInit = hae.Config.scopeOptions.split("\\|");
|
String[] scopeInit = hae.Config.scopeOptions.split("\\|");
|
||||||
String[] scopeMode = configLoader.getScope().split("\\|");
|
String[] scopeMode = configLoader.getScope().split("\\|");
|
||||||
for (String scope : scopeInit) {
|
for (String scope : scopeInit) {
|
||||||
JCheckBox checkBox = new JCheckBox(scope);
|
JCheckBox checkBox = new JCheckBox(scope);
|
||||||
scopePanel.add(checkBox);
|
scopePanel.add(checkBox);
|
||||||
|
checkBox.addActionListener(e -> updateScope(checkBox));
|
||||||
for (String mode : scopeMode) {
|
for (String mode : scopeMode) {
|
||||||
if (scope.equals(mode)) {
|
if (scope.equals(mode)) {
|
||||||
checkBox.setSelected(true);
|
checkBox.setSelected(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
updateScope(checkBox);
|
||||||
checkBox.addActionListener(e -> updateScope(checkBox));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return scopePanel;
|
return scopePanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private JPanel getModePanel() {
|
||||||
|
JPanel modePanel = new JPanel();
|
||||||
|
modePanel.setLayout(new BoxLayout(modePanel, BoxLayout.X_AXIS));
|
||||||
|
|
||||||
|
JCheckBox checkBox = new JCheckBox("Enable active http message handler");
|
||||||
|
modePanel.add(checkBox);
|
||||||
|
checkBox.addActionListener(e -> updateModeStatus(checkBox));
|
||||||
|
checkBox.setSelected(configLoader.getMode());
|
||||||
|
updateModeStatus(checkBox);
|
||||||
|
|
||||||
|
return modePanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JTextField getLimitPanel() {
|
||||||
|
JTextField limitSizeTextField = new JTextField();
|
||||||
|
limitSizeTextField.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 limitSizeText = limitSizeTextField.getText();
|
||||||
|
configLoader.setLimitSize(limitSizeText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
limitSizeTextField.setText(configLoader.getLimitSize());
|
||||||
|
|
||||||
|
return limitSizeTextField;
|
||||||
|
}
|
||||||
|
|
||||||
private TableModelListener craeteSettingTableModelListener(JComboBox<String> setTypeComboBox, DefaultTableModel model) {
|
private TableModelListener craeteSettingTableModelListener(JComboBox<String> setTypeComboBox, DefaultTableModel model) {
|
||||||
return new TableModelListener() {
|
return new TableModelListener() {
|
||||||
@Override
|
@Override
|
||||||
@@ -148,6 +223,13 @@ public class Config extends JPanel {
|
|||||||
configLoader.setBlockHost(values);
|
configLoader.setBlockHost(values);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selected.equals("Exclude status")) {
|
||||||
|
if (!values.equals(configLoader.getExcludeStatus()) && !values.isEmpty()) {
|
||||||
|
configLoader.setExcludeStatus(values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -166,6 +248,10 @@ public class Config extends JPanel {
|
|||||||
if (selected.equals("Block host")) {
|
if (selected.equals("Block host")) {
|
||||||
addDataToTable(configLoader.getBlockHost().replaceAll("\\|", "\r\n"), model);
|
addDataToTable(configLoader.getBlockHost().replaceAll("\\|", "\r\n"), model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selected.equals("Exclude status")) {
|
||||||
|
addDataToTable(configLoader.getExcludeStatus().replaceAll("\\|", "\r\n"), model);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -211,6 +297,10 @@ public class Config extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private JPanel createConfigTablePanel(String[] mode, String type) {
|
private JPanel createConfigTablePanel(String[] mode, String type) {
|
||||||
|
GridBagConstraints constraints = new GridBagConstraints();
|
||||||
|
constraints.weightx = 1.0;
|
||||||
|
constraints.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
|
||||||
JPanel settingPanel = new JPanel(new BorderLayout());
|
JPanel settingPanel = new JPanel(new BorderLayout());
|
||||||
DefaultTableModel model = new DefaultTableModel();
|
DefaultTableModel model = new DefaultTableModel();
|
||||||
|
|
||||||
@@ -237,12 +327,12 @@ public class Config extends JPanel {
|
|||||||
JComboBox<String> setTypeComboBox = new JComboBox<>();
|
JComboBox<String> setTypeComboBox = new JComboBox<>();
|
||||||
setTypeComboBox.setModel(new DefaultComboBoxModel<>(mode));
|
setTypeComboBox.setModel(new DefaultComboBoxModel<>(mode));
|
||||||
|
|
||||||
|
model.addTableModelListener(type.equals("AI+") ? craeteAITableModelListener(setTypeComboBox, model) : craeteSettingTableModelListener(setTypeComboBox, model));
|
||||||
|
|
||||||
setTypeComboBox.addActionListener(type.equals("AI+") ? createAIActionListener(setTypeComboBox, model) : createSettingActionListener(setTypeComboBox, model));
|
setTypeComboBox.addActionListener(type.equals("AI+") ? createAIActionListener(setTypeComboBox, model) : createSettingActionListener(setTypeComboBox, model));
|
||||||
|
|
||||||
setTypeComboBox.setSelectedItem(mode[0]);
|
setTypeComboBox.setSelectedItem(mode[0]);
|
||||||
|
|
||||||
model.addTableModelListener(type.equals("AI+") ? craeteAITableModelListener(setTypeComboBox, model) : craeteSettingTableModelListener(setTypeComboBox, model));
|
|
||||||
|
|
||||||
constraints.insets = new Insets(0, 0, 3, 0);
|
constraints.insets = new Insets(0, 0, 3, 0);
|
||||||
constraints.gridy = 0;
|
constraints.gridy = 0;
|
||||||
buttonPanel.add(setTypeComboBox, constraints);
|
buttonPanel.add(setTypeComboBox, constraints);
|
||||||
@@ -255,7 +345,7 @@ public class Config extends JPanel {
|
|||||||
constraints.gridy = 4;
|
constraints.gridy = 4;
|
||||||
buttonPanel.add(clearButton, constraints);
|
buttonPanel.add(clearButton, constraints);
|
||||||
|
|
||||||
addTextField = new JTextField();
|
JTextField addTextField = new JTextField();
|
||||||
UIEnhancer.setTextFieldPlaceholder(addTextField, defaultText);
|
UIEnhancer.setTextFieldPlaceholder(addTextField, defaultText);
|
||||||
|
|
||||||
inputPanelB.add(addTextField, BorderLayout.CENTER);
|
inputPanelB.add(addTextField, BorderLayout.CENTER);
|
||||||
@@ -266,13 +356,13 @@ public class Config extends JPanel {
|
|||||||
settingPanel.add(inputPanel, BorderLayout.CENTER);
|
settingPanel.add(inputPanel, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
|
||||||
addButton.addActionListener(e -> addActionPerformed(e, model));
|
addButton.addActionListener(e -> addActionPerformed(e, model, addTextField, setTypeComboBox.getSelectedItem().toString()));
|
||||||
|
|
||||||
addTextField.addKeyListener(new KeyAdapter() {
|
addTextField.addKeyListener(new KeyAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void keyPressed(KeyEvent e) {
|
public void keyPressed(KeyEvent e) {
|
||||||
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
||||||
addActionPerformed(null, model);
|
addActionPerformed(null, model, addTextField, setTypeComboBox.getSelectedItem().toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -281,7 +371,6 @@ public class Config extends JPanel {
|
|||||||
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||||
try {
|
try {
|
||||||
String data = (String) clipboard.getData(DataFlavor.stringFlavor);
|
String data = (String) clipboard.getData(DataFlavor.stringFlavor);
|
||||||
|
|
||||||
if (data != null && !data.isEmpty()) {
|
if (data != null && !data.isEmpty()) {
|
||||||
addDataToTable(data, model);
|
addDataToTable(data, model);
|
||||||
}
|
}
|
||||||
@@ -298,7 +387,6 @@ public class Config extends JPanel {
|
|||||||
|
|
||||||
clearButton.addActionListener(e -> model.setRowCount(0));
|
clearButton.addActionListener(e -> model.setRowCount(0));
|
||||||
|
|
||||||
|
|
||||||
JPanel settingMainPanel = new JPanel(new BorderLayout());
|
JPanel settingMainPanel = new JPanel(new BorderLayout());
|
||||||
settingMainPanel.setBorder(new EmptyBorder(5, 15, 10, 15));
|
settingMainPanel.setBorder(new EmptyBorder(5, 15, 10, 15));
|
||||||
JScrollPane settingScroller = new JScrollPane(settingPanel);
|
JScrollPane settingScroller = new JScrollPane(settingPanel);
|
||||||
@@ -357,6 +445,29 @@ public class Config extends JPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateModeStatus(JCheckBox checkBox) {
|
||||||
|
boolean selected = checkBox.isSelected();
|
||||||
|
configLoader.setMode(selected ? "true" : "false");
|
||||||
|
|
||||||
|
if (checkBox.isSelected()) {
|
||||||
|
if (passiveHandler.isRegistered()) {
|
||||||
|
passiveHandler.deregister();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!activeHandler.isRegistered()) {
|
||||||
|
activeHandler = api.http().registerHttpHandler(new HttpMessageActiveHandler(api, configLoader, messageTableModel));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!passiveHandler.isRegistered()) {
|
||||||
|
passiveHandler = api.scanner().registerScanCheck(new HttpMessagePassiveHandler(api, configLoader, messageTableModel));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeHandler.isRegistered()) {
|
||||||
|
activeHandler.deregister();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void updateScope(JCheckBox checkBox) {
|
public void updateScope(JCheckBox checkBox) {
|
||||||
String boxText = checkBox.getText();
|
String boxText = checkBox.getText();
|
||||||
boolean selected = checkBox.isSelected();
|
boolean selected = checkBox.isSelected();
|
||||||
@@ -372,13 +483,13 @@ public class Config extends JPanel {
|
|||||||
configLoader.setScope(String.join("|", HaEScope));
|
configLoader.setScope(String.join("|", HaEScope));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addActionPerformed(ActionEvent e, DefaultTableModel model) {
|
private void addActionPerformed(ActionEvent e, DefaultTableModel model, JTextField addTextField, String comboBoxSelected) {
|
||||||
String addTextFieldText = addTextField.getText();
|
String addTextFieldText = addTextField.getText();
|
||||||
if (!addTextFieldText.equals(defaultText)) {
|
if (addTextField.getForeground().equals(Color.BLACK)) {
|
||||||
addDataToTable(addTextFieldText, model);
|
addDataToTable(addTextFieldText, model);
|
||||||
|
addTextField.setText("");
|
||||||
|
addTextField.requestFocusInWindow();
|
||||||
}
|
}
|
||||||
addTextField.setText("");
|
|
||||||
addTextField.requestFocusInWindow();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onlineUpdateActionPerformed(ActionEvent e) {
|
private void onlineUpdateActionPerformed(ActionEvent e) {
|
||||||
@@ -3,7 +3,6 @@ package hae.component;
|
|||||||
import burp.api.montoya.MontoyaApi;
|
import burp.api.montoya.MontoyaApi;
|
||||||
import hae.component.board.Databoard;
|
import hae.component.board.Databoard;
|
||||||
import hae.component.board.message.MessageTableModel;
|
import hae.component.board.message.MessageTableModel;
|
||||||
import hae.component.config.Config;
|
|
||||||
import hae.component.rule.Rules;
|
import hae.component.rule.Rules;
|
||||||
import hae.utils.ConfigLoader;
|
import hae.utils.ConfigLoader;
|
||||||
|
|
||||||
@@ -58,7 +57,7 @@ public class Main extends JPanel {
|
|||||||
Rules rules = new Rules(api, configLoader);
|
Rules rules = new Rules(api, configLoader);
|
||||||
mainTabbedPane.addTab("Rules", rules);
|
mainTabbedPane.addTab("Rules", rules);
|
||||||
mainTabbedPane.addTab("Databoard", new Databoard(api, configLoader, messageTableModel));
|
mainTabbedPane.addTab("Databoard", new Databoard(api, configLoader, messageTableModel));
|
||||||
mainTabbedPane.addTab("Config", new Config(api, configLoader, rules));
|
mainTabbedPane.addTab("Config", new Config(api, configLoader, messageTableModel, rules));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isDarkBg(JTabbedPane HaETabbedPane) {
|
private boolean isDarkBg(JTabbedPane HaETabbedPane) {
|
||||||
|
|||||||
@@ -8,14 +8,17 @@ import hae.component.board.message.MessageTableModel.MessageTable;
|
|||||||
import hae.component.board.table.Datatable;
|
import hae.component.board.table.Datatable;
|
||||||
import hae.instances.http.utils.RegularMatcher;
|
import hae.instances.http.utils.RegularMatcher;
|
||||||
import hae.utils.ConfigLoader;
|
import hae.utils.ConfigLoader;
|
||||||
|
import hae.utils.UIEnhancer;
|
||||||
import hae.utils.project.ProjectProcessor;
|
import hae.utils.project.ProjectProcessor;
|
||||||
import hae.utils.project.model.HaeFileContent;
|
import hae.utils.project.model.HaeFileContent;
|
||||||
import hae.utils.string.StringProcessor;
|
import hae.utils.string.StringProcessor;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
import javax.swing.border.TitledBorder;
|
||||||
import javax.swing.event.DocumentEvent;
|
import javax.swing.event.DocumentEvent;
|
||||||
import javax.swing.event.DocumentListener;
|
import javax.swing.event.DocumentListener;
|
||||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||||
|
import javax.swing.table.DefaultTableModel;
|
||||||
import javax.swing.table.TableColumnModel;
|
import javax.swing.table.TableColumnModel;
|
||||||
import javax.swing.table.TableModel;
|
import javax.swing.table.TableModel;
|
||||||
import javax.swing.table.TableRowSorter;
|
import javax.swing.table.TableRowSorter;
|
||||||
@@ -49,8 +52,10 @@ public class Databoard extends JPanel {
|
|||||||
|
|
||||||
private SwingWorker<Map<String, List<String>>, Void> handleComboBoxWorker;
|
private SwingWorker<Map<String, List<String>>, Void> handleComboBoxWorker;
|
||||||
private SwingWorker<Void, Void> applyHostFilterWorker;
|
private SwingWorker<Void, Void> applyHostFilterWorker;
|
||||||
private SwingWorker<List<String>, Void> exportActionWorker;
|
private SwingWorker<List<Object[]>, Void> exportActionWorker;
|
||||||
private SwingWorker<List<String>, Void> importActionWorker;
|
private SwingWorker<List<Object[]>, Void> importActionWorker;
|
||||||
|
|
||||||
|
private final String defaultText = "Please enter the host";
|
||||||
|
|
||||||
public Databoard(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) {
|
public Databoard(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) {
|
||||||
this.api = api;
|
this.api = api;
|
||||||
@@ -83,6 +88,7 @@ public class Databoard extends JPanel {
|
|||||||
menu.add(menuPanel);
|
menu.add(menuPanel);
|
||||||
|
|
||||||
hostTextField = new JTextField();
|
hostTextField = new JTextField();
|
||||||
|
UIEnhancer.setTextFieldPlaceholder(hostTextField, defaultText);
|
||||||
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
|
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
|
||||||
|
|
||||||
dataTabbedPane = new JTabbedPane(JTabbedPane.TOP);
|
dataTabbedPane = new JTabbedPane(JTabbedPane.TOP);
|
||||||
@@ -193,11 +199,11 @@ public class Databoard extends JPanel {
|
|||||||
|
|
||||||
private void handleComboBoxAction(ActionEvent e) {
|
private void handleComboBoxAction(ActionEvent e) {
|
||||||
if (!isMatchHost && hostComboBox.getSelectedItem() != null) {
|
if (!isMatchHost && hostComboBox.getSelectedItem() != null) {
|
||||||
progressBar.setVisible(true);
|
|
||||||
setProgressBar(true);
|
|
||||||
String selectedHost = hostComboBox.getSelectedItem().toString();
|
String selectedHost = hostComboBox.getSelectedItem().toString();
|
||||||
|
|
||||||
if (getHostByList().contains(selectedHost)) {
|
if (getHostByList().contains(selectedHost)) {
|
||||||
|
progressBar.setVisible(true);
|
||||||
|
setProgressBar(true);
|
||||||
hostTextField.setText(selectedHost);
|
hostTextField.setText(selectedHost);
|
||||||
|
|
||||||
if (handleComboBoxWorker != null && !handleComboBoxWorker.isDone()) {
|
if (handleComboBoxWorker != null && !handleComboBoxWorker.isDone()) {
|
||||||
@@ -385,9 +391,9 @@ public class Databoard extends JPanel {
|
|||||||
exportActionWorker.cancel(true);
|
exportActionWorker.cancel(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
exportActionWorker = new SwingWorker<List<String>, Void>() {
|
exportActionWorker = new SwingWorker<List<Object[]>, Void>() {
|
||||||
@Override
|
@Override
|
||||||
protected List<String> doInBackground() {
|
protected List<Object[]> doInBackground() {
|
||||||
ConcurrentHashMap<String, Map<String, List<String>>> dataMap = Config.globalDataMap;
|
ConcurrentHashMap<String, Map<String, List<String>>> dataMap = Config.globalDataMap;
|
||||||
return exportData(selectedHost, exportDir, dataMap);
|
return exportData(selectedHost, exportDir, dataMap);
|
||||||
}
|
}
|
||||||
@@ -395,11 +401,9 @@ public class Databoard extends JPanel {
|
|||||||
@Override
|
@Override
|
||||||
protected void done() {
|
protected void done() {
|
||||||
try {
|
try {
|
||||||
List<String> taskStatusList = get();
|
List<Object[]> taskStatusList = get();
|
||||||
if (!taskStatusList.isEmpty()) {
|
if (!taskStatusList.isEmpty()) {
|
||||||
String exportStatusMessage = String.format("Exported File List Status:\n%s", String.join("\n", taskStatusList));
|
JOptionPane.showMessageDialog(Databoard.this, generateTaskStatusPane(taskStatusList), "Info", JOptionPane.INFORMATION_MESSAGE);
|
||||||
|
|
||||||
JOptionPane.showMessageDialog(Databoard.this, generateTaskStatusPane(exportStatusMessage), "Info", JOptionPane.INFORMATION_MESSAGE);
|
|
||||||
}
|
}
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
@@ -409,17 +413,36 @@ public class Databoard extends JPanel {
|
|||||||
exportActionWorker.execute();
|
exportActionWorker.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
private JScrollPane generateTaskStatusPane(String message) {
|
private JScrollPane generateTaskStatusPane(List<Object[]> dataList) {
|
||||||
JTextArea textArea = new JTextArea(message);
|
String[] columnNames = {"#", "Filename", "Status"};
|
||||||
textArea.setEditable(false);
|
DefaultTableModel taskStatusTableModel = new DefaultTableModel(columnNames, 0);
|
||||||
textArea.setLineWrap(true);
|
JTable taskStatusTable = new JTable(taskStatusTableModel);
|
||||||
JScrollPane scrollPane = new JScrollPane(textArea);
|
|
||||||
scrollPane.setPreferredSize(new Dimension(400, 200));
|
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<DefaultTableModel> 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;
|
return scrollPane;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> exportData(String selectedHost, String exportDir, Map<String, Map<String, List<String>>> dataMap) {
|
private List<Object[]> exportData(String selectedHost, String exportDir, Map<String, Map<String, List<String>>> dataMap) {
|
||||||
return dataMap.entrySet().stream()
|
return dataMap.entrySet().stream()
|
||||||
.filter(entry -> selectedHost.equals("*") || StringProcessor.matchesHostPattern(entry.getKey(), selectedHost))
|
.filter(entry -> selectedHost.equals("*") || StringProcessor.matchesHostPattern(entry.getKey(), selectedHost))
|
||||||
.filter(entry -> !entry.getKey().contains("*"))
|
.filter(entry -> !entry.getKey().contains("*"))
|
||||||
@@ -428,7 +451,7 @@ public class Databoard extends JPanel {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String exportEntry(Map.Entry<String, Map<String, List<String>>> entry, String exportDir) {
|
private Object[] exportEntry(Map.Entry<String, Map<String, List<String>>> entry, String exportDir) {
|
||||||
String key = entry.getKey();
|
String key = entry.getKey();
|
||||||
Map<String, List<String>> ruleMap = entry.getValue();
|
Map<String, List<String>> ruleMap = entry.getValue();
|
||||||
|
|
||||||
@@ -442,7 +465,7 @@ public class Databoard extends JPanel {
|
|||||||
.collect(Collectors.toMap(
|
.collect(Collectors.toMap(
|
||||||
messageEntry -> messageEntry,
|
messageEntry -> messageEntry,
|
||||||
messageEntry -> StringProcessor.getRandomUUID(),
|
messageEntry -> StringProcessor.getRandomUUID(),
|
||||||
(existing, replacement) -> existing // 在冲突时保留现有的映射
|
(existing, replacement) -> existing
|
||||||
));
|
));
|
||||||
|
|
||||||
Map<String, Map<String, Object>> httpMap = processEntries(
|
Map<String, Map<String, Object>> httpMap = processEntries(
|
||||||
@@ -463,7 +486,7 @@ public class Databoard extends JPanel {
|
|||||||
String filename = String.format("%s/%s-%s.hae", exportDir, StringProcessor.getCurrentTime(), hostName);
|
String filename = String.format("%s/%s-%s.hae", exportDir, StringProcessor.getCurrentTime(), hostName);
|
||||||
boolean createdStatus = projectProcessor.createHaeFile(filename, key, ruleMap, urlMap, httpMap);
|
boolean createdStatus = projectProcessor.createHaeFile(filename, key, ruleMap, urlMap, httpMap);
|
||||||
|
|
||||||
return String.format("Filename: %s, Status: %s", filename, createdStatus);
|
return new Object[]{filename, createdStatus};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -507,9 +530,9 @@ public class Databoard extends JPanel {
|
|||||||
importActionWorker.cancel(true);
|
importActionWorker.cancel(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
importActionWorker = new SwingWorker<List<String>, Void>() {
|
importActionWorker = new SwingWorker<List<Object[]>, Void>() {
|
||||||
@Override
|
@Override
|
||||||
protected List<String> doInBackground() {
|
protected List<Object[]> doInBackground() {
|
||||||
List<String> filesWithExtension = findFilesWithExtension(new File(exportDir), ".hae");
|
List<String> filesWithExtension = findFilesWithExtension(new File(exportDir), ".hae");
|
||||||
return filesWithExtension.stream()
|
return filesWithExtension.stream()
|
||||||
.map(Databoard.this::importData)
|
.map(Databoard.this::importData)
|
||||||
@@ -519,10 +542,9 @@ public class Databoard extends JPanel {
|
|||||||
@Override
|
@Override
|
||||||
protected void done() {
|
protected void done() {
|
||||||
try {
|
try {
|
||||||
List<String> taskStatusList = get();
|
List<Object[]> taskStatusList = get();
|
||||||
if (!taskStatusList.isEmpty()) {
|
if (!taskStatusList.isEmpty()) {
|
||||||
String importStatusMessage = "Imported File List Status:\n" + String.join("\n", taskStatusList);
|
JOptionPane.showMessageDialog(Databoard.this, generateTaskStatusPane(taskStatusList), "Info", JOptionPane.INFORMATION_MESSAGE);
|
||||||
JOptionPane.showMessageDialog(Databoard.this, generateTaskStatusPane(importStatusMessage), "Info", JOptionPane.INFORMATION_MESSAGE);
|
|
||||||
}
|
}
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
@@ -532,7 +554,7 @@ public class Databoard extends JPanel {
|
|||||||
importActionWorker.execute();
|
importActionWorker.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String importData(String filename) {
|
private Object[] importData(String filename) {
|
||||||
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
|
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
|
||||||
|
|
||||||
HaeFileContent haeFileContent = projectProcessor.readHaeFile(filename);
|
HaeFileContent haeFileContent = projectProcessor.readHaeFile(filename);
|
||||||
@@ -568,7 +590,7 @@ public class Databoard extends JPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return String.format("Filename: %s, Status: %s", filename, readStatus);
|
return new Object[]{filename, readStatus};
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> findFilesWithExtension(File directory, String extension) {
|
private List<String> findFilesWithExtension(File directory, String extension) {
|
||||||
@@ -648,6 +670,8 @@ public class Databoard extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
messageTableModel.deleteByHost(host);
|
messageTableModel.deleteByHost(host);
|
||||||
|
|
||||||
|
hostTextField.setText("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package hae.component.board.message;
|
package hae.component.board.message;
|
||||||
|
|
||||||
import burp.api.montoya.MontoyaApi;
|
import burp.api.montoya.MontoyaApi;
|
||||||
import burp.api.montoya.core.ByteArray;
|
|
||||||
import burp.api.montoya.http.message.HttpHeader;
|
import burp.api.montoya.http.message.HttpHeader;
|
||||||
import burp.api.montoya.http.message.HttpRequestResponse;
|
import burp.api.montoya.http.message.HttpRequestResponse;
|
||||||
import burp.api.montoya.http.message.requests.HttpRequest;
|
import burp.api.montoya.http.message.requests.HttpRequest;
|
||||||
@@ -11,6 +10,7 @@ import burp.api.montoya.ui.editor.HttpRequestEditor;
|
|||||||
import burp.api.montoya.ui.editor.HttpResponseEditor;
|
import burp.api.montoya.ui.editor.HttpResponseEditor;
|
||||||
import hae.Config;
|
import hae.Config;
|
||||||
import hae.cache.CachePool;
|
import hae.cache.CachePool;
|
||||||
|
import hae.utils.ConfigLoader;
|
||||||
import hae.utils.project.FileProcessor;
|
import hae.utils.project.FileProcessor;
|
||||||
import hae.utils.string.HashCalculator;
|
import hae.utils.string.HashCalculator;
|
||||||
import hae.utils.string.StringProcessor;
|
import hae.utils.string.StringProcessor;
|
||||||
@@ -23,6 +23,8 @@ import javax.swing.table.TableRowSorter;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -30,15 +32,17 @@ import static burp.api.montoya.ui.editor.EditorOptions.READ_ONLY;
|
|||||||
|
|
||||||
public class MessageTableModel extends AbstractTableModel {
|
public class MessageTableModel extends AbstractTableModel {
|
||||||
private final MontoyaApi api;
|
private final MontoyaApi api;
|
||||||
|
private final ConfigLoader configLoader;
|
||||||
private final MessageTable messageTable;
|
private final MessageTable messageTable;
|
||||||
private final JSplitPane splitPane;
|
private final JSplitPane splitPane;
|
||||||
private final LinkedList<MessageEntry> log = new LinkedList<>();
|
private final LinkedList<MessageEntry> log = new LinkedList<>();
|
||||||
private final LinkedList<MessageEntry> filteredLog;
|
private final LinkedList<MessageEntry> filteredLog;
|
||||||
private SwingWorker<Void, Void> currentWorker;
|
private SwingWorker<Void, Void> currentWorker;
|
||||||
|
|
||||||
public MessageTableModel(MontoyaApi api) {
|
public MessageTableModel(MontoyaApi api, ConfigLoader configLoader) {
|
||||||
this.filteredLog = new LinkedList<>();
|
this.filteredLog = new LinkedList<>();
|
||||||
this.api = api;
|
this.api = api;
|
||||||
|
this.configLoader = configLoader;
|
||||||
|
|
||||||
JTabbedPane messageTab = new JTabbedPane();
|
JTabbedPane messageTab = new JTabbedPane();
|
||||||
UserInterface userInterface = api.userInterface();
|
UserInterface userInterface = api.userInterface();
|
||||||
@@ -435,7 +439,7 @@ public class MessageTableModel extends AbstractTableModel {
|
|||||||
|
|
||||||
public class MessageTable extends JTable {
|
public class MessageTable extends JTable {
|
||||||
private MessageEntry messageEntry;
|
private MessageEntry messageEntry;
|
||||||
private SwingWorker<Object, Void> currentWorker;
|
private final ExecutorService executorService;
|
||||||
private int lastSelectedIndex = -1;
|
private int lastSelectedIndex = -1;
|
||||||
private final HttpRequestEditor requestEditor;
|
private final HttpRequestEditor requestEditor;
|
||||||
private final HttpResponseEditor responseEditor;
|
private final HttpResponseEditor responseEditor;
|
||||||
@@ -444,41 +448,31 @@ public class MessageTableModel extends AbstractTableModel {
|
|||||||
super(messageTableModel);
|
super(messageTableModel);
|
||||||
this.requestEditor = requestEditor;
|
this.requestEditor = requestEditor;
|
||||||
this.responseEditor = responseEditor;
|
this.responseEditor = responseEditor;
|
||||||
|
this.executorService = Executors.newSingleThreadExecutor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void changeSelection(int row, int col, boolean toggle, boolean extend) {
|
public void changeSelection(int row, int col, boolean toggle, boolean extend) {
|
||||||
super.changeSelection(row, col, toggle, extend);
|
super.changeSelection(row, col, toggle, extend);
|
||||||
|
int selectedIndex = convertRowIndexToModel(row);
|
||||||
requestEditor.setRequest(HttpRequest.httpRequest("Loading..."));
|
if (lastSelectedIndex != selectedIndex) {
|
||||||
responseEditor.setResponse(HttpResponse.httpResponse("Loading..."));
|
lastSelectedIndex = selectedIndex;
|
||||||
|
executorService.execute(this::getSelectedMessage);
|
||||||
if (currentWorker != null && !currentWorker.isDone()) {
|
|
||||||
currentWorker.cancel(true);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
currentWorker = new SwingWorker<>() {
|
private void getSelectedMessage() {
|
||||||
@Override
|
messageEntry = filteredLog.get(lastSelectedIndex);
|
||||||
protected Void doInBackground() {
|
|
||||||
int selectedIndex = convertRowIndexToModel(row);
|
|
||||||
if (lastSelectedIndex != selectedIndex) {
|
|
||||||
lastSelectedIndex = selectedIndex;
|
|
||||||
messageEntry = filteredLog.get(selectedIndex);
|
|
||||||
|
|
||||||
HttpRequestResponse httpRequestResponse = messageEntry.getRequestResponse();
|
HttpRequestResponse httpRequestResponse = messageEntry.getRequestResponse();
|
||||||
|
|
||||||
ByteArray requestByte = httpRequestResponse.request().toByteArray();
|
requestEditor.setRequest(HttpRequest.httpRequest(messageEntry.getRequestResponse().httpService(), httpRequestResponse.request().toByteArray()));
|
||||||
ByteArray responseByte = httpRequestResponse.response().toByteArray();
|
int responseSizeWithMb = httpRequestResponse.response().toString().length() / 1024 / 1024;
|
||||||
|
if ((responseSizeWithMb < Integer.parseInt(configLoader.getLimitSize())) || configLoader.getLimitSize().equals("0")) {
|
||||||
requestEditor.setRequest(HttpRequest.httpRequest(messageEntry.getRequestResponse().httpService(), requestByte));
|
responseEditor.setResponse(httpRequestResponse.response());
|
||||||
responseEditor.setResponse(HttpResponse.httpResponse(responseByte));
|
} else {
|
||||||
|
responseEditor.setResponse(HttpResponse.httpResponse("Exceeds length limit."));
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
currentWorker.execute();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public class AIPower {
|
|||||||
public AIPower(MontoyaApi api, ConfigLoader configLoader, String aiModel, String aiBaseUrl, String[] apiKey) {
|
public AIPower(MontoyaApi api, ConfigLoader configLoader, String aiModel, String aiBaseUrl, String[] apiKey) {
|
||||||
this.api = api;
|
this.api = api;
|
||||||
this.configLoader = configLoader;
|
this.configLoader = configLoader;
|
||||||
this.httpUtils = new HttpUtils(api);
|
this.httpUtils = new HttpUtils(api, configLoader);
|
||||||
this.aiModel = aiModel;
|
this.aiModel = aiModel;
|
||||||
this.aiBaseUrl = aiBaseUrl;
|
this.aiBaseUrl = aiBaseUrl;
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import java.awt.event.ActionEvent;
|
|||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -34,6 +35,7 @@ public class Datatable extends JPanel {
|
|||||||
private final JTable dataTable;
|
private final JTable dataTable;
|
||||||
private final DefaultTableModel dataTableModel;
|
private final DefaultTableModel dataTableModel;
|
||||||
private final JTextField searchField;
|
private final JTextField searchField;
|
||||||
|
private final JTextField secondSearchField;
|
||||||
private final TableRowSorter<DefaultTableModel> sorter;
|
private final TableRowSorter<DefaultTableModel> sorter;
|
||||||
private final JCheckBox searchMode = new JCheckBox("Reverse search");
|
private final JCheckBox searchMode = new JCheckBox("Reverse search");
|
||||||
private final String tabName;
|
private final String tabName;
|
||||||
@@ -52,7 +54,8 @@ public class Datatable extends JPanel {
|
|||||||
|
|
||||||
this.dataTable = new JTable(dataTableModel);
|
this.dataTable = new JTable(dataTableModel);
|
||||||
this.sorter = new TableRowSorter<>(dataTableModel);
|
this.sorter = new TableRowSorter<>(dataTableModel);
|
||||||
this.searchField = new JTextField();
|
this.searchField = new JTextField(10);
|
||||||
|
this.secondSearchField = new JTextField(10);
|
||||||
this.aiEmpoweredMenu = new JPopupMenu();
|
this.aiEmpoweredMenu = new JPopupMenu();
|
||||||
this.footerPanel = new JPanel(new BorderLayout(0, 5));
|
this.footerPanel = new JPanel(new BorderLayout(0, 5));
|
||||||
|
|
||||||
@@ -70,21 +73,13 @@ public class Datatable extends JPanel {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
dataTable.setRowSorter(sorter);
|
|
||||||
TableColumn idColumn = dataTable.getColumnModel().getColumn(0);
|
|
||||||
idColumn.setMaxWidth(50);
|
|
||||||
|
|
||||||
for (String item : dataList) {
|
for (String item : dataList) {
|
||||||
if (!item.isEmpty()) {
|
if (!item.isEmpty()) {
|
||||||
addRowToTable(new Object[]{item});
|
addRowToTable(new Object[]{item});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置灰色默认文本
|
UIEnhancer.setTextFieldPlaceholder(searchField, "Search");
|
||||||
String searchText = "Search";
|
|
||||||
UIEnhancer.setTextFieldPlaceholder(searchField, searchText);
|
|
||||||
|
|
||||||
// 监听输入框内容输入、更新、删除
|
|
||||||
searchField.getDocument().addDocumentListener(new DocumentListener() {
|
searchField.getDocument().addDocumentListener(new DocumentListener() {
|
||||||
@Override
|
@Override
|
||||||
public void insertUpdate(DocumentEvent e) {
|
public void insertUpdate(DocumentEvent e) {
|
||||||
@@ -103,10 +98,34 @@ public class Datatable extends JPanel {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
UIEnhancer.setTextFieldPlaceholder(secondSearchField, "Second search");
|
||||||
|
secondSearchField.getDocument().addDocumentListener(new DocumentListener() {
|
||||||
|
@Override
|
||||||
|
public void insertUpdate(DocumentEvent e) {
|
||||||
|
performSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUpdate(DocumentEvent e) {
|
||||||
|
performSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changedUpdate(DocumentEvent e) {
|
||||||
|
performSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
// 设置布局
|
// 设置布局
|
||||||
JScrollPane scrollPane = new JScrollPane(dataTable);
|
JScrollPane scrollPane = new JScrollPane(dataTable);
|
||||||
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
||||||
|
|
||||||
|
dataTable.setRowSorter(sorter);
|
||||||
|
TableColumn idColumn = dataTable.getColumnModel().getColumn(0);
|
||||||
|
idColumn.setPreferredWidth(50);
|
||||||
|
idColumn.setMaxWidth(100);
|
||||||
|
|
||||||
setLayout(new BorderLayout(0, 5));
|
setLayout(new BorderLayout(0, 5));
|
||||||
|
|
||||||
JPanel optionsPanel = new JPanel();
|
JPanel optionsPanel = new JPanel();
|
||||||
@@ -162,6 +181,8 @@ public class Datatable extends JPanel {
|
|||||||
optionsPanel.add(Box.createHorizontalStrut(5));
|
optionsPanel.add(Box.createHorizontalStrut(5));
|
||||||
optionsPanel.add(searchField);
|
optionsPanel.add(searchField);
|
||||||
optionsPanel.add(Box.createHorizontalStrut(5));
|
optionsPanel.add(Box.createHorizontalStrut(5));
|
||||||
|
optionsPanel.add(secondSearchField);
|
||||||
|
optionsPanel.add(Box.createHorizontalStrut(5));
|
||||||
optionsPanel.add(aiEmpoweredButton);
|
optionsPanel.add(aiEmpoweredButton);
|
||||||
|
|
||||||
footerPanel.setBorder(BorderFactory.createEmptyBorder(2, 3, 5, 3));
|
footerPanel.setBorder(BorderFactory.createEmptyBorder(2, 3, 5, 3));
|
||||||
@@ -252,29 +273,61 @@ public class Datatable extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void performSearch() {
|
private void performSearch() {
|
||||||
|
RowFilter<Object, Object> firstRowFilter = applyFirstSearchFilter();
|
||||||
|
RowFilter<Object, Object> secondRowFilter = applySecondFilter();
|
||||||
if (searchField.getForeground().equals(Color.BLACK)) {
|
if (searchField.getForeground().equals(Color.BLACK)) {
|
||||||
RowFilter<Object, Object> rowFilter = new RowFilter<Object, Object>() {
|
sorter.setRowFilter(firstRowFilter);
|
||||||
public boolean include(Entry<?, ?> entry) {
|
if (secondSearchField.getForeground().equals(Color.BLACK)) {
|
||||||
String searchFieldTextText = searchField.getText();
|
List<RowFilter<Object, Object>> filters = new ArrayList<>();
|
||||||
Pattern pattern = null;
|
filters.add(firstRowFilter);
|
||||||
try {
|
filters.add(secondRowFilter);
|
||||||
pattern = Pattern.compile(searchFieldTextText, Pattern.CASE_INSENSITIVE);
|
sorter.setRowFilter(RowFilter.andFilter(filters));
|
||||||
} catch (Exception ignored) {
|
}
|
||||||
}
|
|
||||||
|
|
||||||
String entryValue = ((String) entry.getValue(1)).toLowerCase();
|
|
||||||
searchFieldTextText = searchFieldTextText.toLowerCase();
|
|
||||||
if (pattern != null) {
|
|
||||||
return searchFieldTextText.isEmpty() || pattern.matcher(entryValue).find() != searchMode.isSelected();
|
|
||||||
} else {
|
|
||||||
return searchFieldTextText.isEmpty() || entryValue.contains(searchFieldTextText) != searchMode.isSelected();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
sorter.setRowFilter(rowFilter);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RowFilter<Object, Object> applyFirstSearchFilter() {
|
||||||
|
return new RowFilter<Object, Object>() {
|
||||||
|
public boolean include(Entry<?, ?> entry) {
|
||||||
|
String searchFieldTextText = searchField.getText();
|
||||||
|
Pattern pattern = null;
|
||||||
|
try {
|
||||||
|
pattern = Pattern.compile(searchFieldTextText, Pattern.CASE_INSENSITIVE);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
String entryValue = ((String) entry.getValue(1)).toLowerCase();
|
||||||
|
searchFieldTextText = searchFieldTextText.toLowerCase();
|
||||||
|
if (pattern != null) {
|
||||||
|
return searchFieldTextText.isEmpty() || pattern.matcher(entryValue).find() != searchMode.isSelected();
|
||||||
|
} else {
|
||||||
|
return searchFieldTextText.isEmpty() || entryValue.contains(searchFieldTextText) != searchMode.isSelected();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private RowFilter<Object, Object> applySecondFilter() {
|
||||||
|
return new RowFilter<Object, Object>() {
|
||||||
|
public boolean include(Entry<?, ?> entry) {
|
||||||
|
String searchFieldTextText = secondSearchField.getText();
|
||||||
|
Pattern pattern = null;
|
||||||
|
try {
|
||||||
|
pattern = Pattern.compile(searchFieldTextText, Pattern.CASE_INSENSITIVE);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
String entryValue = ((String) entry.getValue(1)).toLowerCase();
|
||||||
|
searchFieldTextText = searchFieldTextText.toLowerCase();
|
||||||
|
if (pattern != null) {
|
||||||
|
return searchFieldTextText.isEmpty() || pattern.matcher(entryValue).find();
|
||||||
|
} else {
|
||||||
|
return searchFieldTextText.isEmpty() || entryValue.contains(searchFieldTextText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public void setTableListener(MessageTableModel messagePanel) {
|
public void setTableListener(MessageTableModel messagePanel) {
|
||||||
// 表格复制功能
|
// 表格复制功能
|
||||||
dataTable.setTransferHandler(new TransferHandler() {
|
dataTable.setTransferHandler(new TransferHandler() {
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ import burp.api.montoya.ui.Selection;
|
|||||||
import burp.api.montoya.ui.editor.extension.EditorCreationContext;
|
import burp.api.montoya.ui.editor.extension.EditorCreationContext;
|
||||||
import burp.api.montoya.ui.editor.extension.ExtensionProvidedHttpRequestEditor;
|
import burp.api.montoya.ui.editor.extension.ExtensionProvidedHttpRequestEditor;
|
||||||
import burp.api.montoya.ui.editor.extension.HttpRequestEditorProvider;
|
import burp.api.montoya.ui.editor.extension.HttpRequestEditorProvider;
|
||||||
|
import hae.Config;
|
||||||
import hae.component.board.table.Datatable;
|
import hae.component.board.table.Datatable;
|
||||||
import hae.instances.http.utils.MessageProcessor;
|
import hae.instances.http.utils.MessageProcessor;
|
||||||
import hae.utils.ConfigLoader;
|
import hae.utils.ConfigLoader;
|
||||||
|
import hae.utils.http.HttpUtils;
|
||||||
import hae.utils.string.StringProcessor;
|
import hae.utils.string.StringProcessor;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
@@ -37,6 +39,7 @@ public class RequestEditor implements HttpRequestEditorProvider {
|
|||||||
private static class Editor implements ExtensionProvidedHttpRequestEditor {
|
private static class Editor implements ExtensionProvidedHttpRequestEditor {
|
||||||
private final MontoyaApi api;
|
private final MontoyaApi api;
|
||||||
private final ConfigLoader configLoader;
|
private final ConfigLoader configLoader;
|
||||||
|
private final HttpUtils httpUtils;
|
||||||
private final EditorCreationContext creationContext;
|
private final EditorCreationContext creationContext;
|
||||||
private final MessageProcessor messageProcessor;
|
private final MessageProcessor messageProcessor;
|
||||||
private HttpRequestResponse requestResponse;
|
private HttpRequestResponse requestResponse;
|
||||||
@@ -47,6 +50,7 @@ public class RequestEditor implements HttpRequestEditorProvider {
|
|||||||
public Editor(MontoyaApi api, ConfigLoader configLoader, EditorCreationContext creationContext) {
|
public Editor(MontoyaApi api, ConfigLoader configLoader, EditorCreationContext creationContext) {
|
||||||
this.api = api;
|
this.api = api;
|
||||||
this.configLoader = configLoader;
|
this.configLoader = configLoader;
|
||||||
|
this.httpUtils = new HttpUtils(api, configLoader);
|
||||||
this.creationContext = creationContext;
|
this.creationContext = creationContext;
|
||||||
this.messageProcessor = new MessageProcessor(api);
|
this.messageProcessor = new MessageProcessor(api);
|
||||||
}
|
}
|
||||||
@@ -69,16 +73,10 @@ public class RequestEditor implements HttpRequestEditorProvider {
|
|||||||
try {
|
try {
|
||||||
String host = StringProcessor.getHostByUrl(request.url());
|
String host = StringProcessor.getHostByUrl(request.url());
|
||||||
if (!host.isEmpty()) {
|
if (!host.isEmpty()) {
|
||||||
String[] hostList = configLoader.getBlockHost().split("\\|");
|
|
||||||
boolean isBlockHost = isBlockHost(hostList, host);
|
|
||||||
|
|
||||||
List<String> suffixList = Arrays.asList(configLoader.getExcludeSuffix().split("\\|"));
|
|
||||||
String toolType = creationContext.toolSource().toolType().toolName();
|
String toolType = creationContext.toolSource().toolType().toolName();
|
||||||
boolean isToolScope = configLoader.getScope().contains(toolType);
|
boolean matches = httpUtils.verifyHttpRequestResponse(requestResponse, toolType);
|
||||||
|
|
||||||
boolean matches = suffixList.contains(request.fileExtension().toLowerCase()) || isBlockHost || !isToolScope;
|
if (!matches) {
|
||||||
|
|
||||||
if (!matches && !request.bodyToString().equals("Loading...")) {
|
|
||||||
this.dataList = messageProcessor.processRequest("", request, false);
|
this.dataList = messageProcessor.processRequest("", request, false);
|
||||||
return isListHasData(this.dataList);
|
return isListHasData(this.dataList);
|
||||||
}
|
}
|
||||||
@@ -121,19 +119,6 @@ public class RequestEditor implements HttpRequestEditorProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isBlockHost(String[] hostList, String host) {
|
|
||||||
boolean isBlockHost = false;
|
|
||||||
for (String hostName : hostList) {
|
|
||||||
String cleanedHost = StringProcessor.replaceFirstOccurrence(hostName, "*.", "");
|
|
||||||
if (hostName.contains("*.") && StringProcessor.matchFromEnd(host, cleanedHost)) {
|
|
||||||
isBlockHost = true;
|
|
||||||
} else if (host.equals(hostName) || hostName.equals("*")) {
|
|
||||||
isBlockHost = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return isBlockHost;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isListHasData(List<Map<String, String>> dataList) {
|
public static boolean isListHasData(List<Map<String, String>> dataList) {
|
||||||
if (dataList != null && !dataList.isEmpty()) {
|
if (dataList != null && !dataList.isEmpty()) {
|
||||||
Map<String, String> dataMap = dataList.get(0);
|
Map<String, String> dataMap = dataList.get(0);
|
||||||
@@ -148,7 +133,7 @@ public class RequestEditor implements HttpRequestEditorProvider {
|
|||||||
Map<String, String> dataMap = result.get(0);
|
Map<String, String> dataMap = result.get(0);
|
||||||
if (dataMap != null && !dataMap.isEmpty()) {
|
if (dataMap != null && !dataMap.isEmpty()) {
|
||||||
dataMap.keySet().forEach(i -> {
|
dataMap.keySet().forEach(i -> {
|
||||||
String[] extractData = dataMap.get(i).split("\n");
|
String[] extractData = dataMap.get(i).split(Config.boundary);
|
||||||
Datatable dataPanel = new Datatable(api, configLoader, i, Arrays.asList(extractData));
|
Datatable dataPanel = new Datatable(api, configLoader, i, Arrays.asList(extractData));
|
||||||
tabbedPane.addTab(i, dataPanel);
|
tabbedPane.addTab(i, dataPanel);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ import burp.api.montoya.ui.editor.extension.HttpResponseEditorProvider;
|
|||||||
import hae.component.board.table.Datatable;
|
import hae.component.board.table.Datatable;
|
||||||
import hae.instances.http.utils.MessageProcessor;
|
import hae.instances.http.utils.MessageProcessor;
|
||||||
import hae.utils.ConfigLoader;
|
import hae.utils.ConfigLoader;
|
||||||
|
import hae.utils.http.HttpUtils;
|
||||||
import hae.utils.string.StringProcessor;
|
import hae.utils.string.StringProcessor;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -38,6 +38,7 @@ public class ResponseEditor implements HttpResponseEditorProvider {
|
|||||||
private static class Editor implements ExtensionProvidedHttpResponseEditor {
|
private static class Editor implements ExtensionProvidedHttpResponseEditor {
|
||||||
private final MontoyaApi api;
|
private final MontoyaApi api;
|
||||||
private final ConfigLoader configLoader;
|
private final ConfigLoader configLoader;
|
||||||
|
private final HttpUtils httpUtils;
|
||||||
private final EditorCreationContext creationContext;
|
private final EditorCreationContext creationContext;
|
||||||
private final MessageProcessor messageProcessor;
|
private final MessageProcessor messageProcessor;
|
||||||
private HttpRequestResponse requestResponse;
|
private HttpRequestResponse requestResponse;
|
||||||
@@ -48,6 +49,7 @@ public class ResponseEditor implements HttpResponseEditorProvider {
|
|||||||
public Editor(MontoyaApi api, ConfigLoader configLoader, EditorCreationContext creationContext) {
|
public Editor(MontoyaApi api, ConfigLoader configLoader, EditorCreationContext creationContext) {
|
||||||
this.api = api;
|
this.api = api;
|
||||||
this.configLoader = configLoader;
|
this.configLoader = configLoader;
|
||||||
|
this.httpUtils = new HttpUtils(api, configLoader);
|
||||||
this.creationContext = creationContext;
|
this.creationContext = creationContext;
|
||||||
this.messageProcessor = new MessageProcessor(api);
|
this.messageProcessor = new MessageProcessor(api);
|
||||||
}
|
}
|
||||||
@@ -75,20 +77,14 @@ public class ResponseEditor implements HttpResponseEditorProvider {
|
|||||||
try {
|
try {
|
||||||
String host = StringProcessor.getHostByUrl(request.url());
|
String host = StringProcessor.getHostByUrl(request.url());
|
||||||
if (!host.isEmpty()) {
|
if (!host.isEmpty()) {
|
||||||
String[] hostList = configLoader.getBlockHost().split("\\|");
|
|
||||||
boolean isBlockHost = RequestEditor.isBlockHost(hostList, host);
|
|
||||||
|
|
||||||
List<String> suffixList = Arrays.asList(configLoader.getExcludeSuffix().split("\\|"));
|
|
||||||
String toolType = creationContext.toolSource().toolType().toolName();
|
String toolType = creationContext.toolSource().toolType().toolName();
|
||||||
boolean isToolScope = configLoader.getScope().contains(toolType);
|
matches = httpUtils.verifyHttpRequestResponse(requestResponse, toolType);
|
||||||
|
|
||||||
matches = suffixList.contains(request.fileExtension().toLowerCase()) || isBlockHost || !isToolScope;
|
|
||||||
}
|
}
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!matches && !response.bodyToString().equals("Loading...")) {
|
if (!matches) {
|
||||||
this.dataList = messageProcessor.processResponse("", response, false);
|
this.dataList = messageProcessor.processResponse("", response, false);
|
||||||
return RequestEditor.isListHasData(this.dataList);
|
return RequestEditor.isListHasData(this.dataList);
|
||||||
}
|
}
|
||||||
|
|||||||
106
src/main/java/hae/instances/http/HttpMessageActiveHandler.java
Normal file
106
src/main/java/hae/instances/http/HttpMessageActiveHandler.java
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
package hae.instances.http;
|
||||||
|
|
||||||
|
import burp.api.montoya.MontoyaApi;
|
||||||
|
import burp.api.montoya.core.Annotations;
|
||||||
|
import burp.api.montoya.core.HighlightColor;
|
||||||
|
import burp.api.montoya.http.handler.*;
|
||||||
|
import burp.api.montoya.http.message.HttpRequestResponse;
|
||||||
|
import burp.api.montoya.http.message.requests.HttpRequest;
|
||||||
|
import hae.component.board.message.MessageTableModel;
|
||||||
|
import hae.instances.http.utils.MessageProcessor;
|
||||||
|
import hae.utils.ConfigLoader;
|
||||||
|
import hae.utils.http.HttpUtils;
|
||||||
|
import hae.utils.string.StringProcessor;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class HttpMessageActiveHandler implements HttpHandler {
|
||||||
|
private final MontoyaApi api;
|
||||||
|
private final ConfigLoader configLoader;
|
||||||
|
private final HttpUtils httpUtils;
|
||||||
|
private final MessageTableModel messageTableModel;
|
||||||
|
private final MessageProcessor messageProcessor;
|
||||||
|
|
||||||
|
// Montoya API对HTTP消息的处理分为了请求和响应,因此此处设置高亮和标记需要使用全局变量的方式,以此兼顾请求和响应
|
||||||
|
// 同时采用 ThreadLocal 来保证多线程并发的情况下全局变量的安全性
|
||||||
|
private final ThreadLocal<String> host = ThreadLocal.withInitial(() -> "");
|
||||||
|
private final ThreadLocal<List<String>> colorList = ThreadLocal.withInitial(ArrayList::new);
|
||||||
|
private final ThreadLocal<List<String>> commentList = ThreadLocal.withInitial(ArrayList::new);
|
||||||
|
|
||||||
|
public HttpMessageActiveHandler(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) {
|
||||||
|
this.api = api;
|
||||||
|
this.configLoader = configLoader;
|
||||||
|
this.httpUtils = new HttpUtils(api, configLoader);
|
||||||
|
this.messageTableModel = messageTableModel;
|
||||||
|
this.messageProcessor = new MessageProcessor(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent httpRequestToBeSent) {
|
||||||
|
colorList.get().clear();
|
||||||
|
commentList.get().clear();
|
||||||
|
|
||||||
|
Annotations annotations = httpRequestToBeSent.annotations();
|
||||||
|
|
||||||
|
try {
|
||||||
|
host.set(StringProcessor.getHostByUrl(httpRequestToBeSent.url()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
api.logging().logToError("handleHttpRequestToBeSent: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return RequestToBeSentAction.continueWith(httpRequestToBeSent, annotations);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseReceivedAction handleHttpResponseReceived(HttpResponseReceived httpResponseReceived) {
|
||||||
|
Annotations annotations = httpResponseReceived.annotations();
|
||||||
|
HttpRequest request = httpResponseReceived.initiatingRequest();
|
||||||
|
HttpRequestResponse requestResponse = HttpRequestResponse.httpRequestResponse(request, httpResponseReceived);
|
||||||
|
String toolType = httpResponseReceived.toolSource().toolType().toolName();
|
||||||
|
|
||||||
|
boolean matches = httpUtils.verifyHttpRequestResponse(requestResponse, toolType);
|
||||||
|
|
||||||
|
if (!matches) {
|
||||||
|
try {
|
||||||
|
setColorAndCommentList(messageProcessor.processRequest(host.get(), request, true));
|
||||||
|
setColorAndCommentList(messageProcessor.processResponse(host.get(), httpResponseReceived, true));
|
||||||
|
|
||||||
|
if (!colorList.get().isEmpty() && !commentList.get().isEmpty()) {
|
||||||
|
HttpRequestResponse httpRequestResponse = HttpRequestResponse.httpRequestResponse(request, httpResponseReceived);
|
||||||
|
|
||||||
|
String color = messageProcessor.retrieveFinalColor(messageProcessor.retrieveColorIndices(colorList.get()));
|
||||||
|
annotations.setHighlightColor(HighlightColor.highlightColor(color));
|
||||||
|
String comment = StringProcessor.mergeComment(String.join(", ", commentList.get()));
|
||||||
|
annotations.setNotes(comment);
|
||||||
|
|
||||||
|
String method = request.method();
|
||||||
|
String url = request.url();
|
||||||
|
String status = String.valueOf(httpResponseReceived.statusCode());
|
||||||
|
String length = String.valueOf(httpResponseReceived.toByteArray().length());
|
||||||
|
|
||||||
|
new SwingWorker<Void, Void>() {
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground() {
|
||||||
|
messageTableModel.add(httpRequestResponse, url, method, status, length, comment, color, "", "");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
api.logging().logToError("handleHttpResponseReceived: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseReceivedAction.continueWith(httpResponseReceived, annotations);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setColorAndCommentList(List<Map<String, String>> result) {
|
||||||
|
if (result != null && !result.isEmpty()) {
|
||||||
|
colorList.get().add(result.get(0).get("color"));
|
||||||
|
commentList.get().add(result.get(1).get("comment"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
package hae.instances.http;
|
|
||||||
|
|
||||||
import burp.api.montoya.MontoyaApi;
|
|
||||||
import burp.api.montoya.core.Annotations;
|
|
||||||
import burp.api.montoya.core.HighlightColor;
|
|
||||||
import burp.api.montoya.http.handler.*;
|
|
||||||
import burp.api.montoya.http.message.HttpRequestResponse;
|
|
||||||
import burp.api.montoya.http.message.requests.HttpRequest;
|
|
||||||
import hae.component.board.message.MessageTableModel;
|
|
||||||
import hae.instances.editor.RequestEditor;
|
|
||||||
import hae.instances.http.utils.MessageProcessor;
|
|
||||||
import hae.utils.ConfigLoader;
|
|
||||||
import hae.utils.string.StringProcessor;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class HttpMessageHandler implements HttpHandler {
|
|
||||||
private final MontoyaApi api;
|
|
||||||
private final ConfigLoader configLoader;
|
|
||||||
private final MessageTableModel messageTableModel;
|
|
||||||
private final MessageProcessor messageProcessor;
|
|
||||||
|
|
||||||
// Montoya API对HTTP消息的处理分为了请求和响应,因此此处设置高亮和标记需要使用全局变量的方式,以此兼顾请求和响应
|
|
||||||
// 同时采用 ThreadLocal 来保证多线程并发的情况下全局变量的安全性
|
|
||||||
private final ThreadLocal<String> host = ThreadLocal.withInitial(() -> "");
|
|
||||||
private final ThreadLocal<List<String>> colorList = ThreadLocal.withInitial(ArrayList::new);
|
|
||||||
private final ThreadLocal<List<String>> commentList = ThreadLocal.withInitial(ArrayList::new);
|
|
||||||
private final ThreadLocal<Boolean> matches = ThreadLocal.withInitial(() -> false);
|
|
||||||
private final ThreadLocal<HttpRequest> httpRequest = new ThreadLocal<>();
|
|
||||||
|
|
||||||
public HttpMessageHandler(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) {
|
|
||||||
this.api = api;
|
|
||||||
this.configLoader = configLoader;
|
|
||||||
this.messageTableModel = messageTableModel;
|
|
||||||
this.messageProcessor = new MessageProcessor(api);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent httpRequestToBeSent) {
|
|
||||||
colorList.get().clear();
|
|
||||||
commentList.get().clear();
|
|
||||||
|
|
||||||
Annotations annotations = httpRequestToBeSent.annotations();
|
|
||||||
|
|
||||||
try {
|
|
||||||
httpRequest.set(httpRequestToBeSent);
|
|
||||||
host.set(StringProcessor.getHostByUrl(httpRequestToBeSent.url()));
|
|
||||||
|
|
||||||
String[] hostList = configLoader.getBlockHost().split("\\|");
|
|
||||||
boolean isBlockHost = RequestEditor.isBlockHost(hostList, host.get());
|
|
||||||
|
|
||||||
String toolType = httpRequestToBeSent.toolSource().toolType().toolName();
|
|
||||||
boolean isToolScope = configLoader.getScope().contains(toolType);
|
|
||||||
|
|
||||||
List<String> suffixList = Arrays.asList(configLoader.getExcludeSuffix().split("\\|"));
|
|
||||||
matches.set(suffixList.contains(httpRequestToBeSent.fileExtension().toLowerCase()) || isBlockHost || !isToolScope);
|
|
||||||
|
|
||||||
if (!matches.get()) {
|
|
||||||
List<Map<String, String>> result = messageProcessor.processRequest(host.get(), httpRequestToBeSent, true);
|
|
||||||
setColorAndCommentList(result);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
api.logging().logToError("handleHttpRequestToBeSent: " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return RequestToBeSentAction.continueWith(httpRequestToBeSent, annotations);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResponseReceivedAction handleHttpResponseReceived(HttpResponseReceived httpResponseReceived) {
|
|
||||||
Annotations annotations = httpResponseReceived.annotations();
|
|
||||||
|
|
||||||
if (!matches.get()) {
|
|
||||||
List<Map<String, String>> result = messageProcessor.processResponse(host.get(), httpResponseReceived, true);
|
|
||||||
setColorAndCommentList(result);
|
|
||||||
// 设置高亮颜色和注释
|
|
||||||
if (!colorList.get().isEmpty() && !commentList.get().isEmpty()) {
|
|
||||||
String color = messageProcessor.retrieveFinalColor(messageProcessor.retrieveColorIndices(colorList.get()));
|
|
||||||
annotations.setHighlightColor(HighlightColor.highlightColor(color));
|
|
||||||
String comment = StringProcessor.mergeComment(String.join(", ", commentList.get()));
|
|
||||||
annotations.setNotes(comment);
|
|
||||||
|
|
||||||
HttpRequestResponse httpRequestResponse = HttpRequestResponse.httpRequestResponse(httpRequest.get(), httpResponseReceived);
|
|
||||||
|
|
||||||
// 添加到Databoard
|
|
||||||
String method = httpRequest.get().method();
|
|
||||||
String url = httpRequest.get().url();
|
|
||||||
String status = String.valueOf(httpResponseReceived.statusCode());
|
|
||||||
String length = String.valueOf(httpResponseReceived.toByteArray().length());
|
|
||||||
|
|
||||||
// 后台提交,防止线程阻塞
|
|
||||||
new SwingWorker<Void, Void>() {
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground() {
|
|
||||||
messageTableModel.add(httpRequestResponse, url, method, status, length, comment, color, "", "");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResponseReceivedAction.continueWith(httpResponseReceived, annotations);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setColorAndCommentList(List<Map<String, String>> result) {
|
|
||||||
if (result != null && !result.isEmpty()) {
|
|
||||||
colorList.get().add(result.get(0).get("color"));
|
|
||||||
commentList.get().add(result.get(1).get("comment"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package hae.instances.http;
|
||||||
|
|
||||||
|
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.scanner.AuditResult;
|
||||||
|
import burp.api.montoya.scanner.ConsolidationAction;
|
||||||
|
import burp.api.montoya.scanner.ScanCheck;
|
||||||
|
import burp.api.montoya.scanner.audit.insertionpoint.AuditInsertionPoint;
|
||||||
|
import burp.api.montoya.scanner.audit.issues.AuditIssue;
|
||||||
|
import hae.component.board.message.MessageTableModel;
|
||||||
|
import hae.instances.http.utils.MessageProcessor;
|
||||||
|
import hae.utils.ConfigLoader;
|
||||||
|
import hae.utils.http.HttpUtils;
|
||||||
|
import hae.utils.string.StringProcessor;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static burp.api.montoya.scanner.AuditResult.auditResult;
|
||||||
|
import static burp.api.montoya.scanner.ConsolidationAction.KEEP_BOTH;
|
||||||
|
import static burp.api.montoya.scanner.ConsolidationAction.KEEP_EXISTING;
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
|
||||||
|
public class HttpMessagePassiveHandler implements ScanCheck {
|
||||||
|
private final MontoyaApi api;
|
||||||
|
private final ConfigLoader configLoader;
|
||||||
|
private final HttpUtils httpUtils;
|
||||||
|
private final MessageTableModel messageTableModel;
|
||||||
|
private final MessageProcessor messageProcessor;
|
||||||
|
|
||||||
|
public HttpMessagePassiveHandler(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) {
|
||||||
|
this.api = api;
|
||||||
|
this.configLoader = configLoader;
|
||||||
|
this.httpUtils = new HttpUtils(api, configLoader);
|
||||||
|
this.messageTableModel = messageTableModel;
|
||||||
|
this.messageProcessor = new MessageProcessor(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuditResult activeAudit(HttpRequestResponse httpRequestResponse, AuditInsertionPoint auditInsertionPoint) {
|
||||||
|
return auditResult(emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuditResult passiveAudit(HttpRequestResponse httpRequestResponse) {
|
||||||
|
List<String> colorList = new ArrayList<>();
|
||||||
|
List<String> commentList = new ArrayList<>();
|
||||||
|
|
||||||
|
HttpRequest request = httpRequestResponse.request();
|
||||||
|
HttpResponse response = httpRequestResponse.response();
|
||||||
|
|
||||||
|
boolean matches = httpUtils.verifyHttpRequestResponse(httpRequestResponse, "Proxy");
|
||||||
|
|
||||||
|
if (!matches) {
|
||||||
|
try {
|
||||||
|
String host = StringProcessor.getHostByUrl(request.url());
|
||||||
|
setColorAndCommentList(messageProcessor.processRequest(host, request, true), colorList, commentList);
|
||||||
|
setColorAndCommentList(messageProcessor.processResponse(host, response, true), colorList, commentList);
|
||||||
|
|
||||||
|
String url = request.url();
|
||||||
|
String method = request.method();
|
||||||
|
String status = String.valueOf(response.statusCode());
|
||||||
|
String color = messageProcessor.retrieveFinalColor(messageProcessor.retrieveColorIndices(colorList));
|
||||||
|
String comment = StringProcessor.mergeComment(String.join(", ", commentList));
|
||||||
|
String length = String.valueOf(response.toByteArray().length());
|
||||||
|
|
||||||
|
new SwingWorker<Void, Void>() {
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground() {
|
||||||
|
messageTableModel.add(httpRequestResponse, url, method, status, length, comment, color, "", "");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
} catch (Exception e) {
|
||||||
|
api.logging().logToError("passiveAudit: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return auditResult(emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setColorAndCommentList(List<Map<String, String>> result, List<String> colorList, List<String> commentList) {
|
||||||
|
if (result != null && !result.isEmpty()) {
|
||||||
|
colorList.add(result.get(0).get("color"));
|
||||||
|
commentList.add(result.get(1).get("comment"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConsolidationAction consolidateIssues(AuditIssue newIssue, AuditIssue existingIssue) {
|
||||||
|
return existingIssue.name().equals(newIssue.name()) ? KEEP_EXISTING : KEEP_BOTH;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -92,7 +92,7 @@ public class RegularMatcher {
|
|||||||
|
|
||||||
if (!result.isEmpty()) {
|
if (!result.isEmpty()) {
|
||||||
tmpMap.put("color", color);
|
tmpMap.put("color", color);
|
||||||
String dataStr = String.join("\n", result);
|
String dataStr = String.join(Config.boundary, result);
|
||||||
tmpMap.put("data", dataStr);
|
tmpMap.put("data", dataStr);
|
||||||
|
|
||||||
String nameAndSize = String.format("%s (%s)", name, result.size());
|
String nameAndSize = String.format("%s (%s)", name, result.size());
|
||||||
|
|||||||
@@ -77,8 +77,11 @@ public class ConfigLoader {
|
|||||||
|
|
||||||
public void initConfig() {
|
public void initConfig() {
|
||||||
Map<String, Object> r = new LinkedHashMap<>();
|
Map<String, Object> r = new LinkedHashMap<>();
|
||||||
r.put("excludeSuffix", getExcludeSuffix());
|
r.put("ExcludeSuffix", getExcludeSuffix());
|
||||||
r.put("blockHost", getBlockHost());
|
r.put("BlockHost", getBlockHost());
|
||||||
|
r.put("ExcludeStatus", getExcludeStatus());
|
||||||
|
r.put("LimitSize", getLimitSize());
|
||||||
|
r.put("HaEScope", getScope());
|
||||||
try {
|
try {
|
||||||
Writer ws = new OutputStreamWriter(Files.newOutputStream(Paths.get(configFilePath)), StandardCharsets.UTF_8);
|
Writer ws = new OutputStreamWriter(Files.newOutputStream(Paths.get(configFilePath)), StandardCharsets.UTF_8);
|
||||||
yaml.dump(r, ws);
|
yaml.dump(r, ws);
|
||||||
@@ -147,21 +150,33 @@ public class ConfigLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getBlockHost() {
|
public String getBlockHost() {
|
||||||
return getValueFromConfig("blockHost", Config.host);
|
return getValueFromConfig("BlockHost", Config.host);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getExcludeSuffix() {
|
public String getExcludeSuffix() {
|
||||||
return getValueFromConfig("excludeSuffix", Config.suffix);
|
return getValueFromConfig("ExcludeSuffix", Config.suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getExcludeStatus() {
|
||||||
|
return getValueFromConfig("ExcludeStatus", Config.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLimitSize() {
|
||||||
|
return getValueFromConfig("LimitSize", Config.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getScope() {
|
public String getScope() {
|
||||||
return getValueFromConfig("HaEScope", Config.scopeOptions);
|
return getValueFromConfig("HaEScope", Config.scopeOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getValueFromConfig(String name, String value) {
|
public boolean getMode() {
|
||||||
|
return getValueFromConfig("HaEModeStatus", Config.modeStatus).equals("true");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getValueFromConfig(String name, String defaultValue) {
|
||||||
File yamlSetting = new File(configFilePath);
|
File yamlSetting = new File(configFilePath);
|
||||||
if (!yamlSetting.exists() || !yamlSetting.isFile()) {
|
if (!yamlSetting.exists() || !yamlSetting.isFile()) {
|
||||||
return value;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (InputStream inorder = Files.newInputStream(Paths.get(configFilePath))) {
|
try (InputStream inorder = Files.newInputStream(Paths.get(configFilePath))) {
|
||||||
@@ -173,7 +188,7 @@ public class ConfigLoader {
|
|||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAlibabaAIAPIKey(String apiKey) {
|
public void setAlibabaAIAPIKey(String apiKey) {
|
||||||
@@ -189,17 +204,29 @@ public class ConfigLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setExcludeSuffix(String excludeSuffix) {
|
public void setExcludeSuffix(String excludeSuffix) {
|
||||||
setValueToConfig("excludeSuffix", excludeSuffix);
|
setValueToConfig("ExcludeSuffix", excludeSuffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBlockHost(String blockHost) {
|
public void setBlockHost(String blockHost) {
|
||||||
setValueToConfig("blockHost", blockHost);
|
setValueToConfig("BlockHost", blockHost);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExcludeStatus(String status) {
|
||||||
|
setValueToConfig("ExcludeStatus", status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLimitSize(String size) {
|
||||||
|
setValueToConfig("LimitSize", size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setScope(String scope) {
|
public void setScope(String scope) {
|
||||||
setValueToConfig("HaEScope", scope);
|
setValueToConfig("HaEScope", scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setMode(String mode) {
|
||||||
|
setValueToConfig("HaEModeStatus", mode);
|
||||||
|
}
|
||||||
|
|
||||||
private void setValueToConfig(String name, String value) {
|
private void setValueToConfig(String name, String value) {
|
||||||
Map<String, Object> currentConfig = loadCurrentConfig();
|
Map<String, Object> currentConfig = loadCurrentConfig();
|
||||||
currentConfig.put(name, value);
|
currentConfig.put(name, value);
|
||||||
|
|||||||
@@ -7,24 +7,40 @@ import java.awt.event.FocusListener;
|
|||||||
|
|
||||||
public class UIEnhancer {
|
public class UIEnhancer {
|
||||||
public static void setTextFieldPlaceholder(JTextField textField, String placeholderText) {
|
public static void setTextFieldPlaceholder(JTextField textField, String placeholderText) {
|
||||||
textField.setForeground(Color.GRAY);
|
// 使用客户端属性来存储占位符文本和占位符状态
|
||||||
textField.setText(placeholderText);
|
textField.putClientProperty("placeholderText", placeholderText);
|
||||||
|
textField.putClientProperty("isPlaceholder", true);
|
||||||
|
|
||||||
|
// 设置占位符文本和颜色
|
||||||
|
setPlaceholderText(textField);
|
||||||
|
|
||||||
textField.addFocusListener(new FocusListener() {
|
textField.addFocusListener(new FocusListener() {
|
||||||
@Override
|
@Override
|
||||||
public void focusGained(FocusEvent e) {
|
public void focusGained(FocusEvent e) {
|
||||||
if (textField.getText().equals(placeholderText)) {
|
// 当获得焦点且文本是占位符时,清除文本并更改颜色
|
||||||
|
if ((boolean) textField.getClientProperty("isPlaceholder")) {
|
||||||
textField.setText("");
|
textField.setText("");
|
||||||
textField.setForeground(Color.BLACK);
|
textField.setForeground(Color.BLACK);
|
||||||
|
textField.putClientProperty("isPlaceholder", false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void focusLost(FocusEvent e) {
|
public void focusLost(FocusEvent e) {
|
||||||
|
// 当失去焦点且文本为空时,设置占位符文本和颜色
|
||||||
if (textField.getText().isEmpty()) {
|
if (textField.getText().isEmpty()) {
|
||||||
textField.setForeground(Color.GRAY);
|
setPlaceholderText(textField);
|
||||||
textField.setText(placeholderText);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void setPlaceholderText(JTextField textField) {
|
||||||
|
String placeholderText = (String) textField.getClientProperty("placeholderText");
|
||||||
|
textField.setForeground(Color.GRAY);
|
||||||
|
textField.setText(placeholderText);
|
||||||
|
textField.putClientProperty("isPlaceholder", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,24 @@
|
|||||||
package hae.utils.http;
|
package hae.utils.http;
|
||||||
|
|
||||||
import burp.api.montoya.MontoyaApi;
|
import burp.api.montoya.MontoyaApi;
|
||||||
import burp.api.montoya.http.HttpService;
|
import burp.api.montoya.http.message.HttpRequestResponse;
|
||||||
import burp.api.montoya.http.message.requests.HttpRequest;
|
import burp.api.montoya.http.message.requests.HttpRequest;
|
||||||
import burp.api.montoya.http.message.requests.HttpTransformation;
|
import burp.api.montoya.http.message.requests.HttpTransformation;
|
||||||
|
import burp.api.montoya.http.message.responses.HttpResponse;
|
||||||
import burp.api.montoya.utilities.RandomUtils;
|
import burp.api.montoya.utilities.RandomUtils;
|
||||||
|
import hae.utils.ConfigLoader;
|
||||||
|
import hae.utils.string.StringProcessor;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class HttpUtils {
|
public class HttpUtils {
|
||||||
private final MontoyaApi api;
|
private final MontoyaApi api;
|
||||||
|
private final ConfigLoader configLoader;
|
||||||
|
|
||||||
public HttpUtils(MontoyaApi api) {
|
public HttpUtils(MontoyaApi api, ConfigLoader configLoader) {
|
||||||
this.api = api;
|
this.api = api;
|
||||||
|
this.configLoader = configLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpRequest generateRequestByMultipartUploadMethod(String url, String name, String filename, String content) {
|
public HttpRequest generateRequestByMultipartUploadMethod(String url, String name, String filename, String content) {
|
||||||
@@ -18,25 +26,54 @@ public class HttpUtils {
|
|||||||
|
|
||||||
String boundary = api.utilities().randomUtils().randomString(32, RandomUtils.CharacterSet.ASCII_LETTERS);
|
String boundary = api.utilities().randomUtils().randomString(32, RandomUtils.CharacterSet.ASCII_LETTERS);
|
||||||
|
|
||||||
StringBuilder newBody = new StringBuilder();
|
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) +
|
||||||
newBody.append(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") +
|
||||||
newBody.append(String.format("--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n", boundary, "purpose", "file-extract"));
|
"--" + boundary + "--\r\n";
|
||||||
newBody.append("--").append(boundary).append("--\r\n");
|
|
||||||
|
|
||||||
baseRequest = baseRequest.withUpdatedHeader("Content-Type", "multipart/form-data; boundary=" + boundary).withBody(newBody.toString());
|
baseRequest = baseRequest.withUpdatedHeader("Content-Type", "multipart/form-data; boundary=" + boundary).withBody(newBody);
|
||||||
|
|
||||||
return baseRequest;
|
return baseRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpRequest generateRequestByJsonMethod(String url, String data) {
|
|
||||||
HttpRequest baseRequest = HttpRequest.httpRequestFromUrl(url).withTransformationApplied(HttpTransformation.TOGGLE_METHOD);
|
|
||||||
HttpService baseService = baseRequest.httpService();
|
|
||||||
String requestString = baseRequest.toString().replace("application/x-www-form-urlencoded", "application/json");
|
|
||||||
baseRequest = HttpRequest.httpRequest(baseService, requestString).withBody(data);
|
|
||||||
return baseRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpRequest generateRequestByDeleteMethod(String url) {
|
public HttpRequest generateRequestByDeleteMethod(String url) {
|
||||||
return HttpRequest.httpRequestFromUrl(url).withMethod("DELETE");
|
return HttpRequest.httpRequestFromUrl(url).withMethod("DELETE");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean verifyHttpRequestResponse(HttpRequestResponse requestResponse, String toolType) {
|
||||||
|
HttpRequest request = requestResponse.request();
|
||||||
|
HttpResponse response = requestResponse.response();
|
||||||
|
boolean retStatus = false;
|
||||||
|
try {
|
||||||
|
String host = StringProcessor.getHostByUrl(request.url());
|
||||||
|
String[] hostList = configLoader.getBlockHost().split("\\|");
|
||||||
|
boolean isBlockHost = isBlockHost(hostList, host);
|
||||||
|
|
||||||
|
List<String> suffixList = Arrays.asList(configLoader.getExcludeSuffix().split("\\|"));
|
||||||
|
boolean isExcludeSuffix = suffixList.contains(request.fileExtension().toLowerCase());
|
||||||
|
|
||||||
|
boolean isToolScope = !configLoader.getScope().contains(toolType);
|
||||||
|
|
||||||
|
List<String> statusList = Arrays.asList(configLoader.getExcludeStatus().split("\\|"));
|
||||||
|
boolean isExcludeStatus = statusList.contains(String.valueOf(response.statusCode()));
|
||||||
|
|
||||||
|
retStatus = isExcludeSuffix || isBlockHost || isToolScope || isExcludeStatus;
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return retStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isBlockHost(String[] hostList, String host) {
|
||||||
|
boolean isBlockHost = false;
|
||||||
|
for (String hostName : hostList) {
|
||||||
|
String cleanedHost = StringProcessor.replaceFirstOccurrence(hostName, "*.", "");
|
||||||
|
if (hostName.contains("*.") && StringProcessor.matchFromEnd(host, cleanedHost)) {
|
||||||
|
isBlockHost = true;
|
||||||
|
} else if (host.equals(hostName) || hostName.equals("*")) {
|
||||||
|
isBlockHost = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isBlockHost;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ rules:
|
|||||||
sensitive: false
|
sensitive: false
|
||||||
- name: Windows File/Dir Path
|
- name: Windows File/Dir Path
|
||||||
loaded: true
|
loaded: true
|
||||||
f_regex: '[^\w](([a-zA-Z]:\\(?:\w+\\?)*)|([a-zA-Z]:\\(?:\w+\\)*\w+\.\w+))'
|
f_regex: '[^\w]([a-zA-Z]:\\\\?(?:[^<>:/\\|?*]+\\\\?)*)([^<>:/\\|?*]+(?:\.[^<>:/\\|?*]+)?)'
|
||||||
s_regex: ''
|
s_regex: ''
|
||||||
format: '{0}'
|
format: '{0}'
|
||||||
color: green
|
color: green
|
||||||
@@ -209,7 +209,7 @@ rules:
|
|||||||
sensitive: false
|
sensitive: false
|
||||||
- name: Sensitive Field
|
- name: Sensitive Field
|
||||||
loaded: true
|
loaded: true
|
||||||
f_regex: ((\[)?('|")?([\w]{0,10})((key)|(secret)|(token)|(config)|(auth)|(access)|(admin))([\w]{0,10})('|")?(\])?(
|
f_regex: ((\[)?('|")?([\w]{0,10})((key)|(secret)|(token)|(config)|(auth)|(access)|(admin)|(ticket))([\w]{0,10})('|")?(\])?(
|
||||||
|)(:|=)( |)('|")(.*?)('|")(|,))
|
|)(:|=)( |)('|")(.*?)('|")(|,))
|
||||||
s_regex: ''
|
s_regex: ''
|
||||||
format: '{0}'
|
format: '{0}'
|
||||||
@@ -237,18 +237,9 @@ rules:
|
|||||||
scope: response body
|
scope: response body
|
||||||
engine: dfa
|
engine: dfa
|
||||||
sensitive: false
|
sensitive: false
|
||||||
- name: HTML Notes
|
|
||||||
loaded: true
|
|
||||||
f_regex: (<!--.*?-->)
|
|
||||||
s_regex: ''
|
|
||||||
format: '{0}'
|
|
||||||
color: magenta
|
|
||||||
scope: response body
|
|
||||||
engine: nfa
|
|
||||||
sensitive: false
|
|
||||||
- name: Create Script
|
- name: Create Script
|
||||||
loaded: true
|
loaded: true
|
||||||
f_regex: (\+\{.*?\}\[[a-zA-Z]\]\+".*?\.js")
|
f_regex: (\{[^{}]*\}\s*\[[^\s]*\]\s*\+\s*"[^\s]*\.js")
|
||||||
s_regex: '"?([\w].*?)"?:"(.*?)"'
|
s_regex: '"?([\w].*?)"?:"(.*?)"'
|
||||||
format: '{0}.{1}'
|
format: '{0}.{1}'
|
||||||
color: green
|
color: green
|
||||||
@@ -257,7 +248,7 @@ rules:
|
|||||||
sensitive: false
|
sensitive: false
|
||||||
- name: URL Schemes
|
- name: URL Schemes
|
||||||
loaded: true
|
loaded: true
|
||||||
f_regex: ((?![http]|[https])(([-A-Za-z0-9]{1,20})://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]))
|
f_regex: (\b(?![\w]{0,10}?https?://)(([-A-Za-z0-9]{1,20})://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]))
|
||||||
s_regex: ''
|
s_regex: ''
|
||||||
format: '{0}'
|
format: '{0}'
|
||||||
color: yellow
|
color: yellow
|
||||||
@@ -282,3 +273,21 @@ rules:
|
|||||||
scope: response body
|
scope: response body
|
||||||
engine: nfa
|
engine: nfa
|
||||||
sensitive: true
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user