├── .idea ├── .gitignore ├── compiler.xml ├── encodings.xml ├── jarRepositories.xml ├── misc.xml ├── uiDesigner.xml └── vcs.xml ├── README.md ├── pom.xml └── src └── main ├── java └── AutoBurp │ ├── bypass │ ├── BypassBotDetection.java │ ├── Constants.java │ ├── DomainSettingsManager.java │ ├── DomainSettingsPanel.java │ ├── TLSContextMenuItemsProvider.java │ ├── TriggerCipherGuesser.java │ ├── Utilities.java │ └── beens │ │ ├── Browsers.java │ │ ├── ClientCertificate.java │ │ ├── DomainSettings.java │ │ ├── HTTP.java │ │ ├── HTTP2.java │ │ ├── MatchAndReplace.java │ │ ├── Negotiation.java │ │ ├── OS.java │ │ ├── ProjectOptions.java │ │ ├── Proxy.java │ │ ├── ProxySettings.java │ │ ├── SSL.java │ │ ├── TLSNegotiation.java │ │ └── TLSSettings.java │ ├── fingerprint │ ├── FingerPrintScanner.java │ ├── model │ │ ├── FingerPrintRule.java │ │ └── TableLogModel.java │ ├── ui │ │ ├── ControlPanel.java │ │ ├── FingerPrintRulePanel.java │ │ ├── FingerPrintTab.java │ │ ├── LogTablePanel.java │ │ ├── RequestResponsePanel.java │ │ ├── TagsPanel.java │ │ ├── WrapLayout.java │ │ └── renderer │ │ │ ├── CenterRenderer.java │ │ │ └── HeaderIconRenderer.java │ └── util │ │ ├── FingerPrintUtils.java │ │ ├── HTTPUtils.java │ │ └── Utils.java │ ├── fuzzer │ ├── phone │ │ └── PhoneFuzzer.java │ └── upload │ │ └── UploadFuzzer.java │ └── generator │ ├── PhonePayloadGeneratorFactory.java │ └── UploadPayloadGenerator.java └── resources ├── conf └── finger-important.json ├── project_options.json └── strings.properties /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # 默认忽略的文件 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /.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 | # AutoBurpFuzz 2 | 3 | # 简介 4 | 1.tls and http 浏览器指纹绕过 5 | 6 | 2.被动指纹扫描 7 | 8 | 3.文件上传 Intruder fuzz 9 | 10 | 4.手机号 Intruder fuzz 11 | 12 | # 本人环境参考 13 | > burp环境burpsuite 2025.2 14 | > 15 | > 工具环境: 16 | > java17编写 17 | > java17编译 18 | 19 | # tls指纹绕过使用方法 20 | 1.将需要绕过的域名,直接添加既可 21 | 22 | ![image](https://github.com/user-attachments/assets/a02d0a3d-8800-4ae3-9810-72d569858ca8) 23 | 24 | 25 | # upload_fuzz使用方法 26 | 1.成功加载该插件,smb_fuzz和下面同理 27 | 28 | 2.将需要fuzz的包,传送到Intruder中 29 | 30 | 3.设置这种部位为payload地址 31 | 32 | d7ddb43e46fa0a43360b7acd9741c31 33 | 34 | 4.然后设置如下内容 35 | 36 | 5bb289c7ea7184f13ce952090852061 37 | 38 | 39 | 40 | # 参考项目 41 | > https://github.com/T3nk0/Upload_Auto_Fuzz 42 | > 43 | > https://github.com/shuanx/BurpFingerPrint 44 | > 45 | > https://github.com/yuziiiiiiiiii/SMS_Bomb_Fuzzer 46 | > 47 | > https://github.com/PortSwigger/bypass-bot-detection 48 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.example 8 | AutoBurpFuzz 9 | 2.0-SNAPSHOT 10 | 11 | 12 | 17 13 | 17 14 | UTF-8 15 | 16 | 17 | 18 | net.portswigger.burp.extender 19 | burp-extender-api 20 | 1.7.22 21 | 22 | 23 | com.google.code.gson 24 | gson 25 | 2.10.1 26 | 27 | 28 | net.portswigger.burp.extensions 29 | montoya-api 30 | 2024.12 31 | 32 | 33 | 34 | 35 | 36 | org.apache.maven.plugins 37 | maven-assembly-plugin 38 | 39 | 40 | package 41 | 42 | single 43 | 44 | 45 | 46 | 47 | 48 | jar-with-dependencies 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/main/java/AutoBurp/bypass/BypassBotDetection.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.bypass; 2 | 3 | import AutoBurp.fingerprint.FingerPrintScanner; 4 | import AutoBurp.fingerprint.model.FingerPrintRule; 5 | import AutoBurp.fingerprint.ui.FingerPrintTab; 6 | import AutoBurp.fuzzer.phone.PhoneFuzzer; 7 | import AutoBurp.fuzzer.upload.UploadFuzzer; 8 | import burp.*; 9 | import burp.api.montoya.BurpExtension; 10 | import burp.api.montoya.MontoyaApi; 11 | import AutoBurp.bypass.beens.Browsers; 12 | import AutoBurp.bypass.beens.MatchAndReplace; 13 | import com.google.gson.Gson; 14 | import com.google.gson.reflect.TypeToken; 15 | 16 | import java.io.*; 17 | import java.lang.reflect.Type; 18 | import java.nio.charset.StandardCharsets; 19 | import java.util.*; 20 | import java.util.concurrent.BlockingQueue; 21 | import java.util.concurrent.Executors; 22 | import java.util.concurrent.LinkedBlockingQueue; 23 | import java.util.concurrent.ScheduledExecutorService; 24 | import java.util.concurrent.ThreadPoolExecutor; 25 | import java.util.concurrent.TimeUnit; 26 | import java.util.concurrent.atomic.AtomicInteger; 27 | 28 | public class BypassBotDetection implements BurpExtension, IBurpExtender, IIntruderPayloadGeneratorFactory, IProxyListener, IExtensionStateListener { 29 | private MontoyaApi montoyaApi; 30 | private IExtensionHelpers helpers; 31 | private IBurpExtenderCallbacks callbacks; 32 | private PrintWriter stdout; 33 | private PrintWriter stderr; 34 | private FingerPrintTab fingerPrintTab; 35 | private FingerPrintScanner fingerPrintScanner; 36 | private List fingerprintRules; 37 | private Set scannedUrls = Collections.synchronizedSet(new HashSet<>()); 38 | private final ScheduledExecutorService memoryMonitor = Executors.newSingleThreadScheduledExecutor(); 39 | private final AtomicInteger requestCount = new AtomicInteger(0); 40 | private final AtomicInteger successCount = new AtomicInteger(0); 41 | 42 | @Override 43 | public void initialize(MontoyaApi montoyaApi) { 44 | this.montoyaApi = montoyaApi; 45 | montoyaApi.extension().setName("综合Bypass"); 46 | try { 47 | new Utilities(montoyaApi); 48 | BlockingQueue tasks = new LinkedBlockingQueue<>(); 49 | ThreadPoolExecutor taskEngine = new ThreadPoolExecutor(1, 1, 1, TimeUnit.MINUTES, tasks); 50 | Utilities.saveTLSSettings(); 51 | 52 | 53 | DomainSettingsManager.initialize(montoyaApi); 54 | 55 | 56 | montoyaApi.userInterface().registerContextMenuItemsProvider(new TLSContextMenuItemsProvider(taskEngine, montoyaApi)); 57 | 58 | 59 | DomainSettingsPanel settingsPanel = new DomainSettingsPanel(montoyaApi); 60 | montoyaApi.userInterface().registerSuiteTab("TLS WAF", settingsPanel); 61 | 62 | montoyaApi.extension().registerUnloadingHandler(()-> { 63 | Utilities.unloaded.set(true); 64 | try { 65 | taskEngine.getQueue().clear(); 66 | taskEngine.shutdown(); 67 | 68 | 69 | memoryMonitor.shutdownNow(); 70 | if (fingerPrintScanner != null) { 71 | fingerPrintScanner.shutdown(); 72 | } 73 | }finally { 74 | Utilities.loadTLSSettings(); 75 | 76 | } 77 | }); 78 | 79 | Thread thread = new Thread(() -> { 80 | try { 81 | Thread.sleep(3000); 82 | } catch (InterruptedException e) { 83 | Thread.currentThread().interrupt(); 84 | } 85 | 86 | Utilities.updateTLSSettings(Constants.BROWSERS_PROTOCOLS.get(Browsers.FIREFOX.name), Constants.BROWSERS_CIPHERS.get(Browsers.FIREFOX.name)); 87 | Utilities.updateProxySettings(MatchAndReplace.create(Browsers.FIREFOX)); 88 | }); 89 | thread.start(); 90 | 91 | montoyaApi.logging().logToOutput("Auto Fuzz & FingerPrint loaded successfully - Author: e0e1 - Version: 2.0"); 92 | montoyaApi.logging().logToOutput("github: https://github.com/eeeeeeeeee-code/AutoFuzzBurp"); 93 | 94 | } catch (Exception e) { 95 | montoyaApi.logging().logToError(e.getMessage()); 96 | } 97 | } 98 | 99 | @Override 100 | public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) { 101 | this.callbacks = callbacks; 102 | this.helpers = callbacks.getHelpers(); 103 | this.stdout = new PrintWriter(callbacks.getStdout(), true); 104 | this.stderr = new PrintWriter(callbacks.getStderr(), true); 105 | callbacks.setExtensionName("Bot Detection Bypass & Auto Fuzz"); 106 | 107 | 108 | callbacks.registerIntruderPayloadGeneratorFactory(this); 109 | 110 | 111 | PhoneFuzzer phoneFuzzer = new PhoneFuzzer(this.helpers, callbacks); 112 | callbacks.registerIntruderPayloadGeneratorFactory(phoneFuzzer); 113 | 114 | 115 | loadFingerPrintRules(); 116 | 117 | 118 | fingerPrintTab = new FingerPrintTab(callbacks, helpers); 119 | callbacks.addSuiteTab(fingerPrintTab); 120 | 121 | 122 | fingerPrintTab.setRulePanel(fingerprintRules); 123 | 124 | 125 | fingerPrintScanner = new FingerPrintScanner(callbacks, helpers, fingerprintRules, fingerPrintTab, scannedUrls, requestCount, successCount); 126 | 127 | 128 | fingerPrintTab.setScanner(fingerPrintScanner); 129 | 130 | 131 | callbacks.registerProxyListener(this); 132 | 133 | 134 | callbacks.registerExtensionStateListener(this); 135 | 136 | 137 | startMemoryMonitor(); 138 | } 139 | 140 | private void loadFingerPrintRules() { 141 | try { 142 | 143 | File localFile = new File("finger-important.json"); 144 | InputStream inputStream = null; 145 | 146 | if (localFile.exists() && localFile.isFile()) { 147 | 148 | inputStream = new FileInputStream(localFile); 149 | 150 | } else { 151 | 152 | ClassLoader classLoader = getClass().getClassLoader(); 153 | inputStream = classLoader.getResourceAsStream("conf/finger-important.json"); 154 | 155 | if (inputStream != null) { 156 | 157 | try (InputStream resourceStream = classLoader.getResourceAsStream("conf/finger-important.json"); 158 | FileOutputStream outputStream = new FileOutputStream(localFile)) { 159 | 160 | if (resourceStream != null) { 161 | byte[] buffer = new byte[1024]; 162 | int length; 163 | while ((length = resourceStream.read(buffer)) > 0) { 164 | outputStream.write(buffer, 0, length); 165 | } 166 | 167 | 168 | 169 | inputStream = new FileInputStream(localFile); 170 | } 171 | } catch (Exception e) { 172 | stderr.println("[!] 复制资源文件到当前目录失败: " + e.getMessage()); 173 | } 174 | } 175 | } 176 | 177 | if (inputStream == null) { 178 | stderr.println("[!] 无法加载指纹规则文件"); 179 | fingerprintRules = new ArrayList<>(); 180 | return; 181 | } 182 | 183 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { 184 | Gson gson = new Gson(); 185 | Type fingerprintRuleListType = new TypeToken>>(){}.getType(); 186 | Map> rulesWrapper = gson.fromJson(reader, fingerprintRuleListType); 187 | fingerprintRules = rulesWrapper.get("fingerprint"); 188 | 189 | } 190 | } catch (Exception e) { 191 | stderr.println("[!] 加载指纹规则失败: " + e.getMessage()); 192 | e.printStackTrace(stderr); 193 | fingerprintRules = new ArrayList<>(); 194 | } 195 | } 196 | 197 | private void startMemoryMonitor() { 198 | memoryMonitor.scheduleAtFixedRate(() -> { 199 | Runtime runtime = Runtime.getRuntime(); 200 | long usedMemory = runtime.totalMemory() - runtime.freeMemory(); 201 | long maxMemory = runtime.maxMemory(); 202 | double memoryUsageRatio = (double) usedMemory / maxMemory; 203 | 204 | if (memoryUsageRatio > 0.7) { 205 | System.gc(); 206 | } 207 | }, 30, 30, TimeUnit.SECONDS); 208 | } 209 | 210 | @Override 211 | public void processProxyMessage(boolean messageIsRequest, IInterceptedProxyMessage message) { 212 | if (!messageIsRequest) { 213 | fingerPrintScanner.processMessage(message); 214 | } 215 | } 216 | 217 | @Override 218 | public void extensionUnloaded() { 219 | memoryMonitor.shutdownNow(); 220 | 221 | if (fingerPrintScanner != null) { 222 | fingerPrintScanner.shutdown(); 223 | } 224 | 225 | 226 | 227 | 228 | } 229 | 230 | @Override 231 | public String getGeneratorName() { 232 | return "Upload Auto Fuzz"; 233 | } 234 | 235 | @Override 236 | public IIntruderPayloadGenerator createNewInstance(IIntruderAttack attack) { 237 | return new UploadFuzzer(this.helpers, attack, this.callbacks); 238 | } 239 | } -------------------------------------------------------------------------------- /src/main/java/AutoBurp/bypass/Constants.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.bypass; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class Constants { 7 | 8 | 9 | public static int MAX_ATTEMPTS = 3; 10 | public static String BURP_TLS_NEGOTIATION = "use_custom"; 11 | public static String MATCH_AND_REPLACE_RULE_TYPE = "request_header"; 12 | public static String MATCH_AND_REPLACE_REGEXP = "^User-Agent.*$"; 13 | 14 | public static String[] MATCH_AND_REPLACE_REGEXP_HTTP2 = new String[] { 15 | "^Sec-Ch-Ua:.*$", 16 | "^Sec-Ch-Ua-Mobile:.*$", 17 | "^Sec-Ch-Ua-Platform:.*$", 18 | "^Sec-Fetch-Site:.*$", 19 | "^Sec-Fetch-Mode:.*$", 20 | "^Sec-Fetch-User:.*$", 21 | "^Sec-Fetch-Dest:.*$", 22 | "^Sec-CH-UA-Arch:.*$", 23 | "^Sec-CH-UA-Bitness:.*$", 24 | "^Sec-CH-UA-Model:.*$", 25 | "^Sec-CH-UA-Platform-Version:.*$", 26 | "^Sec-CH-UA-Form-Factors:.*$", 27 | "^Sec-CH-UA-Full-Version-List:.*$", 28 | "^Sec-CH-UA-WoW64:.*$", 29 | "^Priority:.*$" 30 | }; 31 | public static String FROZEN_UA = "User-Agent: Mozilla/5.0 (%s) %s"; 32 | 33 | public static Map FIREFOX_PLATFORMS = Map.of( 34 | "Windows", 35 | "Windows NT 10.0; Win64; x64; rv:134.0", 36 | "Mac", 37 | "Macintosh; Intel Mac OS X 14.7; rv:134.0", 38 | "Linux", 39 | "X11; Linux x86_64; rv:134.0"); 40 | public static Map CHROME_PLATFORMS = Map.of( 41 | "Windows", 42 | "Windows NT 10.0; Win64; x64", 43 | "Mac", 44 | "Macintosh; Intel Mac OS X 10_15_7", 45 | "Linux", 46 | "X11; Linux x86_64"); 47 | public static Map SAFARI_PLATFORMS = Map.of( 48 | "Windows", 49 | "Macintosh; Intel Mac OS X 10_15_7", 50 | "Mac", 51 | "Macintosh; Intel Mac OS X 10_15_7", 52 | "Linux", 53 | "Macintosh; Intel Mac OS X 10_15_7"); 54 | 55 | public static Map> BROWSERS_PLATFORMS = Map.of( 56 | "Firefox", FIREFOX_PLATFORMS, 57 | "Chrome", CHROME_PLATFORMS, 58 | "Safari", SAFARI_PLATFORMS 59 | ); 60 | 61 | public static Map BROWSERS_USER_AGENTS = Map.of( 62 | "Firefox", "User-Agent: Mozilla/5.0 (%s) Gecko/20100101 Firefox/134.0", 63 | "Chrome", "User-Agent: Mozilla/5.0 (%s) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36", 64 | "Safari", "User-Agent: Mozilla/5.0 (%s) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.2 Safari/605.1.15" 65 | ); 66 | public static Map BROWSERS_PROTOCOLS = Map.of( 67 | "Firefox", new String[]{"TLSv1.2", "TLSv1.3"}, 68 | "Chrome", new String[]{"TLSv1.2", "TLSv1.3"}, 69 | 70 | "Safari", new String[]{"TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1"} 71 | ); 72 | public static Map BROWSERS_CIPHERS = Map.of( 73 | "Firefox", new String[]{ 74 | "TLS_AES_128_GCM_SHA256", 75 | "TLS_CHACHA20_POLY1305_SHA256", 76 | "TLS_AES_256_GCM_SHA384", 77 | "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", 78 | "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 79 | "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", 80 | "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", 81 | "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 82 | "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", 83 | "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", 84 | "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", 85 | "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", 86 | "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", 87 | "TLS_RSA_WITH_AES_128_GCM_SHA256", 88 | "TLS_RSA_WITH_AES_256_GCM_SHA384", 89 | "TLS_RSA_WITH_AES_128_CBC_SHA", 90 | "TLS_RSA_WITH_AES_256_CBC_SHA"}, 91 | "Chrome", new String[]{ 92 | "TLS_AES_128_GCM_SHA256", 93 | "TLS_AES_256_GCM_SHA384", 94 | "TLS_CHACHA20_POLY1305_SHA256", 95 | "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", 96 | "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 97 | "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 98 | "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", 99 | "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", 100 | "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", 101 | "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", 102 | "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", 103 | "TLS_RSA_WITH_AES_128_GCM_SHA256", 104 | "TLS_RSA_WITH_AES_256_GCM_SHA384", 105 | "TLS_RSA_WITH_AES_128_CBC_SHA", 106 | "TLS_RSA_WITH_AES_256_CBC_SHA" 107 | }, 108 | "Safari", new String[]{ 109 | "TLS_AES_128_GCM_SHA256", 110 | "TLS_AES_256_GCM_SHA384", 111 | "TLS_CHACHA20_POLY1305_SHA256", 112 | "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 113 | "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", 114 | "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", 115 | "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", 116 | "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 117 | "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", 118 | "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", 119 | "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", 120 | "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", 121 | "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", 122 | "TLS_RSA_WITH_AES_256_GCM_SHA384", 123 | "TLS_RSA_WITH_AES_128_GCM_SHA256", 124 | "TLS_RSA_WITH_AES_256_CBC_SHA", 125 | "TLS_RSA_WITH_AES_128_CBC_SHA", 126 | "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", 127 | "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", 128 | "SSL_RSA_WITH_3DES_EDE_CBC_SHA" 129 | } 130 | ); 131 | 132 | 133 | public static Map BRUTEFORCE_CIPHERS = Map.of( 134 | new String[]{"TLSv1.3"}, new String[]{ 135 | "TLS_AES_128_GCM_SHA256", 136 | "TLS_AES_256_GCM_SHA384", 137 | "TLS_CHACHA20_POLY1305_SHA256"}, 138 | new String[]{"TLSv1.2"}, new String[]{ 139 | "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", 140 | "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", 141 | "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 142 | "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", 143 | "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", 144 | "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", 145 | "TLS_RSA_WITH_AES_128_GCM_SHA256", 146 | "TLS_RSA_WITH_AES_128_CBC_SHA256", 147 | "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 148 | "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", 149 | "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", 150 | "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", 151 | "TLS_RSA_WITH_AES_256_GCM_SHA384", 152 | "TLS_RSA_WITH_AES_256_CBC_SHA256" }, 153 | new String[]{"TLSv1"}, new String[]{ 154 | "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", 155 | "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", 156 | "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", 157 | "TLS_RSA_WITH_AES_128_CBC_SHA", 158 | "TLS_RSA_WITH_AES_256_CBC_SHA", 159 | "SSL_RSA_WITH_3DES_EDE_CBC_SHA"}, 160 | 161 | new String[]{"TLSv1.2", "TLSv1.3"},new String[]{ 162 | "TLS_AES_128_GCM_SHA256", 163 | "TLS_AES_256_GCM_SHA384", 164 | "TLS_CHACHA20_POLY1305_SHA256", 165 | "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", 166 | "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 167 | "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 168 | "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", 169 | "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", 170 | "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", 171 | "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", 172 | "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", 173 | "TLS_RSA_WITH_AES_128_GCM_SHA256", 174 | "TLS_RSA_WITH_AES_256_GCM_SHA384", 175 | "TLS_RSA_WITH_AES_128_CBC_SHA", 176 | "TLS_RSA_WITH_AES_256_CBC_SHA" 177 | }, 178 | 179 | new String[]{"TLSv1.2", "TLSv1.3"}, new String[]{ 180 | "TLS_AES_128_GCM_SHA256", 181 | "TLS_CHACHA20_POLY1305_SHA256", 182 | "TLS_AES_256_GCM_SHA384", 183 | "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", 184 | "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 185 | "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", 186 | "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", 187 | "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 188 | "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", 189 | "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", 190 | "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", 191 | "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", 192 | "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", 193 | "TLS_RSA_WITH_AES_128_GCM_SHA256", 194 | "TLS_RSA_WITH_AES_256_GCM_SHA384", 195 | "TLS_RSA_WITH_AES_128_CBC_SHA", 196 | "TLS_RSA_WITH_AES_256_CBC_SHA"}, 197 | 198 | new String[]{"TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1"}, new String[]{ 199 | "TLS_AES_128_GCM_SHA256", 200 | "TLS_AES_256_GCM_SHA384", 201 | "TLS_CHACHA20_POLY1305_SHA256", 202 | "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 203 | "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", 204 | "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", 205 | "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", 206 | "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 207 | "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", 208 | "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", 209 | "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", 210 | "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", 211 | "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", 212 | "TLS_RSA_WITH_AES_256_GCM_SHA384", 213 | "TLS_RSA_WITH_AES_128_GCM_SHA256", 214 | "TLS_RSA_WITH_AES_256_CBC_SHA", 215 | "TLS_RSA_WITH_AES_128_CBC_SHA", 216 | "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", 217 | "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", 218 | "SSL_RSA_WITH_3DES_EDE_CBC_SHA" 219 | } 220 | ); 221 | public static Map defaultContextCipherSuites() { 222 | Map cipherSuiteMap = new HashMap<>(); 223 | cipherSuiteMap.put(47, "TLS_RSA_WITH_AES_128_CBC_SHA"); 224 | cipherSuiteMap.put(50, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA"); 225 | cipherSuiteMap.put(51, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"); 226 | cipherSuiteMap.put(53, "TLS_RSA_WITH_AES_256_CBC_SHA"); 227 | cipherSuiteMap.put(56, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA"); 228 | cipherSuiteMap.put(57, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"); 229 | cipherSuiteMap.put(60, "TLS_RSA_WITH_AES_128_CBC_SHA256"); 230 | cipherSuiteMap.put(61, "TLS_RSA_WITH_AES_256_CBC_SHA256"); 231 | cipherSuiteMap.put(64, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256"); 232 | cipherSuiteMap.put(103, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"); 233 | cipherSuiteMap.put(106, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"); 234 | cipherSuiteMap.put(107, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"); 235 | cipherSuiteMap.put(156, "TLS_RSA_WITH_AES_128_GCM_SHA256"); 236 | cipherSuiteMap.put(157, "TLS_RSA_WITH_AES_256_GCM_SHA384"); 237 | cipherSuiteMap.put(158, "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"); 238 | cipherSuiteMap.put(159, "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"); 239 | cipherSuiteMap.put(162, "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256"); 240 | cipherSuiteMap.put(163, "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384"); 241 | cipherSuiteMap.put(255, "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"); 242 | cipherSuiteMap.put(4865, "TLS_AES_128_GCM_SHA256"); 243 | cipherSuiteMap.put(4866, "TLS_AES_256_GCM_SHA384"); 244 | cipherSuiteMap.put(4867, "TLS_CHACHA20_POLY1305_SHA256"); 245 | cipherSuiteMap.put(49161, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"); 246 | cipherSuiteMap.put(49162, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"); 247 | cipherSuiteMap.put(49171, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"); 248 | cipherSuiteMap.put(49172, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"); 249 | cipherSuiteMap.put(49187, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"); 250 | cipherSuiteMap.put(49188, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384"); 251 | cipherSuiteMap.put(49191, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"); 252 | cipherSuiteMap.put(49192, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"); 253 | cipherSuiteMap.put(49195, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"); 254 | cipherSuiteMap.put(49196, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"); 255 | cipherSuiteMap.put(49199, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"); 256 | cipherSuiteMap.put(49200, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"); 257 | cipherSuiteMap.put(52392, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"); 258 | cipherSuiteMap.put(52393, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"); 259 | cipherSuiteMap.put(52394, "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256"); 260 | 261 | cipherSuiteMap.put(49160,"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA"); 262 | cipherSuiteMap.put(49170,"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"); 263 | cipherSuiteMap.put(10,"SSL_RSA_WITH_3DES_EDE_CBC_SHA"); 264 | 265 | return cipherSuiteMap; 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /src/main/java/AutoBurp/bypass/DomainSettingsManager.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.bypass; 2 | 3 | import burp.api.montoya.MontoyaApi; 4 | import com.google.gson.Gson; 5 | import com.google.gson.reflect.TypeToken; 6 | import AutoBurp.bypass.beens.Browsers; 7 | import AutoBurp.bypass.beens.DomainSettings; 8 | import AutoBurp.bypass.beens.MatchAndReplace; 9 | 10 | import java.lang.reflect.Type; 11 | import java.net.URI; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.Optional; 15 | 16 | public class DomainSettingsManager { 17 | private static final String SETTINGS_KEY = "domain_tls_settings"; 18 | private static List domainSettingsList = new ArrayList<>(); 19 | private static MontoyaApi montoyaApi; 20 | private static Gson gson = new Gson(); 21 | 22 | public static void initialize(MontoyaApi api) { 23 | montoyaApi = api; 24 | loadSettings(); 25 | } 26 | 27 | public static void addDomainSettings(String url, Browsers browser, String[] protocols, String[] ciphers, boolean httpDowngrade) { 28 | try { 29 | String domain = extractDomain(url); 30 | if (domain == null || domain.isEmpty()) { 31 | return; 32 | } 33 | 34 | 35 | Optional existingSettings = domainSettingsList.stream() 36 | .filter(settings -> settings.getDomain().equals(domain)) 37 | .findFirst(); 38 | 39 | if (existingSettings.isPresent()) { 40 | 41 | domainSettingsList.remove(existingSettings.get()); 42 | } 43 | 44 | 45 | DomainSettings newSettings = new DomainSettings( 46 | domain, 47 | browser.name, 48 | protocols, 49 | ciphers, 50 | httpDowngrade 51 | ); 52 | domainSettingsList.add(newSettings); 53 | saveSettings(); 54 | 55 | 56 | Utilities.updateTLSSettingsSync(protocols, ciphers); 57 | Utilities.updateProxySettingsSync(MatchAndReplace.create(browser)); 58 | 59 | 60 | if (Utilities.enabledHTTPDowngrade() != httpDowngrade) { 61 | Utilities.updateHTTPSettings(); 62 | } 63 | 64 | 65 | } catch (Exception e) { 66 | Utilities.error("添加域名设置失败: " + e.getMessage()); 67 | } 68 | } 69 | 70 | public static void updateDomainSettings(String domain, Browsers browser, String[] protocols, String[] ciphers, boolean httpDowngrade) { 71 | 72 | Optional existingSettings = domainSettingsList.stream() 73 | .filter(settings -> settings.getDomain().equals(domain)) 74 | .findFirst(); 75 | 76 | if (existingSettings.isPresent()) { 77 | 78 | domainSettingsList.remove(existingSettings.get()); 79 | 80 | 81 | DomainSettings newSettings = new DomainSettings( 82 | domain, 83 | browser.name, 84 | protocols, 85 | ciphers, 86 | httpDowngrade 87 | ); 88 | domainSettingsList.add(newSettings); 89 | saveSettings(); 90 | 91 | 92 | Utilities.updateTLSSettingsSync(protocols, ciphers); 93 | Utilities.updateProxySettingsSync(MatchAndReplace.create(browser)); 94 | 95 | 96 | if (Utilities.enabledHTTPDowngrade() != httpDowngrade) { 97 | Utilities.updateHTTPSettings(); 98 | } 99 | 100 | Utilities.log("已更新并应用域名 " + domain + " 的 TLS 设置"); 101 | } 102 | } 103 | 104 | public static void removeDomainSettings(String domain) { 105 | boolean removed = domainSettingsList.removeIf(settings -> settings.getDomain().equals(domain)); 106 | if (removed) { 107 | saveSettings(); 108 | 109 | Utilities.loadTLSSettings(); 110 | 111 | } 112 | } 113 | 114 | public static List getAllDomainSettings() { 115 | return new ArrayList<>(domainSettingsList); 116 | } 117 | 118 | public static Optional getDomainSettings(String domain) { 119 | return domainSettingsList.stream() 120 | .filter(settings -> settings.getDomain().equals(domain)) 121 | .findFirst(); 122 | } 123 | 124 | 125 | public static void applyDomainSettings(String domain) { 126 | Optional settings = getDomainSettings(domain); 127 | if (settings.isPresent()) { 128 | DomainSettings domainSettings = settings.get(); 129 | 130 | 131 | Browsers browser = Browsers.valueOf(domainSettings.getBrowser()); 132 | 133 | 134 | Utilities.updateTLSSettingsSync(domainSettings.getProtocols(), domainSettings.getCiphers()); 135 | 136 | 137 | Utilities.updateProxySettingsSync(MatchAndReplace.create(browser)); 138 | 139 | 140 | if (Utilities.enabledHTTPDowngrade() != domainSettings.isHttpDowngrade()) { 141 | Utilities.updateHTTPSettings(); 142 | } 143 | 144 | 145 | } 146 | } 147 | 148 | private static void saveSettings() { 149 | String serializedSettings = gson.toJson(domainSettingsList); 150 | montoyaApi.persistence().preferences().setString(SETTINGS_KEY, serializedSettings); 151 | } 152 | 153 | private static void loadSettings() { 154 | String serializedSettings = montoyaApi.persistence().preferences().getString(SETTINGS_KEY); 155 | if (serializedSettings != null && !serializedSettings.isEmpty()) { 156 | Type listType = new TypeToken>(){}.getType(); 157 | domainSettingsList = gson.fromJson(serializedSettings, listType); 158 | } 159 | } 160 | 161 | private static String extractDomain(String url) { 162 | try { 163 | if (!url.startsWith("http")) { 164 | url = "http://" + url; 165 | } 166 | URI uri = new URI(url); 167 | String domain = uri.getHost(); 168 | return domain; 169 | } catch (Exception e) { 170 | return url; 171 | } 172 | } 173 | } -------------------------------------------------------------------------------- /src/main/java/AutoBurp/bypass/DomainSettingsPanel.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.bypass; 2 | 3 | import burp.api.montoya.MontoyaApi; 4 | import burp.api.montoya.core.ByteArray; 5 | import burp.api.montoya.ui.editor.RawEditor; 6 | import AutoBurp.bypass.beens.Browsers; 7 | import AutoBurp.bypass.beens.DomainSettings; 8 | 9 | import javax.swing.*; 10 | import javax.swing.border.EmptyBorder; 11 | import javax.swing.border.CompoundBorder; 12 | import javax.swing.border.LineBorder; 13 | import javax.swing.table.AbstractTableModel; 14 | import javax.swing.table.DefaultTableCellRenderer; 15 | import javax.swing.table.JTableHeader; 16 | import java.awt.*; 17 | import java.awt.event.MouseAdapter; 18 | import java.awt.event.MouseEvent; 19 | import java.util.Arrays; 20 | import java.util.List; 21 | 22 | public class DomainSettingsPanel extends JPanel { 23 | private final MontoyaApi montoyaApi; 24 | private final JTable settingsTable; 25 | private final SettingsTableModel tableModel; 26 | private final RawEditor detailsEditor; 27 | private Timer autoRefreshTimer; 28 | 29 | 30 | private static final Color PRIMARY_COLOR = new Color(120, 103, 180); 31 | private static final Color SECONDARY_COLOR = new Color(52, 128, 83); 32 | private static final Color BACKGROUND_COLOR = new Color(250, 250, 250); 33 | private static final Color CARD_BACKGROUND = new Color(255, 255, 255); 34 | private static final Color TEXT_COLOR = new Color(60, 64, 67); 35 | private static final Color BORDER_COLOR = new Color(218, 220, 224); 36 | 37 | 38 | private static final Font TITLE_FONT = new Font("Dialog", Font.BOLD, 14); 39 | private static final Font NORMAL_FONT = new Font("Dialog", Font.BOLD, 13); 40 | private static final Font BUTTON_FONT = new Font("Dialog", Font.BOLD, 13); 41 | 42 | public DomainSettingsPanel(MontoyaApi montoyaApi) { 43 | this.montoyaApi = montoyaApi; 44 | this.setLayout(new BorderLayout(0, 10)); 45 | this.setBackground(BACKGROUND_COLOR); 46 | this.setBorder(new EmptyBorder(15, 15, 15, 15)); 47 | 48 | 49 | tableModel = new SettingsTableModel(); 50 | settingsTable = createStylizedTable(tableModel); 51 | 52 | 53 | detailsEditor = montoyaApi.userInterface().createRawEditor(); 54 | detailsEditor.setEditable(false); 55 | 56 | 57 | JPanel titlePanel = new JPanel(new BorderLayout()); 58 | titlePanel.setBackground(BACKGROUND_COLOR); 59 | titlePanel.setBorder(new EmptyBorder(0, 0, 10, 0)); 60 | 61 | JLabel titleLabel = new JLabel("TLS WAF"); 62 | titleLabel.setFont(TITLE_FONT); 63 | titleLabel.setForeground(PRIMARY_COLOR); 64 | titlePanel.add(titleLabel, BorderLayout.WEST); 65 | 66 | 67 | JPanel buttonPanel = new JPanel(); 68 | buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT, 10, 0)); 69 | buttonPanel.setBackground(BACKGROUND_COLOR); 70 | 71 | 72 | JButton addButton = createStylizedButton("添加域名", PRIMARY_COLOR); 73 | addButton.addActionListener(e -> showAddDomainDialog()); 74 | 75 | 76 | JButton editButton = createStylizedButton("修改设置", SECONDARY_COLOR); 77 | editButton.addActionListener(e -> { 78 | int selectedRow = settingsTable.getSelectedRow(); 79 | if (selectedRow >= 0) { 80 | DomainSettings settings = tableModel.getSettingAt(selectedRow); 81 | showEditDomainDialog(settings); 82 | } else { 83 | showStylizedMessage("请先选择一个域名", "提示", JOptionPane.INFORMATION_MESSAGE); 84 | } 85 | }); 86 | 87 | 88 | JButton deleteButton = createStylizedButton("删除设置", new Color(234, 67, 53)); 89 | deleteButton.addActionListener(e -> { 90 | int selectedRow = settingsTable.getSelectedRow(); 91 | if (selectedRow >= 0) { 92 | DomainSettings settings = tableModel.getSettingAt(selectedRow); 93 | int result = showConfirmDialog( 94 | "确定要删除域名 " + settings.getDomain() + " 的设置吗?", 95 | "确认删除" 96 | ); 97 | if (result == JOptionPane.YES_OPTION) { 98 | DomainSettingsManager.removeDomainSettings(settings.getDomain()); 99 | Utilities.loadTLSSettings(); 100 | refreshTable(); 101 | showStylizedMessage("已删除域名设置并恢复默认 TLS 配置", "提示", JOptionPane.INFORMATION_MESSAGE); 102 | } 103 | } else { 104 | showStylizedMessage("请先选择一个域名", "提示", JOptionPane.INFORMATION_MESSAGE); 105 | } 106 | }); 107 | 108 | 109 | buttonPanel.add(addButton); 110 | buttonPanel.add(editButton); 111 | buttonPanel.add(deleteButton); 112 | titlePanel.add(buttonPanel, BorderLayout.EAST); 113 | 114 | 115 | JPanel tableCard = createCardPanel(); 116 | tableCard.setLayout(new BorderLayout()); 117 | JScrollPane tableScrollPane = new JScrollPane(settingsTable); 118 | tableScrollPane.setBorder(null); 119 | tableScrollPane.getViewport().setBackground(CARD_BACKGROUND); 120 | tableCard.add(tableScrollPane, BorderLayout.CENTER); 121 | 122 | 123 | JPanel detailsCard = createCardPanel(); 124 | detailsCard.setLayout(new BorderLayout()); 125 | JLabel detailsLabel = new JLabel("详细信息"); 126 | detailsLabel.setFont(TITLE_FONT); 127 | detailsLabel.setForeground(TEXT_COLOR); 128 | detailsLabel.setBorder(new EmptyBorder(10, 10, 10, 10)); 129 | detailsCard.add(detailsLabel, BorderLayout.NORTH); 130 | 131 | JPanel editorPanel = new JPanel(new BorderLayout()); 132 | editorPanel.setBackground(CARD_BACKGROUND); 133 | editorPanel.setBorder(new EmptyBorder(0, 10, 10, 10)); 134 | editorPanel.add(detailsEditor.uiComponent(), BorderLayout.CENTER); 135 | detailsCard.add(editorPanel, BorderLayout.CENTER); 136 | 137 | 138 | JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); 139 | splitPane.setTopComponent(tableCard); 140 | splitPane.setBottomComponent(detailsCard); 141 | splitPane.setDividerLocation(300); 142 | splitPane.setDividerSize(8); 143 | splitPane.setBorder(null); 144 | 145 | this.add(titlePanel, BorderLayout.NORTH); 146 | this.add(splitPane, BorderLayout.CENTER); 147 | 148 | refreshTable(); 149 | 150 | autoRefreshTimer = new Timer(5000, e -> refreshTable()); 151 | autoRefreshTimer.start(); 152 | } 153 | 154 | 155 | private JPanel createCardPanel() { 156 | JPanel panel = new JPanel(); 157 | panel.setBackground(CARD_BACKGROUND); 158 | panel.setBorder(new CompoundBorder( 159 | new LineBorder(BORDER_COLOR, 1, true), 160 | new EmptyBorder(5, 5, 5, 5) 161 | )); 162 | return panel; 163 | } 164 | 165 | 166 | private JButton createStylizedButton(String text, Color color) { 167 | JButton button = new JButton(text); 168 | button.setFont(BUTTON_FONT); 169 | button.setForeground(Color.WHITE); 170 | button.setBackground(color); 171 | button.setBorderPainted(false); 172 | button.setFocusPainted(false); 173 | button.setCursor(new Cursor(Cursor.HAND_CURSOR)); 174 | button.setPreferredSize(new Dimension(100, 30)); 175 | 176 | 177 | button.addMouseListener(new MouseAdapter() { 178 | @Override 179 | public void mouseEntered(MouseEvent e) { 180 | button.setBackground(darken(color, 0.1f)); 181 | } 182 | 183 | @Override 184 | public void mouseExited(MouseEvent e) { 185 | button.setBackground(color); 186 | } 187 | }); 188 | 189 | return button; 190 | } 191 | 192 | 193 | private JTable createStylizedTable(SettingsTableModel model) { 194 | JTable table = new JTable(model); 195 | table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 196 | table.setRowHeight(30); 197 | table.setShowGrid(false); 198 | table.setIntercellSpacing(new Dimension(0, 0)); 199 | table.setFont(NORMAL_FONT); 200 | table.setForeground(TEXT_COLOR); 201 | table.setSelectionBackground(new Color(232, 240, 254)); 202 | table.setSelectionForeground(TEXT_COLOR); 203 | 204 | 205 | JTableHeader header = table.getTableHeader(); 206 | header.setFont(BUTTON_FONT); 207 | header.setBackground(new Color(240, 240, 240)); 208 | header.setForeground(TEXT_COLOR); 209 | header.setBorder(new LineBorder(BORDER_COLOR, 1)); 210 | 211 | 212 | DefaultTableCellRenderer renderer = new DefaultTableCellRenderer() { 213 | @Override 214 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 215 | Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 216 | if (!isSelected) { 217 | c.setBackground(row % 2 == 0 ? CARD_BACKGROUND : new Color(248, 249, 250)); 218 | } 219 | setBorder(new EmptyBorder(0, 10, 0, 10)); 220 | return c; 221 | } 222 | }; 223 | renderer.setHorizontalAlignment(SwingConstants.LEFT); 224 | 225 | for (int i = 0; i < table.getColumnCount(); i++) { 226 | table.getColumnModel().getColumn(i).setCellRenderer(renderer); 227 | } 228 | 229 | 230 | table.getSelectionModel().addListSelectionListener(e -> { 231 | if (!e.getValueIsAdjusting()) { 232 | updateDetailsPanel(); 233 | } 234 | }); 235 | 236 | return table; 237 | } 238 | 239 | 240 | private int showConfirmDialog(String message, String title) { 241 | UIManager.put("OptionPane.background", CARD_BACKGROUND); 242 | UIManager.put("Panel.background", CARD_BACKGROUND); 243 | UIManager.put("OptionPane.messageForeground", TEXT_COLOR); 244 | UIManager.put("OptionPane.messageFont", NORMAL_FONT); 245 | UIManager.put("OptionPane.buttonFont", BUTTON_FONT); 246 | 247 | return JOptionPane.showConfirmDialog( 248 | this, 249 | message, 250 | title, 251 | JOptionPane.YES_NO_OPTION, 252 | JOptionPane.QUESTION_MESSAGE 253 | ); 254 | } 255 | 256 | 257 | private void showStylizedMessage(String message, String title, int messageType) { 258 | UIManager.put("OptionPane.background", CARD_BACKGROUND); 259 | UIManager.put("Panel.background", CARD_BACKGROUND); 260 | UIManager.put("OptionPane.messageForeground", TEXT_COLOR); 261 | UIManager.put("OptionPane.messageFont", NORMAL_FONT); 262 | UIManager.put("OptionPane.buttonFont", BUTTON_FONT); 263 | 264 | JOptionPane.showMessageDialog(this, message, title, messageType); 265 | } 266 | 267 | 268 | private Color darken(Color color, float fraction) { 269 | int red = Math.max(0, Math.round(color.getRed() * (1 - fraction))); 270 | int green = Math.max(0, Math.round(color.getGreen() * (1 - fraction))); 271 | int blue = Math.max(0, Math.round(color.getBlue() * (1 - fraction))); 272 | return new Color(red, green, blue); 273 | } 274 | 275 | 276 | @Override 277 | public void removeNotify() { 278 | super.removeNotify(); 279 | if (autoRefreshTimer != null) { 280 | autoRefreshTimer.stop(); 281 | autoRefreshTimer = null; 282 | } 283 | } 284 | 285 | private void refreshTable() { 286 | 287 | String selectedDomain = null; 288 | int selectedRow = settingsTable.getSelectedRow(); 289 | if (selectedRow >= 0 && selectedRow < tableModel.getRowCount()) { 290 | selectedDomain = (String) tableModel.getValueAt(selectedRow, 0); 291 | } 292 | 293 | 294 | tableModel.setData(DomainSettingsManager.getAllDomainSettings()); 295 | tableModel.fireTableDataChanged(); 296 | 297 | 298 | if (selectedDomain != null) { 299 | for (int i = 0; i < tableModel.getRowCount(); i++) { 300 | if (selectedDomain.equals(tableModel.getValueAt(i, 0))) { 301 | settingsTable.setRowSelectionInterval(i, i); 302 | 303 | Rectangle rect = settingsTable.getCellRect(i, 0, true); 304 | settingsTable.scrollRectToVisible(rect); 305 | break; 306 | } 307 | } 308 | } 309 | } 310 | 311 | private void updateDetailsPanel() { 312 | int selectedRow = settingsTable.getSelectedRow(); 313 | if (selectedRow >= 0 && selectedRow < tableModel.getRowCount()) { 314 | DomainSettings settings = tableModel.getSettingAt(selectedRow); 315 | StringBuilder details = new StringBuilder(); 316 | details.append("域名: ").append(settings.getDomain()).append("\n\n"); 317 | details.append("浏览器: ").append(settings.getBrowser()).append("\n\n"); 318 | details.append("协议: ").append(Arrays.toString(settings.getProtocols())).append("\n\n"); 319 | details.append("密码套件: ").append(Arrays.toString(settings.getCiphers())).append("\n\n"); 320 | details.append("HTTP降级: ").append(settings.isHttpDowngrade() ? "是" : "否").append("\n\n"); 321 | details.append("记录时间: ").append(settings.getTimestamp()).append("\n"); 322 | 323 | detailsEditor.setContents(ByteArray.byteArray(details.toString().getBytes())); 324 | } else { 325 | detailsEditor.setContents(ByteArray.byteArray("请选择一个域名查看详细信息".getBytes())); 326 | } 327 | } 328 | 329 | private void showAddDomainDialog() { 330 | 331 | JTextField domainField = new JTextField(20); 332 | styleTextField(domainField); 333 | 334 | JComboBox browserCombo = new JComboBox<>(Browsers.values()); 335 | styleComboBox(browserCombo); 336 | 337 | JCheckBox httpDowngradeCheckbox = new JCheckBox("启用 HTTP 降级"); 338 | styleCheckBox(httpDowngradeCheckbox); 339 | 340 | JPanel panel = new JPanel(new GridLayout(0, 1, 0, 10)); 341 | panel.setBackground(CARD_BACKGROUND); 342 | panel.setBorder(new EmptyBorder(15, 15, 15, 15)); 343 | 344 | JLabel domainLabel = new JLabel("域名:"); 345 | domainLabel.setFont(NORMAL_FONT); 346 | domainLabel.setForeground(TEXT_COLOR); 347 | 348 | JLabel browserLabel = new JLabel("浏览器:"); 349 | browserLabel.setFont(NORMAL_FONT); 350 | browserLabel.setForeground(TEXT_COLOR); 351 | 352 | panel.add(domainLabel); 353 | panel.add(domainField); 354 | panel.add(browserLabel); 355 | panel.add(browserCombo); 356 | panel.add(httpDowngradeCheckbox); 357 | 358 | 359 | UIManager.put("OptionPane.background", CARD_BACKGROUND); 360 | UIManager.put("Panel.background", CARD_BACKGROUND); 361 | UIManager.put("OptionPane.messageForeground", TEXT_COLOR); 362 | 363 | int result = JOptionPane.showConfirmDialog( 364 | this, panel, "添加域名设置", 365 | JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); 366 | 367 | if (result == JOptionPane.OK_OPTION) { 368 | String domain = domainField.getText().trim(); 369 | if (domain.isEmpty()) { 370 | showStylizedMessage("域名不能为空", "错误", JOptionPane.ERROR_MESSAGE); 371 | return; 372 | } 373 | 374 | Browsers browser = (Browsers) browserCombo.getSelectedItem(); 375 | boolean httpDowngrade = httpDowngradeCheckbox.isSelected(); 376 | 377 | DomainSettingsManager.addDomainSettings( 378 | domain, 379 | browser, 380 | Constants.BROWSERS_PROTOCOLS.get(browser.name), 381 | Constants.BROWSERS_CIPHERS.get(browser.name), 382 | httpDowngrade 383 | ); 384 | 385 | refreshTable(); 386 | showStylizedMessage("已添加域名 " + domain + " 的设置", "提示", JOptionPane.INFORMATION_MESSAGE); 387 | } 388 | } 389 | 390 | private void showEditDomainDialog(DomainSettings settings) { 391 | try { 392 | 393 | JTextField domainField = new JTextField(settings.getDomain(), 20); 394 | domainField.setEditable(false); 395 | styleTextField(domainField); 396 | 397 | 398 | Browsers selectedBrowser = null; 399 | try { 400 | selectedBrowser = Browsers.valueOf(settings.getBrowser()); 401 | } catch (IllegalArgumentException e) { 402 | selectedBrowser = Browsers.FIREFOX; 403 | Utilities.error("无法识别的浏览器类型: " + settings.getBrowser() + ", 使用默认值 FIREFOX"); 404 | } 405 | 406 | JComboBox browserCombo = new JComboBox<>(Browsers.values()); 407 | browserCombo.setSelectedItem(selectedBrowser); 408 | styleComboBox(browserCombo); 409 | 410 | JCheckBox httpDowngradeCheckbox = new JCheckBox("启用 HTTP 降级", settings.isHttpDowngrade()); 411 | styleCheckBox(httpDowngradeCheckbox); 412 | 413 | JPanel panel = new JPanel(new GridLayout(0, 1, 0, 10)); 414 | panel.setBackground(CARD_BACKGROUND); 415 | panel.setBorder(new EmptyBorder(15, 15, 15, 15)); 416 | 417 | JLabel domainLabel = new JLabel("域名:"); 418 | domainLabel.setFont(NORMAL_FONT); 419 | domainLabel.setForeground(TEXT_COLOR); 420 | 421 | JLabel browserLabel = new JLabel("浏览器:"); 422 | browserLabel.setFont(NORMAL_FONT); 423 | browserLabel.setForeground(TEXT_COLOR); 424 | 425 | panel.add(domainLabel); 426 | panel.add(domainField); 427 | panel.add(browserLabel); 428 | panel.add(browserCombo); 429 | panel.add(httpDowngradeCheckbox); 430 | 431 | 432 | UIManager.put("OptionPane.background", CARD_BACKGROUND); 433 | UIManager.put("Panel.background", CARD_BACKGROUND); 434 | UIManager.put("OptionPane.messageForeground", TEXT_COLOR); 435 | 436 | int result = JOptionPane.showConfirmDialog( 437 | this, panel, "修改域名设置", 438 | JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); 439 | 440 | if (result == JOptionPane.OK_OPTION) { 441 | Browsers browser = (Browsers) browserCombo.getSelectedItem(); 442 | boolean httpDowngrade = httpDowngradeCheckbox.isSelected(); 443 | 444 | DomainSettingsManager.updateDomainSettings( 445 | settings.getDomain(), 446 | browser, 447 | Constants.BROWSERS_PROTOCOLS.get(browser.name), 448 | Constants.BROWSERS_CIPHERS.get(browser.name), 449 | httpDowngrade 450 | ); 451 | 452 | refreshTable(); 453 | showStylizedMessage("已更新域名 " + settings.getDomain() + " 的设置", "提示", JOptionPane.INFORMATION_MESSAGE); 454 | } 455 | } catch (Exception e) { 456 | Utilities.error("修改域名设置时出错: " + e.getMessage()); 457 | showStylizedMessage("修改设置失败: " + e.getMessage(), "错误", JOptionPane.ERROR_MESSAGE); 458 | } 459 | } 460 | 461 | 462 | private void styleTextField(JTextField textField) { 463 | textField.setFont(NORMAL_FONT); 464 | textField.setForeground(TEXT_COLOR); 465 | textField.setBorder(new CompoundBorder( 466 | new LineBorder(BORDER_COLOR, 1, true), 467 | new EmptyBorder(5, 8, 5, 8) 468 | )); 469 | } 470 | 471 | 472 | private void styleComboBox(JComboBox comboBox) { 473 | comboBox.setFont(NORMAL_FONT); 474 | comboBox.setForeground(TEXT_COLOR); 475 | comboBox.setBackground(Color.WHITE); 476 | comboBox.setBorder(new LineBorder(BORDER_COLOR, 1, true)); 477 | } 478 | 479 | 480 | private void styleCheckBox(JCheckBox checkBox) { 481 | checkBox.setFont(NORMAL_FONT); 482 | checkBox.setForeground(TEXT_COLOR); 483 | checkBox.setBackground(CARD_BACKGROUND); 484 | } 485 | 486 | private static class SettingsTableModel extends AbstractTableModel { 487 | private final String[] columnNames = {"域名", "浏览器", "HTTP降级", "记录时间"}; 488 | private List data; 489 | 490 | public void setData(List data) { 491 | this.data = data; 492 | } 493 | 494 | public DomainSettings getSettingAt(int row) { 495 | return data.get(row); 496 | } 497 | 498 | @Override 499 | public int getRowCount() { 500 | return data == null ? 0 : data.size(); 501 | } 502 | 503 | @Override 504 | public int getColumnCount() { 505 | return columnNames.length; 506 | } 507 | 508 | @Override 509 | public String getColumnName(int column) { 510 | return columnNames[column]; 511 | } 512 | 513 | @Override 514 | public Object getValueAt(int rowIndex, int columnIndex) { 515 | DomainSettings settings = data.get(rowIndex); 516 | return switch (columnIndex) { 517 | case 0 -> settings.getDomain(); 518 | case 1 -> settings.getBrowser(); 519 | case 2 -> settings.isHttpDowngrade() ? "是" : "否"; 520 | case 3 -> settings.getTimestamp(); 521 | default -> null; 522 | }; 523 | } 524 | } 525 | } -------------------------------------------------------------------------------- /src/main/java/AutoBurp/bypass/TLSContextMenuItemsProvider.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.bypass; 2 | 3 | import burp.api.montoya.MontoyaApi; 4 | import burp.api.montoya.core.ToolType; 5 | import burp.api.montoya.http.message.HttpRequestResponse; 6 | import burp.api.montoya.ui.contextmenu.ContextMenuEvent; 7 | import burp.api.montoya.ui.contextmenu.ContextMenuItemsProvider; 8 | import burp.api.montoya.ui.contextmenu.MessageEditorHttpRequestResponse; 9 | import AutoBurp.bypass.beens.Browsers; 10 | import AutoBurp.bypass.beens.MatchAndReplace; 11 | 12 | import javax.swing.*; 13 | import java.awt.*; 14 | import java.util.ArrayList; 15 | import java.util.Arrays; 16 | import java.util.List; 17 | import java.util.Optional; 18 | import java.util.concurrent.ThreadPoolExecutor; 19 | 20 | public class TLSContextMenuItemsProvider implements ContextMenuItemsProvider { 21 | private ThreadPoolExecutor taskEngine; 22 | 23 | private List requestResponses; 24 | private MontoyaApi montoyaApi; 25 | 26 | public TLSContextMenuItemsProvider(ThreadPoolExecutor taskEngine, MontoyaApi montoyaApi) { 27 | this.taskEngine = taskEngine; 28 | this.montoyaApi = montoyaApi; 29 | this.requestResponses = new ArrayList<>(); 30 | } 31 | 32 | @Override 33 | public List provideMenuItems(ContextMenuEvent contextMenuEvent) { 34 | if (contextMenuEvent.isFromTool(ToolType.LOGGER, ToolType.PROXY, ToolType.TARGET, ToolType.ORGANIZER)) { 35 | 36 | List menuItemList = new ArrayList<>(); 37 | 38 | this.requestResponses = new ArrayList<>(); 39 | 40 | if(contextMenuEvent.messageEditorRequestResponse().isPresent()) { 41 | MessageEditorHttpRequestResponse message = contextMenuEvent.messageEditorRequestResponse().get(); 42 | this.requestResponses.add(message.requestResponse()); 43 | String negotiation = Utilities.getComment(message.requestResponse()); 44 | if(negotiation != null) { 45 | JMenuItem negotiationItem = new JMenuItem(Utilities.getResourceString("negotiation")); 46 | negotiationItem.addActionListener(e -> addManualSettings(negotiation)); 47 | menuItemList.add(negotiationItem); 48 | } 49 | } else { 50 | this.requestResponses = contextMenuEvent.selectedRequestResponses(); 51 | } 52 | 53 | if(this.requestResponses.isEmpty()) return null; 54 | 55 | 56 | HttpRequestResponse requestResponse = this.requestResponses.get(0); 57 | String userAgent = requestResponse.request().header("User-Agent").value(); 58 | if (userAgent == null || userAgent.isBlank()) { 59 | Arrays.stream(Browsers.values()).forEach( 60 | browser -> { 61 | JMenuItem item = new JMenuItem(browser.name); 62 | item.addActionListener(e -> addTLSCiphers(browser)); 63 | menuItemList.add(item); 64 | } 65 | ); 66 | } else { 67 | Optional br = Arrays.stream(Browsers.values()) 68 | .filter(browsers -> userAgent.contains(browsers.name)).findAny(); 69 | if (br.isPresent()) { 70 | JMenuItem message = new JMenuItem(Utilities.getResourceString("message")); 71 | message.addActionListener(e -> addTLSCiphers(br.get())); 72 | menuItemList.add(message); 73 | } else { 74 | Arrays.stream(Browsers.values()).forEach( 75 | browser -> { 76 | JMenuItem item = new JMenuItem(browser.name); 77 | item.addActionListener(e -> addTLSCiphers(browser)); 78 | menuItemList.add(item); 79 | } 80 | ); 81 | } 82 | } 83 | 84 | String menuLabel = Utilities.enabledHTTPDowngrade() ? "Enable " : "Disable "; 85 | JMenuItem downgradeMenu = new JMenuItem(menuLabel + Utilities.getResourceString("menu_downgrade")); 86 | downgradeMenu.addActionListener(e -> downgradeHttp()); 87 | menuItemList.add(downgradeMenu); 88 | 89 | JMenuItem item = new JMenuItem(Utilities.getResourceString("menu_brute_force")); 90 | item.addActionListener(new TriggerCipherGuesser(taskEngine, this.requestResponses)); 91 | menuItemList.add(item); 92 | 93 | 94 | return menuItemList; 95 | } 96 | 97 | return null; 98 | } 99 | 100 | public void downgradeHttp(){ 101 | Utilities.updateHTTPSettings(); 102 | 103 | 104 | if (!this.requestResponses.isEmpty()) { 105 | HttpRequestResponse requestResponse = this.requestResponses.get(0); 106 | String url = requestResponse.request().url(); 107 | boolean isHttpDowngradeEnabled = !Utilities.enabledHTTPDowngrade(); 108 | 109 | 110 | String userAgent = requestResponse.request().header("User-Agent").value(); 111 | Browsers browser = Browsers.FIREFOX; 112 | for (Browsers b : Browsers.values()) { 113 | if (userAgent != null && userAgent.contains(b.name)) { 114 | browser = b; 115 | break; 116 | } 117 | } 118 | 119 | DomainSettingsManager.addDomainSettings( 120 | url, 121 | browser, 122 | Constants.BROWSERS_PROTOCOLS.get(browser.name), 123 | Constants.BROWSERS_CIPHERS.get(browser.name), 124 | isHttpDowngradeEnabled 125 | ); 126 | } 127 | } 128 | 129 | public void addTLSCiphers(Browsers browser){ 130 | Utilities.updateTLSSettingsSync(Constants.BROWSERS_PROTOCOLS.get(browser.name), Constants.BROWSERS_CIPHERS.get(browser.name)); 131 | Utilities.updateProxySettingsSync(MatchAndReplace.create(browser)); 132 | 133 | 134 | if (!this.requestResponses.isEmpty()) { 135 | HttpRequestResponse requestResponse = this.requestResponses.get(0); 136 | String url = requestResponse.request().url(); 137 | boolean isHttpDowngradeEnabled = Utilities.enabledHTTPDowngrade(); 138 | 139 | DomainSettingsManager.addDomainSettings( 140 | url, 141 | browser, 142 | Constants.BROWSERS_PROTOCOLS.get(browser.name), 143 | Constants.BROWSERS_CIPHERS.get(browser.name), 144 | isHttpDowngradeEnabled 145 | ); 146 | } 147 | } 148 | public void addManualSettings(String negotiation){ 149 | Utilities.montoyaApi.burpSuite().importProjectOptionsFromJson(negotiation); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/main/java/AutoBurp/bypass/TriggerCipherGuesser.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.bypass; 2 | 3 | import AutoBurp.bypass.beens.Browsers; 4 | import AutoBurp.bypass.beens.MatchAndReplace; 5 | import burp.api.montoya.http.message.HttpRequestResponse; 6 | import burp.api.montoya.http.message.responses.HttpResponse; 7 | 8 | import java.awt.event.ActionEvent; 9 | import java.awt.event.ActionListener; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.ListIterator; 13 | import java.util.concurrent.ThreadPoolExecutor; 14 | 15 | import static AutoBurp.bypass.Constants.MAX_ATTEMPTS; 16 | 17 | public class TriggerCipherGuesser implements ActionListener, Runnable { 18 | private ThreadPoolExecutor taskEngine; 19 | private final List requestResponses; 20 | 21 | public TriggerCipherGuesser(ThreadPoolExecutor taskEngine, List requestResponses) { 22 | this.taskEngine = taskEngine; 23 | this.requestResponses = requestResponses; 24 | } 25 | 26 | @Override 27 | public void actionPerformed(ActionEvent e) { 28 | if(requestResponses.isEmpty()) return; 29 | (new Thread(this)).start(); 30 | } 31 | 32 | 33 | @Override 34 | public void run() { 35 | taskEngine.execute(new Runnable() { 36 | @Override 37 | public void run() { 38 | try { 39 | Utilities.log(String.format("|*| Starting attack on %s targets", requestResponses.size())); 40 | Utilities.loadTLSSettings(); 41 | ListIterator it = requestResponses.listIterator(); 42 | for(String[] protocol : Constants.BRUTEFORCE_CIPHERS.keySet()) { 43 | if(!it.hasNext()) { 44 | Utilities.log("|*| Nothing to do!"); 45 | break; 46 | } 47 | String[] ciphers = Constants.BRUTEFORCE_CIPHERS.get(protocol); 48 | Utilities.updateTLSSettings(protocol, ciphers); 49 | Utilities.log(String.format("|*| Probing protocols: %s", Utilities.stringify(protocol))); 50 | while (it.hasNext()) { 51 | HttpRequestResponse requestResponse = it.next(); 52 | String negotiation = Utilities.negotiation(protocol,ciphers); 53 | List probs = new ArrayList<>(); 54 | for(int i = 0; i < MAX_ATTEMPTS; i++) { 55 | HttpRequestResponse prob = Utilities.attemptRequest(requestResponse, negotiation); 56 | probs.add(prob); 57 | } 58 | if ( !probs.isEmpty() && Utilities.compareResponses(requestResponse, probs)) { 59 | String comment = String.format( 60 | "|*| URL %s response was changed. Status code %s. TLS settings: %s", 61 | requestResponse.request().url(), 62 | probs.stream() 63 | .map(HttpRequestResponse::response) 64 | .map(HttpResponse::statusCode) 65 | .map(String::valueOf) 66 | .reduce("",(partial,element) -> element + "," + partial), 67 | negotiation ); 68 | Utilities.log(comment); 69 | Utilities.addComment(requestResponse,negotiation); 70 | it.remove(); 71 | } 72 | } 73 | while (it.hasPrevious()) { 74 | it.previous(); 75 | } 76 | Thread.sleep(100); 77 | } 78 | 79 | }catch (Exception e) { 80 | Utilities.log(e.getMessage()); 81 | } 82 | finally { 83 | Utilities.updateTLSSettingsSync(Constants.BROWSERS_PROTOCOLS.get(Browsers.FIREFOX.name), Constants.BROWSERS_CIPHERS.get(Browsers.FIREFOX.name)); 84 | Utilities.updateProxySettingsSync(MatchAndReplace.create(Browsers.FIREFOX)); 85 | } 86 | } 87 | }); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/AutoBurp/bypass/Utilities.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.bypass; 2 | 3 | import AutoBurp.bypass.beens.*; 4 | import burp.api.montoya.MontoyaApi; 5 | import burp.api.montoya.core.Annotations; 6 | import burp.api.montoya.core.HighlightColor; 7 | import burp.api.montoya.http.message.HttpRequestResponse; 8 | import burp.api.montoya.http.message.responses.analysis.Attribute; 9 | import burp.api.montoya.proxy.ProxyHistoryFilter; 10 | import burp.api.montoya.proxy.ProxyHttpRequestResponse; 11 | import burp.api.montoya.ui.contextmenu.MessageEditorHttpRequestResponse; 12 | import com.google.gson.Gson; 13 | 14 | import javax.swing.*; 15 | import java.net.InetAddress; 16 | import java.net.URI; 17 | import java.util.List; 18 | import java.util.Optional; 19 | import java.util.ResourceBundle; 20 | import java.util.concurrent.atomic.AtomicBoolean; 21 | 22 | import static burp.api.montoya.http.message.responses.analysis.AttributeType.WORD_COUNT; 23 | 24 | public class Utilities { 25 | private static final String RESOURCE_BUNDLE = "strings"; 26 | static AtomicBoolean unloaded = new AtomicBoolean(false); 27 | static MontoyaApi montoyaApi; 28 | static Gson gson; 29 | 30 | 31 | Utilities(MontoyaApi api) { 32 | montoyaApi = api; 33 | gson = new Gson(); 34 | } 35 | 36 | static void updateProxySettings(MatchAndReplace rule) { 37 | String proxy = montoyaApi.burpSuite().exportProjectOptionsAsJson("proxy.match_replace_rules"); 38 | ProxySettings currentProxySettings = gson.fromJson(proxy, ProxySettings.class); 39 | ProxySettings changedProxySettings = currentProxySettings.toggleMatchAndReplace(rule); 40 | String serializedProxySettings = gson.toJson(changedProxySettings); 41 | importProject(serializedProxySettings); 42 | } 43 | 44 | static void updateTLSSettings(String[] protocols, String[] ciphers) { 45 | String project_settings = montoyaApi.burpSuite().exportProjectOptionsAsJson("project_options"); 46 | TLSSettings currentTLSSettings = gson.fromJson(project_settings, TLSSettings.class); 47 | currentTLSSettings.addProtocols(protocols); 48 | currentTLSSettings.addCiphers(ciphers); 49 | String serializedTLSSettings = gson.toJson(currentTLSSettings); 50 | importProject(serializedTLSSettings); 51 | } 52 | static void updateProxySettingsSync(MatchAndReplace rule) { 53 | String proxy = montoyaApi.burpSuite().exportProjectOptionsAsJson("proxy.match_replace_rules"); 54 | ProxySettings currentProxySettings = gson.fromJson(proxy, ProxySettings.class); 55 | ProxySettings changedProxySettings = currentProxySettings.toggleMatchAndReplace(rule); 56 | String serializedProxySettings = gson.toJson(changedProxySettings); 57 | montoyaApi.burpSuite().importProjectOptionsFromJson(serializedProxySettings); 58 | } 59 | static void updateTLSSettingsSync(String[] protocols, String[] ciphers) { 60 | String project_settings = montoyaApi.burpSuite().exportProjectOptionsAsJson("project_options"); 61 | TLSSettings currentTLSSettings = gson.fromJson(project_settings, TLSSettings.class); 62 | currentTLSSettings.addProtocols(protocols); 63 | currentTLSSettings.addCiphers(ciphers); 64 | String serializedTLSSettings = gson.toJson(currentTLSSettings); 65 | montoyaApi.burpSuite().importProjectOptionsFromJson(serializedTLSSettings); 66 | } 67 | static boolean enabledHTTPDowngrade() { 68 | String project_settings = montoyaApi.burpSuite().exportProjectOptionsAsJson("project_options"); 69 | TLSSettings currentTLSSettings = gson.fromJson(project_settings, TLSSettings.class); 70 | return currentTLSSettings.enabledHTTPDowngrade(); 71 | } 72 | static void updateHTTPSettings() { 73 | List rules = MatchAndReplace.createDowngradeRules(); 74 | String proxy = montoyaApi.burpSuite().exportProjectOptionsAsJson("proxy.match_replace_rules"); 75 | ProxySettings currentProxySettings = gson.fromJson(proxy, ProxySettings.class); 76 | ProxySettings changedProxySettings = currentProxySettings.toggleHTTPDowngradeMatchAndReplace(rules); 77 | String serializedProxySettings = gson.toJson(changedProxySettings); 78 | montoyaApi.burpSuite().importProjectOptionsFromJson(serializedProxySettings); 79 | 80 | String project_settings = montoyaApi.burpSuite().exportProjectOptionsAsJson("project_options"); 81 | TLSSettings currentTLSSettings = gson.fromJson(project_settings, TLSSettings.class); 82 | currentTLSSettings.toggleHTTPSettings(); 83 | String serializedTLSSettings = gson.toJson(currentTLSSettings); 84 | montoyaApi.burpSuite().importProjectOptionsFromJson(serializedTLSSettings); 85 | } 86 | 87 | static void importProject(String serializedSettings) { 88 | try { 89 | SwingUtilities.invokeAndWait(() -> { 90 | montoyaApi.burpSuite().importProjectOptionsFromJson(serializedSettings); 91 | }); 92 | } catch (Exception ignored){} 93 | } 94 | 95 | 96 | static void saveTLSSettings() { 97 | String project_settings = montoyaApi.burpSuite().exportProjectOptionsAsJson("project_options"); 98 | String proxy_settings = montoyaApi.burpSuite().exportProjectOptionsAsJson("proxy.match_replace_rules"); 99 | montoyaApi.persistence().preferences().setString(Utilities.getResourceString("network_preferences"), project_settings); 100 | montoyaApi.persistence().preferences().setString(Utilities.getResourceString("proxy_preferences"), proxy_settings); 101 | } 102 | 103 | static void loadTLSSettings() { 104 | String project_settings = montoyaApi.persistence().preferences().getString(Utilities.getResourceString("network_preferences")); 105 | String proxy_settings = montoyaApi.persistence().preferences().getString(Utilities.getResourceString("proxy_preferences")); 106 | montoyaApi.burpSuite().importProjectOptionsFromJson(project_settings); 107 | montoyaApi.burpSuite().importProjectOptionsFromJson(proxy_settings); 108 | } 109 | 110 | static void log(String message) { 111 | montoyaApi.logging().logToOutput(message); 112 | } 113 | 114 | static void error(String message) { 115 | montoyaApi.logging().logToError(message); 116 | } 117 | 118 | static String getResourceString(String id) { 119 | return ResourceBundle.getBundle(RESOURCE_BUNDLE).getString(id); 120 | } 121 | 122 | public static boolean doesHostExist(String urlString) { 123 | try { 124 | URI url = new URI(urlString); 125 | String host = url.getHost(); 126 | InetAddress address = InetAddress.getByName(host); 127 | return address != null; 128 | } catch (Exception e) { 129 | return false; 130 | } 131 | } 132 | 133 | static HttpRequestResponse attemptRequest(HttpRequestResponse requestResponse, String negotiation) { 134 | if (unloaded.get()) { 135 | log("Extension unloaded - aborting attack"); 136 | throw new RuntimeException("Extension unloaded"); 137 | } else { 138 | try { 139 | if (doesHostExist(requestResponse.request().url())) { 140 | return montoyaApi.http().sendRequest(requestResponse.request()).withAnnotations(Annotations.annotations(negotiation)); 141 | } 142 | return null; 143 | } catch (Exception e) { 144 | error(e.getMessage()); 145 | return null; 146 | } 147 | } 148 | } 149 | 150 | static HttpRequestResponse unpredictable(List comparableResponses) { 151 | HttpRequestResponse etalon = null; 152 | double P = 0.2; 153 | int base = -1; 154 | for(HttpRequestResponse requestResponse: comparableResponses) { 155 | if (requestResponse.hasResponse() && requestResponse.response() != null) { 156 | Optional words = requestResponse.response().attributes(WORD_COUNT).stream().map(Attribute::value).findFirst(); 157 | if (words.isPresent()) { 158 | if (base == -1) { 159 | base = words.get(); 160 | etalon = requestResponse; 161 | } else { 162 | int diff = Math.abs(base - words.get()); 163 | if (diff > Math.abs(base) * P) etalon = requestResponse; 164 | } 165 | } 166 | } 167 | } 168 | return etalon; 169 | } 170 | 171 | static boolean compareResponses(HttpRequestResponse baseRequest, List comparableResponses) { 172 | HttpRequestResponse requestResponse = unpredictable(comparableResponses); 173 | if (baseRequest.response() == null || requestResponse == null) return false; 174 | double P = 0.2; 175 | int b = 0; 176 | int c = 0; 177 | List baseAttributes = baseRequest.response().attributes(WORD_COUNT); 178 | List comparableAttributes = requestResponse.response().attributes(WORD_COUNT); 179 | Optional baseWordCount = baseAttributes.stream().map(Attribute::value).findFirst(); 180 | Optional comparableWordCount = comparableAttributes.stream().map(Attribute::value).findFirst(); 181 | if (baseWordCount.isPresent() && comparableWordCount.isPresent()) { 182 | b = baseWordCount.get(); 183 | c = comparableWordCount.get(); 184 | } else { 185 | b = baseRequest.response().headers().size(); 186 | c = requestResponse.response().headers().size(); 187 | } 188 | int diff = Math.abs(b - c); 189 | return diff > Math.abs(b) * P || diff > Math.abs(c) * P; 190 | } 191 | 192 | static String stringify(Object o) { 193 | return gson.toJson(o); 194 | } 195 | 196 | static String negotiation(String[] protocols, String[] ciphers) { 197 | return stringify(new TLSSettings(new ProjectOptions(new SSL(new Negotiation( ciphers, protocols, true))))); 198 | } 199 | 200 | static void addComment(HttpRequestResponse baseRequest, String comments) { 201 | List items = montoyaApi.proxy().history(new ProxyHistoryFilter() { 202 | @Override 203 | public boolean matches(ProxyHttpRequestResponse requestResponse) { 204 | return requestResponse.finalRequest().equals(baseRequest.request()); 205 | } 206 | }); 207 | items.forEach(item -> { 208 | item.annotations().setNotes(comments); 209 | item.annotations().setHighlightColor(HighlightColor.RED); 210 | }); 211 | } 212 | static String getComment(HttpRequestResponse baseRequest) { 213 | List items = montoyaApi.proxy().history(new ProxyHistoryFilter() { 214 | @Override 215 | public boolean matches(ProxyHttpRequestResponse requestResponse) { 216 | return requestResponse.hasResponse() && baseRequest.hasResponse() && requestResponse.originalResponse().equals(baseRequest.response()) && 217 | requestResponse.annotations().hasNotes(); 218 | } 219 | }); 220 | Optional optionalItem = items.stream().findFirst(); 221 | if (optionalItem.isEmpty()) return null; 222 | String negotiation = optionalItem.get().annotations().notes(); 223 | try { 224 | TLSSettings validator = gson.fromJson(negotiation, TLSSettings.class); 225 | return stringify(validator); 226 | }catch (Exception ignored){ 227 | return null; 228 | } 229 | } 230 | 231 | } 232 | -------------------------------------------------------------------------------- /src/main/java/AutoBurp/bypass/beens/Browsers.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.bypass.beens; 2 | 3 | public enum Browsers { 4 | FIREFOX("Firefox"), 5 | CHROME("Chrome"), 6 | SAFARI("Safari"); 7 | public final String name; 8 | 9 | Browsers(String name) { 10 | this.name = name; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/AutoBurp/bypass/beens/ClientCertificate.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.bypass.beens; 2 | 3 | import com.google.gson.annotations.Expose; 4 | 5 | public class ClientCertificate { 6 | private final @Expose String[] certificates; 7 | private final @Expose boolean use; 8 | 9 | public ClientCertificate(String[] certificates, boolean use) { 10 | this.certificates = certificates; 11 | this.use = use; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/AutoBurp/bypass/beens/DomainSettings.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.bypass.beens; 2 | 3 | import java.time.LocalDateTime; 4 | import java.time.format.DateTimeFormatter; 5 | 6 | public class DomainSettings { 7 | private String domain; 8 | private String browser; 9 | private String[] protocols; 10 | private String[] ciphers; 11 | private boolean httpDowngrade; 12 | private String timestamp; 13 | 14 | public DomainSettings(String domain, String browser, String[] protocols, String[] ciphers, boolean httpDowngrade) { 15 | this.domain = domain; 16 | this.browser = browser; 17 | this.protocols = protocols; 18 | this.ciphers = ciphers; 19 | this.httpDowngrade = httpDowngrade; 20 | this.timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); 21 | } 22 | 23 | public String getDomain() { 24 | return domain; 25 | } 26 | 27 | public String getBrowser() { 28 | return browser; 29 | } 30 | 31 | public String[] getProtocols() { 32 | return protocols; 33 | } 34 | 35 | public String[] getCiphers() { 36 | return ciphers; 37 | } 38 | 39 | public boolean isHttpDowngrade() { 40 | return httpDowngrade; 41 | } 42 | 43 | public String getTimestamp() { 44 | return timestamp; 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return String.format("域名: %s, 浏览器: %s, HTTP降级: %s, 时间: %s", 50 | domain, browser, httpDowngrade ? "是" : "否", timestamp); 51 | } 52 | } -------------------------------------------------------------------------------- /src/main/java/AutoBurp/bypass/beens/HTTP.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.bypass.beens; 2 | 3 | import com.google.gson.annotations.Expose; 4 | 5 | public class HTTP { 6 | private @Expose HTTP2 http2; 7 | 8 | public HTTP(HTTP2 http2) { 9 | this.http2 = http2; 10 | } 11 | 12 | public HTTP2 getHttp2() { 13 | return http2; 14 | } 15 | 16 | public void setHttp2(HTTP2 http2) { 17 | this.http2 = http2; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/AutoBurp/bypass/beens/HTTP2.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.bypass.beens; 2 | 3 | import com.google.gson.annotations.Expose; 4 | 5 | public class HTTP2 { 6 | private @Expose boolean enable_http2; 7 | 8 | public HTTP2(boolean enable_http2) { 9 | this.enable_http2 = enable_http2; 10 | } 11 | 12 | public boolean getEnableHTTP2() { 13 | return enable_http2; 14 | } 15 | 16 | public void setEnableHTTP2(boolean enable_http2) { 17 | this.enable_http2 = enable_http2; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/AutoBurp/bypass/beens/MatchAndReplace.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.bypass.beens; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import AutoBurp.bypass.Constants; 5 | 6 | import java.util.*; 7 | 8 | import static AutoBurp.bypass.Constants.*; 9 | 10 | public class MatchAndReplace { 11 | private @Expose String comment; 12 | private @Expose boolean enabled; 13 | private @Expose boolean is_simple_match; 14 | private @Expose String rule_type; 15 | private @Expose String string_match; 16 | private @Expose String string_replace; 17 | 18 | public MatchAndReplace(String comment, boolean enabled, boolean is_simple_match, String rule_type, String string_match, String string_replace) { 19 | this.comment = comment; 20 | this.enabled = enabled; 21 | this.is_simple_match = is_simple_match; 22 | this.rule_type = rule_type; 23 | this.string_match = string_match; 24 | this.string_replace = string_replace; 25 | } 26 | 27 | public static List createDowngradeRules(){ 28 | List rules = new ArrayList<>(); 29 | for(String header : MATCH_AND_REPLACE_REGEXP_HTTP2) { 30 | rules.add(new MatchAndReplace( 31 | String.format("HTTP2 Header %s downgrade rule", header), 32 | true, 33 | false, 34 | Constants.MATCH_AND_REPLACE_RULE_TYPE, 35 | header, 36 | "" 37 | )); 38 | } 39 | return rules; 40 | } 41 | 42 | public static MatchAndReplace create(Browsers browser){ 43 | String platform = System.getProperty("os.name","Windows"); 44 | OS optionalOS = Arrays.stream(OS.values()).filter(os -> platform.contains(os.name)).findFirst().get(); 45 | String format = BROWSERS_USER_AGENTS.get(browser.name); 46 | String value = BROWSERS_PLATFORMS.get(browser.name).get(optionalOS.name); 47 | return new MatchAndReplace( 48 | String.format("Emulate %s User-Agent", browser.name), 49 | true, 50 | false, 51 | Constants.MATCH_AND_REPLACE_RULE_TYPE, 52 | Constants.MATCH_AND_REPLACE_REGEXP, 53 | String.format(format,value) 54 | ); 55 | } 56 | public boolean filterByComment(MatchAndReplace filter) { 57 | return this.comment != null && filter.comment != null && this.comment.equalsIgnoreCase(filter.comment); 58 | } 59 | 60 | public boolean filterByBrowser(Browsers browser) { 61 | return this.comment != null && browser != null && 62 | this.comment.equalsIgnoreCase(String.format("Emulate %s User-Agent", browser.name)); 63 | } 64 | 65 | public void setEnabled(boolean enabled) { 66 | this.enabled = enabled; 67 | } 68 | 69 | public boolean isEnabled() { 70 | return enabled; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/AutoBurp/bypass/beens/Negotiation.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.bypass.beens; 2 | 3 | import com.google.gson.annotations.Expose; 4 | 5 | public class Negotiation { 6 | private @Expose String[] enabled_ciphers; 7 | private @Expose String[] enabled_protocols; 8 | private @Expose String tls_negotiation_behavior; 9 | private @Expose boolean disable_ssl_session_resume; 10 | private @Expose boolean allow_unsafe_renegotiation; 11 | private @Expose boolean enforce_upstream_trust; 12 | 13 | public Negotiation(String[] enabled_ciphers, String[] enabled_protocols, boolean disable_ssl_session_resume) { 14 | this.enabled_ciphers = enabled_ciphers; 15 | this.enabled_protocols = enabled_protocols; 16 | this.tls_negotiation_behavior = "use_custom"; 17 | this.disable_ssl_session_resume = disable_ssl_session_resume; 18 | } 19 | 20 | public void setEnabledProtocols(String[] enabled_protocols) { 21 | this.enabled_protocols = enabled_protocols; 22 | } 23 | 24 | public void setEnabledCiphers(String[] enabled_ciphers) { 25 | this.enabled_ciphers = enabled_ciphers; 26 | } 27 | 28 | public void setDisableSslSessionResume(boolean disable_ssl_session_resume) { 29 | this.disable_ssl_session_resume = disable_ssl_session_resume; 30 | } 31 | 32 | public void setTlsNegotiationBehavior(String tls_negotiation_behavior) { 33 | this.tls_negotiation_behavior = tls_negotiation_behavior; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/AutoBurp/bypass/beens/OS.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.bypass.beens; 2 | 3 | public enum OS { 4 | WINDOWS("Windows"), 5 | MACOS("Mac"), 6 | LINUX("Linux"); 7 | public final String name; 8 | 9 | OS(String name) { 10 | this.name = name; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/AutoBurp/bypass/beens/ProjectOptions.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.bypass.beens; 2 | 3 | import com.google.gson.annotations.Expose; 4 | 5 | public class ProjectOptions { 6 | private @Expose SSL ssl; 7 | private @Expose HTTP http; 8 | 9 | public ProjectOptions(SSL ssl) { 10 | this.ssl = ssl; 11 | } 12 | 13 | public SSL getSsl() { 14 | return ssl; 15 | } 16 | public HTTP getHTTP() { 17 | return http; 18 | } 19 | 20 | public void setSsl(SSL ssl) { 21 | this.ssl = ssl; 22 | } 23 | public void setHTTP(HTTP http) { 24 | this.http = http; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/AutoBurp/bypass/beens/Proxy.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.bypass.beens; 2 | 3 | import com.google.gson.annotations.Expose; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | public class Proxy { 11 | private @Expose MatchAndReplace[] match_replace_rules; 12 | 13 | public Proxy(MatchAndReplace[] match_replace_rules) { 14 | this.match_replace_rules = match_replace_rules; 15 | } 16 | 17 | public MatchAndReplace[] getMatchReplaceRules() { 18 | return match_replace_rules; 19 | } 20 | 21 | public void setMatchReplaceRules(MatchAndReplace[] match_replace_rules) { 22 | this.match_replace_rules = match_replace_rules; 23 | } 24 | public void toggleMatchAndReplace(MatchAndReplace rule){ 25 | List existing = new ArrayList<>(List.of(this.match_replace_rules)); 26 | Arrays.stream(Browsers.values()).forEach(browser -> { 27 | Optional optional = existing.stream().filter(r -> r.filterByBrowser(browser)).findFirst(); 28 | if (optional.isPresent()) { 29 | MatchAndReplace availableRule = optional.get(); 30 | existing.remove(availableRule); 31 | availableRule.setEnabled(false); 32 | existing.add(availableRule); 33 | 34 | } 35 | }); 36 | 37 | Optional found = existing.stream().filter(r -> r.filterByComment(rule)).findFirst(); 38 | if (found.isEmpty()) { 39 | existing.add(rule); 40 | } else { 41 | MatchAndReplace availableRule = found.get(); 42 | existing.remove(availableRule); 43 | availableRule.setEnabled(true); 44 | existing.add(availableRule); 45 | } 46 | setMatchReplaceRules(existing.toArray(new MatchAndReplace[0])); 47 | } 48 | public void toggleHTTPDowngradeMatchAndReplace(List rules){ 49 | List existing = new ArrayList<>(List.of(this.match_replace_rules)); 50 | for(MatchAndReplace rule: rules) { 51 | Optional found = existing.stream().filter(r -> r.filterByComment(rule)).findFirst(); 52 | if (found.isEmpty()) { 53 | existing.add(rule); 54 | } else { 55 | MatchAndReplace availableRule = found.get(); 56 | existing.remove(availableRule); 57 | availableRule.setEnabled(!availableRule.isEnabled()); 58 | existing.add(availableRule); 59 | } 60 | } 61 | setMatchReplaceRules(existing.toArray(new MatchAndReplace[0])); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/AutoBurp/bypass/beens/ProxySettings.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.bypass.beens; 2 | 3 | import com.google.gson.annotations.Expose; 4 | 5 | import java.util.List; 6 | 7 | public class ProxySettings { 8 | private final @Expose Proxy proxy; 9 | 10 | public ProxySettings(Proxy proxy) { 11 | this.proxy = proxy; 12 | } 13 | public ProxySettings toggleMatchAndReplace(MatchAndReplace rule) { 14 | Proxy existing = this.proxy; 15 | existing.toggleMatchAndReplace(rule); 16 | return new ProxySettings(existing); 17 | } 18 | public ProxySettings toggleHTTPDowngradeMatchAndReplace(List rules) { 19 | Proxy existing = this.proxy; 20 | existing.toggleHTTPDowngradeMatchAndReplace(rules); 21 | return new ProxySettings(existing); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/AutoBurp/bypass/beens/SSL.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.bypass.beens; 2 | 3 | import com.google.gson.annotations.Expose; 4 | 5 | public class SSL { 6 | private @Expose Negotiation negotiation; 7 | 8 | public SSL(Negotiation negotiation) { 9 | this.negotiation = negotiation; 10 | } 11 | 12 | public Negotiation getNegotiation() { 13 | return negotiation; 14 | } 15 | 16 | public void setNegotiation(Negotiation negotiation) { 17 | this.negotiation = negotiation; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/AutoBurp/bypass/beens/TLSNegotiation.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.bypass.beens; 2 | 3 | import java.util.Arrays; 4 | 5 | public record TLSNegotiation(String[] protocols, String[] ciphers) { 6 | public TLSNegotiation { 7 | } 8 | 9 | @Override 10 | public String toString() { 11 | return "TLSNegotiation{" + 12 | "protocols=" + Arrays.toString(protocols) + 13 | ", ciphers=" + Arrays.toString(ciphers) + 14 | '}'; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/AutoBurp/bypass/beens/TLSSettings.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.bypass.beens; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import AutoBurp.bypass.Constants; 5 | 6 | public class TLSSettings { 7 | private @Expose ProjectOptions project_options; 8 | 9 | public TLSSettings(ProjectOptions project_options) { 10 | this.project_options = project_options; 11 | } 12 | 13 | public void addProtocols(String[] protocols) { 14 | SSL ssl = this.project_options.getSsl(); 15 | Negotiation negotiation = ssl.getNegotiation(); 16 | negotiation.setEnabledProtocols(protocols); 17 | negotiation.setTlsNegotiationBehavior(Constants.BURP_TLS_NEGOTIATION); 18 | ssl.setNegotiation(negotiation); 19 | this.project_options.setSsl(ssl); 20 | } 21 | 22 | public void addCiphers(String[] ciphers) { 23 | SSL ssl = this.project_options.getSsl(); 24 | Negotiation negotiation = ssl.getNegotiation(); 25 | negotiation.setEnabledCiphers(ciphers); 26 | negotiation.setTlsNegotiationBehavior(Constants.BURP_TLS_NEGOTIATION); 27 | ssl.setNegotiation(negotiation); 28 | this.project_options.setSsl(ssl); 29 | } 30 | 31 | public void toggleHTTPSettings() { 32 | HTTP http = this.project_options.getHTTP(); 33 | boolean enabled = http.getHttp2().getEnableHTTP2(); 34 | http.setHttp2(new HTTP2(!enabled)); 35 | this.project_options.setHTTP(http); 36 | } 37 | 38 | public boolean enabledHTTPDowngrade() { 39 | return this.project_options.getHTTP().getHttp2().getEnableHTTP2(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/AutoBurp/fingerprint/FingerPrintScanner.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.fingerprint; 2 | 3 | import AutoBurp.fingerprint.model.FingerPrintRule; 4 | import AutoBurp.fingerprint.model.TableLogModel; 5 | import AutoBurp.fingerprint.ui.FingerPrintTab; 6 | import AutoBurp.fingerprint.util.FingerPrintUtils; 7 | import AutoBurp.fingerprint.util.HTTPUtils; 8 | import AutoBurp.fingerprint.util.Utils; 9 | import burp.*; 10 | 11 | import java.io.PrintWriter; 12 | import java.net.URL; 13 | import java.text.SimpleDateFormat; 14 | import java.util.*; 15 | import java.util.concurrent.*; 16 | import java.util.concurrent.atomic.AtomicInteger; 17 | 18 | public class FingerPrintScanner { 19 | private final IBurpExtenderCallbacks callbacks; 20 | private final IExtensionHelpers helpers; 21 | private List fingerprintRules; 22 | private final FingerPrintTab fingerPrintTab; 23 | private final Set scannedUrls; 24 | private final AtomicInteger requestCount; 25 | private final AtomicInteger successCount; 26 | private final ThreadPoolExecutor executorService; 27 | private final Object scanLock = new Object(); 28 | private volatile boolean acceptingNewTasks; 29 | 30 | 31 | private final static String[] STATIC_FILE_EXT = new String[]{ 32 | "png", "jpg", "jpeg", "gif", "pdf", "bmp", "css", "woff", "woff2", 33 | "ttf", "otf", "ttc", "svg", "psd", "exe", "zip", "rar", "7z", 34 | "msi", "tar", "gz", "mp3", "mp4", "mkv", "swf", "iso" 35 | }; 36 | 37 | 38 | private final static String[] STATIC_URL_EXT = new String[]{ 39 | "js", "ppt", "pptx", "doc", "docx", "xls", "xlsx", "cvs" 40 | }; 41 | 42 | public FingerPrintScanner(IBurpExtenderCallbacks callbacks, IExtensionHelpers helpers, 43 | List fingerprintRules, FingerPrintTab fingerPrintTab, 44 | Set scannedUrls, AtomicInteger requestCount, AtomicInteger successCount) { 45 | this.callbacks = callbacks; 46 | this.helpers = helpers; 47 | this.fingerprintRules = fingerprintRules; 48 | this.fingerPrintTab = fingerPrintTab; 49 | this.scannedUrls = scannedUrls; 50 | this.requestCount = requestCount; 51 | this.successCount = successCount; 52 | 53 | 54 | this.acceptingNewTasks = fingerPrintTab.isScanEnabled(); 55 | 56 | 57 | int coreCount = Runtime.getRuntime().availableProcessors(); 58 | coreCount = Math.max(coreCount, 10); 59 | int maxPoolSize = coreCount * 2; 60 | 61 | executorService = new ThreadPoolExecutor( 62 | coreCount, 63 | maxPoolSize, 64 | 60L, 65 | TimeUnit.SECONDS, 66 | new LinkedBlockingQueue<>(), 67 | Executors.defaultThreadFactory(), 68 | new ThreadPoolExecutor.CallerRunsPolicy() 69 | ); 70 | 71 | 72 | 73 | } 74 | 75 | public void processMessage(IInterceptedProxyMessage message) { 76 | 77 | if (!fingerPrintTab.isScanEnabled() || !acceptingNewTasks) { 78 | 79 | return; 80 | } 81 | 82 | IHttpRequestResponse requestResponse = message.getMessageInfo(); 83 | IRequestInfo requestInfo = helpers.analyzeRequest(requestResponse); 84 | String url = requestInfo.getUrl().toString(); 85 | 86 | 87 | fingerPrintTab.updateRequestCount(requestCount.incrementAndGet()); 88 | 89 | 90 | if (scannedUrls.contains(url)) { 91 | return; 92 | } 93 | 94 | 95 | if (isStaticFile(url) && !url.contains("favicon.") && !url.contains(".ico")) { 96 | 97 | return; 98 | } 99 | 100 | 101 | synchronized (scanLock) { 102 | if (scannedUrls.contains(url)) { 103 | return; 104 | } 105 | scannedUrls.add(url); 106 | } 107 | 108 | 109 | if (!fingerPrintTab.isScanEnabled() || !acceptingNewTasks) { 110 | 111 | return; 112 | } 113 | 114 | 115 | executorService.submit(() -> { 116 | try { 117 | 118 | if (!fingerPrintTab.isScanEnabled()) { 119 | 120 | return; 121 | } 122 | processUrl(message, requestResponse, url); 123 | } catch (Exception e) { 124 | callbacks.printError("[!] Error processing URL " + url + ": " + e.getMessage()); 125 | e.printStackTrace(new PrintWriter(callbacks.getStderr(), true)); 126 | } 127 | }); 128 | } 129 | 130 | private void processUrl(IInterceptedProxyMessage message, IHttpRequestResponse requestResponse, String url) { 131 | 132 | if (!fingerPrintTab.isScanEnabled()) { 133 | 134 | return; 135 | } 136 | 137 | 138 | Map totalUrlResponse = new HashMap<>(); 139 | 140 | 141 | Map originalData = new HashMap<>(); 142 | originalData.put("responseRequest", requestResponse); 143 | originalData.put("isFindUrl", false); 144 | originalData.put("method", helpers.analyzeRequest(requestResponse).getMethod()); 145 | totalUrlResponse.put(url, originalData); 146 | 147 | 148 | byte[] responseBytes = requestResponse.getResponse(); 149 | if (responseBytes != null && responseBytes.length > 0 && !url.contains("favicon.") && !url.contains(".ico")) { 150 | IResponseInfo responseInfo = helpers.analyzeResponse(responseBytes); 151 | String mime = responseInfo.getInferredMimeType().toLowerCase(); 152 | URL urlObj = helpers.analyzeRequest(requestResponse).getUrl(); 153 | 154 | 155 | Set urlSet = new HashSet<>(Utils.extractUrlsFromHtml(url, new String(responseBytes))); 156 | 157 | 158 | if (mime.equals("script") || mime.equals("html") || url.contains(".htm") || isExtractableUrl(url)) { 159 | urlSet.addAll(Utils.findUrls(urlObj, new String(responseBytes))); 160 | } 161 | 162 | 163 | 164 | 165 | for (String extractedUrl : urlSet) { 166 | 167 | synchronized (scanLock) { 168 | if (scannedUrls.contains(extractedUrl)) { 169 | continue; 170 | } 171 | scannedUrls.add(extractedUrl); 172 | } 173 | 174 | Map responseData = HTTPUtils.makeGetRequest(extractedUrl, helpers, callbacks); 175 | if (responseData != null) { 176 | totalUrlResponse.put(extractedUrl, responseData); 177 | } 178 | } 179 | } 180 | 181 | 182 | 183 | 184 | for (Map.Entry entry : totalUrlResponse.entrySet()) { 185 | 186 | if (!fingerPrintTab.isScanEnabled()) { 187 | 188 | return; 189 | } 190 | 191 | String oneUrl = entry.getKey(); 192 | Object value = entry.getValue(); 193 | 194 | if (value instanceof Map) { 195 | @SuppressWarnings("unchecked") 196 | Map oneResult = (Map) value; 197 | IHttpRequestResponse oneRequestResponse = (IHttpRequestResponse) oneResult.get("responseRequest"); 198 | 199 | if (oneRequestResponse == null || oneRequestResponse.getResponse() == null) { 200 | continue; 201 | } 202 | 203 | byte[] oneResponseBytes = oneRequestResponse.getResponse(); 204 | if (oneResponseBytes == null || oneResponseBytes.length == 0) { 205 | callbacks.printOutput("返回结果为空: " + oneUrl); 206 | continue; 207 | } 208 | 209 | String oneMethod = (String) oneResult.get("method"); 210 | IResponseInfo responseInfo = helpers.analyzeResponse(oneResponseBytes); 211 | 212 | 213 | TableLogModel result = FingerPrintUtils.fingerFilter( 214 | callbacks, 215 | message.getMessageReference(), 216 | oneUrl, 217 | oneResponseBytes, 218 | oneRequestResponse.getHttpService(), 219 | helpers, 220 | fingerprintRules, 221 | fingerPrintTab.isScanEnabled() 222 | ); 223 | 224 | 225 | if (result == null) { 226 | if (!fingerPrintTab.isScanEnabled()) { 227 | 228 | } 229 | continue; 230 | } 231 | 232 | 233 | if (result.getResult().isEmpty()) { 234 | 235 | continue; 236 | } 237 | 238 | 239 | result.setStatus(Integer.parseInt(Short.toString(responseInfo.getStatusCode()))); 240 | 241 | result.setContentType(responseInfo.getStatedMimeType()); 242 | result.setMethod(oneMethod); 243 | 244 | 245 | 246 | if (!result.getResult().isEmpty()) { 247 | 248 | result.setHttpRequestResponse(oneRequestResponse); 249 | fingerPrintTab.addLogEntry(result); 250 | 251 | fingerPrintTab.updateSuccessCount(successCount.incrementAndGet()); 252 | callbacks.printOutput("[+] 识别到指纹: " + oneUrl + " -> " + result.getResult()); 253 | } 254 | } 255 | } 256 | 257 | 258 | } 259 | 260 | private boolean isStaticFile(String url) { 261 | for (String ext : STATIC_FILE_EXT) { 262 | if (ext.equalsIgnoreCase(Utils.getUriExt(url))) { 263 | return true; 264 | } 265 | } 266 | return false; 267 | } 268 | 269 | private boolean isExtractableUrl(String url) { 270 | for (String ext : STATIC_URL_EXT) { 271 | if (ext.equalsIgnoreCase(Utils.getUriExt(url))) { 272 | return true; 273 | } 274 | } 275 | return false; 276 | } 277 | 278 | public void shutdown() { 279 | if (executorService != null && !executorService.isShutdown()) { 280 | executorService.shutdownNow(); 281 | 282 | } 283 | } 284 | 285 | 286 | public void setAcceptingNewTasks(boolean accepting) { 287 | this.acceptingNewTasks = accepting; 288 | callbacks.printOutput("[+] 指纹扫描器" + (accepting ? "开始" : "停止") + "接受新任务"); 289 | 290 | 291 | if (!accepting && executorService != null) { 292 | 293 | int queueSize = executorService.getQueue().size(); 294 | if (queueSize > 0) { 295 | 296 | executorService.getQueue().clear(); 297 | } 298 | } 299 | } 300 | 301 | /** 302 | * 更新指纹规则 303 | * @param newRules 新的规则列表 304 | */ 305 | public void updateRules(List newRules) { 306 | if (newRules == null) { 307 | callbacks.printError("[!] 更新规则失败: 规则列表为空"); 308 | return; 309 | } 310 | 311 | synchronized (scanLock) { 312 | this.fingerprintRules = new ArrayList<>(newRules); 313 | 314 | } 315 | } 316 | } -------------------------------------------------------------------------------- /src/main/java/AutoBurp/fingerprint/model/FingerPrintRule.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.fingerprint.model; 2 | 3 | import java.util.List; 4 | 5 | public class FingerPrintRule { 6 | private String cms; 7 | private String method; 8 | private String location; 9 | private List keyword; 10 | private boolean isImportant; 11 | private String type; 12 | 13 | public FingerPrintRule() { 14 | } 15 | 16 | public FingerPrintRule(String type, boolean isImportant, String cms, String method, String location, List keyword) { 17 | this.cms = cms; 18 | this.method = method; 19 | this.location = location; 20 | this.keyword = keyword; 21 | this.type = type; 22 | this.isImportant = isImportant; 23 | } 24 | 25 | public String getType() { 26 | return type; 27 | } 28 | 29 | 30 | public boolean getIsImportant() { 31 | return isImportant; 32 | } 33 | 34 | 35 | public String getCms() { 36 | return cms; 37 | } 38 | 39 | public String getMethod() { 40 | return method; 41 | } 42 | 43 | public String getLocation() { 44 | return location; 45 | } 46 | 47 | 48 | public List getKeyword() { 49 | return keyword; 50 | } 51 | 52 | public String getInfo() { 53 | return "cms: " + cms + "\r\nmethod: " + method + "\r\nlocation: " + location + "\r\nkeyword: " + keyword.toString(); 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /src/main/java/AutoBurp/fingerprint/model/TableLogModel.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.fingerprint.model; 2 | 3 | import burp.IHttpRequestResponse; 4 | import burp.IHttpService; 5 | 6 | public class TableLogModel { 7 | private int id; 8 | private String url; 9 | private String method; 10 | private int status; 11 | private String title; 12 | private String result; 13 | private String type; 14 | private boolean isImportant; 15 | private String resultInfo; 16 | 17 | private String matchPattern; 18 | private final IHttpService httpService; 19 | private int requestResponseIndex; 20 | private String time; 21 | 22 | private String contentType; 23 | 24 | public TableLogModel(int id, String url, String method, String title, String result, String type, 25 | String resultInfo, boolean isImportant, IHttpService httpService, 26 | int requestResponseIndex, String currentTime) { 27 | this.id = id; 28 | this.url = url; 29 | this.method = method; 30 | this.title = title; 31 | this.result = result; 32 | this.type = type; 33 | this.resultInfo = resultInfo; 34 | this.isImportant = isImportant; 35 | this.time = currentTime; 36 | this.httpService = httpService; 37 | this.requestResponseIndex = requestResponseIndex; 38 | this.contentType = ""; 39 | this.matchPattern = ""; 40 | } 41 | 42 | 43 | public int getId() { 44 | return id; 45 | } 46 | 47 | 48 | public void setId(int id) { 49 | this.id = id; 50 | } 51 | 52 | public String getUrl() { 53 | return url; 54 | } 55 | 56 | public int getStatus() { 57 | return status; 58 | } 59 | 60 | public void setStatus(int status) { 61 | this.status = status; 62 | } 63 | 64 | public String getTitle() { 65 | return title; 66 | } 67 | 68 | public void setTitle(String title) { 69 | this.title = title; 70 | } 71 | 72 | public String getMethod() { 73 | return method; 74 | } 75 | 76 | public void setMethod(String method) { 77 | this.method = method; 78 | } 79 | 80 | public String getResult() { 81 | return result; 82 | } 83 | 84 | public void setResult(String result) { 85 | this.result = result; 86 | } 87 | 88 | public String getType() { 89 | return type; 90 | } 91 | 92 | public void setType(String type) { 93 | this.type = type; 94 | } 95 | 96 | public boolean getIsImportant() { 97 | return isImportant; 98 | } 99 | 100 | public void setIsImportant(boolean important) { 101 | isImportant = important; 102 | } 103 | 104 | public String getTime() { 105 | return time; 106 | } 107 | 108 | public int getRequestResponseIndex() { 109 | return requestResponseIndex; 110 | } 111 | 112 | public String getResultInfo() { 113 | return resultInfo; 114 | } 115 | 116 | public String getMatchPattern() { 117 | return matchPattern; 118 | } 119 | 120 | public void setMatchPattern(String matchPattern) { 121 | this.matchPattern = matchPattern; 122 | } 123 | 124 | public void setResultInfo(String resultInfo) { 125 | this.resultInfo = resultInfo; 126 | } 127 | 128 | public void setContentType(String contentType) { 129 | this.contentType = contentType; 130 | } 131 | 132 | 133 | private IHttpRequestResponse httpRequestResponse; 134 | 135 | @Override 136 | public String toString() { 137 | return "TableLogModel{" + 138 | "id=" + id + 139 | ", url='" + url + '\'' + 140 | ", status=" + status + 141 | ", title='" + title + '\'' + 142 | ", method='" + method + '\'' + 143 | ", result='" + result + '\'' + 144 | ", type='" + type + '\'' + 145 | ", isImportant=" + isImportant + 146 | ", time='" + time + '\'' + 147 | ", requestResponseIndex=" + requestResponseIndex + 148 | ", resultInfo='" + resultInfo + '\'' + 149 | ", contentType='" + contentType + '\'' + 150 | ", matchPattern='" + matchPattern + '\'' + 151 | '}'; 152 | } 153 | 154 | 155 | public IHttpRequestResponse getHttpRequestResponse() { 156 | return httpRequestResponse; 157 | } 158 | 159 | public void setHttpRequestResponse(IHttpRequestResponse httpRequestResponse) { 160 | this.httpRequestResponse = httpRequestResponse; 161 | } 162 | } -------------------------------------------------------------------------------- /src/main/java/AutoBurp/fingerprint/ui/ControlPanel.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.fingerprint.ui; 2 | 3 | import burp.IBurpExtenderCallbacks; 4 | 5 | import javax.swing.*; 6 | import javax.swing.border.EmptyBorder; 7 | import java.awt.*; 8 | import java.awt.event.MouseAdapter; 9 | import java.awt.event.MouseEvent; 10 | 11 | public class ControlPanel extends JPanel { 12 | private JLabel lbRequestCount; 13 | private JLabel lbSuccessCount; 14 | private JToggleButton scanToggleButton; 15 | private boolean isScanEnabled; 16 | private static final String SCAN_PREF_KEY = "fingerprint_scan_enabled"; 17 | private final IBurpExtenderCallbacks callbacks; 18 | 19 | 20 | private static final Color PRIMARY_COLOR = new Color(60, 141, 188); 21 | private static final Color ACCENT_COLOR = new Color(0, 166, 90); 22 | private static final Color LIGHT_TEXT_COLOR = new Color(119, 119, 119); 23 | private static final Color BORDER_COLOR = new Color(221, 221, 221); 24 | 25 | 26 | private Runnable onRefreshListener; 27 | private Runnable onClearListener; 28 | 29 | 30 | private Runnable onScanStateChangedListener; 31 | 32 | public ControlPanel(IBurpExtenderCallbacks callbacks) { 33 | this.callbacks = callbacks; 34 | 35 | setLayout(new BorderLayout(10, 0)); 36 | setBackground(Color.WHITE); 37 | 38 | setBorder(BorderFactory.createCompoundBorder( 39 | BorderFactory.createLineBorder(BORDER_COLOR, 1, true), 40 | new EmptyBorder(10, 15, 9, 15) 41 | )); 42 | 43 | 44 | JPanel statsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 35, 0)); 45 | statsPanel.setBackground(Color.WHITE); 46 | 47 | 48 | Object[] result = createStatPanel("Total Requests Count: ", "0", PRIMARY_COLOR); 49 | statsPanel.add((JPanel)result[0]); 50 | lbRequestCount = (JLabel)result[1]; 51 | 52 | 53 | result = createStatPanel("Success Requests Count: ", "0", ACCENT_COLOR); 54 | statsPanel.add((JPanel)result[0]); 55 | lbSuccessCount = (JLabel)result[1]; 56 | 57 | add(statsPanel, BorderLayout.WEST); 58 | 59 | 60 | JPanel actionPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 10)); 61 | actionPanel.setBackground(Color.WHITE); 62 | 63 | 64 | 65 | String savedPref = callbacks.loadExtensionSetting(SCAN_PREF_KEY); 66 | isScanEnabled = savedPref != null && savedPref.equals("true"); 67 | 68 | scanToggleButton = new JToggleButton(isScanEnabled ? "扫描: 开启" : "扫描: 关闭"); 69 | scanToggleButton.setSelected(isScanEnabled); 70 | scanToggleButton.setBackground(isScanEnabled ? ACCENT_COLOR : new Color(221, 75, 57)); 71 | scanToggleButton.setForeground(Color.WHITE); 72 | scanToggleButton.setFocusPainted(false); 73 | scanToggleButton.setBorderPainted(false); 74 | scanToggleButton.setFont(new Font(scanToggleButton.getFont().getName(), Font.BOLD, 12)); 75 | scanToggleButton.setPreferredSize(new Dimension(100, 25)); 76 | 77 | scanToggleButton.addActionListener(e -> { 78 | isScanEnabled = scanToggleButton.isSelected(); 79 | scanToggleButton.setText(isScanEnabled ? "扫描: 开启" : "扫描: 关闭"); 80 | scanToggleButton.setBackground(isScanEnabled ? ACCENT_COLOR : new Color(221, 75, 57)); 81 | 82 | callbacks.saveExtensionSetting(SCAN_PREF_KEY, String.valueOf(isScanEnabled)); 83 | 84 | 85 | if (onScanStateChangedListener != null) { 86 | onScanStateChangedListener.run(); 87 | } 88 | }); 89 | 90 | scanToggleButton.addMouseListener(new MouseAdapter() { 91 | @Override 92 | public void mouseEntered(MouseEvent e) { 93 | scanToggleButton.setBackground(scanToggleButton.isSelected() ? 94 | ACCENT_COLOR.darker() : new Color(221, 75, 57).darker()); 95 | } 96 | 97 | @Override 98 | public void mouseExited(MouseEvent e) { 99 | scanToggleButton.setBackground(scanToggleButton.isSelected() ? 100 | ACCENT_COLOR : new Color(221, 75, 57)); 101 | } 102 | }); 103 | 104 | actionPanel.add(scanToggleButton); 105 | 106 | 107 | JButton refreshButton = createStyledButton("刷新", new Color(60, 141, 188)); 108 | refreshButton.addActionListener(e -> { 109 | if (onRefreshListener != null) { 110 | onRefreshListener.run(); 111 | } 112 | }); 113 | actionPanel.add(refreshButton); 114 | 115 | 116 | JButton clearButton = createStyledButton("清空", new Color(221, 75, 57)); 117 | clearButton.addActionListener(e -> { 118 | if (onClearListener != null) { 119 | onClearListener.run(); 120 | } 121 | }); 122 | actionPanel.add(clearButton); 123 | 124 | add(actionPanel, BorderLayout.EAST); 125 | } 126 | 127 | 128 | private Object[] createStatPanel(String title, String value, Color color) { 129 | JPanel panel = new JPanel(); 130 | panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); 131 | panel.setBackground(Color.WHITE); 132 | 133 | JLabel titleLabel = new JLabel(title); 134 | titleLabel.setForeground(LIGHT_TEXT_COLOR); 135 | titleLabel.setFont(new Font(titleLabel.getFont().getName(), Font.PLAIN, 12)); 136 | panel.add(titleLabel); 137 | 138 | 139 | panel.add(Box.createHorizontalStrut(2)); 140 | 141 | JLabel valueLabel = new JLabel(value); 142 | valueLabel.setForeground(color); 143 | valueLabel.setFont(new Font(valueLabel.getFont().getName(), Font.BOLD, 16)); 144 | panel.add(valueLabel); 145 | 146 | 147 | return new Object[]{panel, valueLabel}; 148 | } 149 | 150 | private JButton createStyledButton(String text, Color color) { 151 | JButton button = new JButton(text); 152 | button.setBackground(color); 153 | button.setForeground(Color.WHITE); 154 | button.setFocusPainted(false); 155 | button.setBorderPainted(false); 156 | button.setFont(new Font(button.getFont().getName(), Font.BOLD, 12)); 157 | button.setPreferredSize(new Dimension(80, 30)); 158 | 159 | 160 | button.addMouseListener(new MouseAdapter() { 161 | @Override 162 | public void mouseEntered(MouseEvent e) { 163 | button.setBackground(color.darker()); 164 | } 165 | 166 | @Override 167 | public void mouseExited(MouseEvent e) { 168 | button.setBackground(color); 169 | } 170 | }); 171 | 172 | return button; 173 | } 174 | 175 | public void setRequestCount(int count) { 176 | lbRequestCount.setText(String.valueOf(count)); 177 | } 178 | 179 | public void setSuccessCount(int count) { 180 | lbSuccessCount.setText(String.valueOf(count)); 181 | } 182 | 183 | public boolean isScanEnabled() { 184 | return isScanEnabled; 185 | } 186 | 187 | public void setOnRefreshListener(Runnable listener) { 188 | this.onRefreshListener = listener; 189 | } 190 | 191 | public void setOnClearListener(Runnable listener) { 192 | this.onClearListener = listener; 193 | } 194 | 195 | 196 | public void setOnScanStateChangedListener(Runnable listener) { 197 | this.onScanStateChangedListener = listener; 198 | } 199 | } -------------------------------------------------------------------------------- /src/main/java/AutoBurp/fingerprint/ui/FingerPrintTab.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.fingerprint.ui; 2 | 3 | import AutoBurp.bypass.DomainSettingsPanel; 4 | import AutoBurp.fingerprint.FingerPrintScanner; 5 | import AutoBurp.fingerprint.model.FingerPrintRule; 6 | import AutoBurp.fingerprint.model.TableLogModel; 7 | import burp.*; 8 | 9 | import javax.swing.*; 10 | import javax.swing.border.EmptyBorder; 11 | import java.awt.*; 12 | import java.awt.event.ComponentAdapter; 13 | import java.awt.event.ComponentEvent; 14 | import java.io.PrintWriter; 15 | import java.util.Collections; 16 | import java.util.HashSet; 17 | import java.util.List; 18 | import java.util.Set; 19 | import java.util.concurrent.CopyOnWriteArrayList; 20 | 21 | public class FingerPrintTab extends JPanel implements IMessageEditorController, ITab { 22 | private final IBurpExtenderCallbacks callbacks; 23 | private final IExtensionHelpers helpers; 24 | private final JSplitPane splitPane; 25 | private IHttpRequestResponse currentlyDisplayedItem; 26 | private final List logEntries = new CopyOnWriteArrayList<>(); 27 | private final Set uniqueTypes = Collections.synchronizedSet(new HashSet<>()); 28 | 29 | 30 | private ControlPanel controlPanel; 31 | private TagsPanel tagsPanel; 32 | private LogTablePanel logTablePanel; 33 | private RequestResponsePanel requestResponsePanel; 34 | 35 | 36 | private FingerPrintRulePanel rulePanel; 37 | private JTabbedPane tabbedPane; 38 | 39 | 40 | private DomainSettingsPanel domainSettingsPanel; 41 | 42 | 43 | private static final Color SECONDARY_COLOR = new Color(245, 245, 245); 44 | private FingerPrintScanner scanner; 45 | 46 | private JPanel createTopPanel() { 47 | JPanel panel = new JPanel(new GridBagLayout()); 48 | panel.setBackground(SECONDARY_COLOR); 49 | panel.setBorder(new EmptyBorder(0, 0, 5, 0)); 50 | 51 | GridBagConstraints gbc = new GridBagConstraints(); 52 | gbc.fill = GridBagConstraints.BOTH; 53 | gbc.gridx = 0; 54 | gbc.weightx = 1.0; 55 | 56 | 57 | gbc.gridy = 0; 58 | gbc.weighty = 0.0; 59 | gbc.insets = new Insets(0, 0, 5, 0); 60 | controlPanel = new ControlPanel(callbacks); 61 | controlPanel.setOnRefreshListener(this::refreshTable); 62 | controlPanel.setOnClearListener(this::clearTable); 63 | 64 | controlPanel.setOnScanStateChangedListener(this::onScanStateChanged); 65 | 66 | 67 | controlPanel.setPreferredSize(new Dimension(0, 60)); 68 | panel.add(controlPanel, gbc); 69 | 70 | 71 | gbc.gridy = 1; 72 | gbc.insets = new Insets(0, 0, 5, 0); 73 | tagsPanel = new TagsPanel(); 74 | tagsPanel.setOnTagSelectedListener(this::filterTableByType); 75 | 76 | tagsPanel.setPreferredSize(new Dimension(0, 50)); 77 | tagsPanel.setMinimumSize(new Dimension(0, 50)); 78 | panel.add(tagsPanel, gbc); 79 | 80 | 81 | gbc.gridy = 2; 82 | gbc.weighty = 1.0; 83 | gbc.insets = new Insets(0, 0, 0, 0); 84 | logTablePanel = new LogTablePanel(callbacks, helpers, logEntries, uniqueTypes); 85 | logTablePanel.setOnRowSelectedListener(this::onLogEntrySelected); 86 | logTablePanel.setOnTypeFilterChangedListener(tagsPanel::selectTag); 87 | 88 | logTablePanel.setBorder(null); 89 | panel.add(logTablePanel, gbc); 90 | 91 | return panel; 92 | } 93 | 94 | 95 | public FingerPrintTab(IBurpExtenderCallbacks callbacks, IExtensionHelpers helpers) { 96 | this.callbacks = callbacks; 97 | this.helpers = helpers; 98 | 99 | 100 | setLayout(new BorderLayout()); 101 | setBackground(SECONDARY_COLOR); 102 | setBorder(new EmptyBorder(10, 10, 10, 10)); 103 | 104 | 105 | tabbedPane = new JTabbedPane(); 106 | tabbedPane.setBackground(Color.WHITE); 107 | 108 | 109 | splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); 110 | splitPane.setBorder(null); 111 | splitPane.setBackground(SECONDARY_COLOR); 112 | splitPane.setDividerSize(5); 113 | 114 | splitPane.setContinuousLayout(true); 115 | 116 | 117 | JPanel topPanel = createTopPanel(); 118 | splitPane.setLeftComponent(topPanel); 119 | 120 | 121 | requestResponsePanel = new RequestResponsePanel(callbacks, this); 122 | splitPane.setRightComponent(requestResponsePanel); 123 | 124 | 125 | splitPane.setResizeWeight(0.75); 126 | 127 | 128 | addComponentListener(new ComponentAdapter() { 129 | @Override 130 | public void componentResized(ComponentEvent e) { 131 | SwingUtilities.invokeLater(() -> { 132 | int height = getHeight(); 133 | splitPane.setDividerLocation((int)(height * 0.70)); 134 | }); 135 | } 136 | }); 137 | 138 | 139 | tabbedPane.addTab("指纹识别", splitPane); 140 | 141 | 142 | add(tabbedPane, BorderLayout.CENTER); 143 | 144 | } 145 | 146 | 147 | public void setRulePanel(List rules) { 148 | 149 | rulePanel = new FingerPrintRulePanel(callbacks, helpers, rules); 150 | 151 | 152 | rulePanel.setOnRulesUpdatedListener(this::onRulesUpdated); 153 | 154 | 155 | tabbedPane.addTab("指纹管理", rulePanel); 156 | 157 | 158 | 159 | } 160 | 161 | private void onLogEntrySelected(TableLogModel entry) { 162 | 163 | if (entry != null) { 164 | try { 165 | 166 | IHttpRequestResponse requestResponse = entry.getHttpRequestResponse(); 167 | 168 | if (requestResponse != null) { 169 | currentlyDisplayedItem = requestResponse; 170 | requestResponsePanel.setRequestResponse(currentlyDisplayedItem); 171 | 172 | 173 | 174 | 175 | } else { 176 | 177 | 178 | currentlyDisplayedItem = null; 179 | requestResponsePanel.clear(); 180 | } 181 | } catch (Exception e) { 182 | callbacks.printError("[!] 加载请求/响应数据时出错: " + e.getMessage()); 183 | e.printStackTrace(new PrintWriter(callbacks.getStderr(), true)); 184 | currentlyDisplayedItem = null; 185 | requestResponsePanel.clear(); 186 | } 187 | } else { 188 | currentlyDisplayedItem = null; 189 | requestResponsePanel.clear(); 190 | } 191 | } 192 | 193 | private void filterTableByType(String type) { 194 | logTablePanel.filterTable(type, null); 195 | } 196 | 197 | private void refreshTable() { 198 | logTablePanel.filterTable(null, null); 199 | } 200 | 201 | public void addLogEntry(TableLogModel entry) { 202 | if (entry != null) { 203 | 204 | synchronized (logEntries) { 205 | 206 | entry.setId(logEntries.size() + 1); 207 | 208 | 209 | if (entry.getHttpRequestResponse() == null) { 210 | callbacks.printError("[!] 警告: 添加的日志条目没有关联的请求/响应对象: " + entry.getUrl()); 211 | } 212 | 213 | logEntries.add(entry); 214 | 215 | 216 | final int entriesSize = logEntries.size(); 217 | 218 | 219 | final long successCount = logEntries.stream() 220 | .filter(e -> e.getStatus() >= 200 && e.getStatus() < 400) 221 | .count(); 222 | 223 | 224 | boolean needUpdateTags = false; 225 | if (entry.getType() != null && !entry.getType().isEmpty()) { 226 | synchronized (uniqueTypes) { 227 | needUpdateTags = uniqueTypes.add(entry.getType()); 228 | } 229 | } 230 | 231 | 232 | final boolean finalNeedUpdateTags = needUpdateTags; 233 | SwingUtilities.invokeLater(() -> { 234 | controlPanel.setRequestCount(entriesSize); 235 | controlPanel.setSuccessCount((int) successCount); 236 | 237 | 238 | if (finalNeedUpdateTags && entry.getType() != null && !entry.getType().isEmpty()) { 239 | tagsPanel.addTag(entry.getType()); 240 | } 241 | 242 | 243 | logTablePanel.safeUpdateTable(); 244 | }); 245 | } 246 | } 247 | } 248 | 249 | 250 | private void clearTable() { 251 | 252 | synchronized (logEntries) { 253 | logEntries.clear(); 254 | 255 | synchronized (uniqueTypes) { 256 | uniqueTypes.clear(); 257 | } 258 | 259 | 260 | SwingUtilities.invokeLater(() -> { 261 | logTablePanel.clearTable(); 262 | 263 | logTablePanel.safeUpdateTable(); 264 | tagsPanel.clearTags(); 265 | requestResponsePanel.clear(); 266 | controlPanel.setRequestCount(0); 267 | controlPanel.setSuccessCount(0); 268 | }); 269 | } 270 | } 271 | 272 | public boolean isScanEnabled() { 273 | return controlPanel.isScanEnabled(); 274 | } 275 | 276 | 277 | @Override 278 | public IHttpService getHttpService() { 279 | return currentlyDisplayedItem == null ? null : currentlyDisplayedItem.getHttpService(); 280 | } 281 | 282 | @Override 283 | public byte[] getRequest() { 284 | return currentlyDisplayedItem == null ? null : currentlyDisplayedItem.getRequest(); 285 | } 286 | 287 | @Override 288 | public byte[] getResponse() { 289 | return currentlyDisplayedItem == null ? null : currentlyDisplayedItem.getResponse(); 290 | } 291 | 292 | 293 | @Override 294 | public String getTabCaption() { 295 | return "Auto fuzz"; 296 | } 297 | 298 | @Override 299 | public Component getUiComponent() { 300 | return this; 301 | } 302 | 303 | 304 | public void updateRequestCount(int count) { 305 | controlPanel.setRequestCount(count); 306 | } 307 | 308 | public void updateSuccessCount(int count) { 309 | controlPanel.setSuccessCount(count); 310 | } 311 | 312 | /** 313 | * 处理扫描状态变化的方法 314 | */ 315 | 316 | 317 | public void setScanner(FingerPrintScanner scanner) { 318 | this.scanner = scanner; 319 | } 320 | 321 | 322 | private void onScanStateChanged() { 323 | boolean isEnabled = controlPanel.isScanEnabled(); 324 | 325 | 326 | if (scanner != null) { 327 | scanner.setAcceptingNewTasks(isEnabled); 328 | } 329 | 330 | 331 | SwingUtilities.invokeLater(() -> { 332 | JOptionPane.showMessageDialog(this, 333 | "指纹识别扫描已" + (isEnabled ? "开启" : "停止") + "," + 334 | (isEnabled ? "新的请求将被扫描。" : "新的请求将不再被扫描。"), 335 | "扫描状态变更", 336 | JOptionPane.INFORMATION_MESSAGE); 337 | }); 338 | 339 | 340 | 341 | } 342 | 343 | 344 | private void onRulesUpdated(List updatedRules) { 345 | 346 | if (scanner != null) { 347 | scanner.updateRules(updatedRules); 348 | 349 | 350 | 351 | SwingUtilities.invokeLater(() -> { 352 | JOptionPane.showMessageDialog(this, 353 | "指纹规则已成功更新,共 " + updatedRules.size() + " 条规则", 354 | "规则更新成功", 355 | JOptionPane.INFORMATION_MESSAGE); 356 | }); 357 | } else { 358 | callbacks.printError("[!] 无法更新指纹规则:扫描器未初始化"); 359 | } 360 | } 361 | } -------------------------------------------------------------------------------- /src/main/java/AutoBurp/fingerprint/ui/LogTablePanel.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.fingerprint.ui; 2 | 3 | import AutoBurp.fingerprint.model.TableLogModel; 4 | import AutoBurp.fingerprint.ui.renderer.CenterRenderer; 5 | import AutoBurp.fingerprint.ui.renderer.HeaderIconRenderer; 6 | import burp.IBurpExtenderCallbacks; 7 | import burp.IExtensionHelpers; 8 | import burp.IHttpRequestResponse; 9 | 10 | import javax.swing.*; 11 | import javax.swing.border.EmptyBorder; 12 | import javax.swing.border.MatteBorder; 13 | import javax.swing.plaf.basic.BasicScrollBarUI; 14 | import javax.swing.table.DefaultTableCellRenderer; 15 | import javax.swing.table.DefaultTableModel; 16 | import javax.swing.table.JTableHeader; 17 | import javax.swing.table.TableRowSorter; 18 | import java.awt.*; 19 | import java.awt.event.MouseAdapter; 20 | import java.awt.event.MouseEvent; 21 | import java.util.*; 22 | import java.util.List; 23 | import java.util.function.Consumer; 24 | 25 | 26 | @SuppressWarnings("unchecked") 27 | public class LogTablePanel extends JPanel { 28 | private final IBurpExtenderCallbacks callbacks; 29 | private final IExtensionHelpers helpers; 30 | private JTable logTable; 31 | private DefaultTableModel logTableModel; 32 | private final List logEntries; 33 | private final Set uniqueTypes; 34 | private Consumer onRowSelectedListener; 35 | private Consumer typeFilterChangedListener; 36 | 37 | 38 | private final Object tableLock = new Object(); 39 | private volatile boolean isUpdating = false; 40 | 41 | 42 | private static final Color BACKGROUND_COLOR = new Color(252, 252, 252); 43 | private static final Color HEADER_BACKGROUND = new Color(245, 247, 250); 44 | private static final Color HEADER_FOREGROUND = new Color(80, 90, 108); 45 | private static final Color BORDER_COLOR = new Color(230, 235, 240); 46 | private static final Color ALTERNATE_ROW_COLOR = new Color(248, 250, 252); 47 | private static final Color TEXT_COLOR = new Color(60, 70, 85); 48 | private static final Color SELECTION_BACKGROUND = new Color(66, 139, 202, 160); 49 | private static final Color SELECTION_FOREGROUND = Color.WHITE; 50 | 51 | 52 | private static final int CORNER_RADIUS = 8; 53 | 54 | public LogTablePanel(IBurpExtenderCallbacks callbacks, IExtensionHelpers helpers, 55 | List logEntries, Set uniqueTypes) { 56 | this.callbacks = callbacks; 57 | this.helpers = helpers; 58 | this.logEntries = logEntries; 59 | this.uniqueTypes = uniqueTypes; 60 | 61 | setLayout(new BorderLayout(0, 0)); 62 | setBackground(BACKGROUND_COLOR); 63 | setBorder(new EmptyBorder(12, 12, 12, 12)); 64 | 65 | 66 | createLogTable(); 67 | 68 | 69 | JScrollPane tableScrollPane = new JScrollPane(logTable) { 70 | @Override 71 | protected void paintComponent(Graphics g) { 72 | Graphics2D g2 = (Graphics2D) g.create(); 73 | g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 74 | g2.setColor(BACKGROUND_COLOR); 75 | g2.fillRoundRect(0, 0, getWidth()-1, getHeight()-1, CORNER_RADIUS, CORNER_RADIUS); 76 | 77 | 78 | for (int i = 0; i < 3; i++) { 79 | g2.setColor(new Color(0, 0, 0, 3 - i)); 80 | g2.drawRoundRect(i, i, getWidth() - 1 - 2*i, getHeight() - 1 - 2*i, CORNER_RADIUS, CORNER_RADIUS); 81 | } 82 | g2.dispose(); 83 | } 84 | }; 85 | 86 | tableScrollPane.setBorder(null); 87 | tableScrollPane.getViewport().setBackground(BACKGROUND_COLOR); 88 | tableScrollPane.setOpaque(false); 89 | tableScrollPane.getViewport().setOpaque(false); 90 | 91 | 92 | JScrollBar verticalScrollBar = tableScrollPane.getVerticalScrollBar(); 93 | verticalScrollBar.setUI(new BasicScrollBarUI() { 94 | @Override 95 | protected void configureScrollBarColors() { 96 | this.thumbColor = new Color(180, 190, 200, 120); 97 | this.trackColor = BACKGROUND_COLOR; 98 | } 99 | 100 | @Override 101 | protected JButton createDecreaseButton(int orientation) { 102 | return createZeroButton(); 103 | } 104 | 105 | @Override 106 | protected JButton createIncreaseButton(int orientation) { 107 | return createZeroButton(); 108 | } 109 | 110 | private JButton createZeroButton() { 111 | JButton button = new JButton(); 112 | button.setPreferredSize(new Dimension(0, 0)); 113 | button.setMinimumSize(new Dimension(0, 0)); 114 | button.setMaximumSize(new Dimension(0, 0)); 115 | return button; 116 | } 117 | 118 | @Override 119 | protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) { 120 | if (thumbBounds.isEmpty() || !scrollbar.isEnabled()) { 121 | return; 122 | } 123 | 124 | Graphics2D g2 = (Graphics2D) g.create(); 125 | g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 126 | g2.setColor(thumbColor); 127 | g2.fillRoundRect(thumbBounds.x + 2, thumbBounds.y + 2, 128 | thumbBounds.width - 4, thumbBounds.height - 4, 8, 8); 129 | g2.dispose(); 130 | } 131 | }); 132 | 133 | verticalScrollBar.setPreferredSize(new Dimension(8, 0)); 134 | 135 | add(tableScrollPane, BorderLayout.CENTER); 136 | } 137 | 138 | private void createLogTable() { 139 | 140 | logTableModel = new DefaultTableModel() { 141 | @Override 142 | public boolean isCellEditable(int row, int column) { 143 | return false; 144 | } 145 | 146 | @Override 147 | public Class getColumnClass(int columnIndex) { 148 | if (columnIndex == 7) { 149 | return Boolean.class; 150 | } 151 | return String.class; 152 | } 153 | }; 154 | 155 | 156 | logTableModel.addColumn("ID"); 157 | logTableModel.addColumn("URL"); 158 | logTableModel.addColumn("Status"); 159 | logTableModel.addColumn("Title"); 160 | logTableModel.addColumn("Method"); 161 | logTableModel.addColumn("Result"); 162 | logTableModel.addColumn("Type"); 163 | logTableModel.addColumn("Important"); 164 | logTableModel.addColumn("Match Pattern"); 165 | logTableModel.addColumn("Time"); 166 | 167 | 168 | logTable = new JTable(logTableModel) { 169 | 170 | @Override 171 | public void paint(Graphics g) { 172 | Graphics2D g2 = (Graphics2D) g.create(); 173 | g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 174 | g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); 175 | super.paint(g2); 176 | g2.dispose(); 177 | } 178 | }; 179 | 180 | 181 | logTable.setShowGrid(false); 182 | logTable.setIntercellSpacing(new Dimension(0, 0)); 183 | logTable.setRowHeight(28); 184 | logTable.setBackground(BACKGROUND_COLOR); 185 | logTable.setForeground(TEXT_COLOR); 186 | 187 | 188 | logTable.setSelectionBackground(SELECTION_BACKGROUND); 189 | logTable.setSelectionForeground(SELECTION_FOREGROUND); 190 | 191 | logTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); 192 | logTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 193 | logTable.setFont(new Font(logTable.getFont().getName(), Font.PLAIN, 12)); 194 | 195 | 196 | JTableHeader header = logTable.getTableHeader(); 197 | header.setBackground(HEADER_BACKGROUND); 198 | header.setForeground(HEADER_FOREGROUND); 199 | header.setFont(new Font(header.getFont().getName(), Font.PLAIN, 12)); 200 | header.setBorder(new MatteBorder(0, 0, 1, 0, BORDER_COLOR)); 201 | header.setPreferredSize(new Dimension(header.getPreferredSize().width, 36)); 202 | 203 | 204 | logTable.getColumnModel().getColumn(0).setPreferredWidth(50); 205 | logTable.getColumnModel().getColumn(1).setPreferredWidth(300); 206 | logTable.getColumnModel().getColumn(2).setPreferredWidth(60); 207 | logTable.getColumnModel().getColumn(3).setPreferredWidth(150); 208 | logTable.getColumnModel().getColumn(4).setPreferredWidth(60); 209 | logTable.getColumnModel().getColumn(5).setPreferredWidth(150); 210 | logTable.getColumnModel().getColumn(6).setPreferredWidth(100); 211 | logTable.getColumnModel().getColumn(7).setPreferredWidth(80); 212 | logTable.getColumnModel().getColumn(8).setPreferredWidth(200); 213 | logTable.getColumnModel().getColumn(9).setPreferredWidth(150); 214 | 215 | 216 | DefaultTableCellRenderer centerRenderer = new CenterRenderer() { 217 | @Override 218 | public Component getTableCellRendererComponent(JTable table, Object value, 219 | boolean isSelected, boolean hasFocus, int row, int column) { 220 | Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 221 | ((JComponent)c).setBorder(new EmptyBorder(0, 8, 0, 8)); 222 | return c; 223 | } 224 | }; 225 | centerRenderer.setBackground(BACKGROUND_COLOR); 226 | 227 | 228 | logTable.getColumnModel().getColumn(0).setCellRenderer(centerRenderer); 229 | logTable.getColumnModel().getColumn(2).setCellRenderer(centerRenderer); 230 | logTable.getColumnModel().getColumn(4).setCellRenderer(centerRenderer); 231 | 232 | 233 | HeaderIconRenderer headerRenderer = new HeaderIconRenderer() { 234 | @Override 235 | public Component getTableCellRendererComponent(JTable table, Object value, 236 | boolean isSelected, boolean hasFocus, int row, int column) { 237 | Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 238 | ((JComponent)c).setBorder(new EmptyBorder(0, 8, 0, 8)); 239 | return c; 240 | } 241 | }; 242 | 243 | 244 | headerRenderer.addFilterColumn(6); 245 | headerRenderer.addFilterColumn(7); 246 | header.setDefaultRenderer(headerRenderer); 247 | 248 | 249 | header.addMouseListener(new MouseAdapter() { 250 | @Override 251 | public void mouseClicked(MouseEvent e) { 252 | int columnIndex = logTable.getColumnModel().getColumnIndexAtX(e.getX()); 253 | 254 | if (columnIndex == 6) { 255 | showTypeFilterMenu(e); 256 | } else if (columnIndex == 7) { 257 | showImportantFilterMenu(e); 258 | } 259 | } 260 | }); 261 | 262 | 263 | logTable.getSelectionModel().addListSelectionListener(e -> { 264 | if (!e.getValueIsAdjusting()) { 265 | int selectedRow = logTable.getSelectedRow(); 266 | if (selectedRow != -1) { 267 | try { 268 | 269 | int modelRow = logTable.convertRowIndexToModel(selectedRow); 270 | int id = Integer.parseInt(logTableModel.getValueAt(modelRow, 0).toString()); 271 | 272 | 273 | for (TableLogModel entry : logEntries) { 274 | if (entry.getId() == id) { 275 | 276 | if (entry.getHttpRequestResponse() != null) { 277 | if (onRowSelectedListener != null) { 278 | onRowSelectedListener.accept(entry); 279 | } 280 | } 281 | break; 282 | } 283 | } 284 | } catch (Exception ex) { 285 | 286 | System.err.println("处理行选择时出错: " + ex.getMessage()); 287 | } 288 | } 289 | } 290 | }); 291 | 292 | 293 | TableRowSorter sorter = new TableRowSorter<>(logTableModel); 294 | logTable.setRowSorter(sorter); 295 | 296 | 297 | logTable.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() { 298 | @Override 299 | public Component getTableCellRendererComponent(JTable table, Object value, 300 | boolean isSelected, boolean hasFocus, int row, int column) { 301 | Component c = super.getTableCellRendererComponent( 302 | table, value, isSelected, hasFocus, row, column); 303 | 304 | if (isSelected) { 305 | c.setBackground(SELECTION_BACKGROUND); 306 | c.setForeground(SELECTION_FOREGROUND); 307 | } else { 308 | 309 | if (row % 2 == 0) { 310 | c.setBackground(BACKGROUND_COLOR); 311 | } else { 312 | c.setBackground(ALTERNATE_ROW_COLOR); 313 | } 314 | c.setForeground(TEXT_COLOR); 315 | } 316 | 317 | 318 | ((JComponent) c).setBorder(BorderFactory.createEmptyBorder(0, 8, 0, 8)); 319 | 320 | return c; 321 | } 322 | }); 323 | 324 | 325 | logTable.setDefaultRenderer(Boolean.class, new DefaultTableCellRenderer() { 326 | @Override 327 | public Component getTableCellRendererComponent(JTable table, Object value, 328 | boolean isSelected, boolean hasFocus, int row, int column) { 329 | 330 | JCheckBox checkBox = new JCheckBox(); 331 | checkBox.setHorizontalAlignment(SwingConstants.CENTER); 332 | checkBox.setSelected(value != null && (Boolean) value); 333 | 334 | if (isSelected) { 335 | checkBox.setBackground(SELECTION_BACKGROUND); 336 | checkBox.setForeground(SELECTION_FOREGROUND); 337 | } else { 338 | if (row % 2 == 0) { 339 | checkBox.setBackground(BACKGROUND_COLOR); 340 | } else { 341 | checkBox.setBackground(ALTERNATE_ROW_COLOR); 342 | } 343 | checkBox.setForeground(TEXT_COLOR); 344 | } 345 | 346 | return checkBox; 347 | } 348 | }); 349 | } 350 | 351 | private void showTypeFilterMenu(MouseEvent e) { 352 | JPopupMenu filterMenu = createStyledPopupMenu(); 353 | 354 | 355 | JMenuItem allItem = createStyledMenuItem("全部"); 356 | allItem.addActionListener(e1 -> { 357 | filterTable(null, null); 358 | if (typeFilterChangedListener != null) { 359 | typeFilterChangedListener.accept("全部"); 360 | } 361 | }); 362 | filterMenu.add(allItem); 363 | 364 | filterMenu.add(new JSeparator()); 365 | 366 | 367 | for (String type : uniqueTypes) { 368 | JMenuItem menuItem = createStyledMenuItem(type); 369 | menuItem.addActionListener(e1 -> { 370 | filterTable(type, null); 371 | if (typeFilterChangedListener != null) { 372 | typeFilterChangedListener.accept(type); 373 | } 374 | }); 375 | filterMenu.add(menuItem); 376 | } 377 | 378 | showStyledPopupMenu(filterMenu, e); 379 | } 380 | 381 | private void showImportantFilterMenu(MouseEvent e) { 382 | JPopupMenu filterMenu = createStyledPopupMenu(); 383 | 384 | List importantOptions = Arrays.asList("全部", "重点", "普通"); 385 | 386 | for (String option : importantOptions) { 387 | JMenuItem menuItem = createStyledMenuItem(option); 388 | menuItem.addActionListener(e1 -> { 389 | Boolean important = null; 390 | if (option.equals("重点")) { 391 | important = true; 392 | } else if (option.equals("普通")) { 393 | important = false; 394 | } 395 | 396 | filterTable(null, important); 397 | }); 398 | filterMenu.add(menuItem); 399 | } 400 | 401 | showStyledPopupMenu(filterMenu, e); 402 | } 403 | 404 | 405 | private JPopupMenu createStyledPopupMenu() { 406 | JPopupMenu menu = new JPopupMenu(); 407 | menu.setBorder(BorderFactory.createCompoundBorder( 408 | BorderFactory.createLineBorder(BORDER_COLOR, 1), 409 | BorderFactory.createEmptyBorder(4, 0, 4, 0) 410 | )); 411 | menu.setBackground(BACKGROUND_COLOR); 412 | return menu; 413 | } 414 | 415 | 416 | private JMenuItem createStyledMenuItem(String text) { 417 | JMenuItem item = new JMenuItem(text); 418 | item.setFont(new Font("Segoe UI", Font.PLAIN, 12)); 419 | item.setBackground(BACKGROUND_COLOR); 420 | item.setForeground(TEXT_COLOR); 421 | 422 | 423 | item.addMouseListener(new MouseAdapter() { 424 | @Override 425 | public void mouseEntered(MouseEvent e) { 426 | item.setBackground(new Color(240, 245, 250)); 427 | } 428 | 429 | @Override 430 | public void mouseExited(MouseEvent e) { 431 | item.setBackground(BACKGROUND_COLOR); 432 | } 433 | }); 434 | 435 | return item; 436 | } 437 | 438 | 439 | private void showStyledPopupMenu(JPopupMenu menu, MouseEvent e) { 440 | menu.show(e.getComponent(), e.getX(), e.getY()); 441 | } 442 | 443 | @SuppressWarnings("unchecked") 444 | private void sortTable(String column) { 445 | TableRowSorter sorter = (TableRowSorter) logTable.getRowSorter(); 446 | List sortKeys = new ArrayList<>(); 447 | 448 | int columnIndex = -1; 449 | switch (column) { 450 | case "Type": 451 | columnIndex = 6; 452 | break; 453 | case "Result": 454 | columnIndex = 5; 455 | break; 456 | case "Important": 457 | columnIndex = 7; 458 | break; 459 | case "Time": 460 | columnIndex = 8; 461 | break; 462 | } 463 | 464 | if (columnIndex != -1) { 465 | 466 | List currentKeys = sorter.getSortKeys(); 467 | SortOrder order = SortOrder.ASCENDING; 468 | 469 | if (!currentKeys.isEmpty() && currentKeys.get(0).getColumn() == columnIndex) { 470 | 471 | order = currentKeys.get(0).getSortOrder() == SortOrder.ASCENDING ? 472 | SortOrder.DESCENDING : SortOrder.ASCENDING; 473 | } 474 | 475 | sortKeys.add(new RowSorter.SortKey(columnIndex, order)); 476 | sorter.setSortKeys(sortKeys); 477 | sorter.sort(); 478 | } 479 | } 480 | 481 | public void setOnTypeFilterChangedListener(Consumer listener) { 482 | this.typeFilterChangedListener = listener; 483 | } 484 | 485 | public void setOnRowSelectedListener(Consumer listener) { 486 | this.onRowSelectedListener = listener; 487 | } 488 | 489 | 490 | private void updateTableInternal() { 491 | synchronized (tableLock) { 492 | if (isUpdating) { 493 | return; 494 | } 495 | 496 | isUpdating = true; 497 | try { 498 | 499 | int selectedId = -1; 500 | int selectedRow = logTable.getSelectedRow(); 501 | if (selectedRow != -1) { 502 | try { 503 | int modelRow = logTable.convertRowIndexToModel(selectedRow); 504 | if (modelRow >= 0 && modelRow < logTableModel.getRowCount()) { 505 | Object idValue = logTableModel.getValueAt(modelRow, 0); 506 | if (idValue != null) { 507 | selectedId = Integer.parseInt(idValue.toString()); 508 | } 509 | } 510 | } catch (Exception e) { 511 | 512 | } 513 | } 514 | 515 | 516 | logTableModel.setRowCount(0); 517 | 518 | 519 | List rowsToAdd = new ArrayList<>(); 520 | for (TableLogModel entry : logEntries) { 521 | 522 | String url = entry.getUrl(); 523 | String fullUrl = getFullUrl(entry, url); 524 | 525 | rowsToAdd.add(new Object[]{ 526 | entry.getId(), 527 | fullUrl, 528 | entry.getStatus(), 529 | entry.getTitle(), 530 | entry.getMethod(), 531 | entry.getResult(), 532 | entry.getType(), 533 | entry.getIsImportant(), 534 | entry.getMatchPattern(), 535 | entry.getTime() 536 | }); 537 | } 538 | 539 | 540 | for (Object[] row : rowsToAdd) { 541 | logTableModel.addRow(row); 542 | } 543 | 544 | 545 | if (selectedId != -1) { 546 | for (int i = 0; i < logTableModel.getRowCount(); i++) { 547 | try { 548 | Object idValue = logTableModel.getValueAt(i, 0); 549 | if (idValue != null && Integer.parseInt(idValue.toString()) == selectedId) { 550 | int viewRow = logTable.convertRowIndexToView(i); 551 | if (viewRow >= 0) { 552 | logTable.setRowSelectionInterval(viewRow, viewRow); 553 | logTable.scrollRectToVisible(logTable.getCellRect(viewRow, 0, true)); 554 | break; 555 | } 556 | } 557 | } catch (Exception e) { 558 | 559 | } 560 | } 561 | } else if (logTableModel.getRowCount() > 0) { 562 | 563 | int lastRow = logTable.convertRowIndexToView(logTableModel.getRowCount() - 1); 564 | if (lastRow >= 0) { 565 | logTable.scrollRectToVisible(logTable.getCellRect(lastRow, 0, true)); 566 | } 567 | } 568 | } finally { 569 | isUpdating = false; 570 | } 571 | } 572 | } 573 | 574 | 575 | public void safeUpdateTable() { 576 | if (SwingUtilities.isEventDispatchThread()) { 577 | updateTableInternal(); 578 | } else { 579 | SwingUtilities.invokeLater(this::updateTableInternal); 580 | } 581 | } 582 | 583 | 584 | public void filterTable(String type, Boolean important) { 585 | if (SwingUtilities.isEventDispatchThread()) { 586 | filterTableInternal(type, important); 587 | } else { 588 | SwingUtilities.invokeLater(() -> filterTableInternal(type, important)); 589 | } 590 | } 591 | 592 | 593 | private void filterTableInternal(String type, Boolean important) { 594 | synchronized (tableLock) { 595 | DefaultTableModel model = (DefaultTableModel) logTable.getModel(); 596 | model.setRowCount(0); 597 | 598 | 599 | List rowsToAdd = new ArrayList<>(); 600 | 601 | for (TableLogModel entry : logEntries) { 602 | boolean typeMatch = type == null || entry.getType().equals(type); 603 | boolean importantMatch = important == null || entry.getIsImportant() == important; 604 | 605 | if (typeMatch && importantMatch) { 606 | 607 | String url = entry.getUrl(); 608 | String fullUrl = getFullUrl(entry, url); 609 | 610 | 611 | rowsToAdd.add(new Object[]{ 612 | entry.getId(), 613 | fullUrl, 614 | entry.getStatus(), 615 | entry.getTitle(), 616 | entry.getMethod(), 617 | entry.getResult(), 618 | entry.getType(), 619 | entry.getIsImportant(), 620 | entry.getMatchPattern(), 621 | entry.getTime() 622 | }); 623 | } 624 | } 625 | 626 | 627 | for (Object[] row : rowsToAdd) { 628 | model.addRow(row); 629 | } 630 | } 631 | } 632 | 633 | 634 | private String getFullUrl(TableLogModel entry, String url) { 635 | if (url == null || url.isEmpty()) { 636 | return ""; 637 | } 638 | 639 | 640 | if (url.startsWith("http://") || url.startsWith("https://")) { 641 | return url; 642 | } 643 | 644 | 645 | try { 646 | int index = entry.getRequestResponseIndex(); 647 | if (index >= 0) { 648 | IHttpRequestResponse[] proxyHistory = callbacks.getProxyHistory(); 649 | if (proxyHistory != null && index < proxyHistory.length) { 650 | IHttpRequestResponse requestResponse = proxyHistory[index]; 651 | if (requestResponse != null && requestResponse.getHttpService() != null) { 652 | String protocol = requestResponse.getHttpService().getProtocol(); 653 | String host = requestResponse.getHttpService().getHost(); 654 | int port = requestResponse.getHttpService().getPort(); 655 | 656 | StringBuilder fullUrlBuilder = new StringBuilder(); 657 | fullUrlBuilder.append(protocol).append("://").append(host); 658 | 659 | if ((protocol.equals("http") && port != 80) || 660 | (protocol.equals("https") && port != 443)) { 661 | fullUrlBuilder.append(":").append(port); 662 | } 663 | 664 | if (!url.startsWith("/")) { 665 | fullUrlBuilder.append("/"); 666 | } 667 | 668 | fullUrlBuilder.append(url); 669 | return fullUrlBuilder.toString(); 670 | } 671 | } 672 | } 673 | } catch (Exception e) { 674 | 675 | System.err.println("构建完整URL时出错: " + e.getMessage()); 676 | } 677 | 678 | 679 | return url; 680 | } 681 | 682 | public void clearTable() { 683 | if (SwingUtilities.isEventDispatchThread()) { 684 | synchronized (tableLock) { 685 | logTableModel.setRowCount(0); 686 | } 687 | } else { 688 | SwingUtilities.invokeLater(() -> { 689 | synchronized (tableLock) { 690 | logTableModel.setRowCount(0); 691 | } 692 | }); 693 | } 694 | } 695 | } -------------------------------------------------------------------------------- /src/main/java/AutoBurp/fingerprint/ui/RequestResponsePanel.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.fingerprint.ui; 2 | 3 | import burp.*; 4 | 5 | import javax.swing.*; 6 | import java.awt.*; 7 | import java.util.Objects; 8 | 9 | public class RequestResponsePanel extends JPanel { 10 | private final IBurpExtenderCallbacks callbacks; 11 | private final IMessageEditorController controller; 12 | 13 | 14 | private IMessageEditor requestEditor; 15 | private IMessageEditor responseEditor; 16 | 17 | public RequestResponsePanel(IBurpExtenderCallbacks callbacks, IMessageEditorController controller) { 18 | this.callbacks = callbacks; 19 | this.controller = controller; 20 | 21 | setLayout(new BorderLayout()); 22 | 23 | 24 | JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); 25 | splitPane.setDividerLocation(0.5); 26 | splitPane.setContinuousLayout(true); 27 | add(splitPane, BorderLayout.CENTER); 28 | 29 | 30 | requestEditor = callbacks.createMessageEditor(controller, false); 31 | JPanel requestPanel = new JPanel(new BorderLayout()); 32 | requestPanel.add(new JLabel("请求"), BorderLayout.NORTH); 33 | requestPanel.add(requestEditor.getComponent(), BorderLayout.CENTER); 34 | splitPane.setLeftComponent(requestPanel); 35 | 36 | 37 | responseEditor = callbacks.createMessageEditor(controller, false); 38 | JPanel responsePanel = new JPanel(new BorderLayout()); 39 | responsePanel.add(new JLabel("响应"), BorderLayout.NORTH); 40 | responsePanel.add(responseEditor.getComponent(), BorderLayout.CENTER); 41 | splitPane.setRightComponent(responsePanel); 42 | } 43 | 44 | public void setRequestResponse(IHttpRequestResponse requestResponse) { 45 | if (requestResponse != null) { 46 | 47 | byte[] request = requestResponse.getRequest(); 48 | byte[] response = requestResponse.getResponse(); 49 | 50 | 51 | requestEditor.setMessage(new byte[0], true); 52 | responseEditor.setMessage(new byte[0], false); 53 | 54 | 55 | if (request != null) { 56 | requestEditor.setMessage(request, true); 57 | } 58 | 59 | if (response != null) { 60 | responseEditor.setMessage(response, false); 61 | } 62 | } else { 63 | clear(); 64 | } 65 | } 66 | 67 | public void clear() { 68 | requestEditor.setMessage(new byte[0], true); 69 | responseEditor.setMessage(new byte[0], false); 70 | } 71 | } -------------------------------------------------------------------------------- /src/main/java/AutoBurp/fingerprint/ui/TagsPanel.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.fingerprint.ui; 2 | 3 | import javax.swing.*; 4 | import javax.swing.border.EmptyBorder; 5 | import java.awt.*; 6 | import java.awt.event.MouseAdapter; 7 | import java.awt.event.MouseEvent; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | import java.util.function.Consumer; 11 | 12 | public class TagsPanel extends JPanel { 13 | private JPanel tagsPanel; 14 | private JLabel currentSelectedLabel = null; 15 | private final Map resultLabels = new HashMap<>(); 16 | private Consumer onTagSelectedListener; 17 | 18 | 19 | private static final Color PRIMARY_COLOR = new Color(60, 141, 188); 20 | private static final Color TEXT_COLOR = new Color(51, 51, 51); 21 | private static final Color BORDER_COLOR = new Color(221, 221, 221); 22 | private static final Color HOVER_COLOR = new Color(240, 248, 255); 23 | 24 | public TagsPanel() { 25 | setLayout(new BorderLayout()); 26 | setBackground(Color.WHITE); 27 | 28 | setBorder(BorderFactory.createCompoundBorder( 29 | BorderFactory.createMatteBorder(0, 0, 1, 0, BORDER_COLOR), 30 | new EmptyBorder(8, 15, 8, 15) 31 | )); 32 | 33 | tagsPanel = new JPanel(); 34 | tagsPanel.setLayout(new WrapLayout(FlowLayout.LEFT, 10, 3)); 35 | tagsPanel.setBackground(Color.WHITE); 36 | 37 | 38 | JLabel allLabel = createTagLabel("全部"); 39 | allLabel.setBackground(PRIMARY_COLOR); 40 | allLabel.setForeground(Color.WHITE); 41 | allLabel.setOpaque(true); 42 | currentSelectedLabel = allLabel; 43 | tagsPanel.add(allLabel); 44 | 45 | 46 | JScrollPane tagsScrollPane = new JScrollPane(tagsPanel); 47 | tagsScrollPane.setBorder(null); 48 | 49 | tagsScrollPane.setPreferredSize(new Dimension(0, 35)); 50 | tagsScrollPane.setMinimumSize(new Dimension(0, 35)); 51 | 52 | tagsScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 53 | tagsScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); 54 | tagsScrollPane.getViewport().setBackground(Color.WHITE); 55 | 56 | add(tagsScrollPane, BorderLayout.CENTER); 57 | 58 | 59 | setMinimumSize(new Dimension(0, 50)); 60 | setPreferredSize(new Dimension(0, 50)); 61 | } 62 | 63 | private JLabel createTagLabel(String text) { 64 | JLabel label = new JLabel(text); 65 | label.setOpaque(false); 66 | label.setForeground(TEXT_COLOR); 67 | label.setFont(new Font(label.getFont().getName(), Font.PLAIN, 13)); 68 | label.setBorder(BorderFactory.createCompoundBorder( 69 | BorderFactory.createLineBorder(BORDER_COLOR, 1, true), 70 | new EmptyBorder(3, 8, 3, 8) 71 | )); 72 | 73 | 74 | label.addMouseListener(new MouseAdapter() { 75 | @Override 76 | public void mouseEntered(MouseEvent e) { 77 | if (label != currentSelectedLabel) { 78 | label.setBackground(HOVER_COLOR); 79 | label.setOpaque(true); 80 | } 81 | } 82 | 83 | @Override 84 | public void mouseExited(MouseEvent e) { 85 | if (label != currentSelectedLabel) { 86 | label.setOpaque(false); 87 | } 88 | } 89 | 90 | @Override 91 | public void mouseClicked(MouseEvent e) { 92 | 93 | if (e.getButton() == MouseEvent.BUTTON1) { 94 | if (currentSelectedLabel != null) { 95 | currentSelectedLabel.setBackground(null); 96 | currentSelectedLabel.setForeground(TEXT_COLOR); 97 | currentSelectedLabel.setOpaque(false); 98 | } 99 | 100 | label.setBackground(PRIMARY_COLOR); 101 | label.setForeground(Color.WHITE); 102 | label.setOpaque(true); 103 | currentSelectedLabel = label; 104 | 105 | 106 | if (onTagSelectedListener != null) { 107 | String type = label.getText(); 108 | onTagSelectedListener.accept(type.equals("全部") ? null : type); 109 | } 110 | } 111 | 112 | else if (e.getButton() == MouseEvent.BUTTON3 && onRightClickListener != null) { 113 | onRightClickListener.accept(e.getComponent(), e.getX(), e.getY()); 114 | } 115 | } 116 | }); 117 | 118 | return label; 119 | } 120 | 121 | public void addTag(String type) { 122 | if (!resultLabels.containsKey(type)) { 123 | JLabel typeLabel = createTagLabel(type); 124 | tagsPanel.add(typeLabel); 125 | resultLabels.put(type, typeLabel); 126 | tagsPanel.revalidate(); 127 | tagsPanel.repaint(); 128 | } 129 | } 130 | 131 | public void clearTags() { 132 | tagsPanel.removeAll(); 133 | JLabel allLabel = createTagLabel("全部"); 134 | allLabel.setBackground(PRIMARY_COLOR); 135 | allLabel.setForeground(Color.WHITE); 136 | allLabel.setOpaque(true); 137 | tagsPanel.add(allLabel); 138 | currentSelectedLabel = allLabel; 139 | 140 | resultLabels.clear(); 141 | 142 | tagsPanel.revalidate(); 143 | tagsPanel.repaint(); 144 | } 145 | 146 | public void selectTag(String type) { 147 | if (currentSelectedLabel != null) { 148 | currentSelectedLabel.setBackground(null); 149 | currentSelectedLabel.setForeground(TEXT_COLOR); 150 | currentSelectedLabel.setOpaque(false); 151 | } 152 | 153 | JLabel targetLabel = null; 154 | if (type == null || type.equals("全部")) { 155 | 156 | for (Component component : tagsPanel.getComponents()) { 157 | if (component instanceof JLabel && ((JLabel) component).getText().equals("全部")) { 158 | targetLabel = (JLabel) component; 159 | break; 160 | } 161 | } 162 | } else { 163 | targetLabel = resultLabels.get(type); 164 | } 165 | 166 | if (targetLabel != null) { 167 | targetLabel.setBackground(PRIMARY_COLOR); 168 | targetLabel.setForeground(Color.WHITE); 169 | targetLabel.setOpaque(true); 170 | currentSelectedLabel = targetLabel; 171 | } 172 | } 173 | 174 | public void setOnTagSelectedListener(Consumer listener) { 175 | this.onTagSelectedListener = listener; 176 | } 177 | 178 | 179 | private TriConsumer onRightClickListener; 180 | 181 | 182 | @FunctionalInterface 183 | public interface TriConsumer { 184 | void accept(T t, U u, V v); 185 | } 186 | } -------------------------------------------------------------------------------- /src/main/java/AutoBurp/fingerprint/ui/WrapLayout.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.fingerprint.ui; 2 | 3 | import java.awt.*; 4 | 5 | /** 6 | * 自动换行的FlowLayout实现 7 | * 当容器宽度不足时,组件会自动换行显示 8 | */ 9 | public class WrapLayout extends FlowLayout { 10 | 11 | public WrapLayout(int align, int hgap, int vgap) { 12 | super(align, hgap, vgap); 13 | } 14 | 15 | @Override 16 | public Dimension preferredLayoutSize(Container target) { 17 | return layoutSize(target, true); 18 | } 19 | 20 | @Override 21 | public Dimension minimumLayoutSize(Container target) { 22 | Dimension minimum = layoutSize(target, false); 23 | minimum.width -= (getHgap() + 1); 24 | return minimum; 25 | } 26 | 27 | private Dimension layoutSize(Container target, boolean preferred) { 28 | synchronized (target.getTreeLock()) { 29 | 30 | int targetWidth = target.getSize().width; 31 | 32 | 33 | if (targetWidth == 0) { 34 | targetWidth = Integer.MAX_VALUE; 35 | } 36 | 37 | int hgap = getHgap(); 38 | int vgap = getVgap(); 39 | Insets insets = target.getInsets(); 40 | int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2); 41 | 42 | 43 | int maxWidth = targetWidth - horizontalInsetsAndGap; 44 | 45 | 46 | int x = 0; 47 | int y = insets.top; 48 | int rowHeight = 0; 49 | 50 | 51 | int nmembers = target.getComponentCount(); 52 | 53 | for (int i = 0; i < nmembers; i++) { 54 | Component m = target.getComponent(i); 55 | if (m.isVisible()) { 56 | Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize(); 57 | 58 | 59 | if (x > 0 && x + d.width > maxWidth) { 60 | x = 0; 61 | y += rowHeight + vgap; 62 | rowHeight = 0; 63 | } 64 | 65 | 66 | if (x > 0) { 67 | x += hgap; 68 | } 69 | x += d.width; 70 | rowHeight = Math.max(rowHeight, d.height); 71 | } 72 | } 73 | 74 | 75 | y += rowHeight + insets.bottom; 76 | 77 | return new Dimension(targetWidth, y); 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /src/main/java/AutoBurp/fingerprint/ui/renderer/CenterRenderer.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.fingerprint.ui.renderer; 2 | 3 | import javax.swing.table.DefaultTableCellRenderer; 4 | import java.awt.*; 5 | 6 | /** 7 | * 居中显示的表格单元格渲染器 8 | */ 9 | public class CenterRenderer extends DefaultTableCellRenderer { 10 | public CenterRenderer() { 11 | setHorizontalAlignment(CENTER); 12 | } 13 | 14 | @Override 15 | public Component getTableCellRendererComponent(javax.swing.JTable table, Object value, 16 | boolean isSelected, boolean hasFocus, 17 | int row, int column) { 18 | Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 19 | 20 | if (isSelected) { 21 | c.setBackground(new Color(66, 139, 202)); 22 | c.setForeground(Color.WHITE); 23 | } else { 24 | 25 | if (row % 2 == 0) { 26 | c.setBackground(Color.WHITE); 27 | } else { 28 | c.setBackground(new Color(249, 249, 249)); 29 | } 30 | c.setForeground(new Color(51, 51, 51)); 31 | } 32 | 33 | return c; 34 | } 35 | } -------------------------------------------------------------------------------- /src/main/java/AutoBurp/fingerprint/ui/renderer/HeaderIconRenderer.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.fingerprint.ui.renderer; 2 | 3 | import javax.swing.*; 4 | import javax.swing.border.EmptyBorder; 5 | import javax.swing.table.DefaultTableCellRenderer; 6 | import javax.swing.table.JTableHeader; 7 | import java.awt.*; 8 | import java.util.Set; 9 | import java.util.HashSet; 10 | 11 | /** 12 | * 表头渲染器,支持显示排序图标和过滤图标 13 | * 优化后支持动态配置哪些列显示过滤图标 14 | */ 15 | public class HeaderIconRenderer extends DefaultTableCellRenderer { 16 | private static final Color HEADER_BG_COLOR = new Color(245, 245, 245); 17 | private static final Color HEADER_TEXT_COLOR = new Color(51, 51, 51); 18 | private static final Color HEADER_BORDER_COLOR = new Color(221, 221, 221); 19 | private static final Color ICON_COLOR = new Color(119, 119, 119); 20 | 21 | 22 | private final Set filterColumns = new HashSet<>(); 23 | 24 | public HeaderIconRenderer() { 25 | setHorizontalAlignment(LEFT); 26 | setHorizontalTextPosition(LEFT); 27 | setVerticalAlignment(CENTER); 28 | setOpaque(true); 29 | } 30 | 31 | /** 32 | * 添加需要显示过滤图标的列 33 | * @param columnIndex 列索引 34 | */ 35 | public void addFilterColumn(int columnIndex) { 36 | filterColumns.add(columnIndex); 37 | } 38 | 39 | @Override 40 | public Component getTableCellRendererComponent(JTable table, Object value, 41 | boolean isSelected, boolean hasFocus, 42 | int row, int column) { 43 | JTableHeader header = table.getTableHeader(); 44 | if (header != null) { 45 | setForeground(HEADER_TEXT_COLOR); 46 | setBackground(HEADER_BG_COLOR); 47 | setFont(header.getFont()); 48 | } 49 | 50 | setBorder(BorderFactory.createCompoundBorder( 51 | BorderFactory.createMatteBorder(0, 0, 1, 1, HEADER_BORDER_COLOR), 52 | new EmptyBorder(5, 5, 5, 5) 53 | )); 54 | 55 | 56 | if (filterColumns.contains(column)) { 57 | setIcon(createFilterIcon()); 58 | setIconTextGap(5); 59 | } else { 60 | setIcon(null); 61 | } 62 | 63 | setText(value == null ? "" : value.toString()); 64 | 65 | return this; 66 | } 67 | 68 | private Icon createFilterIcon() { 69 | return new Icon() { 70 | @Override 71 | public void paintIcon(Component c, Graphics g, int x, int y) { 72 | Graphics2D g2d = (Graphics2D) g.create(); 73 | g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 74 | 75 | int size = 8; 76 | int[] xPoints = {x, x + size, x + size/2}; 77 | int[] yPoints = {y, y, y + size/2}; 78 | 79 | g2d.setColor(ICON_COLOR); 80 | g2d.fillPolygon(xPoints, yPoints, 3); 81 | 82 | g2d.dispose(); 83 | } 84 | 85 | @Override 86 | public int getIconWidth() { 87 | return 10; 88 | } 89 | 90 | @Override 91 | public int getIconHeight() { 92 | return 10; 93 | } 94 | }; 95 | } 96 | } -------------------------------------------------------------------------------- /src/main/java/AutoBurp/fingerprint/util/FingerPrintUtils.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.fingerprint.util; 2 | 3 | import AutoBurp.fingerprint.model.FingerPrintRule; 4 | import AutoBurp.fingerprint.model.TableLogModel; 5 | import burp.IBurpExtenderCallbacks; 6 | import burp.IExtensionHelpers; 7 | import burp.IHttpService; 8 | import burp.IResponseInfo; 9 | 10 | import java.nio.charset.StandardCharsets; 11 | import java.text.SimpleDateFormat; 12 | import java.util.Arrays; 13 | import java.util.Date; 14 | import java.util.List; 15 | 16 | public class FingerPrintUtils { 17 | 18 | 19 | public static TableLogModel fingerFilter(IBurpExtenderCallbacks callbacks, int pid, String oneUrl, byte[] oneResponseBytes, 20 | IHttpService iHttpService, IExtensionHelpers helpers, 21 | List fingerprintRules, boolean isScanEnabled) { 22 | if (!isScanEnabled) { 23 | return null; 24 | } 25 | 26 | 27 | 28 | TableLogModel logModel = new TableLogModel(pid, oneUrl, "", "", "", "", "", false, 29 | iHttpService, pid, new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date())); 30 | 31 | IResponseInfo responseInfo = helpers.analyzeResponse(oneResponseBytes); 32 | 33 | String responseBody = new String(oneResponseBytes, StandardCharsets.UTF_8); 34 | 35 | String responseHeaders = responseInfo.getHeaders().toString(); 36 | 37 | String responseTitle = Utils.getTitle(responseBody); 38 | 39 | String mimeType = responseInfo.getStatedMimeType().toLowerCase(); 40 | 41 | if (responseTitle.isEmpty()) { 42 | responseTitle = responseBody; 43 | } 44 | String finalResponseTitle = responseTitle; 45 | 46 | String faviconHash = "0"; 47 | 48 | if (finalResponseTitle.equals(responseBody)) { 49 | logModel.setTitle("-"); 50 | } else { 51 | logModel.setTitle(finalResponseTitle); 52 | } 53 | 54 | 55 | if (mimeType.contains("png") || mimeType.contains("jpeg") || mimeType.contains("icon") || 56 | mimeType.contains("image") || oneUrl.contains("favicon.") || oneUrl.contains(".ico")) { 57 | byte[] body = Arrays.copyOfRange(oneResponseBytes, responseInfo.getBodyOffset(), oneResponseBytes.length); 58 | faviconHash = Utils.getFaviconHash(body); 59 | 60 | 61 | 62 | } 63 | 64 | 65 | 66 | for (FingerPrintRule rule : fingerprintRules) { 67 | String locationContent = ""; 68 | String matchLocation = ""; 69 | 70 | if ("faviconhash".equals(rule.getMethod())) { 71 | locationContent = faviconHash; 72 | matchLocation = "faviconhash"; 73 | } else if ("body".equals(rule.getLocation())) { 74 | locationContent = responseBody; 75 | matchLocation = "body"; 76 | } else if ("header".equals(rule.getLocation())) { 77 | locationContent = responseHeaders; 78 | matchLocation = "header"; 79 | } else if ("title".equals(rule.getLocation())) { 80 | locationContent = finalResponseTitle; 81 | matchLocation = "title"; 82 | } else { 83 | continue; 84 | } 85 | 86 | boolean allKeywordsPresent = true; 87 | StringBuilder matchPatternBuilder; 88 | 89 | 90 | if ("faviconhash".equals(rule.getMethod())) { 91 | matchPatternBuilder = new StringBuilder("faviconhash").append(": "); 92 | try { 93 | if (!rule.getKeyword().isEmpty() && !faviconHash.equals(rule.getKeyword().get(0))) { 94 | allKeywordsPresent = false; 95 | } else if (!rule.getKeyword().isEmpty()) { 96 | matchPatternBuilder.append(rule.getKeyword().get(0)); 97 | } 98 | } catch (Exception e) { 99 | allKeywordsPresent = false; 100 | } 101 | } else { 102 | matchPatternBuilder = new StringBuilder(matchLocation).append(": "); 103 | 104 | for (int i = 0; i < rule.getKeyword().size(); i++) { 105 | String keyword = rule.getKeyword().get(i); 106 | if (!locationContent.contains(keyword)) { 107 | allKeywordsPresent = false; 108 | break; 109 | } 110 | 111 | matchPatternBuilder.append(keyword); 112 | if (i < rule.getKeyword().size() - 1) { 113 | matchPatternBuilder.append(", "); 114 | } 115 | } 116 | } 117 | 118 | if (allKeywordsPresent) { 119 | if (!logModel.getResult().isEmpty()) { 120 | 121 | if (!logModel.getResult().contains(rule.getCms())) { 122 | logModel.setResult(logModel.getResult() + ", " + rule.getCms()); 123 | 124 | 125 | if (logModel.getMatchPattern() != null && !logModel.getMatchPattern().isEmpty()) { 126 | logModel.setMatchPattern(logModel.getMatchPattern() + " | " + matchPatternBuilder.toString()); 127 | } else { 128 | logModel.setMatchPattern(matchPatternBuilder.toString()); 129 | } 130 | } 131 | } else { 132 | 133 | logModel.setResult(rule.getCms()); 134 | logModel.setMatchPattern(matchPatternBuilder.toString()); 135 | } 136 | 137 | 138 | logModel.setType(rule.getType()); 139 | logModel.setIsImportant(rule.getIsImportant()); 140 | 141 | String detailInfo = "Time: " + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()) + 142 | "\r\nUrl:" + oneUrl + "\r\n指纹详细信息如下:\r\n" + rule.getInfo(); 143 | 144 | if (logModel.getResultInfo().isEmpty()) { 145 | logModel.setResultInfo(detailInfo); 146 | } else { 147 | logModel.setResultInfo(logModel.getResultInfo() + "\r\n\r\n" + detailInfo); 148 | } 149 | } 150 | } 151 | 152 | return logModel; 153 | } 154 | } -------------------------------------------------------------------------------- /src/main/java/AutoBurp/fingerprint/util/HTTPUtils.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.fingerprint.util; 2 | 3 | import burp.IBurpExtenderCallbacks; 4 | import burp.IExtensionHelpers; 5 | import burp.IHttpRequestResponse; 6 | import burp.IHttpService; 7 | 8 | import java.net.URL; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | public class HTTPUtils { 13 | 14 | public static Map makeGetRequest(String getUrl, IExtensionHelpers helpers, IBurpExtenderCallbacks callbacks) { 15 | 16 | String host; 17 | int port; 18 | String protocol; 19 | String path; 20 | 21 | try { 22 | 23 | URL url = new URL(getUrl); 24 | 25 | protocol = url.getProtocol(); 26 | host = url.getHost(); 27 | port = url.getPort(); 28 | 29 | if (port == -1 && protocol.equalsIgnoreCase("http")) { 30 | port = 80; 31 | } else if (port == -1 && protocol.equalsIgnoreCase("https")) { 32 | port = 443; 33 | } 34 | 35 | path = url.getPath(); 36 | if (path.isEmpty()) { 37 | path = "/"; 38 | } 39 | 40 | if (url.getQuery() != null) { 41 | path += "?" + url.getQuery(); 42 | } 43 | } catch (Exception e) { 44 | 45 | callbacks.printError("Invalid URL: " + getUrl); 46 | return null; 47 | } 48 | 49 | 50 | IHttpService httpService = helpers.buildHttpService(host, port, protocol); 51 | 52 | 53 | String request = "GET " + path + " HTTP/1.1\r\n" + 54 | "Host: " + host + "\r\n" + 55 | "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" + 56 | "Accept: */*\r\n" + 57 | "Accept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n" + 58 | "Connection: close\r\n" + 59 | "\r\n"; 60 | 61 | byte[] requestBytes = request.getBytes(); 62 | 63 | 64 | IHttpRequestResponse response = callbacks.makeHttpRequest(httpService, requestBytes); 65 | 66 | 67 | Map responseData = new HashMap<>(); 68 | responseData.put("responseRequest", response); 69 | responseData.put("isFindUrl", true); 70 | responseData.put("method", "GET"); 71 | 72 | return responseData; 73 | } 74 | } -------------------------------------------------------------------------------- /src/main/java/AutoBurp/fingerprint/util/Utils.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.fingerprint.util; 2 | 3 | import java.net.URL; 4 | import java.util.Base64; 5 | import java.util.HashSet; 6 | import java.util.Set; 7 | import java.util.regex.Matcher; 8 | import java.util.regex.Pattern; 9 | 10 | public class Utils { 11 | 12 | 13 | 14 | public static String getUriExt(String url) { 15 | try { 16 | String path = new URL(url).getPath(); 17 | int lastDotPos = path.lastIndexOf('.'); 18 | if (lastDotPos > 0) { 19 | return path.substring(lastDotPos + 1).toLowerCase(); 20 | } 21 | } catch (Exception e) { 22 | 23 | } 24 | return ""; 25 | } 26 | 27 | 28 | public static String getTitle(String html) { 29 | Pattern pattern = Pattern.compile("(.*?)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); 30 | Matcher matcher = pattern.matcher(html); 31 | if (matcher.find()) { 32 | return matcher.group(1).trim(); 33 | } 34 | return ""; 35 | } 36 | 37 | public static String getFaviconHash(byte[] data) { 38 | try { 39 | 40 | String base64Favicon = Base64.getEncoder().encodeToString(data); 41 | 42 | 43 | String formattedBase64Favicon = formatBase64(base64Favicon); 44 | 45 | 46 | return String.valueOf(MurmurHash3.hash32( 47 | formattedBase64Favicon.getBytes(), 48 | 0, 49 | formattedBase64Favicon.length(), 50 | 0)); 51 | } catch (Exception e) { 52 | return "0"; 53 | } 54 | } 55 | 56 | 57 | private static String formatBase64(String base64) { 58 | Pattern pattern = Pattern.compile(".{76}"); 59 | Matcher matcher = pattern.matcher(base64); 60 | StringBuilder formattedBase64 = new StringBuilder(); 61 | 62 | while (matcher.find()) { 63 | formattedBase64.append(matcher.group()).append("\n"); 64 | } 65 | 66 | int remainder = base64.length() % 76; 67 | if (remainder > 0) { 68 | formattedBase64.append(base64.substring(base64.length() - remainder)).append("\n"); 69 | } 70 | 71 | return formattedBase64.toString(); 72 | } 73 | 74 | 75 | public static Set extractUrlsFromHtml(String baseUrl, String html) { 76 | Set urls = new HashSet<>(); 77 | 78 | 79 | Pattern hrefPattern = Pattern.compile("href=[\"'](.*?)[\"']", Pattern.CASE_INSENSITIVE); 80 | Matcher hrefMatcher = hrefPattern.matcher(html); 81 | while (hrefMatcher.find()) { 82 | String url = hrefMatcher.group(1); 83 | if (!url.startsWith("#") && !url.startsWith("javascript:")) { 84 | urls.add(processUrl(baseUrl, url)); 85 | } 86 | } 87 | 88 | 89 | Pattern srcPattern = Pattern.compile("src=[\"'](.*?)[\"']", Pattern.CASE_INSENSITIVE); 90 | Matcher srcMatcher = srcPattern.matcher(html); 91 | while (srcMatcher.find()) { 92 | String url = srcMatcher.group(1); 93 | urls.add(processUrl(baseUrl, url)); 94 | } 95 | 96 | return urls; 97 | } 98 | 99 | 100 | public static String processUrl(String baseUrl, String relativeUrl) { 101 | try { 102 | URL base = new URL(baseUrl); 103 | URL absolute = new URL(base, relativeUrl); 104 | return absolute.toString(); 105 | } catch (Exception e) { 106 | return relativeUrl; 107 | } 108 | } 109 | 110 | 111 | public static Set findUrls(URL baseUrl, String text) { 112 | Set urls = new HashSet<>(); 113 | 114 | 115 | Pattern urlPattern = Pattern.compile("(https?://[^\\s\"'<>]+)", Pattern.CASE_INSENSITIVE); 116 | Matcher urlMatcher = urlPattern.matcher(text); 117 | 118 | while (urlMatcher.find()) { 119 | urls.add(urlMatcher.group(1)); 120 | } 121 | 122 | return urls; 123 | } 124 | 125 | 126 | private static class MurmurHash3 { 127 | public static int hash32(byte[] data, int offset, int length, int seed) { 128 | int h1 = seed; 129 | int c1 = 0xcc9e2d51; 130 | int c2 = 0x1b873593; 131 | int roundedEnd = offset + (length & 0xfffffffc); 132 | 133 | for (int i = offset; i < roundedEnd; i += 4) { 134 | int k1 = (data[i] & 0xff) | ((data[i + 1] & 0xff) << 8) | 135 | ((data[i + 2] & 0xff) << 16) | ((data[i + 3] & 0xff) << 24); 136 | 137 | k1 *= c1; 138 | k1 = (k1 << 15) | (k1 >>> 17); 139 | k1 *= c2; 140 | 141 | h1 ^= k1; 142 | h1 = (h1 << 13) | (h1 >>> 19); 143 | h1 = h1 * 5 + 0xe6546b64; 144 | } 145 | 146 | int k1 = 0; 147 | switch (length & 0x03) { 148 | case 3: 149 | k1 = (data[roundedEnd + 2] & 0xff) << 16; 150 | case 2: 151 | k1 |= (data[roundedEnd + 1] & 0xff) << 8; 152 | case 1: 153 | k1 |= (data[roundedEnd] & 0xff); 154 | k1 *= c1; 155 | k1 = (k1 << 15) | (k1 >>> 17); 156 | k1 *= c2; 157 | h1 ^= k1; 158 | } 159 | 160 | h1 ^= length; 161 | h1 ^= h1 >>> 16; 162 | h1 *= 0x85ebca6b; 163 | h1 ^= h1 >>> 13; 164 | h1 *= 0xc2b2ae35; 165 | h1 ^= h1 >>> 16; 166 | 167 | return h1; 168 | } 169 | } 170 | } -------------------------------------------------------------------------------- /src/main/java/AutoBurp/fuzzer/phone/PhoneFuzzer.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.fuzzer.phone; 2 | 3 | import AutoBurp.generator.PhonePayloadGeneratorFactory; 4 | import burp.*; 5 | 6 | public class PhoneFuzzer implements IIntruderPayloadGeneratorFactory { 7 | private IExtensionHelpers helpers; 8 | private IBurpExtenderCallbacks callbacks; 9 | 10 | public PhoneFuzzer(IExtensionHelpers helpers, IBurpExtenderCallbacks callbacks) { 11 | this.helpers = helpers; 12 | this.callbacks = callbacks; 13 | } 14 | 15 | @Override 16 | public String getGeneratorName() { 17 | 18 | return "Phone Bypass Fuzzer"; 19 | } 20 | 21 | @Override 22 | public IIntruderPayloadGenerator createNewInstance(IIntruderAttack attack) { 23 | 24 | return new PhonePayloadGeneratorFactory(this.helpers, attack, this.callbacks); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/AutoBurp/fuzzer/upload/UploadFuzzer.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.fuzzer.upload; 2 | 3 | import AutoBurp.generator.UploadPayloadGenerator; 4 | import burp.IBurpExtenderCallbacks; 5 | import burp.IExtensionHelpers; 6 | import burp.IIntruderAttack; 7 | import burp.IIntruderPayloadGenerator; 8 | 9 | import java.util.ArrayList; 10 | import java.util.HashSet; 11 | import java.util.List; 12 | import java.util.Set; 13 | import java.util.regex.Matcher; 14 | import java.util.regex.Pattern; 15 | 16 | public class UploadFuzzer implements IIntruderPayloadGenerator { 17 | private final IExtensionHelpers helpers; 18 | private final IIntruderAttack attack; 19 | private int payloadIndex = 0; 20 | private List attackPayloads = new ArrayList<>(); 21 | private boolean initialized = false; 22 | 23 | private final IBurpExtenderCallbacks callbacks; 24 | 25 | public UploadFuzzer(IExtensionHelpers helpers, IIntruderAttack attack, IBurpExtenderCallbacks callbacks) { 26 | this.helpers = helpers; 27 | this.attack = attack; 28 | this.callbacks = callbacks; 29 | } 30 | 31 | @Override 32 | public boolean hasMorePayloads() { 33 | if (!initialized) { 34 | return true; 35 | } 36 | 37 | return payloadIndex < attackPayloads.size(); 38 | } 39 | 40 | @Override 41 | public byte[] getNextPayload(byte[] baseValue) { 42 | if (!initialized) { 43 | 44 | initializePayloads(baseValue); 45 | initialized = true; 46 | 47 | } 48 | 49 | if (payloadIndex >= attackPayloads.size()) { 50 | return baseValue; 51 | } 52 | 53 | String payload = attackPayloads.get(payloadIndex); 54 | payloadIndex++; 55 | return payload.getBytes(); 56 | } 57 | 58 | private void initializePayloads(byte[] baseValue) { 59 | String selectedArea = new String(baseValue); 60 | 61 | boolean isFullSection = selectedArea.contains("Content-Disposition:") && 62 | (selectedArea.contains("filename=") || selectedArea.contains("filename=\"")) && 63 | selectedArea.contains("Content-Type:"); 64 | 65 | 66 | 67 | if (isFullSection) { 68 | Matcher filenameMatcher = Pattern.compile("filename=\"([^\"]*)\"").matcher(selectedArea); 69 | Matcher namematcher = Pattern.compile("name=\"([^\"]*)\"").matcher(selectedArea); 70 | Matcher contentTypeMatcher = Pattern.compile("Content-Type:\\s*([^\\r\\n]*)").matcher(selectedArea); 71 | 72 | 73 | if (filenameMatcher.find() && filenameMatcher.group(1).contains(".")) { 74 | String originalFilename = filenameMatcher.group(1); 75 | String originalExt = originalFilename.substring(originalFilename.lastIndexOf('.') + 1); 76 | String name = namematcher.find() ? namematcher.group(1) : "file"; 77 | String contentType = contentTypeMatcher.find() ? contentTypeMatcher.group(1).trim() : "image/jpeg"; 78 | 79 | List sectionPayloads = UploadPayloadGenerator.getFuzzPayloadsForFullSection(selectedArea); 80 | 81 | 82 | String template = "Content-Disposition: form-data; name=\""+name+"\"; filename=\"test." + originalExt + 83 | "\"\r\nContent-Type:"+contentType; 84 | List singleElementPayloads = UploadPayloadGenerator.getAttackPayloads(template); 85 | 86 | List convertedPayloads = new ArrayList<>(singleElementPayloads); 87 | 88 | Set uniquePayloads = new HashSet<>(); 89 | uniquePayloads.addAll(sectionPayloads); 90 | uniquePayloads.addAll(convertedPayloads); 91 | attackPayloads = new ArrayList<>(uniquePayloads); 92 | } else { 93 | attackPayloads = UploadPayloadGenerator.getFuzzPayloadsForFullSection(selectedArea); 94 | } 95 | } else { 96 | attackPayloads = UploadPayloadGenerator.getAttackPayloads(selectedArea); 97 | } 98 | 99 | 100 | if (attackPayloads.size() > 1000) { 101 | attackPayloads = attackPayloads.subList(0, 1000); 102 | } 103 | 104 | attack.getHttpService().getHost(); 105 | } 106 | 107 | @Override 108 | public void reset() { 109 | payloadIndex = 0; 110 | } 111 | } -------------------------------------------------------------------------------- /src/main/java/AutoBurp/generator/PhonePayloadGeneratorFactory.java: -------------------------------------------------------------------------------- 1 | package AutoBurp.generator; 2 | 3 | import burp.*; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public class PhonePayloadGeneratorFactory implements IIntruderPayloadGenerator { 9 | private final IExtensionHelpers helpers; 10 | private final IIntruderAttack attack; 11 | private final IBurpExtenderCallbacks callbacks; 12 | private int payloadIndex = 0; 13 | private final List payloads = new ArrayList<>(); 14 | private String basePhoneNumber = "18888888888"; 15 | 16 | public PhonePayloadGeneratorFactory(IExtensionHelpers helpers, IIntruderAttack attack, IBurpExtenderCallbacks callbacks) { 17 | this.helpers = helpers; 18 | this.attack = attack; 19 | this.callbacks = callbacks; 20 | 21 | initializePayloads(basePhoneNumber.getBytes()); 22 | } 23 | 24 | 25 | public void initializePayloads(byte[] selectedValue) { 26 | 27 | if (selectedValue != null && selectedValue.length > 0) { 28 | basePhoneNumber = new String(selectedValue); 29 | 30 | } 31 | 32 | 33 | 34 | payloads.clear(); 35 | 36 | 37 | String[] PAYLOAD_PATTERNS = new String[] { 38 | "xxxxxxxxxxx,", 39 | "xxxxxxxxxxx,,", 40 | "xxxxxxxxxxx,,,", 41 | "xxxxxxxxxxx,,,,", 42 | "xxxxxxxxxxx,,,,,", 43 | ",,,,,xxxxxxxxxxx", 44 | ",,,,xxxxxxxxxxx", 45 | ",,,xxxxxxxxxxx", 46 | ",,xxxxxxxxxxx", 47 | ",xxxxxxxxxxx", 48 | " xxxxxxxxxxx", 49 | " xxxxxxxxxxx", 50 | " xxxxxxxxxxx", 51 | "%20xxxxxxxxxxx", 52 | "%20%20xxxxxxxxxxx", 53 | "%20%20%20xxxxxxxxxxx", 54 | "xxxxxxxxxxx ", 55 | "xxxxxxxxxxx ", 56 | "xxxxxxxxxxx ", 57 | "xxxxxxxxxxx%20", 58 | "xxxxxxxxxxx%20%20", 59 | "xxxxxxxxxxx%20%20%20", 60 | "@xxxxxxxxxxx", 61 | "@@xxxxxxxxxxx", 62 | "@@@xxxxxxxxxxx", 63 | "xxxxxxxxxxx@", 64 | "xxxxxxxxxxx@@", 65 | "xxxxxxxxxxx@@@", 66 | "%00xxxxxxxxxxx", 67 | "%00%00xxxxxxxxxxx", 68 | "%00%00%00xxxxxxxxxxx", 69 | "xxxxxxxxxxx%00", 70 | "xxxxxxxxxxx%00%00", 71 | "xxxxxxxxxxx%00%00%00", 72 | "xxxxxxxxxxx\\n", 73 | "xxxxxxxxxxx\\n\\n", 74 | "xxxxxxxxxxx\\n\\n\\n", 75 | "xxxxxxxxxxx\\n\\n\\n\\n", 76 | "\\nxxxxxxxxxxx", 77 | "\\n\\nxxxxxxxxxxx", 78 | "\\n\\n\\nxxxxxxxxxxx", 79 | "\\n\\n\\n\\nxxxxxxxxxxx", 80 | "xxxxxxxxxxx\\r", 81 | "xxxxxxxxxxx\\r\\r", 82 | "xxxxxxxxxxx\\r\\r\\r", 83 | "xxxxxxxxxxx\\r\\r\\r\\r", 84 | "\\rxxxxxxxxxxx", 85 | "\\r\\rxxxxxxxxxxx", 86 | "\\r\\r\\rxxxxxxxxxxx", 87 | "\\r\\r\\r\\rxxxxxxxxxxx", 88 | "xxxxxxxxxxx+", 89 | "xxxxxxxxxxx++", 90 | "xxxxxxxxxxx+++", 91 | "xxxxxxxxxxx++++", 92 | "+xxxxxxxxxxx", 93 | "++xxxxxxxxxxx", 94 | "+++xxxxxxxxxxx", 95 | "++++xxxxxxxxxxx", 96 | "xxxxxxxxxxx-", 97 | "xxxxxxxxxxx--", 98 | "xxxxxxxxxxx---", 99 | "xxxxxxxxxxx----", 100 | "-xxxxxxxxxxx", 101 | "--xxxxxxxxxxx", 102 | "---xxxxxxxxxxx", 103 | "----xxxxxxxxxxx", 104 | "xxxxxxxxxxx*", 105 | "xxxxxxxxxxx**", 106 | "xxxxxxxxxxx***", 107 | "xxxxxxxxxxx****", 108 | "*xxxxxxxxxxx", 109 | "**xxxxxxxxxxx", 110 | "***xxxxxxxxxxx", 111 | "****xxxxxxxxxxx", 112 | "xxxxxxxxxxx/", 113 | "xxxxxxxxxxx//", 114 | "xxxxxxxxxxx///", 115 | "xxxxxxxxxxx////", 116 | "/xxxxxxxxxxx", 117 | "//xxxxxxxxxxx", 118 | "///xxxxxxxxxxx", 119 | "////xxxxxxxxxxx", 120 | "+86xxxxxxxxxxx", 121 | "+86 xxxxxxxxxxx", 122 | "+86%20xxxxxxxxxxx", 123 | "+12xxxxxxxxxxx", 124 | "+12 xxxxxxxxxxx", 125 | "+12%20xxxxxxxxxxx", 126 | "+852xxxxxxxxxxx", 127 | "+852 xxxxxxxxxxx", 128 | "+852%20xxxxxxxxxxx", 129 | "+853xxxxxxxxxxx", 130 | "+853 xxxxxxxxxxx", 131 | "+853%20xxxxxxxxxxx", 132 | "0086xxxxxxxxxxx", 133 | "0086 xxxxxxxxxxx", 134 | "0086%20xxxxxxxxxxx", 135 | "0012xxxxxxxxxxx", 136 | "0012 xxxxxxxxxxx", 137 | "0012%20xxxxxxxxxxx", 138 | "00852xxxxxxxxxxx", 139 | "00852 xxxxxxxxxxx", 140 | "00852%20xxxxxxxxxxx", 141 | "00853xxxxxxxxxxx", 142 | "00853 xxxxxxxxxxx", 143 | "00853%20xxxxxxxxxxx", 144 | "9986xxxxxxxxxxx", 145 | "9986 xxxxxxxxxxx", 146 | "9986%20xxxxxxxxxxx", 147 | "9912xxxxxxxxxxx", 148 | "9912 xxxxxxxxxxx", 149 | "9912%20xxxxxxxxxxx", 150 | "99852xxxxxxxxxxx", 151 | "99852 xxxxxxxxxxx", 152 | "99852%20xxxxxxxxxxx", 153 | "99853xxxxxxxxxxx", 154 | "99853 xxxxxxxxxxx", 155 | "99853%20xxxxxxxxxxx", 156 | "86xxxxxxxxxxx", 157 | "86 xxxxxxxxxxx", 158 | "86%20xxxxxxxxxxx", 159 | "12xxxxxxxxxxx", 160 | "12 xxxxxxxxxxx", 161 | "12%20xxxxxxxxxxx", 162 | "852xxxxxxxxxxx", 163 | "852 xxxxxxxxxxx", 164 | "852%20xxxxxxxxxxx", 165 | "853xxxxxxxxxxx", 166 | "853 xxxxxxxxxxx", 167 | "853%20xxxxxxxxxxx", 168 | "086xxxxxxxxxxx", 169 | "086 xxxxxxxxxxx", 170 | "086%20xxxxxxxxxxx", 171 | "012xxxxxxxxxxx", 172 | "012 xxxxxxxxxxx", 173 | "012%20xxxxxxxxxxx", 174 | "0852xxxxxxxxxxx", 175 | "0852 xxxxxxxxxxx", 176 | "0852%20xxxxxxxxxxx", 177 | "0853xxxxxxxxxxx", 178 | "0853 xxxxxxxxxxx", 179 | "0853%20xxxxxxxxxxx", 180 | "%86xxxxxxxxxxx", 181 | "%86 xxxxxxxxxxx", 182 | "%86%2%xxxxxxxxxxx", 183 | "%12xxxxxxxxxxx", 184 | "%12 xxxxxxxxxxx", 185 | "%12%2%xxxxxxxxxxx", 186 | "%852xxxxxxxxxxx", 187 | "%852 xxxxxxxxxxx", 188 | "%852%2%xxxxxxxxxxx", 189 | "%853xxxxxxxxxxx", 190 | "%853 xxxxxxxxxxx", 191 | "%853%2%xxxxxxxxxxx", 192 | " 0xxxxxxxxxxx", 193 | "%200xxxxxxxxxxx", 194 | "0xxxxxxxxxxx", 195 | "00xxxxxxxxxxx", 196 | "000xxxxxxxxxxx", 197 | "0000xxxxxxxxxxx", 198 | "00000xxxxxxxxxxx", 199 | "+)WAFXR#!Txxxxxxxxxxx", 200 | "xxxxxxxxxxx+)WAFXR#!T", 201 | "xxxxxxxxxxx.0", 202 | "xxxxxxxxxxx.1", 203 | "xxxxxxxxxxx.2", 204 | "xxxxxxxxxxx.3", 205 | "xxxxxxxxxxx,13811111111", 206 | "xxxxxxxxxxx,,13811111111", 207 | "xxxxxxxxxxx,,,13811111111", 208 | "xxxxxxxxxxx&13811111111", 209 | "xxxxxxxxxxx&&13811111111", 210 | "xxxxxxxxxxx&&&13811111111", 211 | "xxxxxxxxxxx&&&&13811111111", 212 | "13811111111&xxxxxxxxxxx", 213 | "13811111111&&xxxxxxxxxxx", 214 | "13811111111&&&xxxxxxxxxxx", 215 | "13811111111&&&&xxxxxxxxxxx", 216 | "13811111111,xxxxxxxxxxx", 217 | "13811111111,,xxxxxxxxxxx", 218 | "13811111111,,,xxxxxxxxxxx", 219 | }; 220 | 221 | 222 | for (String pattern : PAYLOAD_PATTERNS) { 223 | String payload = pattern.replace("xxxxxxxxxxx", basePhoneNumber); 224 | payloads.add(payload); 225 | } 226 | 227 | 228 | payloads.add(basePhoneNumber + "/**/"); 229 | payloads.add("/**/"+basePhoneNumber); 230 | payloads.add(basePhoneNumber + "||'1'='1"); 231 | payloads.add(basePhoneNumber + "' OR '1'='1"); 232 | payloads.add(basePhoneNumber + "' AND '1'='1"); 233 | 234 | 235 | payloads.add(basePhoneNumber.replace("8", "8")); 236 | payloads.add(basePhoneNumber.replace("1", "1")); 237 | 238 | 239 | payloads.add(basePhoneNumber.replace("8", "八")); 240 | payloads.add(basePhoneNumber.replace("1", "一")); 241 | 242 | } 243 | 244 | @Override 245 | public boolean hasMorePayloads() { 246 | boolean result = payloadIndex < payloads.size(); 247 | 248 | 249 | 250 | return result; 251 | } 252 | 253 | 254 | 255 | @Override 256 | public byte[] getNextPayload(byte[] baseValue) { 257 | if (payloadIndex == 0) { 258 | initializePayloads(baseValue); 259 | } 260 | 261 | if (payloadIndex < payloads.size()) { 262 | String currentPayload = payloads.get(payloadIndex); 263 | byte[] payload = currentPayload.getBytes(); 264 | payloadIndex++; 265 | return payload; 266 | } 267 | 268 | return baseValue; 269 | } 270 | 271 | @Override 272 | public void reset() { 273 | payloadIndex = 0; 274 | } 275 | } -------------------------------------------------------------------------------- /src/main/resources/project_options.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_options":{ 3 | "ssl":{ 4 | "client_certificates":{ 5 | "certificates":[], 6 | "use_user_options":true 7 | }, 8 | "negotiation":{ 9 | "allow_unsafe_renegotiation":false, 10 | "disable_ssl_session_resume":true, 11 | "enabled_ciphers":[ 12 | "TLS_AES_128_GCM_SHA256", 13 | "TLS_AES_256_GCM_SHA384", 14 | "TLS_CHACHA20_POLY1305_SHA256", 15 | "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", 16 | "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 17 | "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 18 | "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", 19 | "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", 20 | "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", 21 | "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", 22 | "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", 23 | "TLS_RSA_WITH_AES_128_GCM_SHA256", 24 | "TLS_RSA_WITH_AES_256_GCM_SHA384", 25 | "TLS_RSA_WITH_AES_128_CBC_SHA", 26 | "TLS_RSA_WITH_AES_256_CBC_SHA" 27 | ], 28 | "enabled_protocols":[ 29 | "TLSv1.2", 30 | "TLSv1.3" 31 | ], 32 | "enforce_upstream_trust":false, 33 | "tls_negotiation_behavior":"use_custom" 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/resources/strings.properties: -------------------------------------------------------------------------------- 1 | menu_brute_force=Brute force ciphers 2 | menu_downgrade=downgrade HTTP2 3 | network_preferences=AutoBurp.bypass 4 | proxy_preferences=AutoBurp.bypass.beens.proxy 5 | negotiation=Bypass! 6 | message=Adjust ciphers to common browsers --------------------------------------------------------------------------------