Skip to content

Commit

Permalink
Avoid broken-poc issue by offering both CLTE and TECL attack types
Browse files Browse the repository at this point in the history
  • Loading branch information
albinowax committed Sep 12, 2019
1 parent cc750b1 commit 8e4ba8a
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 33 deletions.
37 changes: 18 additions & 19 deletions src/burp/ChunkContentScan.java
Expand Up @@ -25,17 +25,23 @@ public boolean doConfiguredScan(byte[] original, IHttpService service, HashMap<S
original = Utilities.addOrReplaceHeader(original, "Transfer-Encoding", "foo");
original = Utilities.setHeader(original, "Connection", "close");

byte[] baseReq = makeChunked(original, 0, 0, config, true);
Resp syncedResp = request(service, baseReq);
if (!syncedResp.failed() && !(Utilities.globalSettings.getBoolean("only report exploitable") && syncedResp.getStatus() == 400)) {
byte[] syncedReq = makeChunked(original, 0, 0, config, false);
Resp syncedResp = request(service, syncedReq);
if (syncedResp.failed() || (Utilities.globalSettings.getBoolean("only report exploitable") && syncedResp.getStatus() == 400)) {
Utilities.log("Timeout on first request. Aborting.");
return false;
}

byte[] syncedBreakReq = makeChunked(original, 0, 0, config, true);
Resp syncedBreakResp = request(service, syncedBreakReq);
if (!syncedBreakResp.failed()) {

byte[] reverseLength = makeChunked(original, -6, 0, config, true);

Resp truncatedChunk = request(service, reverseLength, 3);
if (truncatedChunk.timedOut()) {

if (request(service, baseReq).timedOut()) {
if (request(service, syncedBreakReq).timedOut()) {
return false;
}

Expand All @@ -45,10 +51,10 @@ public boolean doConfiguredScan(byte[] original, IHttpService service, HashMap<S

String title = "HTTP Request Smuggling: CL.TE " + String.join("|", config.keySet());

if (leftAlive(baseReq, service) ) {
Resp retryAlive = leftAlive(syncedReq, service);
if (retryAlive != null ) {
syncedResp = retryAlive;
title += " left-alive";
} else {
title += " closed";
}

if (truncatedChunk.getReq().getResponse() != null) {
Expand All @@ -60,18 +66,11 @@ public boolean doConfiguredScan(byte[] original, IHttpService service, HashMap<S
"This suggests that the front-end system is using the Content-Length header, and the backend is using the Transfer-Encoding: chunked header. You should be able to manually verify this using the Repeater, provided you uncheck the 'Update Content-Length' setting on the top menu. <br/>" +
"As such, it may be vulnerable to HTTP Desync attacks, aka Request Smuggling. <br/>" +
"To attempt an actual Desync attack, right click on the attached request and choose 'Desync attack'. Please note that this is not risk-free - other genuine visitors to the site may be affected.<br/><br/>Please refer to <a href=\"https://portswigger.net/blog/http-desync-attacks\">https://portswigger.net/blog/http-desync-attacks</a> for further information. ",
syncedResp, truncatedChunk);
syncedResp, syncedBreakResp, truncatedChunk);
return true;
}
}

baseReq = makeChunked(original, 0, 0, config, false);
syncedResp = request(service, baseReq);
if (syncedResp.failed() || (Utilities.globalSettings.getBoolean("only report exploitable") && syncedResp.getStatus() == 400)) {
Utilities.log("Timeout on first request. Aborting.");
return false;
}

// this is unsafe for CL-TE, so we only attempt it if CL-TE detection failed
byte[] reverseLength = makeChunked(original, 1, 0, config, false); //Utilities.setHeader(baseReq, "Content-Length", "4");
ByteArrayOutputStream reverseLengthBuilder = new ByteArrayOutputStream();
Expand All @@ -86,16 +85,16 @@ public boolean doConfiguredScan(byte[] original, IHttpService service, HashMap<S

if (truncatedChunk.timedOut()) {

if (request(service, baseReq).timedOut()) {
if (request(service, syncedReq).timedOut()) {
return false;
}

String title = "HTTP Request Smuggling: TE.CL " + String.join("|", config.keySet());

if (leftAlive(baseReq, service) ) {
Resp retryAlive = leftAlive(syncedReq, service);
if (retryAlive != null ) {
syncedResp = retryAlive;
title += " left-alive";
} else {
title += " closed";
}

if (truncatedChunk.getReq().getResponse() != null) {
Expand Down
1 change: 1 addition & 0 deletions src/burp/ConfigurableSettings.java
Expand Up @@ -46,6 +46,7 @@ public void registerListener(String key, ConfigListener listener) {
registerSetting("skip vulnerable hosts", false);
registerSetting("skip obsolete permutations", false);
registerSetting("only report exploitable", false);
registerSetting("risky mode", false);


NumberFormat format = NumberFormat.getInstance();
Expand Down
15 changes: 11 additions & 4 deletions src/burp/SmuggleScanBox.java
Expand Up @@ -143,12 +143,14 @@ static ArrayList<Integer> getSpecialChars() {
return chars;
}

boolean leftAlive(byte[] req, IHttpService service) {
Resp leftAlive(byte[] req, IHttpService service) {
byte[] keepalive = Utilities.setHeader(req, "Connection", "keep-alive");
Resp resp = request(service, keepalive);
String connectionType = Utilities.getHeader(resp.getReq().getResponse(), "Connection");
return connectionType.toLowerCase().contains("alive");

if (connectionType.toLowerCase().contains("alive")) {
return resp;
}
return null;
}

static Resp buildPoc(byte[] req, IHttpService service, HashMap<String, Boolean> config) {
Expand Down Expand Up @@ -299,7 +301,12 @@ else if (settings.containsKey("linewrapped1")) {

String ending = "0\r\n\r\n";
if (malformedClose) {
ending = "1\r\nZ\r\nQ\r\n\r\n";
if (Utilities.globalSettings.getBoolean("risky mode")) {
ending = "1\r\nZ\r\n0\r\n\r\n";
}
else {
ending = "1\r\nZ\r\nQ\r\n\r\n";
}
}

int bodySize = baseReq.length - Utilities.getBodyStart(baseReq);
Expand Down
61 changes: 51 additions & 10 deletions src/burp/SuggestAttack.java
Expand Up @@ -7,6 +7,10 @@

public class SuggestAttack implements IContextMenuFactory {

final static String UNKNOWN = "";
final static String CLTE = "CL.TE";
final static String TECL = "TE.CL";

@Override
public List<JMenuItem> createMenuItems(IContextMenuInvocation invocation) {
ArrayList<JMenuItem> options = new ArrayList<>();
Expand All @@ -18,10 +22,38 @@ public List<JMenuItem> createMenuItems(IContextMenuInvocation invocation) {
String request = new String(message.getRequest());
String headers = Utils.getHeaders(request);




if (headers.contains("chunked") || headers.contains("Transfer-Encoding")) {
JMenuItem probeButton = new JMenuItem("Smuggle attack");
probeButton.addActionListener(new LaunchSuggestedAttack(message));
options.add(probeButton);
String type = "";
if (invocation.getSelectedIssues() != null) {
String name = invocation.getSelectedIssues()[0].getIssueName();
if (name.contains("CL.TE")) {
type = CLTE;
}
else if (name.contains("TE.CL")) {
type = TECL;
}
}

if (type.equals(UNKNOWN)) {
type = CLTE;
JMenuItem probeButton = new JMenuItem("Smuggle attack ("+type+")");
probeButton.addActionListener(new LaunchSuggestedAttack(message, type));
options.add(probeButton);
type = TECL;
JMenuItem probeButton2 = new JMenuItem("Smuggle attack ("+type+")");
probeButton2.addActionListener(new LaunchSuggestedAttack(message, type));
options.add(probeButton2);
} else {
JMenuItem probeButton = new JMenuItem("Smuggle attack ("+type+")");
probeButton.addActionListener(new LaunchSuggestedAttack(message, type));
options.add(probeButton);
}



}

}
Expand All @@ -33,9 +65,11 @@ public List<JMenuItem> createMenuItems(IContextMenuInvocation invocation) {
class LaunchSuggestedAttack implements ActionListener {

private IHttpRequestResponse message;
private String type = SuggestAttack.UNKNOWN;

LaunchSuggestedAttack(IHttpRequestResponse message) {
LaunchSuggestedAttack(IHttpRequestResponse message, String type) {
this.message = message;
this.type = type;
}

@Override
Expand All @@ -45,14 +79,21 @@ public void actionPerformed(ActionEvent e) {
String request = new String(message.getRequest());
String script;

if (request.contains(PAYLOAD)) {
// this is CL.TE
script = Utilities.getResource("/CL-TE.py");
if (type.equals(SuggestAttack.CLTE)) {
request = request.replaceFirst(PAYLOAD, "\r\n0\r\n\r\n");
}
else {
// this is either a normal chunked request, or TE.CL
script = Utilities.getResource("/CL-TE.py");
} else if (type.equals(SuggestAttack.TECL)) {
script = Utilities.getResource("/TE-CL.py");
} else {

if (request.contains(PAYLOAD)) {
// this is CL.TE
script = Utilities.getResource("/CL-TE.py");
request = request.replaceFirst(PAYLOAD, "\r\n0\r\n\r\n");
} else {
// this is either a normal chunked request, or TE.CL
script = Utilities.getResource("/TE-CL.py");
}
}

// amend the script to try and ensure the smuggled request gets a different response
Expand Down

0 comments on commit 8e4ba8a

Please sign in to comment.