Skip to content

Commit dae4ea2

Browse files
committed
Release v5.0
1 parent 090fa65 commit dae4ea2

28 files changed

Lines changed: 10184 additions & 952 deletions

README.md

Lines changed: 1004 additions & 134 deletions
Large diffs are not rendered by default.

pom.xml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>com.Bypass</groupId>
88
<artifactId>BypassPro</artifactId>
9-
<version>4.0</version>
9+
<version>5.0</version>
1010
<packaging>jar</packaging>
1111

1212
<dependencies>
@@ -23,6 +23,13 @@
2323
<version>1.33</version>
2424
</dependency>
2525

26+
<dependency>
27+
<groupId>junit</groupId>
28+
<artifactId>junit</artifactId>
29+
<version>4.13.2</version>
30+
<scope>test</scope>
31+
</dependency>
32+
2633
</dependencies>
2734

2835
<build>
@@ -63,4 +70,4 @@
6370
</plugins>
6471
</build>
6572

66-
</project>
73+
</project>

src/main/java/Main/Bypass.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,14 @@ public class Bypass {
1515
final String method;
1616
final String title;
1717
final long id;
18+
/**
19+
* Short source label shown in Dashboard.
20+
* Expected values: auto / send access / send waf / manual waf.
21+
*/
1822
final String tool;
23+
final String reason;
1924

20-
public Bypass(String timestamp, String method, String length, IHttpRequestResponsePersisted requestResponse, URL url, short status, String mimeType, String title, long id, String tool) {
25+
public Bypass(String timestamp, String method, String length, IHttpRequestResponsePersisted requestResponse, URL url, short status, String mimeType, String title, long id, String tool, String reason) {
2126

2227
this.timestamp = timestamp;
2328
this.method = method;
@@ -29,6 +34,7 @@ public Bypass(String timestamp, String method, String length, IHttpRequestRespon
2934
this.title = title;
3035
this.id = id;
3136
this.tool = tool;
37+
this.reason = reason;
3238
}
3339
}
3440

src/main/java/Main/BypassMain.java

Lines changed: 219 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package Main;
22

3+
import Main.ghostbits.AutoGhostBitsGenerator;
4+
import Main.ghostbits.GhostBitsAutoVariant;
5+
import Main.ghostbits.RawSocketSender;
36
import burp.*;
47
import org.apache.commons.lang3.StringUtils;
58

@@ -317,7 +320,8 @@ public void run() {
317320

318321
if (finalResponse != null && finalResponseBytes != null && shouldLog) {
319322
String title = Utils.getBodyTitle(new String(finalResponseBytes, "utf-8"));
320-
addLog(finalResponse, 0, 0, 0, title, tool);
323+
String reason = buildAutoReason(oldStatus, newStatus, ratio, threshold, statusClassChanged);
324+
addLog(finalResponse, title, tool, reason);
321325
}
322326

323327
} catch (Throwable ee) {
@@ -333,6 +337,45 @@ private static boolean isCandidateStatus(short statusCode) {
333337
|| statusCode == 405 || statusCode == 415;
334338
}
335339

340+
/**
341+
* Dashboard only persists addLog's 200/405/415 subset; this reason explains
342+
* the already-accepted candidate without changing that existing filter.
343+
*/
344+
static String buildAutoReason(short oldStatus, short newStatus,
345+
double ratio, double threshold,
346+
boolean statusClassChanged) {
347+
StringBuilder sb = new StringBuilder();
348+
boolean hasStatus = oldStatus > 0 || newStatus > 0;
349+
if (hasStatus) {
350+
sb.append("status:")
351+
.append(formatStatus(oldStatus))
352+
.append(" -> ")
353+
.append(formatStatus(newStatus));
354+
}
355+
356+
boolean ratioUsable = Double.isFinite(ratio) && Double.isFinite(threshold);
357+
if (ratioUsable && ratio < threshold) {
358+
if (sb.length() > 0) {
359+
sb.append("; ");
360+
}
361+
sb.append("sim:")
362+
.append(String.format(Locale.ROOT, "%.2f", ratio))
363+
.append(" < ")
364+
.append(String.format(Locale.ROOT, "%.2f", threshold));
365+
} else if (statusClassChanged) {
366+
if (sb.length() > 0) {
367+
sb.append("; ");
368+
}
369+
sb.append("class changed");
370+
}
371+
372+
return sb.toString();
373+
}
374+
375+
private static String formatStatus(short status) {
376+
return status <= 0 ? "?" : Short.toString(status);
377+
}
378+
336379
private static boolean isRedirectStatus(short statusCode) {
337380
return statusCode == 301 || statusCode == 302 || statusCode == 303 || statusCode == 307 || statusCode == 308;
338381
}
@@ -523,7 +566,7 @@ public void actionPerformed(ActionEvent e) {
523566

524567
new Thread(() -> {
525568
IHttpRequestResponse[] iHttpRequestResponses = invocation.getSelectedMessages();
526-
processHttp(iHttpRequestResponses, "Auth Bypass (Auto)", "access_control");
569+
processHttp(iHttpRequestResponses, "send access", Utils.PROFILE_AUTO_ACCESS_BYPASS);
527570
}).start();
528571

529572
}
@@ -533,8 +576,8 @@ public void actionPerformed(ActionEvent e) {
533576
public void actionPerformed(ActionEvent e) {
534577
new Thread(() -> {
535578
IHttpRequestResponse[] iHttpRequestResponses = invocation.getSelectedMessages();
536-
// WAF 模式:若配置未提供 waf profile,会回退到 access_control
537-
processHttp(iHttpRequestResponses, "WAF Bypass (Auto)", "waf");
579+
// WAF 模式:使用 profiles.auto_waf_bypass
580+
processHttp(iHttpRequestResponses, "send waf", Utils.PROFILE_AUTO_WAF_BYPASS);
538581
}).start();
539582
}
540583
});
@@ -558,7 +601,7 @@ public void actionPerformed(ActionEvent e) {
558601
return list;
559602
}
560603

561-
private void addLog(IHttpRequestResponse messageInfo, int toolFlag, long time, int row, String title, String tool) {
604+
private void addLog(IHttpRequestResponse messageInfo, String title, String tool, String reason) {
562605
// 入表写 UI:放到 EDT 串行执行,避免并发写 ArrayList 造成数据竞争
563606
try {
564607
short statusCode = Utils.helpers.analyzeResponse(messageInfo.getResponse()).getStatusCode();
@@ -572,7 +615,7 @@ private void addLog(IHttpRequestResponse messageInfo, int toolFlag, long time, i
572615
Utils.helpers.analyzeResponse(messageInfo.getResponse()).getStatusCode(),
573616
Utils.helpers.analyzeResponse(messageInfo.getResponse()).getStatedMimeType(),
574617
title,
575-
nextId(), tool));
618+
nextId(), tool, reason));
576619
}
577620
} catch (Exception ignored) {
578621
}
@@ -641,7 +684,7 @@ public void processHttp(IHttpRequestResponse[] iHttpRequestResponses, String too
641684
}
642685

643686
// WAF 模式:额外生成 Body 编码变体(仅 POST/PUT/PATCH)
644-
if ("waf".equals(profile)) {
687+
if (Utils.PROFILE_AUTO_WAF_BYPASS.equals(profile)) {
645688
byte[] requestBytes = iHttpRequestResponse.getRequest();
646689
if (requestBytes != null) {
647690
IRequestInfo wafReqInfo = Utils.helpers.analyzeRequest(iHttpRequestResponse.getHttpService(),
@@ -657,6 +700,12 @@ public void processHttp(IHttpRequestResponse[] iHttpRequestResponses, String too
657700
es.submit(new Run_body_encoded_request(encodedRequest, iHttpRequestResponse, tool));
658701
}
659702
}
703+
704+
List<GhostBitsAutoVariant> ghostVariants = generateGhostBitsAutoVariants(requestBytes);
705+
addAllRequestNum(ghostVariants.size());
706+
for (GhostBitsAutoVariant variant : ghostVariants) {
707+
es.submit(new Run_ghost_bits_request(variant, iHttpRequestResponse, tool));
708+
}
660709
}
661710
}
662711

@@ -665,6 +714,13 @@ public void processHttp(IHttpRequestResponse[] iHttpRequestResponses, String too
665714

666715
}
667716

717+
private List<GhostBitsAutoVariant> generateGhostBitsAutoVariants(byte[] requestBytes) {
718+
AutoGhostBitsGenerator generator = new AutoGhostBitsGenerator(
719+
Utils.getGhostBitsRule(),
720+
Utils.getWafGhostBitsOptions());
721+
return generator.generate(requestBytes);
722+
}
723+
668724
/**
669725
* 判断请求是否有 Body
670726
*/
@@ -1140,16 +1196,169 @@ public void run() {
11401196

11411197
if (finalResponse != null && finalResponseBytes != null && shouldLog) {
11421198
String title = Utils.getBodyTitle(new String(finalResponseBytes, "utf-8"));
1143-
addLog(finalResponse, 0, 0, 0, title, tool);
1199+
String reason = buildAutoReason(oldStatus, newStatus, ratio, threshold, statusClassChanged);
1200+
addLog(finalResponse, title, tool, reason);
11441201
}
11451202
} catch (Throwable e) {
11461203
Utils.panel.addErrorRequestNum(1);
11471204
}
11481205
}
11491206
}
11501207

1208+
private String ghostToolLabel(String tool) {
1209+
if (tool == null || tool.trim().isEmpty()) {
1210+
return "ghost";
1211+
}
1212+
String normalized = tool.trim().toLowerCase();
1213+
if ("auto".equals(normalized)) {
1214+
return "auto-waf/ghost";
1215+
}
1216+
if ("send waf".equals(normalized)) {
1217+
return "send-waf/ghost";
1218+
}
1219+
return tool + "/ghost";
1220+
}
1221+
1222+
/**
1223+
* Auto WAF 的 Ghost Bits 模板探测请求。
1224+
* 关键区别:path/header 含非 ASCII 或模板 sender=raw 时走 RawSocketSender。
1225+
*/
1226+
class Run_ghost_bits_request implements Runnable {
1227+
private final GhostBitsAutoVariant variant;
1228+
private final IHttpRequestResponse originalReqResp;
1229+
private final String tool;
1230+
1231+
public Run_ghost_bits_request(GhostBitsAutoVariant variant, IHttpRequestResponse originalReqResp, String tool) {
1232+
this.variant = variant;
1233+
this.originalReqResp = originalReqResp;
1234+
this.tool = tool;
1235+
}
1236+
1237+
@Override
1238+
public void run() {
1239+
try {
1240+
IHttpService service = originalReqResp.getHttpService();
1241+
IHttpRequestResponse finalResponse;
1242+
if (variant.isRawRequired()) {
1243+
finalResponse = sendGhostVariantRaw(variant, service);
1244+
} else {
1245+
IHttpRequestResponse firstResponse = Utils.callbacks.makeHttpRequest(service, variant.getRequestBytes());
1246+
finalResponse = followRedirectsIfNeeded(firstResponse, variant.getRequestBytes(),
1247+
service, MAX_REDIRECT_HOPS);
1248+
}
1249+
1250+
byte[] finalResponseBytes = finalResponse == null ? null : finalResponse.getResponse();
1251+
byte[] oldResponseBytes = originalReqResp.getResponse();
1252+
short oldStatus = getStatusSafe(oldResponseBytes);
1253+
short newStatus = getStatusSafe(finalResponseBytes);
1254+
String newMime = getMimeSafe(finalResponseBytes);
1255+
1256+
String oldBody = extractBodyAsString(oldResponseBytes);
1257+
String newBody = extractBodyAsString(finalResponseBytes);
1258+
double threshold = Utils.panel.getSimilarityThreshold();
1259+
double ratio = DiffPage.getRatio(oldBody, newBody, newMime);
1260+
boolean statusClassChanged = (oldStatus > 0 && newStatus > 0) && (oldStatus / 100 != newStatus / 100);
1261+
1262+
String baseReason = buildAutoReason(oldStatus, newStatus, ratio, threshold, statusClassChanged);
1263+
String signatureReason = buildGhostSignatureReason(newBody);
1264+
boolean signatureMatched = !signatureReason.isEmpty();
1265+
boolean shouldLog = isCandidateStatus(newStatus)
1266+
&& (signatureMatched || ratio < threshold || statusClassChanged);
1267+
String reason = variant.getReason();
1268+
if (!baseReason.isEmpty()) {
1269+
reason += "; " + baseReason;
1270+
}
1271+
if (signatureMatched) {
1272+
reason += "; " + signatureReason;
1273+
}
1274+
1275+
addFinishRequestNum(1);
1276+
1277+
if (finalResponse != null && finalResponseBytes != null && shouldLog) {
1278+
String title = Utils.getBodyTitle(new String(finalResponseBytes, "utf-8"));
1279+
addLog(finalResponse, title, ghostToolLabel(tool), reason);
1280+
}
1281+
} catch (Throwable e) {
1282+
Utils.panel.addErrorRequestNum(1);
1283+
}
1284+
}
1285+
}
1286+
1287+
private IHttpRequestResponse sendGhostVariantRaw(GhostBitsAutoVariant variant, IHttpService service) throws Exception {
1288+
String host = service.getHost();
1289+
int port = service.getPort();
1290+
boolean https = "https".equalsIgnoreCase(service.getProtocol());
1291+
if (port <= 0) {
1292+
port = https ? 443 : 80;
1293+
}
1294+
RawSocketSender.RawResponse raw = new RawSocketSender().send(host, port, https,
1295+
variant.getRequestBytes(), 5000, 5000);
1296+
return new SimpleHttpRequestResponse(service,
1297+
raw.getRequestBytesActuallySent(),
1298+
raw.getResponseBytes());
1299+
}
1300+
1301+
private String buildGhostSignatureReason(String body) {
1302+
if (body == null || body.isEmpty()) {
1303+
return "";
1304+
}
1305+
String lower = body.toLowerCase(Locale.ROOT);
1306+
if (lower.contains("root:x:") || lower.contains("/bin/bash")
1307+
|| lower.contains("nologin") || lower.contains("daemon:x:")) {
1308+
return "ghost file signature matched";
1309+
}
1310+
if (lower.contains("[fonts]") || lower.contains("[extensions]") || lower.contains("[mci extensions]")) {
1311+
return "ghost file signature matched";
1312+
}
1313+
return "";
1314+
}
1315+
1316+
static class SimpleHttpRequestResponse implements IHttpRequestResponse {
1317+
private byte[] request;
1318+
private byte[] response;
1319+
private String comment;
1320+
private String highlight;
1321+
private IHttpService service;
1322+
1323+
SimpleHttpRequestResponse(IHttpService service, byte[] request, byte[] response) {
1324+
this.service = service;
1325+
this.request = request == null ? new byte[0] : request;
1326+
this.response = response == null ? new byte[0] : response;
1327+
}
1328+
1329+
@Override
1330+
public byte[] getRequest() { return request; }
1331+
1332+
@Override
1333+
public void setRequest(byte[] message) { request = message == null ? new byte[0] : message; }
1334+
1335+
@Override
1336+
public byte[] getResponse() { return response; }
1337+
1338+
@Override
1339+
public void setResponse(byte[] message) { response = message == null ? new byte[0] : message; }
1340+
1341+
@Override
1342+
public String getComment() { return comment; }
1343+
1344+
@Override
1345+
public void setComment(String comment) { this.comment = comment; }
1346+
1347+
@Override
1348+
public String getHighlight() { return highlight; }
1349+
1350+
@Override
1351+
public void setHighlight(String color) { this.highlight = color; }
1352+
1353+
@Override
1354+
public IHttpService getHttpService() { return service; }
1355+
1356+
@Override
1357+
public void setHttpService(IHttpService httpService) { this.service = httpService; }
1358+
}
1359+
11511360
public void processHttp(IHttpRequestResponse[] iHttpRequestResponses, String tool) {
1152-
processHttp(iHttpRequestResponses, tool, "access_control");
1361+
processHttp(iHttpRequestResponses, tool, Utils.PROFILE_AUTO_ACCESS_BYPASS);
11531362
}
11541363

11551364
/**
@@ -1177,7 +1386,7 @@ public void processProxyMessage(boolean messageIsRequest, IInterceptedProxyMessa
11771386
}
11781387
}
11791388
new Thread(() -> {
1180-
processHttp(iHttpRequestResponses, "Auto Scan", "access_control");
1389+
processHttp(iHttpRequestResponses, "auto", Utils.PROFILE_AUTO_ACCESS_BYPASS);
11811390
}).start();
11821391

11831392
}

0 commit comments

Comments
 (0)