Automating Authenticated Testing with Custom BurpSuite Extension & Browser Tooling

16 min read

Overview

During an in-depth security assessment of a modern web application, I encountered a recurring friction point that significantly slowed down my testing workflow: short-lived JWT sessions combined with aggressive inactivity timeouts.

While these controls are valid from a security standpoint, they created a practical challenge during testing. Every few minutes, I was forced to manually re-authenticate, copy fresh tokens, and update requests in Burp tools such as the Repeater. Over time, this overhead diverted focus away from what mattered most — deep analysis and pentest of application logic and vulnerability discovery.

Rather than working around the problem repeatedly, I decided to engineer a solution.

The Challenge

The application implemented the following authentication behavior:

From a testing perspective, this caused several issues:

I wanted a setup where:

Design Approach

I broke the problem into two distinct automation layers:

  1. Browser-side automation Ensure the authenticated browser session never expires

  2. Burp-side automation Ensure all Burp tools always use the most recent access token

This separation allowed each layer to do one thing well and remain adaptable to other applications.

Browser Automation: Maintaining an Active Session

Why browser automation was necessary

The frontend session timeout was not passive. Even minor mouse movement caused the popup to disappear without refreshing the token, which meant the session would still expire shortly after.

To reliably maintain the session, the popup had to be explicitly clicked, simulating a real user action.

Solution: Tampermonkey Automation

Following some research, I settled on the Tampermonkey browser extension (Github repo: tampermonkey).

In short, Tampermonkey is a powerful browser extension that allows userscripts to be executed on specific web pages.

Userscripts are small JavaScript programs that can modify page behavior, automate user interactions, and extend application functionality directly within the browser context.

After understanding how Tampermonkey operates and validating that it could effectively address the challenge I was facing, I implemented the following lightweight Tampermonkey userscript to automate the session-continuation workflow.

Tampermonkey Script

// ==UserScript==
// @name         Auto-Click Continue Session
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  Automatically clicks the "Continue Session" popup to keep the session alive
// @match        https://{my-web-app-root-url}/*
// @grant        none
// ==/UserScript==

(function () {
  'use strict';

  // Run every 3 seconds to detect the inactivity popup
  setInterval(() => {
    const btn = document.querySelector(
      '[data-role="activity-monitor-continue-session"]'
    );

    if (btn) {
      console.log("Session popup detected. Refreshing session.");

      // Ensure the button is visible
      btn.scrollIntoView({ behavior: "smooth", block: "center" });

      // Simulate a real user click sequence
      ["mouseover", "mousedown", "mouseup", "click"].forEach(eventType => {
        btn.dispatchEvent(
          new MouseEvent(eventType, {
            bubbles: true,
            cancelable: true,
            view: window
          })
        );
      });
    }
  }, 3000);
})();

Outcome: The browser remained authenticated for the entire testing session without manual interaction.

Burp Automation: Injecting Fresh JWTs Automatically

Why Burp’s native features weren’t enough

I evaluated Burp’s Session Handling Rules and macros, but they didn’t fully meet my needs:

This led me to build a custom Burp Suite extension in Python. I loaded the extension using Jython, leveraging Burp’s native support for Python-based extensions via the Jython standalone package. This approach allowed me to integrate directly with Burp’s request-handling workflow while maintaining the flexibility and speed of Python for rapid iteration.

Burp Extension: Auto-Inject Latest JWT

How the extension works

The extension implements the following logic:

  1. Continuously scans Burp’s Proxy history
  2. Identifies responses from the token refresh endpoint
  3. Extracts the latest access_token
  4. Stores it in memory
  5. Automatically injects it into all in-scope Repeater requests
  6. Refreshes the token pool periodically in the background

Key Benefits

Burp Extension Code (Python)

# -*- coding: utf-8 -*-
# Burp Extension: Auto-Inject Latest JWT. Its purpose is to automatically inject the latest valid JWT into in-scope Repeater requests

from burp import IBurpExtender, IHttpListener, IExtensionStateListener
import re
import threading
import time

REFRESH_ENDPOINT = "{path-to-token-endpoint}/token"
ACCESS_TOKEN_REGEX = r'"access_token"\s*:\s*"([^"]+)"'

class BurpExtender(IBurpExtender, IHttpListener, IExtensionStateListener):

    def registerExtenderCallbacks(self, callbacks):
        self._callbacks = callbacks
        self._helpers = callbacks.getHelpers()
        callbacks.setExtensionName("Auto JWT Injector")

        self.latest_token = None
        self.running = True

        callbacks.registerHttpListener(self)
        callbacks.registerExtensionStateListener(self)

        # Background thread to refresh token cache
        t = threading.Thread(target=self.refresh_loop)
        t.daemon = True
        t.start()

        print("[AutoJWT] Extension loaded.")

    def refresh_loop(self):
        """Periodically scans proxy history for fresh tokens"""
        while self.running:
            self.check_latest_token()
            time.sleep(180)

    def check_latest_token(self):
        """Extract latest access token from refresh endpoint responses"""
        history = self._callbacks.getProxyHistory()
        for item in reversed(history):
            req_info = self._helpers.analyzeRequest(item)
            if REFRESH_ENDPOINT in req_info.getUrl().getPath():
                resp = item.getResponse()
                if resp:
                    resp_str = self._helpers.bytesToString(resp)
                    match = re.search(ACCESS_TOKEN_REGEX, resp_str)
                    if match:
                        token = match.group(1)
                        if token != self.latest_token:
                            self.latest_token = token
                            print("[AutoJWT] Token updated.")
                        return

    def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo):
        """Inject latest JWT into in-scope Repeater requests"""
        if not messageIsRequest or not self.latest_token:
            return

        if toolFlag == self._callbacks.TOOL_REPEATER:
            req_info = self._helpers.analyzeRequest(messageInfo)
            url = req_info.getUrl()

            if not self._callbacks.isInScope(url):
                return

            headers = list(req_info.getHeaders())
            new_headers = []
            auth_found = False

            for h in headers:
                if h.lower().startswith("authorization:"):
                    new_headers.append("Authorization: Bearer " + self.latest_token)
                    auth_found = True
                else:
                    new_headers.append(h)

            if not auth_found:
                new_headers.append("Authorization: Bearer " + self.latest_token)

            body = messageInfo.getRequest()[req_info.getBodyOffset():]
            new_req = self._helpers.buildHttpMessage(new_headers, body)
            messageInfo.setRequest(new_req)

    def extensionUnloaded(self):
        self.running = False
        print("[AutoJWT] Extension unloaded.")

Results & Impact

By automating both the browser session lifecycle and Burp token handling, I achieved:

This setup became reusable across other applications with similar authentication patterns, requiring only minor configuration changes.

Key Takeaway

This work reinforced an important principle in my approach to security testing:

Effective security testing is not just about tools — it’s about removing friction so attention stays on logic, trust boundaries, vulnerabbility and wekanesses discovery and real risk.

By investing time in automation and custom tooling, I was able to improve both the quality and depth of my assessment work.


✦ nzesi

© 2026

𝕏 GitHub