Skip to content

Commit 506cb62

Browse files
committed
Initial release
0 parents  commit 506cb62

File tree

12 files changed

+513
-0
lines changed

12 files changed

+513
-0
lines changed

LICENSE

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Copyright 2020 James Otten, Optiv Security Inc.
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4+
5+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6+
7+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Azure API Management Tracing Helper
2+
3+
Burp extension to show Azure API Management [tracing](https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-api-inspector) output inside Burp.
4+
5+
## Screenshots
6+
7+
The "Azure API Management Tracing" tab shows pretty-printed tracing data.
8+
![tab](images/tab.png)
9+
10+
Issues are created when tracing is identified.
11+
![issue](images/issue.png)

build.gradle

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
plugins {
2+
id 'java'
3+
}
4+
5+
group 'com.optiv.azureapimanagementtracing'
6+
version '1.0-SNAPSHOT'
7+
8+
sourceCompatibility = 1.8
9+
10+
repositories {
11+
mavenCentral()
12+
}
13+
14+
dependencies {
15+
compile 'net.portswigger.burp.extender:burp-extender-api:1.7.13'
16+
compile 'com.google.code.gson:gson:2.8.5'
17+
}
18+
19+
task fatJar(type: Jar) {
20+
baseName = project.name + '-all'
21+
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
22+
with jar
23+
}

images/issue.png

71.5 KB
Loading

images/tab.png

105 KB
Loading

settings.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
rootProject.name = 'azure-api-management-tracing'
2+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package burp;
2+
3+
import com.optiv.azureapimanagementtracing.TraceScanner;
4+
import com.optiv.azureapimanagementtracing.TraceViewTabFactory;
5+
6+
import java.io.PrintWriter;
7+
8+
public class BurpExtender implements IBurpExtender {
9+
private static final String name = "Azure API Management Request Tracing Helper";
10+
11+
@Override
12+
public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {
13+
// Set our extension name
14+
callbacks.setExtensionName(name);
15+
16+
// Register the custom editor tab
17+
TraceViewTabFactory tabFactory = new TraceViewTabFactory(callbacks);
18+
callbacks.registerMessageEditorTabFactory(tabFactory);
19+
20+
// Register the scanner
21+
TraceScanner scanner = new TraceScanner(callbacks);
22+
callbacks.registerScannerCheck(scanner);
23+
24+
PrintWriter stdout = new PrintWriter(callbacks.getStdout(), true);
25+
stdout.println(name + " started");
26+
}
27+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package com.optiv.azureapimanagementtracing;
2+
3+
import burp.*;
4+
5+
import java.net.URL;
6+
7+
class ScanIssue implements IScanIssue {
8+
public enum TraceIssueType {
9+
RequestHeaderIncorrectValue,
10+
ResponseHeader
11+
}
12+
13+
private final IExtensionHelpers helpers;
14+
private final IHttpRequestResponseWithMarkers requestResponse;
15+
private final IHttpRequestResponseWithMarkers traceRequestResponse;
16+
private final String traceUrl;
17+
private final TraceIssueType issueType;
18+
19+
public ScanIssue(IExtensionHelpers helpers, IHttpRequestResponseWithMarkers requestResponse, TraceIssueType issueType) {
20+
this.helpers = helpers;
21+
this.requestResponse = requestResponse;
22+
this.traceRequestResponse = null;
23+
this.traceUrl = null;
24+
this.issueType = issueType;
25+
}
26+
27+
public ScanIssue(IExtensionHelpers helpers, IHttpRequestResponseWithMarkers requestResponse, IHttpRequestResponseWithMarkers traceInfo, String traceUrl, TraceIssueType issueType) {
28+
this.helpers = helpers;
29+
this.requestResponse = requestResponse;
30+
this.traceRequestResponse = traceInfo;
31+
this.traceUrl = traceUrl;
32+
this.issueType = issueType;
33+
}
34+
35+
@Override
36+
public URL getUrl() {
37+
return this.helpers.analyzeRequest(requestResponse.getHttpService(), this.requestResponse.getRequest()).getUrl();
38+
}
39+
40+
@Override
41+
public String getIssueName() {
42+
switch (this.issueType) {
43+
case RequestHeaderIncorrectValue:
44+
return "Azure API Management Tracing";
45+
case ResponseHeader:
46+
return "Azure API Management Tracing Enabled";
47+
}
48+
return null;
49+
}
50+
51+
@Override
52+
public int getIssueType() {
53+
return 0;
54+
}
55+
56+
@Override
57+
public String getSeverity() {
58+
switch (this.issueType) {
59+
case RequestHeaderIncorrectValue:
60+
return "Information";
61+
case ResponseHeader:
62+
return "Medium";
63+
}
64+
return null;
65+
}
66+
67+
@Override
68+
public String getConfidence() {
69+
switch (this.issueType) {
70+
case RequestHeaderIncorrectValue:
71+
return "Tentative";
72+
case ResponseHeader:
73+
return "Certain";
74+
}
75+
return null;
76+
}
77+
78+
@Override
79+
public String getIssueBackground() {
80+
return "<p>Azure API Management allows developers to view tracing information when this option is configured by system administrators." +
81+
" Trace information often contains technical information and URLs to backend services. Tracing can be enabled for each consumer/subscriber to the API.</p>" +
82+
"<ul><li><a href='https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-api-inspector'>https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-api-inspector</a></li></ul>";
83+
}
84+
85+
@Override
86+
public String getRemediationBackground() {
87+
switch (this.issueType) {
88+
case RequestHeaderIncorrectValue:
89+
return "Ensure that tracing is disabled in the Azure portal.";
90+
case ResponseHeader:
91+
return "<p>Disable tracing in the Azure portal for the current subscriber.</p>" +
92+
"<b>Vulnerability classifications</b><br/>" +
93+
"<ul><li><a href='https://cwe.mitre.org/data/definitions/200.html'>CWE-200: Information Exposure</a></li></ul>";
94+
}
95+
return null;
96+
}
97+
98+
@Override
99+
public String getIssueDetail() {
100+
switch (this.issueType) {
101+
case RequestHeaderIncorrectValue:
102+
return "The HTTP request header 'Ocp-Apim-Trace' was found to be in use by the application.";
103+
case ResponseHeader:
104+
return "<p>Tracing was found to be enabled for the current user on the endpoint:</p>" +
105+
"<ul><li>" + this.getUrl().toString() + "</li></ul>" +
106+
"<p>The following temporary URL was returned in the HTTP response header \"Ocp-Apim-Trace-Location\":</p>" +
107+
"<ul><li><a href='" + this.traceUrl + "'>"+ this.traceUrl + "</a></li></ul>";
108+
}
109+
return null;
110+
}
111+
112+
@Override
113+
public String getRemediationDetail() {
114+
return null;
115+
}
116+
117+
@Override
118+
public IHttpRequestResponseWithMarkers[] getHttpMessages() {
119+
if (this.traceRequestResponse == null) {
120+
return new IHttpRequestResponseWithMarkers[]{requestResponse};
121+
} else {
122+
return new IHttpRequestResponseWithMarkers[]{requestResponse, traceRequestResponse};
123+
}
124+
}
125+
126+
@Override
127+
public IHttpService getHttpService() {
128+
return this.requestResponse.getHttpService();
129+
}
130+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package com.optiv.azureapimanagementtracing;
2+
3+
import burp.*;
4+
5+
import java.io.ByteArrayOutputStream;
6+
import java.io.IOException;
7+
import java.util.ArrayList;
8+
import java.util.Arrays;
9+
import java.util.List;
10+
11+
public class TraceScanner implements IScannerCheck {
12+
private final IBurpExtenderCallbacks callbacks;
13+
private final IExtensionHelpers helpers;
14+
15+
public TraceScanner(IBurpExtenderCallbacks callbacks) {
16+
this.callbacks = callbacks;
17+
this.helpers = callbacks.getHelpers();
18+
}
19+
20+
@Override
21+
public List<IScanIssue> doPassiveScan(IHttpRequestResponse baseRequestResponse) {
22+
List<IScanIssue> issues = new ArrayList<>();
23+
ArrayList<int[]> reqMarkers = new ArrayList<>();
24+
ArrayList<int[]> resMarkers = new ArrayList<>();
25+
26+
reqMarkers.add(Utils.getRequestHeaderOffsets(baseRequestResponse.getRequest(), this.helpers));
27+
resMarkers.add(Utils.getResponseHeaderOffsets(baseRequestResponse.getResponse(), this.helpers));
28+
IHttpRequestResponseWithMarkers markedRequestResponse = this.callbacks.applyMarkers(baseRequestResponse, reqMarkers, resMarkers);
29+
30+
if(resMarkers.get(0) != null) {
31+
int start = resMarkers.get(0)[0] + Utils.responseHeaderSearchText.length();
32+
int end = resMarkers.get(0)[1];
33+
String url = this.helpers.bytesToString(Arrays.copyOfRange(baseRequestResponse.getResponse(), start, end));
34+
IHttpRequestResponseWithMarkers traceRequestResponse = this.callbacks.applyMarkers(Utils.sendRequest(url, this.callbacks), null, null);
35+
issues.add(new ScanIssue(helpers, markedRequestResponse, traceRequestResponse, url, ScanIssue.TraceIssueType.ResponseHeader));
36+
} else {
37+
// Only check for the request header if we didn't get the response header
38+
byte[] request = baseRequestResponse.getRequest();
39+
String requestHeaderValue = Utils.getRequestHeader(baseRequestResponse.getRequest(), this.helpers);
40+
41+
if(reqMarkers.get(0) != null) {
42+
if (!requestHeaderValue.equals("true")) {
43+
// Add an info if the right header was there with the wrong value
44+
issues.add(new ScanIssue(helpers, markedRequestResponse, ScanIssue.TraceIssueType.RequestHeaderIncorrectValue));
45+
46+
// The header was set, but not set to true. Try setting it to true
47+
ByteArrayOutputStream modifiedRequest = new ByteArrayOutputStream();
48+
try {
49+
modifiedRequest.write(Arrays.copyOfRange(request, 0, reqMarkers.get(0)[0]));
50+
modifiedRequest.write(this.helpers.stringToBytes("Ocp-Apim-Trace: true"));
51+
modifiedRequest.write(Arrays.copyOfRange(request, reqMarkers.get(0)[1], request.length));
52+
} catch (IOException e) {
53+
return issues;
54+
}
55+
56+
issues.addAll(sendActiveRequest(baseRequestResponse, modifiedRequest));
57+
return issues;
58+
}
59+
} else {
60+
//There was no header, try adding it
61+
IRequestInfo requestInfo = this.helpers.analyzeRequest(request);
62+
63+
ByteArrayOutputStream modifiedRequest = new ByteArrayOutputStream();
64+
try {
65+
modifiedRequest.write(Arrays.copyOfRange(request, 0, requestInfo.getBodyOffset() - 2));
66+
modifiedRequest.write(this.helpers.stringToBytes("Ocp-Apim-Trace: true\r\n\r\n"));
67+
modifiedRequest.write(Arrays.copyOfRange(request, requestInfo.getBodyOffset(), request.length));
68+
} catch (IOException e) {
69+
return issues;
70+
}
71+
72+
return sendActiveRequest(baseRequestResponse, modifiedRequest);
73+
}
74+
}
75+
76+
return issues.size() > 0 ? issues : null;
77+
}
78+
79+
private List<IScanIssue> sendActiveRequest(IHttpRequestResponse baseRequestResponse, ByteArrayOutputStream outputStream) {
80+
ArrayList<int[]> reqMarkersActive = new ArrayList<>();
81+
ArrayList<int[]> resMarkersActive = new ArrayList<>();
82+
83+
byte[] modifiedRequest = outputStream.toByteArray();
84+
IHttpRequestResponse checkRequestResponse = callbacks.makeHttpRequest(baseRequestResponse.getHttpService(), modifiedRequest);
85+
86+
reqMarkersActive.add(Utils.getRequestHeaderOffsets(modifiedRequest, this.helpers));
87+
resMarkersActive.add(Utils.getResponseHeaderOffsets(checkRequestResponse.getResponse(), this.helpers));
88+
IHttpRequestResponseWithMarkers marked = this.callbacks.applyMarkers(checkRequestResponse, reqMarkersActive, resMarkersActive);
89+
90+
if (resMarkersActive.get(0) != null) {
91+
List<IScanIssue> issues = new ArrayList<>();
92+
int start = resMarkersActive.get(0)[0] + Utils.responseHeaderSearchText.length();
93+
int end = resMarkersActive.get(0)[1];
94+
String url = this.helpers.bytesToString(Arrays.copyOfRange(checkRequestResponse.getResponse(), start, end));
95+
IHttpRequestResponseWithMarkers traceRequestResponse = this.callbacks.applyMarkers(Utils.sendRequest(url, this.callbacks), null, null);
96+
issues.add(new ScanIssue(helpers, marked, traceRequestResponse, url, ScanIssue.TraceIssueType.ResponseHeader));
97+
return issues;
98+
}
99+
return null;
100+
}
101+
102+
@Override
103+
public List<IScanIssue> doActiveScan(IHttpRequestResponse baseRequestResponse, IScannerInsertionPoint insertionPoint) {
104+
return null;
105+
}
106+
107+
@Override
108+
public int consolidateDuplicateIssues(IScanIssue existingIssue, IScanIssue newIssue) {
109+
return 0;
110+
}
111+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.optiv.azureapimanagementtracing;
2+
3+
import burp.IBurpExtenderCallbacks;
4+
import burp.IMessageEditorController;
5+
import burp.IMessageEditorTab;
6+
import burp.IMessageEditorTabFactory;
7+
8+
public class TraceViewTabFactory implements IMessageEditorTabFactory {
9+
private final IBurpExtenderCallbacks callbacks;
10+
11+
public TraceViewTabFactory(IBurpExtenderCallbacks callbacks) {
12+
this.callbacks = callbacks;
13+
}
14+
15+
@Override
16+
public IMessageEditorTab createNewInstance(IMessageEditorController controller, boolean editable) {
17+
return new TraceViewerTab(controller, editable, callbacks);
18+
}
19+
}

0 commit comments

Comments
 (0)