diff --git a/build.gradle b/build.gradle index d6b9d0c..07fc93f 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ repositories { } dependencies { - compile 'net.portswigger.burp.extender:burp-extender-api:1.7.22' + implementation 'net.portswigger.burp.extender:burp-extender-api:1.7.22' } sourceSets { diff --git a/burp/BurpExtender.java b/burp/BurpExtender.java index c754e10..8f85d9c 100644 --- a/burp/BurpExtender.java +++ b/burp/BurpExtender.java @@ -1,6 +1,17 @@ package burp; import javax.swing.SwingUtilities; + +import burp.IBurpExtender; +import burp.IBurpExtenderCallbacks; +import burp.IExtensionHelpers; +import burp.IExtensionStateListener; +import burp.IHttpRequestResponse; +import burp.IRequestInfo; +import burp.IResponseInfo; +import burp.ISessionHandlingAction; +import burp.ITab; + import java.awt.Component; import java.util.ArrayList; import java.util.Arrays; @@ -12,6 +23,7 @@ public class BurpExtender implements IBurpExtender, ISessionHandlingAction, ITab IExtensionHelpers helpers = null; Pattern p; + private String lastToken = null; static String extensionName = "Add Custom Header"; IBurpExtenderCallbacks callbacks = null; @@ -28,6 +40,7 @@ public class BurpExtender implements IBurpExtender, ISessionHandlingAction, ITab final String KEY_MODE = "setting_mode"; final String KEY_HEADER_NAME = "settings_header_name"; final String KEY_HEADER_VALUE_PREFIX = "settings_header_value_prefix"; + final String KEY_LAST_TOKEN = "setting_last_token"; private BurpTab tab; @@ -35,6 +48,14 @@ void useRegExp() { } + private String ellipsize(String text, int maxLength) { + final int minLength = 30; + if (text == null || text.length() <= maxLength || maxLength <= minLength) { + return text; + } + return text.substring(0, minLength) + "..." + text.substring(text.length() - maxLength + minLength); + } + @Override public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) { this.callbacks = callbacks; @@ -79,6 +100,7 @@ public void run() { }else if("HARD_CODED".equalsIgnoreCase(mode)){ tab.setUseHardCoded(); } + lastToken = callbacks.loadExtensionSetting(KEY_LAST_TOKEN); callbacks.printOutput("Mode loaded: " + mode); // force update the example label @@ -102,16 +124,18 @@ public String getActionName() { public void performAction(IHttpRequestResponse currentRequest, IHttpRequestResponse[] macroItems) { + if (tab == null) { + callbacks.printError("Cannot apply custom header because extension UI is not ready"); + return; + } + String token = null; + int macroCount = macroItems == null ? 0 : macroItems.length; if (tab.useHardCoded()) { // token has priority over regexp token = tab.getHardCodedText(); } else if (tab.useRegExp()) { - if (macroItems.length == 0) { - this.callbacks.issueAlert("No macro configured or macro did not return any response"); - return; - } String regexp = tab.getRegExpText(); try { p = Pattern.compile(regexp); @@ -122,16 +146,23 @@ public void performAction(IHttpRequestResponse currentRequest, } // go through all macros and run the regular expression on their body - for (int i = 0; i < macroItems.length; i++) { + for (int i = 0; i < macroCount; i++) { byte[] _responseBody = macroItems[i].getResponse(); - if (_responseBody == null) return; + if (_responseBody == null) { + return; + } IResponseInfo macroResponse = helpers.analyzeResponse(_responseBody); - if (macroResponse == null ) return; + if (macroResponse == null ) { + return; + } String responseBody = helpers.bytesToString(_responseBody); Matcher m = p.matcher(responseBody); if (m.find()) { token = m.group(1); if (token != null && token.length() > 0) { + lastToken = token; + callbacks.saveExtensionSetting(KEY_LAST_TOKEN, lastToken); + callbacks.printOutput("Regex matched new token (macro index " + i + ", preview=" + ellipsize(token, 80) + ")"); // found it break; } @@ -143,9 +174,14 @@ public void performAction(IHttpRequestResponse currentRequest, } if (token == null) { - // nothing found: failing silently to avoid polluting the logs - callbacks.printError("No token found"); - return; + if (lastToken != null && lastToken.length() > 0) { + token = lastToken; + callbacks.printOutput("No token found in macro response, reusing last stored token"); + } else { + // nothing found: failing silently to avoid polluting the logs + callbacks.printError("No token found"); + return; + } } String headerName = tab.getHeaderName(); @@ -162,7 +198,7 @@ public void performAction(IHttpRequestResponse currentRequest, } String newHeader = headerName + ": " + headerValuePrefix + token; headers.add(newHeader); - callbacks.printOutput("Added header: '" + newHeader + "'"); + callbacks.printOutput("Added header: '" + ellipsize(newHeader, 80) + "'"); byte[] message = helpers.buildHttpMessage(headers, Arrays.copyOfRange(currentRequest.getRequest(), rqInfo.getBodyOffset(), currentRequest.getRequest().length)); currentRequest.setRequest(message); @@ -195,6 +231,7 @@ public void extensionUnloaded() { mode = "REGEX"; } callbacks.saveExtensionSetting(KEY_MODE, mode); + callbacks.saveExtensionSetting(KEY_LAST_TOKEN, lastToken); callbacks.printOutput("Settings saved!"); } // end ITab methods