diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..2eac001 --- /dev/null +++ b/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/.externalToolBuilders/MakeHTTPSmugglerJAR.launch b/.externalToolBuilders/MakeHTTPSmugglerJAR.launch new file mode 100644 index 0000000..6e45db7 --- /dev/null +++ b/.externalToolBuilders/MakeHTTPSmugglerJAR.launch @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..031206a --- /dev/null +++ b/.project @@ -0,0 +1,27 @@ + + + HTTP Smuggler + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.ui.externaltools.ExternalToolBuilder + auto,full,incremental, + + + LaunchConfigHandle + <project>/.externalToolBuilders/MakeHTTPSmugglerJAR.launch + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..417bbde --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding//src/mutation/HttpEncoding.java=UTF-8 diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..3a21537 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..6b30308 --- /dev/null +++ b/build.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/burp/BurpExtender.java b/src/burp/BurpExtender.java new file mode 100644 index 0000000..560ba37 --- /dev/null +++ b/src/burp/BurpExtender.java @@ -0,0 +1,262 @@ +/* + * Burp Suite HTTP Smuggler + * + * Released as open source by NCC Group - https://www.nccgroup.trust/ + * + * Developed by: + * Soroush Dalili (@irsdl) + * + * Project link: https://github.com/nccgroup/BurpSuiteHTTPSmuggler/ + * + * Released under AGPL v3.0 see LICENSE for more information + * + * */ + +package burp; + +import java.awt.Component; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.net.URL; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import javax.swing.SwingUtilities; + +import mutation.HTTPEncodingObject; + +public class BurpExtender implements IBurpExtender, ITab, IHttpListener +{ + + private PrintWriter _stdout; + private PrintWriter _stderr; + private JTabbedPane _topTabs; + private IBurpExtenderCallbacks _callbacks; + private JTabbedPane topTabs; + + public void registerExtenderCallbacks (IBurpExtenderCallbacks callbacks) + { + _callbacks = callbacks; + // obtain our output stream + _stdout = new PrintWriter(_callbacks.getStdout(), true); + _stderr = new PrintWriter(_callbacks.getStderr(), true); + + // set our extension name + _callbacks.setExtensionName("HTTP Smuggler"); + + // register ourselves as an HTTP listener + callbacks.registerHttpListener(BurpExtender.this); + + // create our UI + SwingUtilities.invokeLater(new Runnable() + { + @Override + public void run() + { + topTabs = new JTabbedPane(); + + topTabs.addTab("Scope", null, new myui.ScopeTab(callbacks, _stdout, _stderr), null); + topTabs.addTab("Encoding", null, new myui.EncodingTab(callbacks, _stdout, _stderr), null); + topTabs.addTab("About", null, new myui.AboutTab(callbacks, _stdout, _stderr), null); + + // customize our UI components + callbacks.customizeUiComponent(topTabs); + helper.UIStuff.updateJCheckBoxBackground(topTabs); + + // add the custom tab to Burp's UI + callbacks.addSuiteTab(BurpExtender.this); + } + }); + + } + + @Override + public String getTabCaption() + { + return "HTTP Smuggler Settings"; + } + + @Override + public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) { + if(messageIsRequest) { + /* to calculate the scope, OR has not been implemented yet*/ + boolean isDisabled = false; + boolean isInScope = false; + boolean isTargetInScope = true; + boolean isURLPathInScope = true; + boolean isHeaderInScope = true; + + IRequestInfo analyzedReq = _callbacks.getHelpers().analyzeRequest(messageInfo); + URL uUrl = analyzedReq.getUrl(); + /* find the right scope based on the settings*/ + int targetScopeOption =(int) loadExtensionSettingHelper("targetScopeOption","int",0); + int pathRegExOption =(int) loadExtensionSettingHelper("pathRegExOption","int",0); + String pathRegEx =(String) loadExtensionSettingHelper("pathRegEx","string",""); + int headerRegExOption =(int) loadExtensionSettingHelper("headerRegExOption","int",0); + String headerRegEx =(String) loadExtensionSettingHelper("headerRegEx","string",""); + + boolean chckbxAllTools =(boolean) loadExtensionSettingHelper("chckbxAllTools","bool",false); + boolean chckbxProxy =(boolean) loadExtensionSettingHelper("chckbxProxy","bool",false); + boolean chckbxScanner =(boolean) loadExtensionSettingHelper("chckbxScanner","bool",false); + boolean chckbxIntruder =(boolean) loadExtensionSettingHelper("chckbxIntruder","bool",false); + boolean chckbxRepeator =(boolean) loadExtensionSettingHelper("chckbxRepeator","bool",true); + boolean chckbxExtender =(boolean) loadExtensionSettingHelper("chckbxExtender","bool",false); + boolean chckbxTarget =(boolean) loadExtensionSettingHelper("chckbxTarget","bool",false); + boolean chckbxSequencer =(boolean) loadExtensionSettingHelper("chckbxSequencer","bool",false); + boolean chckbxSpider =(boolean) loadExtensionSettingHelper("chckbxSpider","bool",false); + + if(targetScopeOption==1 && pathRegExOption==1 && headerRegExOption==1) { + //evrything is disabled + isDisabled = true; + } + + if(!isDisabled) { + if(targetScopeOption < 1 && !_callbacks.isInScope(uUrl)) { + isTargetInScope = false; + } + + + if(isTargetInScope && pathRegExOption < 1 && !pathRegEx.isEmpty()){ + // AND rule for path/url regex + Pattern pathPattern = Pattern.compile(pathRegEx); + Matcher matcher_pathURL = pathPattern.matcher(uUrl.toString()); + if (!matcher_pathURL.find()) + { + isURLPathInScope = false; + } + } + + if(isTargetInScope && isURLPathInScope && headerRegExOption < 1 && !headerRegEx.isEmpty()){ + // AND rule for header regex + Pattern headerPattern = Pattern.compile(headerRegEx); + + StringBuilder sb = new StringBuilder(); + for (String headerLine : analyzedReq.getHeaders()) + { + sb.append(headerLine); + sb.append("\r\n"); + } + Matcher matcher_header = headerPattern.matcher(sb.toString()); + if (!matcher_header.find()) + { + isHeaderInScope = false; + } + } + + + + if (isTargetInScope && isURLPathInScope && isHeaderInScope){ + // check the tool! + if(chckbxAllTools){ + isInScope = true; + }else if(chckbxProxy && toolFlag==_callbacks.TOOL_PROXY){ + isInScope = true; + }else if(chckbxIntruder && toolFlag==_callbacks.TOOL_INTRUDER){ + isInScope = true; + }else if(chckbxRepeator && toolFlag==_callbacks.TOOL_REPEATER){ + isInScope = true; + }else if(chckbxScanner && toolFlag==_callbacks.TOOL_SCANNER){ + isInScope = true; + }else if(chckbxSequencer && toolFlag==_callbacks.TOOL_SEQUENCER){ + isInScope = true; + }else if(chckbxSpider && toolFlag==_callbacks.TOOL_SPIDER){ + isInScope = true; + }else if(chckbxExtender && toolFlag==_callbacks.TOOL_EXTENDER){ + isInScope = true; + }else if(chckbxTarget && toolFlag==_callbacks.TOOL_TARGET){ + isInScope = true; + } + } + + + if (isInScope){ + //logIt(toolFlag, messageIsRequest, messageInfo, null); + mutation.HttpEncoding httpEcnoding = new mutation.HttpEncoding(_callbacks,_stdout,_stderr,true); + try { + String newHTTPMessage = httpEcnoding.encodeHTTPMessage(messageInfo.getRequest(), loadHTTPEncodingObjectFromExtensionSetting()); + if(newHTTPMessage.isEmpty()) { + _stdout.println("Message was not encoded - perhaps it was not eligible or there was an error (see the error tab)"); + _stdout.println("Enable the debug mode to see more details"); + }else { + byte[] requestByte = newHTTPMessage.getBytes("ISO-8859-1"); + messageInfo.setRequest(requestByte); + } + } catch (UnsupportedEncodingException e) { + _stderr.println(e.getMessage()); + } + } + } + } + } + + @Override + public Component getUiComponent() { + return topTabs; + } + + private Object loadExtensionSettingHelper(String name, String type, Object defaultValue) { + Object value = null; + try { + String temp_value = _callbacks.loadExtensionSetting(name); + if(temp_value!=null && !temp_value.equals("")) { + switch(type.toLowerCase()){ + case "int": + case "integer": + value = Integer.valueOf(temp_value); + break; + case "bool": + case "boolean": + value = Boolean.valueOf(temp_value); + break; + default: + value = temp_value; + break; + } + } + }catch(Exception e) { + _stderr.println(e.getMessage()); + } + + if(value==null) { + value = defaultValue; + } + return value; + } + + private HTTPEncodingObject loadHTTPEncodingObjectFromExtensionSetting() { + HTTPEncodingObject currentHTTPEncodingObject = new HTTPEncodingObject(); + currentHTTPEncodingObject.setPreventReEncoding((boolean) loadExtensionSettingHelper("preventReEncoding", "bool", true)); + currentHTTPEncodingObject.setEncodeMicrosoftURLEncode((boolean) loadExtensionSettingHelper("encodeMicrosoftURLEncode", "bool", false)); + currentHTTPEncodingObject.setEncodeDespiteErrors((boolean) loadExtensionSettingHelper("encodeDespiteErrors", "bool", false)); + currentHTTPEncodingObject.setAddACharToEmptyBody((boolean) loadExtensionSettingHelper("addACharToEmptyBody", "bool", true)); + currentHTTPEncodingObject.setReplaceGETwithPOST((boolean) loadExtensionSettingHelper("replaceGETwithPOST", "bool", false)); + currentHTTPEncodingObject.setEncodable_QS((boolean) loadExtensionSettingHelper("isEncodable_QS", "bool", true)); + currentHTTPEncodingObject.setEncodable_body((boolean) loadExtensionSettingHelper("isEncodable_body", "bool", true)); + currentHTTPEncodingObject.setEncodable_QS_delimiter((boolean) loadExtensionSettingHelper("isEncodable_QS_delimiter", "bool", false)); + currentHTTPEncodingObject.setEncodable_urlencoded_body_delimiter((boolean) loadExtensionSettingHelper("isEncodable_urlencoded_body_delimiter", "bool", false)); + currentHTTPEncodingObject.setEncodable_QS_equal_sign((boolean) loadExtensionSettingHelper("isEncodable_QS_equal_sign", "bool", false)); + currentHTTPEncodingObject.setEncodable_urlencoded_body_equal_sign((boolean) loadExtensionSettingHelper("isEncodable_urlencoded_body_equal_sign", "bool", false)); + currentHTTPEncodingObject.setURLEncoded_incoming_QS((boolean) loadExtensionSettingHelper("isURLEncoded_incoming_QS", "bool", true)); + currentHTTPEncodingObject.setURLEncoded_incoming_body((boolean) loadExtensionSettingHelper("isURLEncoded_incoming_body", "bool", true)); + currentHTTPEncodingObject.setURLEncoded_outgoing_QS((boolean) loadExtensionSettingHelper("isURLEncoded_outgoing_QS", "bool", true)); + currentHTTPEncodingObject.setURLEncoded_outgoing_body((boolean) loadExtensionSettingHelper("isURLEncoded_outgoing_body", "bool", true)); + currentHTTPEncodingObject.setAllChar_URLEncoded_outgoing_QS((boolean) loadExtensionSettingHelper("isAllChar_URLEncoded_outgoing_QS", "bool", true)); + currentHTTPEncodingObject.setAllChar_URLEncoded_outgoing_body((boolean) loadExtensionSettingHelper("isAllChar_URLEncoded_outgoing_body", "bool", true)); + currentHTTPEncodingObject.setTrimSpacesInContentTypeHeaderValues((boolean) loadExtensionSettingHelper("trimSpacesInContentTypeHeaderValues", "bool", true)); + currentHTTPEncodingObject.setEncodeNameValueOnlyMultipart((boolean) loadExtensionSettingHelper("encodeNameValueOnlyMultipart", "bool", false)); + currentHTTPEncodingObject.setUse_incoming_charset_for_request_encoding((boolean) loadExtensionSettingHelper("use_incoming_charset_for_request_encoding", "bool", true)); + + currentHTTPEncodingObject.setDelimiter_QS((String) loadExtensionSettingHelper("delimiter_QS", "string", "?")); + currentHTTPEncodingObject.setDelimiter_QS_param((String) loadExtensionSettingHelper("delimiter_QS_param", "string", "&")); + currentHTTPEncodingObject.setQS_equalSign((String) loadExtensionSettingHelper("QS_equalSign", "string", "=")); + currentHTTPEncodingObject.setDelimiter_urlencoded_body_param((String) loadExtensionSettingHelper("delimiter_urlencoded_body_param", "string", "&")); + currentHTTPEncodingObject.setBody_param_equalSign((String) loadExtensionSettingHelper("body_param_equalSign", "string", "=")); + currentHTTPEncodingObject.setOutgoing_request_encoding((String) loadExtensionSettingHelper("outgoing_request_encoding", "string", "ibm500")); + currentHTTPEncodingObject.setIncoming_request_encoding((String) loadExtensionSettingHelper("incoming_request_encoding", "string", "utf-8")); + + return currentHTTPEncodingObject; + } + +} \ No newline at end of file diff --git a/src/burp/IBurpCollaboratorClientContext.java b/src/burp/IBurpCollaboratorClientContext.java new file mode 100644 index 0000000..0ffe19a --- /dev/null +++ b/src/burp/IBurpCollaboratorClientContext.java @@ -0,0 +1,97 @@ +package burp; + +/* + * @(#)IBurpCollaboratorClientContext.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +import java.util.List; + +/** + * This interface represents an instance of a Burp Collaborator client context, + * which can be used to generate Burp Collaborator payloads and poll the + * Collaborator server for any network interactions that result from using those + * payloads. Extensions can obtain new instances of this class by calling + * IBurpExtenderCallbacks.createBurpCollaboratorClientContext(). + * Note that each Burp Collaborator client context is tied to the Collaborator + * server configuration that was in place at the time the context was created. + */ +public interface IBurpCollaboratorClientContext +{ + + /** + * This method is used to generate new Burp Collaborator payloads. + * + * @param includeCollaboratorServerLocation Specifies whether to include the + * Collaborator server location in the generated payload. + * @return The payload that was generated. + * + * @throws IllegalStateException if Burp Collaborator is disabled + */ + String generatePayload(boolean includeCollaboratorServerLocation); + + /** + * This method is used to retrieve all interactions received by the + * Collaborator server resulting from payloads that were generated for this + * context. + * + * @return The Collaborator interactions that have occurred resulting from + * payloads that were generated for this context. + * + * @throws IllegalStateException if Burp Collaborator is disabled + */ + List fetchAllCollaboratorInteractions(); + + /** + * This method is used to retrieve interactions received by the Collaborator + * server resulting from a single payload that was generated for this + * context. + * + * @param payload The payload for which interactions will be retrieved. + * @return The Collaborator interactions that have occurred resulting from + * the given payload. + * + * @throws IllegalStateException if Burp Collaborator is disabled + */ + List fetchCollaboratorInteractionsFor(String payload); + + /** + * This method is used to retrieve all interactions made by Burp Infiltrator + * instrumentation resulting from payloads that were generated for this + * context. + * + * @return The interactions triggered by the Burp Infiltrator + * instrumentation that have occurred resulting from payloads that were + * generated for this context. + * + * @throws IllegalStateException if Burp Collaborator is disabled + */ + List fetchAllInfiltratorInteractions(); + + /** + * This method is used to retrieve interactions made by Burp Infiltrator + * instrumentation resulting from a single payload that was generated for + * this context. + * + * @param payload The payload for which interactions will be retrieved. + * @return The interactions triggered by the Burp Infiltrator + * instrumentation that have occurred resulting from the given payload. + * + * @throws IllegalStateException if Burp Collaborator is disabled + */ + List fetchInfiltratorInteractionsFor(String payload); + + /** + * This method is used to retrieve the network location of the Collaborator + * server. + * + * @return The hostname or IP address of the Collaborator server. + * + * @throws IllegalStateException if Burp Collaborator is disabled + */ + String getCollaboratorServerLocation(); +} diff --git a/src/burp/IBurpCollaboratorInteraction.java b/src/burp/IBurpCollaboratorInteraction.java new file mode 100644 index 0000000..4299c7b --- /dev/null +++ b/src/burp/IBurpCollaboratorInteraction.java @@ -0,0 +1,41 @@ +package burp; + +/* + * @(#)IBurpCollaboratorInteraction.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +import java.util.Map; + +/** + * This interface represents a network interaction that occurred with the Burp + * Collaborator server. + */ +public interface IBurpCollaboratorInteraction +{ + + /** + * This method is used to retrieve a property of the interaction. Properties + * of all interactions are: interaction_id, type, client_ip, and time_stamp. + * Properties of DNS interactions are: query_type and raw_query. The + * raw_query value is Base64-encoded. Properties of HTTP interactions are: + * protocol, request, and response. The request and response values are + * Base64-encoded. + * + * @param name The name of the property to retrieve. + * @return A string representing the property value, or null if not present. + */ + String getProperty(String name); + + /** + * This method is used to retrieve a map containing all properties of the + * interaction. + * + * @return A map containing all properties of the interaction. + */ + Map getProperties(); +} diff --git a/src/burp/IBurpExtender.java b/src/burp/IBurpExtender.java new file mode 100644 index 0000000..8cb7390 --- /dev/null +++ b/src/burp/IBurpExtender.java @@ -0,0 +1,31 @@ +package burp; + +/* + * @(#)IBurpExtender.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +/** + * All extensions must implement this interface. + * + * Implementations must be called BurpExtender, in the package burp, must be + * declared public, and must provide a default (public, no-argument) + * constructor. + */ +public interface IBurpExtender +{ + /** + * This method is invoked when the extension is loaded. It registers an + * instance of the + * IBurpExtenderCallbacks interface, providing methods that may + * be invoked by the extension to perform various actions. + * + * @param callbacks An + * IBurpExtenderCallbacks object. + */ + void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks); +} diff --git a/src/burp/IBurpExtenderCallbacks.java b/src/burp/IBurpExtenderCallbacks.java new file mode 100644 index 0000000..03c8db0 --- /dev/null +++ b/src/burp/IBurpExtenderCallbacks.java @@ -0,0 +1,1088 @@ +package burp; + +/* + * @(#)IBurpExtenderCallbacks.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +import java.awt.Component; +import java.io.OutputStream; +import java.util.List; +import java.util.Map; + +/** + * This interface is used by Burp Suite to pass to extensions a set of callback + * methods that can be used by extensions to perform various actions within + * Burp. + * + * When an extension is loaded, Burp invokes its + * registerExtenderCallbacks() method and passes an instance of the + * IBurpExtenderCallbacks interface. The extension may then invoke + * the methods of this interface as required in order to extend Burp's + * functionality. + */ +public interface IBurpExtenderCallbacks +{ + + /** + * Flag used to identify Burp Suite as a whole. + */ + int TOOL_SUITE = 0x00000001; + /** + * Flag used to identify the Burp Target tool. + */ + int TOOL_TARGET = 0x00000002; + /** + * Flag used to identify the Burp Proxy tool. + */ + int TOOL_PROXY = 0x00000004; + /** + * Flag used to identify the Burp Spider tool. + */ + int TOOL_SPIDER = 0x00000008; + /** + * Flag used to identify the Burp Scanner tool. + */ + int TOOL_SCANNER = 0x00000010; + /** + * Flag used to identify the Burp Intruder tool. + */ + int TOOL_INTRUDER = 0x00000020; + /** + * Flag used to identify the Burp Repeater tool. + */ + int TOOL_REPEATER = 0x00000040; + /** + * Flag used to identify the Burp Sequencer tool. + */ + int TOOL_SEQUENCER = 0x00000080; + /** + * Flag used to identify the Burp Decoder tool. + */ + int TOOL_DECODER = 0x00000100; + /** + * Flag used to identify the Burp Comparer tool. + */ + int TOOL_COMPARER = 0x00000200; + /** + * Flag used to identify the Burp Extender tool. + */ + int TOOL_EXTENDER = 0x00000400; + + /** + * This method is used to set the display name for the current extension, + * which will be displayed within the user interface for the Extender tool. + * + * @param name The extension name. + */ + void setExtensionName(String name); + + /** + * This method is used to obtain an IExtensionHelpers object, + * which can be used by the extension to perform numerous useful tasks. + * + * @return An object containing numerous helper methods, for tasks such as + * building and analyzing HTTP requests. + */ + IExtensionHelpers getHelpers(); + + /** + * This method is used to obtain the current extension's standard output + * stream. Extensions should write all output to this stream, allowing the + * Burp user to configure how that output is handled from within the UI. + * + * @return The extension's standard output stream. + */ + OutputStream getStdout(); + + /** + * This method is used to obtain the current extension's standard error + * stream. Extensions should write all error messages to this stream, + * allowing the Burp user to configure how that output is handled from + * within the UI. + * + * @return The extension's standard error stream. + */ + OutputStream getStderr(); + + /** + * This method prints a line of output to the current extension's standard + * output stream. + * + * @param output The message to print. + */ + void printOutput(String output); + + /** + * This method prints a line of output to the current extension's standard + * error stream. + * + * @param error The message to print. + */ + void printError(String error); + + /** + * This method is used to register a listener which will be notified of + * changes to the extension's state. Note: Any extensions that start + * background threads or open system resources (such as files or database + * connections) should register a listener and terminate threads / close + * resources when the extension is unloaded. + * + * @param listener An object created by the extension that implements the + * IExtensionStateListener interface. + */ + void registerExtensionStateListener(IExtensionStateListener listener); + + /** + * This method is used to retrieve the extension state listeners that are + * registered by the extension. + * + * @return A list of extension state listeners that are currently registered + * by this extension. + */ + List getExtensionStateListeners(); + + /** + * This method is used to remove an extension state listener that has been + * registered by the extension. + * + * @param listener The extension state listener to be removed. + */ + void removeExtensionStateListener(IExtensionStateListener listener); + + /** + * This method is used to register a listener which will be notified of + * requests and responses made by any Burp tool. Extensions can perform + * custom analysis or modification of these messages by registering an HTTP + * listener. + * + * @param listener An object created by the extension that implements the + * IHttpListener interface. + */ + void registerHttpListener(IHttpListener listener); + + /** + * This method is used to retrieve the HTTP listeners that are registered by + * the extension. + * + * @return A list of HTTP listeners that are currently registered by this + * extension. + */ + List getHttpListeners(); + + /** + * This method is used to remove an HTTP listener that has been registered + * by the extension. + * + * @param listener The HTTP listener to be removed. + */ + void removeHttpListener(IHttpListener listener); + + /** + * This method is used to register a listener which will be notified of + * requests and responses being processed by the Proxy tool. Extensions can + * perform custom analysis or modification of these messages, and control + * in-UI message interception, by registering a proxy listener. + * + * @param listener An object created by the extension that implements the + * IProxyListener interface. + */ + void registerProxyListener(IProxyListener listener); + + /** + * This method is used to retrieve the Proxy listeners that are registered + * by the extension. + * + * @return A list of Proxy listeners that are currently registered by this + * extension. + */ + List getProxyListeners(); + + /** + * This method is used to remove a Proxy listener that has been registered + * by the extension. + * + * @param listener The Proxy listener to be removed. + */ + void removeProxyListener(IProxyListener listener); + + /** + * This method is used to register a listener which will be notified of new + * issues that are reported by the Scanner tool. Extensions can perform + * custom analysis or logging of Scanner issues by registering a Scanner + * listener. + * + * @param listener An object created by the extension that implements the + * IScannerListener interface. + */ + void registerScannerListener(IScannerListener listener); + + /** + * This method is used to retrieve the Scanner listeners that are registered + * by the extension. + * + * @return A list of Scanner listeners that are currently registered by this + * extension. + */ + List getScannerListeners(); + + /** + * This method is used to remove a Scanner listener that has been registered + * by the extension. + * + * @param listener The Scanner listener to be removed. + */ + void removeScannerListener(IScannerListener listener); + + /** + * This method is used to register a listener which will be notified of + * changes to Burp's suite-wide target scope. + * + * @param listener An object created by the extension that implements the + * IScopeChangeListener interface. + */ + void registerScopeChangeListener(IScopeChangeListener listener); + + /** + * This method is used to retrieve the scope change listeners that are + * registered by the extension. + * + * @return A list of scope change listeners that are currently registered by + * this extension. + */ + List getScopeChangeListeners(); + + /** + * This method is used to remove a scope change listener that has been + * registered by the extension. + * + * @param listener The scope change listener to be removed. + */ + void removeScopeChangeListener(IScopeChangeListener listener); + + /** + * This method is used to register a factory for custom context menu items. + * When the user invokes a context menu anywhere within Burp, the factory + * will be passed details of the invocation event, and asked to provide any + * custom context menu items that should be shown. + * + * @param factory An object created by the extension that implements the + * IContextMenuFactory interface. + */ + void registerContextMenuFactory(IContextMenuFactory factory); + + /** + * This method is used to retrieve the context menu factories that are + * registered by the extension. + * + * @return A list of context menu factories that are currently registered by + * this extension. + */ + List getContextMenuFactories(); + + /** + * This method is used to remove a context menu factory that has been + * registered by the extension. + * + * @param factory The context menu factory to be removed. + */ + void removeContextMenuFactory(IContextMenuFactory factory); + + /** + * This method is used to register a factory for custom message editor tabs. + * For each message editor that already exists, or is subsequently created, + * within Burp, the factory will be asked to provide a new instance of an + * IMessageEditorTab object, which can provide custom rendering + * or editing of HTTP messages. + * + * @param factory An object created by the extension that implements the + * IMessageEditorTabFactory interface. + */ + void registerMessageEditorTabFactory(IMessageEditorTabFactory factory); + + /** + * This method is used to retrieve the message editor tab factories that are + * registered by the extension. + * + * @return A list of message editor tab factories that are currently + * registered by this extension. + */ + List getMessageEditorTabFactories(); + + /** + * This method is used to remove a message editor tab factory that has been + * registered by the extension. + * + * @param factory The message editor tab factory to be removed. + */ + void removeMessageEditorTabFactory(IMessageEditorTabFactory factory); + + /** + * This method is used to register a provider of Scanner insertion points. + * For each base request that is actively scanned, Burp will ask the + * provider to provide any custom scanner insertion points that are + * appropriate for the request. + * + * @param provider An object created by the extension that implements the + * IScannerInsertionPointProvider interface. + */ + void registerScannerInsertionPointProvider( + IScannerInsertionPointProvider provider); + + /** + * This method is used to retrieve the Scanner insertion point providers + * that are registered by the extension. + * + * @return A list of Scanner insertion point providers that are currently + * registered by this extension. + */ + List getScannerInsertionPointProviders(); + + /** + * This method is used to remove a Scanner insertion point provider that has + * been registered by the extension. + * + * @param provider The Scanner insertion point provider to be removed. + */ + void removeScannerInsertionPointProvider( + IScannerInsertionPointProvider provider); + + /** + * This method is used to register a custom Scanner check. When performing + * scanning, Burp will ask the check to perform active or passive scanning + * on the base request, and report any Scanner issues that are identified. + * + * @param check An object created by the extension that implements the + * IScannerCheck interface. + */ + void registerScannerCheck(IScannerCheck check); + + /** + * This method is used to retrieve the Scanner checks that are registered by + * the extension. + * + * @return A list of Scanner checks that are currently registered by this + * extension. + */ + List getScannerChecks(); + + /** + * This method is used to remove a Scanner check that has been registered by + * the extension. + * + * @param check The Scanner check to be removed. + */ + void removeScannerCheck(IScannerCheck check); + + /** + * This method is used to register a factory for Intruder payloads. Each + * registered factory will be available within the Intruder UI for the user + * to select as the payload source for an attack. When this is selected, the + * factory will be asked to provide a new instance of an + * IIntruderPayloadGenerator object, which will be used to + * generate payloads for the attack. + * + * @param factory An object created by the extension that implements the + * IIntruderPayloadGeneratorFactory interface. + */ + void registerIntruderPayloadGeneratorFactory( + IIntruderPayloadGeneratorFactory factory); + + /** + * This method is used to retrieve the Intruder payload generator factories + * that are registered by the extension. + * + * @return A list of Intruder payload generator factories that are currently + * registered by this extension. + */ + List + getIntruderPayloadGeneratorFactories(); + + /** + * This method is used to remove an Intruder payload generator factory that + * has been registered by the extension. + * + * @param factory The Intruder payload generator factory to be removed. + */ + void removeIntruderPayloadGeneratorFactory( + IIntruderPayloadGeneratorFactory factory); + + /** + * This method is used to register a custom Intruder payload processor. Each + * registered processor will be available within the Intruder UI for the + * user to select as the action for a payload processing rule. + * + * @param processor An object created by the extension that implements the + * IIntruderPayloadProcessor interface. + */ + void registerIntruderPayloadProcessor(IIntruderPayloadProcessor processor); + + /** + * This method is used to retrieve the Intruder payload processors that are + * registered by the extension. + * + * @return A list of Intruder payload processors that are currently + * registered by this extension. + */ + List getIntruderPayloadProcessors(); + + /** + * This method is used to remove an Intruder payload processor that has been + * registered by the extension. + * + * @param processor The Intruder payload processor to be removed. + */ + void removeIntruderPayloadProcessor(IIntruderPayloadProcessor processor); + + /** + * This method is used to register a custom session handling action. Each + * registered action will be available within the session handling rule UI + * for the user to select as a rule action. Users can choose to invoke an + * action directly in its own right, or following execution of a macro. + * + * @param action An object created by the extension that implements the + * ISessionHandlingAction interface. + */ + void registerSessionHandlingAction(ISessionHandlingAction action); + + /** + * This method is used to retrieve the session handling actions that are + * registered by the extension. + * + * @return A list of session handling actions that are currently registered + * by this extension. + */ + List getSessionHandlingActions(); + + /** + * This method is used to remove a session handling action that has been + * registered by the extension. + * + * @param action The extension session handling action to be removed. + */ + void removeSessionHandlingAction(ISessionHandlingAction action); + + /** + * This method is used to unload the extension from Burp Suite. + */ + void unloadExtension(); + + /** + * This method is used to add a custom tab to the main Burp Suite window. + * + * @param tab An object created by the extension that implements the + * ITab interface. + */ + void addSuiteTab(ITab tab); + + /** + * This method is used to remove a previously-added tab from the main Burp + * Suite window. + * + * @param tab An object created by the extension that implements the + * ITab interface. + */ + void removeSuiteTab(ITab tab); + + /** + * This method is used to customize UI components in line with Burp's UI + * style, including font size, colors, table line spacing, etc. The action + * is performed recursively on any child components of the passed-in + * component. + * + * @param component The UI component to be customized. + */ + void customizeUiComponent(Component component); + + /** + * This method is used to create a new instance of Burp's HTTP message + * editor, for the extension to use in its own UI. + * + * @param controller An object created by the extension that implements the + * IMessageEditorController interface. This parameter is + * optional and may be null. If it is provided, then the + * message editor will query the controller when required to obtain details + * about the currently displayed message, including the + * IHttpService for the message, and the associated request or + * response message. If a controller is not provided, then the message + * editor will not support context menu actions, such as sending requests to + * other Burp tools. + * @param editable Indicates whether the editor created should be editable, + * or used only for message viewing. + * @return An object that implements the IMessageEditor + * interface, and which the extension can use in its own UI. + */ + IMessageEditor createMessageEditor(IMessageEditorController controller, + boolean editable); + + /** + * This method returns the command line arguments that were passed to Burp + * on startup. + * + * @return The command line arguments that were passed to Burp on startup. + */ + String[] getCommandLineArguments(); + + /** + * This method is used to save configuration settings for the extension in a + * persistent way that survives reloads of the extension and of Burp Suite. + * Saved settings can be retrieved using the method + * loadExtensionSetting(). + * + * @param name The name of the setting. + * @param value The value of the setting. If this value is null + * then any existing setting with the specified name will be removed. + */ + void saveExtensionSetting(String name, String value); + + /** + * This method is used to load configuration settings for the extension that + * were saved using the method saveExtensionSetting(). + * + * @param name The name of the setting. + * @return The value of the setting, or null if no value is + * set. + */ + String loadExtensionSetting(String name); + + /** + * This method is used to create a new instance of Burp's plain text editor, + * for the extension to use in its own UI. + * + * @return An object that implements the ITextEditor interface, + * and which the extension can use in its own UI. + */ + ITextEditor createTextEditor(); + + /** + * This method can be used to send an HTTP request to the Burp Repeater + * tool. The request will be displayed in the user interface, but will not + * be issued until the user initiates this action. + * + * @param host The hostname of the remote HTTP server. + * @param port The port of the remote HTTP server. + * @param useHttps Flags whether the protocol is HTTPS or HTTP. + * @param request The full HTTP request. + * @param tabCaption An optional caption which will appear on the Repeater + * tab containing the request. If this value is null then a + * default tab index will be displayed. + */ + void sendToRepeater( + String host, + int port, + boolean useHttps, + byte[] request, + String tabCaption); + + /** + * This method can be used to send an HTTP request to the Burp Intruder + * tool. The request will be displayed in the user interface, and markers + * for attack payloads will be placed into default locations within the + * request. + * + * @param host The hostname of the remote HTTP server. + * @param port The port of the remote HTTP server. + * @param useHttps Flags whether the protocol is HTTPS or HTTP. + * @param request The full HTTP request. + */ + void sendToIntruder( + String host, + int port, + boolean useHttps, + byte[] request); + + /** + * This method can be used to send an HTTP request to the Burp Intruder + * tool. The request will be displayed in the user interface, and markers + * for attack payloads will be placed into the specified locations within + * the request. + * + * @param host The hostname of the remote HTTP server. + * @param port The port of the remote HTTP server. + * @param useHttps Flags whether the protocol is HTTPS or HTTP. + * @param request The full HTTP request. + * @param payloadPositionOffsets A list of index pairs representing the + * payload positions to be used. Each item in the list must be an int[2] + * array containing the start and end offsets for the payload position. + */ + void sendToIntruder( + String host, + int port, + boolean useHttps, + byte[] request, + List payloadPositionOffsets); + + /** + * This method can be used to send data to the Comparer tool. + * + * @param data The data to be sent to Comparer. + */ + void sendToComparer(byte[] data); + + /** + * This method can be used to send a seed URL to the Burp Spider tool. If + * the URL is not within the current Spider scope, the user will be asked if + * they wish to add the URL to the scope. If the Spider is not currently + * running, it will be started. The seed URL will be requested, and the + * Spider will process the application's response in the normal way. + * + * @param url The new seed URL to begin spidering from. + */ + void sendToSpider( + java.net.URL url); + + /** + * This method can be used to send an HTTP request to the Burp Scanner tool + * to perform an active vulnerability scan. If the request is not within the + * current active scanning scope, the user will be asked if they wish to + * proceed with the scan. + * + * @param host The hostname of the remote HTTP server. + * @param port The port of the remote HTTP server. + * @param useHttps Flags whether the protocol is HTTPS or HTTP. + * @param request The full HTTP request. + * @return The resulting scan queue item. + */ + IScanQueueItem doActiveScan( + String host, + int port, + boolean useHttps, + byte[] request); + + /** + * This method can be used to send an HTTP request to the Burp Scanner tool + * to perform an active vulnerability scan, based on a custom list of + * insertion points that are to be scanned. If the request is not within the + * current active scanning scope, the user will be asked if they wish to + * proceed with the scan. + * + * @param host The hostname of the remote HTTP server. + * @param port The port of the remote HTTP server. + * @param useHttps Flags whether the protocol is HTTPS or HTTP. + * @param request The full HTTP request. + * @param insertionPointOffsets A list of index pairs representing the + * positions of the insertion points that should be scanned. Each item in + * the list must be an int[2] array containing the start and end offsets for + * the insertion point. + * @return The resulting scan queue item. + */ + IScanQueueItem doActiveScan( + String host, + int port, + boolean useHttps, + byte[] request, + List insertionPointOffsets); + + /** + * This method can be used to send an HTTP request to the Burp Scanner tool + * to perform a passive vulnerability scan. + * + * @param host The hostname of the remote HTTP server. + * @param port The port of the remote HTTP server. + * @param useHttps Flags whether the protocol is HTTPS or HTTP. + * @param request The full HTTP request. + * @param response The full HTTP response. + */ + void doPassiveScan( + String host, + int port, + boolean useHttps, + byte[] request, + byte[] response); + + /** + * This method can be used to issue HTTP requests and retrieve their + * responses. + * + * @param httpService The HTTP service to which the request should be sent. + * @param request The full HTTP request. + * @return An object that implements the IHttpRequestResponse + * interface, and which the extension can query to obtain the details of the + * response. + */ + IHttpRequestResponse makeHttpRequest(IHttpService httpService, + byte[] request); + + /** + * This method can be used to issue HTTP requests and retrieve their + * responses. + * + * @param host The hostname of the remote HTTP server. + * @param port The port of the remote HTTP server. + * @param useHttps Flags whether the protocol is HTTPS or HTTP. + * @param request The full HTTP request. + * @return The full response retrieved from the remote server. + */ + byte[] makeHttpRequest( + String host, + int port, + boolean useHttps, + byte[] request); + + /** + * This method can be used to query whether a specified URL is within the + * current Suite-wide scope. + * + * @param url The URL to query. + * @return Returns true if the URL is within the current + * Suite-wide scope. + */ + boolean isInScope(java.net.URL url); + + /** + * This method can be used to include the specified URL in the Suite-wide + * scope. + * + * @param url The URL to include in the Suite-wide scope. + */ + void includeInScope(java.net.URL url); + + /** + * This method can be used to exclude the specified URL from the Suite-wide + * scope. + * + * @param url The URL to exclude from the Suite-wide scope. + */ + void excludeFromScope(java.net.URL url); + + /** + * This method can be used to display a specified message in the Burp Suite + * alerts tab. + * + * @param message The alert message to display. + */ + void issueAlert(String message); + + /** + * This method returns details of all items in the Proxy history. + * + * @return The contents of the Proxy history. + */ + IHttpRequestResponse[] getProxyHistory(); + + /** + * This method returns details of items in the site map. + * + * @param urlPrefix This parameter can be used to specify a URL prefix, in + * order to extract a specific subset of the site map. The method performs a + * simple case-sensitive text match, returning all site map items whose URL + * begins with the specified prefix. If this parameter is null, the entire + * site map is returned. + * + * @return Details of items in the site map. + */ + IHttpRequestResponse[] getSiteMap(String urlPrefix); + + /** + * This method returns all of the current scan issues for URLs matching the + * specified literal prefix. + * + * @param urlPrefix This parameter can be used to specify a URL prefix, in + * order to extract a specific subset of scan issues. The method performs a + * simple case-sensitive text match, returning all scan issues whose URL + * begins with the specified prefix. If this parameter is null, all issues + * are returned. + * @return Details of the scan issues. + */ + IScanIssue[] getScanIssues(String urlPrefix); + + /** + * This method is used to generate a report for the specified Scanner + * issues. The report format can be specified. For all other reporting + * options, the default settings that appear in the reporting UI wizard are + * used. + * + * @param format The format to be used in the report. Accepted values are + * HTML and XML. + * @param issues The Scanner issues to be reported. + * @param file The file to which the report will be saved. + */ + void generateScanReport(String format, IScanIssue[] issues, + java.io.File file); + + /** + * This method is used to retrieve the contents of Burp's session handling + * cookie jar. Extensions that provide an + * ISessionHandlingAction can query and update the cookie jar + * in order to handle unusual session handling mechanisms. + * + * @return A list of ICookie objects representing the contents + * of Burp's session handling cookie jar. + */ + List getCookieJarContents(); + + /** + * This method is used to update the contents of Burp's session handling + * cookie jar. Extensions that provide an + * ISessionHandlingAction can query and update the cookie jar + * in order to handle unusual session handling mechanisms. + * + * @param cookie An ICookie object containing details of the + * cookie to be updated. If the cookie jar already contains a cookie that + * matches the specified domain and name, then that cookie will be updated + * with the new value and expiration, unless the new value is + * null, in which case the cookie will be removed. If the + * cookie jar does not already contain a cookie that matches the specified + * domain and name, then the cookie will be added. + */ + void updateCookieJar(ICookie cookie); + + /** + * This method can be used to add an item to Burp's site map with the + * specified request/response details. This will overwrite the details of + * any existing matching item in the site map. + * + * @param item Details of the item to be added to the site map + */ + void addToSiteMap(IHttpRequestResponse item); + + /** + * This method can be used to restore Burp's state from a specified saved + * state file. This method blocks until the restore operation is completed, + * and must not be called from the event dispatch thread. + * + * @param file The file containing Burp's saved state. + * @deprecated State files have been replaced with Burp project files. + */ + @Deprecated + void restoreState(java.io.File file); + + /** + * This method can be used to save Burp's state to a specified file. This + * method blocks until the save operation is completed, and must not be + * called from the event dispatch thread. + * + * @param file The file to save Burp's state in. + * @deprecated State files have been replaced with Burp project files. + */ + @Deprecated + void saveState(java.io.File file); + + /** + * This method is no longer supported. Please use saveConfigAsJson() instead. + * + * @return A Map of name/value Strings reflecting Burp's current + * configuration. + * @deprecated Use saveConfigAsJson() instead. + */ + @Deprecated + Map saveConfig(); + + /** + * This method is no longer supported. Please use loadConfigFromJson() instead. + * + * @param config A map of name/value Strings to use as Burp's new + * configuration. + * @deprecated Use loadConfigFromJson() instead. + */ + @Deprecated + void loadConfig(Map config); + + /** + * This method causes Burp to save its current project-level configuration + * in JSON format. This is the same format that can be saved and loaded via + * the Burp user interface. To include only certain sections of the + * configuration, you can optionally supply the path to each section that + * should be included, for example: "project_options.connections". If no + * paths are provided, then the entire configuration will be saved. + * + * @param configPaths A list of Strings representing the path to each + * configuration section that should be included. + * @return A String representing the current configuration in JSON format. + */ + String saveConfigAsJson(String... configPaths); + + /** + * This method causes Burp to load a new project-level configuration from + * the JSON String provided. This is the same format that can be saved and + * loaded via the Burp user interface. Partial configurations are + * acceptable, and any settings not specified will be left unmodified. + * + * Any user-level configuration options contained in the input will be + * ignored. + * + * @param config A JSON String containing the new configuration. + */ + void loadConfigFromJson(String config); + + /** + * This method sets the master interception mode for Burp Proxy. + * + * @param enabled Indicates whether interception of Proxy messages should be + * enabled. + */ + void setProxyInterceptionEnabled(boolean enabled); + + /** + * This method retrieves information about the version of Burp in which the + * extension is running. It can be used by extensions to dynamically adjust + * their behavior depending on the functionality and APIs supported by the + * current version. + * + * @return An array of Strings comprised of: the product name (e.g. Burp + * Suite Professional), the major version (e.g. 1.5), the minor version + * (e.g. 03) + */ + String[] getBurpVersion(); + + /** + * This method retrieves the absolute path name of the file from which the + * current extension was loaded. + * + * @return The absolute path name of the file from which the current + * extension was loaded. + */ + String getExtensionFilename(); + + /** + * This method determines whether the current extension was loaded as a BApp + * (a Burp App from the BApp Store). + * + * @return Returns true if the current extension was loaded as a BApp. + */ + boolean isExtensionBapp(); + + /** + * This method can be used to shut down Burp programmatically, with an + * optional prompt to the user. If the method returns, the user canceled the + * shutdown prompt. + * + * @param promptUser Indicates whether to prompt the user to confirm the + * shutdown. + */ + void exitSuite(boolean promptUser); + + /** + * This method is used to create a temporary file on disk containing the + * provided data. Extensions can use temporary files for long-term storage + * of runtime data, avoiding the need to retain that data in memory. + * + * @param buffer The data to be saved to a temporary file. + * @return An object that implements the ITempFile interface. + */ + ITempFile saveToTempFile(byte[] buffer); + + /** + * This method is used to save the request and response of an + * IHttpRequestResponse object to temporary files, so that they + * are no longer held in memory. Extensions can used this method to convert + * IHttpRequestResponse objects into a form suitable for + * long-term storage. + * + * @param httpRequestResponse The IHttpRequestResponse object + * whose request and response messages are to be saved to temporary files. + * @return An object that implements the + * IHttpRequestResponsePersisted interface. + */ + IHttpRequestResponsePersisted saveBuffersToTempFiles( + IHttpRequestResponse httpRequestResponse); + + /** + * This method is used to apply markers to an HTTP request or response, at + * offsets into the message that are relevant for some particular purpose. + * Markers are used in various situations, such as specifying Intruder + * payload positions, Scanner insertion points, and highlights in Scanner + * issues. + * + * @param httpRequestResponse The IHttpRequestResponse object + * to which the markers should be applied. + * @param requestMarkers A list of index pairs representing the offsets of + * markers to be applied to the request message. Each item in the list must + * be an int[2] array containing the start and end offsets for the marker. + * The markers in the list should be in sequence and not overlapping. This + * parameter is optional and may be null if no request markers + * are required. + * @param responseMarkers A list of index pairs representing the offsets of + * markers to be applied to the response message. Each item in the list must + * be an int[2] array containing the start and end offsets for the marker. + * The markers in the list should be in sequence and not overlapping. This + * parameter is optional and may be null if no response markers + * are required. + * @return An object that implements the + * IHttpRequestResponseWithMarkers interface. + */ + IHttpRequestResponseWithMarkers applyMarkers( + IHttpRequestResponse httpRequestResponse, + List requestMarkers, + List responseMarkers); + + /** + * This method is used to obtain the descriptive name for the Burp tool + * identified by the tool flag provided. + * + * @param toolFlag A flag identifying a Burp tool ( TOOL_PROXY, + * TOOL_SCANNER, etc.). Tool flags are defined within this + * interface. + * @return The descriptive name for the specified tool. + */ + String getToolName(int toolFlag); + + /** + * This method is used to register a new Scanner issue. Note: + * Wherever possible, extensions should implement custom Scanner checks + * using IScannerCheck and report issues via those checks, so + * as to integrate with Burp's user-driven workflow, and ensure proper + * consolidation of duplicate reported issues. This method is only designed + * for tasks outside of the normal testing workflow, such as importing + * results from other scanning tools. + * + * @param issue An object created by the extension that implements the + * IScanIssue interface. + */ + void addScanIssue(IScanIssue issue); + + /** + * This method is used to create a new Burp Collaborator client context, + * which can be used to generate Burp Collaborator payloads and poll the + * Collaborator server for any network interactions that result from using + * those payloads. + * + * @return A new instance of IBurpCollaboratorClientContext + * that can be used to generate Collaborator payloads and retrieve + * interactions. + */ + IBurpCollaboratorClientContext createBurpCollaboratorClientContext(); + + /** + * This method parses the specified request and returns details of each + * request parameter. + * + * @param request The request to be parsed. + * @return An array of: String[] { name, value, type } + * containing details of the parameters contained within the request. + * @deprecated Use IExtensionHelpers.analyzeRequest() instead. + */ + @Deprecated + String[][] getParameters(byte[] request); + + /** + * This method parses the specified request and returns details of each HTTP + * header. + * + * @param message The request to be parsed. + * @return An array of HTTP headers. + * @deprecated Use IExtensionHelpers.analyzeRequest() or + * IExtensionHelpers.analyzeResponse() instead. + */ + @Deprecated + String[] getHeaders(byte[] message); + + /** + * This method can be used to register a new menu item which will appear on + * the various context menus that are used throughout Burp Suite to handle + * user-driven actions. + * + * @param menuItemCaption The caption to be displayed on the menu item. + * @param menuItemHandler The handler to be invoked when the user clicks on + * the menu item. + * @deprecated Use registerContextMenuFactory() instead. + */ + @Deprecated + void registerMenuItem( + String menuItemCaption, + IMenuItemHandler menuItemHandler); +} diff --git a/src/burp/IContextMenuFactory.java b/src/burp/IContextMenuFactory.java new file mode 100644 index 0000000..1ff9f0f --- /dev/null +++ b/src/burp/IContextMenuFactory.java @@ -0,0 +1,39 @@ +package burp; + +/* + * @(#)IContextMenuFactory.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ + +import javax.swing.JMenuItem; +import java.util.List; + +/** + * Extensions can implement this interface and then call + * IBurpExtenderCallbacks.registerContextMenuFactory() to register + * a factory for custom context menu items. + */ +public interface IContextMenuFactory +{ + /** + * This method will be called by Burp when the user invokes a context menu + * anywhere within Burp. The factory can then provide any custom context + * menu items that should be displayed in the context menu, based on the + * details of the menu invocation. + * + * @param invocation An object that implements the + * IContextMenuInvocation interface, which the extension can + * query to obtain details of the context menu invocation. + * @return A list of custom menu items (which may include sub-menus, + * checkbox menu items, etc.) that should be displayed. Extensions may + * return + * null from this method, to indicate that no menu items are + * required. + */ + List createMenuItems(IContextMenuInvocation invocation); +} diff --git a/src/burp/IContextMenuInvocation.java b/src/burp/IContextMenuInvocation.java new file mode 100644 index 0000000..032d107 --- /dev/null +++ b/src/burp/IContextMenuInvocation.java @@ -0,0 +1,156 @@ +package burp; + +/* + * @(#)IContextMenuInvocation.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +import java.awt.event.InputEvent; + +/** + * This interface is used when Burp calls into an extension-provided + * IContextMenuFactory with details of a context menu invocation. + * The custom context menu factory can query this interface to obtain details of + * the invocation event, in order to determine what menu items should be + * displayed. + */ +public interface IContextMenuInvocation +{ + /** + * Used to indicate that the context menu is being invoked in a request + * editor. + */ + static final byte CONTEXT_MESSAGE_EDITOR_REQUEST = 0; + /** + * Used to indicate that the context menu is being invoked in a response + * editor. + */ + static final byte CONTEXT_MESSAGE_EDITOR_RESPONSE = 1; + /** + * Used to indicate that the context menu is being invoked in a non-editable + * request viewer. + */ + static final byte CONTEXT_MESSAGE_VIEWER_REQUEST = 2; + /** + * Used to indicate that the context menu is being invoked in a non-editable + * response viewer. + */ + static final byte CONTEXT_MESSAGE_VIEWER_RESPONSE = 3; + /** + * Used to indicate that the context menu is being invoked in the Target + * site map tree. + */ + static final byte CONTEXT_TARGET_SITE_MAP_TREE = 4; + /** + * Used to indicate that the context menu is being invoked in the Target + * site map table. + */ + static final byte CONTEXT_TARGET_SITE_MAP_TABLE = 5; + /** + * Used to indicate that the context menu is being invoked in the Proxy + * history. + */ + static final byte CONTEXT_PROXY_HISTORY = 6; + /** + * Used to indicate that the context menu is being invoked in the Scanner + * results. + */ + static final byte CONTEXT_SCANNER_RESULTS = 7; + /** + * Used to indicate that the context menu is being invoked in the Intruder + * payload positions editor. + */ + static final byte CONTEXT_INTRUDER_PAYLOAD_POSITIONS = 8; + /** + * Used to indicate that the context menu is being invoked in an Intruder + * attack results. + */ + static final byte CONTEXT_INTRUDER_ATTACK_RESULTS = 9; + /** + * Used to indicate that the context menu is being invoked in a search + * results window. + */ + static final byte CONTEXT_SEARCH_RESULTS = 10; + + /** + * This method can be used to retrieve the native Java input event that was + * the trigger for the context menu invocation. + * + * @return The InputEvent that was the trigger for the context + * menu invocation. + */ + InputEvent getInputEvent(); + + /** + * This method can be used to retrieve the Burp tool within which the + * context menu was invoked. + * + * @return A flag indicating the Burp tool within which the context menu was + * invoked. Burp tool flags are defined in the + * IBurpExtenderCallbacks interface. + */ + int getToolFlag(); + + /** + * This method can be used to retrieve the context within which the menu was + * invoked. + * + * @return An index indicating the context within which the menu was + * invoked. The indices used are defined within this interface. + */ + byte getInvocationContext(); + + /** + * This method can be used to retrieve the bounds of the user's selection + * into the current message, if applicable. + * + * @return An int[2] array containing the start and end offsets of the + * user's selection in the current message. If the user has not made any + * selection in the current message, both offsets indicate the position of + * the caret within the editor. If the menu is not being invoked from a + * message editor, the method returns null. + */ + int[] getSelectionBounds(); + + /** + * This method can be used to retrieve details of the HTTP requests / + * responses that were shown or selected by the user when the context menu + * was invoked. + * + * Note: For performance reasons, the objects returned from this + * method are tied to the originating context of the messages within the + * Burp UI. For example, if a context menu is invoked on the Proxy intercept + * panel, then the + * IHttpRequestResponse returned by this method will reflect + * the current contents of the interception panel, and this will change when + * the current message has been forwarded or dropped. If your extension + * needs to store details of the message for which the context menu has been + * invoked, then you should query those details from the + * IHttpRequestResponse at the time of invocation, or you + * should use + * IBurpExtenderCallbacks.saveBuffersToTempFiles() to create a + * persistent read-only copy of the + * IHttpRequestResponse. + * + * @return An array of IHttpRequestResponse objects + * representing the items that were shown or selected by the user when the + * context menu was invoked. This method returns null if no + * messages are applicable to the invocation. + */ + IHttpRequestResponse[] getSelectedMessages(); + + /** + * This method can be used to retrieve details of the Scanner issues that + * were selected by the user when the context menu was invoked. + * + * @return An array of IScanIssue objects representing the + * issues that were selected by the user when the context menu was invoked. + * This method returns null if no Scanner issues are applicable + * to the invocation. + */ + IScanIssue[] getSelectedIssues(); +} diff --git a/src/burp/ICookie.java b/src/burp/ICookie.java new file mode 100644 index 0000000..8c3aaa1 --- /dev/null +++ b/src/burp/ICookie.java @@ -0,0 +1,61 @@ +package burp; + +/* + * @(#)ICookie.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +import java.util.Date; + +/** + * This interface is used to hold details about an HTTP cookie. + */ +public interface ICookie +{ + /** + * This method is used to retrieve the domain for which the cookie is in + * scope. + * + * @return The domain for which the cookie is in scope. Note: For + * cookies that have been analyzed from responses (by calling + * IExtensionHelpers.analyzeResponse() and then + * IResponseInfo.getCookies(), the domain will be + * null if the response did not explicitly set a domain + * attribute for the cookie. + */ + String getDomain(); + + /** + * This method is used to retrieve the path for which the cookie is in + * scope. + * + * @return The path for which the cookie is in scope or null if none is set. + */ + String getPath(); + + /** + * This method is used to retrieve the expiration time for the cookie. + * + * @return The expiration time for the cookie, or + * null if none is set (i.e., for non-persistent session + * cookies). + */ + Date getExpiration(); + + /** + * This method is used to retrieve the name of the cookie. + * + * @return The name of the cookie. + */ + String getName(); + + /** + * This method is used to retrieve the value of the cookie. + * @return The value of the cookie. + */ + String getValue(); +} diff --git a/src/burp/IExtensionHelpers.java b/src/burp/IExtensionHelpers.java new file mode 100644 index 0000000..49beb97 --- /dev/null +++ b/src/burp/IExtensionHelpers.java @@ -0,0 +1,356 @@ +package burp; + +/* + * @(#)IExtensionHelpers.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +import java.net.URL; +import java.util.List; + +/** + * This interface contains a number of helper methods, which extensions can use + * to assist with various common tasks that arise for Burp extensions. + * + * Extensions can call IBurpExtenderCallbacks.getHelpers to obtain + * an instance of this interface. + */ +public interface IExtensionHelpers +{ + + /** + * This method can be used to analyze an HTTP request, and obtain various + * key details about it. + * + * @param request An IHttpRequestResponse object containing the + * request to be analyzed. + * @return An IRequestInfo object that can be queried to obtain + * details about the request. + */ + IRequestInfo analyzeRequest(IHttpRequestResponse request); + + /** + * This method can be used to analyze an HTTP request, and obtain various + * key details about it. + * + * @param httpService The HTTP service associated with the request. This is + * optional and may be null, in which case the resulting + * IRequestInfo object will not include the full request URL. + * @param request The request to be analyzed. + * @return An IRequestInfo object that can be queried to obtain + * details about the request. + */ + IRequestInfo analyzeRequest(IHttpService httpService, byte[] request); + + /** + * This method can be used to analyze an HTTP request, and obtain various + * key details about it. The resulting IRequestInfo object will + * not include the full request URL. To obtain the full URL, use one of the + * other overloaded analyzeRequest() methods. + * + * @param request The request to be analyzed. + * @return An IRequestInfo object that can be queried to obtain + * details about the request. + */ + IRequestInfo analyzeRequest(byte[] request); + + /** + * This method can be used to analyze an HTTP response, and obtain various + * key details about it. + * + * @param response The response to be analyzed. + * @return An IResponseInfo object that can be queried to + * obtain details about the response. + */ + IResponseInfo analyzeResponse(byte[] response); + + /** + * This method can be used to retrieve details of a specified parameter + * within an HTTP request. Note: Use analyzeRequest() to + * obtain details of all parameters within the request. + * + * @param request The request to be inspected for the specified parameter. + * @param parameterName The name of the parameter to retrieve. + * @return An IParameter object that can be queried to obtain + * details about the parameter, or null if the parameter was + * not found. + */ + IParameter getRequestParameter(byte[] request, String parameterName); + + /** + * This method can be used to URL-decode the specified data. + * + * @param data The data to be decoded. + * @return The decoded data. + */ + String urlDecode(String data); + + /** + * This method can be used to URL-encode the specified data. Any characters + * that do not need to be encoded within HTTP requests are not encoded. + * + * @param data The data to be encoded. + * @return The encoded data. + */ + String urlEncode(String data); + + /** + * This method can be used to URL-decode the specified data. + * + * @param data The data to be decoded. + * @return The decoded data. + */ + byte[] urlDecode(byte[] data); + + /** + * This method can be used to URL-encode the specified data. Any characters + * that do not need to be encoded within HTTP requests are not encoded. + * + * @param data The data to be encoded. + * @return The encoded data. + */ + byte[] urlEncode(byte[] data); + + /** + * This method can be used to Base64-decode the specified data. + * + * @param data The data to be decoded. + * @return The decoded data. + */ + byte[] base64Decode(String data); + + /** + * This method can be used to Base64-decode the specified data. + * + * @param data The data to be decoded. + * @return The decoded data. + */ + byte[] base64Decode(byte[] data); + + /** + * This method can be used to Base64-encode the specified data. + * + * @param data The data to be encoded. + * @return The encoded data. + */ + String base64Encode(String data); + + /** + * This method can be used to Base64-encode the specified data. + * + * @param data The data to be encoded. + * @return The encoded data. + */ + String base64Encode(byte[] data); + + /** + * This method can be used to convert data from String form into an array of + * bytes. The conversion does not reflect any particular character set, and + * a character with the hex representation 0xWXYZ will always be converted + * into a byte with the representation 0xYZ. It performs the opposite + * conversion to the method bytesToString(), and byte-based + * data that is converted to a String and back again using these two methods + * is guaranteed to retain its integrity (which may not be the case with + * conversions that reflect a given character set). + * + * @param data The data to be converted. + * @return The converted data. + */ + byte[] stringToBytes(String data); + + /** + * This method can be used to convert data from an array of bytes into + * String form. The conversion does not reflect any particular character + * set, and a byte with the representation 0xYZ will always be converted + * into a character with the hex representation 0x00YZ. It performs the + * opposite conversion to the method stringToBytes(), and + * byte-based data that is converted to a String and back again using these + * two methods is guaranteed to retain its integrity (which may not be the + * case with conversions that reflect a given character set). + * + * @param data The data to be converted. + * @return The converted data. + */ + String bytesToString(byte[] data); + + /** + * This method searches a piece of data for the first occurrence of a + * specified pattern. It works on byte-based data in a way that is similar + * to the way the native Java method String.indexOf() works on + * String-based data. + * + * @param data The data to be searched. + * @param pattern The pattern to be searched for. + * @param caseSensitive Flags whether or not the search is case-sensitive. + * @param from The offset within data where the search should + * begin. + * @param to The offset within data where the search should + * end. + * @return The offset of the first occurrence of the pattern within the + * specified bounds, or -1 if no match is found. + */ + int indexOf(byte[] data, + byte[] pattern, + boolean caseSensitive, + int from, + int to); + + /** + * This method builds an HTTP message containing the specified headers and + * message body. If applicable, the Content-Length header will be added or + * updated, based on the length of the body. + * + * @param headers A list of headers to include in the message. + * @param body The body of the message, of null if the message + * has an empty body. + * @return The resulting full HTTP message. + */ + byte[] buildHttpMessage(List headers, byte[] body); + + /** + * This method creates a GET request to the specified URL. The headers used + * in the request are determined by the Request headers settings as + * configured in Burp Spider's options. + * + * @param url The URL to which the request should be made. + * @return A request to the specified URL. + */ + byte[] buildHttpRequest(URL url); + + /** + * This method adds a new parameter to an HTTP request, and if appropriate + * updates the Content-Length header. + * + * @param request The request to which the parameter should be added. + * @param parameter An IParameter object containing details of + * the parameter to be added. Supported parameter types are: + * PARAM_URL, PARAM_BODY and + * PARAM_COOKIE. + * @return A new HTTP request with the new parameter added. + */ + byte[] addParameter(byte[] request, IParameter parameter); + + /** + * This method removes a parameter from an HTTP request, and if appropriate + * updates the Content-Length header. + * + * @param request The request from which the parameter should be removed. + * @param parameter An IParameter object containing details of + * the parameter to be removed. Supported parameter types are: + * PARAM_URL, PARAM_BODY and + * PARAM_COOKIE. + * @return A new HTTP request with the parameter removed. + */ + byte[] removeParameter(byte[] request, IParameter parameter); + + /** + * This method updates the value of a parameter within an HTTP request, and + * if appropriate updates the Content-Length header. Note: This + * method can only be used to update the value of an existing parameter of a + * specified type. If you need to change the type of an existing parameter, + * you should first call removeParameter() to remove the + * parameter with the old type, and then call addParameter() to + * add a parameter with the new type. + * + * @param request The request containing the parameter to be updated. + * @param parameter An IParameter object containing details of + * the parameter to be updated. Supported parameter types are: + * PARAM_URL, PARAM_BODY and + * PARAM_COOKIE. + * @return A new HTTP request with the parameter updated. + */ + byte[] updateParameter(byte[] request, IParameter parameter); + + /** + * This method can be used to toggle a request's method between GET and + * POST. Parameters are relocated between the URL query string and message + * body as required, and the Content-Length header is created or removed as + * applicable. + * + * @param request The HTTP request whose method should be toggled. + * @return A new HTTP request using the toggled method. + */ + byte[] toggleRequestMethod(byte[] request); + + /** + * This method constructs an IHttpService object based on the + * details provided. + * + * @param host The HTTP service host. + * @param port The HTTP service port. + * @param protocol The HTTP service protocol. + * @return An IHttpService object based on the details + * provided. + */ + IHttpService buildHttpService(String host, int port, String protocol); + + /** + * This method constructs an IHttpService object based on the + * details provided. + * + * @param host The HTTP service host. + * @param port The HTTP service port. + * @param useHttps Flags whether the HTTP service protocol is HTTPS or HTTP. + * @return An IHttpService object based on the details + * provided. + */ + IHttpService buildHttpService(String host, int port, boolean useHttps); + + /** + * This method constructs an IParameter object based on the + * details provided. + * + * @param name The parameter name. + * @param value The parameter value. + * @param type The parameter type, as defined in the IParameter + * interface. + * @return An IParameter object based on the details provided. + */ + IParameter buildParameter(String name, String value, byte type); + + /** + * This method constructs an IScannerInsertionPoint object + * based on the details provided. It can be used to quickly create a simple + * insertion point based on a fixed payload location within a base request. + * + * @param insertionPointName The name of the insertion point. + * @param baseRequest The request from which to build scan requests. + * @param from The offset of the start of the payload location. + * @param to The offset of the end of the payload location. + * @return An IScannerInsertionPoint object based on the + * details provided. + */ + IScannerInsertionPoint makeScannerInsertionPoint( + String insertionPointName, + byte[] baseRequest, + int from, + int to); + + /** + * This method analyzes one or more responses to identify variations in a + * number of attributes and returns an IResponseVariations + * object that can be queried to obtain details of the variations. + * + * @param responses The responses to analyze. + * @return An IResponseVariations object representing the + * variations in the responses. + */ + IResponseVariations analyzeResponseVariations(byte[]... responses); + + /** + * This method analyzes one or more responses to identify the number of + * occurrences of the specified keywords and returns an + * IResponseKeywords object that can be queried to obtain + * details of the number of occurrences of each keyword. + * + * @param keywords The keywords to look for. + * @param responses The responses to analyze. + * @return An IResponseKeywords object representing the counts + * of the keywords appearing in the responses. + */ + IResponseKeywords analyzeResponseKeywords(List keywords, byte[]... responses); +} diff --git a/src/burp/IExtensionStateListener.java b/src/burp/IExtensionStateListener.java new file mode 100644 index 0000000..dad60ee --- /dev/null +++ b/src/burp/IExtensionStateListener.java @@ -0,0 +1,27 @@ +package burp; + +/* + * @(#)IExtensionStateListener.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +/** + * Extensions can implement this interface and then call + * IBurpExtenderCallbacks.registerExtensionStateListener() to + * register an extension state listener. The listener will be notified of + * changes to the extension's state. Note: Any extensions that start + * background threads or open system resources (such as files or database + * connections) should register a listener and terminate threads / close + * resources when the extension is unloaded. + */ +public interface IExtensionStateListener +{ + /** + * This method is called when the extension is unloaded. + */ + void extensionUnloaded(); +} diff --git a/src/burp/IHttpListener.java b/src/burp/IHttpListener.java new file mode 100644 index 0000000..b781c12 --- /dev/null +++ b/src/burp/IHttpListener.java @@ -0,0 +1,37 @@ +package burp; + +/* + * @(#)IHttpListener.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +/** + * Extensions can implement this interface and then call + * IBurpExtenderCallbacks.registerHttpListener() to register an + * HTTP listener. The listener will be notified of requests and responses made + * by any Burp tool. Extensions can perform custom analysis or modification of + * these messages by registering an HTTP listener. + */ +public interface IHttpListener +{ + /** + * This method is invoked when an HTTP request is about to be issued, and + * when an HTTP response has been received. + * + * @param toolFlag A flag indicating the Burp tool that issued the request. + * Burp tool flags are defined in the + * IBurpExtenderCallbacks interface. + * @param messageIsRequest Flags whether the method is being invoked for a + * request or response. + * @param messageInfo Details of the request / response to be processed. + * Extensions can call the setter methods on this object to update the + * current message and so modify Burp's behavior. + */ + void processHttpMessage(int toolFlag, + boolean messageIsRequest, + IHttpRequestResponse messageInfo); +} diff --git a/src/burp/IHttpRequestResponse.java b/src/burp/IHttpRequestResponse.java new file mode 100644 index 0000000..7a239de --- /dev/null +++ b/src/burp/IHttpRequestResponse.java @@ -0,0 +1,102 @@ +package burp; + +/* + * @(#)IHttpRequestResponse.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +/** + * This interface is used to retrieve and update details about HTTP messages. + * + * Note: The setter methods generally can only be used before the message + * has been processed, and not in read-only contexts. The getter methods + * relating to response details can only be used after the request has been + * issued. + */ +public interface IHttpRequestResponse +{ + /** + * This method is used to retrieve the request message. + * + * @return The request message. + */ + byte[] getRequest(); + + /** + * This method is used to update the request message. + * + * @param message The new request message. + */ + void setRequest(byte[] message); + + /** + * This method is used to retrieve the response message. + * + * @return The response message. + */ + byte[] getResponse(); + + /** + * This method is used to update the response message. + * + * @param message The new response message. + */ + void setResponse(byte[] message); + + /** + * This method is used to retrieve the user-annotated comment for this item, + * if applicable. + * + * @return The user-annotated comment for this item, or null if none is set. + */ + String getComment(); + + /** + * This method is used to update the user-annotated comment for this item. + * + * @param comment The comment to be assigned to this item. + */ + void setComment(String comment); + + /** + * This method is used to retrieve the user-annotated highlight for this + * item, if applicable. + * + * @return The user-annotated highlight for this item, or null if none is + * set. + */ + String getHighlight(); + + /** + * This method is used to update the user-annotated highlight for this item. + * + * @param color The highlight color to be assigned to this item. Accepted + * values are: red, orange, yellow, green, cyan, blue, pink, magenta, gray, + * or a null String to clear any existing highlight. + */ + void setHighlight(String color); + + /** + * This method is used to retrieve the HTTP service for this request / + * response. + * + * @return An + * IHttpService object containing details of the HTTP service. + */ + IHttpService getHttpService(); + + /** + * This method is used to update the HTTP service for this request / + * response. + * + * @param httpService An + * IHttpService object containing details of the new HTTP + * service. + */ + void setHttpService(IHttpService httpService); + +} diff --git a/src/burp/IHttpRequestResponsePersisted.java b/src/burp/IHttpRequestResponsePersisted.java new file mode 100644 index 0000000..6ba9f6f --- /dev/null +++ b/src/burp/IHttpRequestResponsePersisted.java @@ -0,0 +1,25 @@ +package burp; + +/* + * @(#)IHttpRequestResponsePersisted.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +/** + * This interface is used for an + * IHttpRequestResponse object whose request and response messages + * have been saved to temporary files using + * IBurpExtenderCallbacks.saveBuffersToTempFiles(). + */ +public interface IHttpRequestResponsePersisted extends IHttpRequestResponse +{ + /** + * This method is deprecated and no longer performs any action. + */ + @Deprecated + void deleteTempFiles(); +} diff --git a/src/burp/IHttpRequestResponseWithMarkers.java b/src/burp/IHttpRequestResponseWithMarkers.java new file mode 100644 index 0000000..de06e9b --- /dev/null +++ b/src/burp/IHttpRequestResponseWithMarkers.java @@ -0,0 +1,44 @@ +package burp; + +/* + * @(#)IHttpRequestResponseWithMarkers.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +import java.util.List; + +/** + * This interface is used for an + * IHttpRequestResponse object that has had markers applied. + * Extensions can create instances of this interface using + * IBurpExtenderCallbacks.applyMarkers(), or provide their own + * implementation. Markers are used in various situations, such as specifying + * Intruder payload positions, Scanner insertion points, and highlights in + * Scanner issues. + */ +public interface IHttpRequestResponseWithMarkers extends IHttpRequestResponse +{ + /** + * This method returns the details of the request markers. + * + * @return A list of index pairs representing the offsets of markers for the + * request message. Each item in the list is an int[2] array containing the + * start and end offsets for the marker. The method may return + * null if no request markers are defined. + */ + List getRequestMarkers(); + + /** + * This method returns the details of the response markers. + * + * @return A list of index pairs representing the offsets of markers for the + * response message. Each item in the list is an int[2] array containing the + * start and end offsets for the marker. The method may return + * null if no response markers are defined. + */ + List getResponseMarkers(); +} diff --git a/src/burp/IHttpService.java b/src/burp/IHttpService.java new file mode 100644 index 0000000..d137838 --- /dev/null +++ b/src/burp/IHttpService.java @@ -0,0 +1,39 @@ +package burp; + +/* + * @(#)IHttpService.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +/** + * This interface is used to provide details about an HTTP service, to which + * HTTP requests can be sent. + */ +public interface IHttpService +{ + /** + * This method returns the hostname or IP address for the service. + * + * @return The hostname or IP address for the service. + */ + String getHost(); + + /** + * This method returns the port number for the service. + * + * @return The port number for the service. + */ + int getPort(); + + /** + * This method returns the protocol for the service. + * + * @return The protocol for the service. Expected values are "http" or + * "https". + */ + String getProtocol(); +} diff --git a/src/burp/IInterceptedProxyMessage.java b/src/burp/IInterceptedProxyMessage.java new file mode 100644 index 0000000..cf44f4f --- /dev/null +++ b/src/burp/IInterceptedProxyMessage.java @@ -0,0 +1,116 @@ +package burp; + +/* + * @(#)IInterceptedProxyMessage.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +import java.net.InetAddress; + +/** + * This interface is used to represent an HTTP message that has been intercepted + * by Burp Proxy. Extensions can register an + * IProxyListener to receive details of proxy messages using this + * interface. * + */ +public interface IInterceptedProxyMessage +{ + /** + * This action causes Burp Proxy to follow the current interception rules to + * determine the appropriate action to take for the message. + */ + static final int ACTION_FOLLOW_RULES = 0; + /** + * This action causes Burp Proxy to present the message to the user for + * manual review or modification. + */ + static final int ACTION_DO_INTERCEPT = 1; + /** + * This action causes Burp Proxy to forward the message to the remote server + * or client, without presenting it to the user. + */ + static final int ACTION_DONT_INTERCEPT = 2; + /** + * This action causes Burp Proxy to drop the message. + */ + static final int ACTION_DROP = 3; + /** + * This action causes Burp Proxy to follow the current interception rules to + * determine the appropriate action to take for the message, and then make a + * second call to processProxyMessage. + */ + static final int ACTION_FOLLOW_RULES_AND_REHOOK = 0x10; + /** + * This action causes Burp Proxy to present the message to the user for + * manual review or modification, and then make a second call to + * processProxyMessage. + */ + static final int ACTION_DO_INTERCEPT_AND_REHOOK = 0x11; + /** + * This action causes Burp Proxy to skip user interception, and then make a + * second call to processProxyMessage. + */ + static final int ACTION_DONT_INTERCEPT_AND_REHOOK = 0x12; + + /** + * This method retrieves a unique reference number for this + * request/response. + * + * @return An identifier that is unique to a single request/response pair. + * Extensions can use this to correlate details of requests and responses + * and perform processing on the response message accordingly. + */ + int getMessageReference(); + + /** + * This method retrieves details of the intercepted message. + * + * @return An IHttpRequestResponse object containing details of + * the intercepted message. + */ + IHttpRequestResponse getMessageInfo(); + + /** + * This method retrieves the currently defined interception action. The + * default action is + * ACTION_FOLLOW_RULES. If multiple proxy listeners are + * registered, then other listeners may already have modified the + * interception action before it reaches the current listener. This method + * can be used to determine whether this has occurred. + * + * @return The currently defined interception action. Possible values are + * defined within this interface. + */ + int getInterceptAction(); + + /** + * This method is used to update the interception action. + * + * @param interceptAction The new interception action. Possible values are + * defined within this interface. + */ + void setInterceptAction(int interceptAction); + + /** + * This method retrieves the name of the Burp Proxy listener that is + * processing the intercepted message. + * + * @return The name of the Burp Proxy listener that is processing the + * intercepted message. The format is the same as that shown in the Proxy + * Listeners UI - for example, "127.0.0.1:8080". + */ + String getListenerInterface(); + + /** + * This method retrieves the client IP address from which the request for + * the intercepted message was received. + * + * @return The client IP address from which the request for the intercepted + * message was received. + */ + InetAddress getClientIpAddress(); +} diff --git a/src/burp/IIntruderAttack.java b/src/burp/IIntruderAttack.java new file mode 100644 index 0000000..8aa6b6b --- /dev/null +++ b/src/burp/IIntruderAttack.java @@ -0,0 +1,31 @@ +package burp; + +/* + * @(#)IIntruderAttack.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +/** + * This interface is used to hold details about an Intruder attack. + */ +public interface IIntruderAttack +{ + /** + * This method is used to retrieve the HTTP service for the attack. + * + * @return The HTTP service for the attack. + */ + IHttpService getHttpService(); + + /** + * This method is used to retrieve the request template for the attack. + * + * @return The request template for the attack. + */ + byte[] getRequestTemplate(); + +} diff --git a/src/burp/IIntruderPayloadGenerator.java b/src/burp/IIntruderPayloadGenerator.java new file mode 100644 index 0000000..7458620 --- /dev/null +++ b/src/burp/IIntruderPayloadGenerator.java @@ -0,0 +1,50 @@ +package burp; + +/* + * @(#)IIntruderPayloadGenerator.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +/** + * This interface is used for custom Intruder payload generators. Extensions + * that have registered an + * IIntruderPayloadGeneratorFactory must return a new instance of + * this interface when required as part of a new Intruder attack. + */ +public interface IIntruderPayloadGenerator +{ + /** + * This method is used by Burp to determine whether the payload generator is + * able to provide any further payloads. + * + * @return Extensions should return + * false when all the available payloads have been used up, + * otherwise + * true. + */ + boolean hasMorePayloads(); + + /** + * This method is used by Burp to obtain the value of the next payload. + * + * @param baseValue The base value of the current payload position. This + * value may be + * null if the concept of a base value is not applicable (e.g. + * in a battering ram attack). + * @return The next payload to use in the attack. + */ + byte[] getNextPayload(byte[] baseValue); + + /** + * This method is used by Burp to reset the state of the payload generator + * so that the next call to + * getNextPayload() returns the first payload again. This + * method will be invoked when an attack uses the same payload generator for + * more than one payload position, for example in a sniper attack. + */ + void reset(); +} diff --git a/src/burp/IIntruderPayloadGeneratorFactory.java b/src/burp/IIntruderPayloadGeneratorFactory.java new file mode 100644 index 0000000..b0ef9f0 --- /dev/null +++ b/src/burp/IIntruderPayloadGeneratorFactory.java @@ -0,0 +1,40 @@ +package burp; + +/* + * @(#)IIntruderPayloadGeneratorFactory.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +/** + * Extensions can implement this interface and then call + * IBurpExtenderCallbacks.registerIntruderPayloadGeneratorFactory() + * to register a factory for custom Intruder payloads. + */ +public interface IIntruderPayloadGeneratorFactory +{ + /** + * This method is used by Burp to obtain the name of the payload generator. + * This will be displayed as an option within the Intruder UI when the user + * selects to use extension-generated payloads. + * + * @return The name of the payload generator. + */ + String getGeneratorName(); + + /** + * This method is used by Burp when the user starts an Intruder attack that + * uses this payload generator. + * + * @param attack An + * IIntruderAttack object that can be queried to obtain details + * about the attack in which the payload generator will be used. + * @return A new instance of + * IIntruderPayloadGenerator that will be used to generate + * payloads for the attack. + */ + IIntruderPayloadGenerator createNewInstance(IIntruderAttack attack); +} diff --git a/src/burp/IIntruderPayloadProcessor.java b/src/burp/IIntruderPayloadProcessor.java new file mode 100644 index 0000000..bf993c9 --- /dev/null +++ b/src/burp/IIntruderPayloadProcessor.java @@ -0,0 +1,45 @@ +package burp; + +/* + * @(#)IIntruderPayloadProcessor.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +/** + * Extensions can implement this interface and then call + * IBurpExtenderCallbacks.registerIntruderPayloadProcessor() to + * register a custom Intruder payload processor. + */ +public interface IIntruderPayloadProcessor +{ + /** + * This method is used by Burp to obtain the name of the payload processor. + * This will be displayed as an option within the Intruder UI when the user + * selects to use an extension-provided payload processor. + * + * @return The name of the payload processor. + */ + String getProcessorName(); + + /** + * This method is invoked by Burp each time the processor should be applied + * to an Intruder payload. + * + * @param currentPayload The value of the payload to be processed. + * @param originalPayload The value of the original payload prior to + * processing by any already-applied processing rules. + * @param baseValue The base value of the payload position, which will be + * replaced with the current payload. + * @return The value of the processed payload. This may be + * null to indicate that the current payload should be skipped, + * and the attack will move directly to the next payload. + */ + byte[] processPayload( + byte[] currentPayload, + byte[] originalPayload, + byte[] baseValue); +} diff --git a/src/burp/IMenuItemHandler.java b/src/burp/IMenuItemHandler.java new file mode 100644 index 0000000..34313df --- /dev/null +++ b/src/burp/IMenuItemHandler.java @@ -0,0 +1,36 @@ +package burp; + +/* + * @(#)IMenuItemHandler.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +/** + * Extensions can implement this interface and then call + * IBurpExtenderCallbacks.registerMenuItem() to register a custom + * context menu item. + * + * @deprecated Use + * IContextMenuFactory instead. + */ +@Deprecated +public interface IMenuItemHandler +{ + /** + * This method is invoked by Burp Suite when the user clicks on a custom + * menu item which the extension has registered with Burp. + * + * @param menuItemCaption The caption of the menu item which was clicked. + * This parameter enables extensions to provide a single implementation + * which handles multiple different menu items. + * @param messageInfo Details of the HTTP message(s) for which the context + * menu was displayed. + */ + void menuItemClicked( + String menuItemCaption, + IHttpRequestResponse[] messageInfo); +} diff --git a/src/burp/IMessageEditor.java b/src/burp/IMessageEditor.java new file mode 100644 index 0000000..3d9b75a --- /dev/null +++ b/src/burp/IMessageEditor.java @@ -0,0 +1,77 @@ +package burp; + +/* + * @(#)IMessageEditor.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +import java.awt.Component; + +/** + * This interface is used to provide extensions with an instance of Burp's HTTP + * message editor, for the extension to use in its own UI. Extensions should + * call IBurpExtenderCallbacks.createMessageEditor() to obtain an + * instance of this interface. + */ +public interface IMessageEditor +{ + + /** + * This method returns the UI component of the editor, for extensions to add + * to their own UI. + * + * @return The UI component of the editor. + */ + Component getComponent(); + + /** + * This method is used to display an HTTP message in the editor. + * + * @param message The HTTP message to be displayed. + * @param isRequest Flags whether the message is an HTTP request or + * response. + */ + void setMessage(byte[] message, boolean isRequest); + + /** + * This method is used to retrieve the currently displayed message, which + * may have been modified by the user. + * + * @return The currently displayed HTTP message. + */ + byte[] getMessage(); + + /** + * This method is used to determine whether the current message has been + * modified by the user. + * + * @return An indication of whether the current message has been modified by + * the user since it was first displayed. + */ + boolean isMessageModified(); + + /** + * This method returns the data that is currently selected by the user. + * + * @return The data that is currently selected by the user, or + * null if no selection is made. + */ + byte[] getSelectedData(); + + /** + * This method can be used to retrieve the bounds of the user's selection + * into the displayed message, if applicable. + * + * @return An int[2] array containing the start and end offsets of the + * user's selection within the displayed message. If the user has not made + * any selection in the current message, both offsets indicate the position + * of the caret within the editor. For some editor views, the concept of + * selection within the message does not apply, in which case this method + * returns null. + */ + int[] getSelectionBounds(); +} diff --git a/src/burp/IMessageEditorController.java b/src/burp/IMessageEditorController.java new file mode 100644 index 0000000..df0eb16 --- /dev/null +++ b/src/burp/IMessageEditorController.java @@ -0,0 +1,49 @@ +package burp; + +/* + * @(#)IMessageEditorController.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +/** + * This interface is used by an + * IMessageEditor to obtain details about the currently displayed + * message. Extensions that create instances of Burp's HTTP message editor can + * optionally provide an implementation of + * IMessageEditorController, which the editor will invoke when it + * requires further information about the current message (for example, to send + * it to another Burp tool). Extensions that provide custom editor tabs via an + * IMessageEditorTabFactory will receive a reference to an + * IMessageEditorController object for each tab instance they + * generate, which the tab can invoke if it requires further information about + * the current message. + */ +public interface IMessageEditorController +{ + /** + * This method is used to retrieve the HTTP service for the current message. + * + * @return The HTTP service for the current message. + */ + IHttpService getHttpService(); + + /** + * This method is used to retrieve the HTTP request associated with the + * current message (which may itself be a response). + * + * @return The HTTP request associated with the current message. + */ + byte[] getRequest(); + + /** + * This method is used to retrieve the HTTP response associated with the + * current message (which may itself be a request). + * + * @return The HTTP response associated with the current message. + */ + byte[] getResponse(); +} diff --git a/src/burp/IMessageEditorTab.java b/src/burp/IMessageEditorTab.java new file mode 100644 index 0000000..38965ec --- /dev/null +++ b/src/burp/IMessageEditorTab.java @@ -0,0 +1,103 @@ +package burp; + +/* + * @(#)IMessageEditorTab.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +import java.awt.Component; + +/** + * Extensions that register an + * IMessageEditorTabFactory must return instances of this + * interface, which Burp will use to create custom tabs within its HTTP message + * editors. + */ +public interface IMessageEditorTab +{ + /** + * This method returns the caption that should appear on the custom tab when + * it is displayed. Note: Burp invokes this method once when the tab + * is first generated, and the same caption will be used every time the tab + * is displayed. + * + * @return The caption that should appear on the custom tab when it is + * displayed. + */ + String getTabCaption(); + + /** + * This method returns the component that should be used as the contents of + * the custom tab when it is displayed. Note: Burp invokes this + * method once when the tab is first generated, and the same component will + * be used every time the tab is displayed. + * + * @return The component that should be used as the contents of the custom + * tab when it is displayed. + */ + Component getUiComponent(); + + /** + * The hosting editor will invoke this method before it displays a new HTTP + * message, so that the custom tab can indicate whether it should be enabled + * for that message. + * + * @param content The message that is about to be displayed, or a zero-length + * array if the existing message is to be cleared. + * @param isRequest Indicates whether the message is a request or a + * response. + * @return The method should return + * true if the custom tab is able to handle the specified + * message, and so will be displayed within the editor. Otherwise, the tab + * will be hidden while this message is displayed. + */ + boolean isEnabled(byte[] content, boolean isRequest); + + /** + * The hosting editor will invoke this method to display a new message or to + * clear the existing message. This method will only be called with a new + * message if the tab has already returned + * true to a call to + * isEnabled() with the same message details. + * + * @param content The message that is to be displayed, or + * null if the tab should clear its contents and disable any + * editable controls. + * @param isRequest Indicates whether the message is a request or a + * response. + */ + void setMessage(byte[] content, boolean isRequest); + + /** + * This method returns the currently displayed message. + * + * @return The currently displayed message. + */ + byte[] getMessage(); + + /** + * This method is used to determine whether the currently displayed message + * has been modified by the user. The hosting editor will always call + * getMessage() before calling this method, so any pending + * edits should be completed within + * getMessage(). + * + * @return The method should return + * true if the user has modified the current message since it + * was first displayed. + */ + boolean isModified(); + + /** + * This method is used to retrieve the data that is currently selected by + * the user. + * + * @return The data that is currently selected by the user. This may be + * null if no selection is currently made. + */ + byte[] getSelectedData(); +} diff --git a/src/burp/IMessageEditorTabFactory.java b/src/burp/IMessageEditorTabFactory.java new file mode 100644 index 0000000..6aae96e --- /dev/null +++ b/src/burp/IMessageEditorTabFactory.java @@ -0,0 +1,38 @@ +package burp; + +/* + * @(#)IMessageEditorTabFactory.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +/** + * Extensions can implement this interface and then call + * IBurpExtenderCallbacks.registerMessageEditorTabFactory() to + * register a factory for custom message editor tabs. This allows extensions to + * provide custom rendering or editing of HTTP messages, within Burp's own HTTP + * editor. + */ +public interface IMessageEditorTabFactory +{ + /** + * Burp will call this method once for each HTTP message editor, and the + * factory should provide a new instance of an + * IMessageEditorTab object. + * + * @param controller An + * IMessageEditorController object, which the new tab can query + * to retrieve details about the currently displayed message. This may be + * null for extension-invoked message editors where the + * extension has not provided an editor controller. + * @param editable Indicates whether the hosting editor is editable or + * read-only. + * @return A new + * IMessageEditorTab object for use within the message editor. + */ + IMessageEditorTab createNewInstance(IMessageEditorController controller, + boolean editable); +} diff --git a/src/burp/IParameter.java b/src/burp/IParameter.java new file mode 100644 index 0000000..7651bde --- /dev/null +++ b/src/burp/IParameter.java @@ -0,0 +1,104 @@ +package burp; + +/* + * @(#)IParameter.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +/** + * This interface is used to hold details about an HTTP request parameter. + */ +public interface IParameter +{ + /** + * Used to indicate a parameter within the URL query string. + */ + static final byte PARAM_URL = 0; + /** + * Used to indicate a parameter within the message body. + */ + static final byte PARAM_BODY = 1; + /** + * Used to indicate an HTTP cookie. + */ + static final byte PARAM_COOKIE = 2; + /** + * Used to indicate an item of data within an XML structure. + */ + static final byte PARAM_XML = 3; + /** + * Used to indicate the value of a tag attribute within an XML structure. + */ + static final byte PARAM_XML_ATTR = 4; + /** + * Used to indicate the value of a parameter attribute within a multi-part + * message body (such as the name of an uploaded file). + */ + static final byte PARAM_MULTIPART_ATTR = 5; + /** + * Used to indicate an item of data within a JSON structure. + */ + static final byte PARAM_JSON = 6; + + /** + * This method is used to retrieve the parameter type. + * + * @return The parameter type. The available types are defined within this + * interface. + */ + byte getType(); + + /** + * This method is used to retrieve the parameter name. + * + * @return The parameter name. + */ + String getName(); + + /** + * This method is used to retrieve the parameter value. + * + * @return The parameter value. + */ + String getValue(); + + /** + * This method is used to retrieve the start offset of the parameter name + * within the HTTP request. + * + * @return The start offset of the parameter name within the HTTP request, + * or -1 if the parameter is not associated with a specific request. + */ + int getNameStart(); + + /** + * This method is used to retrieve the end offset of the parameter name + * within the HTTP request. + * + * @return The end offset of the parameter name within the HTTP request, or + * -1 if the parameter is not associated with a specific request. + */ + int getNameEnd(); + + /** + * This method is used to retrieve the start offset of the parameter value + * within the HTTP request. + * + * @return The start offset of the parameter value within the HTTP request, + * or -1 if the parameter is not associated with a specific request. + */ + int getValueStart(); + + /** + * This method is used to retrieve the end offset of the parameter value + * within the HTTP request. + * + * @return The end offset of the parameter value within the HTTP request, or + * -1 if the parameter is not associated with a specific request. + */ + int getValueEnd(); +} diff --git a/src/burp/IProxyListener.java b/src/burp/IProxyListener.java new file mode 100644 index 0000000..e8fb903 --- /dev/null +++ b/src/burp/IProxyListener.java @@ -0,0 +1,37 @@ +package burp; + +/* + * @(#)IProxyListener.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +/** + * Extensions can implement this interface and then call + * IBurpExtenderCallbacks.registerProxyListener() to register a + * Proxy listener. The listener will be notified of requests and responses being + * processed by the Proxy tool. Extensions can perform custom analysis or + * modification of these messages, and control in-UI message interception, by + * registering a proxy listener. + */ +public interface IProxyListener +{ + /** + * This method is invoked when an HTTP message is being processed by the + * Proxy. + * + * @param messageIsRequest Indicates whether the HTTP message is a request + * or a response. + * @param message An + * IInterceptedProxyMessage object that extensions can use to + * query and update details of the message, and control whether the message + * should be intercepted and displayed to the user for manual review or + * modification. + */ + void processProxyMessage( + boolean messageIsRequest, + IInterceptedProxyMessage message); +} diff --git a/src/burp/IRequestInfo.java b/src/burp/IRequestInfo.java new file mode 100644 index 0000000..9737e1f --- /dev/null +++ b/src/burp/IRequestInfo.java @@ -0,0 +1,95 @@ +package burp; + +/* + * @(#)IRequestInfo.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +import java.net.URL; +import java.util.List; + +/** + * This interface is used to retrieve key details about an HTTP request. + * Extensions can obtain an + * IRequestInfo object for a given request by calling + * IExtensionHelpers.analyzeRequest(). + */ +public interface IRequestInfo +{ + /** + * Used to indicate that there is no content. + */ + static final byte CONTENT_TYPE_NONE = 0; + /** + * Used to indicate URL-encoded content. + */ + static final byte CONTENT_TYPE_URL_ENCODED = 1; + /** + * Used to indicate multi-part content. + */ + static final byte CONTENT_TYPE_MULTIPART = 2; + /** + * Used to indicate XML content. + */ + static final byte CONTENT_TYPE_XML = 3; + /** + * Used to indicate JSON content. + */ + static final byte CONTENT_TYPE_JSON = 4; + /** + * Used to indicate AMF content. + */ + static final byte CONTENT_TYPE_AMF = 5; + /** + * Used to indicate unknown content. + */ + static final byte CONTENT_TYPE_UNKNOWN = -1; + + /** + * This method is used to obtain the HTTP method used in the request. + * + * @return The HTTP method used in the request. + */ + String getMethod(); + + /** + * This method is used to obtain the URL in the request. + * + * @return The URL in the request. + */ + URL getUrl(); + + /** + * This method is used to obtain the HTTP headers contained in the request. + * + * @return The HTTP headers contained in the request. + */ + List getHeaders(); + + /** + * This method is used to obtain the parameters contained in the request. + * + * @return The parameters contained in the request. + */ + List getParameters(); + + /** + * This method is used to obtain the offset within the request where the + * message body begins. + * + * @return The offset within the request where the message body begins. + */ + int getBodyOffset(); + + /** + * This method is used to obtain the content type of the message body. + * + * @return An indication of the content type of the message body. Available + * types are defined within this interface. + */ + byte getContentType(); +} diff --git a/src/burp/IResponseInfo.java b/src/burp/IResponseInfo.java new file mode 100644 index 0000000..a887d2f --- /dev/null +++ b/src/burp/IResponseInfo.java @@ -0,0 +1,73 @@ +package burp; + +/* + * @(#)IResponseInfo.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +import java.util.List; + +/** + * This interface is used to retrieve key details about an HTTP response. + * Extensions can obtain an + * IResponseInfo object for a given response by calling + * IExtensionHelpers.analyzeResponse(). + */ +public interface IResponseInfo +{ + /** + * This method is used to obtain the HTTP headers contained in the response. + * + * @return The HTTP headers contained in the response. + */ + List getHeaders(); + + /** + * This method is used to obtain the offset within the response where the + * message body begins. + * + * @return The offset within the response where the message body begins. + */ + int getBodyOffset(); + + /** + * This method is used to obtain the HTTP status code contained in the + * response. + * + * @return The HTTP status code contained in the response. + */ + short getStatusCode(); + + /** + * This method is used to obtain details of the HTTP cookies set in the + * response. + * + * @return A list of ICookie objects representing the cookies + * set in the response, if any. + */ + List getCookies(); + + /** + * This method is used to obtain the MIME type of the response, as stated in + * the HTTP headers. + * + * @return A textual label for the stated MIME type, or an empty String if + * this is not known or recognized. The possible labels are the same as + * those used in the main Burp UI. + */ + String getStatedMimeType(); + + /** + * This method is used to obtain the MIME type of the response, as inferred + * from the contents of the HTTP message body. + * + * @return A textual label for the inferred MIME type, or an empty String if + * this is not known or recognized. The possible labels are the same as + * those used in the main Burp UI. + */ + String getInferredMimeType(); +} diff --git a/src/burp/IResponseKeywords.java b/src/burp/IResponseKeywords.java new file mode 100644 index 0000000..924e9dc --- /dev/null +++ b/src/burp/IResponseKeywords.java @@ -0,0 +1,58 @@ +package burp; + +/* + * @(#)IResponseKeywords.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +import java.util.List; + +/** + * This interface is used to represent the counts of keywords appearing in a + * number of HTTP responses. + */ +public interface IResponseKeywords +{ + + /** + * This method is used to obtain the list of keywords whose counts vary + * between the analyzed responses. + * + * @return The keywords whose counts vary between the analyzed responses. + */ + List getVariantKeywords(); + + /** + * This method is used to obtain the list of keywords whose counts do not + * vary between the analyzed responses. + * + * @return The keywords whose counts do not vary between the analyzed + * responses. + */ + List getInvariantKeywords(); + + /** + * This method is used to obtain the number of occurrences of an individual + * keyword in a response. + * + * @param keyword The keyword whose count will be retrieved. + * @param responseIndex The index of the response. Note responses are + * indexed from zero in the order they were originally supplied to the + * IExtensionHelpers.analyzeResponseKeywords() and + * IResponseKeywords.updateWith() methods. + * @return The number of occurrences of the specified keyword for the + * specified response. + */ + int getKeywordCount(String keyword, int responseIndex); + + /** + * This method is used to update the analysis based on additional responses. + * + * @param responses The new responses to include in the analysis. + */ + void updateWith(byte[]... responses); +} diff --git a/src/burp/IResponseVariations.java b/src/burp/IResponseVariations.java new file mode 100644 index 0000000..39cee40 --- /dev/null +++ b/src/burp/IResponseVariations.java @@ -0,0 +1,62 @@ +package burp; + +/* + * @(#)IResponseVariations.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +import java.util.List; + +/** + * This interface is used to represent variations between a number HTTP + * responses, according to various attributes. + */ +public interface IResponseVariations +{ + + /** + * This method is used to obtain the list of attributes that vary between + * the analyzed responses. + * + * @return The attributes that vary between the analyzed responses. + */ + List getVariantAttributes(); + + /** + * This method is used to obtain the list of attributes that do not vary + * between the analyzed responses. + * + * @return The attributes that do not vary between the analyzed responses. + */ + List getInvariantAttributes(); + + /** + * This method is used to obtain the value of an individual attribute in a + * response. Note that the values of some attributes are intrinsically + * meaningful (e.g. a word count) while the values of others are less so + * (e.g. a checksum of the HTML tag names). + * + * @param attributeName The name of the attribute whose value will be + * retrieved. Extension authors can obtain the list of supported attributes + * by generating an IResponseVariations object for a single + * response and calling + * IResponseVariations.getInvariantAttributes(). + * @param responseIndex The index of the response. Note that responses are + * indexed from zero in the order they were originally supplied to the + * IExtensionHelpers.analyzeResponseVariations() and + * IResponseVariations.updateWith() methods. + * @return The value of the specified attribute for the specified response. + */ + int getAttributeValue(String attributeName, int responseIndex); + + /** + * This method is used to update the analysis based on additional responses. + * + * @param responses The new responses to include in the analysis. + */ + void updateWith(byte[]... responses); +} diff --git a/src/burp/IScanIssue.java b/src/burp/IScanIssue.java new file mode 100644 index 0000000..9529cbb --- /dev/null +++ b/src/burp/IScanIssue.java @@ -0,0 +1,123 @@ +package burp; + +/* + * @(#)IScanIssue.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +/** + * This interface is used to retrieve details of Scanner issues. Extensions can + * obtain details of issues by registering an IScannerListener or + * by calling IBurpExtenderCallbacks.getScanIssues(). Extensions + * can also add custom Scanner issues by registering an + * IScannerCheck or calling + * IBurpExtenderCallbacks.addScanIssue(), and providing their own + * implementations of this interface. Note that issue descriptions and other + * text generated by extensions are subject to an HTML whitelist that allows + * only formatting tags and simple hyperlinks. + */ +public interface IScanIssue +{ + + /** + * This method returns the URL for which the issue was generated. + * + * @return The URL for which the issue was generated. + */ + java.net.URL getUrl(); + + /** + * This method returns the name of the issue type. + * + * @return The name of the issue type (e.g. "SQL injection"). + */ + String getIssueName(); + + /** + * This method returns a numeric identifier of the issue type. See the Burp + * Scanner help documentation for a listing of all the issue types. + * + * @return A numeric identifier of the issue type. + */ + int getIssueType(); + + /** + * This method returns the issue severity level. + * + * @return The issue severity level. Expected values are "High", "Medium", + * "Low", "Information" or "False positive". + * + */ + String getSeverity(); + + /** + * This method returns the issue confidence level. + * + * @return The issue confidence level. Expected values are "Certain", "Firm" + * or "Tentative". + */ + String getConfidence(); + + /** + * This method returns a background description for this type of issue. + * + * @return A background description for this type of issue, or + * null if none applies. A limited set of HTML tags may be + * used. + */ + String getIssueBackground(); + + /** + * This method returns a background description of the remediation for this + * type of issue. + * + * @return A background description of the remediation for this type of + * issue, or null if none applies. A limited set of HTML tags + * may be used. + */ + String getRemediationBackground(); + + /** + * This method returns detailed information about this specific instance of + * the issue. + * + * @return Detailed information about this specific instance of the issue, + * or null if none applies. A limited set of HTML tags may be + * used. + */ + String getIssueDetail(); + + /** + * This method returns detailed information about the remediation for this + * specific instance of the issue. + * + * @return Detailed information about the remediation for this specific + * instance of the issue, or null if none applies. A limited + * set of HTML tags may be used. + */ + String getRemediationDetail(); + + /** + * This method returns the HTTP messages on the basis of which the issue was + * generated. + * + * @return The HTTP messages on the basis of which the issue was generated. + * Note: The items in this array should be instances of + * IHttpRequestResponseWithMarkers if applicable, so that + * details of the relevant portions of the request and response messages are + * available. + */ + IHttpRequestResponse[] getHttpMessages(); + + /** + * This method returns the HTTP service for which the issue was generated. + * + * @return The HTTP service for which the issue was generated. + */ + IHttpService getHttpService(); + +} diff --git a/src/burp/IScanQueueItem.java b/src/burp/IScanQueueItem.java new file mode 100644 index 0000000..47d9f34 --- /dev/null +++ b/src/burp/IScanQueueItem.java @@ -0,0 +1,80 @@ +package burp; + +/* + * @(#)IScanQueueItem.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +/** + * This interface is used to retrieve details of items in the Burp Scanner + * active scan queue. Extensions can obtain references to scan queue items by + * calling + * IBurpExtenderCallbacks.doActiveScan(). + */ +public interface IScanQueueItem +{ + /** + * This method returns a description of the status of the scan queue item. + * + * @return A description of the status of the scan queue item. + */ + String getStatus(); + + /** + * This method returns an indication of the percentage completed for the + * scan queue item. + * + * @return An indication of the percentage completed for the scan queue + * item. + */ + byte getPercentageComplete(); + + /** + * This method returns the number of requests that have been made for the + * scan queue item. + * + * @return The number of requests that have been made for the scan queue + * item. + */ + int getNumRequests(); + + /** + * This method returns the number of network errors that have occurred for + * the scan queue item. + * + * @return The number of network errors that have occurred for the scan + * queue item. + */ + int getNumErrors(); + + /** + * This method returns the number of attack insertion points being used for + * the scan queue item. + * + * @return The number of attack insertion points being used for the scan + * queue item. + */ + int getNumInsertionPoints(); + + /** + * This method allows the scan queue item to be canceled. + */ + void cancel(); + + /** + * This method returns details of the issues generated for the scan queue + * item. Note: different items within the scan queue may contain + * duplicated versions of the same issues - for example, if the same request + * has been scanned multiple times. Duplicated issues are consolidated in + * the main view of scan results. Extensions can register an + * IScannerListener to get details only of unique, newly + * discovered Scanner issues post-consolidation. + * + * @return Details of the issues generated for the scan queue item. + */ + IScanIssue[] getIssues(); +} diff --git a/src/burp/IScannerCheck.java b/src/burp/IScannerCheck.java new file mode 100644 index 0000000..976f3d7 --- /dev/null +++ b/src/burp/IScannerCheck.java @@ -0,0 +1,83 @@ +package burp; + +/* + * @(#)IScannerCheck.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +import java.util.List; + +/** + * Extensions can implement this interface and then call + * IBurpExtenderCallbacks.registerScannerCheck() to register a + * custom Scanner check. When performing scanning, Burp will ask the check to + * perform active or passive scanning on the base request, and report any + * Scanner issues that are identified. + */ +public interface IScannerCheck +{ + + /** + * The Scanner invokes this method for each base request / response that is + * passively scanned. Note: Extensions should only analyze the + * HTTP messages provided during passive scanning, and should not make any + * new HTTP requests of their own. + * + * @param baseRequestResponse The base HTTP request / response that should + * be passively scanned. + * @return A list of IScanIssue objects, or null + * if no issues are identified. + */ + List doPassiveScan(IHttpRequestResponse baseRequestResponse); + + /** + * The Scanner invokes this method for each insertion point that is actively + * scanned. Extensions may issue HTTP requests as required to carry out + * active scanning, and should use the + * IScannerInsertionPoint object provided to build scan + * requests for particular payloads. + * Note: + * Scan checks should submit raw non-encoded payloads to insertion points, + * and the insertion point has responsibility for performing any data + * encoding that is necessary given the nature and location of the insertion + * point. + * + * @param baseRequestResponse The base HTTP request / response that should + * be actively scanned. + * @param insertionPoint An IScannerInsertionPoint object that + * can be queried to obtain details of the insertion point being tested, and + * can be used to build scan requests for particular payloads. + * @return A list of IScanIssue objects, or null + * if no issues are identified. + */ + List doActiveScan( + IHttpRequestResponse baseRequestResponse, + IScannerInsertionPoint insertionPoint); + + /** + * The Scanner invokes this method when the custom Scanner check has + * reported multiple issues for the same URL path. This can arise either + * because there are multiple distinct vulnerabilities, or because the same + * (or a similar) request has been scanned more than once. The custom check + * should determine whether the issues are duplicates. In most cases, where + * a check uses distinct issue names or descriptions for distinct issues, + * the consolidation process will simply be a matter of comparing these + * features for the two issues. + * + * @param existingIssue An issue that was previously reported by this + * Scanner check. + * @param newIssue An issue at the same URL path that has been newly + * reported by this Scanner check. + * @return An indication of which issue(s) should be reported in the main + * Scanner results. The method should return -1 to report the + * existing issue only, 0 to report both issues, and + * 1 to report the new issue only. + */ + int consolidateDuplicateIssues( + IScanIssue existingIssue, + IScanIssue newIssue); +} diff --git a/src/burp/IScannerInsertionPoint.java b/src/burp/IScannerInsertionPoint.java new file mode 100644 index 0000000..3b98369 --- /dev/null +++ b/src/burp/IScannerInsertionPoint.java @@ -0,0 +1,174 @@ +package burp; + +/* + * @(#)IScannerInsertionPoint.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +/** + * This interface is used to define an insertion point for use by active Scanner + * checks. Extensions can obtain instances of this interface by registering an + * IScannerCheck, or can create instances for use by Burp's own + * scan checks by registering an + * IScannerInsertionPointProvider. + */ +public interface IScannerInsertionPoint +{ + + /** + * Used to indicate where the payload is inserted into the value of a URL + * parameter. + */ + static final byte INS_PARAM_URL = 0x00; + /** + * Used to indicate where the payload is inserted into the value of a body + * parameter. + */ + static final byte INS_PARAM_BODY = 0x01; + /** + * Used to indicate where the payload is inserted into the value of an HTTP + * cookie. + */ + static final byte INS_PARAM_COOKIE = 0x02; + /** + * Used to indicate where the payload is inserted into the value of an item + * of data within an XML data structure. + */ + static final byte INS_PARAM_XML = 0x03; + /** + * Used to indicate where the payload is inserted into the value of a tag + * attribute within an XML structure. + */ + static final byte INS_PARAM_XML_ATTR = 0x04; + /** + * Used to indicate where the payload is inserted into the value of a + * parameter attribute within a multi-part message body (such as the name of + * an uploaded file). + */ + static final byte INS_PARAM_MULTIPART_ATTR = 0x05; + /** + * Used to indicate where the payload is inserted into the value of an item + * of data within a JSON structure. + */ + static final byte INS_PARAM_JSON = 0x06; + /** + * Used to indicate where the payload is inserted into the value of an AMF + * parameter. + */ + static final byte INS_PARAM_AMF = 0x07; + /** + * Used to indicate where the payload is inserted into the value of an HTTP + * request header. + */ + static final byte INS_HEADER = 0x20; + /** + * Used to indicate where the payload is inserted into a URL path folder. + */ + static final byte INS_URL_PATH_FOLDER = 0x21; + /** + * Used to indicate where the payload is inserted into a URL path folder. + * This is now deprecated; use INS_URL_PATH_FOLDER instead. + */ + @Deprecated + static final byte INS_URL_PATH_REST = INS_URL_PATH_FOLDER; + /** + * Used to indicate where the payload is inserted into the name of an added + * URL parameter. + */ + static final byte INS_PARAM_NAME_URL = 0x22; + /** + * Used to indicate where the payload is inserted into the name of an added + * body parameter. + */ + static final byte INS_PARAM_NAME_BODY = 0x23; + /** + * Used to indicate where the payload is inserted into the body of the HTTP + * request. + */ + static final byte INS_ENTIRE_BODY = 0x24; + /** + * Used to indicate where the payload is inserted into the URL path + * filename. + */ + static final byte INS_URL_PATH_FILENAME = 0x25; + /** + * Used to indicate where the payload is inserted at a location manually + * configured by the user. + */ + static final byte INS_USER_PROVIDED = 0x40; + /** + * Used to indicate where the insertion point is provided by an + * extension-registered + * IScannerInsertionPointProvider. + */ + static final byte INS_EXTENSION_PROVIDED = 0x41; + /** + * Used to indicate where the payload is inserted at an unknown location + * within the request. + */ + static final byte INS_UNKNOWN = 0x7f; + + /** + * This method returns the name of the insertion point. + * + * @return The name of the insertion point (for example, a description of a + * particular request parameter). + */ + String getInsertionPointName(); + + /** + * This method returns the base value for this insertion point. + * + * @return the base value that appears in this insertion point in the base + * request being scanned, or null if there is no value in the + * base request that corresponds to this insertion point. + */ + String getBaseValue(); + + /** + * This method is used to build a request with the specified payload placed + * into the insertion point. There is no requirement for extension-provided + * insertion points to adjust the Content-Length header in requests if the + * body length has changed, although Burp-provided insertion points will + * always do this and will return a request with a valid Content-Length + * header. + * Note: + * Scan checks should submit raw non-encoded payloads to insertion points, + * and the insertion point has responsibility for performing any data + * encoding that is necessary given the nature and location of the insertion + * point. + * + * @param payload The payload that should be placed into the insertion + * point. + * @return The resulting request. + */ + byte[] buildRequest(byte[] payload); + + /** + * This method is used to determine the offsets of the payload value within + * the request, when it is placed into the insertion point. Scan checks may + * invoke this method when reporting issues, so as to highlight the relevant + * part of the request within the UI. + * + * @param payload The payload that should be placed into the insertion + * point. + * @return An int[2] array containing the start and end offsets of the + * payload within the request, or null if this is not applicable (for + * example, where the insertion point places a payload into a serialized + * data structure, the raw payload may not literally appear anywhere within + * the resulting request). + */ + int[] getPayloadOffsets(byte[] payload); + + /** + * This method returns the type of the insertion point. + * + * @return The type of the insertion point. Available types are defined in + * this interface. + */ + byte getInsertionPointType(); +} diff --git a/src/burp/IScannerInsertionPointProvider.java b/src/burp/IScannerInsertionPointProvider.java new file mode 100644 index 0000000..41472a1 --- /dev/null +++ b/src/burp/IScannerInsertionPointProvider.java @@ -0,0 +1,38 @@ +package burp; + +/* + * @(#)IScannerInsertionPointProvider.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +import java.util.List; + +/** + * Extensions can implement this interface and then call + * IBurpExtenderCallbacks.registerScannerInsertionPointProvider() + * to register a factory for custom Scanner insertion points. + */ +public interface IScannerInsertionPointProvider +{ + /** + * When a request is actively scanned, the Scanner will invoke this method, + * and the provider should provide a list of custom insertion points that + * will be used in the scan. Note: these insertion points are used in + * addition to those that are derived from Burp Scanner's configuration, and + * those provided by any other Burp extensions. + * + * @param baseRequestResponse The base request that will be actively + * scanned. + * @return A list of + * IScannerInsertionPoint objects that should be used in the + * scanning, or + * null if no custom insertion points are applicable for this + * request. + */ + List getInsertionPoints( + IHttpRequestResponse baseRequestResponse); +} diff --git a/src/burp/IScannerListener.java b/src/burp/IScannerListener.java new file mode 100644 index 0000000..0bd51d9 --- /dev/null +++ b/src/burp/IScannerListener.java @@ -0,0 +1,30 @@ +package burp; + +/* + * @(#)IScannerListener.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +/** + * Extensions can implement this interface and then call + * IBurpExtenderCallbacks.registerScannerListener() to register a + * Scanner listener. The listener will be notified of new issues that are + * reported by the Scanner tool. Extensions can perform custom analysis or + * logging of Scanner issues by registering a Scanner listener. + */ +public interface IScannerListener +{ + /** + * This method is invoked when a new issue is added to Burp Scanner's + * results. + * + * @param issue An + * IScanIssue object that the extension can query to obtain + * details about the new issue. + */ + void newScanIssue(IScanIssue issue); +} diff --git a/src/burp/IScopeChangeListener.java b/src/burp/IScopeChangeListener.java new file mode 100644 index 0000000..a289388 --- /dev/null +++ b/src/burp/IScopeChangeListener.java @@ -0,0 +1,25 @@ +package burp; + +/* + * @(#)IScopeChangeListener.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +/** + * Extensions can implement this interface and then call + * IBurpExtenderCallbacks.registerScopeChangeListener() to register + * a scope change listener. The listener will be notified whenever a change + * occurs to Burp's suite-wide target scope. + */ +public interface IScopeChangeListener +{ + /** + * This method is invoked whenever a change occurs to Burp's suite-wide + * target scope. + */ + void scopeChanged(); +} diff --git a/src/burp/ISessionHandlingAction.java b/src/burp/ISessionHandlingAction.java new file mode 100644 index 0000000..c953e22 --- /dev/null +++ b/src/burp/ISessionHandlingAction.java @@ -0,0 +1,51 @@ +package burp; + +/* + * @(#)ISessionHandlingAction.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +/** + * Extensions can implement this interface and then call + * IBurpExtenderCallbacks.registerSessionHandlingAction() to + * register a custom session handling action. Each registered action will be + * available within the session handling rule UI for the user to select as a + * rule action. Users can choose to invoke an action directly in its own right, + * or following execution of a macro. + */ +public interface ISessionHandlingAction +{ + /** + * This method is used by Burp to obtain the name of the session handling + * action. This will be displayed as an option within the session handling + * rule editor when the user selects to execute an extension-provided + * action. + * + * @return The name of the action. + */ + String getActionName(); + + /** + * This method is invoked when the session handling action should be + * executed. This may happen as an action in its own right, or as a + * sub-action following execution of a macro. + * + * @param currentRequest The base request that is currently being processed. + * The action can query this object to obtain details about the base + * request. It can issue additional requests of its own if necessary, and + * can use the setter methods on this object to update the base request. + * @param macroItems If the action is invoked following execution of a + * macro, this parameter contains the result of executing the macro. + * Otherwise, it is + * null. Actions can use the details of the macro items to + * perform custom analysis of the macro to derive values of non-standard + * session handling tokens, etc. + */ + void performAction( + IHttpRequestResponse currentRequest, + IHttpRequestResponse[] macroItems); +} diff --git a/src/burp/ITab.java b/src/burp/ITab.java new file mode 100644 index 0000000..12be08f --- /dev/null +++ b/src/burp/ITab.java @@ -0,0 +1,38 @@ +package burp; + +/* + * @(#)ITab.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +import java.awt.Component; + +/** + * This interface is used to provide Burp with details of a custom tab that will + * be added to Burp's UI, using a method such as + * IBurpExtenderCallbacks.addSuiteTab(). + */ +public interface ITab +{ + /** + * Burp uses this method to obtain the caption that should appear on the + * custom tab when it is displayed. + * + * @return The caption that should appear on the custom tab when it is + * displayed. + */ + String getTabCaption(); + + /** + * Burp uses this method to obtain the component that should be used as the + * contents of the custom tab when it is displayed. + * + * @return The component that should be used as the contents of the custom + * tab when it is displayed. + */ + Component getUiComponent(); +} diff --git a/src/burp/ITempFile.java b/src/burp/ITempFile.java new file mode 100644 index 0000000..d20785c --- /dev/null +++ b/src/burp/ITempFile.java @@ -0,0 +1,33 @@ +package burp; + +/* + * @(#)ITempFile.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +/** + * This interface is used to hold details of a temporary file that has been + * created via a call to + * IBurpExtenderCallbacks.saveToTempFile(). + * + */ +public interface ITempFile +{ + /** + * This method is used to retrieve the contents of the buffer that was saved + * in the temporary file. + * + * @return The contents of the buffer that was saved in the temporary file. + */ + byte[] getBuffer(); + + /** + * This method is deprecated and no longer performs any action. + */ + @Deprecated + void delete(); +} diff --git a/src/burp/ITextEditor.java b/src/burp/ITextEditor.java new file mode 100644 index 0000000..25a979a --- /dev/null +++ b/src/burp/ITextEditor.java @@ -0,0 +1,90 @@ +package burp; + +/* + * @(#)ITextEditor.java + * + * Copyright PortSwigger Ltd. All rights reserved. + * + * This code may be used to extend the functionality of Burp Suite Community Edition + * and Burp Suite Professional, provided that this usage does not violate the + * license terms for those products. + */ +import java.awt.Component; + +/** + * This interface is used to provide extensions with an instance of Burp's raw + * text editor, for the extension to use in its own UI. Extensions should call + * IBurpExtenderCallbacks.createTextEditor() to obtain an instance + * of this interface. + */ +public interface ITextEditor +{ + /** + * This method returns the UI component of the editor, for extensions to add + * to their own UI. + * + * @return The UI component of the editor. + */ + Component getComponent(); + + /** + * This method is used to control whether the editor is currently editable. + * This status can be toggled on and off as required. + * + * @param editable Indicates whether the editor should be currently + * editable. + */ + void setEditable(boolean editable); + + /** + * This method is used to update the currently displayed text in the editor. + * + * @param text The text to be displayed. + */ + void setText(byte[] text); + + /** + * This method is used to retrieve the currently displayed text. + * + * @return The currently displayed text. + */ + byte[] getText(); + + /** + * This method is used to determine whether the user has modified the + * contents of the editor. + * + * @return An indication of whether the user has modified the contents of + * the editor since the last call to + * setText(). + */ + boolean isTextModified(); + + /** + * This method is used to obtain the currently selected text. + * + * @return The currently selected text, or + * null if the user has not made any selection. + */ + byte[] getSelectedText(); + + /** + * This method can be used to retrieve the bounds of the user's selection + * into the displayed text, if applicable. + * + * @return An int[2] array containing the start and end offsets of the + * user's selection within the displayed text. If the user has not made any + * selection in the current message, both offsets indicate the position of + * the caret within the editor. + */ + int[] getSelectionBounds(); + + /** + * This method is used to update the search expression that is shown in the + * search bar below the editor. The editor will automatically highlight any + * regions of the displayed text that match the search expression. + * + * @param expression The search expression. + */ + void setSearchExpression(String expression); +} diff --git a/src/helper/BurpFunctions.java b/src/helper/BurpFunctions.java new file mode 100644 index 0000000..f1e0ed6 --- /dev/null +++ b/src/helper/BurpFunctions.java @@ -0,0 +1,50 @@ +/* + * Burp Suite HTTP Smuggler + * + * Released as open source by NCC Group - https://www.nccgroup.trust/ + * + * Developed by: + * Soroush Dalili (@irsdl) + * + * Project link: https://github.com/nccgroup/BurpSuiteHTTPSmuggler/ + * + * Released under AGPL v3.0 see LICENSE for more information + * + * */ + +package helper; + +import java.io.PrintWriter; + +import burp.IBurpExtenderCallbacks; + +public class BurpFunctions { + public static Object loadExtensionSettingHelper(String name, String type, Object defaultValue,IBurpExtenderCallbacks callbacks, PrintWriter stderr) { + Object value = null; + try { + String temp_value = callbacks.loadExtensionSetting(name); + if(temp_value!=null && !temp_value.equals("")) { + switch(type.toLowerCase()){ + case "int": + case "integer": + value = Integer.valueOf(temp_value); + break; + case "bool": + case "boolean": + value = Boolean.valueOf(temp_value); + break; + default: + value = temp_value; + break; + } + } + }catch(Exception e) { + stderr.println(e.getMessage()); + } + + if(value==null) { + value = defaultValue; + } + return value; + } +} diff --git a/src/helper/HTTPMessage.java b/src/helper/HTTPMessage.java new file mode 100644 index 0000000..3f45d87 --- /dev/null +++ b/src/helper/HTTPMessage.java @@ -0,0 +1,350 @@ +/* + * Burp Suite HTTP Smuggler + * + * Released as open source by NCC Group - https://www.nccgroup.trust/ + * + * Developed by: + * Soroush Dalili (@irsdl) + * + * Project link: https://github.com/nccgroup/BurpSuiteHTTPSmuggler/ + * + * Released under AGPL v3.0 see LICENSE for more information + * + * */ + +package helper; + +import java.awt.Component; +import java.awt.Container; +import java.io.UnsupportedEncodingException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.swing.JCheckBox; +import javax.swing.JOptionPane; + +public class HTTPMessage { + + //private static String LWSP_Regex= "(([\\r\\n]|\\r\\n)[ \\t]+|[ \\t])*"; // https://tools.ietf.org/html/rfc5234 - ToDo -> add support of LWSP when finding header values + + // Reads the Content-Type value from the header - no LWSP support yet! - reads the value before ";", "," or space + public static String findHeaderContentType(String strHeader){ + String contentType=""; + if(!strHeader.equals("")){ + Pattern my_pattern = Pattern.compile("(?im)^content-type:[ \\t]*([^;,\\s]+)"); + Matcher m = my_pattern.matcher(strHeader); + if (m.find()) { + contentType = m.group(1); + } + } + return contentType; + } + + // Reads the Content-Type charset value from the header - no LWSP support yet! no support for double quotes around charset value either! + public static String findCharsetFromHeader(String strHeader, boolean trimSpaces){ + String charset=""; + if(!strHeader.equals("")){ + Pattern my_pattern = Pattern.compile("(?im)^content-type:.*?[ \\t;,]+charset=[ \\t]*([\"]([^\"]+)[\"]|([^;\\s,]+))"); + Matcher m = my_pattern.matcher(strHeader); + if (m.find()) { + charset = m.group(1); + charset = charset.replaceAll("\"", ""); + if (trimSpaces) + charset = charset.trim(); + } + } + return charset; + } + + // Reads the Content-Type boundary value from the header - no LWSP support yet! + public static String findBoundaryFromHeader(String strHeader, boolean trimSpaces){ + String boundary=""; + if(!strHeader.equals("")){ + Pattern my_pattern = Pattern.compile("(?im)^content-type:.*?[ \\t;,]+boundary=[ \\t]*([\"]([^\"]+)[\"]|([^\\s,]+))"); + Matcher m = my_pattern.matcher(strHeader); + if (m.find()) { + boundary = m.group(1); + boundary = boundary.replaceAll("\"", ""); + if (trimSpaces) + boundary = boundary.trim(); + } + } + return boundary; + } + + // Makes a content-type header using provided parameters + // Obviously the ; delimiter can be changed by comma in certain cases but that's not for discussion here! + public static String createContentTypeHeader(String cType, String charset, String boundary, boolean trimSpaces){ + String contentType=""; + if(trimSpaces) { + charset = charset.trim(); + boundary = boundary.trim(); + } + + if(charset.contains(" ")) + charset = "\""+charset+"\""; + if(boundary.contains(" ")) + boundary = "\""+boundary+"\""; + + contentType = cType + "; charset=" + charset; + + if(!boundary.isEmpty()) { + contentType = cType + "; boundary="+boundary + " ; charset=" + charset; + // contentType = cType + "; charset=" + charset + ", boundary="+boundary; // another format + } + + return contentType; + } + + // Reads the Content-Type value from the header - reads the value before ";", "," or space + public static String findHeaderContentType(List headers){ + String contentType=""; + for(String strHeader : headers){ + if(!strHeader.equals("")){ + Pattern my_pattern = Pattern.compile("(?im)^content-type:[ \\t]*([^;, \\s]+)"); + Matcher m = my_pattern.matcher(strHeader); + if (m.find()) { + contentType = m.group(1); + break; + } + } + } + return contentType; + } + + + // Splits header and body of a request or response + public static String[] getHeaderAndBody(byte[] fullMessage,String encoding) throws UnsupportedEncodingException{ + String[] result = {"",""}; + String strFullMessage = ""; + if(fullMessage != null){ + // splitting the message to retrieve the header and the body + strFullMessage = new String(fullMessage,encoding); + if(strFullMessage.contains("\r\n\r\n")) + result = strFullMessage.split("\r\n\r\n",2); + } + return result; + } + + // Splits header and body of a request or response + public static String[] getHeaderAndBody(String fullMessage) { + String[] result = {"",""}; + if(fullMessage != null){ + // splitting the message to retrieve the header and the body + if(fullMessage.contains("\r\n\r\n")) + result = fullMessage.split("\r\n\r\n",2); + } + return result; + } + + + public static List> getQueryString(String fullMessage){ + return getQueryString(fullMessage, "" , ""); + } + public static List> getQueryString(String fullMessage, String delimiter_QS_param){ + return getQueryString(fullMessage, "" , delimiter_QS_param); + } + // gets querystring parameters because burp can't handle special cases such as when we have jsessionid after ; + public static List> getQueryString(String reqMessage, String delimiter_QS, String delimiter_QS_param){ + if (delimiter_QS.isEmpty()) delimiter_QS = "?"; + if (delimiter_QS_param.isEmpty()) delimiter_QS = "&"; + // final object with qs name and its value + List> qs_list = new ArrayList>(); + + // we assume that we are dealing with one HTTP message (not multiple in a pipeline) + String firstline = reqMessage.split("\r\n|\r|\n", 2)[0]; + + // we assume that we are dealing with an standard HTTP message in which there is a space after the last parameter value + String QS = ""; + Pattern pattern = Pattern.compile("\\"+delimiter_QS+"([^ \\s]+)"); + Matcher matcher = pattern.matcher(firstline); + if (matcher.find()) + { + QS = matcher.group(1); + } + + if (!QS.isEmpty()) { + String[] keyValues = QS.split("\\"+delimiter_QS_param); + for(String keyValue:keyValues){ + List keyValueList = new ArrayList(); + String key = keyValue; + String value = ""; + if(keyValue.contains("=")) { + key = keyValue.split("=",2)[0]; + value = keyValue.split("=",2)[1]; + } + keyValueList.add(key); + keyValueList.add(value); + qs_list.add(keyValueList); + } + } + return qs_list; + } + + + public static List> getURLEncodedBodyParams(String strMessage, boolean isBodyOnly){ + return getURLEncodedBodyParams(strMessage, isBodyOnly, ""); + } + // gets URLEncoded POST parameters - it can use different delimiters than & + public static List> getURLEncodedBodyParams(String strMessage, boolean isBodyOnly, String delimiter_urlencoded_body_param){ + if (delimiter_urlencoded_body_param.isEmpty()) delimiter_urlencoded_body_param = "&"; + if(!isBodyOnly) { + strMessage = getHeaderAndBody(strMessage)[1]; + } + // final object with param name and its value + List> param_list = new ArrayList>(); + String[] keyValues = strMessage.split("\\"+delimiter_urlencoded_body_param); + for(String keyValue:keyValues){ + List keyValueList = new ArrayList(); + String key = keyValue; + String value = ""; + if(keyValue.contains("=")) { + key = keyValue.split("=",2)[0]; + value = keyValue.split("=",2)[1]; + } + keyValueList.add(key); + keyValueList.add(value); + param_list.add(keyValueList); + } + return param_list; + } + + + public static String replaceQueryString(String reqMessage, String newQS){ + return replaceQueryString(reqMessage, newQS, ""); + } + // replaces querystring or adds it if empty in a request + public static String replaceQueryString(String reqMessage, String newQS, String delimiter_QS){ + String finalMessage = reqMessage; + if (delimiter_QS.isEmpty()) delimiter_QS = "?"; + // we assume that we are dealing with one HTTP message (not multiple in a pipeline) + String[] splittedRequest = reqMessage.split("\r\n|\r|\n", 2); + String firstline = splittedRequest[0]; + firstline = firstline.trim(); // we don't have spaces before or after the first line if it is standard! + + String QS_pattern = "\\"+delimiter_QS+"[^ \\s]+"; + Pattern pattern = Pattern.compile(QS_pattern); + Matcher matcher = pattern.matcher(firstline); + if(matcher.find()) { + // replacing existing QS + firstline = matcher.replaceAll(delimiter_QS + newQS); + }else { + // adding QS to the request + String HTTP_version_pattern = "([ ]+HTTP/[^ \\s]+)"; + pattern = Pattern.compile(HTTP_version_pattern); + matcher = pattern.matcher(firstline); + if(matcher.find()) { + firstline = matcher.replaceAll(delimiter_QS + newQS + "$1"); + }else { + // HTTP v0.9?! + firstline += delimiter_QS + newQS; + } + + } + finalMessage = firstline + "\r\n" + splittedRequest[1]; + return finalMessage; + } + + // get values of a header even when it is duplicated + public static ArrayList getHeaderValuesByName(List headers, String headername){ + ArrayList result = new ArrayList(); + headername = headername.toLowerCase(); + for(String item:headers){ + if(item.indexOf(":")>=0){ + String[] headerItem = item.split(":",2); + String headerNameLC = headerItem[0].toLowerCase(); + if(headerNameLC.equals(headername)){ + // We have a match + result.add(headerItem[1].trim()); + } + } + } + return result; + } + + // get the first value of a header + public static String getHeaderValueByName(List headers, String headerName){ + String result = ""; + headerName = headerName.toLowerCase(); + for(String item:headers){ + if(item.indexOf(":")>=0){ + String[] headerItem = item.split(":",2); + String headerNameLC = headerItem[0].toLowerCase(); + if(headerNameLC.equals(headerName)){ + // We have a match + result = headerItem[1].trim(); + break; + } + } + } + return result; + } + + // replace a header value with the new value + public static List replaceHeaderValue(List headers, String headerName, String newHeaderValue, boolean isCaseSensitive) { + List result = new ArrayList(); + if(!isCaseSensitive) + headerName = headerName.toLowerCase(); + int counter = 0; + for(String item:headers){ + if(item.indexOf(":")>=0 && counter != 0){ + String[] headerItem = item.split(":",2); + String headerNameForComp = headerItem[0]; + if(!isCaseSensitive) + headerNameForComp = headerNameForComp.toLowerCase(); + if(headerNameForComp.equals(headerName)){ + // We have a match + headerItem[1] = newHeaderValue; + } + result.add(headerItem[0]+": "+headerItem[1].trim()); + }else{ + result.add(item); + } + counter++; + } + return result; + } + + // replace a header value with the new value + public static String replaceHeaderValue(String strHeader, String headerName, String newHeaderValue, boolean isCaseSensitive) { + String result = ""; + String header_pattern_string = "(?im)^("+Pattern.quote(headerName)+":).*$"; + if(isCaseSensitive) { + header_pattern_string = "(?m)^("+Pattern.quote(headerName)+":).*$"; + } + + Pattern header_pattern = Pattern.compile(header_pattern_string); + Matcher m = header_pattern.matcher(strHeader); + if(m.find()) { + // replacing + result = m.replaceAll("$1 " + newHeaderValue); + }else { + // adding + result = addHeader(strHeader, headerName, newHeaderValue); + } + return result; + } + + // add a new header and its value - this is vulnerable to CRLF but that's intentional + public static String addHeader(String strHeader, String newHeaderName, String newHeaderValue) { + return addHeader(strHeader, newHeaderName + ": " +newHeaderValue); + } + + // add a new header - this is vulnerable to CRLF but that's intentional + public static String addHeader(String strHeader, String newHeader) { + String result = ""; + // adding the new header to the second line after the HTTP version! + result = strHeader.replaceFirst("([\r\n]+)", "$1"+newHeader+"$1"); + return result; + } + + // replace a header verb with a new verb + public static String replaceHeaderVerb(String strHeader, String newVerb) { + String result = ""; + result = strHeader.replaceFirst("^[^ \t]+", newVerb); + return result; + } +} diff --git a/src/helper/UIStuff.java b/src/helper/UIStuff.java new file mode 100644 index 0000000..6926b31 --- /dev/null +++ b/src/helper/UIStuff.java @@ -0,0 +1,87 @@ +/* + * Burp Suite HTTP Smuggler + * + * Released as open source by NCC Group - https://www.nccgroup.trust/ + * + * Developed by: + * Soroush Dalili (@irsdl) + * + * Project link: https://github.com/nccgroup/BurpSuiteHTTPSmuggler/ + * + * Released under AGPL v3.0 see LICENSE for more information + * + * */ + +package helper; + +import java.awt.Component; +import java.awt.Container; +import javax.swing.JCheckBox; +import javax.swing.JOptionPane; + +public class UIStuff { + + // Show a message to the user + public static void showMessage(final String strMsg){ + new Thread(new Runnable() + { + @Override + public void run() + { + JOptionPane.showMessageDialog(null, strMsg); + } + }).start(); + + } + + // Show a message to the user + public static void showWarningMessage(final String strMsg){ + new Thread(new Runnable() + { + @Override + public void run() + { + JOptionPane.showMessageDialog(null, strMsg, "Warning", JOptionPane.WARNING_MESSAGE); + } + }).start(); + + } + + // Show a message to the user + public static String showPlainInputMessage(final String strMessage, final String strTitle, final String defaultValue){ + String output = (String)JOptionPane.showInputDialog(null, + strMessage,strTitle,JOptionPane.PLAIN_MESSAGE, null, null, defaultValue); + if(output==null){ + output = defaultValue; + } + return output; + } + + // Common method to ask a multiple question + public static Integer askConfirmMessage(final String strTitle, final String strQuestion, String[] msgOptions){ + final Object[] options = msgOptions; + final int[] choice = new int[1]; + choice[0] = 0; + choice[0] = JOptionPane.showOptionDialog(null, + strQuestion, + strTitle, + JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE, + null, + options, + options[0]); + return choice[0]; + } + + // to update the JCheckbox background colour after using the customizeUiComponent() method + public static void updateJCheckBoxBackground(Container c) { + Component[] components = c.getComponents(); + for(Component com : components) { + if(com instanceof JCheckBox) { + com.setBackground(c.getBackground()); + } else if(com instanceof Container) { + updateJCheckBoxBackground((Container) com); + } + } + } +} diff --git a/src/helper/Utilities.java b/src/helper/Utilities.java new file mode 100644 index 0000000..2c7d682 --- /dev/null +++ b/src/helper/Utilities.java @@ -0,0 +1,101 @@ +/* + * Burp Suite HTTP Smuggler + * + * Released as open source by NCC Group - https://www.nccgroup.trust/ + * + * Developed by: + * Soroush Dalili (@irsdl) + * + * Project link: https://github.com/nccgroup/BurpSuiteHTTPSmuggler/ + * + * Released under AGPL v3.0 see LICENSE for more information + * + * */ + +package helper; + +public class Utilities { + private static final char[] hexChar = { + '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' + }; + + public static String URLEncodeAll(String input) { + return URLEncode(input, ""); + } + + public static String URLEncodeSpecial(String input, String specialChars) { + if (specialChars.isEmpty()) specialChars = "!#$&'()*+,/:;=?@[] \"%-.<>\\^_`{|}~"; + return URLEncode(input, specialChars); + } + + public static String URLEncodeSpecial(String input) { + return URLEncodeSpecial(input, ""); + } + + public static String URLEncode(String input, String specialChars) { + // idea from https://codereview.stackexchange.com/questions/102591/efficient-url-escape-percent-encoding + if (input == null || input.isEmpty()) { + return input; + } + StringBuilder result = new StringBuilder(input); + for (int i = input.length() - 1; i >= 0; i--) { + if(specialChars.isEmpty()) { + result.replace(i, i + 1, "%" + String.format("%2s",Integer.toHexString(input.charAt(i))).replace(' ', '0').toUpperCase()); + }else if(specialChars.indexOf(input.charAt(i)) != -1) { + result.replace(i, i + 1, "%" + String.format("%2s",Integer.toHexString(input.charAt(i)).replace(' ', '0').toUpperCase())); + } + } + return result.toString(); + } + + public static String URLEncodeAllBytes(byte[] input) { + // idea from https://codereview.stackexchange.com/questions/102591/efficient-url-escape-percent-encoding + if (input == null) { + return ""; + } + StringBuilder result = new StringBuilder(); + for (byte b: input) { + result.append("%" + String.format("%02x", b).toUpperCase()); + } + return result.toString(); + } + + // https://docs.oracle.com/javase/tutorial/i18n/text/examples/UnicodeFormatter.java + static public String byteToHex(byte b) { + // Returns hex String representation of byte b + char[] array = { hexChar[(b >> 4) & 0x0f], hexChar[b & 0x0f] }; + return new String(array); + } + + // https://docs.oracle.com/javase/tutorial/i18n/text/examples/UnicodeFormatter.java + static public String charToHex(char c) { + // Returns hex String representation of char c + byte hi = (byte) (c >>> 8); + byte lo = (byte) (c & 0xff); + return byteToHex(hi) + byteToHex(lo); + } + + + // http://www.xinotes.net/notes/note/812/ + static public String unicodeEscape(String s, boolean encodeAll, boolean isURL) { + StringBuilder sb = new StringBuilder(); + String escapePrefix = "\\u"; + if(isURL) escapePrefix = "%u"; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if ((c >> 7) > 0 || encodeAll) { + sb.append(escapePrefix); + sb.append(hexChar[(c >> 12) & 0xF]); // append the hex character for the left-most 4-bits + sb.append(hexChar[(c >> 8) & 0xF]); // hex for the second group of 4-bits from the left + sb.append(hexChar[(c >> 4) & 0xF]); // hex for the third group + sb.append(hexChar[c & 0xF]); // hex for the last group, e.g., the right most 4-bits + } + else { + sb.append(c); + } + } + return sb.toString(); + } + + +} diff --git a/src/mutation/HTTPEncodingObject.java b/src/mutation/HTTPEncodingObject.java new file mode 100644 index 0000000..7d6bd17 --- /dev/null +++ b/src/mutation/HTTPEncodingObject.java @@ -0,0 +1,391 @@ +/* + * Burp Suite HTTP Smuggler + * + * Released as open source by NCC Group - https://www.nccgroup.trust/ + * + * Developed by: + * Soroush Dalili (@irsdl) + * + * Project link: https://github.com/nccgroup/BurpSuiteHTTPSmuggler/ + * + * Released under AGPL v3.0 see LICENSE for more information + * + * */ + +package mutation; + +public class HTTPEncodingObject { + boolean preventReEncoding = true; + boolean encodeMicrosoftURLEncode = true; // to encode utf-8 characters to their %uXXXX format + boolean encodeDespiteErrors = false; // this will be ignored if encodeMicrosoftURLEncode=true + boolean addACharToEmptyBody = true; + boolean replaceGETwithPOST = false; + boolean isEncodable_QS = true; + boolean isEncodable_body = true; + boolean isEncodable_QS_delimiter = false; + boolean isEncodable_urlencoded_body_delimiter = false; + boolean isEncodable_QS_equal_sign = false; + boolean isEncodable_urlencoded_body_equal_sign = false; + String delimiter_QS = "?"; + String delimiter_QS_param = "&"; + String QS_equalSign = "="; + String delimiter_urlencoded_body_param = "&"; + String body_param_equalSign = "="; + boolean isURLEncoded_incoming_QS = true; + boolean isURLEncoded_incoming_body = true; // this is not active when it is a multipart message + boolean isURLEncoded_outgoing_QS = true; + boolean isURLEncoded_outgoing_body = true; // this is not active when it is a multipart message + boolean isAllChar_URLEncoded_outgoing_QS = true; // only active when isURLEncoded_outgoing_QS=true to encode all characters rather than just key characters + boolean isAllChar_URLEncoded_outgoing_body = true; // only active when isURLEncoded_outgoing_body=true to encode all characters rather than just key characters + boolean trimSpacesInContentTypeHeaderValues = true; // IIS needs this, Apache does not! + boolean encodeNameValueOnlyMultipart = false; // python django needs this, IIS does not! + String outgoing_request_encoding = "ibm500"; + boolean use_incoming_charset_for_request_encoding = true; + String incoming_request_encoding = "utf-8"; + public String[] normalEncodings = {"UTF-8", "UTF-16", "UTF-32", "ISO-8859-1"}; + + public HTTPEncodingObject(boolean preventReEncoding, boolean encodeMicrosoftURLEncode, boolean encodeDespiteErrors, + boolean addACharToEmptyBody, boolean replaceGETwithPOST, boolean isEncodable_QS, boolean isEncodable_body, + boolean isEncodable_QS_delimiter, boolean isEncodable_urlencoded_body_delimiter, + boolean isEncodable_QS_equal_sign, boolean isEncodable_urlencoded_body_equal_sign, String delimiter_QS, + String delimiter_QS_param, String qS_equalSign, String delimiter_urlencoded_body_param, + String body_param_equalSign, boolean isURLEncoded_incoming_QS, boolean isURLEncoded_incoming_body, + boolean isURLEncoded_outgoing_QS, boolean isURLEncoded_outgoing_body, + boolean isAllChar_URLEncoded_outgoing_QS, boolean isAllChar_URLEncoded_outgoing_body, + boolean trimSpacesInContentTypeHeaderValues, boolean encodeNameValueOnlyMultipart, + String outgoing_request_encoding, boolean use_incoming_charset_for_request_encoding, + String incoming_request_encoding, String[] normalEncodings) { + super(); + this.preventReEncoding = preventReEncoding; + this.encodeMicrosoftURLEncode = encodeMicrosoftURLEncode; + this.encodeDespiteErrors = encodeDespiteErrors; + this.addACharToEmptyBody = addACharToEmptyBody; + this.replaceGETwithPOST = replaceGETwithPOST; + this.isEncodable_QS = isEncodable_QS; + this.isEncodable_body = isEncodable_body; + this.isEncodable_QS_delimiter = isEncodable_QS_delimiter; + this.isEncodable_urlencoded_body_delimiter = isEncodable_urlencoded_body_delimiter; + this.isEncodable_QS_equal_sign = isEncodable_QS_equal_sign; + this.isEncodable_urlencoded_body_equal_sign = isEncodable_urlencoded_body_equal_sign; + this.delimiter_QS = delimiter_QS; + this.delimiter_QS_param = delimiter_QS_param; + QS_equalSign = qS_equalSign; + this.delimiter_urlencoded_body_param = delimiter_urlencoded_body_param; + this.body_param_equalSign = body_param_equalSign; + this.isURLEncoded_incoming_QS = isURLEncoded_incoming_QS; + this.isURLEncoded_incoming_body = isURLEncoded_incoming_body; + this.isURLEncoded_outgoing_QS = isURLEncoded_outgoing_QS; + this.isURLEncoded_outgoing_body = isURLEncoded_outgoing_body; + this.isAllChar_URLEncoded_outgoing_QS = isAllChar_URLEncoded_outgoing_QS; + this.isAllChar_URLEncoded_outgoing_body = isAllChar_URLEncoded_outgoing_body; + this.trimSpacesInContentTypeHeaderValues = trimSpacesInContentTypeHeaderValues; + this.encodeNameValueOnlyMultipart = encodeNameValueOnlyMultipart; + this.outgoing_request_encoding = outgoing_request_encoding; + this.use_incoming_charset_for_request_encoding = use_incoming_charset_for_request_encoding; + this.incoming_request_encoding = incoming_request_encoding; + this.normalEncodings = normalEncodings; + } + + public HTTPEncodingObject() { + + } + + public HTTPEncodingObject(String sampleType) { + switch(sampleType.toLowerCase()) { + case "jsp": + case "jsp/tomcat": + this.preventReEncoding = true; + this.encodeMicrosoftURLEncode = false; // to encode utf-8 characters to their %uXXXX format + this.encodeDespiteErrors = false; // this will be ignored if encodeMicrosoftURLEncode=true + this.addACharToEmptyBody = false; + this.replaceGETwithPOST = false; + this.isEncodable_QS = false; // this is for JSP on Tomcat + this.isEncodable_body = true; + this.isEncodable_QS_delimiter = false; + this.isEncodable_urlencoded_body_delimiter = false; + this.isEncodable_QS_equal_sign = false; + this.isEncodable_urlencoded_body_equal_sign = false; + this.delimiter_QS = "?"; + this.delimiter_QS_param = "&"; + this.QS_equalSign = "="; + this.delimiter_urlencoded_body_param = "&"; + this.body_param_equalSign = "="; + this.isURLEncoded_incoming_QS = true; + this.isURLEncoded_incoming_body = true; // this is not active when it is a multipart message + this.isURLEncoded_outgoing_QS = true; + this.isURLEncoded_outgoing_body = true; // this is not active when it is a multipart message + this.isAllChar_URLEncoded_outgoing_QS = true; // only active when isURLEncoded_outgoing_QS=true to encode all characters rather than just key characters + this.isAllChar_URLEncoded_outgoing_body = true; // only active when isURLEncoded_outgoing_body=true to encode all characters rather than just key characters + this.trimSpacesInContentTypeHeaderValues = true; // similar to IIS and ASPX + this.encodeNameValueOnlyMultipart = true; // probably won't work here... needs more testing + this.outgoing_request_encoding = "ibm500"; + this.use_incoming_charset_for_request_encoding = true; + this.incoming_request_encoding = "utf-8"; + break; + + case "py2": + case "py2/django": + this.preventReEncoding = true; + this.encodeMicrosoftURLEncode = false; // to encode utf-8 characters to their %uXXXX format + this.encodeDespiteErrors = false; // this will be ignored if encodeMicrosoftURLEncode=true + this.addACharToEmptyBody = false; + this.replaceGETwithPOST = false; + this.isEncodable_QS = true; + this.isEncodable_body = true; + this.isEncodable_QS_delimiter = false; + this.isEncodable_urlencoded_body_delimiter = false; + this.isEncodable_QS_equal_sign = false; + this.isEncodable_urlencoded_body_equal_sign = false; + this.delimiter_QS = "?"; + this.delimiter_QS_param = "&"; + this.QS_equalSign = "="; + this.delimiter_urlencoded_body_param = "&"; + this.body_param_equalSign = "="; + this.isURLEncoded_incoming_QS = true; + this.isURLEncoded_incoming_body = true; // this is not active when it is a multipart message + this.isURLEncoded_outgoing_QS = true; + this.isURLEncoded_outgoing_body = true; // this is not active when it is a multipart message + this.isAllChar_URLEncoded_outgoing_QS = true; // only active when isURLEncoded_outgoing_QS=true to encode all characters rather than just key characters + this.isAllChar_URLEncoded_outgoing_body = true; // only active when isURLEncoded_outgoing_body=true to encode all characters rather than just key characters + this.trimSpacesInContentTypeHeaderValues = true; // similar to IIS and ASPX + this.encodeNameValueOnlyMultipart = true; // for python + this.outgoing_request_encoding = "ibm500"; + this.use_incoming_charset_for_request_encoding = true; + this.incoming_request_encoding = "utf-8"; + break; + + case "py3": + case "py3/django": + this.preventReEncoding = true; + this.encodeMicrosoftURLEncode = false; // to encode utf-8 characters to their %uXXXX format + this.encodeDespiteErrors = false; // this will be ignored if encodeMicrosoftURLEncode=true + this.addACharToEmptyBody = false; + this.replaceGETwithPOST = false; + this.isEncodable_QS = true; + this.isEncodable_body = true; + this.isEncodable_QS_delimiter = true; + this.isEncodable_urlencoded_body_delimiter = true; + this.isEncodable_QS_equal_sign = true; + this.isEncodable_urlencoded_body_equal_sign = true; + this.delimiter_QS = "?"; + this.delimiter_QS_param = "&"; + this.QS_equalSign = "="; + this.delimiter_urlencoded_body_param = "&"; + this.body_param_equalSign = "="; + this.isURLEncoded_incoming_QS = true; + this.isURLEncoded_incoming_body = true; // this is not active when it is a multipart message + this.isURLEncoded_outgoing_QS = false; + this.isURLEncoded_outgoing_body = false; // this is not active when it is a multipart message + this.isAllChar_URLEncoded_outgoing_QS = false; // only active when isURLEncoded_outgoing_QS=true to encode all characters rather than just key characters + this.isAllChar_URLEncoded_outgoing_body = false; // only active when isURLEncoded_outgoing_body=true to encode all characters rather than just key characters + this.trimSpacesInContentTypeHeaderValues = false; // similar to IIS and ASPX + this.encodeNameValueOnlyMultipart = true; // for python + this.outgoing_request_encoding = "ibm500"; + this.use_incoming_charset_for_request_encoding = true; + this.incoming_request_encoding = "utf-8"; + break; + + case "aspx": + case "aspx/iis": + this.preventReEncoding = true; + this.encodeMicrosoftURLEncode = true; // to encode utf-8 characters to their %uXXXX format + this.encodeDespiteErrors = false; // this will be ignored if encodeMicrosoftURLEncode=true + this.addACharToEmptyBody = true; + this.replaceGETwithPOST = false; + this.isEncodable_QS = true; + this.isEncodable_body = true; + this.isEncodable_QS_delimiter = false; + this.isEncodable_urlencoded_body_delimiter = false; + this.isEncodable_QS_equal_sign = false; + this.isEncodable_urlencoded_body_equal_sign = false; + this.delimiter_QS = "?"; + this.delimiter_QS_param = "&"; + this.QS_equalSign = "="; + this.delimiter_urlencoded_body_param = "&"; + this.body_param_equalSign = "="; + this.isURLEncoded_incoming_QS = true; + this.isURLEncoded_incoming_body = true; // this is not active when it is a multipart message + this.isURLEncoded_outgoing_QS = true; + this.isURLEncoded_outgoing_body = true; // this is not active when it is a multipart message + this.isAllChar_URLEncoded_outgoing_QS = true; // only active when isURLEncoded_outgoing_QS=true to encode all characters rather than just key characters + this.isAllChar_URLEncoded_outgoing_body = true; // only active when isURLEncoded_outgoing_body=true to encode all characters rather than just key characters + this.trimSpacesInContentTypeHeaderValues = true; // IIS needs this, Apache does not! + this.encodeNameValueOnlyMultipart = false; // python django needs this, IIS does not! + this.outgoing_request_encoding = "ibm500"; + this.use_incoming_charset_for_request_encoding = true; + this.incoming_request_encoding = "utf-8"; + break; + } + } + + public synchronized boolean isPreventReEncoding() { + return preventReEncoding; + } + public synchronized void setPreventReEncoding(boolean preventReEncoding) { + this.preventReEncoding = preventReEncoding; + } + public synchronized boolean isEncodeMicrosoftURLEncode() { + return encodeMicrosoftURLEncode; + } + public synchronized void setEncodeMicrosoftURLEncode(boolean encodeMicrosoftURLEncode) { + this.encodeMicrosoftURLEncode = encodeMicrosoftURLEncode; + } + public synchronized boolean isEncodeDespiteErrors() { + return encodeDespiteErrors; + } + public synchronized void setEncodeDespiteErrors(boolean encodeDespiteErrors) { + this.encodeDespiteErrors = encodeDespiteErrors; + } + public synchronized boolean isAddACharToEmptyBody() { + return addACharToEmptyBody; + } + public synchronized void setAddACharToEmptyBody(boolean addACharToEmptyBody) { + this.addACharToEmptyBody = addACharToEmptyBody; + } + public synchronized boolean isReplaceGETwithPOST() { + return replaceGETwithPOST; + } + public synchronized void setReplaceGETwithPOST(boolean replaceGETwithPOST) { + this.replaceGETwithPOST = replaceGETwithPOST; + } + public synchronized boolean isEncodable_QS() { + return isEncodable_QS; + } + public synchronized void setEncodable_QS(boolean isEncodable_QS) { + this.isEncodable_QS = isEncodable_QS; + } + public synchronized boolean isEncodable_body() { + return isEncodable_body; + } + public synchronized void setEncodable_body(boolean isEncodable_body) { + this.isEncodable_body = isEncodable_body; + } + public synchronized boolean isEncodable_QS_delimiter() { + return isEncodable_QS_delimiter; + } + public synchronized void setEncodable_QS_delimiter(boolean isEncodable_QS_delimiter) { + this.isEncodable_QS_delimiter = isEncodable_QS_delimiter; + } + public synchronized boolean isEncodable_urlencoded_body_delimiter() { + return isEncodable_urlencoded_body_delimiter; + } + public synchronized void setEncodable_urlencoded_body_delimiter(boolean isEncodable_urlencoded_body_delimiter) { + this.isEncodable_urlencoded_body_delimiter = isEncodable_urlencoded_body_delimiter; + } + public synchronized boolean isEncodable_QS_equal_sign() { + return isEncodable_QS_equal_sign; + } + public synchronized void setEncodable_QS_equal_sign(boolean isEncodable_QS_equal_sign) { + this.isEncodable_QS_equal_sign = isEncodable_QS_equal_sign; + } + public synchronized boolean isEncodable_urlencoded_body_equal_sign() { + return isEncodable_urlencoded_body_equal_sign; + } + public synchronized void setEncodable_urlencoded_body_equal_sign(boolean isEncodable_urlencoded_body_equal_sign) { + this.isEncodable_urlencoded_body_equal_sign = isEncodable_urlencoded_body_equal_sign; + } + public synchronized String getDelimiter_QS() { + return delimiter_QS; + } + public synchronized void setDelimiter_QS(String delimiter_QS) { + this.delimiter_QS = delimiter_QS; + } + public synchronized String getDelimiter_QS_param() { + return delimiter_QS_param; + } + public synchronized void setDelimiter_QS_param(String delimiter_QS_param) { + this.delimiter_QS_param = delimiter_QS_param; + } + public synchronized String getQS_equalSign() { + return QS_equalSign; + } + public synchronized void setQS_equalSign(String qS_equalSign) { + QS_equalSign = qS_equalSign; + } + public synchronized String getDelimiter_urlencoded_body_param() { + return delimiter_urlencoded_body_param; + } + public synchronized void setDelimiter_urlencoded_body_param(String delimiter_urlencoded_body_param) { + this.delimiter_urlencoded_body_param = delimiter_urlencoded_body_param; + } + public synchronized String getBody_param_equalSign() { + return body_param_equalSign; + } + public synchronized void setBody_param_equalSign(String body_param_equalSign) { + this.body_param_equalSign = body_param_equalSign; + } + public synchronized boolean isURLEncoded_incoming_QS() { + return isURLEncoded_incoming_QS; + } + public synchronized void setURLEncoded_incoming_QS(boolean isURLEncoded_incoming_QS) { + this.isURLEncoded_incoming_QS = isURLEncoded_incoming_QS; + } + public synchronized boolean isURLEncoded_incoming_body() { + return isURLEncoded_incoming_body; + } + public synchronized void setURLEncoded_incoming_body(boolean isURLEncoded_incoming_body) { + this.isURLEncoded_incoming_body = isURLEncoded_incoming_body; + } + public synchronized boolean isURLEncoded_outgoing_QS() { + return isURLEncoded_outgoing_QS; + } + public synchronized void setURLEncoded_outgoing_QS(boolean isURLEncoded_outgoing_QS) { + this.isURLEncoded_outgoing_QS = isURLEncoded_outgoing_QS; + } + public synchronized boolean isAllChar_URLEncoded_outgoing_QS() { + return isAllChar_URLEncoded_outgoing_QS; + } + public synchronized void setAllChar_URLEncoded_outgoing_QS(boolean isAllChar_URLEncoded_outgoing_QS) { + this.isAllChar_URLEncoded_outgoing_QS = isAllChar_URLEncoded_outgoing_QS; + } + public synchronized boolean isURLEncoded_outgoing_body() { + return isURLEncoded_outgoing_body; + } + public synchronized void setURLEncoded_outgoing_body(boolean isURLEncoded_outgoing_body) { + this.isURLEncoded_outgoing_body = isURLEncoded_outgoing_body; + } + public synchronized boolean isAllChar_URLEncoded_outgoing_body() { + return isAllChar_URLEncoded_outgoing_body; + } + public synchronized void setAllChar_URLEncoded_outgoing_body(boolean isAllChar_URLEncoded_outgoing_body) { + this.isAllChar_URLEncoded_outgoing_body = isAllChar_URLEncoded_outgoing_body; + } + public synchronized boolean isTrimSpacesInContentTypeHeaderValues() { + return trimSpacesInContentTypeHeaderValues; + } + public synchronized void setTrimSpacesInContentTypeHeaderValues(boolean trimSpacesInContentTypeHeaderValues) { + this.trimSpacesInContentTypeHeaderValues = trimSpacesInContentTypeHeaderValues; + } + public synchronized boolean isEncodeNameValueOnlyMultipart() { + return encodeNameValueOnlyMultipart; + } + public synchronized void setEncodeNameValueOnlyMultipart(boolean encodeNameValueOnlyMultipart) { + this.encodeNameValueOnlyMultipart = encodeNameValueOnlyMultipart; + } + public synchronized String getOutgoing_request_encoding() { + return outgoing_request_encoding; + } + public synchronized void setOutgoing_request_encoding(String outgoing_request_encoding) { + this.outgoing_request_encoding = outgoing_request_encoding; + } + public synchronized boolean isUse_incoming_charset_for_request_encoding() { + return use_incoming_charset_for_request_encoding; + } + public synchronized void setUse_incoming_charset_for_request_encoding( + boolean use_incoming_charset_for_request_encoding) { + this.use_incoming_charset_for_request_encoding = use_incoming_charset_for_request_encoding; + } + public synchronized String getIncoming_request_encoding() { + return incoming_request_encoding; + } + public synchronized void setIncoming_request_encoding(String incoming_request_encoding) { + this.incoming_request_encoding = incoming_request_encoding; + } + public synchronized String[] getNormalEncodings() { + return normalEncodings; + } + public synchronized void setNormalEncodings(String[] normalEncodings) { + this.normalEncodings = normalEncodings; + } + +} diff --git a/src/mutation/HttpEncoding.java b/src/mutation/HttpEncoding.java new file mode 100644 index 0000000..e38c96b --- /dev/null +++ b/src/mutation/HttpEncoding.java @@ -0,0 +1,382 @@ +/* + * Burp Suite HTTP Smuggler + * + * Released as open source by NCC Group - https://www.nccgroup.trust/ + * + * Developed by: + * Soroush Dalili (@irsdl) + * + * Project link: https://github.com/nccgroup/BurpSuiteHTTPSmuggler/ + * + * Released under AGPL v3.0 see LICENSE for more information + * + * */ + +package mutation; + +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +//import java.nio.ByteBuffer; +//import java.nio.CharBuffer; +import java.nio.charset.Charset; +//import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import burp.IBurpExtenderCallbacks; +import burp.IExtensionHelpers; +//import burp.IRequestInfo; + +public class HttpEncoding { + private boolean isDebug = false; + private IBurpExtenderCallbacks _callbacks; + private IExtensionHelpers _helpers; + private PrintWriter _stdout; + private PrintWriter _stderr; + private HTTPEncodingObject currentHTTPEncodingObject = new HTTPEncodingObject(); + + public HttpEncoding(IBurpExtenderCallbacks callbacks, PrintWriter stdout, PrintWriter stderr, boolean isDebug) { + _callbacks = callbacks; + _helpers = _callbacks.getHelpers(); + _stdout = stdout; + _stderr = stderr; + this.isDebug = isDebug; + } + + private void showDebugMessage(Object object) { + if(isDebug) + _stdout.println(object.toString()); + } + + private String encode(String inputStr, boolean URLEncodeResult, boolean URLEncodeAll) throws Exception { + String result = ""; + if(currentHTTPEncodingObject.encodeMicrosoftURLEncode) { + // We want to encode characters that are ASCII readable and the rest should be encoded using %uXXXX format + StringBuilder sb = new StringBuilder(); + String MSEncodedInputStr = helper.Utilities.unicodeEscape(inputStr, false, true); + String[] MSEncodedInputStrSplitted = MSEncodedInputStr.split("%u"); + int counter = 0; + for(String str:MSEncodedInputStrSplitted) { + if(str.length()>0) { + String tempStr = ""; + if(counter==0) { + tempStr = encode(str, currentHTTPEncodingObject.outgoing_request_encoding); + }else { + sb.append("%u"+str.substring(0, 4)); + if(str.length()>4) + tempStr = encode(str.substring(4), currentHTTPEncodingObject.outgoing_request_encoding); + } + if(tempStr.length()>0) { + if(URLEncodeResult){ + if(URLEncodeAll) { + tempStr = helper.Utilities.URLEncodeAll(tempStr); + }else { + tempStr = _helpers.urlEncode(tempStr); + } + } + sb.append(tempStr); + } + } + counter++; + } + result = sb.toString(); + }else { + if(!currentHTTPEncodingObject.encodeDespiteErrors) { + // Detecting if there is a character that can be encoded to something like \\u[NotZero][NotZero]XX + boolean hasHigherUTF8 = false; + char[] hexChar = { + '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' + }; + for (int i = 0; i < inputStr.length(); i++) { + char c = inputStr.charAt(i); + if ((c >> 7) > 0) { + if(hexChar[(c >> 12) & 0xF]!=0 || hexChar[(c >> 8) & 0xF]!=0) { + hasHigherUTF8 = true; + } + } + } + if(hasHigherUTF8) { + showDebugMessage("Error in encoding , a character will be converted to a \"?\"."); + throw new Exception("\"Message could not be encoded properly - try it with encodeDespiteErrors=True or encodeMicrosoftURLEncode=True if the request does not change anything on the server\""); + } + } + result = encode(inputStr, currentHTTPEncodingObject.outgoing_request_encoding); + if(URLEncodeResult){ + if(URLEncodeAll) { + result = helper.Utilities.URLEncodeAll(result); + }else { + result = _helpers.urlEncode(result); + } + } + } + + return result; + } + + public String encode(String strInput,String outgoingEncoding) { + String result = strInput; + if(outgoingEncoding.isEmpty()) outgoingEncoding = currentHTTPEncodingObject.outgoing_request_encoding; + try{ + showDebugMessage("Encode method using outgoingEncoding: " + outgoingEncoding); + showDebugMessage("strInput: " + strInput); + result = new String(strInput.getBytes(outgoingEncoding), "ISO-8859-1"); + if(outgoingEncoding.startsWith("ibm") || outgoingEncoding.startsWith("cp")) { + // to support ibm.swapLF=true - see https://stackoverflow.com/questions/24633276/encoding-strangeness-with-cp500-lf-nel + result=result.replaceAll("(?im)\\x15", "%"); + } + //long questionMarkCounterAfter = result.chars().filter(ch -> ch == '?').count(); + showDebugMessage("result: " + result); + }catch(UnsupportedEncodingException e){ + _stderr.println(e.getMessage()); + } + return result; + } + + public String encodeHTTPMessage(burp.IHttpRequestResponse iHttpRequestResponse, String incomingEncoding, String outgoingEncoding) throws UnsupportedEncodingException { + return encodeHTTPMessage(iHttpRequestResponse.getRequest(), incomingEncoding, outgoingEncoding); + } + + + public String encodeHTTPMessage(byte[] fullMessageByte,String incomingEncoding, String outgoingEncoding) throws UnsupportedEncodingException { + boolean hasSomethingChanged = false; + if (incomingEncoding.isEmpty()) incomingEncoding = currentHTTPEncodingObject.incoming_request_encoding; + String fullMessage = new String(fullMessageByte,incomingEncoding); + //IRequestInfo requestInfo = _helpers.analyzeRequest(fullMessageByte); + String encodedRequest = ""; + String validContentType = ""; + String[] acceptable_content_types = {"application/x-www-form-urlencoded","multipart/form-data","xml","json"}; + if(outgoingEncoding.isEmpty()) outgoingEncoding = currentHTTPEncodingObject.outgoing_request_encoding; + String[] headerBody = helper.HTTPMessage.getHeaderAndBody(fullMessage); + String header = headerBody[0]; + String body = headerBody[1]; + String content_type = helper.HTTPMessage.findHeaderContentType(header); + String charset = helper.HTTPMessage.findCharsetFromHeader(header, currentHTTPEncodingObject.trimSpacesInContentTypeHeaderValues).toUpperCase(); + + if(currentHTTPEncodingObject.use_incoming_charset_for_request_encoding && Charset.availableCharsets().keySet().contains(charset)) { + currentHTTPEncodingObject.incoming_request_encoding = charset; + } + + if(currentHTTPEncodingObject.preventReEncoding && !charset.isEmpty() && !(Arrays.asList(currentHTTPEncodingObject.normalEncodings).contains(currentHTTPEncodingObject.incoming_request_encoding))) { + showDebugMessage("The request seems to be encoded already using " + charset + " that is not one of " + Arrays.asList(currentHTTPEncodingObject.normalEncodings).toString()); + return ""; + } + + //List> QS_params = new ArrayList>(); + + showDebugMessage("content-type was: " + content_type); + + for (String acceptable_content_type : acceptable_content_types) { + if(content_type.toLowerCase().contains(acceptable_content_type)) { + validContentType = acceptable_content_type; + break; + } + } + + try { + if(currentHTTPEncodingObject.isEncodable_QS) { + List> original_qs_params = helper.HTTPMessage.getQueryString(header, currentHTTPEncodingObject.delimiter_QS, currentHTTPEncodingObject.delimiter_QS_param); + showDebugMessage("QueryString:"); + showDebugMessage(original_qs_params); + String newQueryString = ""; + + if(currentHTTPEncodingObject.isEncodable_QS_delimiter) { + currentHTTPEncodingObject.delimiter_QS = encode(currentHTTPEncodingObject.delimiter_QS, false, false); + } + if (currentHTTPEncodingObject.isEncodable_QS_equal_sign) { + currentHTTPEncodingObject.QS_equalSign = encode(currentHTTPEncodingObject.QS_equalSign, false, false); + } + // the parameters might be url encoded - we need to decode them before mutation! + // in the future, burp converter should be replaced really so this class can be used independently + for(List item:original_qs_params) { + String param_name = item.get(0); + String param_value = item.get(1); + if(currentHTTPEncodingObject.isURLEncoded_incoming_QS){ + // Burp Sutie can't handle utf-8 in URL decoding! e.g.: _helpers.urlDecode("تست") + param_name = new String(_helpers.urlDecode(param_name.getBytes("ISO-8859-1")),currentHTTPEncodingObject.incoming_request_encoding); + //param_name = new String(param_name.getBytes()); + param_value = new String(_helpers.urlDecode(param_value.getBytes("ISO-8859-1")),currentHTTPEncodingObject.incoming_request_encoding); + } + String param_name_encoded = encode(param_name, currentHTTPEncodingObject.isURLEncoded_outgoing_QS, currentHTTPEncodingObject.isAllChar_URLEncoded_outgoing_QS); + String param_value_encoded = encode(param_value, currentHTTPEncodingObject.isURLEncoded_outgoing_QS, currentHTTPEncodingObject.isAllChar_URLEncoded_outgoing_QS); + + if(!newQueryString.isEmpty()) { + newQueryString += currentHTTPEncodingObject.delimiter_QS_param; + } + newQueryString += param_name_encoded + currentHTTPEncodingObject.QS_equalSign + param_value_encoded; + } + if(!newQueryString.isEmpty()) { + hasSomethingChanged = true; + header = helper.HTTPMessage.replaceQueryString(header,newQueryString); + showDebugMessage(header); + } + } + + if(!validContentType.isEmpty()) { + + if(currentHTTPEncodingObject.isEncodable_body && body.length()>0) { + // encoding body + switch(validContentType) { + case "application/x-www-form-urlencoded": + List> original_body_params = helper.HTTPMessage.getURLEncodedBodyParams(body, true, currentHTTPEncodingObject.delimiter_urlencoded_body_param); + showDebugMessage("Body Params:"); + showDebugMessage(original_body_params); + String newBodyParams = ""; + if(currentHTTPEncodingObject.isEncodable_urlencoded_body_delimiter) { + currentHTTPEncodingObject.delimiter_urlencoded_body_param = encode(currentHTTPEncodingObject.delimiter_urlencoded_body_param, false, false); + } + if (currentHTTPEncodingObject.isEncodable_urlencoded_body_equal_sign) { + currentHTTPEncodingObject.body_param_equalSign = encode(currentHTTPEncodingObject.body_param_equalSign, false, false); + } + // the parameters might be url encoded - we need to decode them before mutation! + // in the future, burp converter should be replaced really so this class can be used independently + for(List item:original_body_params) { + String param_name = item.get(0); + String param_value = item.get(1); + if(currentHTTPEncodingObject.isURLEncoded_incoming_body){ + // Burp Sutie can't handle utf-8 in URL decoding! e.g.: _helpers.urlDecode("تست") + param_name = new String(_helpers.urlDecode(param_name.getBytes("ISO-8859-1")),currentHTTPEncodingObject.incoming_request_encoding); + //param_name = new String(param_name.getBytes()); + param_value = new String(_helpers.urlDecode(param_value.getBytes("ISO-8859-1")),currentHTTPEncodingObject.incoming_request_encoding); + } + String param_name_encoded = encode(param_name, currentHTTPEncodingObject.isURLEncoded_outgoing_body, currentHTTPEncodingObject.isAllChar_URLEncoded_outgoing_body); + String param_value_encoded = encode(param_value, currentHTTPEncodingObject.isURLEncoded_outgoing_body, currentHTTPEncodingObject.isAllChar_URLEncoded_outgoing_body); + + if(!newBodyParams.isEmpty()) { + newBodyParams += currentHTTPEncodingObject.delimiter_urlencoded_body_param; + } + newBodyParams += param_name_encoded + currentHTTPEncodingObject.body_param_equalSign + param_value_encoded; + } + + if(!newBodyParams.isEmpty()) { + body = newBodyParams; + hasSomethingChanged = true; + } + + break; + case "multipart/form-data": + // find the boundary value from the content-type header + String boundaryValue = helper.HTTPMessage.findBoundaryFromHeader(header, currentHTTPEncodingObject.trimSpacesInContentTypeHeaderValues); + if(!charset.equals("UTF-8") && !charset.isEmpty()) { + _stdout.println("Charset is "+charset + " in a MultiPart message, message has probably been corrupted already :("); + } + if(!boundaryValue.isEmpty()) { + // we need to parse it... yes and here is the source of another canonical issue :p + StringBuilder finalBody = new StringBuilder(""); + String [] bodyparts; + bodyparts = body.split("[\r]?[\n]?\\-\\-"+Pattern.quote(boundaryValue)+"(\\-\\-)?[\r]?[\n]?"); + if(bodyparts.length > 1) { + for(int i=0; i < bodyparts.length; i++) { + String bodypart = bodyparts[i]; + String [] bodypart_header_body; + if(bodypart.isEmpty() && i < bodyparts.length-1) { + if(finalBody.length()>0) + finalBody.append("\r\n"); + finalBody.append("--"); + finalBody.append(boundaryValue); + }else { + bodypart_header_body = bodypart.split("\r\n\r\n",2); + if(bodypart_header_body.length==2) { + String part_header_encoded = ""; + String part_body_encoded = ""; + if(currentHTTPEncodingObject.encodeNameValueOnlyMultipart) { + // Example: python Django needs to encode the name value rather than the whole header + String nameRegEx = "(Content\\-Disposition: .*name=[\"])([^\"]+)(.*)"; + Pattern pattern_param_name = Pattern.compile(nameRegEx); + Matcher matcher_param_name = pattern_param_name.matcher(bodypart_header_body[0]); + if (matcher_param_name.find()) + { + String param_name = matcher_param_name.group(2); + String param_name_encoded = encode(param_name, false, false); + part_header_encoded = bodypart_header_body[0].replaceAll(nameRegEx, "$1"+param_name_encoded+"$3"); + }else { + part_header_encoded = bodypart_header_body[0]; + } + part_body_encoded = encode(bodypart_header_body[1], false, false); + }else { + // Example: IIS needs the full header being encoded but line by line! + // Assuming CR LF is used - otherwise we need to split it differently :( + String[] partHeaderArray = bodypart_header_body[0].split("[\r\n]"); + for(String partHeaderLine:partHeaderArray) { + if(!partHeaderLine.isEmpty()) { + if(part_header_encoded.isEmpty()) + part_header_encoded = encode(partHeaderLine, false, false); + else + part_header_encoded += "\r\n" + encode(partHeaderLine, false, false); + } + } + //part_header_encoded = encode(bodypart_header_body[0], false, false); + part_body_encoded = encode(bodypart_header_body[1], false, false); + } + if(finalBody.length()>0) + finalBody.append("\r\n"); + finalBody.append(part_header_encoded); + finalBody.append("\r\n\r\n"); + finalBody.append(part_body_encoded); + finalBody.append("\r\n--"); + finalBody.append(boundaryValue); + }else { + String bodypart_encoded = encode(bodypart, false, false); + finalBody.append("--"); + finalBody.append(boundaryValue); + finalBody.append("\r\n"); + finalBody.append(bodypart_encoded); + finalBody.append("\r\n"); + } + } + + } + finalBody.append("--\r\n"); + body = finalBody.toString(); + + content_type = helper.HTTPMessage.createContentTypeHeader(content_type, currentHTTPEncodingObject.outgoing_request_encoding, boundaryValue, currentHTTPEncodingObject.trimSpacesInContentTypeHeaderValues); + hasSomethingChanged = true; + } + } + break; + case "json": + case "xml": + body = encode(body, false, false); + hasSomethingChanged = true; + break; + } + } + }else { + content_type = "application/x-www-form-urlencoded"; + } + + }catch(Exception e) { + _stderr.println(e.getMessage()); + return ""; + } + + if(hasSomethingChanged) { + if(currentHTTPEncodingObject.addACharToEmptyBody && body.length()==0) { + body = " "; + } + + if(currentHTTPEncodingObject.replaceGETwithPOST) { + header = helper.HTTPMessage.replaceHeaderVerb(header, "POST"); + } + + if(!content_type.contains("charset")) { + content_type = helper.HTTPMessage.createContentTypeHeader(content_type, currentHTTPEncodingObject.outgoing_request_encoding, "", currentHTTPEncodingObject.trimSpacesInContentTypeHeaderValues); + } + + header = helper.HTTPMessage.replaceHeaderValue(header, "content-length", String.valueOf(body.length()), false); + header = helper.HTTPMessage.replaceHeaderValue(header, "content-type", content_type, false); + encodedRequest = header+"\r\n\r\n"+body; + return encodedRequest; + }else { + return ""; + } + + } + + public String encodeHTTPMessage(byte[] request, HTTPEncodingObject selectedHTTPEncodingObject) throws UnsupportedEncodingException { + currentHTTPEncodingObject = selectedHTTPEncodingObject; + return encodeHTTPMessage(request, currentHTTPEncodingObject.incoming_request_encoding, currentHTTPEncodingObject.outgoing_request_encoding); + } + + +} diff --git a/src/myui/AboutTab.java b/src/myui/AboutTab.java new file mode 100644 index 0000000..787b86c --- /dev/null +++ b/src/myui/AboutTab.java @@ -0,0 +1,200 @@ +/* + * Burp Suite HTTP Smuggler + * + * Released as open source by NCC Group - https://www.nccgroup.trust/ + * + * Developed by: + * Soroush Dalili (@irsdl) + * + * Project link: https://github.com/nccgroup/BurpSuiteHTTPSmuggler/ + * + * Released under AGPL v3.0 see LICENSE for more information + * + * */ + +package myui; + +import javax.swing.ImageIcon; +import javax.swing.JPanel; + +import burp.IBurpExtenderCallbacks; + +import java.awt.GridBagLayout; +import javax.swing.JLabel; +import java.awt.Desktop; +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.io.PrintWriter; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.swing.JButton; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; + +public class AboutTab extends JPanel { + + private final burp.IBurpExtenderCallbacks callbacks; + private final PrintWriter stdout; + private final PrintWriter stderr; + + /** + * Create the panel. + */ + public AboutTab(IBurpExtenderCallbacks callbacks, PrintWriter stdout, PrintWriter stderr) { + this.callbacks = callbacks; + this.stdout = stdout; + this.stderr = stderr; + + GridBagLayout gridBagLayout = new GridBagLayout(); + gridBagLayout.columnWidths = new int[]{0, 86, 80, 248, 0}; + gridBagLayout.rowHeights = new int[]{0, 38, 0, 0, 0, 43, 0, 0, 0, 0}; + gridBagLayout.columnWeights = new double[]{0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE}; + gridBagLayout.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE}; + setLayout(gridBagLayout); + + ClassLoader cldr = this.getClass().getClassLoader(); + java.net.URL imageURLMain = cldr.getResource("resources/AboutMain.png"); + ImageIcon imageIconMain = new ImageIcon(imageURLMain); + JLabel lblMain = new JLabel("Main"); // to see the label in eclipse design tab! + if("running".equals("running")) // to see the image while running it. + lblMain = new JLabel(imageIconMain); + GridBagConstraints gbc_lblMain = new GridBagConstraints(); + gbc_lblMain.gridheight = 8; + gbc_lblMain.insets = new Insets(0, 0, 0, 5); + gbc_lblMain.gridx = 1; + gbc_lblMain.gridy = 1; + add(lblMain, gbc_lblMain); + + JLabel lblName = new JLabel("Name"); + GridBagConstraints gbc_lblName = new GridBagConstraints(); + gbc_lblName.anchor = GridBagConstraints.SOUTHWEST; + gbc_lblName.insets = new Insets(0, 0, 5, 5); + gbc_lblName.gridx = 2; + gbc_lblName.gridy = 1; + add(lblName, gbc_lblName); + + JLabel lblDynamicname = new JLabel("dynamic_name"); + GridBagConstraints gbc_lblDynamicname = new GridBagConstraints(); + gbc_lblDynamicname.anchor = GridBagConstraints.SOUTHWEST; + gbc_lblDynamicname.insets = new Insets(0, 0, 5, 0); + gbc_lblDynamicname.gridx = 3; + gbc_lblDynamicname.gridy = 1; + add(lblDynamicname, gbc_lblDynamicname); + + JLabel lblVersion = new JLabel("Version"); + GridBagConstraints gbc_lblVersion = new GridBagConstraints(); + gbc_lblVersion.insets = new Insets(0, 0, 5, 5); + gbc_lblVersion.anchor = GridBagConstraints.NORTHWEST; + gbc_lblVersion.gridx = 2; + gbc_lblVersion.gridy = 2; + add(lblVersion, gbc_lblVersion); + + JLabel lblDynamicversion = new JLabel("dynamic_version"); + GridBagConstraints gbc_lblDynamicversion = new GridBagConstraints(); + gbc_lblDynamicversion.anchor = GridBagConstraints.NORTHWEST; + gbc_lblDynamicversion.insets = new Insets(0, 0, 5, 0); + gbc_lblDynamicversion.gridx = 3; + gbc_lblDynamicversion.gridy = 2; + add(lblDynamicversion, gbc_lblDynamicversion); + + JLabel lblSource = new JLabel("Source"); + GridBagConstraints gbc_lblSource = new GridBagConstraints(); + gbc_lblSource.anchor = GridBagConstraints.NORTHWEST; + gbc_lblSource.insets = new Insets(0, 0, 5, 5); + gbc_lblSource.gridx = 2; + gbc_lblSource.gridy = 3; + add(lblSource, gbc_lblSource); + + JLabel lblDynamicsource = new JLabel("dynamic_source"); + GridBagConstraints gbc_lblDynamicsource = new GridBagConstraints(); + gbc_lblDynamicsource.anchor = GridBagConstraints.NORTHWEST; + gbc_lblDynamicsource.insets = new Insets(0, 0, 5, 0); + gbc_lblDynamicsource.gridx = 3; + gbc_lblDynamicsource.gridy = 3; + add(lblDynamicsource, gbc_lblDynamicsource); + + JLabel lblAuthor = new JLabel("Author"); + GridBagConstraints gbc_lblAuthor = new GridBagConstraints(); + gbc_lblAuthor.anchor = GridBagConstraints.NORTHWEST; + gbc_lblAuthor.insets = new Insets(0, 0, 5, 5); + gbc_lblAuthor.gridx = 2; + gbc_lblAuthor.gridy = 4; + add(lblAuthor, gbc_lblAuthor); + + JLabel lblDynamicauthor = new JLabel("dynamic_author"); + GridBagConstraints gbc_lblDynamicauthor = new GridBagConstraints(); + gbc_lblDynamicauthor.insets = new Insets(0, 0, 5, 0); + gbc_lblDynamicauthor.anchor = GridBagConstraints.NORTHWEST; + gbc_lblDynamicauthor.gridx = 3; + gbc_lblDynamicauthor.gridy = 4; + add(lblDynamicauthor, gbc_lblDynamicauthor); + + JLabel label = new JLabel(" "); + GridBagConstraints gbc_label = new GridBagConstraints(); + gbc_label.insets = new Insets(0, 0, 5, 5); + gbc_label.gridx = 2; + gbc_label.gridy = 5; + add(label, gbc_label); + + JButton btnOpenExtensionHome = new JButton("Open extension homepage"); + btnOpenExtensionHome.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + openWebpage("https://github.com/nccgroup/BurpSuiteHTTPSmuggler"); + } + }); + GridBagConstraints gbc_btnOpenExtensionHome = new GridBagConstraints(); + gbc_btnOpenExtensionHome.insets = new Insets(0, 0, 5, 0); + gbc_btnOpenExtensionHome.gridwidth = 2; + gbc_btnOpenExtensionHome.anchor = GridBagConstraints.NORTHWEST; + gbc_btnOpenExtensionHome.gridx = 2; + gbc_btnOpenExtensionHome.gridy = 6; + add(btnOpenExtensionHome, gbc_btnOpenExtensionHome); + + JButton btnReportAnIssue = new JButton("Report a bug/feature!"); + btnReportAnIssue.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent arg0) { + openWebpage("https://github.com/nccgroup/BurpSuiteHTTPSmuggler/issues"); + } + }); + GridBagConstraints gbc_btnReportAnIssue = new GridBagConstraints(); + gbc_btnReportAnIssue.insets = new Insets(0, 0, 5, 0); + gbc_btnReportAnIssue.anchor = GridBagConstraints.WEST; + gbc_btnReportAnIssue.gridwidth = 2; + gbc_btnReportAnIssue.gridx = 2; + gbc_btnReportAnIssue.gridy = 7; + add(btnReportAnIssue, gbc_btnReportAnIssue); + + lblDynamicname.setText("Burp Suite HTTP Smuggler"); + lblDynamicversion.setText(String.valueOf(0.1)); + lblDynamicsource.setText("https://github.com/nccgroup/BurpSuiteHTTPSmuggler/"); + lblDynamicauthor.setText("Soroush Dalili from NCC Group"); + + } + + private static void openWebpage(URI uri) { + Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null; + if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) { + try { + desktop.browse(uri); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + private static void openWebpage(String url) { + try { + openWebpage((new URL(url)).toURI()); + } catch (URISyntaxException e) { + e.printStackTrace(); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + } + + +} diff --git a/src/myui/EditedRequestTab.java b/src/myui/EditedRequestTab.java new file mode 100644 index 0000000..1e78a78 --- /dev/null +++ b/src/myui/EditedRequestTab.java @@ -0,0 +1,106 @@ +/* + * Burp Suite HTTP Smuggler + * + * Released as open source by NCC Group - https://www.nccgroup.trust/ + * + * Developed by: + * Soroush Dalili (@irsdl) + * + * Project link: https://github.com/nccgroup/BurpSuiteHTTPSmuggler/ + * + * Released under AGPL v3.0 see LICENSE for more information + * + * */ + +package myui; + +import java.awt.Component; +import burp.IBurpExtenderCallbacks; +import burp.IMessageEditorController; +import burp.IMessageEditorTab; +import burp.IMessageEditorTabFactory; +import burp.ITextEditor; + +// based on https://github.com/PortSwigger/example-custom-editor-tab/blob/master/java/BurpExtender.java +public class EditedRequestTab implements IMessageEditorTabFactory, IMessageEditorTab{ + private boolean editable; + private ITextEditor txtInput; + //private byte[] changedMessage; + private byte[] originalMessage; + private IBurpExtenderCallbacks _callbacks; + + public EditedRequestTab(IBurpExtenderCallbacks callbacks, boolean editable, byte[] changedMessage, byte[] originalMessage) + { + this._callbacks = callbacks; + this.editable = editable; + //this.changedMessage = changedMessage; + this.originalMessage = originalMessage; + + txtInput = _callbacks.createTextEditor(); + txtInput.setEditable(editable); + txtInput.setText(changedMessage); + + } + + @Override + public String getTabCaption() { + return "HTTP Smuggler Output"; + } + + @Override + public Component getUiComponent() { + return txtInput.getComponent(); + } + + @Override + public boolean isEnabled(byte[] content, boolean isRequest) { + if(isRequest) + return true; + else + return false; + } + + @Override + public void setMessage(byte[] content, boolean isRequest) { + if (content == null) + { + // clear our display + txtInput.setText(null); + txtInput.setEditable(false); + } + else + { + txtInput.setText(content); + txtInput.setEditable(editable); + } + + // remember the displayed content + // currentMessage = content; + + } + + @Override + public byte[] getMessage() { + return originalMessage; + } + + @Override + public boolean isModified() { + // not needed + return txtInput.isTextModified(); + } + + @Override + public byte[] getSelectedData() { + // not needed + return txtInput.getSelectedText(); + } + + @Override + public IMessageEditorTab createNewInstance(IMessageEditorController controller, boolean editable) { + this.editable = editable; + return this; + } + + +} diff --git a/src/myui/EncodingTab.java b/src/myui/EncodingTab.java new file mode 100644 index 0000000..8cb0257 --- /dev/null +++ b/src/myui/EncodingTab.java @@ -0,0 +1,682 @@ +/* + * Burp Suite HTTP Smuggler + * + * Released as open source by NCC Group - https://www.nccgroup.trust/ + * + * Developed by: + * Soroush Dalili (@irsdl) + * + * Project link: https://github.com/nccgroup/BurpSuiteHTTPSmuggler/ + * + * Released under AGPL v3.0 see LICENSE for more information + * + * */ + +package myui; + +import java.awt.BorderLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.io.PrintWriter; + +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JComboBox; +import javax.swing.JPanel; +import javax.swing.JScrollPane; + +import burp.IBurpExtenderCallbacks; +import burp.IExtensionHelpers; +import mutation.HTTPEncodingObject; + +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.border.TitledBorder; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.JTextField; + +public class EncodingTab extends JScrollPane { + private IBurpExtenderCallbacks _callbacks;; + private IExtensionHelpers _helpers; + private PrintWriter _stdout; + private PrintWriter _stderr; + + private enum policyOptions{ + aspx("ASPX/IIS"), + jsp("JSP/TOMCAT"), + py2("Py2/Django"), + py3("Py3/Django"), + custom("custom"); + private String value; + private policyOptions(String value) + { + this.value = value; + } + + public String toString() + { + return this.value; //This will return , # or + + } + } + private JComboBox comboBoxPolicy = new JComboBox(new DefaultComboBoxModel(policyOptions.values())); + private JTextField delimiter_QS; + private JTextField delimiter_QS_param; + private JTextField QS_equalSign; + private JTextField delimiter_urlencoded_body_param; + private JTextField body_param_equalSign; + private JTextField outgoing_request_encoding; + private JTextField incoming_request_encoding; + JCheckBox preventReEncoding = new JCheckBox("Prevent re-encoding"); + + JCheckBox encodeMicrosoftURLEncode = new JCheckBox("Encode using MS URLEncode"); + JCheckBox encodeDespiteErrors = new JCheckBox("Encode despite errors"); + JCheckBox addACharToEmptyBody = new JCheckBox("Add a character to an empty body"); + JCheckBox replaceGETwithPOST = new JCheckBox("Replace GET with POST"); + JCheckBox isEncodable_QS = new JCheckBox("Encode querystring?"); + JCheckBox isEncodable_body = new JCheckBox("Encode body?"); + JCheckBox isEncodable_QS_delimiter = new JCheckBox("Encode querystring delimiter?"); + JCheckBox isEncodable_urlencoded_body_delimiter = new JCheckBox("Encode URL-encoded body delimiter?"); + JCheckBox isEncodable_QS_equal_sign = new JCheckBox("Encode equal sign in querystring?"); + JCheckBox isEncodable_urlencoded_body_equal_sign = new JCheckBox("Encode equal sign in URL-encoded body?"); + JCheckBox isURLEncoded_incoming_QS = new JCheckBox("Is incoming querystring URL-encoded?"); + JCheckBox isURLEncoded_incoming_body = new JCheckBox("Is incoming body URL-encoded?"); + JCheckBox isURLEncoded_outgoing_QS = new JCheckBox("Is outgoing querystring URL-encoded?"); + JCheckBox isURLEncoded_outgoing_body = new JCheckBox("Is outgoing body URL-encoded?"); + JCheckBox isAllChar_URLEncoded_outgoing_QS = new JCheckBox("URL-encoding all characters in querystring?"); + JCheckBox isAllChar_URLEncoded_outgoing_body = new JCheckBox("URL-encoding all characters in POST body"); + JCheckBox trimSpacesInContentTypeHeaderValues = new JCheckBox("Trim spaces from content-type parts"); + JCheckBox encodeNameValueOnlyMultipart = new JCheckBox("Encode name value only in multipart"); + JCheckBox use_incoming_charset_for_request_encoding = new JCheckBox("Use incoming charset for encoding"); + + /** + * Create the panel. + */ + public EncodingTab(IBurpExtenderCallbacks callbacks, PrintWriter stdout, PrintWriter stderr) { + _callbacks = callbacks; + _helpers = _callbacks.getHelpers(); + _stdout = stdout; + _stderr = stderr; + + + JPanel panel = new JPanel(); + setViewportView(panel); + GridBagLayout gbl_panel = new GridBagLayout(); + gbl_panel.columnWidths = new int[]{0, 0, 0, 0, 0}; + gbl_panel.rowHeights = new int[]{0, 0, 0, 0}; + gbl_panel.columnWeights = new double[]{0.0, 0.0, 1.0, 0.0, Double.MIN_VALUE}; + gbl_panel.rowWeights = new double[]{0.0, 1.0, 0.0, Double.MIN_VALUE}; + panel.setLayout(gbl_panel); + + JLabel lblPolicy = new JLabel("Policy:"); + GridBagConstraints gbc_lblPolicy = new GridBagConstraints(); + gbc_lblPolicy.insets = new Insets(0, 0, 5, 5); + gbc_lblPolicy.anchor = GridBagConstraints.EAST; + gbc_lblPolicy.gridx = 1; + gbc_lblPolicy.gridy = 0; + panel.add(lblPolicy, gbc_lblPolicy); + + GridBagConstraints gbc_comboBoxPolicy = new GridBagConstraints(); + gbc_comboBoxPolicy.insets = new Insets(0, 0, 5, 5); + gbc_comboBoxPolicy.anchor = GridBagConstraints.WEST; + gbc_comboBoxPolicy.gridx = 2; + gbc_comboBoxPolicy.gridy = 0; + panel.add(comboBoxPolicy, gbc_comboBoxPolicy); + + JPanel panel_1 = new JPanel(); + panel_1.setBorder(new TitledBorder(null, "Options", TitledBorder.LEFT, TitledBorder.TOP, null, null)); + GridBagConstraints gbc_panel_1 = new GridBagConstraints(); + gbc_panel_1.insets = new Insets(0, 0, 5, 5); + gbc_panel_1.fill = GridBagConstraints.BOTH; + gbc_panel_1.gridx = 2; + gbc_panel_1.gridy = 1; + panel.add(panel_1, gbc_panel_1); + GridBagLayout gbl_panel_1 = new GridBagLayout(); + gbl_panel_1.columnWidths = new int[]{0, 0, 0, 0}; + gbl_panel_1.rowHeights = new int[]{35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35}; + gbl_panel_1.columnWeights = new double[]{0.0, 0.0, 1.0, Double.MIN_VALUE}; + gbl_panel_1.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE}; + panel_1.setLayout(gbl_panel_1); + + + preventReEncoding.setToolTipText("Only encodes when there is no charset or it is one of these: \"UTF-8\", \"UTF-16\", \"UTF-32\", \"ISO-8859-1\""); + GridBagConstraints gbc_preventReEncoding = new GridBagConstraints(); + gbc_preventReEncoding.anchor = GridBagConstraints.WEST; + gbc_preventReEncoding.insets = new Insets(0, 0, 5, 5); + gbc_preventReEncoding.gridx = 0; + gbc_preventReEncoding.gridy = 0; + panel_1.add(preventReEncoding, gbc_preventReEncoding); + + JLabel lblQuerystringDelimiter = new JLabel("Querystring url delimiter:"); + GridBagConstraints gbc_lblQuerystringDelimiter = new GridBagConstraints(); + gbc_lblQuerystringDelimiter.anchor = GridBagConstraints.EAST; + gbc_lblQuerystringDelimiter.insets = new Insets(0, 0, 5, 5); + gbc_lblQuerystringDelimiter.gridx = 1; + gbc_lblQuerystringDelimiter.gridy = 0; + panel_1.add(lblQuerystringDelimiter, gbc_lblQuerystringDelimiter); + + delimiter_QS = new JTextField(); + delimiter_QS.setText("?"); + GridBagConstraints gbc_delimiter_QS = new GridBagConstraints(); + gbc_delimiter_QS.anchor = GridBagConstraints.WEST; + gbc_delimiter_QS.insets = new Insets(0, 0, 5, 0); + gbc_delimiter_QS.gridx = 2; + gbc_delimiter_QS.gridy = 0; + panel_1.add(delimiter_QS, gbc_delimiter_QS); + delimiter_QS.setColumns(10); + + encodeMicrosoftURLEncode.setToolTipText("to encode utf-8 characters to their %uXXXX format"); + GridBagConstraints gbc_encodeMicrosoftURLEncode = new GridBagConstraints(); + gbc_encodeMicrosoftURLEncode.insets = new Insets(0, 0, 5, 5); + gbc_encodeMicrosoftURLEncode.anchor = GridBagConstraints.WEST; + gbc_encodeMicrosoftURLEncode.gridx = 0; + gbc_encodeMicrosoftURLEncode.gridy = 1; + panel_1.add(encodeMicrosoftURLEncode, gbc_encodeMicrosoftURLEncode); + + JLabel lblQuerystringParameterDelimiter = new JLabel("Querystring parameter delimiter:"); + GridBagConstraints gbc_lblQuerystringParameterDelimiter = new GridBagConstraints(); + gbc_lblQuerystringParameterDelimiter.anchor = GridBagConstraints.EAST; + gbc_lblQuerystringParameterDelimiter.insets = new Insets(0, 0, 5, 5); + gbc_lblQuerystringParameterDelimiter.gridx = 1; + gbc_lblQuerystringParameterDelimiter.gridy = 1; + panel_1.add(lblQuerystringParameterDelimiter, gbc_lblQuerystringParameterDelimiter); + + delimiter_QS_param = new JTextField(); + delimiter_QS_param.setText("&"); + GridBagConstraints gbc_delimiter_QS_param = new GridBagConstraints(); + gbc_delimiter_QS_param.anchor = GridBagConstraints.WEST; + gbc_delimiter_QS_param.insets = new Insets(0, 0, 5, 0); + gbc_delimiter_QS_param.gridx = 2; + gbc_delimiter_QS_param.gridy = 1; + panel_1.add(delimiter_QS_param, gbc_delimiter_QS_param); + delimiter_QS_param.setColumns(10); + + encodeDespiteErrors.setToolTipText("Can be dangerous as it can change all the parameters wrongly. This will be ignored if encodeMicrosoftURLEncode=true"); + GridBagConstraints gbc_encodeDespiteErrors = new GridBagConstraints(); + gbc_encodeDespiteErrors.insets = new Insets(0, 0, 5, 5); + gbc_encodeDespiteErrors.anchor = GridBagConstraints.WEST; + gbc_encodeDespiteErrors.gridx = 0; + gbc_encodeDespiteErrors.gridy = 2; + panel_1.add(encodeDespiteErrors, gbc_encodeDespiteErrors); + + JLabel lblQuerystringEqualSign = new JLabel("Querystring equal sign:"); + GridBagConstraints gbc_lblQuerystringEqualSign = new GridBagConstraints(); + gbc_lblQuerystringEqualSign.anchor = GridBagConstraints.EAST; + gbc_lblQuerystringEqualSign.insets = new Insets(0, 0, 5, 5); + gbc_lblQuerystringEqualSign.gridx = 1; + gbc_lblQuerystringEqualSign.gridy = 2; + panel_1.add(lblQuerystringEqualSign, gbc_lblQuerystringEqualSign); + + QS_equalSign = new JTextField(); + QS_equalSign.setText("="); + GridBagConstraints gbc_QS_equalSign = new GridBagConstraints(); + gbc_QS_equalSign.insets = new Insets(0, 0, 5, 0); + gbc_QS_equalSign.fill = GridBagConstraints.HORIZONTAL; + gbc_QS_equalSign.gridx = 2; + gbc_QS_equalSign.gridy = 2; + panel_1.add(QS_equalSign, gbc_QS_equalSign); + QS_equalSign.setColumns(10); + + GridBagConstraints gbc_addACharToEmptyBody = new GridBagConstraints(); + gbc_addACharToEmptyBody.anchor = GridBagConstraints.WEST; + gbc_addACharToEmptyBody.insets = new Insets(0, 0, 5, 5); + gbc_addACharToEmptyBody.gridx = 0; + gbc_addACharToEmptyBody.gridy = 3; + panel_1.add(addACharToEmptyBody, gbc_addACharToEmptyBody); + + JLabel lblUrlencodedBodyParameter = new JLabel("URL-encoded body parameter delimiter:"); + GridBagConstraints gbc_lblUrlencodedBodyParameter = new GridBagConstraints(); + gbc_lblUrlencodedBodyParameter.anchor = GridBagConstraints.EAST; + gbc_lblUrlencodedBodyParameter.insets = new Insets(0, 0, 5, 5); + gbc_lblUrlencodedBodyParameter.gridx = 1; + gbc_lblUrlencodedBodyParameter.gridy = 3; + panel_1.add(lblUrlencodedBodyParameter, gbc_lblUrlencodedBodyParameter); + + delimiter_urlencoded_body_param = new JTextField(); + delimiter_urlencoded_body_param.setText("&"); + GridBagConstraints gbc_delimiter_urlencoded_body_param = new GridBagConstraints(); + gbc_delimiter_urlencoded_body_param.insets = new Insets(0, 0, 5, 0); + gbc_delimiter_urlencoded_body_param.fill = GridBagConstraints.HORIZONTAL; + gbc_delimiter_urlencoded_body_param.gridx = 2; + gbc_delimiter_urlencoded_body_param.gridy = 3; + panel_1.add(delimiter_urlencoded_body_param, gbc_delimiter_urlencoded_body_param); + delimiter_urlencoded_body_param.setColumns(10); + + GridBagConstraints gbc_replaceGETwithPOST = new GridBagConstraints(); + gbc_replaceGETwithPOST.insets = new Insets(0, 0, 5, 5); + gbc_replaceGETwithPOST.anchor = GridBagConstraints.WEST; + gbc_replaceGETwithPOST.gridx = 0; + gbc_replaceGETwithPOST.gridy = 4; + panel_1.add(replaceGETwithPOST, gbc_replaceGETwithPOST); + + JLabel lblBodyParameterEqual = new JLabel("Body parameter equal sign:"); + GridBagConstraints gbc_lblBodyParameterEqual = new GridBagConstraints(); + gbc_lblBodyParameterEqual.anchor = GridBagConstraints.EAST; + gbc_lblBodyParameterEqual.insets = new Insets(0, 0, 5, 5); + gbc_lblBodyParameterEqual.gridx = 1; + gbc_lblBodyParameterEqual.gridy = 4; + panel_1.add(lblBodyParameterEqual, gbc_lblBodyParameterEqual); + + body_param_equalSign = new JTextField(); + body_param_equalSign.setText("="); + GridBagConstraints gbc_body_param_equalSign = new GridBagConstraints(); + gbc_body_param_equalSign.insets = new Insets(0, 0, 5, 0); + gbc_body_param_equalSign.fill = GridBagConstraints.HORIZONTAL; + gbc_body_param_equalSign.gridx = 2; + gbc_body_param_equalSign.gridy = 4; + panel_1.add(body_param_equalSign, gbc_body_param_equalSign); + body_param_equalSign.setColumns(10); + + GridBagConstraints gbc_isEncodable_QS = new GridBagConstraints(); + gbc_isEncodable_QS.insets = new Insets(0, 0, 5, 5); + gbc_isEncodable_QS.anchor = GridBagConstraints.WEST; + gbc_isEncodable_QS.gridx = 0; + gbc_isEncodable_QS.gridy = 5; + panel_1.add(isEncodable_QS, gbc_isEncodable_QS); + + JLabel lblOutgoingRequestEncoding = new JLabel("Outgoing request encoding:"); + GridBagConstraints gbc_lblOutgoingRequestEncoding = new GridBagConstraints(); + gbc_lblOutgoingRequestEncoding.anchor = GridBagConstraints.EAST; + gbc_lblOutgoingRequestEncoding.insets = new Insets(0, 0, 5, 5); + gbc_lblOutgoingRequestEncoding.gridx = 1; + gbc_lblOutgoingRequestEncoding.gridy = 5; + panel_1.add(lblOutgoingRequestEncoding, gbc_lblOutgoingRequestEncoding); + + outgoing_request_encoding = new JTextField(); + outgoing_request_encoding.setText("ibm500"); + GridBagConstraints gbc_outgoing_request_encoding = new GridBagConstraints(); + gbc_outgoing_request_encoding.insets = new Insets(0, 0, 5, 0); + gbc_outgoing_request_encoding.fill = GridBagConstraints.HORIZONTAL; + gbc_outgoing_request_encoding.gridx = 2; + gbc_outgoing_request_encoding.gridy = 5; + panel_1.add(outgoing_request_encoding, gbc_outgoing_request_encoding); + outgoing_request_encoding.setColumns(10); + + GridBagConstraints gbc_isEncodable_body = new GridBagConstraints(); + gbc_isEncodable_body.anchor = GridBagConstraints.WEST; + gbc_isEncodable_body.insets = new Insets(0, 0, 5, 5); + gbc_isEncodable_body.gridx = 0; + gbc_isEncodable_body.gridy = 6; + panel_1.add(isEncodable_body, gbc_isEncodable_body); + + JLabel lblDefaultIncomingRequest = new JLabel("Default incoming request encoding:"); + GridBagConstraints gbc_lblDefaultIncomingRequest = new GridBagConstraints(); + gbc_lblDefaultIncomingRequest.anchor = GridBagConstraints.EAST; + gbc_lblDefaultIncomingRequest.insets = new Insets(0, 0, 5, 5); + gbc_lblDefaultIncomingRequest.gridx = 1; + gbc_lblDefaultIncomingRequest.gridy = 6; + panel_1.add(lblDefaultIncomingRequest, gbc_lblDefaultIncomingRequest); + + incoming_request_encoding = new JTextField(); + incoming_request_encoding.setText("utf-8"); + GridBagConstraints gbc_incoming_request_encoding = new GridBagConstraints(); + gbc_incoming_request_encoding.insets = new Insets(0, 0, 5, 0); + gbc_incoming_request_encoding.fill = GridBagConstraints.HORIZONTAL; + gbc_incoming_request_encoding.gridx = 2; + gbc_incoming_request_encoding.gridy = 6; + panel_1.add(incoming_request_encoding, gbc_incoming_request_encoding); + incoming_request_encoding.setColumns(10); + + GridBagConstraints gbc_isEncodable_QS_delimiter = new GridBagConstraints(); + gbc_isEncodable_QS_delimiter.anchor = GridBagConstraints.WEST; + gbc_isEncodable_QS_delimiter.insets = new Insets(0, 0, 5, 5); + gbc_isEncodable_QS_delimiter.gridx = 0; + gbc_isEncodable_QS_delimiter.gridy = 7; + panel_1.add(isEncodable_QS_delimiter, gbc_isEncodable_QS_delimiter); + + GridBagConstraints gbc_isEncodable_urlencoded_body_delimiter = new GridBagConstraints(); + gbc_isEncodable_urlencoded_body_delimiter.anchor = GridBagConstraints.WEST; + gbc_isEncodable_urlencoded_body_delimiter.insets = new Insets(0, 0, 5, 5); + gbc_isEncodable_urlencoded_body_delimiter.gridx = 0; + gbc_isEncodable_urlencoded_body_delimiter.gridy = 8; + panel_1.add(isEncodable_urlencoded_body_delimiter, gbc_isEncodable_urlencoded_body_delimiter); + + GridBagConstraints gbc_isEncodable_QS_equal_sign = new GridBagConstraints(); + gbc_isEncodable_QS_equal_sign.anchor = GridBagConstraints.WEST; + gbc_isEncodable_QS_equal_sign.insets = new Insets(0, 0, 5, 5); + gbc_isEncodable_QS_equal_sign.gridx = 0; + gbc_isEncodable_QS_equal_sign.gridy = 9; + panel_1.add(isEncodable_QS_equal_sign, gbc_isEncodable_QS_equal_sign); + + GridBagConstraints gbc_isEncodable_urlencoded_body_equal_sign = new GridBagConstraints(); + gbc_isEncodable_urlencoded_body_equal_sign.anchor = GridBagConstraints.WEST; + gbc_isEncodable_urlencoded_body_equal_sign.insets = new Insets(0, 0, 5, 5); + gbc_isEncodable_urlencoded_body_equal_sign.gridx = 0; + gbc_isEncodable_urlencoded_body_equal_sign.gridy = 10; + panel_1.add(isEncodable_urlencoded_body_equal_sign, gbc_isEncodable_urlencoded_body_equal_sign); + + GridBagConstraints gbc_isURLEncoded_incoming_QS = new GridBagConstraints(); + gbc_isURLEncoded_incoming_QS.anchor = GridBagConstraints.WEST; + gbc_isURLEncoded_incoming_QS.insets = new Insets(0, 0, 5, 5); + gbc_isURLEncoded_incoming_QS.gridx = 0; + gbc_isURLEncoded_incoming_QS.gridy = 11; + panel_1.add(isURLEncoded_incoming_QS, gbc_isURLEncoded_incoming_QS); + + isURLEncoded_incoming_body.setToolTipText("this is not active when it is a multipart message"); + GridBagConstraints gbc_isURLEncoded_incoming_body = new GridBagConstraints(); + gbc_isURLEncoded_incoming_body.anchor = GridBagConstraints.WEST; + gbc_isURLEncoded_incoming_body.insets = new Insets(0, 0, 5, 5); + gbc_isURLEncoded_incoming_body.gridx = 0; + gbc_isURLEncoded_incoming_body.gridy = 12; + panel_1.add(isURLEncoded_incoming_body, gbc_isURLEncoded_incoming_body); + + GridBagConstraints gbc_isURLEncoded_outgoing_QS = new GridBagConstraints(); + gbc_isURLEncoded_outgoing_QS.anchor = GridBagConstraints.WEST; + gbc_isURLEncoded_outgoing_QS.insets = new Insets(0, 0, 5, 5); + gbc_isURLEncoded_outgoing_QS.gridx = 0; + gbc_isURLEncoded_outgoing_QS.gridy = 13; + panel_1.add(isURLEncoded_outgoing_QS, gbc_isURLEncoded_outgoing_QS); + + isURLEncoded_outgoing_body.setToolTipText("this is not active when it is a multipart message"); + GridBagConstraints gbc_isURLEncoded_outgoing_body = new GridBagConstraints(); + gbc_isURLEncoded_outgoing_body.anchor = GridBagConstraints.WEST; + gbc_isURLEncoded_outgoing_body.insets = new Insets(0, 0, 5, 5); + gbc_isURLEncoded_outgoing_body.gridx = 0; + gbc_isURLEncoded_outgoing_body.gridy = 14; + panel_1.add(isURLEncoded_outgoing_body, gbc_isURLEncoded_outgoing_body); + + isAllChar_URLEncoded_outgoing_QS.setToolTipText("only active when isURLEncoded_outgoing_QS=true to encode all characters rather than just key characters"); + GridBagConstraints gbc_isAllChar_URLEncoded_outgoing_QS = new GridBagConstraints(); + gbc_isAllChar_URLEncoded_outgoing_QS.anchor = GridBagConstraints.WEST; + gbc_isAllChar_URLEncoded_outgoing_QS.insets = new Insets(0, 0, 5, 5); + gbc_isAllChar_URLEncoded_outgoing_QS.gridx = 0; + gbc_isAllChar_URLEncoded_outgoing_QS.gridy = 15; + panel_1.add(isAllChar_URLEncoded_outgoing_QS, gbc_isAllChar_URLEncoded_outgoing_QS); + + isAllChar_URLEncoded_outgoing_body.setToolTipText("only active when isURLEncoded_outgoing_body=true to encode all characters rather than just key characters"); + GridBagConstraints gbc_isAllChar_URLEncoded_outgoing_body = new GridBagConstraints(); + gbc_isAllChar_URLEncoded_outgoing_body.anchor = GridBagConstraints.WEST; + gbc_isAllChar_URLEncoded_outgoing_body.insets = new Insets(0, 0, 5, 5); + gbc_isAllChar_URLEncoded_outgoing_body.gridx = 0; + gbc_isAllChar_URLEncoded_outgoing_body.gridy = 16; + panel_1.add(isAllChar_URLEncoded_outgoing_body, gbc_isAllChar_URLEncoded_outgoing_body); + + GridBagConstraints gbc_trimSpacesInContentTypeHeaderValues = new GridBagConstraints(); + gbc_trimSpacesInContentTypeHeaderValues.anchor = GridBagConstraints.WEST; + gbc_trimSpacesInContentTypeHeaderValues.insets = new Insets(0, 0, 5, 5); + gbc_trimSpacesInContentTypeHeaderValues.gridx = 0; + gbc_trimSpacesInContentTypeHeaderValues.gridy = 17; + panel_1.add(trimSpacesInContentTypeHeaderValues, gbc_trimSpacesInContentTypeHeaderValues); + + encodeNameValueOnlyMultipart.setToolTipText("python django needs this, IIS does not!"); + GridBagConstraints gbc_encodeNameValueOnlyMultipart = new GridBagConstraints(); + gbc_encodeNameValueOnlyMultipart.anchor = GridBagConstraints.WEST; + gbc_encodeNameValueOnlyMultipart.insets = new Insets(0, 0, 5, 5); + gbc_encodeNameValueOnlyMultipart.gridx = 0; + gbc_encodeNameValueOnlyMultipart.gridy = 18; + panel_1.add(encodeNameValueOnlyMultipart, gbc_encodeNameValueOnlyMultipart); + + GridBagConstraints gbc_use_incoming_charset_for_request_encoding = new GridBagConstraints(); + gbc_use_incoming_charset_for_request_encoding.anchor = GridBagConstraints.WEST; + gbc_use_incoming_charset_for_request_encoding.insets = new Insets(0, 0, 0, 5); + gbc_use_incoming_charset_for_request_encoding.gridx = 0; + gbc_use_incoming_charset_for_request_encoding.gridy = 19; + panel_1.add(use_incoming_charset_for_request_encoding, gbc_use_incoming_charset_for_request_encoding); + + init(); + } + private void init(){ + //resetSettings(); + loadSettings(); + updateUIState(); + setActions(); + saveSettings(); + } + + private void updateUIState() { + + Object policy = comboBoxPolicy.getSelectedItem(); + + if(!policy.equals(policyOptions.custom)) { + HTTPEncodingObject newHTTPEncodingObject = new HTTPEncodingObject(policy.toString()); + preventReEncoding.setSelected(newHTTPEncodingObject.isPreventReEncoding()); + encodeMicrosoftURLEncode.setSelected(newHTTPEncodingObject.isEncodeMicrosoftURLEncode()); + encodeDespiteErrors.setSelected(newHTTPEncodingObject.isEncodeDespiteErrors()); + addACharToEmptyBody.setSelected(newHTTPEncodingObject.isAddACharToEmptyBody()); + replaceGETwithPOST.setSelected(newHTTPEncodingObject.isReplaceGETwithPOST()); + isEncodable_QS.setSelected(newHTTPEncodingObject.isEncodable_QS()); + isEncodable_body.setSelected(newHTTPEncodingObject.isEncodable_body()); + isEncodable_QS_delimiter.setSelected(newHTTPEncodingObject.isEncodable_QS_delimiter()); + isEncodable_urlencoded_body_delimiter.setSelected(newHTTPEncodingObject.isEncodable_urlencoded_body_delimiter()); + isEncodable_QS_equal_sign.setSelected(newHTTPEncodingObject.isEncodable_QS_equal_sign()); + isEncodable_urlencoded_body_equal_sign.setSelected(newHTTPEncodingObject.isEncodable_urlencoded_body_equal_sign()); + isURLEncoded_incoming_QS.setSelected(newHTTPEncodingObject.isURLEncoded_incoming_QS()); + isURLEncoded_incoming_body.setSelected(newHTTPEncodingObject.isURLEncoded_incoming_body()); + isURLEncoded_outgoing_QS.setSelected(newHTTPEncodingObject.isURLEncoded_outgoing_QS()); + isURLEncoded_outgoing_body.setSelected(newHTTPEncodingObject.isURLEncoded_outgoing_body()); + isAllChar_URLEncoded_outgoing_QS.setSelected(newHTTPEncodingObject.isAllChar_URLEncoded_outgoing_QS()); + isAllChar_URLEncoded_outgoing_body.setSelected(newHTTPEncodingObject.isAllChar_URLEncoded_outgoing_body()); + trimSpacesInContentTypeHeaderValues.setSelected(newHTTPEncodingObject.isTrimSpacesInContentTypeHeaderValues()); + encodeNameValueOnlyMultipart.setSelected(newHTTPEncodingObject.isEncodeNameValueOnlyMultipart()); + use_incoming_charset_for_request_encoding.setSelected(newHTTPEncodingObject.isUse_incoming_charset_for_request_encoding()); + + delimiter_QS.setText(newHTTPEncodingObject.getDelimiter_QS()); + delimiter_QS_param.setText(newHTTPEncodingObject.getDelimiter_QS_param()); + QS_equalSign.setText(newHTTPEncodingObject.getQS_equalSign()); + delimiter_urlencoded_body_param.setText(newHTTPEncodingObject.getDelimiter_urlencoded_body_param()); + body_param_equalSign.setText(newHTTPEncodingObject.getBody_param_equalSign()); + outgoing_request_encoding.setText(newHTTPEncodingObject.getOutgoing_request_encoding()); + incoming_request_encoding.setText(newHTTPEncodingObject.getIncoming_request_encoding()); + + comboBoxPolicy.setSelectedItem(policy); + } + + + } + + + private void setActions() { + setActionsJComboBoxHelper(comboBoxPolicy); + + setActionsJCheckBoxHelper(preventReEncoding); + setActionsJCheckBoxHelper(encodeMicrosoftURLEncode); + setActionsJCheckBoxHelper(encodeDespiteErrors); + setActionsJCheckBoxHelper(addACharToEmptyBody); + setActionsJCheckBoxHelper(replaceGETwithPOST); + setActionsJCheckBoxHelper(isEncodable_QS); + setActionsJCheckBoxHelper(isEncodable_body); + setActionsJCheckBoxHelper(isEncodable_QS_delimiter); + setActionsJCheckBoxHelper(isEncodable_urlencoded_body_delimiter); + setActionsJCheckBoxHelper(isEncodable_QS_equal_sign); + setActionsJCheckBoxHelper(isEncodable_urlencoded_body_equal_sign); + setActionsJCheckBoxHelper(isURLEncoded_incoming_QS); + setActionsJCheckBoxHelper(isURLEncoded_incoming_body); + setActionsJCheckBoxHelper(isURLEncoded_outgoing_QS); + setActionsJCheckBoxHelper(isURLEncoded_outgoing_body); + setActionsJCheckBoxHelper(isAllChar_URLEncoded_outgoing_QS); + setActionsJCheckBoxHelper(isAllChar_URLEncoded_outgoing_body); + setActionsJCheckBoxHelper(trimSpacesInContentTypeHeaderValues); + setActionsJCheckBoxHelper(encodeNameValueOnlyMultipart); + setActionsJCheckBoxHelper(use_incoming_charset_for_request_encoding); + + setActionsJTextFieldHelper(delimiter_QS); + setActionsJTextFieldHelper(delimiter_QS_param); + setActionsJTextFieldHelper(QS_equalSign); + setActionsJTextFieldHelper(delimiter_urlencoded_body_param); + setActionsJTextFieldHelper(body_param_equalSign); + setActionsJTextFieldHelper(outgoing_request_encoding); + setActionsJTextFieldHelper(incoming_request_encoding); + + } + + private void setActionsJCheckBoxHelper(JCheckBox checkbox) { + checkbox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + comboBoxPolicy.setSelectedItem(policyOptions.custom); + saveSettings(); + } + }); + } + + private void setActionsJComboBoxHelper(JComboBox combobox) { + combobox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + updateUIState(); + saveSettings(); + } + }); + } + + private void setActionsJTextFieldHelper(JTextField textfield) { + textfield.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void changedUpdate(DocumentEvent arg0) { + comboBoxPolicy.setSelectedItem(policyOptions.custom); + saveSettings(); + } + @Override + public void insertUpdate(DocumentEvent arg0) { + comboBoxPolicy.setSelectedItem(policyOptions.custom); + saveSettings(); + } + @Override + public void removeUpdate(DocumentEvent arg0) { + comboBoxPolicy.setSelectedItem(policyOptions.custom); + saveSettings(); + } + }); + } + + private void loadSettings() { + setValueFromExtensionSettings(comboBoxPolicy,"comboBoxPolicy",0); + setValueFromExtensionSettings(preventReEncoding,"preventReEncoding",true); + setValueFromExtensionSettings(encodeMicrosoftURLEncode,"encodeMicrosoftURLEncode",false); + setValueFromExtensionSettings(encodeDespiteErrors,"encodeDespiteErrors",false); + setValueFromExtensionSettings(addACharToEmptyBody,"addACharToEmptyBody",true); + setValueFromExtensionSettings(replaceGETwithPOST,"replaceGETwithPOST",false); + setValueFromExtensionSettings(isEncodable_QS,"isEncodable_QS",true); + setValueFromExtensionSettings(isEncodable_body,"isEncodable_body",true); + setValueFromExtensionSettings(isEncodable_QS_delimiter,"isEncodable_QS_delimiter",false); + setValueFromExtensionSettings(isEncodable_urlencoded_body_delimiter,"isEncodable_urlencoded_body_delimiter",false); + setValueFromExtensionSettings(isEncodable_QS_equal_sign,"isEncodable_QS_equal_sign",false); + setValueFromExtensionSettings(isEncodable_urlencoded_body_equal_sign,"isEncodable_urlencoded_body_equal_sign",false); + setValueFromExtensionSettings(isURLEncoded_incoming_QS,"isURLEncoded_incoming_QS",true); + setValueFromExtensionSettings(isURLEncoded_incoming_body,"isURLEncoded_incoming_body",true); + setValueFromExtensionSettings(isURLEncoded_outgoing_QS,"isURLEncoded_outgoing_QS",true); + setValueFromExtensionSettings(isURLEncoded_outgoing_body,"isURLEncoded_outgoing_body",true); + setValueFromExtensionSettings(isAllChar_URLEncoded_outgoing_QS,"isAllChar_URLEncoded_outgoing_QS",true); + setValueFromExtensionSettings(isAllChar_URLEncoded_outgoing_body,"isAllChar_URLEncoded_outgoing_body",true); + setValueFromExtensionSettings(trimSpacesInContentTypeHeaderValues,"trimSpacesInContentTypeHeaderValues",true); + setValueFromExtensionSettings(encodeNameValueOnlyMultipart,"encodeNameValueOnlyMultipart",false); + setValueFromExtensionSettings(use_incoming_charset_for_request_encoding,"use_incoming_charset_for_request_encoding",true); + setValueFromExtensionSettings(delimiter_QS,"delimiter_QS","?"); + setValueFromExtensionSettings(delimiter_QS_param,"delimiter_QS_param","&"); + setValueFromExtensionSettings(QS_equalSign,"QS_equalSign","="); + setValueFromExtensionSettings(delimiter_urlencoded_body_param,"delimiter_urlencoded_body_param","&"); + setValueFromExtensionSettings(body_param_equalSign,"body_param_equalSign","="); + setValueFromExtensionSettings(outgoing_request_encoding,"outgoing_request_encoding","ibm500"); + setValueFromExtensionSettings(incoming_request_encoding,"incoming_request_encoding","utf-8"); + } + + private void saveSettings() { + saveExtensionSettingHelper("comboBoxPolicy", comboBoxPolicy.getSelectedIndex()); + saveExtensionSettingHelper("preventReEncoding", preventReEncoding.isSelected()); + saveExtensionSettingHelper("encodeMicrosoftURLEncode", encodeMicrosoftURLEncode.isSelected()); + saveExtensionSettingHelper("encodeDespiteErrors", encodeDespiteErrors.isSelected()); + saveExtensionSettingHelper("addACharToEmptyBody", addACharToEmptyBody.isSelected()); + saveExtensionSettingHelper("replaceGETwithPOST", replaceGETwithPOST.isSelected()); + saveExtensionSettingHelper("isEncodable_QS", isEncodable_QS.isSelected()); + saveExtensionSettingHelper("isEncodable_body", isEncodable_body.isSelected()); + saveExtensionSettingHelper("isEncodable_QS_delimiter", isEncodable_QS_delimiter.isSelected()); + saveExtensionSettingHelper("isEncodable_urlencoded_body_delimiter", isEncodable_urlencoded_body_delimiter.isSelected()); + saveExtensionSettingHelper("isEncodable_QS_equal_sign", isEncodable_QS_equal_sign.isSelected()); + saveExtensionSettingHelper("isEncodable_urlencoded_body_equal_sign", isEncodable_urlencoded_body_equal_sign.isSelected()); + saveExtensionSettingHelper("delimiter_QS", delimiter_QS.getText()); + saveExtensionSettingHelper("delimiter_QS_param", delimiter_QS_param.getText()); + saveExtensionSettingHelper("QS_equalSign", QS_equalSign.getText()); + saveExtensionSettingHelper("delimiter_urlencoded_body_param", delimiter_urlencoded_body_param.getText()); + saveExtensionSettingHelper("body_param_equalSign", body_param_equalSign.getText()); + saveExtensionSettingHelper("isURLEncoded_incoming_QS", isURLEncoded_incoming_QS.isSelected()); + saveExtensionSettingHelper("isURLEncoded_incoming_body", isURLEncoded_incoming_body.isSelected()); + saveExtensionSettingHelper("isURLEncoded_outgoing_QS", isURLEncoded_outgoing_QS.isSelected()); + saveExtensionSettingHelper("isURLEncoded_outgoing_body", isURLEncoded_outgoing_body.isSelected()); + saveExtensionSettingHelper("isAllChar_URLEncoded_outgoing_QS", isAllChar_URLEncoded_outgoing_QS.isSelected()); + saveExtensionSettingHelper("isAllChar_URLEncoded_outgoing_body", isAllChar_URLEncoded_outgoing_body.isSelected()); + saveExtensionSettingHelper("trimSpacesInContentTypeHeaderValues", trimSpacesInContentTypeHeaderValues.isSelected()); + saveExtensionSettingHelper("encodeNameValueOnlyMultipart", encodeNameValueOnlyMultipart.isSelected()); + saveExtensionSettingHelper("outgoing_request_encoding", outgoing_request_encoding.getText()); + saveExtensionSettingHelper("use_incoming_charset_for_request_encoding", use_incoming_charset_for_request_encoding.isSelected()); + saveExtensionSettingHelper("incoming_request_encoding", incoming_request_encoding.getText()); + } + + private void resetSettings() { + comboBoxPolicy.setSelectedIndex(0); + updateUIState(); + saveSettings(); + } + + private void saveExtensionSettingHelper(String name, Object value) { + try { + _callbacks.saveExtensionSetting(name, String.valueOf(value)); + }catch(Exception e) { + _stderr.println(e.getMessage()); + } + } + + private Object loadExtensionSettingHelper(String name, String type, Object defaultValue) { + Object value = null; + try { + String temp_value = _callbacks.loadExtensionSetting(name); + if(temp_value!=null && !temp_value.equals("")) { + switch(type.toLowerCase()){ + case "int": + case "integer": + value = Integer.valueOf(temp_value); + break; + case "bool": + case "boolean": + value = Boolean.valueOf(temp_value); + break; + default: + value = temp_value; + break; + } + } + }catch(Exception e) { + _stderr.println(e.getMessage()); + } + + if(value==null) { + value = defaultValue; + } + return value; + } + + + private void setValueFromExtensionSettings(JTextField jTextField, String name, Object defaultValue) { + String value = _callbacks.loadExtensionSetting(name); + if(value!=null && !value.equals("") && !value.equals(jTextField.getText())) { + jTextField.setText(value); + }else{ + jTextField.setText((String) defaultValue); + } + } + + private void setValueFromExtensionSettings(JComboBox jComboBox, String name, Object defaultValue) { + String value = _callbacks.loadExtensionSetting(name); + if(value!=null && !value.equals("")) { + int temp_value = Integer.valueOf(value); + if(temp_value!=jComboBox.getSelectedIndex()) + jComboBox.setSelectedIndex(temp_value); + }else { + jComboBox.setSelectedIndex((int) defaultValue); + } + } + + private void setValueFromExtensionSettings(JCheckBox jCheckBox, String name, Object defaultValue) { + String value = _callbacks.loadExtensionSetting(name); + if(value!=null && !value.equals("")) { + boolean temp_value = Boolean.valueOf(value); + if(temp_value!=jCheckBox.isSelected()) + jCheckBox.setSelected(temp_value); + }else { + jCheckBox.setSelected((boolean) defaultValue); + } + } + +} diff --git a/src/myui/ScopeTab.java b/src/myui/ScopeTab.java new file mode 100644 index 0000000..867b15d --- /dev/null +++ b/src/myui/ScopeTab.java @@ -0,0 +1,442 @@ +/* + * Burp Suite HTTP Smuggler + * + * Released as open source by NCC Group - https://www.nccgroup.trust/ + * + * Developed by: + * Soroush Dalili (@irsdl) + * + * Project link: https://github.com/nccgroup/BurpSuiteHTTPSmuggler/ + * + * Released under AGPL v3.0 see LICENSE for more information + * + * */ + +package myui; + +/* + * TODO: implement OR + AND-NOT + OR-NOT + * */ +import javax.swing.JPanel; +import java.awt.GridBagLayout; +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.PrintWriter; +import javax.swing.JLabel; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JTextField; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import burp.IBurpExtenderCallbacks; +import burp.IExtensionHelpers; + +public class ScopeTab extends JPanel { + private IBurpExtenderCallbacks _callbacks;; + private IExtensionHelpers _helpers; + private PrintWriter _stdout; + private PrintWriter _stderr; + + /** + * Create the panel. + */ + + public enum scopeOptions1{ + AND, + OR, + DISABLED + } + + public enum scopeOptions2{ + ENABLED, + DISABLED + } + + private JComboBox targetScopeOption; + private JComboBox pathRegExOption; + private JTextField pathRegEx; + private JComboBox headerRegExOption; + private JTextField headerRegEx; + private JCheckBox chckbxAllTools; + private JCheckBox chckbxProxy; + private JCheckBox chckbxScanner; + private JCheckBox chckbxIntruder; + private JCheckBox chckbxRepeator; + private JCheckBox chckbxExtender; + private JCheckBox chckbxTarget; + private JCheckBox chckbxSequencer; + private JCheckBox chckbxSpider; + + public ScopeTab(IBurpExtenderCallbacks callbacks, PrintWriter stdout, PrintWriter stderr) { + _callbacks = callbacks; + _helpers = _callbacks.getHelpers(); + _stdout = stdout; + _stderr = stderr; + + GridBagLayout gridBagLayout = new GridBagLayout(); + gridBagLayout.columnWidths = new int[]{30, 0, 30, 101, 221, 0, 0, 0}; + gridBagLayout.rowHeights = new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + gridBagLayout.columnWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE}; + gridBagLayout.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE}; + setLayout(gridBagLayout); + + + JLabel lblIn = new JLabel("In Burp Target scope?"); + GridBagConstraints gbc_lblIn = new GridBagConstraints(); + gbc_lblIn.anchor = GridBagConstraints.EAST; + gbc_lblIn.insets = new Insets(0, 0, 5, 5); + gbc_lblIn.gridx = 2; + gbc_lblIn.gridy = 2; + add(lblIn, gbc_lblIn); + + targetScopeOption = new JComboBox(); + targetScopeOption.setModel(new DefaultComboBoxModel(scopeOptions2.values())); + GridBagConstraints gbc_targetScopeOption = new GridBagConstraints(); + gbc_targetScopeOption.anchor = GridBagConstraints.WEST; + gbc_targetScopeOption.insets = new Insets(0, 0, 5, 5); + gbc_targetScopeOption.gridx = 3; + gbc_targetScopeOption.gridy = 2; + add(targetScopeOption, gbc_targetScopeOption); + + JLabel lblPathRegex = new JLabel("Path/URL RegEx:"); + GridBagConstraints gbc_lblPathRegex = new GridBagConstraints(); + gbc_lblPathRegex.anchor = GridBagConstraints.EAST; + gbc_lblPathRegex.insets = new Insets(0, 0, 5, 5); + gbc_lblPathRegex.gridx = 2; + gbc_lblPathRegex.gridy = 3; + add(lblPathRegex, gbc_lblPathRegex); + + pathRegExOption = new JComboBox(); + pathRegExOption.setModel(new DefaultComboBoxModel(scopeOptions2.values())); + GridBagConstraints gbc_pathRegexOption = new GridBagConstraints(); + gbc_pathRegexOption.anchor = GridBagConstraints.WEST; + gbc_pathRegexOption.insets = new Insets(0, 0, 5, 5); + gbc_pathRegexOption.gridx = 3; + gbc_pathRegexOption.gridy = 3; + add(pathRegExOption, gbc_pathRegexOption); + + + pathRegEx = new JTextField(); + GridBagConstraints gbc_pathRegEx = new GridBagConstraints(); + gbc_pathRegEx.insets = new Insets(0, 0, 5, 5); + gbc_pathRegEx.fill = GridBagConstraints.HORIZONTAL; + gbc_pathRegEx.gridx = 4; + gbc_pathRegEx.gridy = 3; + add(pathRegEx, gbc_pathRegEx); + pathRegEx.setColumns(10); + + JLabel lblHeaderRegex = new JLabel("Headers RegEx:"); + GridBagConstraints gbc_lblHeaderRegex = new GridBagConstraints(); + gbc_lblHeaderRegex.anchor = GridBagConstraints.EAST; + gbc_lblHeaderRegex.insets = new Insets(0, 0, 5, 5); + gbc_lblHeaderRegex.gridx = 2; + gbc_lblHeaderRegex.gridy = 4; + add(lblHeaderRegex, gbc_lblHeaderRegex); + + headerRegExOption = new JComboBox(); + headerRegExOption.setModel(new DefaultComboBoxModel(scopeOptions2.values())); + GridBagConstraints gbc_headerRegExOption = new GridBagConstraints(); + gbc_headerRegExOption.anchor = GridBagConstraints.WEST; + gbc_headerRegExOption.insets = new Insets(0, 0, 5, 5); + gbc_headerRegExOption.gridx = 3; + gbc_headerRegExOption.gridy = 4; + add(headerRegExOption, gbc_headerRegExOption); + + headerRegEx = new JTextField(); + GridBagConstraints gbc_headerRegEx = new GridBagConstraints(); + gbc_headerRegEx.insets = new Insets(0, 0, 5, 5); + gbc_headerRegEx.fill = GridBagConstraints.HORIZONTAL; + gbc_headerRegEx.gridx = 4; + gbc_headerRegEx.gridy = 4; + add(headerRegEx, gbc_headerRegEx); + headerRegEx.setColumns(10); + + JLabel lblActiveIn = new JLabel("Rules active in:"); + GridBagConstraints gbc_lblActiveIn = new GridBagConstraints(); + gbc_lblActiveIn.anchor = GridBagConstraints.EAST; + gbc_lblActiveIn.insets = new Insets(0, 0, 5, 5); + gbc_lblActiveIn.gridx = 2; + gbc_lblActiveIn.gridy = 6; + add(lblActiveIn, gbc_lblActiveIn); + + chckbxAllTools = new JCheckBox("All Tools"); + GridBagConstraints gbc_chckbxAllTools = new GridBagConstraints(); + gbc_chckbxAllTools.anchor = GridBagConstraints.WEST; + gbc_chckbxAllTools.insets = new Insets(0, 0, 5, 5); + gbc_chckbxAllTools.gridx = 3; + gbc_chckbxAllTools.gridy = 6; + add(chckbxAllTools, gbc_chckbxAllTools); + + chckbxProxy = new JCheckBox("Proxy"); + GridBagConstraints gbc_chckbxProxy = new GridBagConstraints(); + gbc_chckbxProxy.anchor = GridBagConstraints.WEST; + gbc_chckbxProxy.insets = new Insets(0, 0, 5, 5); + gbc_chckbxProxy.gridx = 3; + gbc_chckbxProxy.gridy = 7; + add(chckbxProxy, gbc_chckbxProxy); + + chckbxScanner = new JCheckBox("Scanner"); + GridBagConstraints gbc_chckbxScanner = new GridBagConstraints(); + gbc_chckbxScanner.anchor = GridBagConstraints.WEST; + gbc_chckbxScanner.insets = new Insets(0, 0, 5, 5); + gbc_chckbxScanner.gridx = 3; + gbc_chckbxScanner.gridy = 8; + add(chckbxScanner, gbc_chckbxScanner); + + chckbxIntruder = new JCheckBox("Intruder"); + GridBagConstraints gbc_chckbxIntruder = new GridBagConstraints(); + gbc_chckbxIntruder.anchor = GridBagConstraints.WEST; + gbc_chckbxIntruder.insets = new Insets(0, 0, 5, 5); + gbc_chckbxIntruder.gridx = 3; + gbc_chckbxIntruder.gridy = 9; + add(chckbxIntruder, gbc_chckbxIntruder); + + chckbxRepeator = new JCheckBox("Repeator"); + GridBagConstraints gbc_chckbxRepeator = new GridBagConstraints(); + gbc_chckbxRepeator.anchor = GridBagConstraints.WEST; + gbc_chckbxRepeator.insets = new Insets(0, 0, 5, 5); + gbc_chckbxRepeator.gridx = 3; + gbc_chckbxRepeator.gridy = 10; + add(chckbxRepeator, gbc_chckbxRepeator); + + chckbxExtender = new JCheckBox("Extender"); + GridBagConstraints gbc_chckbxExtender = new GridBagConstraints(); + gbc_chckbxExtender.anchor = GridBagConstraints.WEST; + gbc_chckbxExtender.insets = new Insets(0, 0, 5, 5); + gbc_chckbxExtender.gridx = 3; + gbc_chckbxExtender.gridy = 11; + add(chckbxExtender, gbc_chckbxExtender); + + chckbxTarget = new JCheckBox("Target"); + GridBagConstraints gbc_chckbxTarget = new GridBagConstraints(); + gbc_chckbxTarget.anchor = GridBagConstraints.WEST; + gbc_chckbxTarget.insets = new Insets(0, 0, 5, 5); + gbc_chckbxTarget.gridx = 3; + gbc_chckbxTarget.gridy = 12; + add(chckbxTarget, gbc_chckbxTarget); + + chckbxSequencer = new JCheckBox("Sequencer"); + GridBagConstraints gbc_chckbxSequencer = new GridBagConstraints(); + gbc_chckbxSequencer.anchor = GridBagConstraints.WEST; + gbc_chckbxSequencer.insets = new Insets(0, 0, 5, 5); + gbc_chckbxSequencer.gridx = 3; + gbc_chckbxSequencer.gridy = 13; + add(chckbxSequencer, gbc_chckbxSequencer); + + chckbxSpider = new JCheckBox("Spider"); + GridBagConstraints gbc_chckbxSpider = new GridBagConstraints(); + gbc_chckbxSpider.anchor = GridBagConstraints.WEST; + gbc_chckbxSpider.insets = new Insets(0, 0, 5, 5); + gbc_chckbxSpider.gridx = 3; + gbc_chckbxSpider.gridy = 14; + add(chckbxSpider, gbc_chckbxSpider); + + init(); + } + + private void init(){ + //resetSettings(); + loadSettings(); + updateUIState(); + setActions(); + saveSettings(); + } + + private void updateUIState() { + if(pathRegExOption.getSelectedIndex()==1) { + pathRegEx.setEnabled(false); + }else { + pathRegEx.setEnabled(true); + } + + if(headerRegExOption.getSelectedIndex()==1) { + headerRegEx.setEnabled(false); + }else { + headerRegEx.setEnabled(true); + } + + if(chckbxAllTools.isSelected()) { + chckbxAllTools.setEnabled(true); + chckbxProxy.setEnabled(false); + chckbxScanner.setEnabled(false); + chckbxIntruder.setEnabled(false); + chckbxRepeator.setEnabled(false); + chckbxExtender.setEnabled(false); + chckbxTarget.setEnabled(false); + chckbxSequencer.setEnabled(false); + chckbxSpider.setEnabled(false); + }else { + chckbxAllTools.setEnabled(true); + chckbxProxy.setEnabled(true); + chckbxScanner.setEnabled(true); + chckbxIntruder.setEnabled(true); + chckbxRepeator.setEnabled(true); + chckbxExtender.setEnabled(true); + chckbxTarget.setEnabled(true); + chckbxSequencer.setEnabled(true); + chckbxSpider.setEnabled(true); + } + + if(targetScopeOption.getSelectedItem().equals(scopeOptions2.DISABLED) && + pathRegExOption.getSelectedItem().equals(scopeOptions2.DISABLED) && + headerRegExOption.getSelectedItem().equals(scopeOptions2.DISABLED)) { + // totally disabled! + chckbxAllTools.setEnabled(false); + chckbxProxy.setEnabled(false); + chckbxScanner.setEnabled(false); + chckbxIntruder.setEnabled(false); + chckbxRepeator.setEnabled(false); + chckbxExtender.setEnabled(false); + chckbxTarget.setEnabled(false); + chckbxSequencer.setEnabled(false); + chckbxSpider.setEnabled(false); + } + + } + + private void setActions() { + setActionsJComboBoxHelper(targetScopeOption); + setActionsJComboBoxHelper(pathRegExOption); + setActionsJTextFieldHelper(pathRegEx); + setActionsJComboBoxHelper(headerRegExOption); + setActionsJTextFieldHelper(headerRegEx); + setActionsJCheckBoxHelper(chckbxAllTools); + setActionsJCheckBoxHelper(chckbxProxy); + setActionsJCheckBoxHelper(chckbxScanner); + setActionsJCheckBoxHelper(chckbxIntruder); + setActionsJCheckBoxHelper(chckbxRepeator); + setActionsJCheckBoxHelper(chckbxExtender); + setActionsJCheckBoxHelper(chckbxTarget); + setActionsJCheckBoxHelper(chckbxSequencer); + setActionsJCheckBoxHelper(chckbxSpider); + } + + private void setActionsJCheckBoxHelper(JCheckBox checkbox) { + checkbox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + updateUIState(); + saveSettings(); + } + }); + } + + private void setActionsJComboBoxHelper(JComboBox combobox) { + combobox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + updateUIState(); + saveSettings(); + } + }); + } + + private void setActionsJTextFieldHelper(JTextField textfield) { + textfield.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void changedUpdate(DocumentEvent arg0) { + updateUIState(); + saveSettings(); + } + @Override + public void insertUpdate(DocumentEvent arg0) { + updateUIState(); + saveSettings(); + } + @Override + public void removeUpdate(DocumentEvent arg0) { + updateUIState(); + saveSettings(); + } + }); + } + + private void loadSettings() { + targetScopeOption.setSelectedIndex((int) loadExtensionSettingHelper("targetScopeOption","int",0)); + pathRegExOption.setSelectedIndex((int) loadExtensionSettingHelper("pathRegExOption","int",0)); + pathRegEx.setText((String) loadExtensionSettingHelper("pathRegEx","string","")); + headerRegExOption.setSelectedIndex((int) loadExtensionSettingHelper("headerRegExOption","int",0)); + headerRegEx.setText((String) loadExtensionSettingHelper("headerRegEx","string","x\\-my\\-http\\-smuggler: active")); + chckbxAllTools.setSelected((boolean) loadExtensionSettingHelper("chckbxAllTools","bool",false)); + chckbxProxy.setSelected((boolean) loadExtensionSettingHelper("chckbxProxy","bool",false)); + chckbxScanner.setSelected((boolean) loadExtensionSettingHelper("chckbxScanner","bool",false)); + chckbxIntruder.setSelected((boolean) loadExtensionSettingHelper("chckbxIntruder","bool",false)); + chckbxRepeator.setSelected((boolean) loadExtensionSettingHelper("chckbxRepeator","bool",true)); + chckbxExtender.setSelected((boolean) loadExtensionSettingHelper("chckbxExtender","bool",false)); + chckbxTarget.setSelected((boolean) loadExtensionSettingHelper("chckbxTarget","bool",false)); + chckbxSequencer.setSelected((boolean) loadExtensionSettingHelper("chckbxSequencer","bool",false)); + chckbxSpider.setSelected((boolean) loadExtensionSettingHelper("chckbxSpider","bool",false)); + } + + private void saveSettings() { + saveExtensionSettingHelper("targetScopeOption", targetScopeOption.getSelectedIndex()); + saveExtensionSettingHelper("pathRegExOption", pathRegExOption.getSelectedIndex()); + saveExtensionSettingHelper("pathRegEx", pathRegEx.getText()); + saveExtensionSettingHelper("headerRegExOption", headerRegExOption.getSelectedIndex()); + saveExtensionSettingHelper("headerRegEx", headerRegEx.getText()); + saveExtensionSettingHelper("chckbxAllTools", chckbxAllTools.isSelected()); + saveExtensionSettingHelper("chckbxProxy", chckbxProxy.isSelected()); + saveExtensionSettingHelper("chckbxScanner", chckbxScanner.isSelected()); + saveExtensionSettingHelper("chckbxIntruder", chckbxIntruder.isSelected()); + saveExtensionSettingHelper("chckbxRepeator", chckbxRepeator.isSelected()); + saveExtensionSettingHelper("chckbxExtender", chckbxExtender.isSelected()); + saveExtensionSettingHelper("chckbxTarget", chckbxTarget.isSelected()); + saveExtensionSettingHelper("chckbxSequencer", chckbxSequencer.isSelected()); + saveExtensionSettingHelper("chckbxSpider", chckbxSpider.isSelected()); + } + + private void resetSettings() { + saveExtensionSettingHelper("targetScopeOption", 0); + saveExtensionSettingHelper("pathRegExOption", 0); + saveExtensionSettingHelper("pathRegEx", ""); + saveExtensionSettingHelper("headerRegExOption", 0); + saveExtensionSettingHelper("headerRegEx", "x\\-my\\-http\\-smuggler: active"); + saveExtensionSettingHelper("chckbxAllTools", false); + saveExtensionSettingHelper("chckbxProxy", false); + saveExtensionSettingHelper("chckbxScanner", false); + saveExtensionSettingHelper("chckbxIntruder", false); + saveExtensionSettingHelper("chckbxRepeator", true); + saveExtensionSettingHelper("chckbxExtender", false); + saveExtensionSettingHelper("chckbxTarget", false); + saveExtensionSettingHelper("chckbxSequencer", false); + saveExtensionSettingHelper("chckbxSpider", false); + } + + private void saveExtensionSettingHelper(String name, Object value) { + try { + _callbacks.saveExtensionSetting(name, String.valueOf(value)); + }catch(Exception e) { + _stderr.println(e.getMessage()); + } + } + + private Object loadExtensionSettingHelper(String name, String type, Object defaultValue) { + Object value = null; + try { + String temp_value = _callbacks.loadExtensionSetting(name); + if(temp_value!=null && !temp_value.equals("")) { + switch(type.toLowerCase()){ + case "int": + case "integer": + value = Integer.valueOf(temp_value); + break; + case "bool": + case "boolean": + value = Boolean.valueOf(temp_value); + break; + default: + value = temp_value; + break; + } + } + }catch(Exception e) { + _stderr.println(e.getMessage()); + } + + if(value==null) { + value = defaultValue; + } + return value; + } +} diff --git a/src/resources/AboutMain.png b/src/resources/AboutMain.png new file mode 100644 index 0000000..4fe33cd Binary files /dev/null and b/src/resources/AboutMain.png differ