├── .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 | 8 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 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 | ![](https://img.shields.io/badge/Author-Shaun-blue) 3 | ![](https://img.shields.io/badge/JDK-9+-yellow) 4 | ![](https://img.shields.io/badge/捡漏是门艺术-往往有意想不到的成果-red) 5 | > 攻击过程中,我们通常会用浏览器访问一些资产,该BurpSuite插件实现被动指纹识别+网站提取链接+OA爆破,可帮助我们发现更多资产。 6 | 7 | ## 功能如下 8 | > 下述功能会在2024年5月底完成,如果有更好的建议都可以提,然后再麻烦点个Star,创作不易,打造最强指纹识别库和弱口令探测库 9 | - [x] 浏览器被动指纹识别,已集成Ehole指纹识别库 10 | - [x] 提取网站的URL链接和解析JS文件中的URL链接后进行指纹识别 11 | - [x] 开界面进行指纹库修改,可导入、导出、重置 12 | - [x] 优化已有指纹库,区分重点指纹和常见指纹,补充部分实战热门漏洞的指纹,方便直接一键getshell 13 | ![img.png](images/importantFinger.png) 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 | img.png 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 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 --------------------------------------------------------------------------------