/*
* ReplayAndDiff - Replay a scan with a fresh session and diff the results
*
* Copyright (c) 2017 Luca Carettoni - Doyensec LLC.
*/
package burp;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.Iterator;
/*
* This extension can be executed in headless mode. Start burp using -Djava.awt.headless=true
*/
public class BurpExtender implements IBurpExtender {
//Default configuration
static String MONGO_HOST = "127.0.0.1";
static int MONGO_PORT = 27017;
static String OUTPUT_DIR = "/tmp/";
static String REPORT_NAME = "burpreport_" + System.currentTimeMillis() + ".html";
static int TIMEOUT = 10; //seconds
private IBurpExtenderCallbacks callbacks;
private IExtensionHelpers helpers;
@Override
public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {
this.callbacks = callbacks;
helpers = callbacks.getHelpers();
callbacks.setExtensionName("ReplayAndDiff");
System.out.println("\n\n:: ReplayAndDiff Headless Extension ::\n\n");
//Parse command line arguments and store values in local variables
//-h|--host=<IP>, -p|--port=<port>, -o|--ouput=<dir>, -e|--report=<filename>, -t|--timeout=<seconds>
String[] args = callbacks.getCommandLineArguments();
for (String arg : args) {
if (arg.contains("-h=") || arg.contains("--host=")) {
MONGO_HOST = arg.substring(arg.indexOf('=') + 1);
} else if (arg.contains("-p=") || arg.contains("--port=")) {
MONGO_PORT = Integer.valueOf(arg.substring(arg.indexOf('=') + 1));
} else if (arg.contains("-o=") || arg.contains("--ouput=")) {
OUTPUT_DIR = arg.substring(arg.indexOf('=') + 1);
} else if (arg.contains("-r=") || arg.contains("--report=")) {
REPORT_NAME = arg.substring(arg.indexOf('=') + 1);
} else if (arg.contains("-t=") || arg.contains("--timeout=")) {
TIMEOUT = Integer.valueOf(arg.substring(arg.indexOf('=') + 1));
}
}
System.out.println("[*] Configuration {MONGO_HOST=" + MONGO_HOST + ",MONGO_PORT=" + MONGO_PORT + ",OUTPUT_DIR=" + OUTPUT_DIR + ",REPORT_NAME=" + REPORT_NAME + ",TIMEOUT=" + TIMEOUT + "}");
//Retrieve site info and login request from MongoDB
MongoClient mongo = null;
try {
mongo = new MongoClient(MONGO_HOST, MONGO_PORT);
} catch (UnknownHostException ex) {
System.err.println("[!] MongoDB Connection Error: " + ex.toString());
}
DB db = mongo.getDB("sitelogger");
DBCollection table = db.getCollection("login");
DBCursor cursor = table.find();
String host = null;
while (cursor.hasNext()) {
DBObject entry = cursor.next();
//Replay the HTTP request and save the fresh cookie in Burp's Cookies JAR
host = (String) entry.get("host");
System.out.println("[*] Retrieving record for: " + host);
byte[] response = callbacks.makeHttpRequest(host, ((Double) entry.get("port")).intValue(), "https".equals((String) entry.get("protocol")), b64d((String) entry.get("request")));
Iterator<ICookie> cookies = helpers.analyzeResponse(response).getCookies().iterator();
while (cookies.hasNext()) {
try {
ICookie cookie = cookies.next();
System.out.println("[*] Obtained cookie: " + cookie.getName() + ":" + cookie.getValue());
callbacks.updateCookieJar(cookie);
} catch (NullPointerException npe) {
System.out.println("[!] Missing cookie attributes - e.g. domain not set");
}
}
}
//Replay a scan on all URLs previously saved for the same site
if (host != null) {
table = db.getCollection(host.replaceAll("\\.", "_") + "_site");
} else {
throw new NullPointerException();
}
cursor = table.find();
URL website = null;
while (cursor.hasNext()) {
DBObject entry = cursor.next();
//Add host in scope. This is meant to prevent popup since the extension is running headless
try {
website = new URL(((String) entry.get("protocol")) + "://" + ((String) entry.get("host")));
callbacks.includeInScope(website);
//Execute passive and active scans
IScanQueueItem item = callbacks.doActiveScan(((String) entry.get("host")), ((int) entry.get("port")), "https".equals((String) entry.get("protocol")), b64d((String) entry.get("request")));
System.out.println(item.getStatus());
System.out.println(item.getPercentageComplete());
try {
Thread.sleep(4000);
} catch (InterruptedException ex) {
System.err.println("[!] InterruptedException: " + ex.toString());
}
System.out.println(item.getStatus());
System.out.println(item.getPercentageComplete());
//Make a new HTTP request and pass request/response to Burp's passive scanner
byte[] response = callbacks.makeHttpRequest(((String) entry.get("host")), ((Double) entry.get("port")).intValue(), "https".equals((String) entry.get("protocol")), b64d((String) entry.get("request")));
callbacks.doPassiveScan(((String) entry.get("host")), ((int) entry.get("port")), "https".equals((String) entry.get("protocol")), b64d((String) entry.get("request")), response);
} catch (MalformedURLException ex) {
System.err.println("[!] Malformed website URL: " + ex.toString());
} catch (NullPointerException ex) {
System.err.println("[!] Missing request or response: " + ex.toString());
}
}
try {
System.out.println("[*] Pausing extension...");
// HOMEWORK - Build a queuing system to check scans status and confirm once all scans are done
Thread.sleep(1000 * TIMEOUT);
System.out.println("[*] Resuming extension...");
} catch (InterruptedException ex) {
System.err.println("[!] InterruptedException: " + ex.toString());
}
table = db.getCollection(host.replaceAll("\\.", "_") + "_vuln");
BasicDBObject searchQuery = null;
IScanIssue[] allVulns = null;
boolean newFinding = false;
//Obtain the new scan findings
if (website != null) {
allVulns = callbacks.getScanIssues(website.toString());
for (IScanIssue allVuln : allVulns) {
//Diff new and old scan results.
searchQuery = new BasicDBObject();
searchQuery.put("type", allVuln.getIssueType());
searchQuery.put("name", allVuln.getIssueName());
searchQuery.put("URL", allVuln.getUrl().toString());
System.out.println("[*] Looking for: " + searchQuery.toString());
cursor = table.find(searchQuery);
if (cursor.size() == 0) {
//There's at least one new finding
System.out.println("[*] Got a new finding!");
newFinding = true;
}
}
if (newFinding) {
System.out.println("[*] New findings! Generating report...");
callbacks.generateScanReport("HTML", allVulns, new File(OUTPUT_DIR + REPORT_NAME));
} else {
System.out.println("[*] Scan and diff completed. No new results.");
}
} else {
throw new NullPointerException();
}
System.out.println("[*] Ready to shutdown...Bye!");
callbacks.exitSuite(false);
}
/* Utility method to Base64 decode */
private byte[] b64d(String input) {
if (input != null) {
return helpers.base64Decode(input);
}
return new byte[0];
}
}