Projects STRLCPY param-miner Commits 98324c0a
🤬
  • ■ ■ ■ ■ ■ ■
    BappManifest.bmf
    skipped 1 lines
    2 2  ExtensionType: 1
    3 3  Name: Param Miner
    4 4  RepoName: param-miner
    5  -ScreenVersion: 1.28
    6  -SerialVersion: 13
     5 +ScreenVersion: 1.31
     6 +SerialVersion: 15
    7 7  MinPlatformVersion: 0
    8 8  ProOnly: False
    9 9  Author: James 'albinowax' Kettle, PortSwigger Web Security
    skipped 4 lines
  • albinowaxUtils-all.jar
    Binary file.
  • ■ ■ ■ ■ ■ ■
    src/burp/BurpExtender.java
    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
  • ■ ■ ■ ■ ■ ■
    src/burp/HeaderPoison.java
     1 +package burp;
     2 + 
     3 +import java.util.List;
     4 + 
     5 +public class HeaderPoison extends Scan {
     6 + 
     7 + HeaderPoison(String name) {
     8 + super(name);
     9 + scanSettings.importSettings(BurpExtender.guessSettings);
     10 + }
     11 + 
     12 + @Override
     13 + List<IScanIssue> doScan(IHttpRequestResponse req) {
     14 + new ParamGuesser(req, false, Utilities.PARAM_HEADER, BurpExtender.paramGrabber, null, 2147483647, Utilities.globalSettings).run();
     15 + return null;
     16 + }
     17 +}
  • ■ ■ ■ ■ ■
    src/burp/OfferParamGuess.java
    skipped 22 lines
    23 23   List<JMenuItem> options = new ArrayList<>();
    24 24   
    25 25   if(reqs == null || reqs.length == 0) {
    26  - return options;
     26 + if (invocation.getSelectedIssues().length > 0) {
     27 + ArrayList<IHttpRequestResponse> newReqs = new ArrayList<>();
     28 + for(IScanIssue issue: invocation.getSelectedIssues()){
     29 + newReqs.add(issue.getHttpMessages()[0]);
     30 + 
     31 + }
     32 + reqs = newReqs.toArray(new IHttpRequestResponse[0]);
     33 + }
     34 + else {
     35 + return options;
     36 + }
    27 37   }
    28 38   
     39 + JMenu scanMenu = new JMenu("Guess params");
     40 + 
    29 41   JMenuItem allButton = new JMenuItem("Guess everything!");
    30 42   allButton.addActionListener(new TriggerParamGuesser(reqs, false, IParameter.PARAM_URL, paramGrabber, taskEngine));
    31 43  
    32 44   JMenuItem probeButton = new JMenuItem("Guess GET parameters");
    33 45   probeButton.addActionListener(new TriggerParamGuesser(reqs, false, IParameter.PARAM_URL, paramGrabber, taskEngine));
    34 46   allButton.addActionListener(new TriggerParamGuesser(reqs, false, IParameter.PARAM_URL, paramGrabber, taskEngine));
    35  - options.add(probeButton);
     47 + scanMenu.add(probeButton);
    36 48   
    37 49   JMenuItem cookieProbeButton = new JMenuItem("Guess cookie parameters");
    38 50   cookieProbeButton.addActionListener(new TriggerParamGuesser(reqs, false, IParameter.PARAM_COOKIE, paramGrabber, taskEngine));
    39 51   allButton.addActionListener(new TriggerParamGuesser(reqs, false, IParameter.PARAM_COOKIE, paramGrabber, taskEngine));
    40  - options.add(cookieProbeButton);
     52 + scanMenu.add(cookieProbeButton);
    41 53   
    42 54   JMenuItem headerProbeButton = new JMenuItem("Guess headers");
    43 55   headerProbeButton.addActionListener(new TriggerParamGuesser(reqs, false, Utilities.PARAM_HEADER, paramGrabber, taskEngine));
    44 56   allButton.addActionListener(new TriggerParamGuesser(reqs, false, Utilities.PARAM_HEADER, paramGrabber, taskEngine));
    45  - options.add(headerProbeButton);
     57 + scanMenu.add(headerProbeButton);
    46 58   
    47 59  // if (invocation.getSelectionBounds() != null && reqs.length == 1) {
    48 60  // JMenuItem valueProbeButton = new JMenuItem("Guess value");
    skipped 9 lines
    58 70   JMenuItem backendProbeButton = new JMenuItem("*Identify backend parameters*");
    59 71   backendProbeButton.addActionListener(new TriggerParamGuesser(reqs, true, IParameter.PARAM_URL, paramGrabber, taskEngine));
    60 72   allButton.addActionListener(new TriggerParamGuesser(reqs, true, IParameter.PARAM_URL, paramGrabber, taskEngine));
    61  - options.add(backendProbeButton);
     73 + scanMenu.add(backendProbeButton);
    62 74   }
     75 + 
     76 +// if (Utilities.containsBytes(resp, "HTTP/1.1".getBytes())) {
     77 +// JMenuItem tunHeaderProbeButton = new JMenuItem("Guess tunneled headers");
     78 +// tunHeaderProbeButton.addActionListener(new TriggerParamGuesser(reqs, false, Utilities.PARAM_HEADER_TUNNELED, paramGrabber, taskEngine));
     79 +// allButton.addActionListener(new TriggerParamGuesser(reqs, false, Utilities.PARAM_HEADER_TUNNELED, paramGrabber, taskEngine));
     80 +// options.add(tunHeaderProbeButton);
     81 +// }
    63 82   
    64 83   if (resp != null && resp.length > 0 && resp[0] == 'P') {
    65 84   IRequestInfo info = Utilities.helpers.analyzeRequest(req);
    skipped 35 lines
    101 120   JMenuItem postProbeButton = new JMenuItem("Guess " + humanType + " parameter");
    102 121   postProbeButton.addActionListener(new TriggerParamGuesser(reqs, false, type, paramGrabber, taskEngine));
    103 122   allButton.addActionListener(new TriggerParamGuesser(reqs, false, type, paramGrabber, taskEngine));
    104  - options.add(postProbeButton);
     123 + scanMenu.add(postProbeButton);
    105 124   }
    106 125   }
    107 126   }
    108 127   
    109  - options.add(allButton);
     128 + scanMenu.add(allButton);
     129 + options.add(scanMenu);
    110 130   return options;
    111 131   }
    112 132  }
    skipped 1 lines
  • ■ ■ ■ ■ ■ ■
    src/burp/ParamGuesser.java
    skipped 109 lines
    110 110  
    111 111   ArrayList<Attack> paramGuesses = guessParams(attack);
    112 112   if (!paramGuesses.isEmpty()) {
    113  - Utilities.callbacks.addScanIssue(Utilities.reportReflectionIssue(paramGuesses.toArray((new Attack[paramGuesses.size()])), req));
     113 + Utilities.callbacks.addScanIssue(Utilities.reportReflectionIssue(paramGuesses.toArray((new Attack[paramGuesses.size()])), req, "", ""));
    114 114   }
    115 115   } catch (Exception e) {
    116 116   Utilities.out("Attack aborted by exception");
    skipped 80 lines
    197 197   } else {
    198 198   if (!config.getBoolean("bruteforce")) {
    199 199   Utilities.out("Completed attack on " + targetURL);
    200  - Utilities.out("Completed " + (taskEngine.getCompletedTaskCount() + 1) + "/" + (taskEngine.getTaskCount() + taskEngine.getCompletedTaskCount()));
     200 + if (taskEngine != null) {
     201 + Utilities.out("Completed " + (taskEngine.getCompletedTaskCount() + 1) + "/" + (taskEngine.getTaskCount()));
     202 + }
    201 203   return attacks;
    202 204   }
    203 205   state.seed = Utilities.generate(state.seed, bucketSize, newParams);
    skipped 75 lines
    279 281   continue;
    280 282   }
    281 283  
    282  - // TODO: could add mutations here. Shouldn't matter though so leaving for now.
    283 284   Attack WAFCatcher = new Attack(Utilities.attemptRequest(service, Utilities.addOrReplaceHeader(baseRequestResponse.getRequest(), "junk-header", submission)));
    284 285   WAFCatcher.addAttack(new Attack(Utilities.attemptRequest(service, Utilities.addOrReplaceHeader(baseRequestResponse.getRequest(), "junk-head", submission))));
    285 286   if (!Utilities.similar(WAFCatcher, confirmParamGuess)) {
    skipped 11 lines
    297 298   if (type == Utilities.PARAM_HEADER || type == IParameter.PARAM_COOKIE) {
    298 299   cacheSuccess = cachePoison(injector, submission, failAttack.getFirstRequest());
    299 300   }
    300  -
    301  - if (!Utilities.CACHE_ONLY) {
     301 + if (!Utilities.globalSettings.getBoolean("poison only")) {
    302 302   String title = "Secret input: " + Utilities.getNameFromType(type);
    303 303   if (!cacheSuccess && canSeeCache(paramGuess.getFirstRequest().getResponse())) {
    304 304   title = "Secret uncached input: " + Utilities.getNameFromType(type);
    skipped 1 lines
    306 306   if (Utilities.globalSettings.getBoolean("name in issue")) {
    307 307   title += ": " + submission.split("~")[0];
    308 308   }
    309  - Utilities.callbacks.addScanIssue(Utilities.reportReflectionIssue(confirmed.toArray(new Attack[2]), baseRequestResponse, title));
    310  -
     309 + Utilities.callbacks.addScanIssue(Utilities.reportReflectionIssue(confirmed.toArray(new Attack[2]), baseRequestResponse, title, "Unlinked parameter identified."));
    311 310   if (type != Utilities.PARAM_HEADER || Utilities.containsBytes(paramGuess.getFirstRequest().getResponse(), staticCanary)) {
    312 311   scanParam(insertionPoint, injector, submission.split("~", 2)[0]);
    313 312   }
    skipped 12 lines
    326 325   }
    327 326   }
    328 327   }
    329  - } else {
     328 + } else{
    330 329   Utilities.log(targetURL + " couldn't replicate: " + candidates);
    331 330   base.addAttack(paramGuess);
    332 331   }
    skipped 66 lines
    399 398   paramBase.addAttack(altInject.probeAttack(Utilities.generateCanary()));
    400 399   ArrayList<Attack> confirmed = altInject.fuzz(paramBase, validParam);
    401 400   if (!confirmed.isEmpty()) {
    402  - Utilities.callbacks.addScanIssue(Utilities.reportReflectionIssue(confirmed.toArray(new Attack[2]), base, "Potentially swappable param"));
     401 + Utilities.callbacks.addScanIssue(Utilities.reportReflectionIssue(confirmed.toArray(new Attack[2]), base, "Potentially swappable param", ""));
    403 402   }
    404 403  
    405 404   byte[] testReq = injector.getInsertionPoint().buildRequest(Utilities.helpers.stringToBytes(param));
    skipped 228 lines
    634 633  
    635 634  
    636 635   private static boolean canSeeCache(byte[] response) {
     636 + if (response == null) {
     637 + return false;
     638 + }
    637 639   String[] headers = new String[]{"Age", "X-Cache", "Cache", "X-Cache-Hits", "X-Varnish-Cache", "X-Drupal-Cache", "X-Varnish", "CF-Cache-Status", "CF-RAY"};
    638 640   for(String header: headers) {
    639 641   if(Utilities.getHeaderOffsets(response, header) != null) {
    skipped 157 lines
Please wait...
Page is in error, reload to recover