Compare commits

..

8 Commits
3.0 ... 3.1

Author SHA1 Message Date
gh0stkey
332b119064 Version: 3.1 Update 2024-05-23 12:00:13 +08:00
gh0stkey
ead03d42b9 Version: 3.0.2 Update 2024-05-12 19:25:33 +08:00
gh0stkey
4da3d3f42d Version: 3.0.2 Update 2024-05-12 19:02:38 +08:00
EvilChen
3363ca25ed Update issue templates 2024-05-11 09:56:23 +08:00
gh0stkey
496d0d2174 Version: 3.0.1 Update 2024-05-11 09:44:19 +08:00
gh0stkey
f387834c4d Version: 3.0.1 Update 2024-05-09 13:34:38 +08:00
gh0stkey
ca773f368b Version: 3.0.1 Update 2024-05-09 13:32:22 +08:00
gh0stkey
a6cd01300b Version: 3.0 Update 2024-05-07 16:08:46 +08:00
26 changed files with 977 additions and 347 deletions

View File

@@ -1,7 +1,7 @@
---
name: 问题反馈
about: 尽可能详细的描述问题并反馈
title: "[BUG] "
title: "[BUG] 问题标题"
labels: bug
assignees: ''
@@ -10,11 +10,11 @@ assignees: ''
## 使用环境
```
HaE版本
是否有自定义的HaE规则:
BurpSuite版本
JDK版本
HaE 版本:
有无自定义规则:
BurpSuite 版本:
操作系统版本:
有无仔细阅读README
```
## 问题详情

View File

@@ -13,7 +13,9 @@
**注意事项**:
1. 由于HaE 3.0版本开始采用`Montoya API`进行开发因此使用新版HaE需要升级你的BurpSuite版本>=2023.12.1)。
2. 自定义HaE规则必须用左右括号`()`将所需提取的表达式内容包含,例如你要匹配一个**Shiro应用**的响应报文,正常匹配规则为`rememberMe=delete`在HaE的规则中就需要变成`(rememberMe=delete)`
2. 由于HaE 2.6版本后对规则字段进行了更新,因此无法适配<=2.6版本的规则,请用户自行前往[规则转换页面](https://gh0st.cn/HaE/ConversionRule.html)进行转换
3. HaE官方规则库存放在[Github](https://raw.githubusercontent.com/gh0stkey/HaE/gh-pages/Rules.yml)上因此默认加载HaE官方规则库需使用代理BApp审核不允许使用CDN
4. 自定义HaE规则必须用左右括号`()`将所需提取的表达式内容包含,例如你要匹配一个**Shiro应用**的响应报文,正常匹配规则为`rememberMe=delete`在HaE的规则中就需要变成`(rememberMe=delete)`
## 使用方法
@@ -28,9 +30,7 @@
### 规则释义
HaE目前的规则一共有8个字段分别是规则名称、规则正则、规则作用域、正则引擎、规则匹配颜色、规则敏感性。
详细的含义如下所示:
HaE目前的规则一共有8个字段详细的含义如下所示:
| 字段 | 含义 |
|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
@@ -38,7 +38,7 @@ HaE目前的规则一共有8个字段分别是规则名称、规则正则、
| F-Regex | 规则正则主要用于填写正则表达式。在HaE中所需提取匹配的内容需要用`(``)`将正则表达式进行包裹。|
| S-Regex | 规则正则作用及使用同F-Regex。S-Regex为二次正则可以用于对F-Regex匹配的数据结果进行二次的匹配提取如不需要的情况下可以留空。|
| Format | 格式化输出在NFA引擎的正则表达式中我们可以通过`{0}``{1}``{2}`…的方式进行取分组格式化输出。默认情况下使用`{0}`即可。 |
| Scope | 规则作用域主要用于表示当前规则作用于HTTP报文的哪个部分。 |
| Scope | 规则作用域主要用于表示当前规则作用于HTTP报文的哪个部分。支持请求、响应的行、头、体,以及完整的报文。 |
| Engine | 正则引擎,主要用于表示当前规则的正则表达式所使用的引擎。**DFA引擎**:对于文本串里的每一个字符只需扫描一次,速度快、特性少;**NFA引擎**:要翻来覆去标注字符、取消标注字符,速度慢,但是特性(如:分组、替换、分割)丰富。 |
| Color | 规则匹配颜色主要用于表示当前规则匹配到对应HTTP报文时所需标记的高亮颜色。在HaE中具备颜色升级算法当出现相同颜色时会自动向上升级一个颜色进行标记。 |
| Sensitive | 规则敏感性,主要用于表示当前规则对于大小写字母是否敏感,敏感(`True`)则严格按照大小写要求匹配,不敏感(`False`)则反之。 |

View File

@@ -8,34 +8,38 @@ import java.util.concurrent.ConcurrentHashMap;
public class Config {
public static String suffix = "3g2|3gp|7z|aac|abw|aif|aifc|aiff|apk|arc|au|avi|azw|bat|bin|bmp|bz|bz2|cmd|cmx|cod|com|csh|css|csv|dll|doc|docx|ear|eot|epub|exe|flac|flv|gif|gz|ico|ics|ief|jar|jfif|jpe|jpeg|jpg|less|m3u|mid|midi|mjs|mkv|mov|mp2|mp3|mp4|mpa|mpe|mpeg|mpg|mpkg|mpp|mpv2|odp|ods|odt|oga|ogg|ogv|ogx|otf|pbm|pdf|pgm|png|pnm|ppm|ppt|pptx|ra|ram|rar|ras|rgb|rmi|rtf|scss|sh|snd|svg|swf|tar|tif|tiff|ttf|vsd|war|wav|weba|webm|webp|wmv|woff|woff2|xbm|xls|xlsx|xpm|xul|xwd|zip";
public static String[] scope = new String[] {
public static String host = "gh0st.cn";
public static String[] scope = new String[]{
"any",
"any header",
"any body",
"response",
"response line",
"response header",
"response body",
"request",
"request line",
"request header",
"request body"
};
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"
};
public static Object[][] ruleTemplate = new Object[][] {
public static Object[][] ruleTemplate = new Object[][]{
{
false, "New Name", "(First Regex)", "(Second Regex)", "{0}", "gray", "any", "nfa", false
}
};
public static String[] engine = new String[] {
public static String[] engine = new String[]{
"nfa",
"dfa"
};
public static String[] color = new String[] {
public static String[] color = new String[]{
"red",
"orange",
"yellow",

View File

@@ -16,7 +16,7 @@ public class HaE implements BurpExtension {
@Override
public void initialize(MontoyaApi api) {
// 设置扩展名称
String version = "3.0";
String version = "3.1";
api.extension().setName(String.format("HaE (%s) - Highlighter and Extractor", version));
// 加载扩展后输出的项目信息
@@ -34,14 +34,14 @@ public class HaE implements BurpExtension {
api.userInterface().registerSuiteTab("HaE", new Main(api, configLoader, messageTableModel));
// 注册HTTP处理器
api.http().registerHttpHandler(new HttpMessageHandler(api, messageTableModel));
api.http().registerHttpHandler(new HttpMessageHandler(api, configLoader, messageTableModel));
// 注册WebSocket处理器
api.proxy().registerWebSocketCreationHandler(proxyWebSocketCreation -> proxyWebSocketCreation.proxyWebSocket().registerProxyMessageHandler(new WebSocketMessageHandler(api)));
// 注册消息编辑框(用于展示数据)
api.userInterface().registerHttpRequestEditorProvider(new RequestEditor(api));
api.userInterface().registerHttpResponseEditorProvider(new ResponseEditor(api));
api.userInterface().registerHttpRequestEditorProvider(new RequestEditor(api, configLoader));
api.userInterface().registerHttpResponseEditorProvider(new ResponseEditor(api, configLoader));
api.userInterface().registerWebSocketMessageEditorProvider(new WebSocketEditor(api));
}
}

View File

@@ -1,19 +1,34 @@
package hae.cache;
import java.util.*;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class CachePool {
private static final Map<String, Map<String, Map<String, Object>>> cache = new HashMap<>();
private static final int MAX_SIZE = 100000;
private static final int EXPIRE_DURATION = 5;
public static void addToCache(String key, Map<String, Map<String, Object>> value) {
private static final Cache<String, Map<String, Map<String, Object>>> cache =
Caffeine.newBuilder()
.maximumSize(MAX_SIZE)
.expireAfterWrite(EXPIRE_DURATION, TimeUnit.HOURS)
.build();
public static void put(String key, Map<String, Map<String, Object>> value) {
cache.put(key, value);
}
public static Map<String, Map<String, Object>> getFromCache(String key) {
return cache.get(key);
public static Map<String, Map<String, Object>> get(String key) {
return cache.getIfPresent(key);
}
public static void removeFromCache(String key) {
cache.remove(key);
public static void remove(String key) {
cache.invalidate(key);
}
public static void clear() {
cache.invalidateAll();
}
}

View File

@@ -28,10 +28,10 @@ public class Main extends JPanel {
private void initComponents() {
setLayout(new GridBagLayout());
((GridBagLayout)getLayout()).columnWidths = new int[] {0, 0};
((GridBagLayout)getLayout()).rowHeights = new int[] {0, 0};
((GridBagLayout)getLayout()).columnWeights = new double[] {1.0, 1.0E-4};
((GridBagLayout)getLayout()).rowWeights = new double[] {1.0, 1.0E-4};
((GridBagLayout) getLayout()).columnWidths = new int[]{0, 0};
((GridBagLayout) getLayout()).rowHeights = new int[]{0, 0};
((GridBagLayout) getLayout()).columnWeights = new double[]{1.0, 1.0E-4};
((GridBagLayout) getLayout()).rowWeights = new double[]{1.0, 1.0E-4};
JTabbedPane mainTabbedPane = new JTabbedPane();
@@ -66,17 +66,17 @@ public class Main extends JPanel {
// 依次添加Rules、Config、Databoard
Rules rules = new Rules(api, configLoader);
mainTabbedPane.addTab("Rules", rules);
mainTabbedPane.addTab("Config", new Config(api, configLoader, rules));
mainTabbedPane.addTab("Databoard", new Databoard(api, configLoader, messageTableModel));
mainTabbedPane.addTab("Config", new Config(api, configLoader, rules));
}
private ImageIcon getImageIcon(boolean isDark) {
ClassLoader classLoader = getClass().getClassLoader();
URL imageURL;
if (isDark) {
imageURL = classLoader.getResource("logo.png");
imageURL = classLoader.getResource("logo/logo.png");
} else {
imageURL = classLoader.getResource("logo_black.png");
imageURL = classLoader.getResource("logo/logo_black.png");
}
ImageIcon originalIcon = new ImageIcon(imageURL);
Image originalImage = originalIcon.getImage();

View File

@@ -3,20 +3,21 @@ package hae.component.board;
import burp.api.montoya.MontoyaApi;
import hae.Config;
import hae.component.board.message.MessageTableModel;
import hae.utils.string.StringProcessor;
import hae.utils.config.ConfigLoader;
import hae.component.board.message.MessageTableModel.MessageTable;
import hae.utils.config.ConfigLoader;
import hae.utils.string.StringProcessor;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import javax.swing.event.*;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import java.awt.*;
import java.awt.event.*;
import java.util.List;
import javax.swing.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class Databoard extends JPanel {
private final MontoyaApi api;
@@ -28,8 +29,8 @@ public class Databoard extends JPanel {
private MessageTable messageTable;
private static Boolean isMatchHost = false;
private DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel();
private JComboBox hostComboBox = new JComboBox(comboBoxModel);
private final DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel();
private final JComboBox hostComboBox = new JComboBox(comboBoxModel);
public Databoard(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) {
this.api = api;
@@ -41,10 +42,10 @@ public class Databoard extends JPanel {
private void initComponents() {
setLayout(new GridBagLayout());
((GridBagLayout)getLayout()).columnWidths = new int[] {25, 0, 0, 0,20, 0};
((GridBagLayout)getLayout()).rowHeights = new int[] {0, 65, 20, 0};
((GridBagLayout)getLayout()).columnWeights = new double[] {0.0, 0.0, 1.0, 0.0, 0.0, 1.0E-4};
((GridBagLayout)getLayout()).rowWeights = new double[] {0.0, 1.0, 0.0, 1.0E-4};
((GridBagLayout) getLayout()).columnWidths = new int[]{25, 0, 0, 0, 20, 0};
((GridBagLayout) getLayout()).rowHeights = new int[]{0, 65, 20, 0};
((GridBagLayout) getLayout()).columnWeights = new double[]{0.0, 0.0, 1.0, 0.0, 0.0, 1.0E-4};
((GridBagLayout) getLayout()).rowWeights = new double[]{0.0, 1.0, 0.0, 1.0E-4};
JLabel hostLabel = new JLabel("Host:");
@@ -81,7 +82,7 @@ public class Databoard extends JPanel {
new Insets(8, 0, 5, 5), 0, 0));
add(hostTextField, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0));
add(actionButton, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
add(actionButton, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0));
add(splitPane, new GridBagConstraints(1, 1, 3, 3, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
@@ -196,7 +197,7 @@ public class Databoard extends JPanel {
Map<String, List<String>> selectedDataMap;
dataTabbedPane.removeAll();
dataTabbedPane.setPreferredSize(new Dimension(500,0));
dataTabbedPane.setPreferredSize(new Dimension(500, 0));
dataTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
splitPane.setLeftComponent(dataTabbedPane);
@@ -249,12 +250,18 @@ public class Databoard extends JPanel {
String cleanedText = StringProcessor.replaceFirstOccurrence(filterText, "*.", "");
if (cleanedText.contains("*")) {
cleanedText = "";
}
RowFilter<Object, Object> rowFilter = new RowFilter<Object, Object>() {
public boolean include(Entry<?, ?> entry) {
if (cleanedText.equals("*")) {
return true;
} else {
String host = StringProcessor.getHostByUrl((String) entry.getValue(1));
return StringProcessor.matchFromEnd(host, cleanedText);
}
}
};
RowFilter<TableModel, Integer> filter = RowFilter.regexFilter(cleanedText, 1);
sorter.setRowFilter(filter);
sorter.setRowFilter(rowFilter);
messageTableModel.applyHostFilter(filterText);
}

View File

@@ -2,20 +2,22 @@ package hae.component.board;
import burp.api.montoya.MontoyaApi;
import hae.component.board.message.MessageTableModel;
import jregex.Pattern;
import jregex.REFlags;
import hae.utils.ui.UIEnhancer;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableRowSorter;
import java.awt.*;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.*;
import java.util.Comparator;
import java.util.List;
import javax.swing.*;
import java.awt.datatransfer.*;
import javax.swing.event.*;
import javax.swing.table.*;
import java.util.regex.Pattern;
public class Datatable extends JPanel {
private final MontoyaApi api;
@@ -30,7 +32,6 @@ public class Datatable extends JPanel {
this.api = api;
this.tabName = tabName;
String[] columnNames = {"#", "Information"};
dataTableModel = new DefaultTableModel(columnNames, 0);
@@ -63,7 +64,7 @@ public class Datatable extends JPanel {
// 设置灰色默认文本
String searchText = "Search";
addPlaceholder(searchField, searchText);
UIEnhancer.setTextFieldPlaceholder(searchField, searchText);
// 监听输入框内容输入、更新、删除
searchField.getDocument().addDocumentListener(new DocumentListener() {
@@ -114,45 +115,10 @@ public class Datatable extends JPanel {
optionsPanel.add(Box.createHorizontalStrut(5));
optionsPanel.add(searchField);
dataTable.setTransferHandler(new TransferHandler() {
@Override
public void exportToClipboard(JComponent comp, Clipboard clip, int action) throws IllegalStateException {
if (comp instanceof JTable) {
StringSelection stringSelection = new StringSelection(getSelectedData(
(JTable) comp));
clip.setContents(stringSelection, null);
} else {
super.exportToClipboard(comp, clip, action);
}
}
});
add(scrollPane, BorderLayout.CENTER);
add(optionsPanel, BorderLayout.SOUTH);
}
public static void addPlaceholder(JTextField textField, String placeholderText) {
textField.setForeground(Color.GRAY);
textField.setText(placeholderText);
textField.addFocusListener(new FocusListener() {
@Override
public void focusGained(FocusEvent e) {
if (textField.getText().equals(placeholderText)) {
textField.setText("");
textField.setForeground(Color.BLACK);
}
}
@Override
public void focusLost(FocusEvent e) {
if (textField.getText().isEmpty()) {
textField.setForeground(Color.GRAY);
textField.setText(placeholderText);
}
}
});
}
private void addRowToTable(Object[] data) {
int rowCount = dataTableModel.getRowCount();
int id = rowCount > 0 ? (Integer) dataTableModel.getValueAt(rowCount - 1, 0) + 1 : 1;
@@ -169,7 +135,7 @@ public class Datatable extends JPanel {
String searchFieldTextText = searchField.getText();
Pattern pattern = null;
try {
pattern = new Pattern(searchFieldTextText, REFlags.IGNORE_CASE);
pattern = Pattern.compile(searchFieldTextText, Pattern.CASE_INSENSITIVE);
} catch (Exception ignored) {
}
@@ -186,26 +152,20 @@ public class Datatable extends JPanel {
}
}
public static String getSelectedData(JTable table) {
int[] selectRows = table.getSelectedRows();
StringBuilder selectData = new StringBuilder();
for (int row : selectRows) {
selectData.append(table.getValueAt(row, 1).toString()).append("\n");
}
// 便于单行复制,去除最后一个换行符
if (!selectData.isEmpty()){
selectData.deleteCharAt(selectData.length() - 1);
}
return selectData.toString();
}
public JTable getDataTable() {
return this.dataTable;
}
public void setTableListener(MessageTableModel messagePanel) {
// 表格复制功能
dataTable.setTransferHandler(new TransferHandler() {
@Override
public void exportToClipboard(JComponent comp, Clipboard clip, int action) throws IllegalStateException {
if (comp instanceof JTable) {
StringSelection stringSelection = new StringSelection(getSelectedDataAtTable((JTable) comp));
clip.setContents(stringSelection, null);
} else {
super.exportToClipboard(comp, clip, action);
}
}
});
dataTable.setDefaultEditor(Object.class, null);
// 表格内容双击事件
@@ -222,5 +182,26 @@ public class Datatable extends JPanel {
}
});
}
public String getSelectedDataAtTable(JTable table) {
int[] selectRows = table.getSelectedRows();
StringBuilder selectData = new StringBuilder();
for (int row : selectRows) {
selectData.append(table.getValueAt(row, 1).toString()).append("\n");
}
// 便于单行复制,去除最后一个换行符
if (!selectData.isEmpty()) {
selectData.deleteCharAt(selectData.length() - 1);
return selectData.toString();
} else {
return "";
}
}
public JTable getDataTable() {
return this.dataTable;
}
}

View File

@@ -1,18 +1,17 @@
package hae.component.board.message;
import java.awt.Color;
import java.awt.Component;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
public class MessageRenderer extends DefaultTableCellRenderer {
private List<MessageEntry> log;
private Map<String, Color> colorMap = new HashMap<>();
private JTable table; // 保存对表格的引用
private final List<MessageEntry> log;
private final Map<String, Color> colorMap = new HashMap<>();
private final JTable table; // 保存对表格的引用
public MessageRenderer(List<MessageEntry> log, JTable table) {
this.log = log;
@@ -42,7 +41,7 @@ public class MessageRenderer extends DefaultTableCellRenderer {
if (isSelected) {
// 通过更改RGB颜色来达成阴影效果
component.setBackground(new Color(color.getRed()-0x20, color.getGreen()-0x20, color.getBlue()-0x20));
component.setBackground(new Color(color.getRed() - 0x20, color.getGreen() - 0x20, color.getBlue() - 0x20));
} else {
// 否则使用原始颜色
component.setBackground(color);

View File

@@ -14,18 +14,14 @@ import hae.cache.CachePool;
import hae.utils.string.HashCalculator;
import hae.utils.string.StringProcessor;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.*;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
@@ -37,7 +33,7 @@ public class MessageTableModel extends AbstractTableModel {
private final JTabbedPane messageTab;
private final JSplitPane splitPane;
private final List<MessageEntry> log = new ArrayList<MessageEntry>();
private LinkedList<MessageEntry> filteredLog;
private final LinkedList<MessageEntry> filteredLog;
public MessageTableModel(MontoyaApi api) {
this.filteredLog = new LinkedList<>();
@@ -74,6 +70,7 @@ public class MessageTableModel extends AbstractTableModel {
int index2 = getIndex(s2);
return Integer.compare(index1, index2);
}
private int getIndex(String color) {
for (int i = 0; i < Config.color.length; i++) {
if (Config.color[i].equals(color)) {
@@ -96,7 +93,7 @@ public class MessageTableModel extends AbstractTableModel {
}
public void add(HttpRequestResponse messageInfo, String comment, String color) {
synchronized(log) {
synchronized (log) {
HttpRequest httpRequest = messageInfo.request();
String url = httpRequest.url();
String method = httpRequest.method();
@@ -120,7 +117,7 @@ public class MessageTableModel extends AbstractTableModel {
byte[] resByteB = reqResMessage.response().toByteArray().getBytes();
try {
// 通过URL、请求和响应报文、匹配数据内容多维度进行对比
if ((entry.getUrl().toString().equals(url.toString()) || (Arrays.equals(reqByteB, reqByteA) || Arrays.equals(resByteB, resByteA))) && (areMapsEqual(getCacheData(reqByteB), getCacheData(reqByteA)) && areMapsEqual(getCacheData(resByteB), getCacheData(resByteA)))) {
if ((entry.getUrl().equals(url) || (Arrays.equals(reqByteB, reqByteA) || Arrays.equals(resByteB, resByteA))) && (areMapsEqual(getCacheData(reqByteB), getCacheData(reqByteA)) && areMapsEqual(getCacheData(resByteB), getCacheData(resByteA)))) {
isDuplicate = true;
break;
}
@@ -243,6 +240,14 @@ public class MessageTableModel extends AbstractTableModel {
case "response body":
isMatch = matchingString(format, filterText, responseBody);
break;
case "request line":
String requestLine = requestString.split("\\r?\\n", 2)[0];
isMatch = matchingString(format, filterText, requestLine);
break;
case "response line":
String responseLine = responseString.split("\\r?\\n", 2)[0];
isMatch = matchingString(format, filterText, responseLine);
break;
default:
break;
}
@@ -285,7 +290,7 @@ public class MessageTableModel extends AbstractTableModel {
private Map<String, Map<String, Object>> getCacheData(byte[] content) {
String hashIndex = HashCalculator.calculateHash(content);
return CachePool.getFromCache(hashIndex);
return CachePool.get(hashIndex);
}
private boolean areMapsEqual(Map<String, Map<String, Object>> map1, Map<String, Map<String, Object>> map2) {
@@ -334,13 +339,11 @@ public class MessageTableModel extends AbstractTableModel {
}
public JSplitPane getSplitPane()
{
public JSplitPane getSplitPane() {
return splitPane;
}
public MessageTable getMessageTable()
{
public MessageTable getMessageTable() {
return messageTable;
}
@@ -360,8 +363,7 @@ public class MessageTableModel extends AbstractTableModel {
}
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
public Object getValueAt(int rowIndex, int columnIndex) {
if (filteredLog.isEmpty()) {
return "";
}
@@ -379,8 +381,7 @@ public class MessageTableModel extends AbstractTableModel {
}
@Override
public String getColumnName(int columnIndex)
{
public String getColumnName(int columnIndex) {
return switch (columnIndex) {
case 0 -> "Method";
case 1 -> "URL";
@@ -395,8 +396,8 @@ public class MessageTableModel extends AbstractTableModel {
public class MessageTable extends JTable {
private MessageEntry MessageEntry;
private SwingWorker<Object, Void> currentWorker;
// 设置响应报文返回的最大长度为3MB
private final int MAX_LENGTH = 3145728;
// 设置响应报文返回的最大长度
private final int MAX_LENGTH = 5242880;
private int lastSelectedIndex = -1;
private final HttpRequestEditor requestEditor;
private final HttpResponseEditor responseEditor;
@@ -443,8 +444,7 @@ public class MessageTableModel extends AbstractTableModel {
ByteArray[] result = (ByteArray[]) get();
requestEditor.setRequest(HttpRequest.httpRequest(MessageEntry.getRequestResponse().httpService(), result[0]));
responseEditor.setResponse(HttpResponse.httpResponse(result[1]));
} catch (Exception e) {
e.printStackTrace();
} catch (Exception ignored) {
}
}
}

View File

@@ -3,10 +3,21 @@ package hae.component.config;
import burp.api.montoya.MontoyaApi;
import hae.component.rule.Rules;
import hae.utils.config.ConfigLoader;
import hae.utils.ui.UIEnhancer;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
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.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
public class Config extends JPanel {
private final MontoyaApi api;
@@ -22,67 +33,214 @@ public class Config extends JPanel {
}
private void initComponents() {
setLayout(new GridBagLayout());
((GridBagLayout) getLayout()).columnWidths = new int[] {0, 0, 0, 0, 0};
((GridBagLayout) getLayout()).rowHeights = new int[] {0, 0, 0};
((GridBagLayout) getLayout()).columnWeights = new double[] {0.0, 1.0, 0.0, 0.0, 1.0E-4};
((GridBagLayout) getLayout()).rowWeights = new double[] {0.0, 0.0, 1.0E-4};
setLayout(new BorderLayout());
JLabel rulesFilePathLabel = new JLabel("Rules Path:");
JTextField rulesFilePathTextField = new JTextField();
JButton onlineUpdateButton = new JButton("Update");
JLabel excludeSuffixLabel = new JLabel("Exclude Suffix:");
JTextField excludeSuffixTextField = new JTextField();
JButton excludeSuffixSaveButton = new JButton("Save");
GridBagConstraints constraints = new GridBagConstraints();
constraints.weightx = 1.0;
constraints.fill = GridBagConstraints.HORIZONTAL;
JPanel ruleInfoPanel = new JPanel(new GridBagLayout());
ruleInfoPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
JLabel ruleLabel = new JLabel("Path:");
JTextField pathTextField = new JTextField();
pathTextField.setEditable(false);
pathTextField.setText(configLoader.getRulesFilePath());
JButton reloadButton = new JButton("Reload");
JButton updateButton = new JButton("Update");
ruleInfoPanel.add(ruleLabel);
ruleInfoPanel.add(pathTextField, constraints);
ruleInfoPanel.add(Box.createHorizontalStrut(5));
ruleInfoPanel.add(reloadButton);
ruleInfoPanel.add(Box.createHorizontalStrut(5));
ruleInfoPanel.add(updateButton);
rulesFilePathTextField.setEditable(false);
onlineUpdateButton.addActionListener(this::onlineUpdateActionPerformed);
excludeSuffixSaveButton.addActionListener(e -> excludeSuffixSaveActionPerformed(e, excludeSuffixTextField.getText()));
reloadButton.addActionListener(this::reloadActionPerformed);
updateButton.addActionListener(this::onlineUpdateActionPerformed);
rulesFilePathTextField.setText(configLoader.getRulesFilePath());
excludeSuffixTextField.setText(configLoader.getExcludeSuffix());
JPanel settingPanel = new JPanel(new BorderLayout());
DefaultTableModel model = new DefaultTableModel();
add(rulesFilePathTextField, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(5, 0, 5, 5), 0, 0));
add(rulesFilePathLabel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.WEST, GridBagConstraints.VERTICAL,
new Insets(5, 5, 5, 5), 0, 0));
add(onlineUpdateButton, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(5, 0, 5, 5), 0, 0));
add(reloadButton, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(5, 0, 5, 5), 0, 0));
add(excludeSuffixLabel, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0,
GridBagConstraints.SOUTHWEST, GridBagConstraints.NONE,
new Insets(0, 5, 5, 5), 0, 0));
add(excludeSuffixTextField, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0,
GridBagConstraints.SOUTH, GridBagConstraints.HORIZONTAL,
new Insets(0, 0, 0, 5), 0, 0));
add(excludeSuffixSaveButton, new GridBagConstraints(2, 1, 1, 1, 0.0, 0.0,
GridBagConstraints.SOUTH, GridBagConstraints.HORIZONTAL,
new Insets(0, 0, 0, 5), 0, 0));
JTable table = new JTable(model);
model.addColumn("Value");
JScrollPane scrollPane = new JScrollPane(table);
JPanel buttonPanel = new JPanel();
buttonPanel.setBorder(new EmptyBorder(0, 3, 0, 0));
GridBagLayout layout = new GridBagLayout();
layout.rowHeights = new int[]{0, 0, 0, 0, 0, 0, 0};
layout.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE};
buttonPanel.setLayout(layout);
JPanel inputPanel = new JPanel(new BorderLayout());
JPanel inputPanelB = new JPanel(new BorderLayout());
inputPanelB.setBorder(new EmptyBorder(0, 0, 3, 0));
constraints.gridx = 1;
JButton addButton = new JButton("Add");
JButton removeButton = new JButton("Remove");
JButton pasteButton = new JButton("Paste");
JButton clearButton = new JButton("Clear");
JComboBox<String> setTypeComboBox = new JComboBox<>();
String[] mode = new String[]{"Exclude suffix", "Block host"};
setTypeComboBox.setModel(new DefaultComboBoxModel<>(mode));
setTypeComboBox.addActionListener(e -> {
String selected = (String) setTypeComboBox.getSelectedItem();
model.setRowCount(0);
if (selected.equals("Exclude suffix")) {
addDataToTable(configLoader.getExcludeSuffix().replaceAll("\\|", "\r\n"), model);
}
if (selected.equals("Block host")) {
addDataToTable(configLoader.getBlockHost().replaceAll("\\|", "\r\n"), model);
}
});
setTypeComboBox.setSelectedItem("Exclude suffix");
model.addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
String selected = (String) setTypeComboBox.getSelectedItem();
String values = getFirstColumnDataAsString(model);
if (selected.equals("Exclude suffix")) {
if (!values.equals(configLoader.getExcludeSuffix()) && !values.isEmpty()) {
configLoader.setExcludeSuffix(values);
}
}
if (selected.equals("Block host")) {
if (!values.equals(configLoader.getExcludeSuffix()) && !values.isEmpty()) {
configLoader.setBlockHost(values);
}
}
}
});
constraints.insets = new Insets(0, 0, 3, 0);
constraints.gridy = 0;
buttonPanel.add(setTypeComboBox, constraints);
constraints.gridy = 1;
buttonPanel.add(addButton, constraints);
constraints.gridy = 2;
buttonPanel.add(removeButton, constraints);
constraints.gridy = 3;
buttonPanel.add(pasteButton, constraints);
constraints.gridy = 4;
buttonPanel.add(clearButton, constraints);
JTextField addTextField = new JTextField();
String defaultText = "Enter a new item";
UIEnhancer.setTextFieldPlaceholder(addTextField, defaultText);
inputPanelB.add(addTextField, BorderLayout.CENTER);
inputPanel.add(scrollPane, BorderLayout.CENTER);
inputPanel.add(inputPanelB, BorderLayout.NORTH);
settingPanel.add(buttonPanel, BorderLayout.EAST);
settingPanel.add(inputPanel, BorderLayout.CENTER);
addButton.addActionListener(e -> {
String addTextFieldText = addTextField.getText();
if (!addTextFieldText.equals(defaultText)) {
addDataToTable(addTextFieldText, model);
}
addTextField.setText("");
addTextField.requestFocusInWindow();
});
pasteButton.addActionListener(e -> {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
try {
String data = (String) clipboard.getData(DataFlavor.stringFlavor);
if (data != null && !data.isEmpty()) {
addDataToTable(data, model);
}
} catch (Exception ignored) {
}
});
removeButton.addActionListener(e -> {
int selectedRow = table.getSelectedRow();
if (selectedRow != -1) {
model.removeRow(selectedRow);
}
});
clearButton.addActionListener(e -> model.setRowCount(0));
JPanel settingMainPanel = new JPanel(new BorderLayout());
JLabel settingLabel = new JLabel("Setting:");
JPanel settingLabelPanel = new JPanel(new BorderLayout());
settingLabelPanel.add(settingLabel, BorderLayout.WEST);
settingMainPanel.setBorder(new EmptyBorder(0, 5, 10, 5));
settingMainPanel.add(settingLabelPanel, BorderLayout.NORTH);
settingMainPanel.add(settingPanel, BorderLayout.CENTER);
add(ruleInfoPanel, BorderLayout.NORTH);
add(settingMainPanel, BorderLayout.CENTER);
}
private String getFirstColumnDataAsString(DefaultTableModel model) {
StringBuilder firstColumnData = new StringBuilder();
int numRows = model.getRowCount();
for (int row = 0; row < numRows; row++) {
firstColumnData.append(model.getValueAt(row, 0));
if (row < numRows - 1) {
firstColumnData.append("|");
}
}
return firstColumnData.toString();
}
private void addDataToTable(String data, DefaultTableModel model) {
if (!data.isBlank()) {
String[] rows = data.split("\\r?\\n");
for (String row : rows) {
model.addRow(new String[]{row});
}
deduplicateTableData(model);
}
}
private void deduplicateTableData(DefaultTableModel model) {
// 使用 Map 存储每一行的数据,用于去重
Set<List<Object>> rowData = new LinkedHashSet<>();
int columnCount = model.getColumnCount();
// 将每一行数据作为一个列表,添加到 Set 中
for (int i = 0; i < model.getRowCount(); i++) {
List<Object> row = new ArrayList<>();
for (int j = 0; j < columnCount; j++) {
row.add(model.getValueAt(i, j));
}
rowData.add(row);
}
// 清除原始数据
model.setRowCount(0);
// 将去重后的数据添加回去
for (List<Object> uniqueRow : rowData) {
model.addRow(uniqueRow.toArray());
}
}
private void onlineUpdateActionPerformed(ActionEvent e) {
// 添加提示框防止用户误触导致配置更新
int retCode = JOptionPane.showConfirmDialog(null, "Do you want to update rules?", "Info", JOptionPane.YES_NO_OPTION);
if (retCode == JOptionPane.YES_OPTION) {
configLoader.initRules();
configLoader.initRulesByNet();
reloadActionPerformed(null);
}
}
private void excludeSuffixSaveActionPerformed(ActionEvent e, String suffix) {
if (!suffix.equals(configLoader.getExcludeSuffix()) && !suffix.isEmpty()) {
configLoader.setExcludeSuffix(suffix);
}
}
private void reloadActionPerformed(ActionEvent e) {
rules.reloadRuleGroup();
}

View File

@@ -1,18 +1,17 @@
package hae.component.rule;
import burp.api.montoya.MontoyaApi;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import javax.swing.table.TableRowSorter;
import java.util.Vector;
import hae.Config;
import hae.utils.config.ConfigLoader;
import hae.utils.rule.RuleProcessor;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableRowSorter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.Vector;
import static javax.swing.JOptionPane.YES_OPTION;
public class Rule extends JPanel {
@@ -32,10 +31,10 @@ public class Rule extends JPanel {
private void initComponents(Object[][] data) {
setLayout(new GridBagLayout());
((GridBagLayout)getLayout()).columnWidths = new int[] {0, 0, 0};
((GridBagLayout)getLayout()).rowHeights = new int[] {0, 0, 0, 0, 0};
((GridBagLayout)getLayout()).columnWeights = new double[] {0.0, 1.0, 1.0E-4};
((GridBagLayout)getLayout()).rowWeights = new double[] {0.0, 0.0, 0.0, 1.0, 1.0E-4};
((GridBagLayout) getLayout()).columnWidths = new int[]{0, 0, 0};
((GridBagLayout) getLayout()).rowHeights = new int[]{0, 0, 0, 0, 0};
((GridBagLayout) getLayout()).columnWeights = new double[]{0.0, 1.0, 1.0E-4};
((GridBagLayout) getLayout()).rowWeights = new double[]{0.0, 0.0, 0.0, 1.0, 1.0E-4};
JButton addButton = new JButton("Add");
JButton editButton = new JButton("Edit");
@@ -74,7 +73,7 @@ public class Rule extends JPanel {
model.setDataVector(data, Config.ruleFields);
model.addTableModelListener(e -> {
if (e.getColumn() == 0 && ruleTable.getSelectedRow() != -1){
if (e.getColumn() == 0 && ruleTable.getSelectedRow() != -1) {
int select = ruleTable.convertRowIndexToModel(ruleTable.getSelectedRow());
ruleProcessor.changeRule(model.getDataVector().get(select), select, tabbedPane.getTitleAt(tabbedPane.getSelectedIndex()));
}
@@ -117,8 +116,8 @@ public class Rule extends JPanel {
}
}
private void ruleEditActionPerformed(ActionEvent e, JTable ruleTable, JTabbedPane tabbedPane){
if (ruleTable.getSelectedRowCount() >= 1){
private void ruleEditActionPerformed(ActionEvent e, JTable ruleTable, JTabbedPane tabbedPane) {
if (ruleTable.getSelectedRowCount() >= 1) {
DefaultTableModel model = (DefaultTableModel) ruleTable.getModel();
Display ruleDisplay = new Display();
@@ -129,12 +128,12 @@ public class Rule extends JPanel {
ruleDisplay.colorComboBox.setSelectedItem(ruleTable.getValueAt(ruleTable.getSelectedRow(), 5).toString());
ruleDisplay.scopeComboBox.setSelectedItem(ruleTable.getValueAt(ruleTable.getSelectedRow(), 6).toString());
ruleDisplay.engineComboBox.setSelectedItem(ruleTable.getValueAt(ruleTable.getSelectedRow(), 7).toString());
ruleDisplay.sensitiveComboBox.setSelectedItem(ruleTable.getValueAt(ruleTable.getSelectedRow(),8));
ruleDisplay.sensitiveComboBox.setSelectedItem(ruleTable.getValueAt(ruleTable.getSelectedRow(), 8));
ruleDisplay.formatTextField.setEnabled(ruleDisplay.engineComboBox.getSelectedItem().toString().equals("nfa"));
int showState = JOptionPane.showConfirmDialog(null, ruleDisplay, "Edit Rule", JOptionPane.OK_OPTION);
if (showState == 0){
if (showState == 0) {
int select = ruleTable.convertRowIndexToModel(ruleTable.getSelectedRow());
model.setValueAt(ruleDisplay.ruleNameTextField.getText(), select, 1);
model.setValueAt(ruleDisplay.firstRegexTextField.getText(), select, 2);
@@ -150,9 +149,9 @@ public class Rule extends JPanel {
}
}
private void ruleRemoveActionPerformed(ActionEvent e, JTable ruleTable, JTabbedPane tabbedPane){
if (ruleTable.getSelectedRowCount() >= 1){
if (JOptionPane.showConfirmDialog(null, "Are you sure you want to delete this rule?", "Info", JOptionPane.OK_OPTION) == 0){
private void ruleRemoveActionPerformed(ActionEvent e, JTable ruleTable, JTabbedPane tabbedPane) {
if (ruleTable.getSelectedRowCount() >= 1) {
if (JOptionPane.showConfirmDialog(null, "Are you sure you want to delete this rule?", "Info", JOptionPane.OK_OPTION) == 0) {
DefaultTableModel model = (DefaultTableModel) ruleTable.getModel();
int select = ruleTable.convertRowIndexToModel(ruleTable.getSelectedRow());

View File

@@ -11,7 +11,7 @@ import java.awt.event.*;
public class Rules extends JTabbedPane {
private final MontoyaApi api;
private final ConfigLoader configLoader;
private ConfigLoader configLoader;
private final RuleProcessor ruleProcessor;
private final JTextField ruleGroupNameTextField;
@@ -101,7 +101,9 @@ public class Rules extends JTabbedPane {
public void reloadRuleGroup() {
removeAll();
Config.globalRules.keySet().forEach(i-> addTab(i, new Rule(api, configLoader, hae.Config.globalRules.get(i), this)));
this.configLoader = new ConfigLoader(api);
Config.globalRules.keySet().forEach(i -> addTab(i, new Rule(api, configLoader, hae.Config.globalRules.get(i), this)));
addTab("...", null);
}
@@ -118,7 +120,7 @@ public class Rules extends JTabbedPane {
}
}
private Action renameTitleActionPerformed = new AbstractAction() {
private final Action renameTitleActionPerformed = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
String title = ruleGroupNameTextField.getText();
@@ -134,7 +136,7 @@ public class Rules extends JTabbedPane {
}
};
private Action cancelActionPerformed = new AbstractAction() {
private final Action cancelActionPerformed = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
if (selectedIndex >= 0) {

View File

@@ -1,16 +1,18 @@
package hae.instances.editor;
import burp.api.montoya.MontoyaApi;
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 burp.api.montoya.core.ByteArray;
import burp.api.montoya.core.Range;
import burp.api.montoya.http.message.HttpRequestResponse;
import burp.api.montoya.http.message.requests.HttpRequest;
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.component.board.Datatable;
import hae.instances.http.utils.MessageProcessor;
import hae.utils.config.ConfigLoader;
import hae.utils.string.StringProcessor;
import javax.swing.*;
import java.awt.*;
@@ -20,27 +22,31 @@ import java.util.Map;
public class RequestEditor implements HttpRequestEditorProvider {
private final MontoyaApi api;
private final ConfigLoader configLoader;
public RequestEditor(MontoyaApi api) {
public RequestEditor(MontoyaApi api, ConfigLoader configLoader) {
this.api = api;
this.configLoader = configLoader;
}
@Override
public ExtensionProvidedHttpRequestEditor provideHttpRequestEditor(EditorCreationContext editorCreationContext) {
return new Editor(api, editorCreationContext);
return new Editor(api, configLoader, editorCreationContext);
}
private static class Editor implements ExtensionProvidedHttpRequestEditor {
private final MontoyaApi api;
private final ConfigLoader configLoader;
private final EditorCreationContext creationContext;
private final MessageProcessor messageProcessor;
private HttpRequestResponse requestResponse;
private List<Map<String, String>> dataList;
private JTabbedPane jTabbedPane = new JTabbedPane();
private final JTabbedPane jTabbedPane = new JTabbedPane();
public Editor(MontoyaApi api, EditorCreationContext creationContext)
{
public Editor(MontoyaApi api, ConfigLoader configLoader, EditorCreationContext creationContext) {
this.api = api;
this.configLoader = configLoader;
this.creationContext = creationContext;
this.messageProcessor = new MessageProcessor(api);
}
@@ -53,15 +59,29 @@ public class RequestEditor implements HttpRequestEditorProvider {
@Override
public void setRequestResponse(HttpRequestResponse requestResponse) {
this.requestResponse = requestResponse;
generateTabbedPaneFromResultMap(api, jTabbedPane, this.dataList);
}
@Override
public synchronized boolean isEnabledFor(HttpRequestResponse requestResponse) {
HttpRequest request = requestResponse.request();
if (request != null && !request.bodyToString().equals("Loading...")) {
List<Map<String, String>> result = messageProcessor.processRequest("", request, false);
jTabbedPane = generateTabbedPaneFromResultMap(api, result);
return jTabbedPane.getTabCount() > 0;
if (request != null) {
try {
String host = StringProcessor.getHostByUrl(request.url());
if (!host.isEmpty()) {
String[] hostList = configLoader.getBlockHost().split("\\|");
boolean isBlockHost = isBlockHost(hostList, host);
List<String> suffixList = Arrays.asList(configLoader.getExcludeSuffix().split("\\|"));
boolean matches = suffixList.contains(request.fileExtension().toLowerCase()) || isBlockHost;
if (!matches && !request.bodyToString().equals("Loading...")) {
this.dataList = messageProcessor.processRequest("", request, false);
return isListHasData(this.dataList);
}
}
} catch (Exception ignored) {
}
}
return false;
}
@@ -81,7 +101,8 @@ public class RequestEditor implements HttpRequestEditorProvider {
return new Selection() {
@Override
public ByteArray contents() {
return ByteArray.byteArray(Datatable.getSelectedData(((Datatable) jTabbedPane.getSelectedComponent()).getDataTable()));
Datatable dataTable = (Datatable) jTabbedPane.getSelectedComponent();
return ByteArray.byteArray(dataTable.getSelectedDataAtTable(dataTable.getDataTable()));
}
@Override
@@ -97,19 +118,36 @@ public class RequestEditor implements HttpRequestEditorProvider {
}
}
public static JTabbedPane generateTabbedPaneFromResultMap(MontoyaApi api, List<Map<String, String>> result) {
JTabbedPane tabbedPane = new JTabbedPane();
if (result != null && !result.isEmpty() && result.size() > 0) {
public static boolean isBlockHost(String[] hostList, String host) {
boolean isBlockHost = false;
for (String hostName : hostList) {
String cleanedHost = StringProcessor.replaceFirstOccurrence(hostName, "*.", "");
if (StringProcessor.matchFromEnd(host, cleanedHost)) {
isBlockHost = true;
}
}
return isBlockHost;
}
public static boolean isListHasData(List<Map<String, String>> dataList) {
if (dataList != null && !dataList.isEmpty()) {
Map<String, String> dataMap = dataList.get(0);
return dataMap != null && !dataMap.isEmpty();
}
return false;
}
public static void generateTabbedPaneFromResultMap(MontoyaApi api, JTabbedPane tabbedPane, List<Map<String, String>> result) {
tabbedPane.removeAll();
if (result != null && !result.isEmpty()) {
Map<String, String> dataMap = result.get(0);
if (dataMap != null && !dataMap.isEmpty() && dataMap.size() > 0) {
dataMap.keySet().forEach(i->{
if (dataMap != null && !dataMap.isEmpty()) {
dataMap.keySet().forEach(i -> {
String[] extractData = dataMap.get(i).split("\n");
Datatable dataPanel = new Datatable(api, i, Arrays.asList(extractData));
tabbedPane.addTab(i, dataPanel);
});
}
}
return tabbedPane;
}
}

View File

@@ -1,45 +1,53 @@
package hae.instances.editor;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.core.ByteArray;
import burp.api.montoya.core.Range;
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.ui.Selection;
import burp.api.montoya.ui.editor.extension.EditorCreationContext;
import burp.api.montoya.ui.editor.extension.ExtensionProvidedHttpResponseEditor;
import burp.api.montoya.ui.editor.extension.HttpResponseEditorProvider;
import burp.api.montoya.core.ByteArray;
import burp.api.montoya.core.Range;
import burp.api.montoya.ui.Selection;
import hae.component.board.Datatable;
import hae.instances.http.utils.MessageProcessor;
import hae.utils.config.ConfigLoader;
import hae.utils.string.StringProcessor;
import javax.swing.*;
import java.awt.*;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class ResponseEditor implements HttpResponseEditorProvider {
private final MontoyaApi api;
private final ConfigLoader configLoader;
public ResponseEditor(MontoyaApi api) {
public ResponseEditor(MontoyaApi api, ConfigLoader configLoader) {
this.api = api;
this.configLoader = configLoader;
}
@Override
public ExtensionProvidedHttpResponseEditor provideHttpResponseEditor(EditorCreationContext editorCreationContext) {
return new Editor(api, editorCreationContext);
return new Editor(api, configLoader, editorCreationContext);
}
private static class Editor implements ExtensionProvidedHttpResponseEditor {
private final MontoyaApi api;
private final ConfigLoader configLoader;
private final EditorCreationContext creationContext;
private final MessageProcessor messageProcessor;
private HttpRequestResponse requestResponse;
private List<Map<String, String>> dataList;
private JTabbedPane jTabbedPane = new JTabbedPane();
private final JTabbedPane jTabbedPane = new JTabbedPane();
public Editor(MontoyaApi api, EditorCreationContext creationContext)
{
public Editor(MontoyaApi api, ConfigLoader configLoader, EditorCreationContext creationContext) {
this.api = api;
this.configLoader = configLoader;
this.creationContext = creationContext;
this.messageProcessor = new MessageProcessor(api);
}
@@ -52,16 +60,37 @@ public class ResponseEditor implements HttpResponseEditorProvider {
@Override
public void setRequestResponse(HttpRequestResponse requestResponse) {
this.requestResponse = requestResponse;
RequestEditor.generateTabbedPaneFromResultMap(api, jTabbedPane, this.dataList);
}
@Override
public synchronized boolean isEnabledFor(HttpRequestResponse requestResponse) {
HttpResponse request = requestResponse.response();
if (request != null && !request.bodyToString().equals("Loading...")) {
List<Map<String, String>> result = messageProcessor.processResponse("", request, false);
jTabbedPane = RequestEditor.generateTabbedPaneFromResultMap(api, result);
return jTabbedPane.getTabCount() > 0;
HttpResponse response = requestResponse.response();
if (response != null) {
HttpRequest request = requestResponse.request();
boolean matches = false;
if (request != null) {
try {
String host = StringProcessor.getHostByUrl(request.url());
if (!host.isEmpty()) {
String[] hostList = configLoader.getBlockHost().split("\\|");
boolean isBlockHost = RequestEditor.isBlockHost(hostList, host);
List<String> suffixList = Arrays.asList(configLoader.getExcludeSuffix().split("\\|"));
matches = suffixList.contains(request.fileExtension().toLowerCase()) || isBlockHost;
}
} catch (Exception ignored) {
}
}
if (!matches && !response.bodyToString().equals("Loading...")) {
this.dataList = messageProcessor.processResponse("", response, false);
return RequestEditor.isListHasData(this.dataList);
}
}
return false;
}
@@ -80,7 +109,8 @@ public class ResponseEditor implements HttpResponseEditorProvider {
return new Selection() {
@Override
public ByteArray contents() {
return ByteArray.byteArray(Datatable.getSelectedData(((Datatable) jTabbedPane.getSelectedComponent()).getDataTable()));
Datatable dataTable = (Datatable) jTabbedPane.getSelectedComponent();
return ByteArray.byteArray(dataTable.getSelectedDataAtTable(dataTable.getDataTable()));
}
@Override

View File

@@ -5,7 +5,9 @@ import burp.api.montoya.core.ByteArray;
import burp.api.montoya.core.Range;
import burp.api.montoya.ui.Selection;
import burp.api.montoya.ui.contextmenu.WebSocketMessage;
import burp.api.montoya.ui.editor.extension.*;
import burp.api.montoya.ui.editor.extension.EditorCreationContext;
import burp.api.montoya.ui.editor.extension.ExtensionProvidedWebSocketMessageEditor;
import burp.api.montoya.ui.editor.extension.WebSocketMessageEditorProvider;
import hae.component.board.Datatable;
import hae.instances.http.utils.MessageProcessor;
@@ -31,8 +33,9 @@ public class WebSocketEditor implements WebSocketMessageEditorProvider {
private final EditorCreationContext creationContext;
private final MessageProcessor messageProcessor;
private ByteArray message;
private List<Map<String, String>> dataList;
private JTabbedPane jTabbedPane = new JTabbedPane();
private final JTabbedPane jTabbedPane = new JTabbedPane();
public Editor(MontoyaApi api, EditorCreationContext creationContext) {
this.api = api;
@@ -48,15 +51,15 @@ public class WebSocketEditor implements WebSocketMessageEditorProvider {
@Override
public void setMessage(WebSocketMessage webSocketMessage) {
this.message = webSocketMessage.payload();
RequestEditor.generateTabbedPaneFromResultMap(api, jTabbedPane, this.dataList);
}
@Override
public boolean isEnabledFor(WebSocketMessage webSocketMessage) {
String websocketMessage = webSocketMessage.payload().toString();
if (!websocketMessage.isEmpty()) {
List<Map<String, String>> result = messageProcessor.processMessage("", websocketMessage, false);
jTabbedPane = RequestEditor.generateTabbedPaneFromResultMap(api, result);
return jTabbedPane.getTabCount() > 0;
this.dataList = messageProcessor.processMessage("", websocketMessage, false);
return RequestEditor.isListHasData(this.dataList);
}
return false;
}
@@ -76,7 +79,8 @@ public class WebSocketEditor implements WebSocketMessageEditorProvider {
return new Selection() {
@Override
public ByteArray contents() {
return ByteArray.byteArray(Datatable.getSelectedData(((Datatable) jTabbedPane.getSelectedComponent()).getDataTable()));
Datatable dataTable = (Datatable) jTabbedPane.getSelectedComponent();
return ByteArray.byteArray(dataTable.getSelectedDataAtTable(dataTable.getDataTable()));
}
@Override

View File

@@ -6,28 +6,34 @@ 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.Config;
import hae.component.board.message.MessageTableModel;
import hae.instances.editor.RequestEditor;
import hae.instances.http.utils.MessageProcessor;
import hae.utils.config.ConfigLoader;
import hae.utils.string.StringProcessor;
import java.util.*;
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 MessageTableModel messageTableModel;
private final ConfigLoader configLoader;
private final MessageTableModel messageTableModel;
private final MessageProcessor messageProcessor;
private String host;
// 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, MessageTableModel messageTableModel) {
public HttpMessageHandler(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) {
this.api = api;
this.configLoader = configLoader;
this.messageTableModel = messageTableModel;
this.messageProcessor = new MessageProcessor(api);
}
@@ -41,13 +47,16 @@ public class HttpMessageHandler implements HttpHandler {
httpRequest.set(httpRequestToBeSent);
host = StringProcessor.getHostByUrl(httpRequestToBeSent.url());
host.set(StringProcessor.getHostByUrl(httpRequestToBeSent.url()));
List<String> suffixList = Arrays.asList(Config.suffix.split("\\|"));
matches.set(suffixList.contains(httpRequestToBeSent.fileExtension()));
String[] hostList = configLoader.getBlockHost().split("\\|");
boolean isBlockHost = RequestEditor.isBlockHost(hostList, host.get());
List<String> suffixList = Arrays.asList(configLoader.getExcludeSuffix().split("\\|"));
matches.set(suffixList.contains(httpRequestToBeSent.fileExtension().toLowerCase()) || isBlockHost);
if (!matches.get()) {
List<Map<String, String>> result = messageProcessor.processRequest(host, httpRequestToBeSent, true);
List<Map<String, String>> result = messageProcessor.processRequest(host.get(), httpRequestToBeSent, true);
setColorAndCommentList(result);
}
@@ -59,7 +68,7 @@ public class HttpMessageHandler implements HttpHandler {
Annotations annotations = httpResponseReceived.annotations();
if (!matches.get()) {
List<Map<String, String>> result = messageProcessor.processResponse(host, httpResponseReceived, true);
List<Map<String, String>> result = messageProcessor.processResponse(host.get(), httpResponseReceived, true);
setColorAndCommentList(result);
// 设置高亮颜色和注释
if (!colorList.get().isEmpty() && !commentList.get().isEmpty()) {

View File

@@ -24,6 +24,7 @@ public class MessageProcessor {
public List<Map<String, String>> processMessage(String host, String message, boolean flag) {
Map<String, Map<String, Object>> obj = null;
try {
obj = regularMatcher.match(host, "any", message, message, message);
} catch (Exception ignored) {
@@ -34,6 +35,7 @@ public class MessageProcessor {
public List<Map<String, String>> processResponse(String host, HttpResponse httpResponse, boolean flag) {
Map<String, Map<String, Object>> obj = null;
try {
String response = new String(httpResponse.toByteArray().getBytes(), StandardCharsets.UTF_8);
String body = new String(httpResponse.body().getBytes(), StandardCharsets.UTF_8);
@@ -57,6 +59,7 @@ public class MessageProcessor {
String header = httpRequest.headers().stream()
.map(HttpHeader::toString)
.collect(Collectors.joining("\n"));
obj = regularMatcher.match(host, "request", request, header, body);
} catch (Exception ignored) {
}
@@ -99,6 +102,7 @@ public class MessageProcessor {
String data = tempMap.get("data").toString();
extractedData.put(key, data);
});
return extractedData;
}
@@ -114,10 +118,11 @@ public class MessageProcessor {
List<List<String>> result = new ArrayList<>();
result.add(colorList);
result.add(commentList);
return result;
}
public List<Integer> retrieveColorIndices(List<String> colors){
public List<Integer> retrieveColorIndices(List<String> colors) {
List<Integer> indices = new ArrayList<>();
String[] colorArray = Config.color;
int size = colorArray.length;
@@ -129,6 +134,7 @@ public class MessageProcessor {
}
}
}
return indices;
}
@@ -154,7 +160,7 @@ public class MessageProcessor {
HashSet tmpList = new HashSet(stack);
if (stack.size() == tmpList.size()) {
stack.sort(Comparator.comparingInt(Integer::intValue));
if(stack.get(0) < 0) {
if (stack.get(0) < 0) {
finalColor = colorArray[0];
} else {
finalColor = colorArray[stack.get(0)];

View File

@@ -9,12 +9,12 @@ import hae.Config;
import hae.cache.CachePool;
import hae.utils.string.HashCalculator;
import hae.utils.string.StringProcessor;
import jregex.Matcher;
import jregex.Pattern;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegularMatcher {
private final MontoyaApi api;
@@ -27,7 +27,7 @@ public class RegularMatcher {
public Map<String, Map<String, Object>> match(String host, String type, String message, String header, String body) {
// 先从缓存池里判断是否有已经匹配好的结果
String messageIndex = HashCalculator.calculateHash(message.getBytes());
Map<String, Map<String, Object>> map = CachePool.getFromCache(messageIndex);
Map<String, Map<String, Object>> map = CachePool.get(messageIndex);
if (map != null) {
return map;
} else {
@@ -69,6 +69,10 @@ public class RegularMatcher {
case "response body":
matchContent = body;
break;
case "request line":
case "response line":
matchContent = message.split("\\r?\\n", 2)[0];
break;
default:
break;
}
@@ -77,6 +81,7 @@ public class RegularMatcher {
result.addAll(matchByRegex(f_regex, s_regex, matchContent, format, engine, sensitive));
} catch (Exception e) {
api.logging().logToError(String.format("[x] Error Info:\nName: %s\nRegex: %s", name, f_regex));
api.logging().logToError(e.getMessage());
continue;
}
@@ -94,27 +99,18 @@ public class RegularMatcher {
// 添加到全局变量中便于Databoard检索
if (!Objects.equals(host, "") && host != null) {
List<String> dataList = Arrays.asList(dataStr.split("\n"));
if (Config.globalDataMap.containsKey(host)) {
ConcurrentHashMap<String, List<String>> gRuleMap = new ConcurrentHashMap<>(Config.globalDataMap.get(host));
if (gRuleMap.containsKey(name)) {
// gDataList为不可变列表因此需要重新创建一个列表以便于使用addAll方法
List<String> gDataList = gRuleMap.get(name);
List<String> newDataList = new ArrayList<>(gDataList);
newDataList.addAll(dataList);
newDataList = new ArrayList<>(new HashSet<>(newDataList));
gRuleMap.remove(name);
gRuleMap.put(name, newDataList);
} else {
gRuleMap.put(name, dataList);
}
Config.globalDataMap.remove(host);
Config.globalDataMap.put(host, gRuleMap);
} else {
Map<String, List<String>> ruleMap = new HashMap<>();
ruleMap.put(name, dataList);
// 添加单一Host
Config.globalDataMap.put(host, ruleMap);
}
Config.globalDataMap.compute(host, (existingHost, existingMap) -> {
Map<String, List<String>> gRuleMap = Optional.ofNullable(existingMap).orElse(new ConcurrentHashMap<>());
gRuleMap.merge(name, new ArrayList<>(dataList), (existingList, newList) -> {
Set<String> combinedSet = new LinkedHashSet<>(existingList);
combinedSet.addAll(newList);
return new ArrayList<>(combinedSet);
});
return gRuleMap;
});
String[] splitHost = host.split("\\.");
String onlyHost = host.split(":")[0];
@@ -135,7 +131,7 @@ public class RegularMatcher {
}
}
});
CachePool.addToCache(messageIndex, finalMap);
CachePool.put(messageIndex, finalMap);
return finalMap;
}
}
@@ -198,8 +194,8 @@ public class RegularMatcher {
while (matcher.find()) {
if (!matcher.group(1).isEmpty()) {
Object[] params = indexList.stream().map(i -> {
if (!matcher.group(i+1).isEmpty()) {
return matcher.group(i+1);
if (!matcher.group(i + 1).isEmpty()) {
return matcher.group(i + 1);
}
return "";
}).toArray();
@@ -225,7 +221,7 @@ public class RegularMatcher {
}
private Matcher createPatternMatcher(String regex, String content, boolean sensitive) {
Pattern pattern = (sensitive) ? new Pattern(regex) : new Pattern(regex, Pattern.IGNORE_CASE);
Pattern pattern = sensitive ? Pattern.compile(regex) : Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
return pattern.matcher(content);
}
@@ -238,7 +234,7 @@ public class RegularMatcher {
private LinkedList<Integer> parseIndexesFromString(String input) {
LinkedList<Integer> indexes = new LinkedList<>();
Pattern pattern = new Pattern("\\{(\\d+)}");
Pattern pattern = Pattern.compile("\\{(\\d+)}");
Matcher matcher = pattern.matcher(input);
while (matcher.find()) {
@@ -260,7 +256,7 @@ public class RegularMatcher {
}
private String reorderIndex(String format) {
Pattern pattern = new Pattern("\\{(\\d+)}");
Pattern pattern = Pattern.compile("\\{(\\d+)}");
Matcher matcher = pattern.matcher(format);
int count = 0;
while (matcher.find()) {

View File

@@ -1,16 +1,7 @@
package hae.utils.config;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.http.RequestOptions;
import burp.api.montoya.http.message.HttpRequestResponse;
import burp.api.montoya.http.message.requests.HttpRequest;
import hae.Config;
@@ -18,6 +9,13 @@ import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.representer.Representer;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
public class ConfigLoader {
private final MontoyaApi api;
private final Yaml yaml;
@@ -32,7 +30,7 @@ public class ConfigLoader {
this.yaml = new Yaml(representer, dop);
String configPath = determineConfigPath();
this.configFilePath = String.format("%s/%s", configPath, "Config.yml");
this.configFilePath = String.format("%s/%s", configPath, "Config.yml");
this.rulesFilePath = String.format("%s/%s", configPath, "Rules.yml");
// 构造函数,初始化配置
@@ -48,7 +46,7 @@ public class ConfigLoader {
File rulesFilePath = new File(this.rulesFilePath);
if (!(rulesFilePath.exists() && rulesFilePath.isFile())) {
initRules();
initRulesByRes();
}
Config.globalRules = getRules();
@@ -80,6 +78,7 @@ public class ConfigLoader {
public void initConfig() {
Map<String, Object> r = new LinkedHashMap<>();
r.put("excludeSuffix", getExcludeSuffix());
r.put("blockHost", getBlockHost());
try {
Writer ws = new OutputStreamWriter(Files.newOutputStream(Paths.get(configFilePath)), StandardCharsets.UTF_8);
yaml.dump(r, ws);
@@ -92,24 +91,6 @@ public class ConfigLoader {
return rulesFilePath;
}
public String getExcludeSuffix() {
File yamlSetting = new File(configFilePath);
if (!yamlSetting.exists() || !yamlSetting.isFile()) {
return Config.suffix;
}
try (InputStream inorder = Files.newInputStream(Paths.get(configFilePath))) {
Map<String, Object> r = new Yaml().load(inorder);
if (r.containsKey("excludeSuffix")) {
return r.get("excludeSuffix").toString();
}
}catch (Exception ignored) {
}
return Config.suffix;
}
// 获取规则配置
public Map<String, Object[][]> getRules() {
Map<String, Object[][]> rules = new HashMap<>();
@@ -147,24 +128,110 @@ public class ConfigLoader {
}
return rules;
} catch (Exception ignored){
} catch (Exception ignored) {
}
return rules;
}
public void setExcludeSuffix(String excludeSuffix) {
Map<String,Object> r = new LinkedHashMap<>();
r.put("excludeSuffix", excludeSuffix);
try{
Writer ws = new OutputStreamWriter(Files.newOutputStream(Paths.get(configFilePath)), StandardCharsets.UTF_8);
yaml.dump(r, ws);
ws.close();
public String getBlockHost() {
File yamlSetting = new File(configFilePath);
if (!yamlSetting.exists() || !yamlSetting.isFile()) {
return Config.host;
}
try (InputStream inorder = Files.newInputStream(Paths.get(configFilePath))) {
Map<String, Object> r = new Yaml().load(inorder);
if (r.containsKey("blockHost")) {
return r.get("blockHost").toString();
}
} catch (Exception ignored) {
}
return Config.host;
}
public String getExcludeSuffix() {
File yamlSetting = new File(configFilePath);
if (!yamlSetting.exists() || !yamlSetting.isFile()) {
return Config.suffix;
}
try (InputStream inorder = Files.newInputStream(Paths.get(configFilePath))) {
Map<String, Object> r = new Yaml().load(inorder);
if (r.containsKey("excludeSuffix")) {
return r.get("excludeSuffix").toString();
}
} catch (Exception ignored) {
}
return Config.suffix;
}
private Map<String, Object> loadCurrentConfig() {
Path path = Paths.get(configFilePath);
if (!Files.exists(path)) {
return new LinkedHashMap<>(); // 返回空的Map表示没有当前配置
}
try (InputStream in = Files.newInputStream(path)) {
return yaml.load(in);
} catch (IOException e) {
return new LinkedHashMap<>(); // 读取失败时也返回空的Map
}
}
public void initRules() {
public void setExcludeSuffix(String excludeSuffix) {
Map<String, Object> currentConfig = loadCurrentConfig();
currentConfig.put("excludeSuffix", excludeSuffix); // 更新配置
try (Writer ws = new OutputStreamWriter(Files.newOutputStream(Paths.get(configFilePath)), StandardCharsets.UTF_8)) {
yaml.dump(currentConfig, ws);
} catch (IOException ignored) {
}
}
public void setBlockHost(String blockHost) {
Map<String, Object> currentConfig = loadCurrentConfig();
currentConfig.put("blockHost", blockHost); // 更新配置
try (Writer ws = new OutputStreamWriter(Files.newOutputStream(Paths.get(configFilePath)), StandardCharsets.UTF_8)) {
yaml.dump(currentConfig, ws);
} catch (IOException ignored) {
}
}
public void initRulesByRes() {
boolean isCopySuccess = copyRulesToFile(this.rulesFilePath);
if (!isCopySuccess) {
api.extension().unload();
}
}
private boolean copyRulesToFile(String targetFilePath) {
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("rules/Rules.yml");
File targetFile = new File(targetFilePath);
try (inputStream; OutputStream outputStream = new FileOutputStream(targetFile)) {
if (inputStream != null) {
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
return true;
}
} catch (IOException ignored) {
}
return false;
}
public void initRulesByNet() {
Thread t = new Thread() {
public void run() {
pullRules();
@@ -181,7 +248,7 @@ public class ConfigLoader {
try {
String url = "https://raw.githubusercontent.com/gh0stkey/HaE/gh-pages/Rules.yml";
HttpRequest httpRequest = HttpRequest.httpRequestFromUrl(url);
HttpRequestResponse requestResponse = api.http().sendRequest(httpRequest);
HttpRequestResponse requestResponse = api.http().sendRequest(httpRequest, RequestOptions.requestOptions().withUpstreamTLSVerification());
String responseBody = requestResponse.response().bodyToString();
if (responseBody.contains("rules")) {
FileOutputStream fileOutputStream = new FileOutputStream(rulesFilePath);

View File

@@ -2,11 +2,11 @@ package hae.utils.rule;
import burp.api.montoya.MontoyaApi;
import hae.Config;
import hae.utils.config.ConfigLoader;
import hae.utils.rule.model.Group;
import hae.utils.rule.model.Info;
import hae.utils.config.ConfigLoader;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.representer.Representer;
import java.io.File;
@@ -72,13 +72,14 @@ public class RuleProcessor {
public void addRule(Vector data, String type) {
ArrayList<Object[]> x = new ArrayList<>(Arrays.asList(Config.globalRules.get(type)));
x.add(data.toArray());
Config.globalRules.put(type,x.toArray(new Object[x.size()][]));
Config.globalRules.put(type, x.toArray(new Object[x.size()][]));
this.rulesFormatAndSave();
}
public void removeRule(int select,String type) {
public void removeRule(int select, String type) {
ArrayList<Object[]> x = new ArrayList<>(Arrays.asList(Config.globalRules.get(type)));
x.remove(select);
Config.globalRules.put(type,x.toArray(new Object[x.size()][]));
Config.globalRules.put(type, x.toArray(new Object[x.size()][]));
this.rulesFormatAndSave();
}

View File

@@ -3,7 +3,7 @@ package hae.utils.string;
import java.security.MessageDigest;
public class HashCalculator {
public static String calculateHash(byte[] bytes){
public static String calculateHash(byte[] bytes) {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");

View File

@@ -0,0 +1,30 @@
package hae.utils.ui;
import javax.swing.*;
import java.awt.*;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
public class UIEnhancer {
public static void setTextFieldPlaceholder(JTextField textField, String placeholderText) {
textField.setForeground(Color.GRAY);
textField.setText(placeholderText);
textField.addFocusListener(new FocusListener() {
@Override
public void focusGained(FocusEvent e) {
if (textField.getText().equals(placeholderText)) {
textField.setText("");
textField.setForeground(Color.BLACK);
}
}
@Override
public void focusLost(FocusEvent e) {
if (textField.getText().isEmpty()) {
textField.setForeground(Color.GRAY);
textField.setText(placeholderText);
}
}
});
}
}

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -0,0 +1,284 @@
rules:
- group: Fingerprint
rule:
- name: Shiro
loaded: true
f_regex: (=deleteMe|rememberMe=)
s_regex: ''
format: '{0}'
color: green
scope: any header
engine: dfa
sensitive: true
- name: JSON Web Token
loaded: true
f_regex: (eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9._-]{10,}|eyJ[A-Za-z0-9_\/+-]{10,}\.[A-Za-z0-9._\/+-]{10,})
s_regex: ''
format: '{0}'
color: green
scope: any
engine: nfa
sensitive: true
- name: Swagger UI
loaded: true
f_regex: ((swagger-ui.html)|(\"swagger\":)|(Swagger UI)|(swaggerUi)|(swaggerVersion))
s_regex: ''
format: '{0}'
color: red
scope: response body
engine: dfa
sensitive: false
- name: Ueditor
loaded: true
f_regex: (ueditor\.(config|all)\.js)
s_regex: ''
format: '{0}'
color: green
scope: response body
engine: dfa
sensitive: false
- name: Druid
loaded: true
f_regex: (Druid Stat Index)
s_regex: ''
format: '{0}'
color: orange
scope: response body
engine: dfa
sensitive: false
- group: Maybe Vulnerability
rule:
- name: Java Deserialization
loaded: true
f_regex: (javax\.faces\.ViewState)
s_regex: ''
format: '{0}'
color: yellow
scope: response body
engine: dfa
sensitive: false
- name: Debug Logic Parameters
loaded: true
f_regex: ((access=)|(adm=)|(admin=)|(alter=)|(cfg=)|(clone=)|(config=)|(create=)|(dbg=)|(debug=)|(delete=)|(disable=)|(edit=)|(enable=)|(exec=)|(execute=)|(grant=)|(load=)|(make=)|(modify=)|(rename=)|(reset=)|(root=)|(shell=)|(test=)|(toggl=))
s_regex: ''
format: '{0}'
color: cyan
scope: request
engine: dfa
sensitive: false
- name: URL As A Value
loaded: true
f_regex: (=(https?)(://|%3a%2f%2f))
s_regex: ''
format: '{0}'
color: cyan
scope: any
engine: nfa
sensitive: false
- name: Upload Form
loaded: true
f_regex: (type\=\"file\")
s_regex: ''
format: '{0}'
color: yellow
scope: response body
engine: dfa
sensitive: false
- name: DoS Paramters
loaded: true
f_regex: ((size=)|(page=)|(num=)|(limit=)|(start=)|(end=)|(count=))
s_regex: ''
format: '{0}'
color: cyan
scope: request
engine: dfa
sensitive: false
- group: Basic Information
rule:
- name: Email
loaded: true
f_regex: (([a-z0-9]+[_|\.])*[a-z0-9]+@([a-z0-9]+[-|_|\.])*[a-z0-9]+\.((?!js|css|jpg|jpeg|png|ico)[a-z]{2,5}))
s_regex: ''
format: '{0}'
color: yellow
scope: response
engine: nfa
sensitive: false
- name: Chinese IDCard
loaded: true
f_regex: '[^0-9]((\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(\d{6}(18|19|20)\d{2}(0[1-9]|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)))[^0-9]'
s_regex: ''
format: '{0}'
color: orange
scope: response body
engine: nfa
sensitive: true
- name: Chinese Mobile Number
loaded: true
f_regex: '[^\w]((?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8})[^\w]'
s_regex: ''
format: '{0}'
color: orange
scope: response body
engine: nfa
sensitive: false
- name: Internal IP Address
loaded: true
f_regex: '[^0-9]((127\.0\.0\.1)|(10\.\d{1,3}\.\d{1,3}\.\d{1,3})|(172\.((1[6-9])|(2\d)|(3[01]))\.\d{1,3}\.\d{1,3})|(192\.168\.\d{1,3}\.\d{1,3}))'
s_regex: ''
format: '{0}'
color: cyan
scope: response
engine: nfa
sensitive: true
- name: MAC Address
loaded: true
f_regex: (^([a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5})|[^a-zA-Z0-9]([a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}))
s_regex: ''
format: '{0}'
color: green
scope: response
engine: nfa
sensitive: true
- group: Sensitive Information
rule:
- name: Cloud Key
loaded: true
f_regex: (((access)(|-|_)(key)(|-|_)(id|secret))|(LTAI[a-z0-9]{12,20}))
s_regex: ''
format: '{0}'
color: yellow
scope: any
engine: nfa
sensitive: false
- name: Windows File/Dir Path
loaded: true
f_regex: '[^\w](([a-zA-Z]:\\(?:\w+\\?)*)|([a-zA-Z]:\\(?:\w+\\)*\w+\.\w+))'
s_regex: ''
format: '{0}'
color: green
scope: response
engine: nfa
sensitive: true
- name: Password Field
loaded: true
f_regex: ((|'|")(|[\w]{1,10})([p](ass|wd|asswd|assword))(|[\w]{1,10})(|'|")(:|=)(
|)('|")(.*?)('|")(|,))
s_regex: ''
format: '{0}'
color: yellow
scope: response body
engine: nfa
sensitive: false
- name: Username Field
loaded: true
f_regex: ((|'|")(|[\w]{1,10})(([u](ser|name|sername))|(account)|((((create|update)((d|r)|(by|on|at)))|(creator))))(|[\w]{1,10})(|'|")(:|=)(
|)('|")(.*?)('|")(|,))
s_regex: ''
format: '{0}'
color: green
scope: response body
engine: nfa
sensitive: false
- name: WeCom Key
loaded: true
f_regex: ((corp)(id|secret))
s_regex: ''
format: '{0}'
color: green
scope: response body
engine: dfa
sensitive: false
- name: JDBC Connection
loaded: true
f_regex: (jdbc:[a-z:]+://[a-z0-9\.\-_:;=/@?,&]+)
s_regex: ''
format: '{0}'
color: yellow
scope: any
engine: nfa
sensitive: false
- name: Authorization Header
loaded: true
f_regex: ((basic [a-z0-9=:_\+\/-]{5,100})|(bearer [a-z0-9_.=:_\+\/-]{5,100}))
s_regex: ''
format: '{0}'
color: yellow
scope: response body
engine: nfa
sensitive: false
- name: Sensitive Field
loaded: true
f_regex: ((\[)?('|")?([\w]{0,10})((key)|(secret)|(token)|(config)|(auth)|(access)|(admin))([\w]{0,10})('|")?(\])?(
|)(:|=)( |)('|")(.*?)('|")(|,))
s_regex: ''
format: '{0}'
color: yellow
scope: response
engine: nfa
sensitive: false
- group: Other
rule:
- name: Linkfinder
loaded: true
f_regex: (?:"|')(((?:[a-zA-Z]{1,10}://|//)[^"'/]{1,}\.[a-zA-Z]{2,}[^"']{0,})|((?:/|\.\./|\./)[^"'><,;|*()(%%$^/\\\[\]][^"'><,;|()]{1,})|([a-zA-Z0-9_\-/]{1,}/[a-zA-Z0-9_\-/]{1,}\.(?:[a-zA-Z]{1,4}|action)(?:[\?|#][^"|']{0,}|))|([a-zA-Z0-9_\-/]{1,}/[a-zA-Z0-9_\-/]{3,}(?:[\?|#][^"|']{0,}|))|([a-zA-Z0-9_\-]{1,}\.(?:\w)(?:[\?|#][^"|']{0,}|)))(?:"|')
s_regex: ''
format: '{0}'
color: gray
scope: response body
engine: nfa
sensitive: true
- name: Source Map
loaded: true
f_regex: (\.js\.map)
s_regex: ''
format: '{0}'
color: pink
scope: response body
engine: dfa
sensitive: false
- name: 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: (\+\{.*?\}\[[a-zA-Z]\]\+".*?\.js")
s_regex: '"?([\w].*?)"?:"(.*?)"'
format: '{0}.{1}'
color: green
scope: response body
engine: nfa
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+&@#/%=~_|]))
s_regex: ''
format: '{0}'
color: yellow
scope: response body
engine: nfa
sensitive: false
- name: Router Push
loaded: true
f_regex: (\$router\.push)
s_regex: ''
format: '{0}'
color: magenta
scope: response body
engine: dfa
sensitive: false
- name: All URL
loaded: true
f_regex: (https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;\u4E00-\u9FFF]+[-A-Za-z0-9+&@#/%=~_|])
s_regex: ''
format: '{0}'
color: gray
scope: response body
engine: nfa
sensitive: true