Skip to content

Commit

Permalink
Huge 2.0 release. Changes below:
Browse files Browse the repository at this point in the history
--------------------------------
Moved to the new Montoya APIs
Switched to newest BurpSuiteGuiLibrary
Refactored and commented code for sanity
Performance and code QoL improvements
  • Loading branch information
Static-Flow committed May 24, 2023
1 parent f5a7630 commit 78f2ad8
Show file tree
Hide file tree
Showing 5 changed files with 324 additions and 182 deletions.
26 changes: 12 additions & 14 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,24 @@

<groupId>staticflow</groupId>
<artifactId>RepeaterSearch</artifactId>
<version>1.0-SNAPSHOT</version>

<version>2.0-SNAPSHOT</version>
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>net.portswigger.burp.extender</groupId>
<artifactId>burp-extender-api</artifactId>
<version>2.1</version>
<groupId>net.portswigger.burp.extensions</groupId>
<artifactId>montoya-api</artifactId>
<version>2023.3</version>
</dependency>
<dependency>
<groupId>staticflow</groupId>
<groupId>com.github.Static-Flow</groupId>
<artifactId>BurpSuiteGuiLibrary</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.27.0-GA</version>
<version>2.1</version>
</dependency>


</dependencies>

<build>
Expand Down
77 changes: 77 additions & 0 deletions src/main/java/com/staticflow/ExtensionState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.staticflow;

import burp.api.montoya.MontoyaApi;
import main.java.com.staticflow.BurpGuiControl;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

/**
* This Singleton class holds all custom state for the extension and provides a central means of accessing it.
*/
public class ExtensionState {
private static final String REPEATER = "Repeater";


private final Component repeaterComponent;
private final JTabbedPane repeaterTabbedPane;
private MontoyaApi callbacks;

private static ExtensionState state;

/**
* Initializes the {@code ExtensionState} Singleton.
*
* This constructor obtains a reference to the Burp Suite Repeater tab swing component using
* {@link BurpGuiControl#getBaseBurpComponent}.
* <br>
* It then attaches a hierarchy listener to the Repeater tab component to determine when the Repeater tab
* is in view.
* <br>
* When the Repeater tab is shown (using the {@link HierarchyEvent#SHOWING_CHANGED} flag of the hierarchy event), the
* custom search bar created by this extension is set to be visible. This is because anytime a new tab is created in Repeater, Burp Suite recreates the
* whole Repeater tab and JTabbedPane which for some magic swing reason sets our custom Component to be hidden.
* <br>
* Finally, this constructor obtains a reference to the {@link JTabbedPane} within the Repeater tab
* component using {@link BurpGuiControl#findFirstComponentOfType}.
*/
private ExtensionState() {
this.repeaterComponent = BurpGuiControl.getBaseBurpComponent(REPEATER);
this.repeaterComponent.addHierarchyListener(e -> {
if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) {
Container component = (Container) e.getComponent();
if (component.isShowing()) {
component.getComponent(0).setVisible(true);
}
}
});
this.repeaterTabbedPane = (JTabbedPane) BurpGuiControl.findFirstComponentOfType((Container) repeaterComponent,JTabbedPane.class);
}

/*
GETTERS/SETTERS BELOW
*/
static ExtensionState getInstance() {
if (state == null) {
state = new ExtensionState();
}
return state;
}

public JTabbedPane getRepeaterTabbedPane() {
return repeaterTabbedPane;
}

public Component getRepeaterComponent() {
return this.repeaterComponent;
}

public MontoyaApi getCallbacks() {
return this.callbacks;
}

public void setCallbacks(MontoyaApi callbacks) {
this.callbacks = callbacks;
}

}
195 changes: 27 additions & 168 deletions src/main/java/com/staticflow/RepeaterSearch.java
Original file line number Diff line number Diff line change
@@ -1,180 +1,39 @@
package com.staticflow;

import burp.IBurpExtender;
import burp.IBurpExtenderCallbacks;
import burp.IExtensionStateListener;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RepeaterSearch implements IBurpExtender, IExtensionStateListener {

public static final String REPEATER = "Repeater";
public static final String SEARCH = "Search";
public static final String ENTER_QUERY = "Enter query...";
private Component repeaterComponent;
private IBurpExtenderCallbacks callbacks;
private boolean searchResponseForText;
private boolean searchRequestForText;
private boolean useRegex;
import burp.api.montoya.BurpExtension;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.extension.ExtensionUnloadingHandler;

/**
* The entry point for a Burp Suite extension that adds a custom search bar to the Repeater tab.
* This extension allows users to search through the request body and/or the response body of requests
* using a simple string or a regular expression (regex).
*
* The `RepeaterSearch` class implements the `BurpExtension` and `ExtensionUnloadingHandler` interfaces,
* allowing it to handle extension initialization and unloading events.
*
* Upon initialization, the `initialize` method registers the extension's unloading handler, sets the
* necessary callbacks, and adds the search bar to the Repeater tab.
*
* When the extension is unloaded, the `extensionUnloaded` method is called to perform any necessary
* clean-up operations.
*
* @see BurpExtension
* @see ExtensionUnloadingHandler
*/
public class RepeaterSearch implements BurpExtension, ExtensionUnloadingHandler {

@Override
public void registerExtenderCallbacks(IBurpExtenderCallbacks iBurpExtenderCallbacks) {
this.searchRequestForText = true;
this.callbacks = iBurpExtenderCallbacks;
iBurpExtenderCallbacks.registerExtensionStateListener(this);
this.repeaterComponent = BurpGuiControl.getBaseBurpComponent(REPEATER);
JPanel combined = new JPanel(new GridBagLayout());
JPanel searchBarPanel = new JPanel(new GridBagLayout());
JPanel searchBarButtonsPanel = new JPanel();
searchBarButtonsPanel.setLayout(new BoxLayout(searchBarButtonsPanel,
BoxLayout.Y_AXIS));
JButton searchButton = new JButton(SEARCH);
JTextField searchBar = new JTextField(ENTER_QUERY);
GridBagConstraints c = new GridBagConstraints();
GridBagConstraints gbc = new GridBagConstraints();

c.gridx = 0;
c.gridy = 0;
c.weightx = 0.90;
c.weighty = 0.05;
c.fill = GridBagConstraints.BOTH;
searchBar.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
if (searchBar.getText().equals(ENTER_QUERY)) {
searchBar.setText("");
}
}

@Override
public void mousePressed(MouseEvent e) {
//UnNeeded
}

@Override
public void mouseReleased(MouseEvent e) {
//UnNeeded
}

@Override
public void mouseEntered(MouseEvent e) {
//UnNeeded
}

@Override
public void mouseExited(MouseEvent e) {
if (searchBar.getText().isEmpty()) {
searchBar.setText(ENTER_QUERY);
}
}
});
gbc.gridx = 0;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.BOTH;
gbc.weightx = 1;
gbc.weighty = 0.50;
searchBarPanel.add(searchBar,gbc);
searchBar.addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent e) {
searchButton.setText(SEARCH);
resetRepeaterTabs();
}

@Override
public void keyPressed(KeyEvent e) {
//UnNeeded
}

@Override
public void keyReleased(KeyEvent e) {
//UnNeeded
}
});
searchButton.addActionListener(e -> {
if(searchButton.getText().equals(SEARCH)) {
searchRepeaterTabsForString(searchBar.getText());
searchButton.setText("Clear");
} else {
resetRepeaterTabs();
searchBar.setText(ENTER_QUERY);
searchButton.setText(SEARCH);
resetRepeaterTabs();
}
});
searchBarButtonsPanel.add(searchButton);
JCheckBox searchRequest = new JCheckBox("Request");
searchRequest.setSelected(true);
searchRequest.addChangeListener(e -> searchRequestForText = !searchRequestForText);
searchBarButtonsPanel.add(searchRequest);
JCheckBox searchResponse = new JCheckBox("Response");
searchResponse.addChangeListener(e -> searchResponseForText = !searchResponseForText);
searchBarButtonsPanel.add(searchResponse);
JCheckBox searchRegex = new JCheckBox("Regex");
searchRegex.addChangeListener(e -> useRegex = !useRegex);
searchBarButtonsPanel.add(searchRegex);
combined.add(searchBarPanel,c);
c.gridx = 1;
c.weightx = 0.10;
combined.add(searchBarButtonsPanel,c);
c.gridy = 1;
c.gridx = 0;
c.gridwidth = 2;
c.weighty = 0.95;
combined.add(repeaterComponent,c);
iBurpExtenderCallbacks.customizeUiComponent(combined);
BurpGuiControl.addBaseBurpComponent(REPEATER,combined);
public void initialize(MontoyaApi api) {
api.extension().registerUnloadingHandler(this);
ExtensionState.getInstance().setCallbacks(api);
Utils.addSearchBarToRepeaterTab();
}

@Override
public void extensionUnloaded() {
resetRepeaterTabs();
BurpGuiControl.replaceBaseBurpComponent(REPEATER,this.repeaterComponent);
}

private void resetRepeaterTabs(){
JTabbedPane repeaterTabs = ((JTabbedPane)this.repeaterComponent);
for(int i=0; i < repeaterTabs.getTabCount()-1; i++) {
repeaterTabs.setBackgroundAt(i,new Color(0xBBBBBB));

}
}

private void searchRepeaterTabsForString(String search) {
JTabbedPane repeaterTabs = ((JTabbedPane)this.repeaterComponent);
for( int i=0; i < repeaterTabs.getTabCount()-1; i++) {
try {
if ( searchRequestForText ) {
JTextArea requestTextArea =
BurpGuiControl.getRepeaterTabRequestTextArea((Container) repeaterTabs.getComponentAt(i));
if (searchTextArea(search,requestTextArea) ) {
repeaterTabs.setBackgroundAt(i,new Color(0xff6633));
}
} else if ( searchResponseForText ) {
JTextArea responseTextArea =
BurpGuiControl.getRepeaterTabResponseTextArea((Container) repeaterTabs.getComponentAt(i));
if (searchTextArea(search, responseTextArea)) {
repeaterTabs.setBackgroundAt(i,new Color(0xff6633));
}
}
}catch(ArrayIndexOutOfBoundsException e) {
this.callbacks.printError(e.getMessage());
}
}
}

private boolean searchTextArea(String search, JTextArea textArea) {
if (useRegex) {
Pattern pattern = Pattern.compile(search,Pattern.MULTILINE);
Matcher matcher = pattern.matcher(textArea.getText());
return matcher.find();
} else {
return textArea.getText().contains(search);
}
Utils.cleanUpExtension();
}

}
Loading

0 comments on commit 78f2ad8

Please sign in to comment.