org.zaproxy.zap.extension.ascanrulesAlpha.CrossDomainScanner.java Source code

Java tutorial

Introduction

Here is the source code for org.zaproxy.zap.extension.ascanrulesAlpha.CrossDomainScanner.java

Source

/*
 * Zed Attack Proxy (ZAP) and its related class files.
 *
 * ZAP is an HTTP/HTTPS proxy for assessing web application security.
 *
 * 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 org.zaproxy.zap.extension.ascanrulesAlpha;

import java.io.ByteArrayInputStream;
import java.io.IOException;

import org.apache.commons.httpclient.URI;
import org.apache.log4j.Logger;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.core.scanner.AbstractHostPlugin;
import org.parosproxy.paros.core.scanner.Alert;
import org.parosproxy.paros.core.scanner.Category;
import org.parosproxy.paros.network.HttpMessage;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.xml.xpath.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPathFactory;

/**
 * A class to actively check if the web server is configured to allow Cross Domain access, from a malicious 
 * third party service, for instance. Currently checks for wildcards in Adobe's crossdomain.xml, and in 
 * SilverLight's clientaccesspolicy.xml 
 * 
 * @author 70pointer@gmail.com
 *
 */
public class CrossDomainScanner extends AbstractHostPlugin {

    /**
     * the logger object
     */
    private static Logger log = Logger.getLogger(CrossDomainScanner.class);

    /**
     * Prefix for internationalized messages used by this rule
     */
    private static final String MESSAGE_PREFIX = "ascanalpha.crossdomain.";
    private static final String MESSAGE_PREFIX_ADOBE = "ascanalpha.crossdomain.adobe.";
    private static final String MESSAGE_PREFIX_ADOBE_READ = "ascanalpha.crossdomain.adobe.read.";
    private static final String MESSAGE_PREFIX_ADOBE_SEND = "ascanalpha.crossdomain.adobe.send.";
    private static final String MESSAGE_PREFIX_SILVERLIGHT = "ascanalpha.crossdomain.silverlight.";

    /**
     * Adobe's cross domain policy file name
     */
    static final String ADOBE_CROSS_DOMAIN_POLICY_FILE = "crossdomain.xml";

    /**
     * Silverlight's cross domain policy file name
     */
    static final String SILVERLIGHT_CROSS_DOMAIN_POLICY_FILE = "clientaccesspolicy.xml";

    /**
     * returns the plugin id
     */
    @Override
    public int getId() {
        return 20016;
    }

    /**
     * returns the name of the plugin
     */
    @Override
    public String getName() {
        return Constant.messages.getString(MESSAGE_PREFIX + "name");
    }

    @Override
    public String[] getDependency() {
        return null;
    }

    @Override
    public String getDescription() {
        return Constant.messages.getString(MESSAGE_PREFIX + "desc");
    }

    @Override
    public int getCategory() {
        return Category.SERVER;
    }

    @Override
    public String getSolution() {
        return Constant.messages.getString(MESSAGE_PREFIX + "soln");
    }

    @Override
    public String getReference() {
        return Constant.messages.getString(MESSAGE_PREFIX + "refs");
    }

    @Override
    public void init() {
    }

    /**
     * scans the node for cross-domain mis-configurations
     */
    @Override
    public void scan() {

        try {
            //get the network details for the attack
            URI originalURI = this.getBaseMsg().getRequestHeader().getURI();

            //retrieve the Adobe cross domain policy file, and assess it
            HttpMessage crossdomainmessage = new HttpMessage(new URI(originalURI.getScheme(),
                    originalURI.getAuthority(), "/" + ADOBE_CROSS_DOMAIN_POLICY_FILE, null, null));
            sendAndReceive(crossdomainmessage, false);
            byte[] crossdomainmessagebytes = crossdomainmessage.getResponseBody().getBytes();

            //parse the file. If it's not parseable, it might have been because of a 404
            DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
            ;
            DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
            ;
            XPath xpath = (XPath) XPathFactory.newInstance().newXPath();

            try {
                //work around the "no protocol" issue by wrapping the content in a ByteArrayInputStream
                Document adobeXmldoc = docBuilder
                        .parse(new InputSource(new ByteArrayInputStream(crossdomainmessagebytes)));

                //check for cross domain read (data load) access
                XPathExpression exprAllowAccessFromDomain = xpath
                        .compile("/cross-domain-policy/allow-access-from/@domain"); //gets the domain attributes
                NodeList exprAllowAccessFromDomainNodes = (NodeList) exprAllowAccessFromDomain.evaluate(adobeXmldoc,
                        XPathConstants.NODESET);
                for (int i = 0; i < exprAllowAccessFromDomainNodes.getLength(); i++) {
                    String domain = exprAllowAccessFromDomainNodes.item(i).getNodeValue();
                    if (domain.equals("*")) {
                        //oh dear me.
                        if (log.isInfoEnabled())
                            log.info("Bingo!  <allow-access-from domain=\"*\"");
                        bingo(getRisk(), Alert.WARNING,
                                Constant.messages.getString(MESSAGE_PREFIX_ADOBE_READ + "name"),
                                Constant.messages.getString(MESSAGE_PREFIX_ADOBE + "desc"),
                                crossdomainmessage.getRequestHeader().getURI().getURI(), //the url field 
                                "", //parameter being attacked: none.
                                "", //attack
                                Constant.messages.getString(MESSAGE_PREFIX_ADOBE_READ + "extrainfo",
                                        "/" + ADOBE_CROSS_DOMAIN_POLICY_FILE), //extrainfo
                                Constant.messages.getString(MESSAGE_PREFIX_ADOBE_READ + "soln"), //solution
                                "<allow-access-from domain=\"*\"", // evidence
                                crossdomainmessage //the message on which to place the alert
                        );
                    }
                }
                //check for cross domain send (upload) access
                XPathExpression exprRequestHeadersFromDomain = xpath
                        .compile("/cross-domain-policy/allow-http-request-headers-from/@domain"); //gets the domain attributes
                NodeList exprRequestHeadersFromDomainNodes = (NodeList) exprRequestHeadersFromDomain
                        .evaluate(adobeXmldoc, XPathConstants.NODESET);
                for (int i = 0; i < exprRequestHeadersFromDomainNodes.getLength(); i++) {
                    String domain = exprRequestHeadersFromDomainNodes.item(i).getNodeValue();
                    if (domain.equals("*")) {
                        //oh dear, dear me.
                        if (log.isInfoEnabled())
                            log.info("Bingo!  <allow-http-request-headers-from domain=\"*\"");
                        bingo(getRisk(), Alert.WARNING,
                                Constant.messages.getString(MESSAGE_PREFIX_ADOBE_SEND + "name"),
                                Constant.messages.getString(MESSAGE_PREFIX_ADOBE + "desc"),
                                crossdomainmessage.getRequestHeader().getURI().getURI(), //the url field 
                                "", //parameter being attacked: none.
                                "", //attack
                                Constant.messages.getString(MESSAGE_PREFIX_ADOBE_SEND + "extrainfo",
                                        "/" + ADOBE_CROSS_DOMAIN_POLICY_FILE), //extrainfo
                                Constant.messages.getString(MESSAGE_PREFIX_ADOBE_SEND + "soln"), //solution
                                "<allow-http-request-headers-from domain=\"*\"", // evidence
                                crossdomainmessage //the message on which to place the alert
                        );
                    }
                }
            } catch (SAXException | IOException e) {
                log.error("An error occurred trying to parse " + ADOBE_CROSS_DOMAIN_POLICY_FILE + " as XML: " + e);
            }

            //retrieve the Silverlight client access policy file, and assess it.
            HttpMessage clientaccesspolicymessage = new HttpMessage(new URI(originalURI.getScheme(),
                    originalURI.getAuthority(), "/" + SILVERLIGHT_CROSS_DOMAIN_POLICY_FILE, null, null));
            sendAndReceive(clientaccesspolicymessage, false);
            byte[] clientaccesspolicymessagebytes = clientaccesspolicymessage.getResponseBody().getBytes();

            //parse the file. If it's not parseable, it might have been because of a 404         
            try {
                //work around the "no protocol" issue by wrapping the content in a ByteArrayInputStream
                Document silverlightXmldoc = docBuilder
                        .parse(new InputSource(new ByteArrayInputStream(clientaccesspolicymessagebytes)));
                XPathExpression exprAllowFromUri = xpath
                        .compile("/access-policy/cross-domain-access/policy/allow-from/domain/@uri"); //gets the uri attributes
                //check the "allow-from" policies
                NodeList exprAllowFromUriNodes = (NodeList) exprAllowFromUri.evaluate(silverlightXmldoc,
                        XPathConstants.NODESET);
                for (int i = 0; i < exprAllowFromUriNodes.getLength(); i++) {
                    String uri = exprAllowFromUriNodes.item(i).getNodeValue();
                    if (uri.equals("*")) {
                        //tut, tut, tut.
                        if (log.isInfoEnabled())
                            log.info("Bingo! " + SILVERLIGHT_CROSS_DOMAIN_POLICY_FILE
                                    + ", at /access-policy/cross-domain-access/policy/allow-from/domain/@uri");
                        bingo(getRisk(), Alert.WARNING,
                                Constant.messages.getString(MESSAGE_PREFIX_SILVERLIGHT + "name"),
                                Constant.messages.getString(MESSAGE_PREFIX_SILVERLIGHT + "desc"),
                                clientaccesspolicymessage.getRequestHeader().getURI().getURI(), //the url field 
                                "", //parameter being attacked: none.
                                "", //attack
                                Constant.messages.getString(MESSAGE_PREFIX_SILVERLIGHT + "extrainfo"), //extrainfo
                                Constant.messages.getString(MESSAGE_PREFIX_SILVERLIGHT + "soln"), //solution
                                "<domain uri=\"*\"", // evidence
                                clientaccesspolicymessage //the message on which to place the alert
                        );
                    }
                }

            } catch (SAXException | IOException e) {
                log.error("An error occurred trying to parse " + SILVERLIGHT_CROSS_DOMAIN_POLICY_FILE + " as XML: "
                        + e);
            }

        } catch (Exception e) {
            //needed to catch exceptions from the "finally" statement 
            log.error("Error scanning a node for Cross Domain misconfigurations: " + e.getMessage(), e);
        }
    }

    @Override
    public int getRisk() {
        return Alert.RISK_HIGH;
    }

    @Override
    public int getCweId() {
        return 264; //CWE 264: Permissions, Privileges, and Access Controls
        //the more specific CWE's under this one are not rally relevant
    }

    @Override
    public int getWascId() {
        return 14; //WASC-14: Server Misconfiguration
    }

}