Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add max resonse size filter & Checkbox for mime-type filtering #20

Merged
merged 2 commits into from
Mar 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 24 additions & 15 deletions src/main/java/cys4/scanner/BurpLeaksScanner.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,27 +58,30 @@ public BurpLeaksScanner(int numThreads, MainUI mainUI, IBurpExtenderCallbacks ca
* Method for analyzing the elements in Burp > Proxy > HTTP history
*/
public void analyzeProxyHistory(JProgressBar progressBar) {
IHttpRequestResponse[] httpRequests;
httpRequests = callbacks.getProxyHistory();

IHttpRequestResponse[] httpProxyItems = callbacks.getProxyHistory();
this.analyzedItems = 0;
progressBar.setMaximum(httpRequests.length);

progressBar.setMaximum(httpProxyItems.length);
progressBar.setValue(this.analyzedItems);
progressBar.setStringPainted(true);

boolean inScope = MainUI.isInScopeSelected();
// create copy of regex list to protect from changes while scanning
List<RegexEntity> allRegexListCopy = Stream
.concat(regexList.stream(), extensionsList.stream())
.map(RegexEntity::new)
.toList();

ExecutorService executor = Executors.newFixedThreadPool(numThreads);
// setup filter parameters for analysis
boolean inScope = MainUI.isInScopeOptionSelected();
boolean checkMimeType = MainUI.isSkipMediaTypeOptionSelected();
int maxRequestSize = MainUI.isSkipMaxSizeOptionSelected() ? MainUI.getMaxSizeValueOption() : -1;

for (int i = 0; i < httpRequests.length; i++) {
IHttpRequestResponse httpProxyItem = httpRequests[i];
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
for (int i = 0; i < httpProxyItems.length; i++) {
IHttpRequestResponse httpProxyItem = httpProxyItems[i];
int reqNumber = i+1;
executor.execute(() -> {
analyzeSingleMessage(httpProxyItem, reqNumber, inScope, allRegexListCopy);
analyzeSingleMessage(httpProxyItem, reqNumber, allRegexListCopy, inScope, checkMimeType, maxRequestSize);

if (interruptScan) return;

Expand Down Expand Up @@ -106,16 +109,22 @@ public void analyzeProxyHistory(JProgressBar progressBar) {
/**
* The main method that scan for regex in the single request body
*/
private void analyzeSingleMessage(IHttpRequestResponse httpProxyItem, int requestNumber, boolean inScopeSelected, List<RegexEntity> regexList) {
private void analyzeSingleMessage(IHttpRequestResponse httpProxyItem, int requestNumber, List<RegexEntity> regexList,
boolean inScopeSelected, boolean checkMimeType, int maxRequestSize) {
// check if URL is in scope
byte[] request = httpProxyItem.getRequest();
IRequestInfo requestInfo = helpers.analyzeRequest(httpProxyItem);
if (inScopeSelected && (!callbacks.isInScope(requestInfo.getUrl()))) return;

// skip empty responses
byte[] response = httpProxyItem.getResponse();
if (Objects.isNull(response)) return;
// check for max request size
if (maxRequestSize > 0 && response.length > maxRequestSize) return;

// check for blacklisted MIME types
IResponseInfo responseInfo = helpers.analyzeResponse(response);
if (!isValidMimeType(responseInfo.getStatedMimeType(), responseInfo.getInferredMimeType())) return;
if (checkMimeType && isMimeTypeBlacklisted(responseInfo.getStatedMimeType(), responseInfo.getInferredMimeType())) return;

int requestBodyOffset = requestInfo.getBodyOffset();
String requestBody = helpers.bytesToString(Arrays.copyOfRange(request, requestBodyOffset, request.length));
Expand Down Expand Up @@ -185,13 +194,13 @@ private void addLogEntry(IHttpRequestResponse httpProxyItem, int requestNumber,
}

/**
* Checks if the MimeType is inside the list of valid mime types "mime_types.json".
* Checks if the MimeType is inside the list of blacklisted mime types "mime_types.json".
* If the stated mime type in the header isBlank, then the inferred mime type is used.
* @param statedMimeType Stated mime type from a IResponseInfo object
* @param inferredMimeType Inferred mime type from a IResponseInfo object
* @return True if the mime type is valid
* @return True if the mime type is blacklisted
*/
private boolean isValidMimeType(String statedMimeType, String inferredMimeType) {
private boolean isMimeTypeBlacklisted(String statedMimeType, String inferredMimeType) {
String mimeType = statedMimeType.isBlank() ? inferredMimeType : statedMimeType;

if (this.blacklistedMimeTypes.isEmpty()) {
Expand All @@ -205,7 +214,7 @@ private boolean isValidMimeType(String statedMimeType, String inferredMimeType)
.forEach(blacklistedMimeTypes::add);
}

return !blacklistedMimeTypes.contains(mimeType.toUpperCase());
return blacklistedMimeTypes.contains(mimeType.toUpperCase());
}

public void setInterruptScan(boolean interruptScan) {
Expand Down
120 changes: 106 additions & 14 deletions src/main/java/cys4/ui/MainUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,21 @@ public class MainUI implements ITab {
private final BurpLeaksScanner burpLeaksScanner;

/**
* Check if the options for the scope is selected or not
* Checkbox to skip responses not in scope
*/
private static boolean inScope = false;
private static boolean inScopeCheckbox = false;
/**
* Checkbox to skip responses over a set max size
*/
private static boolean skipMaxSizeCheckbox = true;
/**
* Max response size in bytes. Defaults to 10MB
*/
private static int maxSizeValue = 10_000_000;
/**
* Checkbox to skip responses of a media MIME-type
*/
private static boolean skipMediaTypeCheckbox = true;

public MainUI(IBurpExtenderCallbacks callbacks) {
this.callbacks = callbacks;
Expand Down Expand Up @@ -94,10 +106,28 @@ public String getNameExtension() {
}

/**
* isInScopeSelected return true if the option is selected
* returns true if the option checkbox is selected
*/
public static boolean isInScopeOptionSelected() {
return inScopeCheckbox;
}
/**
* returns true if the option checkbox is selected
*/
public static boolean isSkipMaxSizeOptionSelected() {
return skipMaxSizeCheckbox;
}
/**
* Returns the set max size for responses
*/
public static boolean isInScopeSelected() {
return inScope;
public static int getMaxSizeValueOption() {
return maxSizeValue;
}
/**
* returns true if the option checkbox is selected
*/
public static boolean isSkipMediaTypeOptionSelected() {
return skipMediaTypeCheckbox;
}

/**
Expand Down Expand Up @@ -590,7 +620,7 @@ private List<JComponent> createOptions_Regex(JPanel tabPaneOptions, JPanel optio

private JPanel createOptions_Configurations() {
JPanel configurationsPanel = new JPanel();
configurationsPanel.setLayout(new BoxLayout(configurationsPanel, BoxLayout.Y_AXIS));
configurationsPanel.setLayout(new BoxLayout(configurationsPanel, BoxLayout.X_AXIS));
configurationsPanel.setAlignmentX(JPanel.RIGHT_ALIGNMENT);

JPanel scopePanel = createOptions_Configuration_Filters();
Expand All @@ -604,7 +634,7 @@ private JPanel createOptions_Configurations() {
private JPanel createOptions_Configuration_Filters() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setMaximumSize(new Dimension(500,100));
// panel.setMaximumSize(new Dimension(500,100));
panel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
panel.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createLineBorder(Color.gray, 1),
Expand All @@ -615,17 +645,28 @@ private JPanel createOptions_Configuration_Filters() {
new Color(255, 102, 51)
));

JCheckBox inScopeCheckBox = new JCheckBox("Show only in-scope items");
inScopeCheckBox.addActionListener(e -> inScope = inScopeCheckBox.getModel().isSelected());
JCheckBox inScopeCheckbox = new JCheckBox("Show only in-scope items");
inScopeCheckbox.getModel().setSelected(MainUI.inScopeCheckbox);
inScopeCheckbox.addActionListener(e -> MainUI.inScopeCheckbox = inScopeCheckbox.getModel().isSelected());
panel.add(inScopeCheckbox);

JCheckBox skipMaxSizeCheckbox = new JCheckBox("Skip responses over set size");
skipMaxSizeCheckbox.getModel().setSelected(MainUI.skipMaxSizeCheckbox);
skipMaxSizeCheckbox.addActionListener(e -> MainUI.skipMaxSizeCheckbox = skipMaxSizeCheckbox.getModel().isSelected());
panel.add(skipMaxSizeCheckbox);

JCheckBox skipMediaTypeCheckbox = new JCheckBox("Skip media-type responses (images, videos, archives, ...)");
skipMediaTypeCheckbox.getModel().setSelected(MainUI.skipMediaTypeCheckbox);
skipMediaTypeCheckbox.addActionListener(e -> MainUI.skipMediaTypeCheckbox = skipMediaTypeCheckbox.getModel().isSelected());
panel.add(skipMediaTypeCheckbox);

panel.add(inScopeCheckBox);
return panel;
}

private JPanel createOptions_Configuration_Scanner() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setMaximumSize(new Dimension(500,100));
// panel.setMaximumSize(new Dimension(500,100));
panel.setAlignmentX(Component.LEFT_ALIGNMENT);
panel.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createLineBorder(Color.gray, 1),
Expand All @@ -636,6 +677,17 @@ private JPanel createOptions_Configuration_Scanner() {
new Color(255, 102, 51)
));

panel.add(createOptions_numThreads());
panel.add(createOptions_maxSizeFilter());

return panel;
}

private JPanel createOptions_numThreads() {
JPanel mainGroup = new JPanel();
mainGroup.setLayout(new BoxLayout(mainGroup, BoxLayout.Y_AXIS));
mainGroup.setAlignmentX(Component.LEFT_ALIGNMENT);

JPanel numThreads = new JPanel();
JLabel numThreadsDescription = new JLabel("Current number of threads: ");
JLabel numThreadsCurrent = new JLabel(String.valueOf(this.burpLeaksScanner.getNumThreads()));
Expand Down Expand Up @@ -664,9 +716,49 @@ private JPanel createOptions_Configuration_Scanner() {
updateNumThreads.add(updateNumThreadsField);
updateNumThreads.add(updateNumThreadsSet);

panel.add(numThreads);
panel.add(updateNumThreads);
return panel;
mainGroup.add(numThreads);
mainGroup.add(updateNumThreads);
return mainGroup;
}

//TODO enable/disable max size input box with MainUI.skipMaxSizeCheckbox
private JPanel createOptions_maxSizeFilter() {
JPanel mainGroup = new JPanel();
mainGroup.setLayout(new BoxLayout(mainGroup, BoxLayout.Y_AXIS));
mainGroup.setAlignmentX(Component.LEFT_ALIGNMENT);

JPanel maxSize = new JPanel();
JLabel maxSizeDescription = new JLabel("Current max response size (bytes): ");
JLabel maxSizeCurrent = new JLabel(String.valueOf(MainUI.maxSizeValue));
maxSize.setLayout(new FlowLayout(FlowLayout.LEFT));
maxSize.add(maxSizeDescription);
maxSize.add(maxSizeCurrent);

JPanel updateMaxSize = new JPanel();
JLabel updateMaxSizeDescription = new JLabel("Update max response size (bytes): ");
JTextField updateMaxSizeField = new JTextField(4);
JButton updateMaxSizeSet = new JButton("Set");
updateMaxSizeSet.addActionListener(e -> {
try {
int newMaxSizeValue = Integer.parseInt(updateMaxSizeField.getText());
if (newMaxSizeValue < 1)
throw new NumberFormatException("Size must be >= 1");

MainUI.maxSizeValue = newMaxSizeValue;
maxSizeCurrent.setText(String.valueOf(MainUI.getMaxSizeValueOption()));
updateMaxSizeField.setText("");
} catch (NumberFormatException ignored) {
}
});
updateMaxSize.setLayout(new FlowLayout(FlowLayout.LEFT));
updateMaxSize.add(updateMaxSizeDescription);
updateMaxSize.add(updateMaxSizeField);
updateMaxSize.add(updateMaxSizeSet);

mainGroup.add(maxSize);
mainGroup.add(updateMaxSize);

return mainGroup;
}

@Override
Expand Down