diff --git a/burp-extender/src/burp/BurpExtender.java b/burp-extender/src/burp/BurpExtender.java index f733c36..a5c5682 100644 --- a/burp-extender/src/burp/BurpExtender.java +++ b/burp-extender/src/burp/BurpExtender.java @@ -1,5 +1,7 @@ package burp; +import java.util.regex.*; + import java.awt.BorderLayout; import java.awt.GridLayout; import java.awt.Font; @@ -128,19 +130,64 @@ public Component getUiComponent() { return this.mainPanel; } + /** + * Parse URL and cookie values from intruderRequest + * return for use in xss-detectors + */ + public String[] fetchRequestVals(byte[] intruderRequest, String proto) { + String request = this.helpers.bytesToString(intruderRequest); + + String urlPattern = "(GET|POST) (.*) H"; + String hostPattern = "Host: (.*)"; + String cookiePattern = "[C|c]ookie: (.*)"; + Pattern url = Pattern.compile(urlPattern); + Pattern host = Pattern.compile(hostPattern); + Pattern cookie = Pattern.compile(cookiePattern); + Matcher urlMatcher = url.matcher(request); + Matcher hostMatcher = host.matcher(request); + Matcher cookieMatcher = cookie.matcher(request); + + String intruderUrl = ""; + String intruderHost = ""; + String cookies = ""; + + // Find specific values + while (urlMatcher.find()) { + intruderUrl = urlMatcher.group(2); + } + + while(hostMatcher.find()) { + intruderHost = hostMatcher.group(1); + } + + while(cookieMatcher.find()) { + cookies = cookieMatcher.group(1); + } + intruderUrl = proto + "://" + intruderHost + intruderUrl; + + String[] requestVals = new String[2]; + requestVals[0] = intruderUrl; + requestVals[1] = cookies; + return requestVals; + } + public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) { if ((toolFlag != 32) || (!messageIsRequest)) { if ((toolFlag == 32) && (!messageIsRequest)) { - boolean vulnerable; + boolean vulnerable; - vulnerable = sendToDetector(this.phantomURL.getText(), messageInfo); + // Grab request information from messageInfo.getRequest() + String[] requestInfo = fetchRequestVals(messageInfo.getRequest(), messageInfo.getHttpService().getProtocol()); - // If Phantom.js doesn't process the payload, try slimer - if(!vulnerable) - vulnerable = sendToDetector(this.slimerURL.getText(), messageInfo); - } + + vulnerable = sendToDetector(this.phantomURL.getText(), messageInfo, requestInfo); + + // If Phantom.js doesn't process the payload, try slimer + if(!vulnerable) + vulnerable = sendToDetector(this.slimerURL.getText(), messageInfo, requestInfo); + } } } @@ -164,19 +211,34 @@ public List doPassiveScan(IHttpRequestResponse baseRequestResponse) } - public boolean sendToDetector(String detectorUrl, IHttpRequestResponse messageInfo) { + public boolean sendToDetector(String detectorUrl, IHttpRequestResponse messageInfo, String[] requestInfo) { HttpPost detector = new HttpPost(detectorUrl); Boolean vulnerable = false; + String intruderURL = requestInfo[0]; + String cookies = requestInfo[1]; - try { + try { + // Encode page Response byte[] encodedBytes = Base64.encodeBase64(messageInfo .getResponse()); String encodedResponse = this.helpers .bytesToString(encodedBytes); - List nameValuePairs = new ArrayList(1); + // Encode URL + byte[] encodedURLBytes = Base64.encodeBase64(intruderURL.getBytes()); + String encodedURL = this.helpers.bytesToString(encodedURLBytes); + + // Encode Cookies + byte[] encodedCookiesBytes = Base64.encodeBase64(cookies.getBytes()); + String encodedCookies = this.helpers.bytesToString(encodedCookiesBytes); + + List nameValuePairs = new ArrayList(3); nameValuePairs.add(new BasicNameValuePair("http-response", encodedResponse)); + nameValuePairs.add(new BasicNameValuePair("http-url", + encodedURL)); + nameValuePairs.add(new BasicNameValuePair("http-cookies", + encodedCookies)); detector .setEntity(new UrlEncodedFormEntity(nameValuePairs)); @@ -185,6 +247,8 @@ public boolean sendToDetector(String detectorUrl, IHttpRequestResponse messageIn String responseAsString = EntityUtils.toString(response .getEntity()); + this.stdout.println("Response: " + responseAsString); + if (responseAsString.toLowerCase().contains( BurpExtender.triggerPhrase.toLowerCase())) { String newResponse = this.helpers @@ -194,7 +258,6 @@ public boolean sendToDetector(String detectorUrl, IHttpRequestResponse messageIn .stringToBytes(newResponse)); this.stdout.println("XSS Found"); vulnerable = true; - } }catch (Exception e) { this.stderr.println(e.getMessage()); @@ -233,12 +296,13 @@ public List doActiveScan(IHttpRequestResponse baseRequestResponse, I baseRequestResponse.getHttpService(), checkRequest); boolean vulnerable; + String[] requestInfo = fetchRequestVals(messageInfo.getRequest(), messageInfo.getHttpService().getProtocol()); - vulnerable = sendToDetector(this.phantomURL.getText(), messageInfo); + vulnerable = sendToDetector(this.phantomURL.getText(), messageInfo, requestInfo); // If Phantom.js doesn't process the payload, try slimer if(!vulnerable) - vulnerable = sendToDetector(this.slimerURL.getText(), messageInfo); + vulnerable = sendToDetector(this.slimerURL.getText(), messageInfo, requestInfo); // Update this to actually detect matches List matches = getMatches(messageInfo.getResponse(), triggerPhrase.getBytes()); @@ -382,7 +446,7 @@ public void run() { */ String payloads = ""; for (byte[] bs:BurpExtender.PAYLOADS) { - payloads += new String(bs) + "\r\n"; + payloads += new String(bs) + "\n"; } BurpExtender.this.attackStringsTextarea = new JTextArea(30, 50); @@ -589,4 +653,4 @@ public IHttpService getHttpService() { return httpService; } -} \ No newline at end of file +} diff --git a/xss-detector/examples/dom-xss.php b/xss-detector/examples/dom-xss.php index cb2ef14..fff789d 100644 --- a/xss-detector/examples/dom-xss.php +++ b/xss-detector/examples/dom-xss.php @@ -1,8 +1,9 @@ - +Hello + diff --git a/xss-detector/xss.js b/xss-detector/xss.js index 5645e73..c7379bc 100644 --- a/xss-detector/xss.js +++ b/xss-detector/xss.js @@ -33,6 +33,22 @@ server = webserver.create(); var host = '127.0.0.1'; var port = '8093'; +/** + * Split cookies by semicolon and add each cookie to the webpage + * object separately. + */ +parseCookies = function(cookies, wp) { + cookieArray = cookies.split(";"); + for (var i = 0; i < cookieArray.length; i++) { + cookieArgs = cookieArray[i].split("="); + wp.addCookie({ + 'name': cookieArgs[0], + 'value': cookieArgs[1] + }); + } + return wp; +} + /** * parse incoming HTTP responses that are provided via BURP intruder. * data is base64 encoded to prevent issues passing via HTTP. @@ -40,24 +56,27 @@ var port = '8093'; * Webkit will parse all responses and alert us of any seemingly * malicious Javascript execution, such as alert, confirm, etc. */ -parsePage = function(data) { +parsePage = function(data,url,cookies) { if (DEBUG) { console.log("Beginning to parse page"); + console.log("\tURL: " + url); + console.log("\tCookies: " + cookies); } var html_response = ""; - data = data.split(/\r\n\r\n/); - wp.content = data[1]; - wp.onAlert = function(msg) { - console.log("On alert: " + msg); - }; - // Evaluate page, rendering javascript + wp.setContent(data, decodeURIComponent(url)); - xssInfo = wp.evaluate(function (wp) { + // Parse cookies from intruder and add to request + wp = parseCookies(cookies,wp); + + // Evaluate page, rendering javascript + xssInfo = wp.evaluate(function (wp) { var tags = ["a", "abbr", "acronym", "address", "applet", "area", "article", "aside", "audio", "audioscope", "b", "base", "basefont", "bdi", "bdo", "bgsound", "big", "blackface", "blink", "blockquote", "body", "bq", "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "command", "comment", "datalist", "dd", "del", "details", "dfn", "dir", "div", "dl", "dt", "em", "embed", "fieldset", "figcaption", "figure", "fn", "font", "footer", "form", "frame", "frameset", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", "i", "iframe", "ilayer", "img", "input", "ins", "isindex", "kbd", "keygen", "label", "layer", "legend", "li", "limittext", "link", "listing", "map", "mark", "marquee", "menu", "meta", "meter", "multicol", "nav", "nobr", "noembed", "noframes", "noscript", "nosmartquotes", "object", "ol", "optgroup", "option", "output", "p", "param", "plaintext", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "script", "section", "select", "server", "shadow", "sidebar", "small", "source", "spacer", "span", "strike", "strong", "style", "sub", "sup", "table", "tbody", "td", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "tt", "u", "ul", "var", "video", "wbr", "xml", "xmp"]; var eventHandler = ["mousemove","mouseout","mouseover"] + // Search document for interactive HTML elements, and hover over each + // In attempt to trigger event handlers. tags.forEach(function(tag) { currentTags = document.querySelector(tag); if (currentTags !== null){ @@ -69,6 +88,7 @@ parsePage = function(data) { } }); // Return information from page, if necessary + return document; }, wp); wp.close(); @@ -146,15 +166,21 @@ var service = server.listen(host + ":" + port, function(request, response) { // At this point in time we're only concerned with POST requests // As such, only process those. if(request.method == "POST") { - if(DEBUG) { - console.log("Processing Post Request"); - } - // Grab pageResponse from POST Data and base64 decode. // pass result to parsePage function to search for XSS. var pageResponse = request.post['http-response']; + var pageUrl = request.post['http-url']; + var pageCookies = request.post['http-cookies']; + pageResponse = atob(pageResponse); - xssResults = parsePage(pageResponse); + pageUrl = atob(pageUrl); + cookies = atob(pageCookies); + + if(DEBUG) { + console.log("Processing Post Request"); + } + + xssResults = parsePage(pageResponse,pageUrl,cookies); // Return XSS Results if(xssResults) {