diff --git a/bulkScan-all.jar b/bulkScan-all.jar index d12b26f..afb2c60 100644 Binary files a/bulkScan-all.jar and b/bulkScan-all.jar differ diff --git a/proxy.md b/proxy.md index 4d08ead..a4ae210 100644 --- a/proxy.md +++ b/proxy.md @@ -3,15 +3,15 @@ Param Miner has a feature called "Identify proxyable destinations". This can lead to high-impact discoveries, such as systems that are meant to be internal-only. -It will work out of the box, but a little configuration will make it a lot more powerful. - -- Tick `external subdomain lookup` to dynmically look up known subdomains using columbus.elmasy.com. Warning: this discloses the top-level private domain that you are targeting. That's why it's not enabled by default. -- Use `subdomains-generic` to specify the path to your own subdomain wordlist. Download them from sources like: https://wordlists.assetnote.io/ and https://github.com/danielmiessler/SecLists/tree/master/Discovery/DNS -- Use `subdomains-specific` to specify a folder blah - - +It will work out of the box, but a little configuration will make it a lot more powerful: +Exploring known subdomains (highly recommended) +- Tick `external subdomain lookup` to dynamically look up known subdomains using columbus.elmasy.com. Warning: this discloses the top-level private domain that you are targeting. For example, if you target `beta.api.example.com`, Elmasy will see `example.com` in their server logs. That's why it's not enabled by default. +- If you have an alternative source of subdomains from your own recon, you can integrate these by placing them into a folder in the format /folder/top-level-domain, and using the `subdomains-specific` setting to load it. For example, if you set the path to `/hostnames/$domain` and scan `proxy.example.com`, Param Miner will load domains from `/hostnames/example.com` +Additional hostname wordlists: +- Use `subdomains-generic` to specify the path to your own subdomain wordlist. Download them from sources like: https://wordlists.assetnote.io/ and https://github.com/danielmiessler/SecLists/tree/master/Discovery/DNS +For further information, please refer to https://portswigger.net/research/listen-to-the-whispers -For further information, please refer to https://portswigger.net/research/listen-to-the-whispers \ No newline at end of file +If it still makes no sense, please let me know - I want people to get the most out of this tool! diff --git a/src/burp/BurpExtender.java b/src/burp/BurpExtender.java index 6179be6..fb683e9 100644 --- a/src/burp/BurpExtender.java +++ b/src/burp/BurpExtender.java @@ -47,6 +47,7 @@ public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks) { // param-guess only //guessSettings.importSettings(globalSettings); + guessSettings.register("quantitative diff keys", "time", "Use timing info to detect parameters. Disable this to make Param Miner faster."); // this overwrites the setting from bulkScan guessSettings.register("learn observed words", false); guessSettings.register("skip boring words", true, "When mining headers, don't check for well known and typically not very exciting headers"); guessSettings.register("only report unique params", false, "Only report a parameter with a given name once, regardless of how many endpoints are scanned"); diff --git a/src/burp/DiscoveredParam.java b/src/burp/DiscoveredParam.java index 1678fd7..fffea8d 100644 --- a/src/burp/DiscoveredParam.java +++ b/src/burp/DiscoveredParam.java @@ -1,5 +1,9 @@ package burp; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.HttpRequestResponse; +import burp.api.montoya.http.message.requests.HttpRequest; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -103,6 +107,8 @@ public void report() { String typeName = BulkUtilities.getNameFromType(type); String title = "Secret input: " + typeName; + String detail = "Unlinked parameter identified. "; + if (!cachePoisoned && canSeeCache) { title = "Secret uncached input: " + typeName; } @@ -117,13 +123,23 @@ public void report() { if (dynamicOnly) { title += " [dynamic-only]"; + detail += "This parameter only causes a response difference when using a fresh value. This suggests that it may be included in the server's cache key. Consider trying cache poisoning attacks on the target. "; } if (magicIP) { title += " [magic-ip]"; } - BulkUtilities.callbacks.addScanIssue(BulkUtilities.reportReflectionIssue(evidence.toArray(new Attack[2]), baseRequestResponse, title, "Unlinked parameter identified.")); + if (!BulkUtilities.isBurpPro()) { // || Utilities.globalSettings.getBoolean("report to organizer") + // todo add some details on the attributes to look at here +// HttpRequest directRequest = HttpRequest.httpRequest(directService, ByteArray.byteArray(evidence.get(0).getFirstRequest().getRequest())); +// HttpRequestResponse resp = Utilities.montoyaApi.http().sendRequest(directRequest); +// Scan.reportToOrganiser(title, null, detail, Collections.singletonList(resp)); + // todo just need one that doesn't have a null response? + //Lensmine.sendToOrganiser(Utilities.buildMontoyaResp(evidence.get(0).), detail); + } + + BulkUtilities.callbacks.addScanIssue(BulkUtilities.reportReflectionIssue(evidence.toArray(new Attack[2]), baseRequestResponse, title, detail)); } diff --git a/src/burp/Lenscrack.java b/src/burp/Lenscrack.java index c28f2ad..7b3b1a8 100644 --- a/src/burp/Lenscrack.java +++ b/src/burp/Lenscrack.java @@ -14,8 +14,13 @@ public class Lenscrack extends Scan { Lenscrack(String name) { super(name); scanSettings.register("overlong-detection", true, "Use overlong dns labels for detection"); - scanSettings.register("auto-scan for proxyable destinations", false, "If wildcard-routing is detected, try to enumerate accessible domains. To configure related settings, run 'Identify proxyable destinations'"); + scanSettings.register("auto-scan for proxyable destinations", true, "If wildcard-routing is detected, try to enumerate accessible domains. To configure related settings, run 'Identify proxyable destinations'"); scanSettings.register("mining: filter 500s", true, "Don't report hostnames that return a 50X status"); + scanSettings.register("subdomains-builtin", true, "Use the builtin wordlist to discover interesting proxyable destinations"); + scanSettings.register("subdomains-generic", "", "/path/to/wordlist"); + scanSettings.register("subdomains-specific", "", "Format: /subdomains/$domain. Read https://github.com/PortSwigger/param-miner/proxy.md for further info."); + scanSettings.register("external subdomain lookup", false, "Look up subdomains using columbus.elmasy.com. Warning: this discloses the top-level private domain that you are targeting."); + scanSettings.register("I read the docs", false, "Read the docs at https://github.com/PortSwigger/param-miner/proxy.md then check this box to stop nagging me to read the docs."); } static String TARGETHEADER = "Host"; diff --git a/src/burp/Lensmine.java b/src/burp/Lensmine.java index 8cf5f12..2622138 100644 --- a/src/burp/Lensmine.java +++ b/src/burp/Lensmine.java @@ -21,11 +21,11 @@ public class Lensmine extends Scan { public Lensmine(String name) { super(name); scanSettings.register("mining: filter 500s", true, "Don't report hostnames that return a 50X status"); - scanSettings.register("subdomains-builtin", true, ""); + scanSettings.register("subdomains-builtin", true, "Use the builtin wordlist to discover interesting proxyable destinations"); scanSettings.register("subdomains-generic", "", "/path/to/wordlist"); - scanSettings.register("subdomains-specific", "", "/subdomains/$domain"); + scanSettings.register("subdomains-specific", "", "Format: /subdomains/$domain. Read https://github.com/PortSwigger/param-miner/proxy.md for further info."); scanSettings.register("external subdomain lookup", false, "Look up subdomains using columbus.elmasy.com. Warning: this discloses the top-level private domain that you are targeting."); - scanSettings.register("I read the docs", false, "Stop nagging me to read the docs."); + scanSettings.register("I read the docs", false, "Read the docs at https://github.com/PortSwigger/param-miner/proxy.md then check this box to stop nagging me to read the docs."); } static MineFindings mineSubdomains(byte[] req, IHttpService service, String domain, int maxDomainsToCheck) { diff --git a/src/burp/ParamGuesser.java b/src/burp/ParamGuesser.java index f6e11bb..d64ca4f 100644 --- a/src/burp/ParamGuesser.java +++ b/src/burp/ParamGuesser.java @@ -5,6 +5,8 @@ import java.util.*; import java.util.concurrent.ThreadPoolExecutor; +import static burp.Lenscrack.reportPairs; + class SimpleScan implements Runnable, IExtensionStateListener { @@ -292,8 +294,9 @@ private ArrayList guessParams(ParamAttack state) { } } - - Probe validParam = new Probe("Found unlinked param: " + submission, 4, submission); + String title = "Found unlinked param: " + submission; + + Probe validParam = new Probe(title, 4, submission); validParam.setEscapeStrings(Keysmith.permute(submission), Keysmith.permute(submission, false)); validParam.setRandomAnchor(false); validParam.setPrefix(Probe.REPLACE); @@ -305,12 +308,22 @@ private ArrayList guessParams(ParamAttack state) { continue; } +// // if timing evidence only, double-confirm +// if (true) { +// AttackPair pair = new AttackPair(title, submission, submission+"z", true); +// pair.attempt(baseRequestResponse, injector.getInsertionPoint()); +// if (pair.valid()) { +// reportPairs("Time-based param detection: "+submission, "", "", baseRequestResponse.getRequest(), pair.result); +// } +// } + state.alreadyReported.add(submission); BulkUtilities.reportedParams.add(submission); state.alreadyReported.add(submission.split("~", 2)[0]); BulkUtilities.reportedParams.add(submission.split("~", 2)[0]); BulkUtilities.out("Identified parameter on " + targetURL + ": " + submission); + DiscoveredParam discoveredParam = new DiscoveredParam(confirmed, injector, submission, failAttack, paramGuess, baseRequestResponse); discoveredParam.exploreAndReport(); base = state.updateBaseline(); diff --git a/src/burp/TimeInjector.java b/src/burp/TimeInjector.java index 18bb7a1..91e77fe 100644 --- a/src/burp/TimeInjector.java +++ b/src/burp/TimeInjector.java @@ -1,11 +1,6 @@ package burp; -import burp.api.montoya.http.message.params.HttpParameter; -import burp.api.montoya.http.message.requests.HttpRequest; -import org.apache.commons.lang3.StringUtils; - import java.util.ArrayList; -import java.util.Base64; import java.util.HashMap; import java.util.List; @@ -26,16 +21,16 @@ List doScan(IHttpRequestResponse baseRequestResponse, IScannerInsert // todo scan path-params for double URL encoding? - attacks.creatAttackPair("Rest", baseValue+"%23"+canary, baseValue+"%21"+canary); + //attacks.createAttackPair("Rest", baseValue+"%23"+canary, baseValue+"%21"+canary); //attacks.creatAttackPair("EL-RCE", baseValue+"${\"x\"}", baseValue+"${\"x\"\"}"); //attacks.creatAttackPair("URL-v3", baseValue+"#"+canary, baseValue+"$"+canary); - attacks.creatAttackPair("Escape-sequence-cb", canary+"\\u0061", canary+"\\v0061"); - attacks.creatAttackPair("Double-quote", canary+"x\"\\yz", canary+"x\\\"z"); - attacks.creatAttackPair("Single-quote", canary+"x'\\z", canary+"x\\'z"); - attacks.creatAttackPair("SQL-apos", canary+"x''z", canary+"x'z'z"); + attacks.createAttackPair("Escape-sequence-cb", canary+"\\u0061", canary+"\\v0061"); + attacks.createAttackPair("Double-quote", canary+"x\"\\yz", canary+"x\\\"z"); + attacks.createAttackPair("Single-quote", canary+"x'\\z", canary+"x\\'z"); + attacks.createAttackPair("SQL-apos", canary+"x''z", canary+"x'z'z"); // // - attacks.creatAttackPair("XML Entity-cb", canary+"&", canary+"&amx;"); + attacks.createAttackPair("XML Entity-cb", canary+"&", canary+"&amx;"); //attacks.creatAttackPair("XML quote", "x\" y='", "x'\" y=\""); //attacks.creatAttackPair("SQL LIKE", "a%", "Z%"); //attacks.creatAttackPair("SQL LIKEDUD", "!", "[!]"); @@ -102,7 +97,7 @@ public AttackPairFactory(IHttpRequestResponse baseRequestResponse, IScannerInser } IScannerInsertionPoint iScannerInsertionPoint; - AttackPair creatAttackPair(String title, String left, String right) { + AttackPair createAttackPair(String title, String left, String right) { AttackPair pair = new AttackPair(title, left, right, true); pairs.put(title, pair); pair.attempt(baseRequestResponse, iScannerInsertionPoint); diff --git a/src/burp/ValueProbes.java b/src/burp/ValueProbes.java index 5e57a44..e95dcac 100644 --- a/src/burp/ValueProbes.java +++ b/src/burp/ValueProbes.java @@ -6,6 +6,10 @@ public class ValueProbes { static boolean triggersPingback(IHttpRequestResponse baseRequestResponse, IScannerInsertionPoint insertionPoint) { + if (!BulkUtilities.isBurpPro()) { + return false; + } + String collab = BasicCollab.getPayload(); Resp resp = Scan.request(baseRequestResponse.getHttpService(), insertionPoint.buildRequest(collab.getBytes())); if (BasicCollab.checkPayload(collab.split("[.]")[0])) {