Skip to content

Commit

Permalink
Added new When constraint: When In Scope. Allow Reshaper to be target…
Browse files Browse the repository at this point in the history
…ed from Session Handler Rules. Use variable tags to access cookie jar items. Disable HTML in dynamic text displays.
  • Loading branch information
ddwightx committed Feb 21, 2022
1 parent b69069e commit 80d95ed
Show file tree
Hide file tree
Showing 24 changed files with 259 additions and 11 deletions.
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ plugins {
}

group 'com.synfron.reshaper.burp'
version '1.6.2'
version '1.7.0'

targetCompatibility = '15'
sourceCompatibility = '15'
Expand All @@ -28,6 +28,7 @@ dependencies {
implementation 'com.jayway.jsonpath:json-path:2.6.0'
implementation 'net.portswigger.burp.extender:burp-extender-api:2.3'
implementation 'org.rypt:f8:1.1-RC1'
implementation 'org.apache.commons:commons-csv:1.8'
implementation files('libs/htmlchardet-1.0.2.1.jar')
compileOnly 'org.projectlombok:lombok:1.18.22'
annotationProcessor 'org.projectlombok:lombok:1.18.22'
Expand Down
2 changes: 2 additions & 0 deletions docs/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ Proxy Name - If received by a certain Burp proxy listener

From Tool - If the HTTP message is from a specific Burp tool

In Scope - If the URL is in the suite-wide scope

[More](https://synfron.github.io/ReshaperForBurp/Rules.html#whens)

### Thens
Expand Down
14 changes: 13 additions & 1 deletion docs/Rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,25 @@ Response MIME Type - HTML, Script, CSS, JSON, SVG, Other XML, Other Text, Image,

If received by a certain Burp proxy listener

#### Fields

Proxy Name - The Burp proxy listener interface (e.g. 127.0.0.1:8080)

### From Tool

If the HTTP message is from a specific Burp tool

Tool - Proxy, Repeater, Intruder, Target, Spider, Scanner, or Extender
#### Fields

Tool - Proxy, Repeater, Intruder, Target, Spider, Scanner, Extender, or Session

### In Scope

If the URL is in the suite-wide scope

#### Fields

URL - The URL to check or leave blank to use the current request's URL. Supports variable tags.

## Thens

Expand Down
2 changes: 2 additions & 0 deletions docs/Variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,7 @@ For example, if Global variable named `firstName` has the value `John` and varia

**Special Character Tag (special, s):** `{{s:specialCharacterSequences}}`. Examples: `{{s:n}}` (new line), `{{s:rn}}` (carriage return + new line), `{{s:u00A9}}` (Copyright symbol)

**Cookie Jar Tag (cookiejar, cj):** `{{cookiejar:domain}}` or `{{cookiejar:domain:path}}`. Example: `{{cookiejar:example.com:/}}`


{% endraw %}
1 change: 1 addition & 0 deletions src/main/java/burp/BurpExtender.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {
callbacks.registerProxyListener(connector);
callbacks.registerHttpListener(connector);
callbacks.registerExtensionStateListener(connector);
callbacks.registerSessionHandlingAction(connector);
callbacks.registerContextMenuFactory(contextMenuHandler);

Log.get().withMessage("Reshaper started").log();
Expand Down
7 changes: 4 additions & 3 deletions src/main/java/synfron/reshaper/burp/core/BurpTool.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ public enum BurpTool {
Target(IBurpExtenderCallbacks.TOOL_TARGET),
Spider(IBurpExtenderCallbacks.TOOL_SPIDER),
Scanner(IBurpExtenderCallbacks.TOOL_SCANNER),
Extender(IBurpExtenderCallbacks.TOOL_EXTENDER);
Extender(IBurpExtenderCallbacks.TOOL_EXTENDER),
Session(null);

@Getter
private final int id;
private final Integer id;

BurpTool(int id) {
BurpTool(Integer id) {
this.id = id;
}

Expand Down
15 changes: 14 additions & 1 deletion src/main/java/synfron/reshaper/burp/core/Connector.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import burp.*;
import lombok.Getter;
import net.jodah.expiringmap.ExpiringMap;
import org.apache.commons.lang3.ArrayUtils;
import synfron.reshaper.burp.core.exceptions.WrappedException;
import synfron.reshaper.burp.core.messages.DataDirection;
import synfron.reshaper.burp.core.messages.EventInfo;
Expand All @@ -25,7 +26,7 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Connector implements IProxyListener, IHttpListener, IExtensionStateListener {
public class Connector implements IProxyListener, IHttpListener, IExtensionStateListener, ISessionHandlingAction {

private static final AtomicInteger lastMessageId = new AtomicInteger(1);
@Getter
Expand Down Expand Up @@ -233,4 +234,16 @@ private BurpTool getBurpToolIfEnabled(int toolFlag) {
BurpTool burpTool = BurpTool.getById(toolFlag);
return burpTool != null && BurpExtender.getGeneralSettings().isCapture(burpTool) ? burpTool : null;
}

@Override
public String getActionName() {
return "Reshaper";
}

@Override
public void performAction(IHttpRequestResponse currentRequest, IHttpRequestResponse[] macroItems) {
boolean messageIsRequest = ArrayUtils.isEmpty(currentRequest.getResponse());
IEventInfo eventInfo = asEventInfo(messageIsRequest, BurpTool.Session, currentRequest);
processEvent(messageIsRequest, eventInfo, null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@ public void logHas(When<?> when, Object value, Object subValue, boolean result)
)));
}

@Override
public void logValue(When<?> when, boolean result, Object... values) {
getRecords().add(new DiagnosticRecord(DiagnosticEntityType.When, String.format(
"%-4sWhen %s('%s') %s\n",
isLast(DiagnosticEntityType.When) ? toPrefix(when.isUseOrCondition()) : "",
when.getType().getName(),
toValuePhrase(values),
toResultPhrase(result, when.isNegate())
)));
}

private boolean isLast(DiagnosticEntityType entityType) {
List<DiagnosticRecord> records = getRecords();
return records.size() > 0 && records.get(records.size() - 1).getEntityType() == entityType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public interface IDiagnostics {

void logHas(When<?> when, Object value, Object subValue, boolean result);

void logValue(When<?> when, boolean result, Object... values);

void logValue(Then<?> then, boolean hasError, Object... values);

void logProperties(Then<?> then, boolean hasError, List<? extends Pair<String, ? extends Serializable>> properties);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import lombok.Getter;
import lombok.Setter;
import synfron.reshaper.burp.core.messages.IEventInfo;
import synfron.reshaper.burp.core.messages.MimeType;
import synfron.reshaper.burp.core.rules.IRuleOperation;
import synfron.reshaper.burp.core.utils.Serializer;

Expand All @@ -13,7 +14,10 @@
@JsonSubTypes.Type(value = WhenEventDirection.class),
@JsonSubTypes.Type(value = WhenHasEntity.class),
@JsonSubTypes.Type(value = WhenMatchesText.class),
@JsonSubTypes.Type(value = WhenProxyName.class)
@JsonSubTypes.Type(value = WhenContentType.class),
@JsonSubTypes.Type(value = WhenMimeType.class),
@JsonSubTypes.Type(value = WhenProxyName.class),
@JsonSubTypes.Type(value = WhenInScope.class)
})
public abstract class When<T extends When<T>> implements IRuleOperation<T> {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package synfron.reshaper.burp.core.rules.whens;

import burp.BurpExtender;
import lombok.Getter;
import lombok.Setter;
import synfron.reshaper.burp.core.messages.IEventInfo;
import synfron.reshaper.burp.core.rules.RuleOperationType;
import synfron.reshaper.burp.core.vars.VariableString;

import java.net.MalformedURLException;
import java.net.URL;

public class WhenInScope extends When<WhenInScope> {


@Getter
@Setter
private VariableString url;

@Override
public boolean isMatch(IEventInfo eventInfo) {
boolean isMatch = false;
String url = VariableString.getTextOrDefault(eventInfo, this.url, eventInfo.getUrl());
try {
isMatch = BurpExtender.getCallbacks().isInScope(new URL(url));
} catch (Exception ignored) {
}
if (eventInfo.getDiagnostics().isEnabled()) eventInfo.getDiagnostics().logValue(this, isMatch, url);
return isMatch;
}

@Override
public RuleOperationType<WhenInScope> getType() {
return WhenType.InScope;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class WhenType<T extends When<T>> extends RuleOperationType<T> {
public static final WhenType<WhenMimeType> MimeType = new WhenType<>("MIME Type", WhenMimeType.class);
public static final WhenType<WhenProxyName> ProxyName = new WhenType<>("Proxy Name", WhenProxyName.class);
public static final WhenType<WhenFromTool> FromTool = new WhenType<>("From Tool", WhenFromTool.class);
public static final WhenType<WhenInScope> InScope = new WhenType<>("In Scope", WhenInScope.class);

private WhenType() {
this(null, null);
Expand All @@ -31,7 +32,8 @@ public static List<WhenType<?>> getTypes() {
ContentType,
MimeType,
ProxyName,
FromTool
FromTool,
InScope
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public boolean isCapture(BurpTool burpTool) {
case Scanner -> isCaptureScanner();
case Intruder -> isCaptureIntruder();
case Extender -> isCaptureExtender();
case Session -> true;
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ public enum VariableSource {
Global("g", false),
Message("m", true),
File("f", true),
Special("s", true);
Special("s", true),
CookieJar("cj", true);

private final String shortName;
private final boolean accessor;
Expand Down
46 changes: 46 additions & 0 deletions src/main/java/synfron/reshaper/burp/core/vars/VariableString.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package synfron.reshaper.burp.core.vars;

import burp.BurpExtender;
import burp.ICookie;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.EnumUtils;
import org.apache.commons.lang3.StringUtils;
Expand All @@ -14,6 +18,7 @@
import synfron.reshaper.burp.core.utils.TextUtils;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
Expand All @@ -36,6 +41,12 @@ public VariableString(String text, List<VariableSourceEntry> variables) {
this.variables = variables;
}

public static CSVFormat getParamFormat() {
return CSVFormat.DEFAULT.withDelimiter(':')
.withAllowMissingColumnNames(true)
.withEscape('\\');
}

public static boolean isValidVariableName(String name) {
return StringUtils.isNotEmpty(name) && !Pattern.matches("\\{\\{|}}", name);
}
Expand Down Expand Up @@ -108,6 +119,7 @@ public String getText(IEventInfo eventInfo)
case Message -> getMessageVariable(eventInfo, variable.getName());
case File -> getFileText(eventInfo, variable.getName());
case Special -> variable.getName();
case CookieJar -> getCookie(variable.getName());
default -> null;
};
variableVals.add(value);
Expand All @@ -124,6 +136,40 @@ public String getText(IEventInfo eventInfo)
return String.format(text, variableVals.toArray());
}

private String getCookie(String locator) {
try {
String[] parts = locator.split(":", 3);
String domain = parts[0];
String name = parts[1];
String path = CollectionUtils.elementAtOrDefault(parts, 2);
if (Arrays.stream(parts).anyMatch(part -> part.startsWith("\""))) {
CSVParser csvParser = CSVParser.parse(locator, getParamFormat());
CSVRecord record = csvParser.getRecords().get(0);
if (record.size() == 2) {
domain = record.get(0);
name = record.get(1);
path = null;
} else if (record.size() == 3) {
domain = record.get(0);
name = record.get(1);
path = record.get(2);
}
}
for (ICookie cookie : BurpExtender.getCallbacks().getCookieJarContents()) {
if (cookie.getDomain().equals(domain)
&& cookie.getName().equals(name)
&& (path == null || StringUtils.defaultString(cookie.getPath()).equals(path))) {
return cookie.getValue();
}
}
} catch (Exception e) {
if (BurpExtender.getGeneralSettings().isEnableEventDiagnostics()) {
Log.get().withMessage(String.format("Invalid use of cookie jar variable tag: %s", VariableSourceEntry.getTag(VariableSource.CookieJar, locator))).withException(e).logErr();
}
}
return "";
}

private String getFileText(IEventInfo eventInfo, String locator) {
try {
String[] variableNameParts = locator.split(":", 2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public class WhenContainerComponent extends RuleOperationContainerComponent {
componentMap.put(WhenModelType.MimeType, WhenMimeTypeComponent.class);
componentMap.put(WhenModelType.ProxyName, WhenProxyNameComponent.class);
componentMap.put(WhenModelType.FromTool, WhenFromToolComponent.class);
componentMap.put(WhenModelType.InScope, WhenInScopeComponent.class);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package synfron.reshaper.burp.ui.components.rules.whens;

import synfron.reshaper.burp.core.rules.whens.WhenInScope;
import synfron.reshaper.burp.ui.models.rules.whens.WhenInScopeModel;
import synfron.reshaper.burp.ui.utils.DocumentActionListener;

import javax.swing.*;
import java.awt.event.ActionEvent;

public class WhenInScopeComponent extends WhenComponent<WhenInScopeModel, WhenInScope> {
private JTextField url;

public WhenInScopeComponent(WhenInScopeModel when) {
super(when);
initComponent();
}

private void initComponent() {
url = createTextField();

url.setText(model.getUrl());

url.getDocument().addDocumentListener(new DocumentActionListener(this::onUrlChanged));

mainContainer.add(getLabeledField("URL", url), "wrap");
getDefaultComponents().forEach(component -> mainContainer.add(component, "wrap"));
mainContainer.add(getPaddedButton(validate));
}

private void onUrlChanged(ActionEvent actionEvent) {
model.setUrl(url.getText());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import synfron.reshaper.burp.core.vars.Variable;
import synfron.reshaper.burp.ui.components.IFormComponent;
import synfron.reshaper.burp.ui.utils.FocusActionListener;
import synfron.reshaper.burp.ui.utils.TableCellRenderer;

import javax.swing.*;
import javax.swing.border.CompoundBorder;
Expand Down Expand Up @@ -98,7 +99,7 @@ private Component getMiscOptions() {
container.add(getLabeledField("Diagnostic Value Max Length", diagnosticValueMaxLength), "wrap");
container.add(enableSanityCheckWarnings, "wrap");
container.add(logInExtenderOutput, "wrap");
container.add(getLabeledField("Log Tab Character Limit", logTabCharacterLimit), "wrap");
container.add(getLabeledField("Logs Tab Character Limit", logTabCharacterLimit), "wrap");
container.add(getLabeledField("Default Encoding", defaultEncoding), "wrap");
return container;
}
Expand Down Expand Up @@ -341,6 +342,7 @@ private Component getExportRulesTable() {
public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) {
}
};
exportRulesTable.setDefaultRenderer(Object.class, new TableCellRenderer());
JScrollPane scrollPane = new JScrollPane(exportRulesTable);
exportRulesModel = createTableModel(getExportRulesData(), new Object[] { "Export", "Rule Name" });
exportRulesTable.setModel(exportRulesModel);
Expand All @@ -354,6 +356,7 @@ private Component getExportVariablesTable() {
public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) {
}
};
exportVariablesTable.setDefaultRenderer(Object.class, new TableCellRenderer());
JScrollPane scrollPane = new JScrollPane(exportVariablesTable);
exportVariablesModel = createTableModel(getExportVariablesData(), new Object[] { "Export", "Variable Name" });
exportVariablesTable.setModel(exportVariablesModel);
Expand Down
Loading

0 comments on commit 80d95ed

Please sign in to comment.