skipped 22 lines 23 23 24 24 public class BurpExtender implements IBurpExtender, IExtensionStateListener { 25 25 private static final String name = "Param Miner"; 26 - private static final String version = "1.28 "; 26 + private static final String version = "1.31 "; 27 27 private ThreadPoolExecutor taskEngine; 28 - 28 + static ParamGrabber paramGrabber; 29 + static SettingsBox configSettings = new SettingsBox(); 30 + static SettingsBox guessSettings = new SettingsBox(); 29 31 30 32 @Override 31 33 public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks) { 32 34 33 - HashMap<String, Object> settings = new HashMap<>(); 34 - settings.put("Add 'fcbz' cachebuster", false); 35 - settings.put("Add dynamic cachebuster", false); 36 - settings .put ("Add header cachebuster", false); 37 - settings.put("include origin in cachebusters", true); 38 - settings.put("identify smuggle mutations", true); 39 - settings .put ("learn observed words", false); 40 - settings.put("skip boring words", true); 41 - settings.put("only report unique params", false); 42 - settings.put("response", true); 43 - settings.put("request", true); 44 - settings.put("use basic wordlist", true); 45 - settings.put("use bonus wordlist", false); 46 - settings.put("use assetnote params", false); 47 - settings.put("use custom wordlist", false); 48 - settings.put("custom wordlist path", "/usr/share/dict/words"); 49 - settings.put("bruteforce", false); 50 - settings.put("skip uncacheable", false); 51 - settings.put("dynamic keyload", false); 52 - settings.put("max one per host", false); 53 - settings.put("max one per host+status", false); 54 - settings.put("probe identified params", true); 55 - settings.put("scan identified params", false); 56 - settings.put("enable auto-mine", false); 57 - settings.put("auto-mine headers", false); 58 - settings.put("auto-mine cookies", false); 59 - settings.put("auto-mine params", false); 60 - settings.put("auto-nest params", false); 61 - settings.put("fuzz detect", false); 62 - settings.put("carpet bomb", false); 63 - settings.put("try cache poison", true); 64 - settings.put("twitchy cache poison", false); 65 - settings.put("try method flip", false); 66 - settings.put("try -_ bypass", false); 67 - settings.put("thread pool size", 8); 68 - settings.put("rotation interval", 200); 69 - settings.put("rotation increment", 4); 70 - settings.put("force bucketsize", -1); 71 - settings.put("max bucketsize", 65536); 72 - settings.put("max param length", 32); 73 - settings.put("lowercase headers", true); 74 - settings.put("name in issue", false); 75 - settings.put("canary", "zwrtxqva"); 35 + new Utilities(callbacks, new HashMap<>(), name); 36 + 37 + // config only (currently param-guess displays everything) 38 + configSettings .register ("Add ' fcbz ' cachebuster", false); 39 + configSettings.register("Add dynamic cachebuster", false); 40 + configSettings.register("Add header cachebuster", false); 41 + configSettings .register ("learn observed words", false); 42 + configSettings.register("enable auto-mine", false); 43 + configSettings.register("auto-mine headers", false); 44 + configSettings.register("auto-mine cookies", false); 45 + configSettings.register("auto-mine params", false); 46 + configSettings.register("auto-nest params", false); 47 + 48 + // param-guess only 49 + //guessSettings.importSettings(globalSettings); 50 + guessSettings.register("learn observed words", false); 51 + guessSettings.register("skip boring words", true); 52 + guessSettings.register("only report unique params", false); 53 + guessSettings.register("response", true); 54 + guessSettings.register("request", true); 55 + guessSettings.register("use basic wordlist", true); 56 + guessSettings.register("use bonus wordlist", false); 57 + guessSettings.register("use assetnote params", false); 58 + guessSettings.register("use custom wordlist", false); 59 + guessSettings.register("custom wordlist path", "/usr/share/dict/words"); 60 + guessSettings.register("bruteforce", false); 61 + guessSettings.register("skip uncacheable", false); 62 + guessSettings.register("dynamic keyload", false); 63 + guessSettings.register("max one per host", false); 64 + guessSettings.register("max one per host+status", false); 65 + guessSettings.register("probe identified params", true); 66 + guessSettings.register("scan identified params", false); 67 + guessSettings.register("fuzz detect", false); 68 + guessSettings.register("carpet bomb", false); 69 + guessSettings.register("try cache poison", true); 70 + guessSettings.register("twitchy cache poison", false); 71 + guessSettings.register("try method flip", false); 72 + guessSettings.register("identify smuggle mutations", true); 73 + guessSettings.register("try -_ bypass", false); 74 + guessSettings.register("rotation interval", 200); 75 + guessSettings.register("rotation increment", 4); 76 + guessSettings.register("force bucketsize", -1); 77 + guessSettings.register("max bucketsize", 65536); 78 + guessSettings.register("max param length", 32); 79 + guessSettings.register("lowercase headers", true); 80 + guessSettings.register("name in issue", false); 81 + guessSettings.register("canary", "zwrtxqva"); 82 + guessSettings.register("force canary", ""); 83 + guessSettings.register("poison only", false); 84 + guessSettings.register("tunnelling retry count", 20); 85 + guessSettings.register("abort on tunnel failure", true); 76 86 77 - new Utilities(callbacks, settings, name); 78 87 loadWordlists(); 79 88 BlockingQueue<Runnable> tasks; 80 89 if (Utilities.globalSettings.getBoolean("enable auto-mine")) { skipped 3 lines 84 93 tasks = new LinkedBlockingQueue<>(); 85 94 } 86 95 96 + Utilities.globalSettings.registerSetting("thread pool size", 8); 87 97 taskEngine = new ThreadPoolExecutor(Utilities.globalSettings.getInt("thread pool size"), Utilities.globalSettings.getInt("thread pool size"), 10, TimeUnit.MINUTES, tasks); 88 98 Utilities.globalSettings.registerListener("thread pool size", value -> { 89 99 Utilities.out("Updating active thread pool size to "+value); skipped 22 lines 112 122 throw new NoSuchMethodError(); 113 123 } 114 124 115 - ParamGrabber paramGrabber = new ParamGrabber(taskEngine); 125 + paramGrabber = new ParamGrabber(taskEngine); 116 126 callbacks.registerContextMenuFactory(new OfferParamGuess(callbacks, paramGrabber, taskEngine)); 117 127 118 128 if(Utilities.isBurpPro()) { skipped 5 lines 124 134 125 135 SwingUtilities.invokeLater(new ConfigMenu()); 126 136 137 + new HeaderPoison("Header poison"); 127 138 new PortDOS("port-DoS"); 128 139 //new ValueScan("param-value probe"); 129 140 new UnkeyedParamScan("Unkeyed param"); skipped 8 lines 138 149 Utilities.callbacks.registerExtensionStateListener(this); 139 150 140 151 Utilities.out("Loaded " + name + " v" + version); 141 - Utilities.out(" CACHE_ONLY "+Utilities.CACHE_ONLY); 142 152 } 143 153 144 154 skipped 143 lines 288 298 } 289 299 290 300 String calculateValue(String unparsed) { 301 + String canary = Utilities.globalSettings.getString("force canary"); 302 + if (!"".equals(canary)) { 303 + return canary; 304 + } 291 305 return Utilities.toCanary(unparsed) + attackID + value + Utilities.fuzzSuffix(); 292 306 } 293 307 skipped 94 lines 388 402 389 403 public byte[] buildBulkRequest(ArrayList<String> params) { 390 404 String merged = prepBulkParams(params); 405 + Iterator<String> dupeCheck= params.iterator(); 406 + byte[] body = Utilities.getBodyBytes(request); 407 + 408 + boolean fooReq = false; 409 + if (Utilities.containsBytes(body, "FOO BAR AAH\r\n".getBytes())) { 410 + fooReq = true; 411 + } 412 + 413 + if (fooReq || Utilities.containsBytes(body, " HTTP/1.1\r\n".getBytes())) { 414 + Utilities.chopNestedResponses = true; 415 + 416 + boolean usingCorrectContentLength = true; 417 + 418 + try { 419 + if (body.length != Integer.parseInt(Utilities.getHeader(request, "Content-Length"))) { 420 + usingCorrectContentLength = false; 421 + } 422 + } catch (Exception e) { 423 + 424 + } 425 + 426 + while (dupeCheck.hasNext()) { 427 + String param = dupeCheck.next().split("~", 2)[0]; 428 + byte[] toReplace = ("\n"+param+": ").getBytes(); 429 + if (Utilities.containsBytes(body, toReplace)) { 430 + body = Utilities.replace(body, toReplace, ("\nold"+param+": ").getBytes()); 431 + } 432 + } 433 + 434 + byte[] newBody; 435 + if (fooReq) { 436 + newBody = Utilities.replaceFirst(body, "FOO BAR AAH\r\n", "GET http://"+Utilities.getHeader(request, "Host")+"/ HTTP/1.1\r\n"+merged+"\r\n"); 437 + } 438 + else { 439 + newBody = Utilities.replaceFirst(body, "HTTP/1.1", "HTTP/1.1\r\n"+merged); 440 + } 441 + 442 + byte[] finalRequest = Utilities.setBody(request, new String(newBody)); 443 + if (usingCorrectContentLength) { 444 + finalRequest = Utilities.fixContentLength(finalRequest); 445 + } 446 + 447 + finalRequest = Utilities.addOrReplaceHeader(finalRequest, "X-Mine-Nested-Request", "1"); 448 + 449 + return finalRequest; 450 + } 451 + 391 452 String replaceKey = "TCZqBcS13SA8QRCpW"; 392 453 byte[] built = Utilities.addOrReplaceHeader(request, replaceKey, "foo"); 393 454 394 455 if (params.isEmpty() || "".equals(merged)) { 395 456 return built; 396 457 } 397 - 398 - Iterator<String> dupeCheck= params.iterator(); 399 458 400 459 while (dupeCheck.hasNext()) { 401 460 String param = dupeCheck.next().split("~", 2)[0]; skipped 135 lines