From 2c67bc3ac92c82c8943e50cdca7c9acdfc774cd1 Mon Sep 17 00:00:00 2001 From: A809826 Date: Mon, 20 Apr 2026 10:38:04 +0200 Subject: [PATCH] Change affects only the regex mode. The last token grabbed from a macro output is stored and applied out of macro context or when no regex matches. This way the step "Add Customer Handler" can be used at the beginning of session rule, followed by "Check session is valid", and being again the last step of a refresh token macro. This is especially useful for Authorization Bearer refresh only on demand, only when the current request's Bearer expires and response is 401 unauthorized. --- build.gradle | 2 +- burp/BurpExtender.java | 59 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 49 insertions(+), 12 deletions(-) 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