├── .gitignore
├── .idea
├── .gitignore
├── artifacts
│ └── BurpFingerPrint_jar.xml
├── encodings.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── misc.xml
├── uiDesigner.xml
└── vcs.xml
├── README.md
├── images
├── config.png
├── fingerPrint.png
├── fingerPrintConfig.png
├── importantFinger.png
└── weixinqun.png
├── pom.xml
└── src
└── main
├── java
├── META-INF
│ └── MANIFEST.MF
└── burp
│ ├── BurpExtender.java
│ ├── IProxyScanner.java
│ ├── Wrapper
│ └── FingerPrintRulesWrapper.java
│ ├── model
│ ├── DatabaseService.java
│ ├── FingerPrintRule.java
│ ├── TableLogModel.java
│ └── WeakPassword.java
│ ├── ui
│ ├── FingerConfigTab.java
│ ├── FingerTab.java
│ ├── Tags.java
│ ├── WeakPasswordTab.java
│ ├── event
│ │ └── FingerTabEventHandlers.java
│ └── renderer
│ │ ├── ButtonRenderer.java
│ │ ├── CenterRenderer.java
│ │ ├── CenterTableCellRenderer.java
│ │ ├── HavingImportantRenderer.java
│ │ ├── HeaderIconRenderer.java
│ │ ├── HeaderIconTypeRenderer.java
│ │ └── IconTableCellRenderer.java
│ ├── util
│ ├── FingerUtils.java
│ ├── HTTPUtils.java
│ ├── UiUtils.java
│ ├── UrlScanCount.java
│ └── Utils.java
│ └── weakpassword
│ └── TomcatWeakPassword.java
└── resources
├── conf
└── finger-important.json
└── icon
├── WeakPasswordBlasting.png
├── WeakPasswordBlastingFalse.png
├── addButtonIcon.png
├── allButtonIcon.png
├── deleteButton.png
├── editButton.png
├── exportItem.png
├── filterIcon.png
├── flashButton.png
├── importItem.png
├── importantButtonIcon.png
├── moreButton.png
├── normalIcon.png
├── openButtonIcon.png
├── resetItem.png
├── runningButton.png
├── saveButton.png
├── saveItem.png
├── searchButton.png
├── shutdownButtonIcon.png
└── weakPasswordSuccess.png
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 | !**/src/main/**/target/
4 | !**/src/test/**/target/
5 |
6 | ### IntelliJ IDEA ###
7 | .idea/modules.xml
8 | .idea/jarRepositories.xml
9 | .idea/compiler.xml
10 | .idea/libraries/
11 | *.iws
12 | *.iml
13 | *.ipr
14 |
15 | ### Eclipse ###
16 | .apt_generated
17 | .classpath
18 | .factorypath
19 | .project
20 | .settings
21 | .springBeans
22 | .sts4-cache
23 |
24 | ### NetBeans ###
25 | /nbproject/private/
26 | /nbbuild/
27 | /dist/
28 | /nbdist/
29 | /.nb-gradle/
30 | build/
31 | !**/src/main/**/build/
32 | !**/src/test/**/build/
33 |
34 | ### VS Code ###
35 | .vscode/
36 |
37 | ### Mac OS ###
38 | .DS_Store
39 |
40 | ### build
41 | out/
42 |
43 | ### log
44 | log/
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 | # Datasource local storage ignored files
7 | /dataSources/
8 | /dataSources.local.xml
9 |
--------------------------------------------------------------------------------
/.idea/artifacts/BurpFingerPrint_jar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | $PROJECT_DIR$/out/artifacts/BurpFingerPrint_jar
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/uiDesigner.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 |
8 | -
9 |
10 |
11 | -
12 |
13 |
14 | -
15 |
16 |
17 | -
18 |
19 |
20 |
21 |
22 |
23 | -
24 |
25 |
26 |
27 |
28 |
29 | -
30 |
31 |
32 |
33 |
34 |
35 | -
36 |
37 |
38 |
39 |
40 |
41 | -
42 |
43 |
44 |
45 |
46 | -
47 |
48 |
49 |
50 |
51 | -
52 |
53 |
54 |
55 |
56 | -
57 |
58 |
59 |
60 |
61 | -
62 |
63 |
64 |
65 |
66 | -
67 |
68 |
69 |
70 |
71 | -
72 |
73 |
74 | -
75 |
76 |
77 |
78 |
79 | -
80 |
81 |
82 |
83 |
84 | -
85 |
86 |
87 |
88 |
89 | -
90 |
91 |
92 |
93 |
94 | -
95 |
96 |
97 |
98 |
99 | -
100 |
101 |
102 | -
103 |
104 |
105 | -
106 |
107 |
108 | -
109 |
110 |
111 | -
112 |
113 |
114 |
115 |
116 | -
117 |
118 |
119 | -
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Burp 指纹识别
2 | 
3 | 
4 | 
5 | > 攻击过程中,我们通常会用浏览器访问一些资产,该BurpSuite插件实现被动指纹识别+网站提取链接+OA爆破,可帮助我们发现更多资产。
6 |
7 | ## 功能如下
8 | > 下述功能会在2024年5月底完成,如果有更好的建议都可以提,然后再麻烦点个Star,创作不易,打造最强指纹识别库和弱口令探测库
9 | - [x] 浏览器被动指纹识别,已集成Ehole指纹识别库
10 | - [x] 提取网站的URL链接和解析JS文件中的URL链接后进行指纹识别
11 | - [x] 开界面进行指纹库修改,可导入、导出、重置
12 | - [x] 优化已有指纹库,区分重点指纹和常见指纹,补充部分实战热门漏洞的指纹,方便直接一键getshell
13 | 
14 | - [x] 优化算法,提升性能、减少内存开销
15 | - [x] 使用sqlite存储扫描结果,放置因BurpSuite意外退出而导致数据丢失
16 | - [ ] 收集github上常见的EXP工具,提起其含有EXP漏洞的指纹,当成重要指纹,一旦页面出现该指纹,就表示有戏有戏
17 |
18 | - 1、https://github.com/R4gd0ll/I-Wanna-Get-All
19 | - 2、https://github.com/YinWC/2021hvv_vul
20 | - 3、https://github.com/zhzyker/vulmap
21 | - 4、https://github.com/SecWiki/CMS-Hunter
22 | - 5、https://github.com/coffeehb/Some-PoC-oR-ExP
23 | - 6、https://github.com/White-hua/Apt_t00ls
24 | - 7、https://github.com/biggerwing/nsfocus-rsas-knowledge-base
25 | - [ ] 优化GUI,指纹识别出来后,可以快速查看对应的利用EXP的github地址
26 | - [ ] OA类弱口令爆破
27 | - [ ] 与本地Packer Fuzzer JS扫描器配合发现隐秘漏洞
28 |
29 |
30 | ## 支持检测指纹
31 |
32 | - [x] 通达OA
33 | - [x] 致远OA
34 | - [x] 蓝凌OA
35 | - [x] 泛微OA
36 | - [x] 万户OA
37 | - [x] 东华OA
38 | - [x] 信呼OA
39 | - [x] 等等
40 |
41 | ## 支持弱口令爆破组件
42 | - [x] Apache Tomcat
43 | - [ ] 通达OA
44 | - [ ] 致远OA
45 | - [ ] 蓝凌OA
46 | - [ ] 泛微OA
47 | - [ ] 万户OA
48 | - [ ] 东华OA
49 | - [ ] 信呼OA
50 |
51 | ## 优化/建议/问题反馈群
52 |
53 |
54 |
55 | ## 额外推荐笔者另一个好用的插件 - BurpAPIFinder
56 | GITHUB: https://github.com/shuanx/BurpAPIFinder
57 | 该插件为作者2024年4月精心开发出来,融合实战攻防中发现未授权、越权、敏感信息泄漏接口发现的经验入内:
58 | 攻防演练过程中,我们通常会用浏览器访问一些资产,但很多未授权/敏感信息/越权隐匿在已访问接口过html、JS文件等,该插件能让我们发现未授权/敏感信息/越权/登陆接口等:
59 | - [x] 发现通过某接口可以进行未授权/越权获取到所有的账号密码
60 | - [x] 发现通过某接口可以枚举用户信息、密码修改、用户创建接口
61 | - [x] 发现登陆后台网址
62 | - [x] 发现在html、JS中泄漏账号密码或者云主机的Access Key和SecretKey
63 | - [x] ...
64 |
65 | *功能如下*
66 | - [x] 提取网站的URL链接和解析JS文件中的URL链接
67 | - [x] 前段界面可自行定义铭感关键词、敏感url匹配
68 | - [x] 界面可配置的开启主动接口探测、铭感信息获取
69 | - [x] 集成主流攻防场景敏感信息泄漏的指纹库
70 | - [x] 集成HaE的敏感信息识别指纹
71 | - [x] 集成APIKit的敏感信息识别指纹
72 | - [x] 集成sweetPotato的敏感信息识别指纹
73 | - [x] ...
74 |
75 |
76 | ## 免责声明
77 |
78 | 本工具仅作为安全研究交流,请勿用于非法用途。如您在使用本工具的过程中存在任何非法行为,您需自行承担相应后果,本人将不承担任何法律及连带责任。
79 |
80 |
81 |
--------------------------------------------------------------------------------
/images/config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/images/config.png
--------------------------------------------------------------------------------
/images/fingerPrint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/images/fingerPrint.png
--------------------------------------------------------------------------------
/images/fingerPrintConfig.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/images/fingerPrintConfig.png
--------------------------------------------------------------------------------
/images/importantFinger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/images/importantFinger.png
--------------------------------------------------------------------------------
/images/weixinqun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/images/weixinqun.png
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | org.example
8 | BurpFingerPrint
9 | 1.0-SNAPSHOT
10 |
11 |
12 | 11
13 | 11
14 | UTF-8
15 |
16 |
17 |
18 |
19 |
20 | net.portswigger.burp.extender
21 | burp-extender-api
22 | 2.3
23 |
24 |
25 | com.google.code.gson
26 | gson
27 | 2.8.9
28 |
29 |
30 |
31 | org.xerial
32 | sqlite-jdbc
33 | 3.45.3.0
34 |
35 |
36 |
37 |
38 | BurpFingerPrint-v1.4
39 |
40 |
41 | org.apache.maven.plugins
42 | maven-assembly-plugin
43 | 3.7.1
44 |
45 |
46 | jar-with-dependencies
47 |
48 | false
49 |
50 |
51 |
52 | make-assembly
53 | package
54 |
55 | single
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/src/main/java/META-INF/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Main-Class: burp.BurpExtender
3 |
4 |
--------------------------------------------------------------------------------
/src/main/java/burp/BurpExtender.java:
--------------------------------------------------------------------------------
1 | package burp;
2 |
3 | import burp.Wrapper.FingerPrintRulesWrapper;
4 | import burp.model.DatabaseService;
5 | import burp.model.FingerPrintRule;
6 | import burp.ui.Tags;
7 | import burp.util.Utils;
8 | import com.google.gson.Gson;
9 |
10 | import java.io.*;
11 | import java.nio.charset.StandardCharsets;
12 | import java.util.HashSet;
13 | import java.util.List;
14 | import java.util.Set;
15 |
16 |
17 | public class BurpExtender implements IBurpExtender, IExtensionStateListener {
18 | public final static String extensionName = "FingerPrint";
19 | public final static String version = "v2024-05-04";
20 | public final static String author = "Shaun";
21 |
22 | private static PrintWriter stdout;
23 | private static PrintWriter stderr;
24 | private static IBurpExtenderCallbacks callbacks;
25 | private static DatabaseService dataBaseService;
26 | private static IExtensionHelpers helpers;
27 | private static Tags tags;
28 | private static IProxyScanner iProxyScanner;
29 | public static List fingerprintRules;
30 | public static Set hasScanDomainSet = new HashSet<>();
31 |
32 | public static PrintWriter getStdout() {
33 | return stdout;
34 | }
35 |
36 | public static PrintWriter getStderr() {
37 | return stderr;
38 | }
39 |
40 | public static IBurpExtenderCallbacks getCallbacks() {
41 | return callbacks;
42 | }
43 |
44 | public static IExtensionHelpers getHelpers() {
45 | return helpers;
46 | }
47 |
48 | public static Tags getTags() {
49 | return tags;
50 | }
51 |
52 | public static IProxyScanner getIProxyScanner() {
53 | return iProxyScanner;
54 | }
55 |
56 | public static DatabaseService getDataBaseService(){
57 | return dataBaseService;
58 | }
59 |
60 | @Override
61 | public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {
62 | BurpExtender.callbacks = callbacks;
63 | BurpExtender.helpers = callbacks.getHelpers();
64 | BurpExtender.stdout = new PrintWriter(callbacks.getStdout(), true);
65 | BurpExtender.stderr = new PrintWriter(callbacks.getStderr(), true);
66 |
67 | // 获取类加载器
68 | ClassLoader classLoader = getClass().getClassLoader();
69 | String extensionPath = Utils.getExtensionFilePath(BurpExtender.callbacks);
70 | File tmpFile = new File(extensionPath, "finger-tmp.json");
71 | InputStream inputStream = null;
72 | if (tmpFile.exists()) {
73 | try {
74 | inputStream = new FileInputStream(tmpFile);
75 | } catch (FileNotFoundException e) {
76 | stderr.println("[!] Failed to load the configuration file finger.json, because finger-tmp.json not found");
77 | return;
78 | }
79 | } else {
80 | inputStream = classLoader.getResourceAsStream("conf/finger-important.json");
81 | }
82 | if (inputStream == null) {
83 | stderr.println("[!] Failed to load the configuration file finger.json, because config/finger.json not found");
84 | return;
85 | }
86 |
87 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
88 | Gson gson = new Gson();
89 | FingerPrintRulesWrapper rulesWrapper = gson.fromJson(reader, FingerPrintRulesWrapper.class);
90 | fingerprintRules = rulesWrapper.getFingerprint();
91 | if (tmpFile.exists()) {
92 | stdout.println("[+] Successfully loaded the configuration file finger-tmp.json");
93 | }else{
94 | stdout.println("[+] Successfully loaded the configuration file finger-important.json");
95 | }
96 | } catch (IOException e) {
97 | stderr.println("[!] Failed to load the configuration file finger.json, because: " + e.getMessage());
98 | }
99 |
100 | dataBaseService = DatabaseService.getInstance();
101 |
102 | // 标签界面, ExtensionTab 构造时依赖 BurpExtender.callbacks, 所以这个必须放在下面
103 | BurpExtender.tags = new Tags(callbacks, extensionName);
104 | BurpExtender.iProxyScanner = new IProxyScanner();
105 |
106 | callbacks.registerProxyListener(iProxyScanner);
107 | callbacks.registerExtensionStateListener(this);
108 |
109 | BurpExtender.stdout.println(Utils.getBanner());
110 | }
111 |
112 | @Override
113 | public void extensionUnloaded() {
114 | // 扩展卸载时,立刻关闭线程池
115 | BurpExtender.getStdout().println("[+] Extension is being unloaded, cleaning up resources...");
116 |
117 | // 立刻关闭线程池
118 | if (iProxyScanner.executorService != null) {
119 | // 尝试立即关闭所有正在执行的任务
120 | List notExecutedTasks = iProxyScanner.executorService.shutdownNow();
121 | BurpExtender.getStdout().println("[+] 尝试停止所有正在执行的任务,未执行的任务数量:" + notExecutedTasks.size());
122 | }
123 |
124 | // 关闭数据库连接
125 | if (dataBaseService != null) {
126 | dataBaseService.closeConnection();
127 | BurpExtender.getStdout().println("[+] 断开数据连接成功.");
128 | }
129 |
130 | // 关闭计划任务
131 | IProxyScanner.shutdownMonitorExecutor();
132 | BurpExtender.getStdout().println("[+] 定时爬去任务断开成功.");
133 | }
134 |
135 |
136 | }
--------------------------------------------------------------------------------
/src/main/java/burp/IProxyScanner.java:
--------------------------------------------------------------------------------
1 | package burp;
2 |
3 | import burp.model.TableLogModel;
4 | import burp.model.WeakPassword;
5 | import burp.ui.FingerTab;
6 | import burp.ui.WeakPasswordTab;
7 | import burp.util.FingerUtils;
8 | import burp.util.HTTPUtils;
9 | import burp.util.UrlScanCount;
10 | import burp.util.Utils;
11 | import burp.weakpassword.TomcatWeakPassword;
12 |
13 | import java.net.URL;
14 | import java.text.SimpleDateFormat;
15 | import java.util.*;
16 | import java.util.concurrent.*;
17 |
18 | /**
19 | * @author: shaun
20 | * @create: 2024/4/5 09:07
21 | * @description:TODO
22 | */
23 | public class IProxyScanner implements IProxyListener {
24 | private static UrlScanCount haveScanUrl = new UrlScanCount();
25 | public static int totalScanCount = 0;
26 | final ThreadPoolExecutor executorService; // 修改这行
27 | private static IExtensionHelpers helpers;
28 | final ExecutorService monitorExecutorService;; // 修改这行
29 | private static ScheduledExecutorService monitorExecutor;
30 | // 暴力破解模块支持指纹
31 | private static final List WEAKPASSWORDMODEL = Arrays.asList("Tomcat");
32 |
33 | public IProxyScanner() {
34 | helpers = BurpExtender.getHelpers();
35 | // 先新建一个进程用于后续处理任务
36 | int coreCount = Runtime.getRuntime().availableProcessors();
37 | coreCount = Math.max(coreCount, 20);
38 | int maxPoolSize = coreCount * 2;
39 | long keepAliveTime = 60L;
40 | executorService = new ThreadPoolExecutor(
41 | coreCount,
42 | maxPoolSize,
43 | keepAliveTime,
44 | TimeUnit.SECONDS,
45 | new LinkedBlockingQueue(), // 可以根据需要调整队列类型和大小
46 | Executors.defaultThreadFactory(),
47 | new ThreadPoolExecutor.CallerRunsPolicy() // 当线程池和队列都满时,任务在调用者线程中执行
48 | );
49 | BurpExtender.getStdout().println("[+] run executorService maxPoolSize: " + coreCount + " ~ " + maxPoolSize);
50 |
51 | monitorExecutorService = Executors.newFixedThreadPool(6); // 用固定数量的线程
52 |
53 | monitorExecutor = Executors.newSingleThreadScheduledExecutor();
54 | startDatabaseMonitor();
55 | BurpExtender.getStdout().println("[+] run Weak password blasting monitorExecutor success~ ");
56 | }
57 |
58 | private void startDatabaseMonitor() {
59 | monitorExecutor.scheduleAtFixedRate(() -> {
60 | monitorExecutorService.submit(() -> {
61 | try {
62 | if (WeakPasswordTab.weakPasswordBlasting.isSelected()){
63 | BurpExtender.getStdout().println("[+] 弱口令爆破模块关闭,不进行定时获取数据进行爆破。");
64 | return;
65 | }
66 | WeakPassword wp = BurpExtender.getDataBaseService().fetchAndMarkSinglePathAsCrawling();
67 | if (wp == null){
68 | BurpExtender.getStdout().println("[+] 弱口令爆破模块运行中,但无需爆破的数据。");
69 | return;
70 | }
71 | wp = TomcatWeakPassword.checkWeakPasswords(wp);
72 | BurpExtender.getStdout().println("[+] url: " + wp.getUrl() + "爆破结果为: " + wp.getStatus());
73 | BurpExtender.getDataBaseService().updateWeakPassword(wp);
74 | } catch (Exception e) {
75 | BurpExtender.getStderr().println("[!] scheduleAtFixedRate error: ");
76 | e.printStackTrace(BurpExtender.getStderr());
77 | }
78 | });
79 | }, 0, 10, TimeUnit.SECONDS);
80 | }
81 |
82 | public static void setHaveScanUrlNew(){
83 | haveScanUrl = new UrlScanCount();
84 | FingerTab.lbSuccessCount.setText("0");
85 | FingerTab.lbRequestCount.setText("0");
86 | BurpExtender.getDataBaseService().clearRequestsResponseTable();
87 | BurpExtender.getDataBaseService().clearTableDataTable();
88 | FingerTab.timer.stop();
89 | }
90 |
91 | public void processProxyMessage(boolean messageIsRequest, final IInterceptedProxyMessage iInterceptedProxyMessage) {
92 | if (!messageIsRequest) {
93 | // 更新总数
94 | FingerTab.lbRequestCount.setText(Integer.toString(BurpExtender.getDataBaseService().getTableDataCount()));
95 |
96 | IHttpRequestResponse requestResponse = iInterceptedProxyMessage.getMessageInfo();
97 | final IHttpRequestResponse resrsp = iInterceptedProxyMessage.getMessageInfo();
98 | String method = helpers.analyzeRequest(resrsp).getMethod();
99 |
100 | // 提取url,过滤掉静态文件
101 | String url = String.valueOf(helpers.analyzeRequest(resrsp).getUrl());
102 | if (Utils.isStaticFile(url) && !url.contains("favicon.") && !url.contains(".ico")){
103 | BurpExtender.getStdout().println("[+]静态文件,不进行url识别:" + url);
104 | return;
105 | }
106 |
107 | byte[] responseBytes = requestResponse.getResponse();
108 |
109 | // 网页提取URL并进行指纹识别
110 | executorService.submit(new Runnable() {
111 | @Override
112 | public void run() {
113 |
114 | // 存储url和对应的response值
115 | Map totalUrlResponse = new HashMap();
116 |
117 | // 当前请求的URL,requests,Response,以及findUrl来区别是否为提取出来的URL
118 | Map originalData = new HashMap();
119 | originalData.put("responseRequest", requestResponse);
120 | originalData.put("isFindUrl", false);
121 | originalData.put("method", method);
122 | totalUrlResponse.put(url, originalData);
123 |
124 | if (!url.contains("favicon.") && !url.contains(".ico") && !FingerTab.toggleButton.isSelected()) {
125 | String mime = helpers.analyzeResponse(responseBytes).getInferredMimeType();
126 | URL urlUrl = helpers.analyzeRequest(resrsp).getUrl();
127 | // 针对html页面提取
128 | Set urlSet = new HashSet<>(Utils.extractUrlsFromHtml(url, new String(responseBytes)));
129 | // 针对JS页面提取
130 | if (mime.equals("script") || mime.equals("HTML") || url.contains(".htm") || Utils.isGetUrlExt(url)) {
131 | urlSet.addAll(Utils.findUrl(urlUrl, new String(responseBytes)));
132 | }
133 | BurpExtender.getStdout().println("[+] 进入网页提取URL页面: " + url + "\r\n URL result: " + urlSet);
134 |
135 | // 依次遍历urlSet获取其返回的response值
136 | for (String getUrl : urlSet) {
137 | totalUrlResponse.put(getUrl, HTTPUtils.makeGetRequest(getUrl));
138 | }
139 | }
140 | BurpExtender.getStdout().println("[+]指纹识别开始: " + totalUrlResponse);
141 |
142 | // 依次提取url和对应的response值进行指纹识别
143 | for (Map.Entry entry : totalUrlResponse.entrySet()) {
144 |
145 | String oneUrl = entry.getKey();
146 | Object value = entry.getValue();
147 | if (value instanceof Map) {
148 | @SuppressWarnings("unchecked")
149 | Map oneResult = (Map) value;
150 | // Now it's safe to use oneResult
151 | IHttpRequestResponse oneRequestsResponse = (IHttpRequestResponse) oneResult.get("responseRequest");
152 | byte[] oneResponseBytes = oneRequestsResponse.getResponse();
153 | int requestsResponseIndex = BurpExtender.getDataBaseService().insertOrUpdateRequestResponse(oneUrl, oneRequestsResponse.getRequest(), oneResponseBytes);
154 | // 返回结果为空则退出
155 | if (oneResponseBytes == null || oneResponseBytes.length == 0) {
156 | BurpExtender.getStdout().println("返回结果为空: " + oneUrl);
157 | continue;
158 | }
159 | String oneMethod = (String) oneResult.get("method");
160 | IResponseInfo responseInfo = helpers.analyzeResponse(oneResponseBytes);
161 |
162 | // 指纹识别并存储匹配结果
163 | TableLogModel mapResult = FingerUtils.FingerFilter(iInterceptedProxyMessage.getMessageReference(), oneUrl, oneResponseBytes, oneRequestsResponse.getHttpService(), helpers, requestsResponseIndex);
164 |
165 | // 无法识别出指纹的,则不添加
166 | if (mapResult.getResult().isEmpty()) {
167 | BurpExtender.getStdout().println("[+]无法识别指纹url: " + oneUrl);
168 | continue;
169 | }
170 |
171 | mapResult.setStatus(Short.toString(responseInfo.getStatusCode()));
172 | mapResult.setMethod(oneMethod);
173 | BurpExtender.getDataBaseService().insertOrUpdateLogEntry(mapResult);
174 | // 判断是否需要需要进行爆破
175 | ifInsertWeakPasswordDatabase(mapResult);
176 | BurpExtender.getStdout().println(mapResult);
177 | }
178 | BurpExtender.getStdout().println("[END]指纹识别结束: " + totalUrlResponse);
179 | }
180 | }
181 | });
182 |
183 | int waitingTasks = executorService.getQueue().size(); // 添加这行
184 | BurpExtender.getStdout().println(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()) + ": 当前还有" + waitingTasks + " 个任务等待运行"); // 添加这行
185 |
186 |
187 | }
188 |
189 | }
190 |
191 | public static void shutdownMonitorExecutor() {
192 | // 关闭监控线程池
193 | if (monitorExecutor != null && !monitorExecutor.isShutdown()) {
194 | monitorExecutor.shutdown();
195 | try {
196 | // 等待线程池终止,设置一个合理的超时时间
197 | if (!monitorExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
198 | // 如果线程池没有在规定时间内终止,则强制关闭
199 | monitorExecutor.shutdownNow();
200 | }
201 | } catch (InterruptedException e) {
202 | // 如果等待期间线程被中断,恢复中断状态
203 | Thread.currentThread().interrupt();
204 | // 强制关闭
205 | monitorExecutor.shutdownNow();
206 | }
207 | }
208 | }
209 |
210 | public static void ifInsertWeakPasswordDatabase(TableLogModel result){
211 | for (String value : WEAKPASSWORDMODEL){
212 | if (result.getResult().contains(value) && !BurpExtender.getDataBaseService().existsWeakPasswordByUrl(Utils.getUriFromUrl(result.getUrl()))){
213 | BurpExtender.getDataBaseService().insertWeakPassword(Utils.getUriFromUrl(result.getUrl()), value, "-", "-", "-", "等待爆破中", "-");
214 | }
215 | }
216 | }
217 |
218 | }
219 |
--------------------------------------------------------------------------------
/src/main/java/burp/Wrapper/FingerPrintRulesWrapper.java:
--------------------------------------------------------------------------------
1 | package burp.Wrapper;
2 |
3 | import burp.model.FingerPrintRule;
4 | import java.util.List;
5 | /**
6 | * @author: shaun
7 | * @create: 2024/3/2 17:51
8 | * @description:TODO
9 | */
10 | public class FingerPrintRulesWrapper {
11 | private List fingerprint;
12 |
13 | public List getFingerprint() {
14 | return fingerprint;
15 | }
16 |
17 | public void setFingerprint(List fingerprint) {
18 | this.fingerprint = fingerprint;
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/burp/model/DatabaseService.java:
--------------------------------------------------------------------------------
1 | package burp.model;
2 |
3 | import burp.BurpExtender;
4 | import burp.util.Utils;
5 | import com.google.gson.Gson;
6 |
7 | import java.nio.file.Paths;
8 | import java.sql.*;
9 | import java.text.SimpleDateFormat;
10 | import java.util.Date;
11 | import java.util.HashMap;
12 | import java.util.Map;
13 | import java.util.ArrayList;
14 | import java.util.List;
15 | import java.time.Duration;
16 | import java.time.LocalDateTime;
17 |
18 | public class DatabaseService {
19 |
20 | private static final String CONNECTION_STRING = "jdbc:sqlite:" + Paths.get(Utils.getExtensionFilePath(BurpExtender.getCallbacks()), "BurpFingerPrint.db").toAbsolutePath().toString();;
21 |
22 | private static DatabaseService instance;
23 | private Connection connection;
24 |
25 | private DatabaseService() {
26 | initializeConnection();
27 | initializeDatabase();
28 | }
29 |
30 | public static synchronized DatabaseService getInstance() {
31 | if (instance == null) {
32 | instance = new DatabaseService();
33 | }
34 | return instance;
35 | }
36 |
37 | private void initializeConnection() {
38 | try {
39 | // 注册 SQLite 驱动程序
40 | Driver driver = new org.sqlite.JDBC();
41 | DriverManager.registerDriver(driver);
42 | connection = DriverManager.getConnection(CONNECTION_STRING);
43 | // Enable foreign key support
44 | connection.createStatement().execute("PRAGMA foreign_keys = ON");
45 | BurpExtender.getStdout().println("[+] load db connect success~ ");
46 | } catch (Exception e) {
47 | BurpExtender.getStderr().println("[!] load db connect Fail, befalse:");
48 | e.printStackTrace(BurpExtender.getStderr());
49 | }
50 | }
51 |
52 | private synchronized void initializeDatabase() {
53 | // 用于创建表的SQL语句
54 | String sql = "CREATE TABLE IF NOT EXISTS table_data (\n"
55 | + " id INTEGER PRIMARY KEY AUTOINCREMENT,\n"
56 | + " pid INTEGER, \n"
57 | + " url TEXT NOT NULL,\n"
58 | + " method TEXT ,\n"
59 | + " title TEXT,\n"
60 | + " status TEXT,\n"
61 | + " result TEXT,\n"
62 | + " type TEXT,\n"
63 | + " is_important INTEGER,\n"
64 | + " time TEXT,\n"
65 | + " result_info TEXT,\n"
66 | + "request_response_index INTEGER, \n"
67 | + "host TEXT, \n"
68 | + "port INTEGER, \n"
69 | + "protocol TEXT\n"
70 | + ");";
71 |
72 | try (Statement stmt = connection.createStatement()) {
73 | stmt.execute(sql);
74 | BurpExtender.getStdout().println("[+] create table_data db success~");
75 | } catch (Exception e) {
76 | BurpExtender.getStderr().println("[!] create table_data db failed, because:");
77 | e.printStackTrace(BurpExtender.getStderr());
78 | }
79 |
80 | // 用来创建数据库requestResponse
81 | String requestsResponseSQL = "CREATE TABLE IF NOT EXISTS requests_response (\n"
82 | + " id INTEGER PRIMARY KEY AUTOINCREMENT,\n"
83 | + " url TEXT NOT NULL,\n"
84 | + " request BLOB, \n"
85 | + " response BLOB\n"
86 | + ");";
87 |
88 | try (Statement stmt = connection.createStatement()) {
89 | stmt.execute(requestsResponseSQL);
90 | BurpExtender.getStdout().println("[+] create requests response db success~");
91 | } catch (Exception e) {
92 | BurpExtender.getStderr().println("[!] create requests response db failed, because:");
93 | e.printStackTrace(BurpExtender.getStderr());
94 | }
95 |
96 | String weakPasswordSQL = "CREATE TABLE IF NOT EXISTS weak_password (\n"
97 | + " id INTEGER PRIMARY KEY AUTOINCREMENT,\n"
98 | + " url TEXT NOT NULL,\n"
99 | + " finger TEXT, \n"
100 | + " weak_password TEXT, \n"
101 | + " test_number TEXT, \n"
102 | + " result_info TEXT, \n"
103 | + " status TEXT, \n"
104 | + " time TEXT, \n"
105 | + " request BLOB, \n"
106 | + " response BLOB \n"
107 | + ");";
108 |
109 | try (Statement stmt = connection.createStatement()) {
110 | stmt.execute(weakPasswordSQL);
111 | BurpExtender.getStdout().println("[+] create weak_password db success~");
112 | } catch (Exception e) {
113 | BurpExtender.getStderr().println("[!] create weak_password db failed, because:");
114 | e.printStackTrace(BurpExtender.getStderr());
115 | }
116 | }
117 |
118 | public Connection getConnection() throws SQLException {
119 | return DriverManager.getConnection(CONNECTION_STRING);
120 | }
121 |
122 | public synchronized int insertOrUpdateLogEntry(TableLogModel logEntry) {
123 | int generatedId = -1; // 默认ID值,如果没有生成ID,则保持此值
124 | String checkSql = "SELECT id, result, result_info, status, request_response_index, type, is_important FROM table_data WHERE url = ?";
125 |
126 | try (Connection conn = getConnection();
127 | PreparedStatement checkStmt = conn.prepareStatement(checkSql)) {
128 | // 检查记录是否存在
129 | checkStmt.setString(1, Utils.getUriFromUrl(logEntry.getUrl()));
130 | ResultSet rs = checkStmt.executeQuery();
131 | if (rs.next()) {
132 | // 记录存在,更新记录
133 |
134 | String updateSql = "UPDATE table_data SET method = ?, title = ?, status = ?, result = ?, type = ?, is_important = ?, result_info = ?, host = ?, port = ?, protocol = ?, time = ?, request_response_index = ? WHERE url = ?";
135 | generatedId = rs.getInt("id");
136 | String result = rs.getString("result");
137 | String status = rs.getString("status");
138 | Boolean isImportant = rs.getBoolean("is_important");
139 | int request_response_index = rs.getInt("request_response_index");
140 |
141 | for (String oneRs : logEntry.getResult().split(", ")){
142 | if (!result.contains(oneRs)){
143 | result = result + ", " + oneRs;
144 | }
145 | }
146 |
147 | String type = rs.getString("type");
148 | for (String oneType : logEntry.getType().split(", ")){
149 | if (!type.contains(oneType)){
150 | type = type + ", " + oneType;
151 | }
152 | }
153 |
154 | if (logEntry.getStatus().equals("200")){
155 | request_response_index = logEntry.getRequestResponseIndex();
156 | status = logEntry.getStatus();
157 | }
158 |
159 | if (!isImportant){
160 | isImportant = logEntry.getIsImportant();
161 | }
162 |
163 | try (PreparedStatement updateStmt = conn.prepareStatement(updateSql)) {
164 | updateStmt.setString(1, logEntry.getMethod());
165 | updateStmt.setString(2, logEntry.getTitle());
166 | updateStmt.setString(3, status);
167 | updateStmt.setString(4, result);
168 | updateStmt.setString(5, type);
169 | updateStmt.setBoolean(6, isImportant);
170 | updateStmt.setString(7, rs.getString("result_info") + "\r\n\r\n" + logEntry.getResultInfo());
171 | updateStmt.setString(8, logEntry.getHost());
172 | updateStmt.setInt(9, logEntry.getPort());
173 | updateStmt.setString(10, logEntry.getProtocol());
174 | updateStmt.setString(11, logEntry.getTime());
175 | updateStmt.setInt(12, request_response_index);
176 | updateStmt.setString(13, Utils.getUriFromUrl(logEntry.getUrl()));
177 | updateStmt.executeUpdate();
178 | }
179 | } else {
180 | // 记录不存在,插入新记录
181 | String insertSql = "INSERT INTO table_data (pid, url, method, title, status, result, type, is_important, result_info, request_response_index, host, port, protocol, time) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
182 | try (PreparedStatement insertStmt = conn.prepareStatement(insertSql, Statement.RETURN_GENERATED_KEYS)) {
183 | insertStmt.setInt(1, logEntry.getPid());
184 | insertStmt.setString(2, logEntry.getUrl());
185 | insertStmt.setString(3, logEntry.getMethod());
186 | insertStmt.setString(4, logEntry.getTitle());
187 | insertStmt.setString(5, logEntry.getStatus());
188 | insertStmt.setString(6, logEntry.getResult());
189 | insertStmt.setString(7, logEntry.getType());
190 | insertStmt.setBoolean(8, logEntry.getIsImportant());
191 | insertStmt.setString(9, logEntry.getResultInfo());
192 | insertStmt.setInt(10, logEntry.getRequestResponseIndex());
193 | insertStmt.setString(11, logEntry.getHost());
194 | insertStmt.setInt(12, logEntry.getPort());
195 | insertStmt.setString(13, logEntry.getProtocol());
196 | insertStmt.setString(14, logEntry.getTime());
197 | insertStmt.executeUpdate();
198 |
199 | // 获取生成的键值
200 | try (ResultSet generatedKeys = insertStmt.getGeneratedKeys()) {
201 | if (generatedKeys.next()) {
202 | generatedId = generatedKeys.getInt(1); // 获取生成的ID
203 | }
204 | }
205 | }
206 | }
207 | } catch (Exception e) {
208 | BurpExtender.getStderr().println("[-] Error inserting or updating table_data: ");
209 | e.printStackTrace(BurpExtender.getStderr());
210 | }
211 |
212 | return generatedId; // 返回ID值,无论是更新还是插入
213 | }
214 |
215 | public synchronized List getAllTableDataModels() {
216 | List allTableDataModels = new ArrayList<>();
217 | String sql = "SELECT * FROM table_data";
218 |
219 | try (Connection conn = getConnection();
220 | PreparedStatement stmt = conn.prepareStatement(sql);
221 | ResultSet rs = stmt.executeQuery()) {
222 |
223 | // loop through the result set
224 | while (rs.next()) {
225 | TableLogModel model = new TableLogModel(
226 | rs.getInt("pid"),
227 | rs.getString("url"),
228 | rs.getString("method"),
229 | rs.getString("title"),
230 | rs.getString("status"),
231 | rs.getString("result"),
232 | rs.getString("type"),
233 | rs.getInt("is_important") != 0, // Convert to Boolean
234 | rs.getString("result_info"),
235 | Utils.iHttpService(rs.getString("host"), rs.getInt("port"), rs.getString("protocol")),
236 | rs.getInt("request_response_index"),
237 | rs.getString("time")
238 | );
239 | // Assuming you have a constructor that matches these parameters.
240 | allTableDataModels.add(model);
241 | }
242 | } catch (SQLException e) {
243 | BurpExtender.getStderr().println("[-] Error retrieving all records from table_data: ");
244 | e.printStackTrace(BurpExtender.getStderr());
245 | }
246 | return allTableDataModels;
247 | }
248 |
249 | public synchronized HashMap getResultCountsFromDatabase() {
250 | HashMap resultCounts = new HashMap<>();
251 |
252 | // 调用 getAllTableDataModels() 方法以从数据库获取所有记录
253 | List allTableDataModels = getAllTableDataModels();
254 |
255 | // 遍历所有记录
256 | for (TableLogModel model : allTableDataModels) {
257 | String result = model.getResult(); // 获取结果值
258 | if (result != null && !result.trim().isEmpty()) {
259 | String[] parts = result.split(", "); // 根据", "进行切分
260 | for (String part : parts) {
261 | resultCounts.put(part, resultCounts.getOrDefault(part, 0) + 1); // 添加到映射中进行去重,并计数
262 | }
263 | }
264 | }
265 |
266 | return resultCounts; // 返回包含每个不同结果出现次数的 HashMap
267 | }
268 | public synchronized List getTableDataModelsByFilter(String typeFilter, String resultFilter, Boolean isImportantFilter) {
269 | List filteredTableDataModels = new ArrayList<>();
270 | // Start with a base SQL query
271 | StringBuilder sqlBuilder = new StringBuilder("SELECT * FROM table_data WHERE 1=1");
272 |
273 | // If typeFilter is not "全部", add it to the query
274 | if (!"全部".equals(typeFilter)) {
275 | sqlBuilder.append(" AND type LIKE ?");
276 | }
277 | // If resultFilter is not "全部", add it to the query
278 | if (!"全部".equals(resultFilter)) {
279 | sqlBuilder.append(" AND result LIKE ?");
280 | }
281 | // If isImportantFilter is not null, add it to the query
282 | if (isImportantFilter != null) {
283 | sqlBuilder.append(" AND is_important = ?");
284 | }
285 |
286 | try (Connection conn = getConnection();
287 | PreparedStatement pstmt = conn.prepareStatement(sqlBuilder.toString())) {
288 |
289 | // Set the parameters to the prepared statement
290 | int paramIndex = 1;
291 | if (!"全部".equals(typeFilter)) {
292 | pstmt.setString(paramIndex++, "%" + typeFilter + "%");
293 | }
294 | if (!"全部".equals(resultFilter)) {
295 | pstmt.setString(paramIndex++, "%" + resultFilter + "%");
296 | }
297 | if (isImportantFilter != null) {
298 | pstmt.setInt(paramIndex, isImportantFilter ? 1 : 0);
299 | }
300 |
301 | ResultSet rs = pstmt.executeQuery();
302 |
303 | // Loop through the result set
304 | while (rs.next()) {
305 | TableLogModel model = new TableLogModel(
306 | rs.getInt("pid"),
307 | rs.getString("url"),
308 | rs.getString("method"),
309 | rs.getString("title"),
310 | rs.getString("status"),
311 | rs.getString("result"),
312 | rs.getString("type"),
313 | rs.getInt("is_important") != 0, // Convert to Boolean
314 | rs.getString("result_info"),
315 | Utils.iHttpService(rs.getString("host"), rs.getInt("port"), rs.getString("protocol")),
316 | rs.getInt("request_response_index"),
317 | rs.getString("time")
318 | );
319 | // Assuming you have a constructor that matches these parameters.
320 | filteredTableDataModels.add(model);
321 | }
322 | } catch (SQLException e) {
323 | BurpExtender.getStderr().println("[-] Error retrieving filtered records from table_data: ");
324 | e.printStackTrace(BurpExtender.getStderr());
325 | }
326 | return filteredTableDataModels;
327 | }
328 |
329 |
330 | public synchronized void deleteDataByUrl(String url) {
331 | // 删除SQL语句
332 | String sql = "DELETE FROM table_data WHERE url = ?";
333 |
334 | try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
335 | // 设置参数
336 | pstmt.setString(1, url);
337 | // 执行删除操作
338 | pstmt.executeUpdate();
339 |
340 | } catch (SQLException e) {
341 | BurpExtender.getStderr().println("[!] Error deleting data from table_data with URL: " + url);
342 | e.printStackTrace(BurpExtender.getStderr());
343 | }
344 | }
345 |
346 | // 根据URL查询table_data表中的数据
347 | public synchronized TableLogModel getTableDataByUrl(String url) {
348 | String sql = "SELECT * FROM table_data WHERE url = ?";
349 |
350 | try (Connection conn = getConnection();
351 | PreparedStatement pstmt = conn.prepareStatement(sql)) {
352 |
353 | pstmt.setString(1, url);
354 | ResultSet rs = pstmt.executeQuery();
355 | if (rs.next()) {
356 | TableLogModel model = new TableLogModel(
357 | rs.getInt("pid"),
358 | rs.getString("url"),
359 | rs.getString("method"),
360 | rs.getString("title"),
361 | rs.getString("status"),
362 | rs.getString("result"),
363 | rs.getString("type"),
364 | rs.getInt("is_important") != 0, // Convert to Boolean
365 | rs.getString("result_info"),
366 | Utils.iHttpService(rs.getString("host"), rs.getInt("port"), rs.getString("protocol")),
367 | rs.getInt("request_response_index"),
368 | rs.getString("time")
369 | );
370 | return model;
371 | }
372 | } catch (SQLException e) {
373 | e.printStackTrace(); // 或更复杂的错误处理
374 | }
375 | return null;
376 | }
377 |
378 | public synchronized int getTableDataCount() {
379 | // 查询表格行数的SQL语句
380 | String sql = "SELECT COUNT(*) AS rowcount FROM table_data";
381 |
382 | try (Connection conn = getConnection();
383 | PreparedStatement checkStmt = conn.prepareStatement(sql)) {
384 | // 如果查询结果存在,返回第一行的计数
385 | ResultSet rs = checkStmt.executeQuery();
386 | if (rs.next()) {
387 | int count = rs.getInt("rowcount");
388 | return count;
389 | }
390 | } catch (SQLException e) {
391 | BurpExtender.getStderr().println("[!] Error getting row count from table_data");
392 | e.printStackTrace(BurpExtender.getStderr());
393 | }
394 | // 如果查询失败,返回0或者适当的错误代码
395 | return 0;
396 | }
397 |
398 | public synchronized void clearRequestsResponseTable() {
399 | String sql = "DELETE FROM requests_response"; // 用 DELETE 语句来清空表
400 |
401 | try (Connection conn = getConnection();
402 | PreparedStatement pstmt = conn.prepareStatement(sql)) {
403 |
404 | pstmt.executeUpdate();
405 | BurpExtender.getStdout().println("[-] requests_response table has been cleared.");
406 | } catch (Exception e) {
407 | BurpExtender.getStderr().println("Error clearing requests_response table: ");
408 | e.printStackTrace(BurpExtender.getStderr());
409 | }
410 | }
411 |
412 | public synchronized void clearTableDataTable() {
413 | String sql = "DELETE FROM table_data"; // 用 DELETE 语句来清空表
414 |
415 | try (Connection conn = getConnection();
416 | PreparedStatement pstmt = conn.prepareStatement(sql)) {
417 |
418 | pstmt.executeUpdate();
419 | BurpExtender.getStdout().println("[-] table_data table has been cleared.");
420 | } catch (Exception e) {
421 | BurpExtender.getStderr().println("Error clearing table_data table: ");
422 | e.printStackTrace(BurpExtender.getStderr());
423 | }
424 | }
425 |
426 | public synchronized void clearWeakPasswordTable() {
427 | String sql = "DELETE FROM weak_password"; // 用 DELETE 语句来清空表
428 |
429 | try (Connection conn = getConnection();
430 | PreparedStatement pstmt = conn.prepareStatement(sql)) {
431 |
432 | pstmt.executeUpdate();
433 | BurpExtender.getStdout().println("[-] weak_password table has been cleared.");
434 | } catch (Exception e) {
435 | BurpExtender.getStderr().println("Error clearing weak_password table: ");
436 | e.printStackTrace(BurpExtender.getStderr());
437 | }
438 | }
439 |
440 |
441 | // 关闭数据库连接的方法
442 | public void closeConnection() {
443 | try {
444 | if (this.connection != null && !this.connection.isClosed()) {
445 | this.connection.close();
446 | }
447 | } catch (SQLException ex) {
448 | BurpExtender.getStderr().println("关闭数据库连接时发生错误: ");
449 | ex.printStackTrace(BurpExtender.getStderr());
450 | }
451 | }
452 |
453 | public synchronized int insertOrUpdateRequestResponse(String url, byte[] request, byte[] response) {
454 | int generatedId = -1; // 默认ID值,如果没有生成ID,则保持此值
455 | String checkSql = "SELECT id FROM requests_response WHERE url = ?";
456 |
457 | try (Connection conn = getConnection();
458 | PreparedStatement checkStmt = conn.prepareStatement(checkSql)) {
459 | // 检查记录是否存在
460 | checkStmt.setString(1, url);
461 | ResultSet rs = checkStmt.executeQuery();
462 | if (rs.next()) {
463 | // 记录存在,更新记录
464 | generatedId = rs.getInt("id");
465 | String updateSql = "UPDATE requests_response SET request = ?, response = ? WHERE id = ?";
466 | try (PreparedStatement updateStmt = conn.prepareStatement(updateSql)) {
467 | updateStmt.setBytes(1, request);
468 | updateStmt.setBytes(2, response);
469 | updateStmt.setInt(3, generatedId);
470 | updateStmt.executeUpdate();
471 | }
472 | } else {
473 | // 记录不存在,插入新记录
474 | String insertSql = "INSERT INTO requests_response(url, request, response) VALUES(?, ?, ?)";
475 | try (PreparedStatement insertStmt = conn.prepareStatement(insertSql, Statement.RETURN_GENERATED_KEYS)) {
476 | insertStmt.setString(1, url);
477 | insertStmt.setBytes(2, request);
478 | insertStmt.setBytes(3, response);
479 | insertStmt.executeUpdate();
480 |
481 | // 获取生成的键值
482 | try (ResultSet generatedKeys = insertStmt.getGeneratedKeys()) {
483 | if (generatedKeys.next()) {
484 | generatedId = generatedKeys.getInt(1); // 获取生成的ID
485 | }
486 | }
487 | }
488 | }
489 | } catch (Exception e) {
490 | BurpExtender.getStderr().println("[-] Error inserting or updating requests_response table: ");
491 | e.printStackTrace(BurpExtender.getStderr());
492 | }
493 |
494 | return generatedId; // 返回ID值,无论是更新还是插入
495 | }
496 |
497 | public synchronized Map selectRequestResponseById(int id) {
498 | String sql = "SELECT * FROM requests_response WHERE id = ?";
499 | Map requestResponse = null;
500 |
501 | try (Connection conn = getConnection();
502 | PreparedStatement pstmt = conn.prepareStatement(sql)) {
503 |
504 | pstmt.setInt(1, id);
505 | try (ResultSet rs = pstmt.executeQuery()) {
506 | if (rs.next()) {
507 | requestResponse = new HashMap<>();
508 | requestResponse.put("request", rs.getBytes("request"));
509 | requestResponse.put("response", rs.getBytes("response"));
510 | }
511 | }
512 | } catch (Exception e) {
513 | BurpExtender.getStderr().println("[-] Error selecting from requests_response table by ID: " + id);
514 | e.printStackTrace(BurpExtender.getStderr());
515 | }
516 | return requestResponse;
517 | }
518 |
519 | public synchronized void insertWeakPassword(String url, String finger, String weakPassword, String testNumber, String resultInfo, String status, String time) {
520 | // SQL 语句来插入新数据
521 | String sql = "INSERT INTO weak_password(url, finger, weak_password, test_number, result_info, status, time) VALUES(?,?,?,?,?,?,?)";
522 |
523 | try (Connection conn = getConnection();
524 | PreparedStatement pstmt = conn.prepareStatement(sql)) {
525 | pstmt.setString(1, url);
526 | pstmt.setString(2, finger);
527 | pstmt.setString(3, weakPassword);
528 | pstmt.setString(4, testNumber);
529 | pstmt.setString(5, resultInfo);
530 | pstmt.setString(6, status);
531 | pstmt.setString(7, time);
532 | pstmt.executeUpdate();
533 | } catch (SQLException e) {
534 | BurpExtender.getStderr().println("[-] Error insertWeakPassword: " + url);
535 | e.printStackTrace(BurpExtender.getStderr());
536 | }
537 | }
538 |
539 | public synchronized void deleteWeakPassword(String url) {
540 | // SQL 语句来删除数据
541 | String sql = "DELETE FROM weak_password WHERE url = ?";
542 |
543 | try (Connection conn = getConnection();
544 | PreparedStatement pstmt = conn.prepareStatement(sql)) {
545 | pstmt.setString(1, url);
546 | pstmt.executeUpdate();
547 | } catch (SQLException e) {
548 | System.out.println(e.getMessage());
549 | }
550 | }
551 |
552 | public synchronized void updateWeakPassword(WeakPassword wp) {
553 | // SQL 语句来更新数据
554 | String sql = "UPDATE weak_password SET finger = ?, weak_password = ?, test_number = ?, result_info = ?, status = ?, time = ?, request = ?, response = ? WHERE url = ?";
555 |
556 | try (Connection conn = getConnection();
557 | PreparedStatement pstmt = conn.prepareStatement(sql)) {
558 | pstmt.setString(1, wp.getFinger());
559 | pstmt.setString(2, wp.getWeakPassword());
560 | pstmt.setString(3, wp.getTestNumber());
561 | pstmt.setString(4, wp.getResultInfo());
562 | pstmt.setString(5, wp.getStatus());
563 | pstmt.setString(6, wp.getTime());
564 | pstmt.setBytes(7, wp.getRequestsByte());
565 | pstmt.setBytes(8, wp.getResponseByte());
566 | pstmt.setString(9, wp.getUrl());
567 | pstmt.executeUpdate();
568 | } catch (SQLException e) {
569 | BurpExtender.getStderr().println("[-] Error updateWeakPassword: " + wp);
570 | e.printStackTrace(BurpExtender.getStderr());
571 | }
572 | }
573 |
574 | public synchronized void updateWeakPasswordStatus(String url, String status) {
575 | // SQL 语句来更新数据
576 | String sql = "UPDATE weak_password SET status = ? WHERE url = ?";
577 |
578 | try (Connection conn = getConnection();
579 | PreparedStatement pstmt = conn.prepareStatement(sql)) {
580 | pstmt.setString(1, "爆破中");
581 | pstmt.setString(2, url);
582 | pstmt.executeUpdate();
583 | } catch (SQLException e) {
584 | System.out.println(e.getMessage());
585 | }
586 | }
587 |
588 | public synchronized List getAllWeakPassword() {
589 | List resultList = new ArrayList<>();
590 | String sql = "SELECT id, url, finger, weak_password, test_number, result_info, status, time FROM weak_password";
591 |
592 | try (Connection conn = getConnection();
593 | Statement stmt = conn.createStatement();
594 | ResultSet rs = stmt.executeQuery(sql)) {
595 | while (rs.next()) {
596 | WeakPassword wp = new WeakPassword();
597 | wp.setId(rs.getInt("id"));
598 | wp.setUrl(rs.getString("url"));
599 | wp.setFinger(rs.getString("finger"));
600 | wp.setWeakPassword(rs.getString("weak_password"));
601 | wp.setTestNumber(rs.getString("test_number"));
602 | wp.setResultInfo(rs.getString("result_info"));
603 | wp.setStatus(rs.getString("status"));
604 | wp.setTime(rs.getString("time"));
605 | resultList.add(wp);
606 | }
607 | } catch (SQLException e) {
608 | BurpExtender.getStderr().println("[-] Error getAllWeakPassword: ");
609 | e.printStackTrace(BurpExtender.getStderr());
610 | }
611 |
612 | return resultList;
613 | }
614 |
615 | public synchronized WeakPassword getWeakPasswordByUrl(String url) {
616 | WeakPassword wp = new WeakPassword();
617 | String sql = "SELECT id, url, finger, weak_password, test_number, result_info, status, time, request, response FROM weak_password where url = ?";
618 |
619 | try (Connection conn = getConnection();
620 | PreparedStatement pstmt = conn.prepareStatement(sql)) {
621 | pstmt.setString(1, url);
622 | try (ResultSet rs = pstmt.executeQuery()) {
623 | if (rs.next()) {
624 | wp.setId(rs.getInt("id"));
625 | wp.setUrl(rs.getString("url"));
626 | wp.setFinger(rs.getString("finger"));
627 | wp.setWeakPassword(rs.getString("weak_password"));
628 | wp.setTestNumber(rs.getString("test_number"));
629 | wp.setResultInfo(rs.getString("result_info"));
630 | wp.setStatus(rs.getString("status"));
631 | wp.setTime(rs.getString("time"));
632 | wp.setRequestsByte(rs.getBytes("request"));
633 | wp.setResponseByte(rs.getBytes("response"));
634 | }
635 | }
636 |
637 | } catch (SQLException e) {
638 | BurpExtender.getStderr().println("[-] Error getAllWeakPassword: ");
639 | e.printStackTrace(BurpExtender.getStderr());
640 | }
641 | return wp;
642 | }
643 |
644 | public synchronized boolean existsWeakPasswordByUrl(String url) {
645 | String sql = "SELECT COUNT(*) FROM weak_password WHERE url = ?";
646 |
647 | try (Connection conn = getConnection();
648 | PreparedStatement pstmt = conn.prepareStatement(sql)) {
649 | pstmt.setString(1, url); // 设置查询参数
650 | try (ResultSet rs = pstmt.executeQuery()) {
651 | if (rs.next()) {
652 | int count = rs.getInt(1); // 获取匹配行的数量
653 | return count > 0; // 如果数量大于0,表示存在数据
654 | }
655 | }
656 | } catch (SQLException e) {
657 | System.out.println(e.getMessage());
658 | }
659 |
660 | return false;
661 | }
662 |
663 |
664 | public synchronized WeakPassword fetchAndMarkSinglePathAsCrawling() throws SQLException {
665 | // 事务开启
666 | WeakPassword wp = null;
667 | // 首先选取一条记录的ID
668 | String selectSQL = "SELECT * FROM weak_password WHERE status = '等待爆破中' LIMIT 1;";
669 | String updateSQL = "UPDATE weak_password SET status = '爆破中' WHERE id = ?;";
670 |
671 | try (PreparedStatement selectStatement = connection.prepareStatement(selectSQL)) {
672 | ResultSet rs = selectStatement.executeQuery();
673 | if (rs.next()) {
674 | int selectedId = rs.getInt("id");
675 | String url = rs.getString("url");
676 | wp = new WeakPassword(rs.getInt("id"), rs.getString("url"), rs.getString("finger"), rs.getString("weak_password"), rs.getString("test_number"), rs.getString("result_info"), "爆破中", new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()));
677 | try (PreparedStatement updateStatement = connection.prepareStatement(updateSQL)) {
678 | updateStatement.setInt(1, selectedId);
679 | int affectedRows = updateStatement.executeUpdate();
680 | if (affectedRows <= 0) {
681 | BurpExtender.getStderr().println("[!] fetchAndMarkSinglePathAsCrawling error: " + url);
682 | }
683 | }
684 | }
685 | }
686 |
687 | return wp;
688 | }
689 |
690 | public synchronized int getWeakPasswordCount() {
691 | // 查询表格行数的SQL语句
692 | String sql = "SELECT COUNT(*) AS rowcount FROM weak_password";
693 |
694 | try (Connection conn = getConnection();
695 | PreparedStatement checkStmt = conn.prepareStatement(sql)) {
696 | // 如果查询结果存在,返回第一行的计数
697 | ResultSet rs = checkStmt.executeQuery();
698 | if (rs.next()) {
699 | int count = rs.getInt("rowcount");
700 | return count;
701 | }
702 | } catch (SQLException e) {
703 | BurpExtender.getStderr().println("[!] Error getting row count from table_data");
704 | e.printStackTrace(BurpExtender.getStderr());
705 | }
706 | // 如果查询失败,返回0或者适当的错误代码
707 | return 0;
708 | }
709 |
710 | public synchronized int getWeakPasswordSuccessCount() {
711 | // 查询表格行数的SQL语句
712 | String sql = "SELECT COUNT(*) AS rowcount FROM weak_password where status = \"爆破成功\" ";
713 |
714 | try (Connection conn = getConnection();
715 | PreparedStatement checkStmt = conn.prepareStatement(sql)) {
716 | // 如果查询结果存在,返回第一行的计数
717 | ResultSet rs = checkStmt.executeQuery();
718 | if (rs.next()) {
719 | int count = rs.getInt("rowcount");
720 | return count;
721 | }
722 | } catch (SQLException e) {
723 | BurpExtender.getStderr().println("[!] Error getting row count from table_data");
724 | e.printStackTrace(BurpExtender.getStderr());
725 | }
726 | // 如果查询失败,返回0或者适当的错误代码
727 | return 0;
728 | }
729 |
730 |
731 | }
732 |
--------------------------------------------------------------------------------
/src/main/java/burp/model/FingerPrintRule.java:
--------------------------------------------------------------------------------
1 | package burp.model;
2 |
3 | import java.util.List;
4 | /**
5 | * @author: shaun
6 | * @create: 2024/3/2 17:49
7 | * @description:TODO
8 | */
9 | public class FingerPrintRule {
10 | private String cms;
11 | private String method;
12 | private String location;
13 | private List keyword;
14 | private boolean isImportant;
15 | private String type;
16 | // 新添加的构造函数
17 | public FingerPrintRule(String type, boolean isImportant, String cms, String method, String location, List keyword) {
18 | this.cms = cms;
19 | this.method = method;
20 | this.location = location;
21 | this.keyword = keyword;
22 | this.type = type;
23 | this.isImportant = isImportant;
24 | }
25 | public String getType(){return type;}
26 | public void setType(String type){this.cms = type;}
27 | public boolean getIsImportant(){return isImportant;}
28 | public void setIsImportant(boolean isImportant){this.isImportant = isImportant;}
29 | public String getCms() {
30 | return cms;
31 | }
32 |
33 | public void setCms(String cms) {
34 | this.cms = cms;
35 | }
36 |
37 | public String getMethod() {
38 | return method;
39 | }
40 |
41 | public void setMethod(String method) {
42 | this.method = method;
43 | }
44 |
45 | public String getLocation() {
46 | return location;
47 | }
48 |
49 | public void setLocation(String location) {
50 | this.location = location;
51 | }
52 |
53 | public List getKeyword() {
54 | return keyword;
55 | }
56 |
57 | public void setKeyword(List keyword) {
58 | this.keyword = keyword;
59 | }
60 |
61 | public String getInfo(){
62 | return "cms: " + cms + "\r\nmethod: " + method + "\r\nlocation: " + location + "\r\nkeyword: " + keyword.toString();
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/burp/model/TableLogModel.java:
--------------------------------------------------------------------------------
1 | package burp.model;
2 |
3 | import burp.IHttpService;
4 |
5 | public class TableLogModel {
6 | final int pid;
7 | int requestResponseIndex;
8 | Boolean isImportant;
9 | String url;
10 | String method;
11 | String title;
12 | String status;
13 | String result;
14 | String type;
15 | String resultInfo;
16 | String time;
17 | IHttpService iHttpService;
18 |
19 | public TableLogModel(int pid, String url, String method, String title, String status, String result, String type, Boolean isImportant, String resultInfo, IHttpService iHttpService, int requestResponseIndex, String time) {
20 | this.pid = pid;
21 | this.url = url;
22 | this.title = title;
23 | this.status = status;
24 | this.result = result;
25 | this.type = type;
26 | this.isImportant = isImportant;
27 | this.method = method;
28 | this.resultInfo = resultInfo;
29 | this.iHttpService = iHttpService;
30 | this.requestResponseIndex = requestResponseIndex;
31 | this.time = time;
32 | }
33 |
34 | public int getPid() {
35 | return pid;
36 | }
37 |
38 | public int getRequestResponseIndex() {
39 | return requestResponseIndex;
40 | }
41 |
42 | public void setRequestResponseIndex(int requestResponseIndex) {
43 | this.requestResponseIndex = requestResponseIndex;
44 | }
45 |
46 | public Boolean getIsImportant() {
47 | return isImportant;
48 | }
49 |
50 | public void setIsImportant(Boolean isImportant) {
51 | this.isImportant = isImportant;
52 | }
53 |
54 | public String getUrl() {
55 | return url;
56 | }
57 |
58 | public void setUrl(String url) {
59 | this.url = url;
60 | }
61 |
62 | public String getMethod() {
63 | return method;
64 | }
65 |
66 | public void setMethod(String method) {
67 | this.method = method;
68 | }
69 |
70 | public String getTitle() {
71 | return title;
72 | }
73 |
74 | public void setTitle(String title) {
75 | this.title = title;
76 | }
77 |
78 | public String getStatus() {
79 | return status;
80 | }
81 |
82 | public void setStatus(String status) {
83 | this.status = status;
84 | }
85 |
86 | public String getResult() {
87 | return result;
88 | }
89 |
90 | public void setResult(String result) {
91 | this.result = result;
92 | }
93 |
94 | public String getType() {
95 | return type;
96 | }
97 |
98 | public void setType(String type) {
99 | this.type = type;
100 | }
101 |
102 | public String getResultInfo() {
103 | return resultInfo;
104 | }
105 |
106 | public void setResultInfo(String resultInfo) {
107 | this.resultInfo = resultInfo;
108 | }
109 |
110 | public String getTime() {
111 | return time;
112 | }
113 |
114 | public void setTime(String time) {
115 | this.time = time;
116 | }
117 |
118 | public IHttpService getIHttpService() {
119 | return iHttpService;
120 | }
121 |
122 | public void setIHttpService(IHttpService iHttpService) {
123 | this.iHttpService = iHttpService;
124 | }
125 |
126 | public String getHost(){
127 | return this.iHttpService.getHost();
128 | }
129 |
130 | public int getPort(){
131 | return this.iHttpService.getPort();
132 | }
133 |
134 | public String getProtocol(){
135 | return this.iHttpService.getProtocol();
136 | }
137 |
138 |
139 | }
140 |
--------------------------------------------------------------------------------
/src/main/java/burp/model/WeakPassword.java:
--------------------------------------------------------------------------------
1 | package burp.model;
2 |
3 | /**
4 | * Represents a weak password record.
5 | */
6 | public class WeakPassword {
7 | private int id;
8 | private String url;
9 | private String finger;
10 | private String weakPassword;
11 | private String testNumber;
12 | private String resultInfo;
13 | private String status;
14 | private String time;
15 | private byte[] requestsByte = new byte[0];
16 | private byte[] responseByte = new byte[0];
17 |
18 | // Constructor with all fields
19 | public WeakPassword(int id, String url, String finger, String weakPassword,
20 | String testNumber, String resultInfo, String status, String time) {
21 | this.id = id;
22 | this.url = url;
23 | this.finger = finger;
24 | this.weakPassword = weakPassword;
25 | this.testNumber = testNumber;
26 | this.resultInfo = resultInfo;
27 | this.status = status;
28 | this.time = time;
29 | }
30 |
31 | public WeakPassword(){
32 |
33 | }
34 |
35 | public byte[] getRequestsByte() {
36 | return requestsByte;
37 | }
38 |
39 | public byte[] getResponseByte() {
40 | return responseByte;
41 | }
42 |
43 | public void setRequestsByte(byte[] requestsByte) {
44 | this.requestsByte = requestsByte;
45 | }
46 |
47 | public void setResponseByte(byte[] responseByte) {
48 | this.responseByte = responseByte;
49 | }
50 |
51 | // Getters and setters for all fields
52 | public int getId() {
53 | return id;
54 | }
55 |
56 | public void setId(int id) {
57 | this.id = id;
58 | }
59 |
60 | public String getUrl() {
61 | return url;
62 | }
63 |
64 | public void setUrl(String url) {
65 | this.url = url;
66 | }
67 |
68 | public String getFinger() {
69 | return finger;
70 | }
71 |
72 | public void setFinger(String finger) {
73 | this.finger = finger;
74 | }
75 |
76 | public String getWeakPassword() {
77 | return weakPassword;
78 | }
79 |
80 | public void setWeakPassword(String weakPassword) {
81 | this.weakPassword = weakPassword;
82 | }
83 |
84 | public String getTestNumber() {
85 | return testNumber;
86 | }
87 |
88 | public void setTestNumber(String testNumber) {
89 | this.testNumber = testNumber;
90 | }
91 |
92 | public String getResultInfo() {
93 | return resultInfo;
94 | }
95 |
96 | public void setResultInfo(String resultInfo) {
97 | this.resultInfo = resultInfo;
98 | }
99 |
100 | public String getStatus() {
101 | return status;
102 | }
103 |
104 | public void setStatus(String status) {
105 | this.status = status;
106 | }
107 |
108 | public String getTime() {
109 | return time;
110 | }
111 |
112 | public void setTime(String time) {
113 | this.time = time;
114 | }
115 |
116 | // Overriding the toString() method for easy printing
117 | @Override
118 | public String toString() {
119 | return "WeakPassword{" +
120 | "id=" + id +
121 | ", url='" + url + '\'' +
122 | ", finger='" + finger + '\'' +
123 | ", weakPassword='" + weakPassword + '\'' +
124 | ", testNumber='" + testNumber + '\'' +
125 | ", resultInfo='" + resultInfo + '\'' +
126 | ", status='" + status + '\'' +
127 | ", time='" + time + '\'' +
128 | '}';
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/main/java/burp/ui/FingerTab.java:
--------------------------------------------------------------------------------
1 | package burp.ui;
2 |
3 | import burp.*;
4 |
5 | import javax.swing.*;
6 | import javax.swing.border.EmptyBorder;
7 | import java.awt.*;
8 | import java.awt.Color;
9 | import java.awt.event.ActionEvent;
10 | import java.awt.event.ActionListener;
11 | import java.time.Duration;
12 | import java.time.LocalDateTime;
13 | import java.util.HashMap;
14 | import java.awt.event.MouseAdapter;
15 | import java.awt.event.MouseEvent;
16 | import javax.swing.table.*;
17 | import java.awt.Component;
18 | import java.util.Map;
19 |
20 | import burp.model.TableLogModel;
21 | import burp.ui.event.FingerTabEventHandlers;
22 | import burp.ui.renderer.HavingImportantRenderer;
23 | import burp.ui.renderer.HeaderIconRenderer;
24 | import burp.util.UiUtils;
25 | import javax.swing.event.ListSelectionListener;
26 | import javax.swing.event.ListSelectionEvent;
27 |
28 |
29 | public class FingerTab implements IMessageEditorController {
30 | public static JPanel contentPane;
31 | private JSplitPane splitPane;
32 | public static IHttpRequestResponse currentlyDisplayedItem;
33 | public static JLabel lbRequestCount;
34 | public static JLabel lbSuccessCount;
35 |
36 | public static IMessageEditor requestViewer;
37 | public static IMessageEditor responseViewer;
38 | public static ITextEditor resultDeViewer;
39 |
40 | public static HashMap resultMap = new HashMap<>();
41 | public static JPanel tagsPanel;
42 |
43 | // 在FingerTab类中添加成员变量
44 | public static JToggleButton toggleButton;
45 | public static JToggleButton weakPasswordBlasting;
46 | private static DefaultTableModel model;
47 | public static JTable table;
48 | public static JToggleButton flashButton;
49 | public static JComboBox choicesComboBox;
50 | public static JLabel flashText;
51 | public static Timer timer;
52 | public static LocalDateTime operationStartTime = LocalDateTime.now();
53 | public static String historyChoiceType = "全部";
54 | public static String historyChoiceJMenuItem = "全部";
55 | public static JLabel currentSelectedLabel = null;
56 |
57 | public FingerTab() {
58 | contentPane = new JPanel();
59 | contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
60 | contentPane.setLayout(new BorderLayout(0, 0));
61 |
62 | JPanel topPanel = new JPanel();
63 | GridBagLayout gridBagLayout = new GridBagLayout();
64 | // 列数,行数
65 | gridBagLayout.columnWidths = new int[] { 0, 0};
66 | gridBagLayout.rowHeights = new int[] {5};
67 | // 各列占宽度比,各行占高度比
68 | gridBagLayout.columnWeights = new double[] { 1.0D, Double.MIN_VALUE };
69 | topPanel.setLayout(gridBagLayout);
70 |
71 | JPanel FilterPanel = new JPanel();
72 | GridBagConstraints gbc_panel_1 = new GridBagConstraints();
73 | gbc_panel_1.insets = new Insets(0, 5, 5, 5);
74 | gbc_panel_1.fill = 2;
75 | gbc_panel_1.gridx = 0;
76 | gbc_panel_1.gridy = 2;
77 | topPanel.add(FilterPanel, gbc_panel_1);
78 | GridBagLayout gbl_panel_1 = new GridBagLayout();
79 | gbl_panel_1.columnWidths = new int[] { 0, 0, 0, 0, 0 };
80 | gbl_panel_1.rowHeights = new int[] { 0, 0 };
81 | gbl_panel_1.columnWeights = new double[] { 0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D, Double.MIN_VALUE};
82 | gbl_panel_1.rowWeights = new double[] { 0.0D, Double.MIN_VALUE };
83 | FilterPanel.setLayout(gbl_panel_1);
84 |
85 |
86 |
87 | // 转发url总数,默认0
88 | JLabel lbRequest = new JLabel("Requests Total:");
89 | GridBagConstraints gbc_lbRequest = new GridBagConstraints();
90 | gbc_lbRequest.insets = new Insets(0, 0, 0, 5);
91 | gbc_lbRequest.fill = GridBagConstraints.HORIZONTAL;
92 | gbc_lbRequest.weightx = 0.0;
93 | gbc_lbRequest.gridx = 0;
94 | gbc_lbRequest.gridy = 0;
95 | FilterPanel.add(lbRequest, gbc_lbRequest);
96 |
97 | lbRequestCount = new JLabel("0");
98 | lbRequestCount.setForeground(new Color(0,0,255));
99 | GridBagConstraints gbc_lbRequestCount = new GridBagConstraints();
100 | gbc_lbRequestCount.insets = new Insets(0, 0, 0, 5);
101 | gbc_lbRequest.fill = GridBagConstraints.HORIZONTAL;
102 | gbc_lbRequest.weightx = 0.0;
103 | gbc_lbRequestCount.gridx = 1;
104 | gbc_lbRequestCount.gridy = 0;
105 | FilterPanel.add(lbRequestCount, gbc_lbRequestCount);
106 |
107 | // 转发成功url数,默认0
108 | JLabel lbSucces = new JLabel("Finger Success:");
109 | GridBagConstraints gbc_lbSucces = new GridBagConstraints();
110 | gbc_lbSucces.insets = new Insets(0, 0, 0, 5);
111 | gbc_lbSucces.fill = 0;
112 | gbc_lbSucces.gridx = 2;
113 | gbc_lbSucces.gridy = 0;
114 | FilterPanel.add(lbSucces, gbc_lbSucces);
115 |
116 | lbSuccessCount = new JLabel("0");
117 | lbSuccessCount.setForeground(new Color(0, 255, 0));
118 | GridBagConstraints gbc_lbSuccessCount = new GridBagConstraints();
119 | gbc_lbSuccessCount.insets = new Insets(0, 0, 0, 5);
120 | gbc_lbSuccessCount.fill = 0;
121 | gbc_lbSuccessCount.gridx = 3;
122 | gbc_lbSuccessCount.gridy = 0;
123 | FilterPanel.add(lbSuccessCount, gbc_lbSuccessCount);
124 |
125 | toggleButton = new JToggleButton(UiUtils.getImageIcon("/icon/openButtonIcon.png", 40, 24));
126 | toggleButton.setSelectedIcon(UiUtils.getImageIcon("/icon/shutdownButtonIcon.png", 40, 24));
127 | toggleButton.setPreferredSize(new Dimension(50, 24));
128 | toggleButton.setBorder(null); // 设置无边框
129 | toggleButton.setFocusPainted(false); // 移除焦点边框
130 | toggleButton.setContentAreaFilled(false); // 移除选中状态下的背景填充
131 | toggleButton.setToolTipText("是否开启对页面提取URL后发起HTTP请求后进行指纹识别");
132 |
133 | // 添加填充以在左侧占位
134 | GridBagConstraints gbc_leftFiller = new GridBagConstraints();
135 | gbc_leftFiller.weightx = 1; // 使得这个组件吸收额外的水平空间
136 | gbc_leftFiller.gridx = 5; // 位置设置为第一个单元格
137 | gbc_leftFiller.gridy = 0; // 第一行
138 | gbc_leftFiller.fill = GridBagConstraints.HORIZONTAL; // 水平填充
139 | FilterPanel.add(Box.createHorizontalGlue(), gbc_leftFiller);
140 |
141 | // 设置按钮的 GridBagConstraints
142 | GridBagConstraints gbc_buttons = new GridBagConstraints();
143 | gbc_buttons.insets = new Insets(0, 5, 0, 5);
144 | gbc_buttons.gridy = 0; // 设置按钮的纵坐标位置
145 | gbc_buttons.fill = GridBagConstraints.NONE; // 不填充
146 | gbc_buttons.gridx = 7; // 将横坐标位置移动到下一个单元格
147 | FilterPanel.add(toggleButton, gbc_buttons);
148 |
149 | // 刷新按钮按钮
150 | flashButton = new JToggleButton(UiUtils.getImageIcon("/icon/runningButton.png", 24, 24));
151 | flashButton.setSelectedIcon(UiUtils.getImageIcon("/icon/flashButton.png", 24, 24));
152 | flashButton.setPreferredSize(new Dimension(30, 30));
153 | flashButton.setBorder(null); // 设置无边框
154 | flashButton.setFocusPainted(false); // 移除焦点边框
155 | flashButton.setContentAreaFilled(false); // 移除选中状态下的背景填充
156 | flashButton.setToolTipText("用于控制表格是否自动化刷新,还是手工点击刷新");
157 |
158 | // 刷新按钮按钮
159 | weakPasswordBlasting = new JToggleButton(UiUtils.getImageIcon("/icon/WeakPasswordBlasting.png", 24, 24));
160 | weakPasswordBlasting.setSelectedIcon(UiUtils.getImageIcon("/icon/WeakPasswordBlastingFalse.png", 24, 24));
161 | weakPasswordBlasting.setPreferredSize(new Dimension(30, 30));
162 | weakPasswordBlasting.setBorder(null); // 设置无边框
163 | weakPasswordBlasting.setFocusPainted(false); // 移除焦点边框
164 | weakPasswordBlasting.setContentAreaFilled(false); // 移除选中状态下的背景填充
165 | weakPasswordBlasting.setToolTipText("弱口令爆破开启(功能开发中,请进群敬请期待)");
166 |
167 | // 刷新按钮
168 | flashButton.addActionListener(new ActionListener() {
169 | @Override
170 | public void actionPerformed(ActionEvent e) {
171 | // 检查按钮的选中状态
172 | if (flashButton.isSelected()) {
173 | // 如果按钮被选中,意味着刷新功能被激活,我们将文本设置为 "暂停刷新中"
174 | flashText.setText("暂停每5秒刷新表格");
175 | } else {
176 | // 如果按钮没有被选中,意味着刷新功能没有被激活,我们将文本设置为 "自动刷新"
177 | flashText.setText("自动每5秒刷新表格中");
178 | }
179 | }
180 | });
181 |
182 | // 刷新文本
183 | flashText = new JLabel("自动每5秒刷新表格中");
184 |
185 | gbc_buttons.gridx = 8; // 将横坐标位置移动到下一个单元格
186 | FilterPanel.add(weakPasswordBlasting, gbc_buttons);
187 | gbc_buttons.gridx = 9; // 将横坐标位置移动到下一个单元格
188 | FilterPanel.add(flashButton, gbc_buttons);
189 | gbc_buttons.gridx = 10; // 将横坐标位置移动到下一个单元格
190 | FilterPanel.add(flashText, gbc_buttons);
191 |
192 | // 添加填充以在右侧占位
193 | GridBagConstraints gbc_rightFiller = new GridBagConstraints();
194 | gbc_rightFiller.weightx = 1; // 使得这个组件吸收额外的水平空间
195 | gbc_rightFiller.gridx = 12; // 位置设置为最后一个单元格
196 | gbc_rightFiller.gridy = 0; // 第一行
197 | gbc_rightFiller.fill = GridBagConstraints.HORIZONTAL; // 水平填充
198 | FilterPanel.add(Box.createHorizontalGlue(), gbc_rightFiller);
199 |
200 |
201 | weakPasswordBlasting.addActionListener(new ActionListener() {
202 | @Override
203 | public void actionPerformed(ActionEvent e) {
204 | // 例如,更新FingerConfigTab中的按钮状态
205 | BurpExtender.getTags().weakPasswordTab.weakPasswordBlasting.setSelected(weakPasswordBlasting.isSelected());
206 | }
207 | });
208 |
209 | // 添加一个 "清除" 按钮
210 | JButton btnClear = new JButton("清空");
211 | GridBagConstraints gbc_btnClear = new GridBagConstraints();
212 | gbc_btnClear.insets = new Insets(0, 0, 0, 5);
213 | gbc_btnClear.fill = 0;
214 | gbc_btnClear.gridx = 13; // 根据该值来确定是确定从左到右的顺序
215 | gbc_btnClear.gridy = 0;
216 | FilterPanel.add(btnClear, gbc_btnClear);
217 |
218 | // 功能按钮
219 | JPopupMenu moreMenu = new JPopupMenu("功能");
220 | JMenuItem exportItem = new JMenuItem("导出");
221 | moreMenu.add(exportItem);
222 | exportItem.setIcon(UiUtils.getImageIcon("/icon/exportItem.png", 17, 17));
223 | moreMenu.add(exportItem);
224 | JButton moreButton = new JButton();
225 | moreButton.setIcon(UiUtils.getImageIcon("/icon/moreButton.png", 17, 17));
226 | GridBagConstraints gbc_btnMore = new GridBagConstraints();
227 | gbc_btnClear.insets = new Insets(0, 0, 0, 5);
228 | gbc_btnClear.fill = 0;
229 | gbc_btnClear.gridx = 14; // 根据该值来确定是确定从左到右的顺序
230 | gbc_btnClear.gridy = 0;
231 | FilterPanel.add(moreButton, gbc_btnMore);
232 |
233 | exportItem.addActionListener(FingerTabEventHandlers.exportItemAddActionListener());
234 |
235 | // 点击”功能“的监听事件
236 | moreButton.addMouseListener(new MouseAdapter() {
237 | public void mouseClicked(MouseEvent e) {
238 | moreMenu.show(e.getComponent(), e.getX(), e.getY());
239 | }
240 | });
241 |
242 | contentPane.add(topPanel,BorderLayout.NORTH);
243 |
244 | tagsPanel = new JPanel();
245 | tagsPanel.setLayout(new WrapLayout(FlowLayout.LEFT)); // 使用 WrapLayout 并设置为左对齐
246 | GridBagConstraints gbc_tagsPanel = new GridBagConstraints();
247 | gbc_tagsPanel.insets = new Insets(0, 0, 5, 0);
248 | gbc_tagsPanel.fill = GridBagConstraints.HORIZONTAL;
249 | gbc_tagsPanel.gridx = 0;
250 | gbc_tagsPanel.gridy = 1; // 新的行
251 | topPanel.add(tagsPanel, gbc_tagsPanel);
252 |
253 | JLabel allLabel = new JLabel("全部");
254 | allLabel.setOpaque(true); // 设置为不透明
255 | allLabel.setBackground(new Color(150, 150, 150)); // 设置背景颜色为浅灰色
256 | allLabel.setForeground(Color.BLACK); // 设置字体颜色为黑色
257 | FingerTab.currentSelectedLabel = allLabel;
258 | // 为标签添加一个有颜色的边框,边框内有5像素的填充
259 | allLabel.setBorder(BorderFactory.createCompoundBorder(
260 | BorderFactory.createLineBorder(new Color(100, 100, 100), 1), // 外部边框,颜色为深灰色,宽度为2像素
261 | BorderFactory.createEmptyBorder(5, 5, 5, 5) // 内部填充,宽度为5像素
262 | ));
263 | allLabel.addMouseListener(new MouseAdapter() {
264 | @Override
265 | public void mouseClicked(MouseEvent e) {
266 | // 取消之前选中标签的颜色
267 | if (FingerTab.currentSelectedLabel != null) {
268 | FingerTab.currentSelectedLabel.setBackground(new Color(200, 200, 200)); // 还原默认背景色
269 | }
270 | setFlashButtonTrue();
271 | // 设置当前点击的标签为选中状态
272 | allLabel.setBackground(new Color(150, 150, 150)); // 选中状态的背景色
273 | FingerTab.currentSelectedLabel = allLabel; // 更新当前选中的标签
274 | // 当用户点击 "全部",展示所有的数据
275 | historyChoiceType = "全部";
276 | historyChoiceJMenuItem = "全部";
277 | filterTable("全部", "全部", null);
278 | }
279 | });
280 | tagsPanel.add(allLabel);
281 |
282 | contentPane.add(topPanel,BorderLayout.NORTH); // 只在 contentPane 的北部添加一个组件
283 |
284 | splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
285 | splitPane.setDividerLocation(0.5);
286 | contentPane.add(splitPane, BorderLayout.CENTER);
287 |
288 | // 数据展示面板
289 | model = new DefaultTableModel(new Object[]{"#", "Method", "URl", "Titled", "Status", "Result", "type", "isImportant", "Time"}, 0) {
290 | @Override
291 | public boolean isCellEditable(int row, int column) {
292 | // This will make all cells of the table non-editable
293 | return false;
294 | }
295 | };
296 | table = new JTable(model){
297 | // 重写getToolTipText方法以返回特定单元格的数据
298 | public String getToolTipText(MouseEvent e) {
299 | int row = rowAtPoint(e.getPoint());
300 | int col = columnAtPoint(e.getPoint());
301 | if (row > -1 && col > -1) {
302 | Object value = getValueAt(row, col);
303 | return value == null ? null : value.toString();
304 | }
305 | return super.getToolTipText(e);
306 | }
307 | };;
308 |
309 |
310 | // 前两列设置宽度 30px、60px
311 | table.getColumnModel().getColumn(0).setMinWidth(20);
312 | table.getColumnModel().getColumn(1).setMinWidth(20);
313 | table.getColumnModel().getColumn(2).setMinWidth(200);
314 | table.getColumnModel().getColumn(3).setMinWidth(60);
315 | table.getColumnModel().getColumn(4).setMinWidth(20);
316 | table.getColumnModel().getColumn(5).setMinWidth(180);
317 | table.getColumnModel().getColumn(6).setMinWidth(180);
318 | table.getColumnModel().getColumn(7).setMinWidth(20);
319 | table.getColumnModel().getColumn(8).setMinWidth(60);
320 |
321 | // 创建一个居中对齐的单元格渲染器
322 | DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer();
323 | centerRenderer.setHorizontalAlignment(JLabel.CENTER);
324 |
325 | DefaultTableCellRenderer leftRenderer = new DefaultTableCellRenderer();
326 | leftRenderer.setHorizontalAlignment(JLabel.LEFT);
327 |
328 | table.getColumnModel().getColumn(0).setCellRenderer(centerRenderer);
329 | table.getColumnModel().getColumn(1).setCellRenderer(centerRenderer);
330 | table.getColumnModel().getColumn(2).setCellRenderer(leftRenderer);
331 | table.getColumnModel().getColumn(3).setCellRenderer(centerRenderer);
332 | table.getColumnModel().getColumn(4).setCellRenderer(centerRenderer);
333 | table.getColumnModel().getColumn(5).setCellRenderer(centerRenderer);
334 | table.getColumnModel().getColumn(6).setCellRenderer(centerRenderer);
335 | table.getColumnModel().getColumn(7).setCellRenderer(leftRenderer);
336 | table.getColumnModel().getColumn(8).setCellRenderer(leftRenderer);
337 |
338 | HavingImportantRenderer havingImportantRenderer = new HavingImportantRenderer();
339 | table.getColumnModel().getColumn(7).setCellRenderer(havingImportantRenderer);
340 |
341 | // 在FingerConfigTab构造函数中设置表头渲染器和监听器的代码
342 | JTableHeader header = table.getTableHeader();
343 | TableColumnModel columnModel = header.getColumnModel();
344 | columnModel.getColumn(6).setHeaderRenderer(new HeaderIconRenderer());
345 | columnModel.getColumn(7).setHeaderRenderer(new HeaderIconRenderer());
346 |
347 | // 在您的FingerConfigTab构造函数中
348 | header.addMouseListener(FingerTabEventHandlers.headerAddMouseListener(table));
349 |
350 | model.addTableModelListener(FingerTabEventHandlers.modelAddTableModelListener(model, tagsPanel, resultMap, table));
351 |
352 | // 创建右键菜单
353 | JPopupMenu popupMenu = new JPopupMenu();
354 | JMenuItem clearItem = new JMenuItem("清除");
355 | popupMenu.add(clearItem);
356 | // 将右键菜单添加到表格
357 | table.setComponentPopupMenu(popupMenu);
358 |
359 | // 为菜单项添加行为
360 | clearItem.addActionListener(FingerTabEventHandlers.clearItemAddActionListener(model, table, lbSuccessCount));
361 |
362 | JScrollPane jspLogTable = new JScrollPane(table);
363 | splitPane.setTopComponent(jspLogTable);
364 |
365 | // 设置表格选择模式为单行选择
366 | table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
367 |
368 | // 添加ListSelectionListener来监听行选择事件
369 | table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
370 | public void valueChanged(ListSelectionEvent event) {
371 | if (!event.getValueIsAdjusting() && table.getSelectedRow() != -1) {
372 | // 在这里获取选中行的数据
373 | int selectedRow = table.getSelectedRow();
374 |
375 | // 根据您的数据模型结构获取数据
376 | String url = model.getValueAt(selectedRow, 2).toString();
377 | TableLogModel logModel = BurpExtender.getDataBaseService().getTableDataByUrl(url);
378 | resultDeViewer.setText(logModel.getResultInfo().getBytes());
379 | Map requestResponse = BurpExtender.getDataBaseService().selectRequestResponseById(logModel.getRequestResponseIndex());
380 |
381 | if (requestResponse != null) {
382 | // 提取请求和响应数据
383 | byte[] requestBytes = requestResponse.get("request");
384 | byte[] responseBytes = requestResponse.get("response");
385 |
386 | // 现在你可以使用这些字节数据了
387 | // 例如,你可以将它们设置到一个 HTTP 消息编辑器组件中
388 | requestViewer.setMessage(requestBytes, true); // true 表示请求消息
389 | responseViewer.setMessage(responseBytes, false); // false 表示响应消息
390 | }
391 |
392 | }
393 | }
394 | });
395 |
396 | // 添加点击事件监听器
397 | btnClear.addActionListener(FingerTabEventHandlers.btnClearAddActionListener(model, lbRequestCount, lbSuccessCount));
398 |
399 |
400 | JTabbedPane tabs = new JTabbedPane();
401 | requestViewer = BurpExtender.getCallbacks().createMessageEditor(this, false);
402 | responseViewer = BurpExtender.getCallbacks().createMessageEditor(this, false);
403 | resultDeViewer = BurpExtender.getCallbacks().createTextEditor();
404 |
405 | tabs.addTab("Result Details", resultDeViewer.getComponent());
406 | tabs.addTab("Request", requestViewer.getComponent());
407 | tabs.addTab("Original Response", responseViewer.getComponent());
408 | splitPane.setBottomComponent(tabs);
409 |
410 | BurpExtender.getCallbacks().customizeUiComponent(topPanel);
411 |
412 | // 构建一个定时刷新页面函数
413 | // 创建一个每5秒触发一次的定时器
414 | int delay = 5000; // 延迟时间,单位为毫秒
415 | timer = new Timer(delay, new ActionListener() {
416 | @Override
417 | public void actionPerformed(ActionEvent e) {
418 | // 调用刷新表格的方法
419 | try{
420 | refreshTableModel();
421 | } catch (Exception ep){
422 | BurpExtender.getStderr().println("[!] 刷新表格报错, 报错如下:");
423 | ep.printStackTrace(BurpExtender.getStderr());
424 | }
425 | }
426 | });
427 |
428 | timer.start();
429 |
430 |
431 |
432 | }
433 |
434 | public static void refreshTableModel(){
435 | // 刷新页面, 如果自动更新关闭,则不刷新页面内容
436 | // lbSuccessCount.setText(String.valueOf(BurpExtender.getDataBaseService().getApiDataCount()));
437 | if(getFlashButtonStatus()){
438 | if (Duration.between(operationStartTime, LocalDateTime.now()).getSeconds() > 600){
439 | setFlashButtonTrue();
440 | }
441 | return;
442 | }
443 | // 设置所有状态码为关闭
444 | showFilter();
445 | }
446 |
447 |
448 | public static void showFilter(){
449 | synchronized (model) {
450 | // 清空model后,根据URL来做匹配
451 | model.setRowCount(0);
452 | lbSuccessCount.setText(Integer.toString(BurpExtender.getDataBaseService().getTableDataCount()));
453 | // 获取数据库中的所有ApiDataModels
454 | java.util.List allApiDataModels = BurpExtender.getDataBaseService().getAllTableDataModels();
455 |
456 | // 遍历apiDataModelMap
457 | for (TableLogModel apiDataModel : allApiDataModels) {
458 | model.insertRow(0, new Object[]{
459 | apiDataModel.getPid(),
460 | apiDataModel.getMethod(),
461 | apiDataModel.getUrl(),
462 | apiDataModel.getTitle(),
463 | apiDataModel.getStatus(),
464 | apiDataModel.getResult(),
465 | apiDataModel.getType(),
466 | apiDataModel.getIsImportant(),
467 | apiDataModel.getTime()
468 | });
469 | }
470 | }
471 | }
472 |
473 | public static void filterTable(String typeFilter, String resultFilter, Boolean isImportantFilter) {
474 | try{
475 | // 清空model后,根据URL来做匹配
476 | model.setRowCount(0);
477 | lbSuccessCount.setText(Integer.toString(BurpExtender.getDataBaseService().getTableDataCount()));
478 | java.util.List allApiDataModels;
479 | // 获取数据库中的所有ApiDataModels
480 | allApiDataModels = BurpExtender.getDataBaseService().getTableDataModelsByFilter(typeFilter, resultFilter, isImportantFilter);
481 | operationStartTime = LocalDateTime.now();
482 | // 遍历apiDataModelMap
483 | for (TableLogModel apiDataModel : allApiDataModels) {
484 | model.insertRow(0, new Object[]{
485 | apiDataModel.getPid(),
486 | apiDataModel.getMethod(),
487 | apiDataModel.getUrl(),
488 | apiDataModel.getTitle(),
489 | apiDataModel.getStatus(),
490 | apiDataModel.getResult(),
491 | apiDataModel.getType(),
492 | apiDataModel.getIsImportant(),
493 | apiDataModel.getTime()
494 | });
495 | }
496 | } catch (Exception e) {
497 | BurpExtender.getStderr().println("[-] Error filterTableByType: ");
498 | e.printStackTrace(BurpExtender.getStderr());
499 | }
500 | }
501 |
502 |
503 |
504 | public Component getComponet(){
505 | return contentPane;
506 | }
507 |
508 | public IHttpService getHttpService() {
509 | return currentlyDisplayedItem.getHttpService();
510 | }
511 |
512 | public byte[] getRequest() {
513 | return currentlyDisplayedItem.getRequest();
514 | }
515 |
516 | public byte[] getResponse() {
517 | return currentlyDisplayedItem.getResponse();
518 | }
519 |
520 | public class WrapLayout extends FlowLayout {
521 | public WrapLayout() {
522 | super();
523 | }
524 |
525 | public WrapLayout(int align) {
526 | super(align);
527 | }
528 |
529 | public WrapLayout(int align, int hgap, int vgap) {
530 | super(align, hgap, vgap);
531 | }
532 |
533 | @Override
534 | public Dimension preferredLayoutSize(Container target) {
535 | return layoutSize(target, true);
536 | }
537 |
538 | @Override
539 | public Dimension minimumLayoutSize(Container target) {
540 | Dimension minimum = layoutSize(target, false);
541 | minimum.width -= (getHgap() + 1);
542 | return minimum;
543 | }
544 |
545 | private Dimension layoutSize(Container target, boolean preferred) {
546 | synchronized (target.getTreeLock()) {
547 | int targetWidth = target.getSize().width;
548 | Container container = target;
549 |
550 | while (container.getSize().width == 0 && container.getParent() != null) {
551 | container = container.getParent();
552 | }
553 |
554 | targetWidth = container.getSize().width;
555 |
556 | if (targetWidth == 0) {
557 | targetWidth = Integer.MAX_VALUE;
558 | }
559 |
560 | int hgap = getHgap();
561 | int vgap = getVgap();
562 | Insets insets = target.getInsets();
563 | int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2);
564 | int maxWidth = targetWidth - horizontalInsetsAndGap;
565 |
566 | // Fit components into the allowed width
567 | Dimension dim = new Dimension(0, 0);
568 | int rowWidth = 0;
569 | int rowHeight = 0;
570 |
571 | int nmembers = target.getComponentCount();
572 |
573 | for (int i = 0; i < nmembers; i++) {
574 | Component m = target.getComponent(i);
575 | if (m.isVisible()) {
576 | Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize();
577 |
578 | // Wrap line if this component doesn't fit
579 | if ((rowWidth + d.width) > maxWidth) {
580 | dim.width = Math.max(rowWidth, dim.width);
581 | dim.height += rowHeight + vgap;
582 | rowWidth = 0;
583 | rowHeight = 0;
584 | }
585 |
586 | // Add component size to current row
587 | if (rowWidth != 0) {
588 | rowWidth += hgap;
589 | }
590 | rowWidth += d.width;
591 | rowHeight = Math.max(rowHeight, d.height);
592 | }
593 | }
594 |
595 | dim.width = Math.max(rowWidth, dim.width);
596 | dim.height += rowHeight + vgap;
597 | dim.width += horizontalInsetsAndGap;
598 | dim.height += insets.top + insets.bottom + vgap * 2;
599 |
600 | // When using a scroll pane or the DecoratedLookAndFeel we need to
601 | // make sure the preferred size is less than the size of the
602 | // target containter so shrinking the container size works
603 | // correctly. Removing the horizontal gap is an easy way to do this.
604 | Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target);
605 | if (scrollPane != null && target.isValid()) {
606 | dim.width -= (hgap + 1);
607 | }
608 |
609 | return dim;
610 | }
611 | }
612 |
613 | }
614 |
615 | public static void setFlashButtonTrue(){
616 | flashButton.setSelected(false);
617 | flashText.setText("自动每5秒刷新表格中");
618 |
619 | }
620 |
621 | public static void setFlashButtonFalse(){
622 | flashButton.setSelected(true);
623 | flashText.setText("暂停每5秒刷新表格");
624 | }
625 |
626 | public static boolean getFlashButtonStatus(){
627 | // 检查按钮的选中状态
628 | if (flashButton.isSelected()) {
629 | // 如果按钮被选中,意味着刷新功能被激活,我们将文本设置为 "暂停刷新中"
630 | return true;
631 | } else {
632 | // 如果按钮没有被选中,意味着刷新功能没有被激活,我们将文本设置为 "自动刷新"
633 | return false;
634 | }
635 | }
636 |
637 |
638 |
639 | }
640 |
641 |
--------------------------------------------------------------------------------
/src/main/java/burp/ui/Tags.java:
--------------------------------------------------------------------------------
1 | package burp.ui;
2 |
3 | import java.awt.*;
4 | import javax.swing.*;
5 |
6 | import burp.ITab;
7 | import burp.IBurpExtenderCallbacks;
8 |
9 |
10 | public class Tags implements ITab {
11 |
12 | private final JTabbedPane tabs;
13 | private String tagName;
14 | public FingerTab fingerTab = new FingerTab();
15 | public FingerConfigTab fingerConfigTab = new FingerConfigTab();
16 | public WeakPasswordTab weakPasswordTab = new WeakPasswordTab();
17 |
18 | public Tags(IBurpExtenderCallbacks callbacks, String name){
19 |
20 | this.tagName = name;
21 | // 定义tab标签页
22 | tabs = new JTabbedPane();
23 | tabs.add("指纹识别", fingerTab.contentPane);
24 | tabs.add("口令爆破", weakPasswordTab.contentPane);
25 | tabs.add("指纹配置", fingerConfigTab);
26 |
27 | // 修改选中的标签页名字颜色
28 |
29 | // 将整个tab加载到平台即可
30 | callbacks.customizeUiComponent(tabs);
31 | // 将自定义选项卡添加到Burp的UI
32 | callbacks.addSuiteTab(Tags.this);
33 |
34 | }
35 |
36 |
37 | @Override
38 | public String getTabCaption() {
39 | return this.tagName;
40 | }
41 |
42 | @Override
43 | public Component getUiComponent() {
44 | return this.tabs;
45 | }
46 | }
--------------------------------------------------------------------------------
/src/main/java/burp/ui/WeakPasswordTab.java:
--------------------------------------------------------------------------------
1 | package burp.ui;
2 |
3 | import burp.*;
4 | import burp.model.TableLogModel;
5 | import burp.model.WeakPassword;
6 | import burp.ui.event.FingerTabEventHandlers;
7 | import burp.ui.renderer.HavingImportantRenderer;
8 | import burp.ui.renderer.HeaderIconRenderer;
9 | import burp.ui.renderer.IconTableCellRenderer;
10 | import burp.util.UiUtils;
11 |
12 | import javax.swing.*;
13 | import javax.swing.border.EmptyBorder;
14 | import javax.swing.event.ListSelectionEvent;
15 | import javax.swing.event.ListSelectionListener;
16 | import javax.swing.table.DefaultTableCellRenderer;
17 | import javax.swing.table.DefaultTableModel;
18 | import javax.swing.table.JTableHeader;
19 | import javax.swing.table.TableColumnModel;
20 | import java.awt.*;
21 | import java.awt.event.ActionEvent;
22 | import java.awt.event.ActionListener;
23 | import java.awt.event.MouseAdapter;
24 | import java.awt.event.MouseEvent;
25 | import java.time.Duration;
26 | import java.time.LocalDateTime;
27 | import java.util.HashMap;
28 | import java.util.Map;
29 |
30 |
31 | public class WeakPasswordTab implements IMessageEditorController {
32 | public static JPanel contentPane;
33 | private JSplitPane splitPane;
34 | public static IHttpRequestResponse currentlyDisplayedItem;
35 | public static JLabel lbRequestCount;
36 | public static JLabel lbSuccessCount;
37 |
38 | public static IMessageEditor requestViewer;
39 | public static IMessageEditor responseViewer;
40 | public static ITextEditor resultDeViewer;
41 |
42 | public static JToggleButton weakPasswordBlasting;
43 | private static DefaultTableModel model;
44 | public static JTable table;
45 | public static JToggleButton flashButton;
46 | public static JComboBox choicesComboBox;
47 | public static JLabel flashText;
48 | public static Timer timer;
49 | public static LocalDateTime operationStartTime = LocalDateTime.now();
50 |
51 | public WeakPasswordTab() {
52 | contentPane = new JPanel();
53 | contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
54 | contentPane.setLayout(new BorderLayout(0, 0));
55 |
56 | JPanel topPanel = new JPanel();
57 | GridBagLayout gridBagLayout = new GridBagLayout();
58 | // 列数,行数
59 | gridBagLayout.columnWidths = new int[] { 0, 0};
60 | gridBagLayout.rowHeights = new int[] {5};
61 | // 各列占宽度比,各行占高度比
62 | gridBagLayout.columnWeights = new double[] { 1.0D, Double.MIN_VALUE };
63 | topPanel.setLayout(gridBagLayout);
64 |
65 | JPanel FilterPanel = new JPanel();
66 | GridBagConstraints gbc_panel_1 = new GridBagConstraints();
67 | gbc_panel_1.insets = new Insets(0, 5, 5, 5);
68 | gbc_panel_1.fill = 2;
69 | gbc_panel_1.gridx = 0;
70 | gbc_panel_1.gridy = 2;
71 | topPanel.add(FilterPanel, gbc_panel_1);
72 | GridBagLayout gbl_panel_1 = new GridBagLayout();
73 | gbl_panel_1.columnWidths = new int[] { 0, 0, 0, 0, 0 };
74 | gbl_panel_1.rowHeights = new int[] { 0, 0 };
75 | gbl_panel_1.columnWeights = new double[] { 0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D, Double.MIN_VALUE};
76 | gbl_panel_1.rowWeights = new double[] { 0.0D, Double.MIN_VALUE };
77 | FilterPanel.setLayout(gbl_panel_1);
78 |
79 |
80 |
81 | // 转发url总数,默认0
82 | JLabel lbRequest = new JLabel("Total:");
83 | GridBagConstraints gbc_lbRequest = new GridBagConstraints();
84 | gbc_lbRequest.insets = new Insets(0, 0, 0, 5);
85 | gbc_lbRequest.fill = GridBagConstraints.HORIZONTAL;
86 | gbc_lbRequest.weightx = 0.0;
87 | gbc_lbRequest.gridx = 0;
88 | gbc_lbRequest.gridy = 0;
89 | FilterPanel.add(lbRequest, gbc_lbRequest);
90 |
91 | lbRequestCount = new JLabel("0");
92 | lbRequestCount.setForeground(new Color(0,0,255));
93 | GridBagConstraints gbc_lbRequestCount = new GridBagConstraints();
94 | gbc_lbRequestCount.insets = new Insets(0, 0, 0, 5);
95 | gbc_lbRequest.fill = GridBagConstraints.HORIZONTAL;
96 | gbc_lbRequest.weightx = 0.0;
97 | gbc_lbRequestCount.gridx = 1;
98 | gbc_lbRequestCount.gridy = 0;
99 | FilterPanel.add(lbRequestCount, gbc_lbRequestCount);
100 |
101 | // 转发成功url数,默认0
102 | JLabel lbSucces = new JLabel("Weak Password Success:");
103 | GridBagConstraints gbc_lbSucces = new GridBagConstraints();
104 | gbc_lbSucces.insets = new Insets(0, 0, 0, 5);
105 | gbc_lbSucces.fill = 0;
106 | gbc_lbSucces.gridx = 2;
107 | gbc_lbSucces.gridy = 0;
108 | FilterPanel.add(lbSucces, gbc_lbSucces);
109 |
110 | lbSuccessCount = new JLabel("0");
111 | lbSuccessCount.setForeground(new Color(0, 255, 0));
112 | GridBagConstraints gbc_lbSuccessCount = new GridBagConstraints();
113 | gbc_lbSuccessCount.insets = new Insets(0, 0, 0, 5);
114 | gbc_lbSuccessCount.fill = 0;
115 | gbc_lbSuccessCount.gridx = 3;
116 | gbc_lbSuccessCount.gridy = 0;
117 | FilterPanel.add(lbSuccessCount, gbc_lbSuccessCount);
118 |
119 | // 添加填充以在左侧占位
120 | GridBagConstraints gbc_leftFiller = new GridBagConstraints();
121 | gbc_leftFiller.weightx = 1; // 使得这个组件吸收额外的水平空间
122 | gbc_leftFiller.gridx = 5; // 位置设置为第一个单元格
123 | gbc_leftFiller.gridy = 0; // 第一行
124 | gbc_leftFiller.fill = GridBagConstraints.HORIZONTAL; // 水平填充
125 | FilterPanel.add(Box.createHorizontalGlue(), gbc_leftFiller);
126 |
127 | // 设置按钮的 GridBagConstraints
128 | GridBagConstraints gbc_buttons = new GridBagConstraints();
129 | gbc_buttons.insets = new Insets(0, 5, 0, 5);
130 | gbc_buttons.gridy = 0; // 设置按钮的纵坐标位置
131 | gbc_buttons.fill = GridBagConstraints.NONE; // 不填充
132 |
133 | // 刷新按钮按钮
134 | flashButton = new JToggleButton(UiUtils.getImageIcon("/icon/runningButton.png", 24, 24));
135 | flashButton.setSelectedIcon(UiUtils.getImageIcon("/icon/flashButton.png", 24, 24));
136 | flashButton.setPreferredSize(new Dimension(30, 30));
137 | flashButton.setBorder(null); // 设置无边框
138 | flashButton.setFocusPainted(false); // 移除焦点边框
139 | flashButton.setContentAreaFilled(false); // 移除选中状态下的背景填充
140 | flashButton.setToolTipText("用于控制表格是否自动化刷新,还是手工点击刷新");
141 |
142 | // 刷新按钮按钮
143 | weakPasswordBlasting = new JToggleButton(UiUtils.getImageIcon("/icon/WeakPasswordBlasting.png", 24, 24));
144 | weakPasswordBlasting.setSelectedIcon(UiUtils.getImageIcon("/icon/WeakPasswordBlastingFalse.png", 24, 24));
145 | weakPasswordBlasting.setPreferredSize(new Dimension(30, 30));
146 | weakPasswordBlasting.setBorder(null); // 设置无边框
147 | weakPasswordBlasting.setFocusPainted(false); // 移除焦点边框
148 | weakPasswordBlasting.setContentAreaFilled(false); // 移除选中状态下的背景填充
149 | weakPasswordBlasting.setToolTipText("弱口令爆破开启(功能开发中,请进群敬请期待)");
150 |
151 | weakPasswordBlasting.addActionListener(new ActionListener() {
152 | @Override
153 | public void actionPerformed(ActionEvent e) {
154 | // 例如,更新FingerConfigTab中的按钮状态
155 | BurpExtender.getTags().fingerTab.weakPasswordBlasting.setSelected(weakPasswordBlasting.isSelected());
156 | }
157 | });
158 |
159 | // 刷新按钮
160 | flashButton.addActionListener(new ActionListener() {
161 | @Override
162 | public void actionPerformed(ActionEvent e) {
163 | // 检查按钮的选中状态
164 | if (flashButton.isSelected()) {
165 | // 如果按钮被选中,意味着刷新功能被激活,我们将文本设置为 "暂停刷新中"
166 | flashText.setText("暂停每5秒刷新表格");
167 | } else {
168 | // 如果按钮没有被选中,意味着刷新功能没有被激活,我们将文本设置为 "自动刷新"
169 | flashText.setText("自动每5秒刷新表格中");
170 | showFilter();
171 | }
172 | }
173 | });
174 |
175 | // 刷新文本
176 | flashText = new JLabel("自动每5秒刷新表格中");
177 |
178 | gbc_buttons.gridx = 8; // 将横坐标位置移动到下一个单元格
179 | FilterPanel.add(weakPasswordBlasting, gbc_buttons);
180 | gbc_buttons.gridx = 9; // 将横坐标位置移动到下一个单元格
181 | FilterPanel.add(flashButton, gbc_buttons);
182 | gbc_buttons.gridx = 10; // 将横坐标位置移动到下一个单元格
183 | FilterPanel.add(flashText, gbc_buttons);
184 |
185 | // 添加填充以在右侧占位
186 | GridBagConstraints gbc_rightFiller = new GridBagConstraints();
187 | gbc_rightFiller.weightx = 1; // 使得这个组件吸收额外的水平空间
188 | gbc_rightFiller.gridx = 12; // 位置设置为最后一个单元格
189 | gbc_rightFiller.gridy = 0; // 第一行
190 | gbc_rightFiller.fill = GridBagConstraints.HORIZONTAL; // 水平填充
191 | FilterPanel.add(Box.createHorizontalGlue(), gbc_rightFiller);
192 |
193 |
194 | // 添加一个 "清除" 按钮
195 | JButton btnClear = new JButton("清空");
196 | GridBagConstraints gbc_btnClear = new GridBagConstraints();
197 | gbc_btnClear.insets = new Insets(0, 0, 0, 5);
198 | gbc_btnClear.fill = 0;
199 | gbc_btnClear.gridx = 13; // 根据该值来确定是确定从左到右的顺序
200 | gbc_btnClear.gridy = 0;
201 | FilterPanel.add(btnClear, gbc_btnClear);
202 |
203 | // 给 "清空" 按钮添加一个监听事件
204 | btnClear.addActionListener(new ActionListener() {
205 | @Override
206 | public void actionPerformed(ActionEvent e) {
207 | BurpExtender.getDataBaseService().clearWeakPasswordTable();
208 | showFilter();
209 | }
210 | });
211 |
212 | // 功能按钮
213 | JPopupMenu moreMenu = new JPopupMenu("功能");
214 | JButton moreButton = new JButton();
215 | moreButton.setIcon(UiUtils.getImageIcon("/icon/moreButton.png", 17, 17));
216 | GridBagConstraints gbc_btnMore = new GridBagConstraints();
217 | gbc_btnClear.insets = new Insets(0, 0, 0, 5);
218 | gbc_btnClear.fill = 0;
219 | gbc_btnClear.gridx = 14; // 根据该值来确定是确定从左到右的顺序
220 | gbc_btnClear.gridy = 0;
221 | FilterPanel.add(moreButton, gbc_btnMore);
222 |
223 |
224 | // 点击”功能“的监听事件
225 | moreButton.addMouseListener(new MouseAdapter() {
226 | public void mouseClicked(MouseEvent e) {
227 | moreMenu.show(e.getComponent(), e.getX(), e.getY());
228 | }
229 | });
230 |
231 | contentPane.add(topPanel,BorderLayout.NORTH);
232 |
233 | contentPane.add(topPanel,BorderLayout.NORTH); // 只在 contentPane 的北部添加一个组件
234 |
235 | splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
236 | splitPane.setDividerLocation(0.5);
237 | contentPane.add(splitPane, BorderLayout.CENTER);
238 |
239 | // 数据展示面板
240 | model = new DefaultTableModel(new Object[]{"#", "url", "finger", "weakPassword", "test_number", "status", "time"}, 0) {
241 | @Override
242 | public boolean isCellEditable(int row, int column) {
243 | // This will make all cells of the table non-editable
244 | return false;
245 | }
246 | };
247 | table = new JTable(model){
248 | // 重写getToolTipText方法以返回特定单元格的数据
249 | public String getToolTipText(MouseEvent e) {
250 | int row = rowAtPoint(e.getPoint());
251 | int col = columnAtPoint(e.getPoint());
252 | if (row > -1 && col > -1) {
253 | Object value = getValueAt(row, col);
254 | return value == null ? null : value.toString();
255 | }
256 | return super.getToolTipText(e);
257 | }
258 | };;
259 |
260 |
261 | // 前两列设置宽度 30px、60px
262 | table.getColumnModel().getColumn(0).setMinWidth(20);
263 | table.getColumnModel().getColumn(1).setMinWidth(300);
264 | table.getColumnModel().getColumn(2).setMinWidth(60);
265 | table.getColumnModel().getColumn(3).setMinWidth(180);
266 | table.getColumnModel().getColumn(4).setMinWidth(60);
267 | table.getColumnModel().getColumn(5).setMinWidth(60);
268 | table.getColumnModel().getColumn(6).setMinWidth(60);
269 |
270 | // 创建一个居中对齐的单元格渲染器
271 | DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer();
272 | centerRenderer.setHorizontalAlignment(JLabel.CENTER);
273 |
274 | DefaultTableCellRenderer leftRenderer = new DefaultTableCellRenderer();
275 | leftRenderer.setHorizontalAlignment(JLabel.LEFT);
276 |
277 | table.getColumnModel().getColumn(0).setCellRenderer(centerRenderer);
278 | table.getColumnModel().getColumn(1).setCellRenderer(leftRenderer);
279 | table.getColumnModel().getColumn(2).setCellRenderer(centerRenderer);
280 | table.getColumnModel().getColumn(3).setCellRenderer(centerRenderer);
281 | table.getColumnModel().getColumn(4).setCellRenderer(centerRenderer);
282 | table.getColumnModel().getColumn(5).setCellRenderer(centerRenderer);
283 | table.getColumnModel().getColumn(6).setCellRenderer(centerRenderer);
284 |
285 | IconTableCellRenderer havingImportantRenderer = new IconTableCellRenderer();
286 | table.getColumnModel().getColumn(5).setCellRenderer(havingImportantRenderer);
287 |
288 | // 创建右键菜单
289 | JPopupMenu popupMenu = new JPopupMenu();
290 | JMenuItem clearItem = new JMenuItem("清除");
291 | popupMenu.add(clearItem);
292 | // 将右键菜单添加到表格
293 | table.setComponentPopupMenu(popupMenu);
294 |
295 |
296 | JScrollPane jspLogTable = new JScrollPane(table);
297 | splitPane.setTopComponent(jspLogTable);
298 |
299 | // 设置表格选择模式为单行选择
300 | table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
301 |
302 | // 添加ListSelectionListener来监听行选择事件
303 | table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
304 | public void valueChanged(ListSelectionEvent event) {
305 | if (!event.getValueIsAdjusting() && table.getSelectedRow() != -1) {
306 | setFlashButtonFalse();
307 | operationStartTime = LocalDateTime.now();
308 | // 在这里获取选中行的数据
309 | int selectedRow = table.getSelectedRow();
310 | // 根据您的数据模型结构获取数据
311 | String url = model.getValueAt(selectedRow, 1).toString();
312 | WeakPassword wp = BurpExtender.getDataBaseService().getWeakPasswordByUrl(url);
313 | resultDeViewer.setText(wp.getResultInfo().getBytes());
314 | // 例如,你可以将它们设置到一个 HTTP 消息编辑器组件中
315 | requestViewer.setMessage(wp.getRequestsByte(), true); // true 表示请求消息
316 | responseViewer.setMessage(wp.getResponseByte(), false); // false 表示响应消息
317 |
318 | }
319 | }
320 | });
321 |
322 |
323 | JTabbedPane tabs = new JTabbedPane();
324 | requestViewer = BurpExtender.getCallbacks().createMessageEditor(this, false);
325 | responseViewer = BurpExtender.getCallbacks().createMessageEditor(this, false);
326 | resultDeViewer = BurpExtender.getCallbacks().createTextEditor();
327 |
328 | tabs.addTab("Result Details", resultDeViewer.getComponent());
329 | tabs.addTab("Request", requestViewer.getComponent());
330 | tabs.addTab("Original Response", responseViewer.getComponent());
331 | splitPane.setBottomComponent(tabs);
332 |
333 | BurpExtender.getCallbacks().customizeUiComponent(topPanel);
334 |
335 | // 创建一个每5秒触发一次的定时器
336 | int delay = 5000; // 延迟时间,单位为毫秒
337 | timer = new Timer(delay, new ActionListener() {
338 | @Override
339 | public void actionPerformed(ActionEvent e) {
340 | // 调用刷新表格的方法
341 | try{
342 | refreshTableModel();
343 | } catch (Exception ep){
344 | BurpExtender.getStderr().println("[!] 刷新表格报错, 报错如下:");
345 | ep.printStackTrace(BurpExtender.getStderr());
346 | }
347 | }
348 | });
349 |
350 | timer.start();
351 |
352 |
353 |
354 | }
355 |
356 | public static void refreshTableModel(){
357 | // 刷新页面, 如果自动更新关闭,则不刷新页面内容
358 | if(getFlashButtonStatus()){
359 | if (Duration.between(operationStartTime, LocalDateTime.now()).getSeconds() > 600){
360 | setFlashButtonTrue();
361 | }
362 | return;
363 | }
364 | // 设置所有状态码为关闭
365 | showFilter();
366 | }
367 |
368 | public static void showFilter(){
369 | synchronized (model) {
370 | // 清空model后,根据URL来做匹配
371 | model.setRowCount(0);
372 | lbSuccessCount.setText(Integer.toString(BurpExtender.getDataBaseService().getWeakPasswordSuccessCount()));
373 | lbRequestCount.setText(Integer.toString(BurpExtender.getDataBaseService().getWeakPasswordCount()));
374 | // 获取数据库中的所有ApiDataModels
375 | java.util.List weakPasswordList = BurpExtender.getDataBaseService().getAllWeakPassword();
376 |
377 | // 遍历apiDataModelMap
378 | for (WeakPassword weakPasswordModel : weakPasswordList) {
379 | model.insertRow(0, new Object[]{
380 | weakPasswordModel.getId(),
381 | weakPasswordModel.getUrl(),
382 | weakPasswordModel.getFinger(),
383 | weakPasswordModel.getWeakPassword(),
384 | weakPasswordModel.getTestNumber(),
385 | weakPasswordModel.getStatus(),
386 | weakPasswordModel.getTime()
387 | });
388 | }
389 | }
390 | }
391 |
392 |
393 | public Component getComponet(){
394 | return contentPane;
395 | }
396 |
397 | public IHttpService getHttpService() {
398 | return currentlyDisplayedItem.getHttpService();
399 | }
400 |
401 | public byte[] getRequest() {
402 | return currentlyDisplayedItem.getRequest();
403 | }
404 |
405 | public byte[] getResponse() {
406 | return currentlyDisplayedItem.getResponse();
407 | }
408 |
409 |
410 | public static void setFlashButtonTrue(){
411 | flashButton.setSelected(false);
412 | flashText.setText("自动每5秒刷新表格中");
413 |
414 | }
415 |
416 | public static void setFlashButtonFalse(){
417 | flashButton.setSelected(true);
418 | flashText.setText("暂停每5秒刷新表格");
419 | }
420 |
421 | public static boolean getFlashButtonStatus(){
422 | // 检查按钮的选中状态
423 | if (flashButton.isSelected()) {
424 | // 如果按钮被选中,意味着刷新功能被激活,我们将文本设置为 "暂停刷新中"
425 | return true;
426 | } else {
427 | // 如果按钮没有被选中,意味着刷新功能没有被激活,我们将文本设置为 "自动刷新"
428 | return false;
429 | }
430 | }
431 |
432 |
433 |
434 | }
435 |
436 |
--------------------------------------------------------------------------------
/src/main/java/burp/ui/event/FingerTabEventHandlers.java:
--------------------------------------------------------------------------------
1 | package burp.ui.event;
2 |
3 | import burp.BurpExtender;
4 | import burp.model.TableLogModel;
5 | import burp.ui.FingerTab;
6 |
7 | import javax.swing.*;
8 | import javax.swing.event.TableModelEvent;
9 | import javax.swing.event.TableModelListener;
10 | import javax.swing.filechooser.FileNameExtensionFilter;
11 | import javax.swing.table.DefaultTableModel;
12 | import java.awt.*;
13 | import java.awt.event.ActionEvent;
14 | import java.awt.event.ActionListener;
15 | import java.awt.event.MouseAdapter;
16 | import java.awt.event.MouseEvent;
17 | import java.io.File;
18 | import java.util.*;
19 | import java.util.List;
20 | import java.io.FileWriter;
21 | import java.io.IOException;
22 |
23 | import static burp.util.Utils.escapeCsv;
24 |
25 | /**
26 | * @author: shaun
27 | * @create: 2024/3/27 21:41
28 | * @description:TODO
29 | */
30 | public class FingerTabEventHandlers {
31 | public static MouseAdapter headerAddMouseListener(JTable logTable) {
32 | return new MouseAdapter() {
33 | @Override
34 | public void mouseClicked(MouseEvent e) {
35 | if (logTable.getColumnModel().getColumnIndexAtX(e.getX()) == 6) { // 假设类型列的索引是1
36 | JPopupMenu filterMenu = new JPopupMenu();
37 |
38 | // “全部”选项用于移除过滤
39 | JMenuItem allItem = new JMenuItem("全部");
40 | allItem.addActionListener(new ActionListener() {
41 | @Override
42 | public void actionPerformed(ActionEvent e) {
43 | FingerTab.historyChoiceType = "全部";
44 | FingerTab.historyChoiceJMenuItem = "全部";
45 | FingerTab.setFlashButtonTrue();
46 | FingerTab.filterTable("全部", "全部", null);
47 | }
48 | });
49 | filterMenu.add(allItem);
50 |
51 | filterMenu.add(new JSeparator()); // 分隔线
52 |
53 | // 为每个独特的类型创建菜单项
54 | for (String type : BurpExtender.getTags().fingerConfigTab.uniqueTypes) {
55 | JMenuItem menuItem = new JMenuItem(type);
56 | menuItem.addActionListener(new ActionListener() {
57 | @Override
58 | public void actionPerformed(ActionEvent e) {
59 | FingerTab.historyChoiceJMenuItem = "全部";
60 | FingerTab.historyChoiceType = type;
61 | FingerTab.setFlashButtonFalse();
62 | FingerTab.filterTable(type, "全部", null); // 根据选中的类型过滤表格
63 | }
64 | });
65 | filterMenu.add(menuItem);
66 | }
67 |
68 | filterMenu.show(e.getComponent(), e.getX(), e.getY()); // 显示菜单
69 | } else if (logTable.getColumnModel().getColumnIndexAtX(e.getX()) == 7) {
70 | JPopupMenu filterMenu = new JPopupMenu();
71 |
72 | List isImportantItem = new ArrayList<>(Arrays.asList("全部", "重点", "普通"));
73 |
74 |
75 | // 为每个独特的类型创建菜单项
76 | for (String itemName : isImportantItem) {
77 | JMenuItem menuItem = new JMenuItem(itemName);
78 | menuItem.addActionListener(new ActionListener() {
79 | @Override
80 | public void actionPerformed(ActionEvent e) {
81 | if (itemName.equals("全部")) {
82 | FingerTab.setFlashButtonTrue();
83 | FingerTab.filterTable(FingerTab.historyChoiceType, FingerTab.historyChoiceJMenuItem, null); // 根据选中的类型过滤表格
84 | } else if (itemName.equals("重点")) {
85 | FingerTab.setFlashButtonFalse();
86 | FingerTab.filterTable(FingerTab.historyChoiceType, FingerTab.historyChoiceJMenuItem, true); // 根据选中的类型过滤表格
87 | } else if (itemName.equals("普通")) {
88 | FingerTab.setFlashButtonFalse();
89 | FingerTab.filterTable(FingerTab.historyChoiceType, FingerTab.historyChoiceJMenuItem, false); // 根据选中的类型过滤表格
90 | }
91 |
92 | }
93 | });
94 | filterMenu.add(menuItem);
95 | }
96 |
97 | filterMenu.show(e.getComponent(), e.getX(), e.getY()); // 显示菜单
98 | }
99 | }
100 |
101 |
102 | };
103 | }
104 |
105 |
106 | public static TableModelListener modelAddTableModelListener(DefaultTableModel model, JPanel tagsPanel, HashMap resultMap, JTable logTable) {
107 | return new TableModelListener() {
108 | private static final int MAX_LABEL = 20;
109 | List labelList = new ArrayList<>();
110 | int ADD_LABEL_NUMBER = 0;
111 | @Override
112 | public void tableChanged(TableModelEvent e) {
113 | HashMap resultCounts = BurpExtender.getDataBaseService().getResultCountsFromDatabase();
114 | // 创建一个 TreeMap 并进行反向排序
115 | TreeMap> sortedResults = new TreeMap<>(Collections.reverseOrder());
116 | for (Map.Entry entry : resultCounts.entrySet()) {
117 | sortedResults.computeIfAbsent(entry.getValue(), k -> new LinkedList<>()).add(entry.getKey());
118 | }
119 | List tmpList = new ArrayList<>();
120 | // 添加新的结果标签
121 | for (Map.Entry> entry : sortedResults.entrySet()) {
122 | Integer count = entry.getKey();
123 | for (String result : entry.getValue()) {
124 | if(ADD_LABEL_NUMBER == MAX_LABEL){
125 | tmpList.add("...(0)");
126 | ADD_LABEL_NUMBER += 1;
127 | continue;
128 | }
129 | if(ADD_LABEL_NUMBER > MAX_LABEL){
130 | continue;
131 | }
132 | ADD_LABEL_NUMBER += 1;
133 | tmpList.add(result + " (" + count + ")");
134 |
135 | }
136 | }
137 | if(!labelList.equals(tmpList)){
138 | labelList = tmpList;
139 | clearAllResultLabels(tagsPanel);
140 | for (String result : tmpList){
141 | addNewResultLabel(result);
142 | }
143 | }
144 | ADD_LABEL_NUMBER = 0;
145 | }
146 |
147 | public void addNewResultLabel(String result) {
148 | // 创建新的标签
149 | JLabel newLabel = new JLabel(result);
150 | newLabel.setOpaque(true); // 设置为不透明
151 | newLabel.setBackground(new Color(200, 200, 200)); // 设置背景颜色为浅灰色
152 | newLabel.setForeground(Color.BLACK); // 设置字体颜色为黑色
153 |
154 | // 为标签添加一个有颜色的边框,边框内有5像素的填充
155 | newLabel.setBorder(BorderFactory.createCompoundBorder(
156 | BorderFactory.createLineBorder(new Color(100, 100, 100), 1), // 外部边框,颜色为深灰色,宽度为2像素
157 | BorderFactory.createEmptyBorder(5, 5, 5, 5) // 内部填充,宽度为5像素
158 | ));
159 |
160 |
161 | if (!result.contains("...(")){
162 |
163 | newLabel.addMouseListener(new MouseAdapter() {
164 | @Override
165 | public void mouseClicked(MouseEvent e) {
166 | // 取消之前选中标签的颜色
167 | if (FingerTab.currentSelectedLabel != null) {
168 | FingerTab.currentSelectedLabel.setBackground(new Color(200, 200, 200)); // 还原默认背景色
169 | }
170 | FingerTab.setFlashButtonFalse();
171 | // 设置当前点击的标签为选中状态
172 | newLabel.setBackground(new Color(150, 150, 150)); // 选中状态的背景色
173 | FingerTab.currentSelectedLabel = newLabel; //
174 | // 当用户点击某个标签时,展示所有包含该标签文本的结果
175 | String filterWithoutCount = result.replaceAll("\\(.*\\)", "").trim();
176 | FingerTab.historyChoiceJMenuItem = filterWithoutCount;
177 | FingerTab.historyChoiceType = "全部";
178 | FingerTab.filterTable("全部", filterWithoutCount, null);
179 | }
180 | });
181 | }
182 |
183 |
184 | // 添加新的标签到面板和 resultMap
185 | tagsPanel.add(newLabel);
186 | resultMap.put(result, newLabel);
187 | // 重新验证和重绘面板
188 | tagsPanel.revalidate();
189 | tagsPanel.repaint();
190 | }
191 |
192 |
193 |
194 | };
195 | }
196 |
197 |
198 | public static ActionListener btnClearAddActionListener(DefaultTableModel model, JLabel lbRequestCount, JLabel lbSuccessCount){
199 | return new ActionListener() {
200 | @Override
201 | public void actionPerformed(ActionEvent e) {
202 | // 清除表格数据
203 | lbRequestCount.setText("0");
204 | lbSuccessCount.setText("0");
205 | model.setRowCount(0);
206 | BurpExtender.getDataBaseService().clearTableDataTable();
207 | BurpExtender.getDataBaseService().clearRequestsResponseTable();
208 | model.fireTableDataChanged();
209 | }
210 | };
211 | }
212 |
213 | public static ActionListener exportItemAddActionListener() {
214 | return new ActionListener() {
215 | @Override
216 | public void actionPerformed(ActionEvent e) {
217 | // 创建一个文件选择器实例
218 | JFileChooser fileChooser = new JFileChooser();
219 | fileChooser.setDialogTitle("Save As"); // 设置对话框标题
220 | fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
221 | fileChooser.setAcceptAllFileFilterUsed(false); // 取消所有文件过滤
222 | fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("CSV Files", "csv")); // 只允许CSV文件格式
223 |
224 | // 设置默认的文件名
225 | fileChooser.setSelectedFile(new File("BurpFingerPrint-TableData.csv"));
226 |
227 | // 弹出保存对话框
228 | int userSelection = fileChooser.showSaveDialog(null);
229 |
230 | if (userSelection == JFileChooser.APPROVE_OPTION) {
231 | // 获取用户选择的文件
232 | File fileToSave = fileChooser.getSelectedFile();
233 | String filePath = fileToSave.getAbsolutePath();
234 | // 确保文件有.csv扩展名
235 | if (!filePath.endsWith(".csv")) {
236 | filePath += ".csv";
237 | }
238 |
239 | // 获取所有数据模型
240 | List allTableDataModels = BurpExtender.getDataBaseService().getAllTableDataModels();
241 |
242 | // 创建CSV文件并写入数据
243 | try (FileWriter csvWriter = new FileWriter(filePath)) {
244 | // 写入标题行(根据TableLogModel的字段)
245 | csvWriter.append("PID,URL,Method,Title,Status,Result,Type,IsImportant,ResultInfo,Host,Port,Protocol,RequestResponseIndex,Time\n");
246 |
247 | // 遍历所有的数据模型并写入CSV
248 | for (TableLogModel model : allTableDataModels) {
249 | csvWriter.append(escapeCsv(String.valueOf(model.getPid())) + ",");
250 | csvWriter.append(escapeCsv(model.getUrl()) + ",");
251 | csvWriter.append(escapeCsv(model.getMethod()) + ",");
252 | csvWriter.append(escapeCsv(model.getTitle()) + ",");
253 | csvWriter.append(escapeCsv(model.getStatus()) + ",");
254 | csvWriter.append(escapeCsv(model.getResult()) + ",");
255 | csvWriter.append(escapeCsv(model.getType()) + ",");
256 | csvWriter.append(escapeCsv(String.valueOf(model.getIsImportant())) + ",");
257 | csvWriter.append(escapeCsv(model.getResultInfo()) + ",");
258 | csvWriter.append(escapeCsv(model.getHost()) + ",");
259 | csvWriter.append(escapeCsv(String.valueOf(model.getPort())) + ",");
260 | csvWriter.append(escapeCsv(model.getProtocol()) + ",");
261 | csvWriter.append(escapeCsv(String.valueOf(model.getRequestResponseIndex())) + ",");
262 | csvWriter.append(escapeCsv(model.getTime()));
263 | csvWriter.append("\n");
264 | }
265 | } catch (IOException ex) {
266 | ex.printStackTrace();
267 | }
268 | }
269 | }
270 | };
271 | }
272 |
273 |
274 | public static ActionListener clearItemAddActionListener(DefaultTableModel model, JTable logTable, JLabel lbSuccessCount){
275 | return new ActionListener() {
276 | @Override
277 | public void actionPerformed(ActionEvent e) {
278 | int selectRow = logTable.getSelectedRow();
279 | if (selectRow >= 0){
280 | String url = model.getValueAt(selectRow, 2).toString();
281 | BurpExtender.getDataBaseService().deleteDataByUrl(url);
282 | }
283 | FingerTab.filterTable(FingerTab.historyChoiceType, FingerTab.historyChoiceJMenuItem, null);
284 | lbSuccessCount.setText(Integer.toString(BurpExtender.getDataBaseService().getTableDataCount()));
285 | }
286 | };
287 | }
288 | //
289 | //
290 | // public static ActionListener allFingerprintsButtonAddActionListener(HttpLogTable logTable, JToggleButton allFingerprintsButton){
291 | // return new ActionListener() {
292 | // @Override
293 | // public void actionPerformed(ActionEvent e) {
294 | // // 检查按钮状态并切换
295 | // if (allFingerprintsButton.isSelected() && logTable != null) {
296 | // // 按钮选中状态,应用过滤器以仅显示 isImportant 列为 true 的行
297 | // RowFilter importanceFilter = new RowFilter() {
298 | // @Override
299 | // public boolean include(Entry extends TableModel, ? extends Integer> entry) {
300 | // // 假设 isImportant 是第7列(注意列索引从0开始计数)
301 | // Boolean isImportant = (Boolean) entry.getValue(7); // 根据您的表格实际的列索引来调整
302 | // return isImportant != null && isImportant;
303 | // }
304 | // };
305 | // ((TableRowSorter) logTable.getRowSorter()).setRowFilter(importanceFilter);
306 | // } else if (logTable != null) {
307 | // // 按钮未选中状态,移除过滤器以显示所有行
308 | // ((TableRowSorter) logTable.getRowSorter()).setRowFilter(null);
309 | // }
310 | // // 更新FingerConfigTab中的按钮状态
311 | // FingerConfigTab.allFingerprintsButton.setSelected(allFingerprintsButton.isSelected());
312 | // // 调用FingerConfigTab中的toggleFingerprintsDisplay方法
313 | // FingerConfigTab.toggleFingerprintsDisplay(false, allFingerprintsButton.isSelected());
314 | // }
315 | // };
316 | // }
317 |
318 | public static void clearAllResultLabels(JPanel tagsPanel) {
319 | for (Component component : tagsPanel.getComponents()) {
320 | if (component instanceof JLabel) {
321 | JLabel label = (JLabel) component;
322 | // 如果标签的文本不是"全部",则移除
323 | if (!"全部".equals(label.getText())) {
324 | tagsPanel.remove(component);
325 | }
326 | }
327 | }
328 | tagsPanel.revalidate();
329 | tagsPanel.repaint();
330 | }
331 | }
--------------------------------------------------------------------------------
/src/main/java/burp/ui/renderer/ButtonRenderer.java:
--------------------------------------------------------------------------------
1 | package burp.ui.renderer;
2 |
3 | import javax.swing.*;
4 | import javax.swing.table.TableCellRenderer;
5 | import java.awt.*;
6 |
7 | import burp.util.UiUtils;
8 |
9 | /**
10 | * @author: shaun
11 | * @create: 2024/3/28 00:19
12 | * @description:TODO
13 | */
14 | public class ButtonRenderer extends JPanel implements TableCellRenderer {
15 | private final JButton editButton;
16 | private final JButton deleteButton;
17 | private static final Icon EDIT_ICON = UiUtils.getImageIcon("/icon/editButton.png");
18 | private static final Icon DELETE_ICON = UiUtils.getImageIcon("/icon/deleteButton.png");
19 |
20 |
21 | public ButtonRenderer() {
22 | setBorder(BorderFactory.createLineBorder(Color.BLACK));
23 | setLayout(new FlowLayout(FlowLayout.CENTER, 5, 0));
24 | editButton = new JButton();
25 | editButton.setIcon(EDIT_ICON);
26 | deleteButton = new JButton();
27 | deleteButton.setIcon(DELETE_ICON);
28 |
29 | editButton.setPreferredSize(new Dimension(40, 20));
30 | deleteButton.setPreferredSize(new Dimension(40, 20));
31 |
32 | add(editButton);
33 | add(deleteButton);
34 | setBorder(BorderFactory.createEmptyBorder());
35 | }
36 |
37 | @Override
38 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
39 | if (isSelected) {
40 | setBackground(table.getSelectionBackground());
41 | } else {
42 | setBackground(table.getBackground());
43 | }
44 | return this;
45 | }
46 | }
--------------------------------------------------------------------------------
/src/main/java/burp/ui/renderer/CenterRenderer.java:
--------------------------------------------------------------------------------
1 | package burp.ui.renderer;
2 |
3 | import javax.swing.*;
4 | import javax.swing.table.DefaultTableCellRenderer;
5 |
6 | /**
7 | * @author: shaun
8 | * @create: 2024/3/28 00:18
9 | * @description:TODO
10 | */
11 | public class CenterRenderer extends DefaultTableCellRenderer {
12 | public CenterRenderer() {
13 | setHorizontalAlignment(JLabel.CENTER);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/burp/ui/renderer/CenterTableCellRenderer.java:
--------------------------------------------------------------------------------
1 | package burp.ui.renderer;
2 |
3 | import javax.swing.*;
4 | import javax.swing.table.DefaultTableCellRenderer;
5 | import java.awt.*;
6 |
7 | /**
8 | * @author: shaun
9 | * @create: 2024/3/27 21:31
10 | * @description:TODO
11 | */
12 | public class CenterTableCellRenderer extends DefaultTableCellRenderer {
13 | public CenterTableCellRenderer() {
14 | setHorizontalAlignment(JLabel.CENTER); // 设置水平对齐为居中
15 | }
16 |
17 | @Override
18 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
19 | super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
20 | return this;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/burp/ui/renderer/HavingImportantRenderer.java:
--------------------------------------------------------------------------------
1 | package burp.ui.renderer;
2 |
3 | import burp.util.UiUtils;
4 |
5 | import javax.swing.*;
6 | import javax.swing.table.DefaultTableCellRenderer;
7 | import java.awt.*;
8 |
9 | /**
10 | * @author: shaun
11 | * @create: 2024/3/27 21:30
12 | * @description:TODO
13 | */
14 | public class HavingImportantRenderer extends DefaultTableCellRenderer {
15 |
16 | private static final Icon IMPORTANT_ICON = UiUtils.getImageIcon("/icon/importantButtonIcon.png", 15, 15);
17 | private static final Icon NORMAL_ICON = UiUtils.getImageIcon("/icon/normalIcon.png", 15, 15);
18 |
19 | public HavingImportantRenderer() {
20 | setHorizontalAlignment(CENTER); // 设置居中
21 | }
22 |
23 | @Override
24 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
25 | super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
26 |
27 | // 设置文本为空,因为我们只显示图标
28 | setText("");
29 |
30 | // 根据单元格值设置相应图标
31 | if (value instanceof Boolean) {
32 | if ((Boolean) value) {
33 | setIcon(IMPORTANT_ICON);
34 | } else {
35 | setIcon(NORMAL_ICON);
36 | }
37 | } else {
38 | setIcon(null); // 如果值不是布尔类型,则不显示图标
39 | }
40 |
41 | return this;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/burp/ui/renderer/HeaderIconRenderer.java:
--------------------------------------------------------------------------------
1 | package burp.ui.renderer;
2 |
3 | import javax.swing.*;
4 | import javax.swing.table.DefaultTableCellRenderer;
5 | import java.awt.*;
6 | import burp.util.UiUtils;
7 |
8 | /**
9 | * @author: shaun
10 | * @create: 2024/3/27 21:20
11 | * @description:TODO
12 | */
13 | public class HeaderIconRenderer extends DefaultTableCellRenderer {
14 | private static final Icon FILTER_ICON = UiUtils.getImageIcon("/icon/filterIcon.png", 17, 17);
15 | @Override
16 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
17 | // 保留原始行为
18 | Component comp = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
19 |
20 | // 如果是类型列
21 | if (column == 6 || column == 7) {
22 | setIcon(FILTER_ICON);
23 | setHorizontalAlignment(JLabel.CENTER);
24 | setHorizontalTextPosition(JLabel.LEFT);
25 | setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
26 | } else {
27 | setIcon(null);
28 | }
29 | return comp;
30 | }
31 | }
--------------------------------------------------------------------------------
/src/main/java/burp/ui/renderer/HeaderIconTypeRenderer.java:
--------------------------------------------------------------------------------
1 | package burp.ui.renderer;
2 |
3 | import javax.swing.*;
4 | import javax.swing.table.DefaultTableCellRenderer;
5 | import javax.swing.table.TableCellRenderer;
6 | import java.awt.*;
7 | import burp.util.UiUtils;
8 | /**
9 | * @author: shaun
10 | * @create: 2024/3/30 22:43
11 | * @description:TODO
12 | */
13 | public class HeaderIconTypeRenderer extends DefaultTableCellRenderer {
14 | private static final Icon FILTER_ICON = UiUtils.getImageIcon("/icon/filterIcon.png");
15 |
16 | @Override
17 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
18 | // 保留原始行为
19 | Component comp = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
20 |
21 | // 如果是类型列
22 | if (column == 1) {
23 | setIcon(FILTER_ICON);
24 | setHorizontalAlignment(JLabel.CENTER);
25 | setHorizontalTextPosition(JLabel.LEFT);
26 | setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
27 | } else {
28 | setIcon(null);
29 | }
30 | return comp;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/burp/ui/renderer/IconTableCellRenderer.java:
--------------------------------------------------------------------------------
1 | package burp.ui.renderer;
2 |
3 | import burp.util.UiUtils;
4 |
5 | import javax.swing.*;
6 | import javax.swing.table.DefaultTableCellRenderer;
7 | import java.awt.*;
8 |
9 | public class IconTableCellRenderer extends DefaultTableCellRenderer {
10 |
11 | // 预加载并缓存图标
12 | private final Icon importantIcon = UiUtils.getImageIcon("/icon/weakPasswordSuccess.png", 15, 15);
13 |
14 | public IconTableCellRenderer() {
15 | setHorizontalAlignment(CENTER); // 设置居中
16 | }
17 |
18 | @Override
19 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
20 | // 调用父类以保留默认行为
21 | super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
22 |
23 | // 检查单元格值是否为“爆破成功”
24 | if ("爆破成功".equals(value)) {
25 | setIcon(importantIcon);
26 | setText(""); // 设置文本为空,因为我们只显示图标
27 | } else {
28 | setIcon(null); // 不显示图标
29 | setText(value != null ? value.toString() : ""); // 显示文本,如果值为null则为空字符串
30 | }
31 |
32 | return this;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/burp/util/FingerUtils.java:
--------------------------------------------------------------------------------
1 | package burp.util;
2 |
3 | import burp.BurpExtender;
4 | import burp.IExtensionHelpers;
5 | import burp.IHttpService;
6 | import burp.IResponseInfo;
7 | import burp.model.FingerPrintRule;
8 | import burp.model.TableLogModel;
9 | import burp.ui.FingerConfigTab;
10 |
11 | import java.nio.charset.StandardCharsets;
12 | import java.text.SimpleDateFormat;
13 | import java.util.Arrays;
14 | import java.util.Date;
15 | import java.util.HashMap;
16 | import java.util.Map;
17 |
18 | /**
19 | * @author: shaun
20 | * @create: 2024/3/6 20:43
21 | * @description:TODO
22 | */
23 | public class FingerUtils {
24 | public static TableLogModel FingerFilter(int pid, String oneUrl, byte[] oneResponseBytes, IHttpService iHttpService, IExtensionHelpers helpers, int requestResponseIndex){
25 | TableLogModel logModel = new TableLogModel(pid, Utils.getUriFromUrl(oneUrl), "", "", "", "", "", false, "", iHttpService, requestResponseIndex, new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()));
26 |
27 | IResponseInfo responseInfo = helpers.analyzeResponse(oneResponseBytes);
28 | // 响应的body值
29 | String responseBody = new String(oneResponseBytes, StandardCharsets.UTF_8);
30 | // 响应的头部字段
31 | String responseHeaders = responseInfo.getHeaders().toString();
32 | // 提取title
33 | String responseTitle = Utils.getTitle(responseBody);
34 | // 提取mimeType
35 | String mimeType = responseInfo.getStatedMimeType().toLowerCase();
36 | if (responseTitle.isEmpty()) {
37 | responseTitle = responseBody;
38 | }
39 | String finalResponseTitle = responseTitle;
40 |
41 | String faviconHash = "0";
42 |
43 | if (finalResponseTitle.equals(responseBody)){
44 | logModel.setTitle("-");
45 | }
46 | else{
47 | logModel.setTitle(finalResponseTitle);
48 | }
49 | if (mimeType.contains("png") || mimeType.contains("jpeg") || mimeType.contains("icon") || mimeType.contains("image") || oneUrl.contains("favicon.") || oneUrl.contains(".ico")) {
50 | byte[] body = Arrays.copyOfRange(oneResponseBytes, responseInfo.getBodyOffset(), oneResponseBytes.length);
51 | faviconHash = Utils.getFaviconHash(body);
52 | BurpExtender.getStdout().println("The MurmurHash3 of the image is: " + oneUrl + ":" + faviconHash);
53 | }
54 |
55 |
56 | for (FingerPrintRule rule : BurpExtender.fingerprintRules) {
57 | // 看是否只对重点指纹进行匹配
58 | if (FingerConfigTab.allFingerprintsButton.isSelected() && !rule.getIsImportant()){
59 | continue;
60 | }
61 | String locationContent = "";
62 | if ("body".equals(rule.getLocation())) {
63 | locationContent = responseBody;
64 | } else if ("header".equals(rule.getLocation())) {
65 | locationContent = responseHeaders;
66 | } else if ("title".equals(rule.getLocation())) {
67 | locationContent = finalResponseTitle;
68 | }else{
69 | BurpExtender.getStderr().println("[!]指纹出现问题:" + rule.getLocation());
70 | }
71 | boolean allKeywordsPresent = true;
72 | if (mimeType.contains("png") || mimeType.contains("jpeg") || mimeType.contains("icon") || mimeType.contains("image") || oneUrl.contains("favicon.") || oneUrl.contains(".ico")) {
73 | // 进入图标匹配逻辑
74 | try {
75 | if (!(faviconHash.equals(rule.getKeyword().get(0)))){
76 | allKeywordsPresent = false;
77 | }
78 | } catch (Exception e) {
79 | BurpExtender.getStderr().println(e.getMessage());
80 | }
81 | }else{
82 | // 进入非图标匹配逻辑
83 | for (String keyword : rule.getKeyword()) {
84 | if (!locationContent.contains(keyword)) {
85 | allKeywordsPresent = false;
86 | break;
87 | }
88 | }
89 | }
90 |
91 | if (allKeywordsPresent) {
92 | if (!logModel.getResult().isEmpty()) {
93 | // 如果result键已经存在,那么获取它的值并进行拼接
94 | if (!logModel.getResult().contains(rule.getCms())){
95 | logModel.setResult(logModel.getResult() + ", " + rule.getCms());
96 | }
97 | } else {
98 | // 如果result键不存在,那么直接添加新的result
99 | logModel.setResult(rule.getCms());
100 | }
101 | String detailInfo = "Time: " + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()) + "\r\nUrl:" + oneUrl + "\r\n指纹详细信息如下:\r\n" + rule.getInfo();
102 | if (logModel.getResultInfo().isEmpty()){
103 | // 如果resultDetail键已经存在,那么获取它的值并进行拼接
104 | logModel.setResultInfo(detailInfo + "\r\n\r\n" + logModel.getResultInfo());
105 | }
106 | else{
107 | // 如果resultDetail键不存在,那么直接添加新的result
108 | logModel.setResultInfo(detailInfo);
109 | }
110 | if(logModel.getIsImportant()){
111 | if (rule.getIsImportant()){
112 | logModel.setIsImportant(rule.getIsImportant());
113 | }
114 | } else{
115 | logModel.setIsImportant(rule.getIsImportant());
116 | }
117 | if (!logModel.getType().isEmpty()) {
118 | // 如果result键已经存在,那么获取它的值并进行拼接
119 | if (logModel.getType().equals("-") && !rule.getType().equals("-")){
120 | logModel.setType(rule.getType());
121 | } else if (!logModel.getType().contains(rule.getType()) && !rule.getType().equals("-")) {
122 | logModel.setType(logModel.getType() + ", " + rule.getType());
123 | }
124 | } else {
125 | // 如果result键不存在,那么直接添加新的result
126 | logModel.setType(rule.getType());
127 | }
128 | }
129 | }
130 | return logModel;
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/main/java/burp/util/HTTPUtils.java:
--------------------------------------------------------------------------------
1 | package burp.util;
2 |
3 | import burp.*;
4 | import java.net.URL;
5 | import java.util.Arrays;
6 | import java.util.HashMap;
7 | import java.util.Map;
8 |
9 | /**
10 | * @author: shaun
11 | * @create: 2024/3/6 20:44
12 | * @description:TODO
13 | */
14 | public class HTTPUtils {
15 | public static Map makeGetRequest(String getUrl) {
16 | // 解析URL
17 | String host;
18 | int port;
19 | String protocol;
20 | String path;
21 | try {
22 | // 创建URL对象
23 | URL url = new URL(getUrl);
24 | // 获取protocol、host、port、path
25 | protocol = url.getProtocol();
26 | host = url.getHost();
27 | port = url.getPort();
28 | if (port == -1 && protocol.equalsIgnoreCase("http")){
29 | port = 80;
30 | } else if (port == -1 && protocol.equalsIgnoreCase("https")) {
31 | port = 443;
32 | }
33 | path = url.getPath();
34 | // 分析URL
35 | } catch (Exception e) {
36 | // 处理可能出现的MalformedURLException
37 | BurpExtender.getStdout().println("Invalid URL: " + getUrl);
38 | return null;
39 | }
40 | // 创建IHttpService对象
41 | IHttpService httpService = BurpExtender.getHelpers().buildHttpService(host, port, protocol);
42 |
43 | // 构造GET请求的字节数组
44 | String request = "GET " + path + " HTTP/1.1\r\n" +
45 | "Host: " + host + "\r\n" +
46 | "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36" + "\r\n" +
47 | "\r\n";
48 | byte[] requestBytes = request.getBytes();
49 |
50 | // 发起请求
51 | IHttpRequestResponse response = BurpExtender.getCallbacks().makeHttpRequest(httpService, requestBytes);
52 |
53 | // 当前请求的URL,requests,Response,以及findUrl来区别是否为提取出来的URL
54 | Map originalData = new HashMap();
55 | originalData.put("responseRequest", response);
56 | originalData.put("isFindUrl", true);
57 | originalData.put("method", "GET");
58 |
59 | return originalData;
60 |
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/burp/util/UiUtils.java:
--------------------------------------------------------------------------------
1 | package burp.util;
2 |
3 | import javax.swing.*;
4 | import java.awt.*;
5 | import java.net.URL;
6 |
7 | /**
8 | * @author: shaun
9 | * @create: 2024/3/27 21:21
10 | * @description:TODO
11 | */
12 | public class UiUtils {
13 | public static ImageIcon getImageIcon(String iconPath, int xWidth, int yWidth){
14 | // 根据按钮的大小缩放图标
15 | URL iconURL = UiUtils.class.getResource(iconPath);
16 | ImageIcon originalIcon = new ImageIcon(iconURL);
17 | Image img = originalIcon.getImage();
18 | Image newImg = img.getScaledInstance(xWidth, yWidth, Image.SCALE_SMOOTH);
19 | return new ImageIcon(newImg);
20 | }
21 |
22 | public static ImageIcon getImageIcon(String iconPath){
23 | // 根据按钮的大小缩放图标
24 | URL iconURL = UiUtils.class.getResource(iconPath);
25 | ImageIcon originalIcon = new ImageIcon(iconURL);
26 | Image img = originalIcon.getImage();
27 | Image newImg = img.getScaledInstance(17, 17, Image.SCALE_SMOOTH);
28 | return new ImageIcon(newImg);
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/burp/util/UrlScanCount.java:
--------------------------------------------------------------------------------
1 | package burp.util;
2 |
3 | import java.util.Map;
4 | import java.util.concurrent.ConcurrentHashMap;
5 |
6 | public class UrlScanCount {
7 |
8 | private final ConcurrentHashMap countMap;
9 |
10 | public UrlScanCount() {
11 | this.countMap = new ConcurrentHashMap<>();
12 | }
13 |
14 | public Map getStringMap() {
15 | return this.countMap;
16 | }
17 |
18 | public Integer get(String key) {
19 | Integer ret = this.countMap.get(key);
20 | if (ret == null) {
21 | return 0;
22 | } else {
23 | return ret;
24 | }
25 | }
26 |
27 | public void add(String key) {
28 | if (key == null || key.length() <= 0) {
29 | throw new IllegalArgumentException("Key 不能为空");
30 | }
31 |
32 | synchronized (this.getStringMap()) {
33 | this.countMap.put(key, (this.get(key) + 1));
34 | }
35 | }
36 |
37 | public void del(String key) {
38 | if (this.countMap.get(key) != null) {
39 | this.countMap.remove(key);
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/burp/util/Utils.java:
--------------------------------------------------------------------------------
1 | package burp.util;
2 |
3 | import burp.BurpExtender;
4 | import burp.IBurpExtenderCallbacks;
5 | import burp.IHttpService;
6 | import burp.model.TableLogModel;
7 |
8 | import java.util.*;
9 | import java.util.ArrayList;
10 | import javax.net.ssl.*;
11 | import java.security.cert.X509Certificate;
12 | import java.net.URL;
13 | import java.io.*;
14 | import java.net.*;
15 | import java.util.Base64;
16 | import java.util.List;
17 | import java.util.regex.Matcher;
18 | import java.util.regex.Pattern;
19 |
20 |
21 | /**
22 | * @author: shaun
23 | * @create: 2024/2/18 21:11
24 | * @description:TODO
25 | */
26 | public class Utils {
27 | // 静态文件直接过滤
28 | public final static String[] STATIC_FILE_EXT = new String[]{
29 | "png",
30 | "jpg",
31 | "jpeg",
32 | "gif",
33 | "pdf",
34 | "bmp",
35 | "css",
36 | "woff",
37 | "woff2",
38 | "ttf",
39 | "otf",
40 | "ttc",
41 | "svg",
42 | "psd",
43 | "exe",
44 | "zip",
45 | "rar",
46 | "7z",
47 | "msi",
48 | "tar",
49 | "gz",
50 | "mp3",
51 | "mp4",
52 | "mkv",
53 | "swf",
54 | "iso"
55 | };
56 |
57 | // 对以下URl提取URL
58 | public final static String[] STATIC_URl_EXT = new String[]{
59 | "js",
60 | "ppt",
61 | "pptx",
62 | "doc",
63 | "docx",
64 | "xls",
65 | "xlsx",
66 | "cvs"
67 | };
68 |
69 | // 不对下面URL进行指纹识别
70 | public final static String[] UNCEKCK_DOMAINS = new String[]{
71 | ".baidu.com",
72 | ".google.com",
73 | ".bing.com",
74 | ".yahoo.com",
75 | ".aliyun.com",
76 | ".alibaba.com"
77 | };
78 |
79 | public static String getBanner(){
80 | String bannerInfo =
81 | "[+] " + BurpExtender.extensionName + " is loaded\n"
82 | + "[+] #####################################\n"
83 | + "[+] " + BurpExtender.extensionName + " v" + BurpExtender.version +"\n"
84 | + "[+] anthor: " + BurpExtender.author + "\n"
85 | + "[+] ####################################\n"
86 | + "[+] Please enjoy it!";
87 | return bannerInfo;
88 | }
89 |
90 | public static boolean urlFilter(String url){
91 | return false;
92 | }
93 |
94 | public static String getTitle(String responseBody){
95 | Pattern pattern = Pattern.compile("(.*?)", Pattern.CASE_INSENSITIVE);
96 | Matcher matcher = pattern.matcher(responseBody);
97 | if (matcher.find()) {
98 | String responseTitle = matcher.group(1);
99 | return responseTitle;
100 | } else {
101 | return "";
102 | }
103 | }
104 |
105 | public static boolean isStaticFile(String url) {
106 | for (String ext : STATIC_FILE_EXT) {
107 | if (ext.equalsIgnoreCase(Utils.getUriExt(url))) return true;
108 | }
109 | return false;
110 | }
111 |
112 | public static boolean isGetUrlExt(String url){
113 | for (String ext : STATIC_URl_EXT){
114 | if (ext.equalsIgnoreCase(Utils.getUriExt(url))) return true;
115 | }
116 | return false;
117 | }
118 |
119 | public static boolean isWhiteDomain(String url){
120 | for (String uncheckDomains : UNCEKCK_DOMAINS){
121 | if (url.contains(uncheckDomains)) return true;
122 | }
123 | return false;
124 | }
125 |
126 |
127 | public static String getUriExt(String url) {
128 | String pureUrl = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
129 | return (pureUrl.lastIndexOf(".") > -1 ? pureUrl.substring(pureUrl.lastIndexOf(".") + 1) : "").toLowerCase();
130 | }
131 |
132 | public static boolean urlExistsInLog(List log, String url) {
133 | for (TableLogModel logEntry : log) {
134 | if (logEntry.getUrl().equals(url))
135 | return true;
136 | }
137 | return false;
138 | }
139 |
140 | public static String getUriFromUrl(String urlString) {
141 | // 匹配 "https://xxx/" 或 "http://xxx/" 或 "https://xxx" 或 "http://xxx" 的正则表达式
142 | String regex = "(https?://[^/]+/?)(?=/|$)";
143 | Pattern pattern = Pattern.compile(regex);
144 | Matcher matcher = pattern.matcher(urlString);
145 |
146 | if (matcher.find()) {
147 | return removeBackSlash(matcher.group(1));
148 | }
149 | else{
150 | return removeBackSlash(urlString);
151 | }
152 | }
153 |
154 | public static String removeBackSlash(String urlString){
155 | if (urlString.endsWith("/")) {
156 | return urlString.substring(0, urlString.length() - 1);
157 | } else {
158 | return urlString;
159 | }
160 | }
161 |
162 | public static String getFaviconHash(byte[] faviconBytes) {
163 |
164 | String base64Favicon = Base64.getEncoder().encodeToString(faviconBytes);
165 |
166 | // 格式化base64字符串
167 | String formattedBase64Favicon = formatBase64(base64Favicon);
168 |
169 | // 计算格式化后base64字符串的murmurHash3值
170 | return String.valueOf(murmurHash3_x86_32(formattedBase64Favicon.getBytes(), 0, formattedBase64Favicon.length(), 0));
171 | }
172 |
173 | public static int murmurHash3_x86_32(final byte[] data, int offset, int len, final int seed) {
174 | final int c1 = 0xcc9e2d51;
175 | final int c2 = 0x1b873593;
176 |
177 | int h1 = seed;
178 | final int roundedEnd = offset + (len & 0xfffffffc); // round down to 4 byte block
179 |
180 | for (int i = offset; i < roundedEnd; i += 4) {
181 | // little endian load order
182 | int k1 = (data[i] & 0xff) | ((data[i + 1] & 0xff) << 8) | ((data[i + 2] & 0xff) << 16) | (data[i + 3] << 24);
183 | k1 *= c1;
184 | k1 = Integer.rotateLeft(k1, 15);
185 | k1 *= c2;
186 |
187 | h1 ^= k1;
188 | h1 = Integer.rotateLeft(h1, 13);
189 | h1 = h1 * 5 + 0xe6546b64;
190 | }
191 |
192 | // handle the last few bytes of the input array
193 | int k1 = 0;
194 | switch (len & 0x03) {
195 | case 3:
196 | k1 = (data[roundedEnd + 2] & 0xff) << 16;
197 | // fall through
198 | case 2:
199 | k1 |= (data[roundedEnd + 1] & 0xff) << 8;
200 | // fall through
201 | case 1:
202 | k1 |= (data[roundedEnd] & 0xff);
203 | k1 *= c1;
204 | k1 = Integer.rotateLeft(k1, 15);
205 | k1 *= c2;
206 | h1 ^= k1;
207 | }
208 |
209 | // finalization
210 | h1 ^= len;
211 |
212 | // fmix
213 | h1 ^= h1 >>> 16;
214 | h1 *= 0x85ebca6b;
215 | h1 ^= h1 >>> 13;
216 | h1 *= 0xc2b2ae35;
217 | h1 ^= h1 >>> 16;
218 |
219 | return h1;
220 | }
221 | private static String formatBase64(String base64) {
222 | Pattern pattern = Pattern.compile(".{76}");
223 | Matcher matcher = pattern.matcher(base64);
224 | StringBuilder formattedBase64 = new StringBuilder();
225 |
226 | while (matcher.find()) {
227 | formattedBase64.append(matcher.group()).append("\n");
228 | }
229 |
230 | int remainder = base64.length() % 76;
231 | if (remainder > 0) {
232 | formattedBase64.append(base64.substring(base64.length() - remainder)).append("\n");
233 | }
234 |
235 | return formattedBase64.toString();
236 | }
237 |
238 | public static List findUrl(URL url, String js)
239 | {
240 | String pattern_raw = "(?:\"|')(((?:[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,}\\.(?:php|asp|aspx|jsp|json|action|html|js|txt|xml)(?:\\?[^\"|']{0,}|)))(?:\"|')";
241 | Pattern r = Pattern.compile(pattern_raw);
242 | Matcher m = r.matcher(js);
243 | int matcher_start = 0;
244 | List ex_urls = new ArrayList();
245 | while (m.find(matcher_start)){
246 | ex_urls.add(m.group(1).replaceAll("\"","").replaceAll("'","").replaceAll("\n","").replaceAll("\t","").trim());
247 | matcher_start = m.end();
248 | }
249 | LinkedHashSet hashSet = new LinkedHashSet<>(ex_urls);
250 | ArrayList temp_urls = new ArrayList<>(hashSet);
251 | List all_urls = new ArrayList<>();
252 | for(String temp_url:temp_urls){
253 | all_urls.add(process_url(url, temp_url));
254 | }
255 | List result = new ArrayList();
256 | for(String singerurl:all_urls){
257 | String domain = url.getHost();
258 | try {
259 | URL subURL = new URL(singerurl);
260 | String subdomain = subURL.getHost();
261 | if(!subdomain.equalsIgnoreCase(domain) && !isStaticFile(singerurl) && !singerurl.endsWith(".js") && !singerurl.contains(".js?") && !isWhiteDomain(singerurl) && !BurpExtender.hasScanDomainSet.contains(Utils.getUriFromUrl(singerurl))){
262 | BurpExtender.hasScanDomainSet.add(Utils.getUriFromUrl(singerurl));
263 | result.add(singerurl);
264 | }
265 |
266 | } catch (Exception e) {
267 | }
268 |
269 | }
270 | return result;
271 | }
272 |
273 | public static String process_url(URL url, String re_URL) {
274 | String black_url = "javascript:";
275 | String ab_URL = url.getHost() + ":"+ url.getPort();
276 | String host_URL = url.getProtocol();
277 | String result = "";
278 | if (re_URL.length() < 4) {
279 | if (re_URL.startsWith("//")) {
280 | result = host_URL + ":" + "//" + ab_URL + re_URL.substring(1);
281 | } else if (!re_URL.startsWith("//")) {
282 | if (re_URL.startsWith("/")) {
283 | result = host_URL + "://" + ab_URL + re_URL;
284 | } else {
285 | if (re_URL.startsWith(".")) {
286 | if (re_URL.startsWith("..")) {
287 | result = host_URL + "://" + ab_URL + re_URL.substring(2);
288 | } else {
289 | result = host_URL + "://" + ab_URL + re_URL.substring(1);
290 | }
291 | } else {
292 | result = host_URL + "://" + ab_URL + "/" + re_URL;
293 | }
294 |
295 | }
296 |
297 | }
298 | } else {
299 | if (re_URL.startsWith("//")) {
300 | result = host_URL + ":" + re_URL;
301 | } else if (re_URL.startsWith("http")) {
302 | result = re_URL;
303 | } else if (!re_URL.startsWith("//") && !re_URL.contains(black_url)) {
304 | if (re_URL.startsWith("/")) {
305 | result = host_URL + "://" + ab_URL + re_URL;
306 | } else {
307 | if (re_URL.startsWith(".")) {
308 | if (re_URL.startsWith("..")) {
309 | result = host_URL + "://" + ab_URL + re_URL.substring(2);
310 | } else {
311 | result = host_URL + "://" + ab_URL + re_URL.substring(1);
312 | }
313 | } else {
314 | result = host_URL + "://" + ab_URL + "/" + re_URL;
315 | }
316 |
317 | }
318 |
319 | } else {
320 | result = url.toString();
321 | }
322 | }
323 | return result;
324 |
325 | }
326 |
327 | public static List find_last(String string, String str)
328 | {
329 | List positions = new ArrayList();
330 | int last_position= -1;
331 | while(true){
332 | int position = string.lastIndexOf(str,last_position+1);
333 | if(position == -1){
334 | break;
335 | }
336 | last_position = position;
337 | positions.add(position);
338 | }
339 |
340 |
341 | return positions;
342 | }
343 |
344 | public static List extractUrlsFromHtml(String uri, String html) {
345 | // 使用正则表达式提取文本内容中的 URL
346 | List urlList = new ArrayList();
347 | Pattern pattern = Pattern.compile(
348 | "(http|https|ftp)://([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])?");
349 | Matcher matcher = pattern.matcher(html);
350 | while (matcher.find()) {
351 | String url = matcher.group();
352 | if (!url.contains("http") && url.startsWith("/")) {
353 | try {
354 | URI baseUri = new URI(uri);
355 | url = baseUri.resolve(url).toString();
356 | } catch (URISyntaxException e) {
357 | continue;
358 | }
359 | }
360 | try{
361 | String subdomain = (new URL(uri)).getHost() + ":" + (new URL(uri)).getPort();
362 | String domain = (new URL(url)).getHost() + ":" + (new URL(uri)).getPort();
363 | if (subdomain.equalsIgnoreCase(domain)){
364 | continue;
365 | }
366 | } catch (Exception e) {
367 | continue;
368 | }
369 |
370 | if (!isStaticFile(url) && !url.endsWith(".js") && !url.contains(".js?") && !isWhiteDomain(url) && !BurpExtender.hasScanDomainSet.contains(Utils.getUriFromUrl(url))){
371 | BurpExtender.hasScanDomainSet.add(Utils.getUriFromUrl(url));
372 | urlList.add(url);
373 | }
374 | }
375 | return urlList;
376 | }
377 |
378 | public static void trustAllCertificates() throws Exception {
379 | TrustManager[] trustAllCerts = new TrustManager[]{
380 | new X509TrustManager() {
381 | public X509Certificate[] getAcceptedIssuers() {
382 | return new X509Certificate[0];
383 | }
384 | public void checkClientTrusted(
385 | X509Certificate[] certs, String authType) {
386 | }
387 | public void checkServerTrusted(
388 | X509Certificate[] certs, String authType) {
389 | }
390 | }
391 | };
392 |
393 | // Install the all-trusting trust manager
394 | SSLContext sc = SSLContext.getInstance("SSL");
395 | sc.init(null, trustAllCerts, new java.security.SecureRandom());
396 | HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
397 |
398 | // Create all-trusting host name verifier
399 | HostnameVerifier allHostsValid = new HostnameVerifier() {
400 | public boolean verify(String hostname, SSLSession session) {
401 | return true;
402 | }
403 | };
404 |
405 | // Install the all-trusting host verifier
406 | HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
407 | }
408 |
409 |
410 |
411 | /**
412 | * 获取-插件运行路径
413 | *
414 | * @return
415 | */
416 | public static String getExtensionFilePath(IBurpExtenderCallbacks callbacks) {
417 | String path = "";
418 | Integer lastIndex = callbacks.getExtensionFilename().lastIndexOf(File.separator);
419 | path = callbacks.getExtensionFilename().substring(0, lastIndex) + File.separator;
420 | return path;
421 | }
422 |
423 | public static IHttpService iHttpService(String host, int port, String protocol){
424 | return new IHttpService() {
425 | @Override
426 | public String getHost() {
427 | return host;
428 | }
429 |
430 | @Override
431 | public int getPort() {
432 | return port;
433 | }
434 |
435 | @Override
436 | public String getProtocol() {
437 | return protocol;
438 | }
439 | };
440 | }
441 |
442 | public static String escapeCsv(String value) {
443 | String escapedValue = value;
444 | if (value.contains("\"")) {
445 | // 将所有的双引号替换成两个双引号
446 | escapedValue = escapedValue.replace("\"", "\"\"");
447 | }
448 | // 如果值包含逗号、双引号或换行符,将其包围在双引号之中
449 | if (value.contains(",") || value.contains("\n") || value.contains("\"")) {
450 | escapedValue = "\"" + escapedValue + "\"";
451 | }
452 | return escapedValue;
453 | }
454 |
455 | }
456 |
--------------------------------------------------------------------------------
/src/main/java/burp/weakpassword/TomcatWeakPassword.java:
--------------------------------------------------------------------------------
1 | package burp.weakpassword;
2 |
3 | import burp.BurpExtender;
4 | import burp.IHttpRequestResponse;
5 | import burp.IHttpService;
6 | import burp.model.WeakPassword;
7 | import burp.util.Utils;
8 |
9 | import java.net.URL;
10 | import java.nio.charset.StandardCharsets;
11 | import java.util.List;
12 | import java.util.Arrays;
13 | import java.util.Base64;
14 |
15 | /**
16 | * @author: shaun
17 | * @create: 2024/5/4 16:11
18 | * @description:TODO
19 | */
20 | public class TomcatWeakPassword {
21 | private static final String Finger = "Apache Tomcat";
22 | private static final List weakCredentials = Arrays.asList(
23 | "tomcat:tomcat",
24 | "admin:admin",
25 | "tomcat:s3cret",
26 | "tomcat:password",
27 | "amdin:password"
28 | );
29 |
30 | private static Integer port = null;
31 | private static String protocol = null;
32 | private static String host = null;
33 | private static final String checkSuccessByBody = "Tomcat Web应用程序管理者";
34 | // 构造GET请求的字节数组
35 | static String tomcat_request = "GET /manager/html HTTP/1.1\r\n" +
36 | "Host: {host:port}\r\n" +
37 | "Authorization: Basic {payload}" + "\r\n" +
38 | "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36" + "\r\n" +
39 | "\r\n";
40 |
41 | public static WeakPassword checkWeakPasswords(WeakPassword wp) {
42 | String checkUrl = Utils.getUriFromUrl(wp.getUrl());
43 | StringBuilder result_info = new StringBuilder();
44 | try {
45 | // 创建URL对象
46 | URL url = new URL(checkUrl);
47 | // 获取protocol、host、port、path
48 | protocol = url.getProtocol();
49 | host = url.getHost();
50 | port = url.getPort();
51 | if (port == -1 && protocol.equalsIgnoreCase("http")){
52 | port = 80;
53 | } else if (port == -1 && protocol.equalsIgnoreCase("https")) {
54 | port = 443;
55 | }
56 | } catch (Exception e) {
57 | wp.setResultInfo("Invalid URL: " + checkUrl);
58 | return wp;
59 | }
60 | int testNumber = 0;
61 | for (String credential : weakCredentials) {
62 | testNumber += 1;
63 | String encodedCredential = Base64.getEncoder().encodeToString(credential.getBytes(StandardCharsets.UTF_8));
64 | String requests_data = tomcat_request.replace("{host:port}", host + ":" + port).replace("{payload}", encodedCredential);
65 | // 创建IHttpService对象
66 | IHttpService httpService = BurpExtender.getHelpers().buildHttpService(host, port, protocol);
67 | // 发起请求
68 | IHttpRequestResponse requestResponse = null;
69 | try {
70 | // 发起请求
71 | requestResponse = BurpExtender.getCallbacks().makeHttpRequest(httpService, requests_data.getBytes(StandardCharsets.UTF_8));
72 | // 空检查
73 | if (requestResponse == null || requestResponse.getResponse() == null) {
74 | throw new IllegalStateException("Request failed, no response received.");
75 | }
76 |
77 | // 获取响应字节
78 | byte[] responseBytes = requestResponse.getResponse();
79 | String statusCode = String.valueOf(BurpExtender.getCallbacks().getHelpers().analyzeResponse(responseBytes).getStatusCode());
80 | if (checkSuccess(responseBytes)){
81 | result_info.append("## 测试项:").append(credential).append("\r\n请求包为:\r\n").append(requests_data).append("\r\n响应包为:\r\n").append(new String(responseBytes.length > 10000 ? Arrays.copyOf(responseBytes, 10000) : responseBytes, StandardCharsets.UTF_8)).append("\r\n测试结果为: 爆破成功,状态码为:").append(statusCode).append("\r\n\r\n");
82 | wp.setWeakPassword(credential);
83 | wp.setStatus("爆破成功");
84 | wp.setTestNumber(String.valueOf(testNumber));
85 | wp.setRequestsByte(requests_data.getBytes());
86 | wp.setResponseByte(responseBytes);
87 | break;
88 | }
89 | result_info.append("## 测试项:").append(credential).append("\r\n请求包为:\r\n").append(requests_data).append("\r\n响应包为:\r\n").append(new String(responseBytes.length > 10000 ? Arrays.copyOf(responseBytes, 10000) : responseBytes, StandardCharsets.UTF_8)).append("\r\n测试结果为: 爆破失败,状态码为:").append(statusCode).append("\r\n\r\n");
90 |
91 | } catch (Exception e) {
92 | result_info.append("## 测试项:").append(credential).append("\r\n测试结果报错: ").append(e.getMessage()).append("\r\n\r\n");
93 | }
94 | }
95 | if (!wp.getStatus().equals("爆破成功")){
96 | wp.setStatus("爆破失败");
97 | wp.setRequestsByte(tomcat_request.getBytes());
98 | wp.setTestNumber(String.valueOf(weakCredentials.size()));
99 | }
100 | wp.setResultInfo(result_info.toString());
101 |
102 | return wp;
103 |
104 | }
105 |
106 | public static boolean checkSuccess(byte[] responseBytes){
107 | String responses = new String(responseBytes, StandardCharsets.UTF_8);
108 | if (responses.contains(checkSuccessByBody)){
109 | return true;
110 | }else{
111 | return false;
112 | }
113 | }
114 | }
115 |
116 |
--------------------------------------------------------------------------------
/src/main/resources/icon/WeakPasswordBlasting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/src/main/resources/icon/WeakPasswordBlasting.png
--------------------------------------------------------------------------------
/src/main/resources/icon/WeakPasswordBlastingFalse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/src/main/resources/icon/WeakPasswordBlastingFalse.png
--------------------------------------------------------------------------------
/src/main/resources/icon/addButtonIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/src/main/resources/icon/addButtonIcon.png
--------------------------------------------------------------------------------
/src/main/resources/icon/allButtonIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/src/main/resources/icon/allButtonIcon.png
--------------------------------------------------------------------------------
/src/main/resources/icon/deleteButton.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/src/main/resources/icon/deleteButton.png
--------------------------------------------------------------------------------
/src/main/resources/icon/editButton.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/src/main/resources/icon/editButton.png
--------------------------------------------------------------------------------
/src/main/resources/icon/exportItem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/src/main/resources/icon/exportItem.png
--------------------------------------------------------------------------------
/src/main/resources/icon/filterIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/src/main/resources/icon/filterIcon.png
--------------------------------------------------------------------------------
/src/main/resources/icon/flashButton.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/src/main/resources/icon/flashButton.png
--------------------------------------------------------------------------------
/src/main/resources/icon/importItem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/src/main/resources/icon/importItem.png
--------------------------------------------------------------------------------
/src/main/resources/icon/importantButtonIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/src/main/resources/icon/importantButtonIcon.png
--------------------------------------------------------------------------------
/src/main/resources/icon/moreButton.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/src/main/resources/icon/moreButton.png
--------------------------------------------------------------------------------
/src/main/resources/icon/normalIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/src/main/resources/icon/normalIcon.png
--------------------------------------------------------------------------------
/src/main/resources/icon/openButtonIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/src/main/resources/icon/openButtonIcon.png
--------------------------------------------------------------------------------
/src/main/resources/icon/resetItem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/src/main/resources/icon/resetItem.png
--------------------------------------------------------------------------------
/src/main/resources/icon/runningButton.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/src/main/resources/icon/runningButton.png
--------------------------------------------------------------------------------
/src/main/resources/icon/saveButton.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/src/main/resources/icon/saveButton.png
--------------------------------------------------------------------------------
/src/main/resources/icon/saveItem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/src/main/resources/icon/saveItem.png
--------------------------------------------------------------------------------
/src/main/resources/icon/searchButton.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/src/main/resources/icon/searchButton.png
--------------------------------------------------------------------------------
/src/main/resources/icon/shutdownButtonIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/src/main/resources/icon/shutdownButtonIcon.png
--------------------------------------------------------------------------------
/src/main/resources/icon/weakPasswordSuccess.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shuanx/BurpFingerPrint/de5701054e174f86499433fd26fd2fb1e7e27d50/src/main/resources/icon/weakPasswordSuccess.png
--------------------------------------------------------------------------------