diff --git a/BappDescription.html b/BappDescription.html index 8e58698..08b8c2f 100644 --- a/BappDescription.html +++ b/BappDescription.html @@ -8,5 +8,6 @@
  • Configure the parameters used for the scan and customize them in any way you want
  • Edit the base request performed (you can add headers, cookies, edit the User Agent, etc)
  • Save the scan output to a file
  • +
  • Create an Intruder Payload Set for guessing complete names from shortnames retrieved from scan results (by using wordlists)
  • The source code is available here

    \ No newline at end of file diff --git a/README.md b/README.md index 9b6e67d..b244ef0 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ In the Burp UI tab you can: * Configure the parameters used for the scan and customize them in any way you want * Edit the base request performed (you can add headers, cookies, edit the User Agent, etc) * Save the scan output to a file +* Create an Intruder Payload Set for guessing complete names from shortnames retrieved from scan results (by using wordlists) ## Screenshots @@ -21,3 +22,13 @@ In the Burp UI tab you can: ### Configuration tab (1366x768) ![config_tab](https://user-images.githubusercontent.com/35109470/146651516-fa33595c-0904-497b-9e28-7591428d5449.png) + +## Changelog + +* v1.1 + + Added an Intruder Payload Set Generator for guessing complete names from shortnames retrieved from scan results (by using wordlists) + +* v1.0 + + First release \ No newline at end of file diff --git a/build.gradle b/build.gradle index 86f4dba..cdb0bca 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ apply plugin: 'java' -version '1.0' +version '1.1' repositories { mavenCentral() diff --git a/src/burp/BurpExtender.java b/src/burp/BurpExtender.java index 22605e6..45ec05a 100644 --- a/src/burp/BurpExtender.java +++ b/src/burp/BurpExtender.java @@ -5,12 +5,17 @@ import java.io.UnsupportedEncodingException; import java.io.FileOutputStream; import java.io.File; +import java.io.FileReader; +import java.io.BufferedReader; +import java.io.FileNotFoundException; import java.util.List; import java.util.Arrays; import java.util.ArrayList; import java.util.Map; import java.util.HashMap; import java.util.LinkedList; +import java.util.LinkedHashSet; +import java.util.Collections; import java.net.URL; import java.net.URLEncoder; import java.awt.Font; @@ -39,13 +44,13 @@ import javax.swing.border.CompoundBorder; import javax.swing.JFileChooser; -public class BurpExtender implements IBurpExtender, ITab, IScannerCheck +public class BurpExtender implements IBurpExtender, ITab, IScannerCheck, IIntruderPayloadGeneratorFactory { private IBurpExtenderCallbacks callbacks; private IExtensionHelpers helpers; private final String NAME = "IIS Tilde Enumeration Scanner"; - private final String VERSION = "1.0"; + private final String VERSION = "1.1"; private final String AUTHOR = "Michele 'cyberaz0r' Di Bonaventura"; private JTabbedPane mainUI; @@ -53,6 +58,7 @@ public class BurpExtender implements IBurpExtender, ITab, IScannerCheck private KillableThread tildeEnumScanner = new KillableThread(); + private List intruderPayloads = new ArrayList(); // extension tab name @Override @@ -84,6 +90,9 @@ public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks) // register scanner checks callbacks.registerScannerCheck(this); + + // register intruder payload generator (for filename guessing from scan results) + callbacks.registerIntruderPayloadGeneratorFactory(this); SwingUtilities.invokeLater(new Runnable() { @@ -110,9 +119,12 @@ public void actionPerformed(ActionEvent e) t.setRepeats(false); // setting up config panel in configuration tab + JPanel rightPanel = new JPanel(); + rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS)); + JScrollPane rightScrollPanel = new JScrollPane(rightPanel); + rightScrollPanel.setBorder(new EmptyBorder(20, 20, 20, 20)); + JPanel confPanel = new JPanel(new GridLayout(27, 1)); - JScrollPane confScrollPanel = new JScrollPane(confPanel); - confScrollPanel.setBorder(new EmptyBorder(20, 20, 20, 20)); JLabel confTitle = new JLabel("Configuration"); confTitle.setForeground(new Color(249, 130, 11)); @@ -135,6 +147,7 @@ public void actionPerformed(ActionEvent e) // setting up elements in request panel in configuration tab JTextPane requestEditor = new JTextPane(); JScrollPane requestScrollEditor = new JScrollPane(requestEditor); + requestEditor.setFont(new Font("Courier", 0, 14)); requestEditor.setBorder(BorderFactory.createLineBorder(Color.BLACK)); requestEditor.setText ( @@ -201,7 +214,30 @@ public void actionPerformed(ActionEvent e) confPanel.add(new JLabel("In-Scope characters:")); confFields.put("inScopeCharacters", new JTextField("ETAONRISHDLFCMUGYPWBVKJXQZ0123456789_-$~()&!#%'@^`{}", 50)); confPanel.add(confFields.get("inScopeCharacters")); + + JPanel filenameGuessingPanel = new JPanel(new GridLayout(6, 1)); + + JLabel filenameGuessingTitle = new JLabel("Complete filename guessing"); + filenameGuessingTitle.setForeground(new Color(249, 130, 11)); + filenameGuessingTitle.setFont(new Font("Nimbus", Font.BOLD, 16)); + filenameGuessingTitle.setAlignmentX(Component.LEFT_ALIGNMENT); + filenameGuessingTitle.setBorder(new CompoundBorder(confTitle.getBorder(), new EmptyBorder(10, 10, 10, 10))); + filenameGuessingPanel.add(filenameGuessingTitle); + + JCheckBox completeFileGuessCheckbox = new JCheckBox("Create an Intruder Payload Set with possible filenames (using wordlists)", false); + filenameGuessingPanel.add(completeFileGuessCheckbox); + + filenameGuessingPanel.add(new JLabel("Complete file name wordlist:")); + confFields.put("fileNameWordlist", new JTextField("", 50)); + filenameGuessingPanel.add(confFields.get("fileNameWordlist")); + + filenameGuessingPanel.add(new JLabel("Complete file extension wordlist:")); + confFields.put("fileExtWordlist", new JTextField("", 50)); + filenameGuessingPanel.add(confFields.get("fileExtWordlist")); + rightPanel.add(confPanel); + rightPanel.add(filenameGuessingPanel); + // setting up scanner tab JSplitPane scannerTab = new JSplitPane(JSplitPane.VERTICAL_SPLIT); scannerTab.setBorder(new EmptyBorder(20, 20, 20, 20)); @@ -215,6 +251,7 @@ public void actionPerformed(ActionEvent e) // setting up elements in scanner tab JTextPane textPane = new JTextPane(); JScrollPane textScrollPane = new JScrollPane(textPane); + textPane.setFont(new Font("Courier", 0, 12)); textPane.setText("IIS Tilde Enumeration Scanner Burp Extension is ready\nThe scan output will be displayed here"); textPane.setEditable(false); @@ -256,6 +293,7 @@ public void actionPerformed(ActionEvent e) confFields, nThreadsField, exploitModeCheckbox, + completeFileGuessCheckbox, requestEditor, statusLabel ) @@ -282,7 +320,7 @@ public void actionPerformed(ActionEvent a) scannerTab.setBottomComponent(scannerBottomPanel); configTab.setTopComponent(requestPanel); - configTab.setBottomComponent(confScrollPanel); + configTab.setBottomComponent(rightScrollPanel); t.start(); // triggering listener for splitting config tab 50:50 @@ -450,6 +488,18 @@ public List doPassiveScan(IHttpRequestResponse baseRequestResponse) { return null; } + + @Override + public String getGeneratorName() + { + return "Filename guessing from Tilde Enumeration scan results"; + } + + @Override + public IIntruderPayloadGenerator createNewInstance(IIntruderAttack attack) + { + return new IntruderPayloadGenerator(intruderPayloads); + } // strip response data in order to get more granular results public String stripResponse(String response, String basePath, String queryString) @@ -510,8 +560,11 @@ class CustomConfig private int deltaResponseLength; private int nThreads; private boolean exploitMode; + private boolean completeFileGuess; + private File fileNameWordlist; + private File fileExtWordlist; - public CustomConfig(HashMap confFields, JTextPane requestEditor, JTextField nThreadsField, JCheckBox exploitModeCheckbox) + public CustomConfig(HashMap confFields, JTextPane requestEditor, JTextField nThreadsField, JCheckBox exploitModeCheckbox, JCheckBox completeFileGuessCheckbox) { this.magicFinalPartList = Arrays.asList(confFields.get("magicFinalPartList").getText().split(",")); this.questionMarkSymbol = confFields.get("questionMarkSymbol").getText(); @@ -529,6 +582,9 @@ public CustomConfig(HashMap confFields, JTextPane requestEdi this.requestString = requestEditor.getText(); this.nThreads = Integer.parseInt(nThreadsField.getText()); this.exploitMode = exploitModeCheckbox.isSelected(); + this.completeFileGuess = completeFileGuessCheckbox.isSelected(); + this.fileNameWordlist = new File(confFields.get("fileNameWordlist").getText()); + this.fileExtWordlist = new File(confFields.get("fileExtWordlist").getText()); } public List getMagicFinalPartList() @@ -610,6 +666,21 @@ public boolean getExploitMode() { return this.exploitMode; } + + public boolean getCompleteFileGuess() + { + return this.completeFileGuess; + } + + public File getFileNameWordlist() + { + return this.fileNameWordlist; + } + + public File getFileExtWordlist() + { + return this.fileExtWordlist; + } public void setQuestionMarkSymbol(String questionMarkSymbol) { @@ -671,10 +742,11 @@ class StartScanButton implements ActionListener private HashMap confFields; private JTextField nThreadsField; private JCheckBox exploitModeCheckbox; + private JCheckBox completeFileGuessCheckbox; private JTextPane requestEditor; private JLabel statusLabel; - public StartScanButton(JTextField targetUrlField, JTextPane textPane, HashMap confFields, JTextField nThreadsField, JCheckBox exploitModeCheckbox, JTextPane requestEditor, JLabel statusLabel) + public StartScanButton(JTextField targetUrlField, JTextPane textPane, HashMap confFields, JTextField nThreadsField, JCheckBox exploitModeCheckbox, JCheckBox completeFileGuessCheckbox, JTextPane requestEditor, JLabel statusLabel) { super(); this.targetUrlField = targetUrlField; @@ -682,6 +754,7 @@ public StartScanButton(JTextField targetUrlField, JTextPane textPane, HashMap insertionPoints = new ArrayList() {{ add(new int[] {offset, offset}); }}; + + // send base request to intruder + callbacks.sendToIntruder + ( + requester.getHttpService().getHost(), + requester.getHttpService().getPort(), + requester.getHttpService().getProtocol().equals("https"), + config.getRequestString() + .replace("§METHOD§", "GET") + .replace("§HOST§", requester.getHttpService().getHost()) + .replace("§PATH§", requester.getBasePath() + "/") + .replace("\n", "\r\n") + .getBytes(), + insertionPoints + ); + + output.print("[+] Generated " + intruderPayloads.size() + " possible complete filenames, switch to Intruder to launch a guessing attack using the generated filenames"); + } - + output.status("Scan completed"); toggleScanButton(false); } catch (RuntimeException e) @@ -2044,6 +2167,116 @@ private boolean isFolder(String strInput) return false; } } + + class MatchList + { + private List matches; + private List elementsFound; + private List possibleFileNames; + private List possibleFileExts; + + public MatchList(List dirsFound, List filesFound, File fileNameWordlist, File fileExtWordlist) + { + this.elementsFound = buildElementList(dirsFound, filesFound); + this.possibleFileNames = Utils.readFile(fileNameWordlist); + this.possibleFileExts = Utils.readFile(fileExtWordlist); + this.matches = buildMatchList(); + } + + private List buildElementList(List dirsFound, List filesFound) + { + List elements = new ArrayList(); + List parsedElements = new ArrayList(); + + elements.addAll(dirsFound); + elements.addAll(filesFound); + Collections.sort(elements); + + for (int i = 0; i < elements.size(); i++) + { + if (i < elements.size() - 1) + { + if (elements.get(i+1).startsWith(elements.get(i))) + { + continue; + } + } + + parsedElements.add(elements.get(i).replace("\\?", "")); + } + + return parsedElements; + } + + private List buildMatchList() + { + List matchesFound = new ArrayList(); + + for (String name : possibleFileNames) + { + for (String elem : elementsFound) + { + String elemName = elem.split("~")[0]; + + if (elemName.length() < 6) + { + continue; + } + + if (name.toUpperCase().startsWith(elemName)) + { + if (elem.indexOf(".") > 0) + { + List elemExts = buildExtensionList(elem.substring(elem.lastIndexOf('.') + 1)); + for (String ext : elemExts) + { + matchesFound.add(name.toUpperCase() + "." + ext); + } + } + else + { + matchesFound.add(name.toUpperCase()); + } + } + + } + } + + Collections.sort(matchesFound); + return matchesFound; + } + + private List buildExtensionList(String elemExt) + { + List fileExts = new ArrayList(); + + for (String ext : possibleFileExts) + { + if (ext.startsWith(".")) + { + ext = ext.substring(1, ext.length()); + } + + if (ext.toUpperCase().startsWith(elemExt)) + { + fileExts.add(ext.toUpperCase()); + } + } + + if (fileExts.isEmpty()) + { + fileExts.add(elemExt); + } + + // remove duplicates before return + return new ArrayList(new LinkedHashSet<>(fileExts)); + } + + public List getMatches() + { + return this.matches; + } + } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -2211,6 +2444,38 @@ public void run() } } +// class to handle Intruder payload generator for filename guessing from scan results +class IntruderPayloadGenerator implements IIntruderPayloadGenerator +{ + private List payloads; + int payloadIndex; + + public IntruderPayloadGenerator(List payloads) + { + this.payloads = payloads; + } + + @Override + public boolean hasMorePayloads() + { + return payloadIndex < payloads.size(); + } + + @Override + public byte[] getNextPayload(byte[] baseValue) + { + byte[] payload = payloads.get(payloadIndex).getBytes(); + payloadIndex++; + return payload; + } + + @Override + public void reset() + { + payloadIndex = 0; + } +} + // custom issue class class CustomScanIssue implements IScanIssue { @@ -2349,4 +2614,30 @@ public static String tree(String str, int level) String dentSpace = new String(new char[level]).replace("\0", " "); return dentSpace + "|_ " + str; } + + public static List readFile(File file) + { + List fileContent = new ArrayList(); + + try (BufferedReader br = new BufferedReader(new FileReader(file))) + { + String line; + while ((line = br.readLine()) != null) + { + fileContent.add(line); + } + } + + catch (FileNotFoundException e) + { + return null; + } + + catch (IOException e) + { + return null; + } + + return fileContent; + } }