├── README.md
├── pom.xml
└── src
├── burp
└── BurpExtender.java
└── requestcleaner
├── BurpCleanerPanel.java
├── BurpCleanerPanel.jfd
├── CleanerPanel.java
├── ExtensionState.java
├── HttpRequestResponse.java
├── HttpService.java
├── ManualRequestSenderContextMenu.java
├── MessageEditorController.java
├── Parameter.java
└── TabButton.java
/README.md:
--------------------------------------------------------------------------------
1 | # BurpRequestCleaner
2 | This extension redacts potentially sensitive header and parameter values from requests using Shannon Entropy analysis.
3 |
4 | While on Twitter I saw Ben's [tweet](https://twitter.com/pry0cc/status/1343629699487039495) wanting Burp to have a "demo" mode that replaced the domain and potentially secret values so that it could be easily shareable. This is my attempt at solving that. It targets the Host header directly by replacing all but the root FQDN, resulting in transformations like so:
5 | - "foo.bar.baz.root.com" ---> "redacted.root.com"
6 | - "foo.root.com" ---> "redacted.root.com"
7 |
8 | For all other headers the extension check them against a list of known sensitive headers and if they match it redact them. Currently it checks for just 2:
9 | - Authorization
10 | - Proxy-Authorization
11 |
12 | This is by far the weakest part of the extension and something I'll give more thought to. Shannon Entropy fails us with headers because lots of headers have very high entropy but are obviously safe.
13 |
14 | For example, the `User-Agent` header shouldn't really ever be redacted but based on Shannon Entropy its a strong candidate for redaction.
15 |
16 | The best I can currently think of is letting the user decide which headers they want to ignore along with some likely safe suggestions.
17 |
18 | For parameters we use Shannon Entropy to determine if values in Cookies, JSON/XML fields, URL query string, body, or multi-part fields are likely sensitive content and should be redacted.
19 |
20 | Some features to add down the road:
21 | - Cleaning Responses
22 | - User cofiguration for headers to clean
23 | - User configuration for redaction string used to replace values
24 | - Other features I haven't thought of yet
25 | - Community Suggestions
26 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | staticflow
8 | BurpRequestCleaner
9 | 1.0-SNAPSHOT
10 |
11 |
12 |
13 | net.portswigger.burp.extender
14 | burp-extender-api
15 | 2.1
16 |
17 |
18 |
19 |
20 | ${project.basedir}/src
21 | BurpSuiteRequestCleaner
22 |
23 |
24 |
25 | org.apache.maven.plugins
26 | maven-eclipse-plugin
27 | 2.9
28 |
29 | true
30 | false
31 |
32 |
33 |
34 |
35 |
36 | org.apache.maven.plugins
37 | maven-compiler-plugin
38 | 2.3.2
39 |
40 | 1.8
41 | 1.8
42 |
43 |
44 |
45 |
46 |
47 | org.apache.maven.plugins
48 | maven-assembly-plugin
49 | 2.4.1
50 |
51 |
52 | false
53 |
54 | jar-with-dependencies
55 |
56 |
57 |
58 |
59 |
60 | make-assembly
61 |
62 | package
63 |
64 | single
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/src/burp/BurpExtender.java:
--------------------------------------------------------------------------------
1 | package burp;
2 |
3 |
4 | import requestcleaner.BurpCleanerPanel;
5 | import requestcleaner.ExtensionState;
6 | import requestcleaner.ManualRequestSenderContextMenu;
7 |
8 | import java.awt.*;
9 |
10 | /*
11 | This extension lets you send requests to a cleaning operation which uses
12 | Shannon Entropy to detect potentially sensitive headers or parameter values
13 | and redacts them. Made because of this tweet https://twitter.com/pry0cc/status/1343629699487039495
14 | */
15 | public class BurpExtender implements IBurpExtender, ITab {
16 | private IBurpExtenderCallbacks callbacks;
17 |
18 | public void registerExtenderCallbacks(IBurpExtenderCallbacks iBurpExtenderCallbacks) {
19 | callbacks = iBurpExtenderCallbacks;
20 | ExtensionState.setCallbacks(iBurpExtenderCallbacks);
21 | callbacks.setExtensionName("Burp Suite Request Cleaner");
22 | iBurpExtenderCallbacks.registerContextMenuFactory(new ManualRequestSenderContextMenu());
23 | iBurpExtenderCallbacks.addSuiteTab(this);
24 |
25 |
26 | }
27 |
28 | public String getTabCaption() {
29 | return "Request Cleaner";
30 | }
31 |
32 | public Component getUiComponent() {
33 | BurpCleanerPanel panel = ExtensionState.getInstance().getCleanerPanel();
34 | callbacks.customizeUiComponent(panel);
35 | return panel;
36 | }
37 |
38 | }
--------------------------------------------------------------------------------
/src/requestcleaner/BurpCleanerPanel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Created by JFormDesigner on Mon Dec 28 22:28:33 CST 2020
3 | */
4 |
5 | package requestcleaner;
6 |
7 | import java.awt.*;
8 | import java.awt.event.*;
9 | import javax.swing.*;
10 |
11 | /*
12 | This is the GUI for the extension. Lots of this is auto-generated
13 | */
14 | public class BurpCleanerPanel extends JPanel {
15 | public BurpCleanerPanel() {
16 | initComponents();
17 | }
18 |
19 | //This is the only custom bit, we create out tab and update the state
20 | public void addTab(JPanel cleanerTab) {
21 | String tabTitle = String.valueOf(ExtensionState.getInstance().getNumberOfCleanedRequests());
22 | this.cleanerTabPane.addTab(tabTitle,cleanerTab);
23 | this.cleanerTabPane.setTabComponentAt(ExtensionState.getInstance().getNumberOfCleanedRequests(),new TabButton(this.cleanerTabPane));
24 | ExtensionState.getInstance().setNumberOfCleanedRequests(ExtensionState.getInstance().getNumberOfCleanedRequests()+1);
25 | }
26 |
27 | private void parameterSetLevelActionPerformed(ActionEvent e) {
28 | ExtensionState.getInstance().setParameterEntropyLevel(Integer.parseInt(parameterLevelText.getText()));
29 | }
30 |
31 | private void initComponents() {
32 | // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
33 | // Generated using JFormDesigner Evaluation license - unknown
34 | cleanerTabPane = new JTabbedPane();
35 | panel1 = new JPanel();
36 | label2 = new JLabel();
37 | parameterLevelText = new JTextField();
38 | parameterSetLevel = new JButton();
39 |
40 | //======== this ========
41 | setBorder(new javax.swing.border.CompoundBorder(new javax.swing.border.TitledBorder(new javax.swing.border.EmptyBorder(
42 | 0,0,0,0), "JF\u006frm\u0044es\u0069gn\u0065r \u0045va\u006cua\u0074io\u006e",javax.swing.border.TitledBorder.CENTER,javax.swing.border.TitledBorder
43 | .BOTTOM,new java.awt.Font("D\u0069al\u006fg",java.awt.Font.BOLD,12),java.awt.Color.
44 | red), getBorder())); addPropertyChangeListener(new java.beans.PropertyChangeListener(){@Override public void propertyChange(java.
45 | beans.PropertyChangeEvent e){if("\u0062or\u0064er".equals(e.getPropertyName()))throw new RuntimeException();}});
46 | setLayout(new BorderLayout());
47 | add(cleanerTabPane, BorderLayout.CENTER);
48 |
49 | //======== panel1 ========
50 | {
51 | panel1.setLayout(new GridLayout(1, 3));
52 |
53 | //---- label2 ----
54 | label2.setText("Parameter Entropy Level");
55 | panel1.add(label2);
56 | panel1.add(parameterLevelText);
57 |
58 | //---- parameterSetLevel ----
59 | parameterSetLevel.setText("Set");
60 | parameterSetLevel.addActionListener(e -> parameterSetLevelActionPerformed(e));
61 | panel1.add(parameterSetLevel);
62 | }
63 | add(panel1, BorderLayout.NORTH);
64 | // JFormDesigner - End of component initialization //GEN-END:initComponents
65 | }
66 |
67 | // JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
68 | // Generated using JFormDesigner Evaluation license - unknown
69 | private JTabbedPane cleanerTabPane;
70 | private JPanel panel1;
71 | private JLabel label2;
72 | private JTextField parameterLevelText;
73 | private JButton parameterSetLevel;
74 | // JFormDesigner - End of variables declaration //GEN-END:variables
75 | }
76 |
--------------------------------------------------------------------------------
/src/requestcleaner/BurpCleanerPanel.jfd:
--------------------------------------------------------------------------------
1 | JFDML JFormDesigner: "7.0.3.0.337" Java: "11.0.8" encoding: "UTF-8"
2 |
3 | new FormModel {
4 | contentType: "form/swing"
5 | root: new FormRoot {
6 | add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
7 | name: "this"
8 | add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
9 | name: "cleanerTabPane"
10 | }, new FormLayoutConstraints( class java.lang.String ) {
11 | "value": "Center"
12 | } )
13 | add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.GridLayout ) {
14 | "columns": 3
15 | } ) {
16 | name: "panel1"
17 | add( new FormComponent( "javax.swing.JLabel" ) {
18 | name: "label2"
19 | "text": "Parameter Entropy Level"
20 | } )
21 | add( new FormComponent( "javax.swing.JTextField" ) {
22 | name: "parameterLevelText"
23 | } )
24 | add( new FormComponent( "javax.swing.JButton" ) {
25 | name: "parameterSetLevel"
26 | "text": "Set"
27 | addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "parameterSetLevelActionPerformed", true ) )
28 | } )
29 | }, new FormLayoutConstraints( class java.lang.String ) {
30 | "value": "North"
31 | } )
32 | }, new FormLayoutConstraints( null ) {
33 | "location": new java.awt.Point( 0, 0 )
34 | "size": new java.awt.Dimension( 400, 300 )
35 | } )
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/requestcleaner/CleanerPanel.java:
--------------------------------------------------------------------------------
1 | package requestcleaner;
2 |
3 | import burp.IMessageEditor;
4 | import burp.IParameter;
5 | import burp.IRequestInfo;
6 |
7 | import javax.swing.*;
8 | import java.awt.*;
9 | import java.util.ArrayList;
10 | import java.util.Arrays;
11 |
12 | /*
13 | This panel is the inner view of each cleaning tab. It shows the original request
14 | alongside the cleaned one
15 | */
16 | public class CleanerPanel extends JPanel {
17 |
18 | /*
19 | This may and should change. Finding a definitive list of headers to clean
20 | is tough so we should probably find a better way. Shannon Entropy doesn't
21 | work well for headers because examples like this:
22 |
23 | Accept-Datetime: Thu, 31 May 2007 20:35:00 GMT
24 |
25 | Have high Shannon Entropy but obviously shouldn't be redacted.
26 | */
27 | String[] CLEAN_HEADERS = new String[]{"Authorization","Proxy" +
28 | "-Authorization"};
29 |
30 | public CleanerPanel(HttpRequestResponse requestResponseToClean) {
31 | this.setLayout(new BorderLayout());
32 | JTabbedPane reqRespTabbedPane = new JTabbedPane();
33 | IMessageEditor requestMessageToDisplay =
34 | ExtensionState.getCallbacks().createMessageEditor(
35 | new MessageEditorController(
36 | requestResponseToClean.getHttpService(),
37 | requestResponseToClean.getRequest(),
38 | requestResponseToClean.getResponse()),
39 | false);
40 | requestMessageToDisplay.setMessage(requestResponseToClean.getRequest(),
41 | true);
42 |
43 | cleanRequest(requestResponseToClean);
44 |
45 |
46 | ExtensionState.getCallbacks().printOutput(new String(requestResponseToClean.getCleanedRequest()));
47 | IMessageEditor requestMessageToDisplayCleaned =
48 | ExtensionState.getCallbacks().createMessageEditor(
49 | new MessageEditorController(
50 | requestResponseToClean.getHttpService(),
51 | requestResponseToClean.getCleanedRequest(),
52 | requestResponseToClean.getResponse()),
53 | true);
54 | requestMessageToDisplayCleaned.setMessage(requestResponseToClean.getCleanedRequest(),
55 | true);
56 |
57 | reqRespTabbedPane.addTab("Original",
58 | requestMessageToDisplay.getComponent());
59 | reqRespTabbedPane.addTab("Cleaned",
60 | requestMessageToDisplayCleaned.getComponent());
61 | this.add(reqRespTabbedPane);
62 | }
63 |
64 | //Cleans the Host header by removing all but the root FDQN
65 | // foo.bar.root.com ---> redacted.root.com
66 | // foo.root.com ----> redacted.root.com
67 | private String cleanHostHeader(String header) {
68 | String[] hostPieces = header.split(":")[1].split("\\.");
69 | if(hostPieces.length > 2) {
70 | return "Host: "+("redacted."+hostPieces[hostPieces.length-2]+"."+hostPieces[hostPieces.length-1]);
71 | } else {
72 | return "Host: "+("redacted."+hostPieces[hostPieces.length-1]);
73 | }
74 | }
75 |
76 |
77 | //Checks if a header should be redacted
78 | private boolean shouldCleanHeader(String header) {
79 | for(String skipHeader : CLEAN_HEADERS) {
80 | if(header.contains(skipHeader)){
81 | return true;
82 | }
83 | }
84 | return false;
85 | }
86 |
87 | //cleans headers that meet the configured Shannon Entropy level
88 | private void cleanHeaders(HttpRequestResponse requestResponseToClean) {
89 | IRequestInfo rinfo =
90 | ExtensionState.getCallbacks().getHelpers().analyzeRequest(requestResponseToClean.getRequest());
91 | ArrayList rInfoHeaders = (ArrayList) rinfo.getHeaders();
92 | ExtensionState.getCallbacks().printOutput("Cleaning headers");
93 |
94 | for(int i = 0;i 1 && shouldCleanHeader(headerPieces[0])) {
104 | String headerValue = headerPieces[1];
105 | ExtensionState.getCallbacks().printOutput(headerValue);
106 | rInfoHeaders.set(i, rInfoHeaders.get(i).split(":")[0] +
107 | ": Redacted");
108 | ExtensionState.getCallbacks().printOutput(rInfoHeaders.get(i));
109 | }
110 |
111 | }
112 | }
113 | requestResponseToClean.setCleanedRequest(ExtensionState.getCallbacks().getHelpers().buildHttpMessage(rInfoHeaders,
114 | Arrays.copyOfRange(requestResponseToClean.getRequest(), rinfo.getBodyOffset(), requestResponseToClean.getRequest().length)));
115 | }
116 |
117 | //cleans parameters that meet the configured Shannon Entropy level
118 | private void cleanParameters(HttpRequestResponse requestResponseToClean) {
119 | IRequestInfo rinfo =
120 | ExtensionState.getCallbacks().getHelpers().analyzeRequest(requestResponseToClean.getRequest());
121 | ArrayList rParams = (ArrayList) rinfo.getParameters();
122 |
123 | for (IParameter rParam : rParams) {
124 | ExtensionState.getCallbacks().printOutput(rParam.getValue());
125 | double entropy =
126 | ExtensionState.getShannonEntropy(rParam.getValue());
127 | ExtensionState.getCallbacks().printOutput("Entropy: " + entropy);
128 | if (entropy > ExtensionState.getInstance().getParameterEntropyLevel()) {
129 | Parameter parameter = new Parameter(rParam);
130 | parameter.setValue("redacted");
131 | requestResponseToClean.setCleanedRequest(ExtensionState.getCallbacks().getHelpers().updateParameter(requestResponseToClean.getCleanedRequest(), parameter));
132 | }
133 | }
134 | }
135 |
136 | //main entry to cleaning a request
137 | private void cleanRequest(HttpRequestResponse requestResponseToClean) {
138 | ExtensionState.getCallbacks().printOutput("Cleaning request");
139 | requestResponseToClean.setCleanedRequest(requestResponseToClean.getRequest());
140 | cleanHeaders(requestResponseToClean);
141 | cleanParameters(requestResponseToClean);
142 | ExtensionState.getCallbacks().printOutput(new String(requestResponseToClean.getCleanedRequest()));
143 | }
144 |
145 | }
146 |
--------------------------------------------------------------------------------
/src/requestcleaner/ExtensionState.java:
--------------------------------------------------------------------------------
1 | package requestcleaner;
2 |
3 | import burp.IBurpExtenderCallbacks;
4 |
5 | import java.util.HashMap;
6 | import java.util.Map;
7 |
8 |
9 | /*
10 | This stores our state for the extension as a Singleton
11 | */
12 | public class ExtensionState {
13 |
14 | //State object
15 | private static ExtensionState instance = null;
16 | //Burp callbacks
17 | private IBurpExtenderCallbacks callbacks;
18 | //Number of current cleaned requests
19 | private int numberOfCleanedRequests;
20 | //User defined Shannon Entropy level for headers
21 | private int headerEntropyLevel;
22 | //User defined Shannon Entropy level for parameters
23 | private int parameterEntropyLevel;
24 | //Reference to the UI panel
25 | private final BurpCleanerPanel teamPanel;
26 |
27 |
28 | private ExtensionState() {
29 | headerEntropyLevel = 3;
30 | parameterEntropyLevel = 3;
31 | numberOfCleanedRequests = 0;
32 | teamPanel = new BurpCleanerPanel();
33 | }
34 |
35 | public static void setCallbacks(IBurpExtenderCallbacks callbacks) {
36 | getInstance().callbacks = callbacks;
37 | }
38 |
39 | public static IBurpExtenderCallbacks getCallbacks() {
40 | return getInstance().callbacks;
41 | }
42 |
43 | public BurpCleanerPanel getCleanerPanel() {
44 | return getInstance().teamPanel;
45 | }
46 |
47 | private static double log2(double a) {
48 | return Math.log(a) / Math.log(2);
49 | }
50 | //Shannon Entropy taken from here: https://rosettacode.org/wiki/Entropy#Java
51 | public static double getShannonEntropy(String s) {
52 | int n = 0;
53 | Map occ = new HashMap<>();
54 |
55 | for (int c_ = 0; c_ < s.length(); ++c_) {
56 | char cx = s.charAt(c_);
57 | if (occ.containsKey(cx)) {
58 | occ.put(cx, occ.get(cx) + 1);
59 | } else {
60 | occ.put(cx, 1);
61 | }
62 | ++n;
63 | }
64 |
65 | double e = 0.0;
66 | for (Map.Entry entry : occ.entrySet()) {
67 | double p = (double) entry.getValue() / n;
68 | e += p * log2(p);
69 | }
70 | return -e;
71 | }
72 |
73 | public static ExtensionState getInstance() {
74 | if(instance==null) {
75 | instance = new ExtensionState();
76 | }
77 | return instance;
78 | }
79 |
80 | public int getNumberOfCleanedRequests() {
81 | return numberOfCleanedRequests;
82 | }
83 |
84 | public void setNumberOfCleanedRequests(int numberOfCleanedRequests) {
85 | this.numberOfCleanedRequests = numberOfCleanedRequests;
86 | }
87 |
88 | public int getHeaderEntropyLevel() {
89 | return headerEntropyLevel;
90 | }
91 |
92 | public void setHeaderEntropyLevel(int entropyLevel) {
93 | this.headerEntropyLevel = entropyLevel;
94 | }
95 |
96 | public int getParameterEntropyLevel() {
97 | return parameterEntropyLevel;
98 | }
99 |
100 | public void setParameterEntropyLevel(int entropyLevel) {
101 | this.parameterEntropyLevel = entropyLevel;
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/requestcleaner/HttpRequestResponse.java:
--------------------------------------------------------------------------------
1 | package requestcleaner;
2 |
3 | import burp.IHttpRequestResponse;
4 | import burp.IHttpService;
5 |
6 | import java.util.Arrays;
7 |
8 | public class HttpRequestResponse implements IHttpRequestResponse
9 | {
10 |
11 | private byte[] request;
12 | private byte[] cleanedRequest;
13 | private byte[] response;
14 | private String comment;
15 | private String highlight;
16 | private HttpService httpService;
17 |
18 | public HttpRequestResponse() {
19 | }
20 |
21 | public HttpRequestResponse(IHttpRequestResponse copy) {
22 | this.request = copy.getRequest();
23 | this.response = copy.getResponse();
24 | this.comment = copy.getComment();
25 | this.highlight = copy.getHighlight();
26 | this.httpService = new HttpService(copy.getHttpService());
27 | }
28 |
29 | @Override
30 | public byte[] getRequest() {
31 | if(request == null) {
32 | return new byte[]{};
33 | }
34 | return request;
35 | }
36 |
37 | @Override
38 | public void setRequest(byte[] message) {
39 | request = message;
40 | }
41 |
42 | @Override
43 | public byte[] getResponse() {
44 | if(response == null) {
45 | return new byte[]{};
46 | }
47 | return response;
48 | }
49 |
50 | @Override
51 | public void setResponse(byte[] message) {
52 | response = message;
53 | }
54 |
55 | @Override
56 | public String getComment() {
57 | return comment;
58 | }
59 |
60 | @Override
61 | public void setComment(String comment) {
62 | this.comment = comment;
63 | }
64 |
65 | @Override
66 | public String getHighlight() {
67 | return highlight;
68 | }
69 |
70 | @Override
71 | public void setHighlight(String color) {
72 | this.highlight = color;
73 | }
74 |
75 | @Override
76 | public IHttpService getHttpService() {
77 | return httpService;
78 | }
79 |
80 | @Override
81 | public void setHttpService(IHttpService httpService) {
82 | this.httpService = new HttpService(httpService);
83 | }
84 |
85 | @Override
86 | public String toString() {
87 | return "HttpRequestResponse{" +
88 | "request=" + Arrays.toString(request) +
89 | ", response=" + Arrays.toString(response) +
90 | ", comment='" + comment + '\'' +
91 | ", highlight='" + highlight + '\'' +
92 | ", httpService=" + httpService +
93 | '}';
94 | }
95 |
96 |
97 | @Override
98 | public boolean equals(Object o) {
99 | if (this == o) return true;
100 | if (o == null || getClass() != o.getClass()) return false;
101 | HttpRequestResponse that = (HttpRequestResponse) o;
102 | return Arrays.equals(request, that.request) &&
103 | Arrays.equals(response, that.response);
104 | }
105 |
106 | @Override
107 | public int hashCode() {
108 | int result = Arrays.hashCode(request);
109 | result = 31 * result + Arrays.hashCode(response);
110 | return result;
111 | }
112 |
113 | public byte[] getCleanedRequest() {
114 | return cleanedRequest;
115 | }
116 |
117 | public void setCleanedRequest(byte[] cleanedRequest) {
118 | this.cleanedRequest = cleanedRequest;
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/requestcleaner/HttpService.java:
--------------------------------------------------------------------------------
1 | package requestcleaner;
2 |
3 | import burp.IHttpService;
4 |
5 | public class HttpService implements IHttpService
6 | {
7 | private final String host;
8 | private final int port;
9 | private final String protocol;
10 |
11 | HttpService(IHttpService copy) {
12 | this.host = copy.getHost();
13 | this.port = copy.getPort();
14 | this.protocol = copy.getProtocol();
15 | }
16 |
17 | @Override
18 | public String getHost() {
19 | if(host == null){
20 | return "";
21 | }
22 | return host;
23 | }
24 |
25 | @Override
26 | public int getPort() {
27 | return port;
28 | }
29 |
30 | @Override
31 | public String getProtocol() {
32 | if(protocol == null){
33 | return "";
34 | }
35 | return protocol;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/requestcleaner/ManualRequestSenderContextMenu.java:
--------------------------------------------------------------------------------
1 | package requestcleaner;
2 |
3 | import burp.IContextMenuFactory;
4 | import burp.IContextMenuInvocation;
5 | import burp.IHttpRequestResponse;
6 |
7 | import javax.swing.*;
8 | import java.util.ArrayList;
9 | import java.util.Collection;
10 | import java.util.List;
11 | import java.util.Objects;
12 |
13 | /*
14 | This handles our context menu for sending requests to the cleaner
15 | */
16 | public class ManualRequestSenderContextMenu implements IContextMenuFactory {
17 |
18 |
19 | @Override
20 | public List createMenuItems(IContextMenuInvocation invocation) {
21 | ArrayList menues = new ArrayList<>();
22 | if (Objects.equals(IContextMenuInvocation.CONTEXT_MESSAGE_EDITOR_REQUEST , invocation.getInvocationContext())||
23 | Objects.equals(IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_REQUEST, invocation.getInvocationContext()) ||
24 | Objects.equals(IContextMenuInvocation.CONTEXT_TARGET_SITE_MAP_TREE, invocation.getInvocationContext()) ||
25 | Objects.equals(IContextMenuInvocation.CONTEXT_TARGET_SITE_MAP_TABLE, invocation.getInvocationContext())) {
26 | menues.addAll(createCleaningMenu(invocation));
27 | }
28 | return menues;
29 | }
30 |
31 | private Collection extends JMenuItem> createCleaningMenu(IContextMenuInvocation invocation) {
32 | JMenuItem sendToCleaner = new JMenuItem("Send to cleaner");
33 | sendToCleaner.addActionListener(e ->
34 | sendRequestToCleaner(invocation));
35 |
36 | ArrayList menuList = new ArrayList<>();
37 | menuList.add(sendToCleaner);
38 | return menuList;
39 | }
40 |
41 |
42 | private void sendRequestToCleaner(IContextMenuInvocation invocation) {
43 | HttpRequestResponse httpRequestResponse =
44 | new HttpRequestResponse();
45 | for (IHttpRequestResponse message : invocation.getSelectedMessages()) {
46 | new SwingWorker() {
47 | @Override
48 | public Boolean doInBackground() {
49 | httpRequestResponse.setRequest(message.getRequest());
50 | httpRequestResponse.setHttpService(message.getHttpService());
51 | ExtensionState.getInstance().getCleanerPanel().addTab(new CleanerPanel(httpRequestResponse));
52 | return Boolean.TRUE;
53 | }
54 |
55 | @Override
56 | public void done() {
57 | //we don't need to do any cleanup so this is empty
58 | }
59 | }.execute();
60 | }
61 |
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/requestcleaner/MessageEditorController.java:
--------------------------------------------------------------------------------
1 | package requestcleaner;
2 |
3 | import burp.IHttpService;
4 | import burp.IMessageEditorController;
5 |
6 | public class MessageEditorController implements IMessageEditorController {
7 |
8 | private IHttpService httpService;
9 | private byte[] request;
10 | private byte[] response;
11 |
12 | MessageEditorController(IHttpService httpService, byte[] request, byte[] response) {
13 | this.httpService = httpService;
14 | this.request = request;
15 | this.response = response;
16 | }
17 |
18 |
19 | @Override
20 | public IHttpService getHttpService() {
21 | return this.httpService;
22 | }
23 |
24 | @Override
25 | public byte[] getRequest() {
26 | return this.request;
27 | }
28 |
29 | @Override
30 | public byte[] getResponse() {
31 | return this.response;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/requestcleaner/Parameter.java:
--------------------------------------------------------------------------------
1 | package requestcleaner;
2 |
3 | import burp.IParameter;
4 |
5 | public class Parameter implements IParameter {
6 |
7 | private byte type;
8 | private String name;
9 | private String value;
10 | private int nameStart;
11 | private int nameEnd;
12 | private int valueStart;
13 | private int valueEnd;
14 |
15 | public Parameter(IParameter parameter) {
16 | this.type = parameter.getType();
17 | this.name = parameter.getName();
18 | this.value = parameter.getValue();
19 | this.nameStart = parameter.getNameStart();
20 | this.nameEnd = parameter.getNameEnd();
21 | this.valueStart = parameter.getValueStart();
22 | this.valueEnd = parameter.getValueEnd();
23 | }
24 |
25 | @Override
26 | public byte getType() {
27 | return this.type;
28 | }
29 |
30 | @Override
31 | public String getName() {
32 | return this.name;
33 | }
34 |
35 | @Override
36 | public String getValue() {
37 | return this.value;
38 | }
39 |
40 | public void setValue(String value) {
41 | this.value = value;
42 | }
43 |
44 | @Override
45 | public int getNameStart() {
46 | return this.nameStart;
47 | }
48 |
49 | @Override
50 | public int getNameEnd() {
51 | return this.nameEnd;
52 | }
53 |
54 | @Override
55 | public int getValueStart() {
56 | return this.valueStart;
57 | }
58 |
59 | @Override
60 | public int getValueEnd() {
61 | return this.valueEnd;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/requestcleaner/TabButton.java:
--------------------------------------------------------------------------------
1 | package requestcleaner;
2 |
3 | import javax.swing.*;
4 | import javax.swing.plaf.basic.BasicButtonUI;
5 | import java.awt.*;
6 | import java.awt.event.*;
7 |
8 | /*
9 | This class lets us have those fancy x's on tab buttons. Taken from here
10 | https://docs.oracle.com/javase/tutorial/uiswing/components/tabbedpane.html#appearanceapi
11 | */
12 | public class TabButton extends JPanel {
13 | private final JTabbedPane pane;
14 |
15 | public TabButton(final JTabbedPane pane) {
16 | //unset default FlowLayout' gaps
17 | super(new FlowLayout(FlowLayout.LEFT, 0, 0));
18 | if (pane == null) {
19 | throw new NullPointerException("TabbedPane is null");
20 | }
21 | this.pane = pane;
22 | setOpaque(false);
23 |
24 | //make JLabel read titles from JTabbedPane
25 | JLabel label = new JLabel() {
26 | public String getText() {
27 | int i = pane.indexOfTabComponent(TabButton.this);
28 | if (i != -1) {
29 | return pane.getTitleAt(i);
30 | }
31 | return null;
32 | }
33 | };
34 |
35 | add(label);
36 | //add more space between the label and the button
37 | label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
38 | //tab button
39 | JButton button = new Button();
40 | add(button);
41 | //add more space to the top of the component
42 | setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));
43 | }
44 |
45 | private class Button extends JButton implements ActionListener {
46 | public Button() {
47 | int size = 17;
48 | setPreferredSize(new Dimension(size, size));
49 | setToolTipText("close this tab");
50 | //Make the button looks the same for all Laf's
51 | setUI(new BasicButtonUI());
52 | //Make it transparent
53 | setContentAreaFilled(false);
54 | //No need to be focusable
55 | setFocusable(false);
56 | setBorder(BorderFactory.createEtchedBorder());
57 | setBorderPainted(false);
58 | //Making nice rollover effect
59 | //we use the same listener for all buttons
60 | addMouseListener(buttonMouseListener);
61 | setRolloverEnabled(true);
62 | //Close the proper tab by clicking the button
63 | addActionListener(this);
64 | }
65 |
66 | public void actionPerformed(ActionEvent e) {
67 | int i = pane.indexOfTabComponent(TabButton.this);
68 | if (i != -1) {
69 | pane.remove(i);
70 | }
71 | ExtensionState.getInstance().setNumberOfCleanedRequests(ExtensionState.getInstance().getNumberOfCleanedRequests()-1);
72 | }
73 |
74 | //we don't want to update UI for this button
75 | public void updateUI() {
76 | }
77 |
78 | //paint the cross
79 | protected void paintComponent(Graphics g) {
80 | super.paintComponent(g);
81 | Graphics2D g2 = (Graphics2D) g.create();
82 | //shift the image for pressed buttons
83 | if (getModel().isPressed()) {
84 | g2.translate(1, 1);
85 | }
86 | g2.setStroke(new BasicStroke(2));
87 | g2.setColor(Color.BLACK);
88 | if (getModel().isRollover()) {
89 | g2.setColor(Color.MAGENTA);
90 | }
91 | int delta = 6;
92 | g2.drawLine(delta, delta, getWidth() - delta - 1, getHeight() - delta - 1);
93 | g2.drawLine(getWidth() - delta - 1, delta, delta, getHeight() - delta - 1);
94 | g2.dispose();
95 | }
96 | }
97 |
98 | private final static MouseListener buttonMouseListener = new MouseAdapter() {
99 | public void mouseEntered(MouseEvent e) {
100 | Component component = e.getComponent();
101 | if (component instanceof AbstractButton) {
102 | AbstractButton button = (AbstractButton) component;
103 | button.setBorderPainted(true);
104 | }
105 | }
106 |
107 | public void mouseExited(MouseEvent e) {
108 | Component component = e.getComponent();
109 | if (component instanceof AbstractButton) {
110 | AbstractButton button = (AbstractButton) component;
111 | button.setBorderPainted(false);
112 | }
113 | }
114 | };
115 | }
--------------------------------------------------------------------------------