Compare commits

..

14 Commits
3.3.1 ... 3.4

Author SHA1 Message Date
gh0stkey
fa35b0a625 Version: 3.4 Update 2024-11-16 19:48:50 +08:00
gh0stkey
8ef98d20a9 Version: 3.4 Update 2024-11-16 18:42:15 +08:00
gh0stkey
e556abb6f7 Version: 3.4 Update 2024-11-16 18:06:49 +08:00
EvilChen
471aab5ea1 Merge pull request #221 from AabyssZG/master
Update Rules.yml
2024-10-24 11:48:57 +08:00
曾哥
76b475bd91 Update Rules.yml 2024-10-24 11:40:29 +08:00
gh0stkey
6014089594 Version: 3.3.4 Update 2024-10-14 16:35:20 +08:00
EvilChen
910658f2e0 Update 问题反馈.md 2024-09-23 00:05:40 +08:00
gh0stkey
8692b0a494 Version: 3.3.3 Update 2024-09-19 17:45:47 +08:00
gh0stkey
5419d4a679 Version: 3.3.3 Update 2024-09-19 17:11:55 +08:00
gh0stkey
ae8cb2fd25 Version: 3.3.3 Update 2024-09-19 17:08:46 +08:00
EvilChen
5b6bdbe5b6 Update README.md 2024-08-28 16:19:24 +08:00
EvilChen
ddb08e9a6e Update README.md 2024-08-28 16:18:25 +08:00
EvilChen
6a2f289d57 Update build.gradle 2024-08-26 10:04:57 +08:00
gh0stkey
84746a7089 Version: 3.3.2 Update 2024-08-23 22:03:31 +08:00
21 changed files with 428 additions and 149 deletions

View File

@@ -14,7 +14,9 @@ HaE 版本:
有无自定义规则:
BurpSuite 版本:
操作系统版本:
有无仔细阅读README
是否阅读README
是否知晓注意事项:
是否查阅历史ISSUE
```
## 问题详情

View File

@@ -12,9 +12,14 @@
> 随着现代化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)
**注意事项**:

View File

@@ -22,6 +22,8 @@ dependencies {
implementation 'org.yaml:snakeyaml:2.0'
implementation 'dk.brics.automaton:automaton:1.11-8'
implementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
implementation 'com.google.code.gson:gson:2.11.0'
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
}
test {

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

View File

@@ -12,6 +12,10 @@ public class Config {
public static String status = "404";
public static String size = "0";
public static String boundary = "\n\t\n";
public static String[] scope = new String[]{
"any",
"any header",
@@ -28,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 modeStatus = "true";
public static String[] ruleFields = {
"Loaded", "Name", "F-Regex", "S-Regex", "Format", "Color", "Scope", "Engine", "Sensitive"
};

View File

@@ -10,7 +10,6 @@ import hae.component.board.message.MessageTableModel;
import hae.instances.editor.RequestEditor;
import hae.instances.editor.ResponseEditor;
import hae.instances.editor.WebSocketEditor;
import hae.instances.http.HttpMessageHandler;
import hae.instances.websocket.WebSocketMessageHandler;
import hae.utils.ConfigLoader;
@@ -18,7 +17,7 @@ public class HaE implements BurpExtension {
@Override
public void initialize(MontoyaApi api) {
// 设置扩展名称
String version = "3.3.1";
String version = "3.4";
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);
MessageTableModel messageTableModel = new MessageTableModel(api);
MessageTableModel messageTableModel = new MessageTableModel(api, configLoader);
// 注册Tab页用于查询数据
api.userInterface().registerSuiteTab("HaE", new Main(api, configLoader, messageTableModel));
// 注册HTTP处理器
api.http().registerHttpHandler(new HttpMessageHandler(api, configLoader, messageTableModel));
// 注册WebSocket处理器
api.proxy().registerWebSocketCreationHandler(proxyWebSocketCreation -> proxyWebSocketCreation.proxyWebSocket().registerProxyMessageHandler(new WebSocketMessageHandler(api)));

View File

@@ -1,7 +1,11 @@
package hae.component.config;
package hae.component;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.core.Registration;
import hae.component.board.message.MessageTableModel;
import hae.component.rule.Rules;
import hae.instances.http.HttpMessageActiveHandler;
import hae.instances.http.HttpMessagePassiveHandler;
import hae.utils.ConfigLoader;
import hae.utils.UIEnhancer;
@@ -16,24 +20,29 @@ import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.*;
import java.util.List;
import java.util.*;
public class Config extends JPanel {
private final MontoyaApi api;
private final ConfigLoader configLoader;
private final MessageTableModel messageTableModel;
private final Rules rules;
private final String defaultText = "Enter a new item";
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.configLoader = configLoader;
this.messageTableModel = messageTableModel;
this.rules = rules;
this.activeHandler = api.http().registerHttpHandler(new HttpMessageActiveHandler(api, configLoader, messageTableModel));
this.passiveHandler = api.scanner().registerScanCheck(new HttpMessagePassiveHandler(api, configLoader, messageTableModel));
initComponents();
}
@@ -68,10 +77,33 @@ public class Config extends JPanel {
String[] settingMode = new String[]{"Exclude suffix", "Block host", "Exclude status"};
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();
JScrollPane scopeScrollPane = new JScrollPane(scopePanel);
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);
String[] aiMode = new String[]{"Alibaba", "Moonshot"};
@@ -112,23 +144,67 @@ public class Config extends JPanel {
private JPanel getScopePanel() {
JPanel scopePanel = new JPanel();
scopePanel.setLayout(new BoxLayout(scopePanel, BoxLayout.X_AXIS));
scopePanel.setBorder(new EmptyBorder(3, 0, 6, 0));
String[] scopeInit = hae.Config.scopeOptions.split("\\|");
String[] scopeMode = configLoader.getScope().split("\\|");
for (String scope : scopeInit) {
JCheckBox checkBox = new JCheckBox(scope);
scopePanel.add(checkBox);
checkBox.addActionListener(e -> updateScope(checkBox));
for (String mode : scopeMode) {
if (scope.equals(mode)) {
checkBox.setSelected(true);
}
}
checkBox.addActionListener(e -> updateScope(checkBox));
updateScope(checkBox);
}
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) {
return new TableModelListener() {
@Override
@@ -153,6 +229,7 @@ public class Config extends JPanel {
configLoader.setExcludeStatus(values);
}
}
}
};
}
@@ -250,12 +327,12 @@ public class Config extends JPanel {
JComboBox<String> setTypeComboBox = new JComboBox<>();
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.setSelectedItem(mode[0]);
model.addTableModelListener(type.equals("AI+") ? craeteAITableModelListener(setTypeComboBox, model) : craeteSettingTableModelListener(setTypeComboBox, model));
constraints.insets = new Insets(0, 0, 3, 0);
constraints.gridy = 0;
buttonPanel.add(setTypeComboBox, constraints);
@@ -279,13 +356,13 @@ public class Config extends JPanel {
settingPanel.add(inputPanel, BorderLayout.CENTER);
addButton.addActionListener(e -> addActionPerformed(e, model, addTextField));
addButton.addActionListener(e -> addActionPerformed(e, model, addTextField, setTypeComboBox.getSelectedItem().toString()));
addTextField.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
addActionPerformed(null, model, addTextField);
addActionPerformed(null, model, addTextField, setTypeComboBox.getSelectedItem().toString());
}
}
});
@@ -294,7 +371,6 @@ public class Config extends JPanel {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
try {
String data = (String) clipboard.getData(DataFlavor.stringFlavor);
if (data != null && !data.isEmpty()) {
addDataToTable(data, model);
}
@@ -311,7 +387,6 @@ public class Config extends JPanel {
clearButton.addActionListener(e -> model.setRowCount(0));
JPanel settingMainPanel = new JPanel(new BorderLayout());
settingMainPanel.setBorder(new EmptyBorder(5, 15, 10, 15));
JScrollPane settingScroller = new JScrollPane(settingPanel);
@@ -370,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) {
String boxText = checkBox.getText();
boolean selected = checkBox.isSelected();
@@ -385,14 +483,13 @@ public class Config extends JPanel {
configLoader.setScope(String.join("|", HaEScope));
}
private void addActionPerformed(ActionEvent e, DefaultTableModel model, JTextField addTextField) {
private void addActionPerformed(ActionEvent e, DefaultTableModel model, JTextField addTextField, String comboBoxSelected) {
String addTextFieldText = addTextField.getText();
api.logging().logToOutput(addTextFieldText);
if (!addTextFieldText.equals(defaultText)) {
if (addTextField.getForeground().equals(Color.BLACK)) {
addDataToTable(addTextFieldText, model);
addTextField.setText("");
addTextField.requestFocusInWindow();
}
addTextField.setText("");
addTextField.requestFocusInWindow();
}
private void onlineUpdateActionPerformed(ActionEvent e) {

View File

@@ -3,7 +3,6 @@ package hae.component;
import burp.api.montoya.MontoyaApi;
import hae.component.board.Databoard;
import hae.component.board.message.MessageTableModel;
import hae.component.config.Config;
import hae.component.rule.Rules;
import hae.utils.ConfigLoader;
@@ -58,7 +57,7 @@ public class Main extends JPanel {
Rules rules = new Rules(api, configLoader);
mainTabbedPane.addTab("Rules", rules);
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) {

View File

@@ -8,6 +8,7 @@ import hae.component.board.message.MessageTableModel.MessageTable;
import hae.component.board.table.Datatable;
import hae.instances.http.utils.RegularMatcher;
import hae.utils.ConfigLoader;
import hae.utils.UIEnhancer;
import hae.utils.project.ProjectProcessor;
import hae.utils.project.model.HaeFileContent;
import hae.utils.string.StringProcessor;
@@ -54,6 +55,8 @@ public class Databoard extends JPanel {
private SwingWorker<List<Object[]>, Void> exportActionWorker;
private SwingWorker<List<Object[]>, Void> importActionWorker;
private final String defaultText = "Please enter the host";
public Databoard(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) {
this.api = api;
this.configLoader = configLoader;
@@ -85,6 +88,7 @@ public class Databoard extends JPanel {
menu.add(menuPanel);
hostTextField = new JTextField();
UIEnhancer.setTextFieldPlaceholder(hostTextField, defaultText);
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
dataTabbedPane = new JTabbedPane(JTabbedPane.TOP);
@@ -415,8 +419,8 @@ public class Databoard extends JPanel {
JTable taskStatusTable = new JTable(taskStatusTableModel);
for (Object[] data : dataList) {
int rowCount = taskStatusTable.getRowCount();
int id = rowCount > 0 ? (Integer) taskStatusTable.getValueAt(rowCount - 1, 0) + 1 : 1;
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);

View File

@@ -1,7 +1,6 @@
package hae.component.board.message;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.core.ByteArray;
import burp.api.montoya.http.message.HttpHeader;
import burp.api.montoya.http.message.HttpRequestResponse;
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 hae.Config;
import hae.cache.CachePool;
import hae.utils.ConfigLoader;
import hae.utils.project.FileProcessor;
import hae.utils.string.HashCalculator;
import hae.utils.string.StringProcessor;
@@ -23,6 +23,8 @@ import javax.swing.table.TableRowSorter;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
@@ -30,15 +32,17 @@ import static burp.api.montoya.ui.editor.EditorOptions.READ_ONLY;
public class MessageTableModel extends AbstractTableModel {
private final MontoyaApi api;
private final ConfigLoader configLoader;
private final MessageTable messageTable;
private final JSplitPane splitPane;
private final LinkedList<MessageEntry> log = new LinkedList<>();
private final LinkedList<MessageEntry> filteredLog;
private SwingWorker<Void, Void> currentWorker;
public MessageTableModel(MontoyaApi api) {
public MessageTableModel(MontoyaApi api, ConfigLoader configLoader) {
this.filteredLog = new LinkedList<>();
this.api = api;
this.configLoader = configLoader;
JTabbedPane messageTab = new JTabbedPane();
UserInterface userInterface = api.userInterface();
@@ -435,7 +439,7 @@ public class MessageTableModel extends AbstractTableModel {
public class MessageTable extends JTable {
private MessageEntry messageEntry;
private SwingWorker<ByteArray[], Void> currentWorker;
private final ExecutorService executorService;
private int lastSelectedIndex = -1;
private final HttpRequestEditor requestEditor;
private final HttpResponseEditor responseEditor;
@@ -444,52 +448,31 @@ public class MessageTableModel extends AbstractTableModel {
super(messageTableModel);
this.requestEditor = requestEditor;
this.responseEditor = responseEditor;
this.executorService = Executors.newSingleThreadExecutor();
}
@Override
public void changeSelection(int row, int col, boolean toggle, boolean extend) {
super.changeSelection(row, col, toggle, extend);
if (currentWorker != null && !currentWorker.isDone()) {
currentWorker.cancel(true);
int selectedIndex = convertRowIndexToModel(row);
if (lastSelectedIndex != selectedIndex) {
lastSelectedIndex = selectedIndex;
executorService.execute(this::getSelectedMessage);
}
}
currentWorker = new SwingWorker<>() {
@Override
protected ByteArray[] doInBackground() {
int selectedIndex = convertRowIndexToModel(row);
if (lastSelectedIndex != selectedIndex) {
lastSelectedIndex = selectedIndex;
messageEntry = filteredLog.get(selectedIndex);
private void getSelectedMessage() {
messageEntry = filteredLog.get(lastSelectedIndex);
HttpRequestResponse httpRequestResponse = messageEntry.getRequestResponse();
HttpRequestResponse httpRequestResponse = messageEntry.getRequestResponse();
ByteArray requestByte = httpRequestResponse.request().toByteArray();
ByteArray responseByte = httpRequestResponse.response().toByteArray();
ByteArray[] httpByteArray = new ByteArray[2];
httpByteArray[0] = requestByte;
httpByteArray[1] = responseByte;
return httpByteArray;
}
return null;
}
@Override
protected void done() {
try {
ByteArray[] retByteArray = get();
if (retByteArray != null) {
requestEditor.setRequest(HttpRequest.httpRequest(messageEntry.getRequestResponse().httpService(), retByteArray[0]));
responseEditor.setResponse(HttpResponse.httpResponse(retByteArray[1]));
}
} catch (Exception ignored) {
}
}
};
currentWorker.execute();
requestEditor.setRequest(HttpRequest.httpRequest(messageEntry.getRequestResponse().httpService(), httpRequestResponse.request().toByteArray()));
int responseSizeWithMb = httpRequestResponse.response().toString().length() / 1024 / 1024;
if ((responseSizeWithMb < Integer.parseInt(configLoader.getLimitSize())) || configLoader.getLimitSize().equals("0")) {
responseEditor.setResponse(httpRequestResponse.response());
} else {
responseEditor.setResponse(HttpResponse.httpResponse("Exceeds length limit."));
}
}
}
}

View File

@@ -23,6 +23,7 @@ import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
@@ -34,6 +35,7 @@ public class Datatable extends JPanel {
private final JTable dataTable;
private final DefaultTableModel dataTableModel;
private final JTextField searchField;
private final JTextField secondSearchField;
private final TableRowSorter<DefaultTableModel> sorter;
private final JCheckBox searchMode = new JCheckBox("Reverse search");
private final String tabName;
@@ -52,7 +54,8 @@ public class Datatable extends JPanel {
this.dataTable = new JTable(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.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) {
if (!item.isEmpty()) {
addRowToTable(new Object[]{item});
}
}
// 设置灰色默认文本
String searchText = "Search";
UIEnhancer.setTextFieldPlaceholder(searchField, searchText);
// 监听输入框内容输入、更新、删除
UIEnhancer.setTextFieldPlaceholder(searchField, "Search");
searchField.getDocument().addDocumentListener(new DocumentListener() {
@Override
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);
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));
JPanel optionsPanel = new JPanel();
@@ -162,6 +181,8 @@ public class Datatable extends JPanel {
optionsPanel.add(Box.createHorizontalStrut(5));
optionsPanel.add(searchField);
optionsPanel.add(Box.createHorizontalStrut(5));
optionsPanel.add(secondSearchField);
optionsPanel.add(Box.createHorizontalStrut(5));
optionsPanel.add(aiEmpoweredButton);
footerPanel.setBorder(BorderFactory.createEmptyBorder(2, 3, 5, 3));
@@ -252,29 +273,61 @@ public class Datatable extends JPanel {
}
private void performSearch() {
RowFilter<Object, Object> firstRowFilter = applyFirstSearchFilter();
RowFilter<Object, Object> secondRowFilter = applySecondFilter();
if (searchField.getForeground().equals(Color.BLACK)) {
RowFilter<Object, Object> rowFilter = 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();
}
}
};
sorter.setRowFilter(rowFilter);
sorter.setRowFilter(firstRowFilter);
if (secondSearchField.getForeground().equals(Color.BLACK)) {
List<RowFilter<Object, Object>> filters = new ArrayList<>();
filters.add(firstRowFilter);
filters.add(secondRowFilter);
sorter.setRowFilter(RowFilter.andFilter(filters));
}
}
}
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) {
// 表格复制功能
dataTable.setTransferHandler(new TransferHandler() {

View File

@@ -9,6 +9,7 @@ import burp.api.montoya.ui.Selection;
import burp.api.montoya.ui.editor.extension.EditorCreationContext;
import burp.api.montoya.ui.editor.extension.ExtensionProvidedHttpRequestEditor;
import burp.api.montoya.ui.editor.extension.HttpRequestEditorProvider;
import hae.Config;
import hae.component.board.table.Datatable;
import hae.instances.http.utils.MessageProcessor;
import hae.utils.ConfigLoader;
@@ -132,7 +133,7 @@ public class RequestEditor implements HttpRequestEditorProvider {
Map<String, String> dataMap = result.get(0);
if (dataMap != null && !dataMap.isEmpty()) {
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));
tabbedPane.addTab(i, dataPanel);
});

View File

@@ -17,7 +17,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class HttpMessageHandler implements HttpHandler {
public class HttpMessageActiveHandler implements HttpHandler {
private final MontoyaApi api;
private final ConfigLoader configLoader;
private final HttpUtils httpUtils;
@@ -29,9 +29,8 @@ public class HttpMessageHandler implements HttpHandler {
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<HttpRequest> httpRequest = new ThreadLocal<>();
public HttpMessageHandler(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) {
public HttpMessageActiveHandler(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) {
this.api = api;
this.configLoader = configLoader;
this.httpUtils = new HttpUtils(api, configLoader);
@@ -47,7 +46,6 @@ public class HttpMessageHandler implements HttpHandler {
Annotations annotations = httpRequestToBeSent.annotations();
try {
httpRequest.set(httpRequestToBeSent);
host.set(StringProcessor.getHostByUrl(httpRequestToBeSent.url()));
} catch (Exception e) {
api.logging().logToError("handleHttpRequestToBeSent: " + e.getMessage());
@@ -70,29 +68,26 @@ public class HttpMessageHandler implements HttpHandler {
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);
HttpRequestResponse httpRequestResponse = HttpRequestResponse.httpRequestResponse(httpRequest.get(), httpResponseReceived);
// 添加到Databoard
String method = httpRequest.get().method();
String url = httpRequest.get().url();
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;
}
}.run();
}.execute();
}
} catch (Exception e) {
api.logging().logToError("handleHttpResponseReceived: " + e.getMessage());

View File

@@ -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;
}
}

View File

@@ -92,7 +92,7 @@ public class RegularMatcher {
if (!result.isEmpty()) {
tmpMap.put("color", color);
String dataStr = String.join("\n", result);
String dataStr = String.join(Config.boundary, result);
tmpMap.put("data", dataStr);
String nameAndSize = String.format("%s (%s)", name, result.size());

View File

@@ -77,8 +77,11 @@ public class ConfigLoader {
public void initConfig() {
Map<String, Object> r = new LinkedHashMap<>();
r.put("excludeSuffix", getExcludeSuffix());
r.put("blockHost", getBlockHost());
r.put("ExcludeSuffix", getExcludeSuffix());
r.put("BlockHost", getBlockHost());
r.put("ExcludeStatus", getExcludeStatus());
r.put("LimitSize", getLimitSize());
r.put("HaEScope", getScope());
try {
Writer ws = new OutputStreamWriter(Files.newOutputStream(Paths.get(configFilePath)), StandardCharsets.UTF_8);
yaml.dump(r, ws);
@@ -158,14 +161,22 @@ public class ConfigLoader {
return getValueFromConfig("ExcludeStatus", Config.status);
}
public String getLimitSize() {
return getValueFromConfig("LimitSize", Config.size);
}
public String getScope() {
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);
if (!yamlSetting.exists() || !yamlSetting.isFile()) {
return value;
return defaultValue;
}
try (InputStream inorder = Files.newInputStream(Paths.get(configFilePath))) {
@@ -177,7 +188,7 @@ public class ConfigLoader {
} catch (Exception ignored) {
}
return value;
return defaultValue;
}
public void setAlibabaAIAPIKey(String apiKey) {
@@ -204,10 +215,18 @@ public class ConfigLoader {
setValueToConfig("ExcludeStatus", status);
}
public void setLimitSize(String size) {
setValueToConfig("LimitSize", size);
}
public void setScope(String scope) {
setValueToConfig("HaEScope", scope);
}
public void setMode(String mode) {
setValueToConfig("HaEModeStatus", mode);
}
private void setValueToConfig(String name, String value) {
Map<String, Object> currentConfig = loadCurrentConfig();
currentConfig.put(name, value);

View File

@@ -7,24 +7,40 @@ import java.awt.event.FocusListener;
public class UIEnhancer {
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() {
@Override
public void focusGained(FocusEvent e) {
if (textField.getText().equals(placeholderText)) {
// 当获得焦点且文本是占位符时,清除文本并更改颜色
if ((boolean) textField.getClientProperty("isPlaceholder")) {
textField.setText("");
textField.setForeground(Color.BLACK);
textField.putClientProperty("isPlaceholder", false);
}
}
@Override
public void focusLost(FocusEvent e) {
// 当失去焦点且文本为空时,设置占位符文本和颜色
if (textField.getText().isEmpty()) {
textField.setForeground(Color.GRAY);
textField.setText(placeholderText);
setPlaceholderText(textField);
}
}
});
}
private static void setPlaceholderText(JTextField textField) {
String placeholderText = (String) textField.getClientProperty("placeholderText");
textField.setForeground(Color.GRAY);
textField.setText(placeholderText);
textField.putClientProperty("isPlaceholder", true);
}
}

View File

@@ -26,12 +26,11 @@ public class HttpUtils {
String boundary = api.utilities().randomUtils().randomString(32, RandomUtils.CharacterSet.ASCII_LETTERS);
StringBuilder newBody = new StringBuilder();
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));
newBody.append(String.format("--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n", boundary, "purpose", "file-extract"));
newBody.append("--").append(boundary).append("--\r\n");
String newBody = String.format("--%s\r\nContent-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n\r\n%s\r\n", boundary, name, filename, content) +
String.format("--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n", boundary, "purpose", "file-extract") +
"--" + boundary + "--\r\n";
baseRequest = baseRequest.withUpdatedHeader("Content-Type", "multipart/form-data; boundary=" + boundary).withBody(newBody.toString());
baseRequest = baseRequest.withUpdatedHeader("Content-Type", "multipart/form-data; boundary=" + boundary).withBody(newBody);
return baseRequest;
}
@@ -44,20 +43,25 @@ public class HttpUtils {
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);
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());
List<String> suffixList = Arrays.asList(configLoader.getExcludeSuffix().split("\\|"));
boolean isExcludeSuffix = suffixList.contains(request.fileExtension().toLowerCase());
boolean isToolScope = !configLoader.getScope().contains(toolType);
boolean isToolScope = !configLoader.getScope().contains(toolType);
List<String> statusList = Arrays.asList(configLoader.getExcludeStatus().split("\\|"));
boolean isExcludeStatus = statusList.contains(String.valueOf(response.statusCode()));
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 isExcludeSuffix || isBlockHost || isToolScope || isExcludeStatus;
return retStatus;
}
private boolean isBlockHost(String[] hostList, String host) {

View File

@@ -153,7 +153,7 @@ rules:
sensitive: false
- name: Windows File/Dir Path
loaded: true
f_regex: '[^\w](([a-zA-Z]:\\(?:\w+\\?)*)|([a-zA-Z]:\\(?:\w+\\)*\w+\.\w+))'
f_regex: '[^\w]([a-zA-Z]:\\\\?(?:[^<>:/\\|?*]+\\\\?)*)([^<>:/\\|?*]+(?:\.[^<>:/\\|?*]+)?)'
s_regex: ''
format: '{0}'
color: green
@@ -237,15 +237,6 @@ rules:
scope: response body
engine: dfa
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
loaded: true
f_regex: (\{[^{}]*\}\s*\[[^\s]*\]\s*\+\s*"[^\s]*\.js")
@@ -257,7 +248,7 @@ rules:
sensitive: false
- name: URL Schemes
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: ''
format: '{0}'
color: yellow
@@ -291,3 +282,12 @@ rules:
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