xmlparser.XmlConfigReader.java Source code

Java tutorial

Introduction

Here is the source code for xmlparser.XmlConfigReader.java

Source

/*
   snuck - XML configuration files parser
   --------------------------------
       
   Author: Mauro Gentile <gentile.mauro.mg@gmail.com>
    
   Copyright 2012 Mauro Gentile
    
   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;
    }
}