TCM Security is offering free Active Directory Health Checks to any company with 10 or more employees. To inquire, please contact us.


In the third part of our series on creating extensions for Burp Suite using the Montoya API, we will learn about design patterns and implement a Singleton object. Additionally, we’ll ensure our custom header only gets added to in-scope requests.

If you haven’t read the other parts of the series yet, we highly recommend you do that first. It will guide you through everything we have done so far in this series.

Whether you’re a web app pentester seeking to optimize your Burp Suite experience or an enthusiastic developer eager to explore the realm of cybersecurity, this guide is for you.

Software Design Patterns

Software design patterns are akin to foundational blueprints, offering reusable solutions to common problems in software design. The Object Pool Pattern, like a recycling center for objects, enhances performance by reusing objects instead of generating new ones.

The focal point of this blog post is the Singleton pattern. This pattern ensures the creation of only a single instance of a certain object. We will use the Singleton pattern as a wrapper for the Montoya API. It is initially provided to us in the ‘initialize’ method of the ‘MyFirstExtension’ class. To simplify its use across different classes, we can pass it to a Singleton object instead of via constructors.

To achieve this, we introduce a new Java class file named ‘MAPI.java’:


import burp.api.montoya.MontoyaApi;

public final class MAPI {

    private static MontoyaApi INSTANCE;

    private MAPI() {}

    public static void initialize(MontoyaApi api) {
        if (INSTANCE == null) {
            INSTANCE = api;

    public static MontoyaApi getAPI() {
        return INSTANCE;

This class stores the MontoyaAPI instance in a static variable ‘INSTANCE’. A private constructor prevents the creation of multiple instances of this class. The class includes an initialization method to receive the MontoyaAPI instance, ensuring single-instance use via a conditional check.

In the ‘MyFirstExtension’ class, we modify the initialize method to use the ‘MAPI’ initialize method, passing in the Montoya API instance:


public void initialize(MontoyaApi api) {


        api.extension().setName("My First Extension");

This change streamlines the process, eliminating the need to pass the Montoya API instance via constructors. For this reason, we can remove the api reference when creating an ‘UnloadingHandler’ instance:


api.extension().registerUnloadingHandler(new UnloadingHandler(handler));

However, this change will cause an error. The ‘UnloadingHandler’ constructor currently requires the MontoyaAPI to be passed to it. We can fix this by overhauling the ‘UnloadingHandler’ code:


import burp.api.montoya.extension.ExtensionUnloadingHandler;

public class UnloadingHandler implements ExtensionUnloadingHandler {

    private MyFirstHttpHandler handler;

    public UnloadingHandler(MyFirstHttpHandler handler) {
        this.handler = handler;
    public void extensionUnloaded() {
        MAPI.getAPI().persistence().preferences().setString("hash", this.handler.getHash());

Furthermore, we need to revise the ‘MyFirstHttpHandler’ code. With our modifications, it now generates and applies the custom header solely for requests within the defined scope. Conditional checks and logical operations are incorporated for efficient request handling. This code also features hash logging using the API wrapper:


public RequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent httpRequestToBeSent) {

    if (!this.hash.isEmpty() && httpRequestToBeSent.isInScope()) {
        HttpRequest request = httpRequestToBeSent.withAddedHeader("X-Hash", this.hash);
        return RequestToBeSentAction.continueWith(request);

    return null;

  public ResponseReceivedAction handleHttpResponseReceived(HttpResponseReceived httpResponseReceived) {

      if (httpResponseReceived.initiatingRequest().isInScope()) {
          String input = "";

          if (httpResponseReceived.hasHeader("Age")) {
              input += httpResponseReceived.headerValue("Age");

          if (httpResponseReceived.hasHeader("Date")) {
              input += httpResponseReceived.headerValue("Date");

          try {
              MessageDigest digest = MessageDigest.getInstance("SHA-256");
              this.hash = HexFormat.of().formatHex(digest.digest());
              MAPI.getAPI().logging().logToOutput("Hash generated: " + this.hash);
          } catch (NoSuchAlgorithmException e) {
              throw new RuntimeException(e);

      return null;

Testing these modifications in Burp Suite is crucial. We can do this by altering the Scope settings and monitoring the behavior of the extension with different URLs:

Adding example.com to the Target Scope in Burp

For instance, when we do not include ‘example.com’ in Burp’s scope settings, the extension does not add the header to its requests. Conversely, once added, the extension applies the header as intended:

A request to example.com with the X-Hash header highlighted.


With these changes, we can now seamlessly integrate the Montoya API anywhere in our extension code. This avoids the need to pass it via a constructor. Additionally, the header generating code will only impact in-scope requests. The next part of our series will introduce more advanced features, so stay tuned!

Penetration Testing – PCI Compliance – Auditing

See How We Can Secure Your Assets

Let’s talk about how TCM Security can solve your cybersecurity needs. Give us a call, send us an e-mail, or fill out the contact form below to get started.


tel: (877) 771-8911 | email: info@tcm-sec.com
hbspt.forms.create({ region: "na1", portalId: "7675483", formId: "166251aa-f1a2-4408-929a-7405f1c123a2" });