Example usage for org.apache.commons.httpclient URI getPath

List of usage examples for org.apache.commons.httpclient URI getPath

Introduction

In this page you can find the example usage for org.apache.commons.httpclient URI getPath.

Prototype

public String getPath() throws URIException 

Source Link

Document

Get the path.

Usage

From source file:org.zaproxy.zap.extension.ascanrulesAlpha.SourceCodeDisclosureCVE20121823.java

@Override
public void scan() {
    try {/*from   www.  j  a  v a 2s .co  m*/
        //at Low or Medium strength, do not attack URLs which returned "Not Found"
        AttackStrength attackStrength = getAttackStrength();
        if ((attackStrength == AttackStrength.LOW || attackStrength == AttackStrength.MEDIUM)
                && (getBaseMsg().getResponseHeader().getStatusCode() == HttpStatus.SC_NOT_FOUND))
            return;

        URI originalURI = getBaseMsg().getRequestHeader().getURI();

        //construct a new URL based on the original URL, but without any of the original parameters
        String attackParam = "?-s";
        URI attackURI = new URI(originalURI.getScheme() + "://" + originalURI.getAuthority()
                + (originalURI.getPath() != null ? originalURI.getPath() : "/") + attackParam, true);
        //and send it as a GET, unauthorised.
        HttpMessage attackmsg = new HttpMessage(attackURI);
        sendAndReceive(attackmsg, false); //do not follow redirects

        if (attackmsg.getResponseHeader().getStatusCode() == HttpStatus.SC_OK) {
            //double-check: does the response contain HTML encoded PHP? 
            //Ignore the case where it contains encoded HTML for now, since thats not a source code disclosure anyway 
            //(HTML is always sent back to the web browser)
            String responseBody = new String(attackmsg.getResponseBody().getBytes());
            String responseBodyDecoded = new Source(responseBody).getRenderer().toString();

            Matcher matcher1 = PHP_PATTERN1.matcher(responseBodyDecoded);
            Matcher matcher2 = PHP_PATTERN2.matcher(responseBodyDecoded);
            boolean match1 = matcher1.matches();
            boolean match2 = matcher2.matches();

            if ((!responseBody.equals(responseBodyDecoded)) && (match1 || match2)) {

                if (log.isDebugEnabled()) {
                    log.debug("Source Code Disclosure alert for: " + originalURI.getURI());
                }

                String sourceCode = null;
                if (match1) {
                    sourceCode = matcher1.group(1);
                } else {
                    sourceCode = matcher2.group(1);
                }

                //bingo.
                bingo(Alert.RISK_HIGH, Alert.WARNING,
                        Constant.messages.getString("ascanalpha.sourcecodedisclosurecve-2012-1823.name"),
                        Constant.messages.getString("ascanalpha.sourcecodedisclosurecve-2012-1823.desc"), null, // originalMessage.getRequestHeader().getURI().getURI(),
                        null, // parameter being attacked: none.
                        "", // attack: none (it's not a parameter being attacked)
                        sourceCode, //extrainfo
                        Constant.messages.getString("ascanalpha.sourcecodedisclosurecve-2012-1823.soln"), "", //evidence, highlighted in the message  (cannot use the source code here, since it is encoded in the message response, and so will not match up)
                        attackmsg //raise the alert on the attack message
                );
            }
        }
    } catch (Exception e) {
        log.error("Error scanning a Host for Source Code Disclosure via CVE-2012-1823: " + e.getMessage(), e);
    }
}

From source file:org.zaproxy.zap.extension.ascanrulesAlpha.SourceCodeDisclosureFileInclusion.java

/**
 * scans the given parameter for source code disclosure vulnerabilities, using path traversal
 * vulnerabilities/*from   www  . ja  va  2s. c  o  m*/
 */
@Override
public void scan(HttpMessage originalmsg, String paramname, String paramvalue) {
    try {
        URI uri = originalmsg.getRequestHeader().getURI();
        String path = uri.getPath();
        if (path == null || "/".equals(path)) {
            // No path or empty path, no point continuing.
            return;
        }

        if (log.isDebugEnabled()) {
            log.debug("Attacking at Attack Strength: " + this.getAttackStrength());
            log.debug("Checking [" + getBaseMsg().getRequestHeader().getMethod() + "] ["
                    + getBaseMsg().getRequestHeader().getURI() + "], parameter [" + paramname
                    + "], with original value [" + paramvalue + "] for Source Code Disclosure");
        }
        // the response of the original message is not populated! so populate it.
        sendAndReceive(originalmsg, false); // do nto follow redirects

        // first send a query for a random parameter value
        // then try a query for the file paths and names that we are using to try to get out the
        // source code for the current URL
        HttpMessage randomfileattackmsg = getNewMsg();
        setParameter(randomfileattackmsg, paramname, NON_EXISTANT_FILENAME);
        sendAndReceive(randomfileattackmsg, false); // do not follow redirects

        int originalversusrandommatchpercentage = calcMatchPercentage(originalmsg.getResponseBody().toString(),
                randomfileattackmsg.getResponseBody().toString());
        if (originalversusrandommatchpercentage > this.thresholdPercentage) {
            // the output for the "random" file does not sufficiently differ. bale out.
            if (log.isDebugEnabled()) {
                log.debug("The output for a non-existent filename [" + NON_EXISTANT_FILENAME
                        + "] does not sufficiently differ from that of the original parameter [" + paramvalue
                        + "], at " + originalversusrandommatchpercentage + "%, compared to a threshold of "
                        + this.thresholdPercentage + "%");
            }
            return;
        }

        if (this.isStop()) {
            if (log.isDebugEnabled())
                log.debug("Stopped, due to a user request");
            return;
        }

        // at this point, there was a sufficient difference between the random filename and the
        // original parameter
        // so lets try the various path names that might point at the source code for this URL
        String pathMinusLeadingSlash = uri.getPath().substring(1);
        String pathMinusApplicationContext = uri.getPath().substring(uri.getPath().indexOf("/", 1) + 1);

        // in the case of wavsep, should give us "wavsep"
        // use this later to build up "wavsep.war", and "wavsep.ear", for instance :)
        String applicationContext = uri.getPath().substring(1, uri.getPath().indexOf("/", 1));

        // all of the sourceFileNames should *not* lead with a slash.
        String[] sourceFileNames = { uri.getName(), pathMinusLeadingSlash, pathMinusApplicationContext };

        // and get the file extension (in uppercase), so we can switch on it (if there was an
        // extension, that is)
        String fileExtension = null;
        if (uri.getName().contains(".")) {
            fileExtension = uri.getName().substring(uri.getName().lastIndexOf(".") + 1);
            fileExtension = fileExtension.toUpperCase();
        }

        // for each of the file names in turn, try it with each of the prefixes
        for (String sourcefilename : sourceFileNames) {
            if (log.isDebugEnabled()) {
                log.debug("Source file is [" + sourcefilename + "]");
            }
            // for the url filename, try each of the prefixes in turn
            for (int h = 0; h < LOCAL_SOURCE_FILE_TARGET_PREFIXES.length; h++) {

                String prefixedUrlfilename = LOCAL_SOURCE_FILE_TARGET_PREFIXES[h] + sourcefilename;
                if (log.isDebugEnabled()) {
                    log.debug("Trying file name [" + prefixedUrlfilename + "]");
                }

                HttpMessage sourceattackmsg = getNewMsg();
                setParameter(sourceattackmsg, paramname, prefixedUrlfilename);
                // send the modified message (with the url filename), and see what we get back
                sendAndReceive(sourceattackmsg, false); // do not follow redirects

                int randomversussourcefilenamematchpercentage = calcMatchPercentage(
                        randomfileattackmsg.getResponseBody().toString(),
                        sourceattackmsg.getResponseBody().toString());
                if (randomversussourcefilenamematchpercentage > this.thresholdPercentage) {
                    // the output for the "source" file does not sufficiently differ from the
                    // random file name. bale out.
                    if (log.isDebugEnabled()) {
                        log.debug("The output for the source code filename [" + prefixedUrlfilename
                                + "] does not sufficiently differ from that of the random parameter, at "
                                + randomversussourcefilenamematchpercentage + "%, compared to a threshold of "
                                + this.thresholdPercentage + "%");
                    }
                } else {
                    // if we verified the response
                    if (dataMatchesExtension(sourceattackmsg.getResponseBody().getBytes(), fileExtension)) {
                        if (log.isDebugEnabled()) {
                            log.debug("Source code disclosure!  The output for the source code filename ["
                                    + prefixedUrlfilename
                                    + "] differs sufficiently from that of the random parameter, at "
                                    + randomversussourcefilenamematchpercentage
                                    + "%, compared to a threshold of " + this.thresholdPercentage + "%");
                        }

                        // if we get to here, is is very likely that we have source file
                        // inclusion attack. alert it.
                        bingo(Alert.RISK_HIGH, Alert.CONFIDENCE_MEDIUM,
                                Constant.messages.getString("ascanalpha.sourcecodedisclosure.lfibased.name"),
                                Constant.messages.getString("ascanalpha.sourcecodedisclosure.desc"),
                                getBaseMsg().getRequestHeader().getURI().getURI(), paramname,
                                prefixedUrlfilename,
                                Constant.messages.getString(
                                        "ascanalpha.sourcecodedisclosure.lfibased.extrainfo",
                                        prefixedUrlfilename, NON_EXISTANT_FILENAME,
                                        randomversussourcefilenamematchpercentage, this.thresholdPercentage),
                                Constant.messages.getString("ascanalpha.sourcecodedisclosure.lfibased.soln"),
                                Constant.messages.getString(
                                        "ascanalpha.sourcecodedisclosure.lfibased.evidence"),
                                sourceattackmsg);
                        // All done on this parameter
                        return;
                    } else {
                        if (log.isDebugEnabled()) {
                            log.debug("Could not verify that the HTML output is source code of type "
                                    + fileExtension + ". Next!");
                        }
                    }
                }
                if (this.isStop()) {
                    if (log.isDebugEnabled())
                        log.debug("Stopped, due to a user request");
                    return;
                }
            }
        }

        if (!inScope(Tech.Tomcat)) {
            return;
        }

        // if the above fails, get the entire WAR/EAR
        // but only if in HIGH or INSANE attack strength, since this generates more work and
        // slows Zap down badly if it actually
        // finds and returns the application WAR file!

        if (this.getAttackStrength() == AttackStrength.INSANE
                || this.getAttackStrength() == AttackStrength.HIGH) {

            // all of the warearFileNames should *not* lead with a slash.
            // TODO: should we consider uppercase / lowercase on (real) OSs such as Linux that
            // support such a thing?
            // Note that each of these file types can contain the Java class files, which can be
            // disassembled into the Java source code.
            // this in fact is one of my favourite hacking techniques.
            String[] warearFileNames = { applicationContext + ".war", applicationContext + ".ear",
                    applicationContext + ".rar" };

            // for each of the EAR / file names in turn, try it with each of the prefixes
            for (String sourcefilename : warearFileNames) {
                if (log.isDebugEnabled()) {
                    log.debug("WAR/EAR file is [" + sourcefilename + "]");
                }
                // for the url filename, try each of the prefixes in turn
                for (int h = 0; h < LOCAL_WAR_EAR_FILE_TARGET_PREFIXES.length; h++) {

                    String prefixedUrlfilename = LOCAL_WAR_EAR_FILE_TARGET_PREFIXES[h] + sourcefilename;
                    if (log.isDebugEnabled()) {
                        log.debug("Trying WAR/EAR file name [" + prefixedUrlfilename + "]");
                    }

                    HttpMessage sourceattackmsg = getNewMsg();
                    setParameter(sourceattackmsg, paramname, prefixedUrlfilename);
                    // send the modified message (with the url filename), and see what we get
                    // back
                    sendAndReceive(sourceattackmsg, false); // do not follow redirects
                    if (log.isDebugEnabled()) {
                        log.debug("Completed WAR/EAR file name [" + prefixedUrlfilename + "]");
                    }

                    // since the WAR/EAR file may be large, and since the LCS does not work well
                    // with such large files, lets just look at the file size,
                    // compared to the original
                    int randomversussourcefilenamematchpercentage = calcLengthMatchPercentage(
                            sourceattackmsg.getResponseBody().length(),
                            randomfileattackmsg.getResponseBody().length());
                    if (randomversussourcefilenamematchpercentage < this.thresholdPercentage) {
                        if (log.isDebugEnabled()) {
                            log.debug("Source code disclosure!  The output for the WAR/EAR filename ["
                                    + prefixedUrlfilename
                                    + "] differs sufficiently (in length) from that of the random parameter, at "
                                    + randomversussourcefilenamematchpercentage
                                    + "%, compared to a threshold of " + this.thresholdPercentage + "%");
                        }

                        // Note: no verification of the file contents in this case.

                        // if we get to here, is is very likely that we have source file
                        // inclusion attack. alert it.
                        bingo(Alert.RISK_HIGH, Alert.CONFIDENCE_MEDIUM,
                                Constant.messages.getString("ascanalpha.sourcecodedisclosure.lfibased.name"),
                                Constant.messages.getString("ascanalpha.sourcecodedisclosure.desc"),
                                getBaseMsg().getRequestHeader().getURI().getURI(), paramname,
                                prefixedUrlfilename,
                                Constant.messages.getString(
                                        "ascanalpha.sourcecodedisclosure.lfibased.extrainfo",
                                        prefixedUrlfilename, NON_EXISTANT_FILENAME,
                                        randomversussourcefilenamematchpercentage, this.thresholdPercentage),
                                Constant.messages.getString("ascanalpha.sourcecodedisclosure.lfibased.soln"),
                                Constant.messages.getString(
                                        "ascanalpha.sourcecodedisclosure.lfibased.evidence"),
                                sourceattackmsg);

                        // All done. No need to look for vulnerabilities on subsequent
                        // parameters on the same request (to reduce performance impact)
                        return;
                    } else {
                        if (log.isDebugEnabled()) {
                            log.debug("The output for the WAR/EAR code filename [" + prefixedUrlfilename
                                    + "] does not sufficiently differ in length from that of the random parameter, at "
                                    + randomversussourcefilenamematchpercentage
                                    + "%, compared to a threshold of " + this.thresholdPercentage + "%");
                        }
                    }
                    if (this.isStop()) {
                        if (log.isDebugEnabled())
                            log.debug("Stopped, due to a user request");
                        return;
                    }
                }
            }
        } else {
            if (log.isDebugEnabled()) {
                log.debug(
                        "Not checking for EAR/WAR files for this request, since the Attack Strength is not HIGH or INSANE");
            }
        }

    } catch (Exception e) {
        log.error("Error scanning parameters for Source Code Disclosure: " + e.getMessage(), e);
    }
}

From source file:org.zaproxy.zap.extension.ascanrulesAlpha.SourceCodeDisclosureGit.java

/**
 * finds the source code for the given file, using Git metadata on the server (if this is
 * available)//w  w  w.j  ava2 s  .co m
 *
 * @param uri the URI of a file, whose source code we want to find
 * @return Did we find the source code?
 */
private boolean findSourceCodeGit(HttpMessage originalMessage) throws Exception {
    byte[] disclosedData = {};
    String gitsha1 = null;
    String gitindexpath = null;
    try {
        URI originalURI = originalMessage.getRequestHeader().getURI();
        // String originalURIWithoutQuery = originalURI.getScheme() + "://" +
        // originalURI.getAuthority() + originalURI.getPath();
        // String canonicalisedOriginalURIStringWithoutQuery =
        // URLCanonicalizer.getCanonicalURL(originalURIWithoutQuery);
        String path = originalURI.getPath();
        if (path == null)
            path = "";
        String filename = originalURI.getName();

        String fileExtension = null;
        if (filename.contains(".")) {
            fileExtension = filename.substring(filename.lastIndexOf(".") + 1);
            fileExtension = fileExtension.toUpperCase();
        }

        URI originalURIWithoutQuery = new URI(originalURI.getScheme(), originalURI.getAuthority(), path, null,
                null);
        GitMetadata git = new GitMetadata(this.getParent(), 4096);
        GitIndexEntryCache gitindexentrycache = GitIndexEntryCache.getSingleton();

        // look for the .git/index file in the directory and parent directories of the file for
        // which we are attempting to get the source code.
        String modifiedpath = path;
        byte[] data = {};
        boolean gitSHA1located = false;
        // work backwards from the original path, stripping off one folder at a time
        // until we find a valid Git index file that contains our file name!
        modifiedpath = modifiedpath.substring(0, modifiedpath.lastIndexOf("/") + 1); // leave the trailing slash on, if there was one
        while ((!modifiedpath.equals("")) && (!gitSHA1located)) {

            if (log.isDebugEnabled())
                log.debug("Path is " + modifiedpath);

            gitindexpath = modifiedpath + ".git/index";

            URI gitindexuri = new URI(originalURI.getScheme(), originalURI.getAuthority(), gitindexpath, null,
                    null);
            try {
                if (log.isDebugEnabled())
                    log.debug("Trying for a Git index file " + gitindexuri.getURI());

                if (!gitindexentrycache.isIndexCached(gitindexuri)) {
                    // The Git index is not cached, so parse it and cache it.
                    if (log.isDebugEnabled())
                        log.debug("Git Index " + gitindexuri.getURI()
                                + " is not cached. We will parse and cache it");

                    data = git.getURIResponseBody(gitindexuri, false, originalMessage);
                    // get the list of relative file paths and Git SHA1s from the file
                    Map<String, String> gitFiles = git.getIndexSha1s(data);
                    if (gitFiles != null) {
                        if (log.isDebugEnabled())
                            log.debug("We found a Git index file at '" + gitindexpath + "'");

                        Set<Entry<String, String>> entrySet = gitFiles.entrySet();
                        Iterator<Entry<String, String>> entryIterator = entrySet.iterator();
                        while (entryIterator.hasNext()) {
                            Entry<String, String> gitIndexEntry = entryIterator.next();

                            // the URIs from the Git index file do not have a query or fragment
                            // component, so no need to strip those off here
                            URI gitIndexEntryUri = new URI(originalURI.getScheme(), originalURI.getAuthority(),
                                    modifiedpath + gitIndexEntry.getKey(), null, null);
                            String gitSHA1Temp = gitIndexEntry.getValue();

                            // cache the entry..
                            if (log.isDebugEnabled())
                                log.debug("Caching Git Index file " + gitindexuri.getURI() + ", Index Entry "
                                        + gitIndexEntryUri.getURI() + ", SHA1 " + gitSHA1Temp);
                            gitindexentrycache.putIndexEntry(gitindexuri, gitIndexEntryUri, gitSHA1Temp);
                        }
                    }
                }
                // at this point, we know the Git index file is cached, one way or another.
                // did we get the Git SHA1 of the file we were interested in, after all that?
                if (gitindexentrycache.isIndexEntryCached(gitindexuri, originalURIWithoutQuery)) {
                    // no need to keep on looping back up, if we found our entry
                    gitSHA1located = true;
                    gitsha1 = gitindexentrycache.getIndexEntry(gitindexuri, originalURIWithoutQuery);
                    log.debug("Git SHA1 '" + gitsha1 + "' was found for Git index file '" + gitindexuri
                            + ", Git index entry file '" + originalURIWithoutQuery + "'");
                    break;
                } else {
                    log.debug("A cache entry was not found for Git index file '" + gitindexuri
                            + ", Git index entry file '" + originalURIWithoutQuery + "'");
                }

            } catch (Exception e) {
                if (log.isDebugEnabled())
                    log.debug("Ignoring an error getting/parsing '" + gitindexpath
                            + "', while trying to find the Git SHA1 value for '" + path + "': " + e);
            } finally {
                // move to the next parent directory, by first stripping off the trailing index,
                // and grabbing up to and including the last index
                modifiedpath = modifiedpath.substring(0, modifiedpath.length() - 1);
                modifiedpath = modifiedpath.substring(0, modifiedpath.lastIndexOf("/") + 1); // leave the trailing slash on, if there was one
            }

            if (isStop()) {
                if (log.isDebugEnabled())
                    log.debug(
                            "Stopped scanner (while trying to find the Git index file), due to a user request");
                return false;
            }
        }

        // do we have a shot at getting the source code using Git?
        if (gitsha1 == null || gitsha1.equals("") || gitindexpath == null || gitindexpath.equals("")) {
            if (log.isDebugEnabled())
                log.debug("A Git SHA1 value or Git index path for '" + path + "' was not found.");
            return false;
        }
        if (!git.validateSHA1(gitsha1)) {
            if (log.isDebugEnabled())
                log.debug("The 'gitsha1' parameter '" + gitsha1
                        + "' does not appear to be a valid format for a Git SHA1 value");
            return false;
        }
        String gitbasepath = git.getBaseFolder(gitindexpath);
        if (gitbasepath == null || gitbasepath.equals("")) {
            if (log.isDebugEnabled())
                log.debug("The 'gitindexpath' parameter '" + gitbasepath + "' does not appear to be valid.");
            return false;
        }
        // get the data from Git, using its SHA1 value.
        disclosedData = git.getObjectData(this.getBaseMsg(), gitbasepath, gitsha1); // look for data for the file's Git SHA1, and inflate it
        String gitURIs = git.getGitURIs();

        // so we have the data from Git for the sha1/file in questions.. does it match the
        // original data?
        // if not (but if it still looks valid), then throw a "source code disclosure" alert
        if (!Arrays.equals(disclosedData, originalMessage.getResponseBody().getBytes())) {

            // check the contents of the output to some degree, if we have a file extension.
            // if not, just try it (could be a false positive, but hey)
            if (dataMatchesExtension(disclosedData, fileExtension)) {
                if (log.isDebugEnabled()) {
                    log.debug("Source code disclosure, using Git metadata leakage!");
                }

                // source file inclusion attack. alert it.
                // Note that, unlike with SVN, the Git data is extracted not from one file, but
                // by parsing a series of files.
                // we cannot meaningfully raise an alert on any one file, except perhaps the
                // file on which the attack was launched.
                // it's the least worst way of doing it, IMHO.
                bingo(Alert.RISK_HIGH, Alert.CONFIDENCE_MEDIUM, getName(), getDescription(),
                        getBaseMsg().getRequestHeader().getURI().getURI(), null, // parameter being attacked: none.
                        null, // attack
                        new String(disclosedData), // Constant.messages.getString("ascanalpha.sourcecodedisclosure.gitbased.extrainfo", filename, StringUtils.join(gitURIs,", ")),     //extraInfo
                        getSolution(), getEvidence(filename, gitURIs), originalMessage);
                return true;
            }
            // does not match the extension
            return false;
        } else {
            if (log.isDebugEnabled())
                log.debug(
                        "The data disclosed via Git meta-data is not source code, since it matches the data served when we requested the file in the normal manner (source code is not served by web apps, and if it is, then you have bigger problems)");
            return false;
        }
    } catch (FileNotFoundException e) {
        if (log.isDebugEnabled())
            log.debug("A file was not found for SHA1 '" + gitsha1 + "'");
        return false;
    } catch (Exception e) {
        log.error("Some other error occurred when reading data for Git SHA1 '" + gitsha1 + "': " + e);
        return false;
    }
}

From source file:org.zaproxy.zap.extension.ascanrulesAlpha.SourceCodeDisclosureSVN.java

/**
 * finds the source code for the given file, using SVN metadata on the server (if this is available)
 * @param uri the URI of a file, whose source code we want to find
 * @return Did we find the source code?// w  w  w .j a v  a 2  s.  c  o  m
 */
private boolean findSourceCodeSVN(HttpMessage originalMessage) throws Exception {

    URI uri = originalMessage.getRequestHeader().getURI();
    String path = uri.getPath();
    if (path == null)
        path = "";
    //String filename = path.substring( path.lastIndexOf('/')+1, path.length() );
    String filename = uri.getName();

    String fileExtension = null;
    if (filename.contains(".")) {
        fileExtension = filename.substring(filename.lastIndexOf(".") + 1);
        fileExtension = fileExtension.toUpperCase();
    }

    //Look for SVN metadata containing source code
    String pathminusfilename = path.substring(0, path.lastIndexOf(filename));

    HttpMessage svnsourcefileattackmsg = new HttpMessage(new URI(uri.getScheme(), uri.getAuthority(),
            pathminusfilename + ".svn/text-base/" + filename + ".svn-base", null, null));
    svnsourcefileattackmsg.setCookieParams(this.getBaseMsg().getCookieParams());
    //svnsourcefileattackmsg.setRequestHeader(this.getBaseMsg().getRequestHeader());
    sendAndReceive(svnsourcefileattackmsg);

    //if we got a 404 specifically, then this is NOT a match
    //note that since we are simply relying on the file existing or not, we 
    //will not attempt any fuzzy matching. Old school.
    //this check is necessary, otherwise a recursive scan on nodes in the url path cause lots of false positives.
    if (svnsourcefileattackmsg.getResponseHeader().getStatusCode() != HttpStatusCode.NOT_FOUND) {

        if (!Arrays.equals(svnsourcefileattackmsg.getResponseBody().getBytes(),
                originalMessage.getResponseBody().getBytes())) {

            String attackFilename = uri.getScheme() + "://" + uri.getAuthority() + pathminusfilename
                    + ".svn/text-base/" + filename + ".svn-base";

            //check the contents of the output to some degree, if we have a file extension.
            //if not, just try it (could be a false positive, but hey)             
            if (dataMatchesExtension(svnsourcefileattackmsg.getResponseBody().getBytes(), fileExtension)) {
                log.info("Source code disclosure, using SVN metadata leakage!");

                //if we get to here, is is very likely that we have source file inclusion attack. alert it.
                bingo(Alert.RISK_HIGH, Alert.WARNING,
                        Constant.messages.getString("ascanalpha.sourcecodedisclosure.svnbased.name"),
                        Constant.messages.getString("ascanalpha.sourcecodedisclosure.desc"),
                        getBaseMsg().getRequestHeader().getURI().getURI(), null, attackFilename,
                        Constant.messages.getString("ascanalpha.sourcecodedisclosure.svnbased.extrainfo",
                                filename, attackFilename),
                        Constant.messages.getString("ascanalpha.sourcecodedisclosure.svnbased.soln"),
                        Constant.messages.getString("ascanalpha.sourcecodedisclosure.svnbased.evidence"),
                        svnsourcefileattackmsg);
                //if we found one, do not even try the "super" method, which tries each of the parameters,
                //since this is slow, and we already found an instance
                return true;
            } else {
                if (log.isDebugEnabled())
                    log.debug("The HTML output does not look like source code of type " + fileExtension);
            }
        } else {
            if (log.isDebugEnabled())
                log.debug(
                        "The data disclosed via SVN meta-data is not source code, since it matches the data served when we requested the file in the normal manner (source code is not served by web apps, and if it is, then you have bigger problems)");
            return false;
        }
    } else {
        if (log.isDebugEnabled()) {
            log.debug("Got a 404, so the SVN source code file was not found");
        }
    }
    return false;
}

From source file:org.zaproxy.zap.extension.ascanrulesBeta.BackupFileDisclosure.java

/**
 * attempts to find a backup file for the given file
 *
 * @param uri the URI of a file/* w w w . ja  va  2s. c  o  m*/
 * @return
 */
private void findBackupFile(HttpMessage originalMessage) throws Exception {

    try {
        boolean gives404s = true;
        boolean parentgives404s = true;
        byte[] nonexistparentmsgdata = null;

        URI originalURI = originalMessage.getRequestHeader().getURI();

        // request a file in the same directory to see how it handles "File not found". Using a
        // 404? Something else?
        String temppath = originalURI.getPath();
        if (temppath == null)
            temppath = "";
        int slashposition = temppath.lastIndexOf("/");
        if (slashposition < 0) {
            // WTF? there was no slash in the path..
            throw new Exception("The message has a path with a malformed path component");
        }
        String filename = originalMessage.getRequestHeader().getURI().getName();

        String randomfilename = RandomStringUtils.random(filename.length(),
                "abcdefghijklmoopqrstuvwxyz9123456789");
        String randomfilepath = temppath.substring(0, slashposition) + "/" + randomfilename;

        if (log.isDebugEnabled())
            log.debug("Trying non-existent file: " + randomfilepath);
        HttpMessage nonexistfilemsg = new HttpMessage(
                new URI(originalURI.getScheme(), originalURI.getAuthority(), randomfilepath, null, null));
        try {
            nonexistfilemsg.setCookieParams(originalMessage.getCookieParams());
        } catch (Exception e) {
            if (log.isDebugEnabled())
                log.debug("Could not set the cookies from the base request:" + e);
        }
        sendAndReceive(nonexistfilemsg, false);
        byte[] nonexistfilemsgdata = nonexistfilemsg.getResponseBody().getBytes();
        // does the server give a 404 for a non-existent file?
        if (nonexistfilemsg.getResponseHeader().getStatusCode() != HttpStatus.SC_NOT_FOUND) {
            gives404s = false;
            if (log.isDebugEnabled())
                log.debug("The server does not return a 404 status for a non-existent path: "
                        + nonexistfilemsg.getRequestHeader().getURI().getURI());
        } else {
            gives404s = true;
            if (log.isDebugEnabled())
                log.debug("The server gives a 404 status for a non-existent path: "
                        + nonexistfilemsg.getRequestHeader().getURI().getURI());
        }

        // now request a different (and non-existent) parent directory,
        // to see whether a non-existent parent folder causes a 404
        String[] pathbreak = temppath.split("/");
        HttpMessage nonexistparentmsg = null;
        if (pathbreak.length > 2) { // the file has a parent folder that is not the root folder (ie, there is
            // a parent folder to mess with)
            String[] temppathbreak = pathbreak;
            String parentfoldername = pathbreak[pathbreak.length - 2];
            String randomparentfoldername = RandomStringUtils.random(parentfoldername.length(),
                    "abcdefghijklmoopqrstuvwxyz9123456789");

            // replace the parent folder name with the random one, and build it back into a
            // string
            temppathbreak[pathbreak.length - 2] = randomparentfoldername;
            String randomparentpath = StringUtils.join(temppathbreak, "/");

            if (log.isDebugEnabled())
                log.debug("Trying non-existent parent path: " + randomparentpath);
            nonexistparentmsg = new HttpMessage(
                    new URI(originalURI.getScheme(), originalURI.getAuthority(), randomparentpath, null, null));
            try {
                nonexistparentmsg.setCookieParams(originalMessage.getCookieParams());
            } catch (Exception e) {
                if (log.isDebugEnabled())
                    log.debug("Could not set the cookies from the base request:" + e);
            }
            sendAndReceive(nonexistparentmsg, false);
            nonexistparentmsgdata = nonexistparentmsg.getResponseBody().getBytes();
            // does the server give a 404 for a non-existent parent folder?
            if (nonexistparentmsg.getResponseHeader().getStatusCode() != HttpStatus.SC_NOT_FOUND) {
                parentgives404s = false;
                if (log.isDebugEnabled())
                    log.debug("The server does not return a 404 status for a non-existent parent path: "
                            + nonexistparentmsg.getRequestHeader().getURI().getURI());
            } else {
                parentgives404s = true;
                if (log.isDebugEnabled())
                    log.debug("The server gives a 404 status for a non-existent parent path: "
                            + nonexistparentmsg.getRequestHeader().getURI().getURI());
            }
        }

        String actualfilename = originalURI.getName();
        String actualfileExtension = null;
        String path = originalURI.getPath();
        if (path == null)
            path = "";

        // record the position of the various injection points, always relative to the full path
        int positionExtensionInjection = 0;
        int positionFileSuffixInjection = 0;
        if (actualfilename.contains(".")) {
            positionExtensionInjection = path.lastIndexOf(".");
            positionFileSuffixInjection = positionExtensionInjection;
            actualfileExtension = actualfilename.substring(actualfilename.lastIndexOf("."));
        } else {
            positionExtensionInjection = path.length();
            positionFileSuffixInjection = path.length();
            actualfileExtension = "";
        }
        int positionFilePrefixInjection = path.lastIndexOf("/") + 1;
        int positionDirectorySuffixInjection = path.lastIndexOf("/");
        int positionDirectoryPrefixInjection = 0;
        if (positionDirectorySuffixInjection >= 0)
            positionDirectoryPrefixInjection = path.substring(0, positionDirectorySuffixInjection)
                    .lastIndexOf("/") + 1;

        // the set of files we will try, in the order of insertion
        Set<URI> candidateBackupFileURIs = new LinkedHashSet<URI>();
        Set<URI> candidateBackupFileChangedFolderURIs = new LinkedHashSet<URI>(); // for a changed parent folder name, which we need to handle
        // separately

        log.debug("The path is " + path);

        // for each file extension to try (both appending, and replacing)
        int counted = 0;
        for (String fileExtensionToTry : fileExtensions) {
            // to append, inject the file extension at the end of the path
            String candidateBackupFilePath = path + fileExtensionToTry;
            log.debug("File Extension (append): '" + candidateBackupFilePath + "'");
            candidateBackupFileURIs.add(new URI(originalURI.getScheme(), originalURI.getAuthority(),
                    candidateBackupFilePath, null, null));

            // to replace the extension, append the file extension at positionExtensionInjection
            candidateBackupFilePath = path.substring(0, positionExtensionInjection) + fileExtensionToTry;
            log.debug("File Extension (replace): '" + candidateBackupFilePath + "'");
            candidateBackupFileURIs.add(new URI(originalURI.getScheme(), originalURI.getAuthority(),
                    candidateBackupFilePath, null, null));

            // to switch the extension (if there was one), append the file extension at
            // positionExtensionInjection
            if (!actualfileExtension.equals("") && doSwitchFileExtension) {
                candidateBackupFilePath = path.substring(0, positionExtensionInjection) + fileExtensionToTry
                        + actualfileExtension;
                log.debug("File Extension (switch): '" + candidateBackupFilePath + "'");
                candidateBackupFileURIs.add(new URI(originalURI.getScheme(), originalURI.getAuthority(),
                        candidateBackupFilePath, null, null));
            }
            counted++;
            if (counted > numExtensionsToTry) {
                break; // out of the loop.
            }
        }

        // for each file suffix to try
        counted = 0;
        for (String fileSuffixToTry : fileSuffixes) {
            // inject the file suffix at positionFileSuffixInjection
            String candidateBackupFilePath = path.substring(0, positionFileSuffixInjection) + fileSuffixToTry
                    + (positionFileSuffixInjection >= path.length() ? ""
                            : path.substring(positionFileSuffixInjection));
            log.debug("File Suffix (insert): '" + candidateBackupFilePath + "'");
            candidateBackupFileURIs.add(new URI(originalURI.getScheme(), originalURI.getAuthority(),
                    candidateBackupFilePath, null, null));
            counted++;
            if (counted > numSuffixesToTry) {
                break; // out of the loop.
            }
        }

        // for each file prefix to try
        counted = 0;
        for (String filePrefixToTry : filePrefixes) {
            // inject the file prefix at positionFilePrefixInjection
            String candidateBackupFilePath = path.substring(0, positionFilePrefixInjection) + filePrefixToTry
                    + (positionFilePrefixInjection >= path.length() ? ""
                            : path.substring(positionFilePrefixInjection));
            log.debug("File Prefix (insert): '" + candidateBackupFilePath + "'");
            candidateBackupFileURIs.add(new URI(originalURI.getScheme(), originalURI.getAuthority(),
                    candidateBackupFilePath, null, null));
            counted++;
            if (counted > numPrefixesToTry) {
                break; // out of the loop.
            }
        }

        // for each directory suffix/prefix to try (using the file prefixes/suffixes - or
        // whatever the plural of prefix/suffix is)
        counted = 0;
        if (pathbreak.length > 2) {
            // if there is a a parent folder to play with
            for (String fileSuffixToTry : fileSuffixes) {
                // inject the directory suffix at positionDirectorySuffixInjection
                String candidateBackupFilePath = path.substring(0, positionDirectorySuffixInjection)
                        + fileSuffixToTry + (positionDirectorySuffixInjection >= path.length() ? ""
                                : path.substring(positionDirectorySuffixInjection));
                log.debug("Directory Suffix (insert): '" + candidateBackupFilePath + "'");
                candidateBackupFileChangedFolderURIs.add(new URI(originalURI.getScheme(),
                        originalURI.getAuthority(), candidateBackupFilePath, null, null));
                counted++;
                if (counted > numSuffixesToTry) {
                    break; // out of the loop.
                }
            }
            for (String filePrefixToTry : filePrefixes) {
                // inject the directory prefix at positionDirectorySuffixInjection
                String candidateBackupFilePath = path.substring(0, positionDirectoryPrefixInjection)
                        + filePrefixToTry + (positionDirectoryPrefixInjection >= path.length() ? ""
                                : path.substring(positionDirectoryPrefixInjection));
                log.debug("Directory Suffix (insert): '" + candidateBackupFilePath + "'");
                candidateBackupFileChangedFolderURIs.add(new URI(originalURI.getScheme(),
                        originalURI.getAuthority(), candidateBackupFilePath, null, null));
                counted++;
                if (counted > numSuffixesToTry) {
                    break; // out of the loop.
                }
            }
        }

        // now we have a set of candidate URIs appropriate to the attack strength chosen by the
        // user
        // try each candidate URI in turn.
        for (URI candidateBackupFileURI : candidateBackupFileURIs) {
            byte[] disclosedData = {};
            if (log.isDebugEnabled())
                log.debug("Trying possible backup file path: " + candidateBackupFileURI.getURI());
            HttpMessage requestmsg = new HttpMessage(candidateBackupFileURI);
            try {
                requestmsg.setCookieParams(originalMessage.getCookieParams());
            } catch (Exception e) {
                if (log.isDebugEnabled())
                    log.debug("Could not set the cookies from the base request:" + e);
            }
            // Do not follow redirects. They're evil. Yep.
            sendAndReceive(requestmsg, false);
            disclosedData = requestmsg.getResponseBody().getBytes();
            int requestStatusCode = requestmsg.getResponseHeader().getStatusCode();

            // just to complicate things.. I have a test case which for the random file, does
            // NOT give a 404 (so gives404s == false)
            // but for a "Copy of" file, actually gives a 404 (for some unknown reason). We need
            // to handle this case.
            if (!isEmptyResponse(disclosedData) && ((gives404s && requestStatusCode != HttpStatus.SC_NOT_FOUND)
                    || ((!gives404s) && nonexistfilemsg.getResponseHeader().getStatusCode() != requestStatusCode
                            && (!Arrays.equals(disclosedData, nonexistfilemsgdata))))) {
                bingo(Alert.RISK_MEDIUM, Alert.CONFIDENCE_MEDIUM,
                        Constant.messages.getString("ascanbeta.backupfiledisclosure.name"),
                        Constant.messages.getString("ascanbeta.backupfiledisclosure.desc"),
                        requestmsg.getRequestHeader().getURI().getURI(), // originalMessage.getRequestHeader().getURI().getURI(),
                        null, // parameter being attacked: none.
                        candidateBackupFileURI.getURI(), // attack
                        originalMessage.getRequestHeader().getURI().getURI(), // new String (disclosedData),  //extrainfo
                        Constant.messages.getString("ascanbeta.backupfiledisclosure.soln"),
                        Constant.messages.getString("ascanbeta.backupfiledisclosure.evidence", originalURI,
                                candidateBackupFileURI.getURI()),
                        requestmsg // originalMessage
                );
            }

            if (isStop()) {
                if (log.isDebugEnabled())
                    log.debug("The scanner was stopped in response to a user request");
                return;
            }
        }

        // now try the changed parent folders (if any)
        // the logic here needs to check using the parent 404 logic, and the output for a
        // non-existent parent folder.
        for (URI candidateBackupFileURI : candidateBackupFileChangedFolderURIs) {
            byte[] disclosedData = {};
            if (log.isDebugEnabled())
                log.debug("Trying possible backup file path (with changed parent folder): "
                        + candidateBackupFileURI.getURI());
            HttpMessage requestmsg = new HttpMessage(candidateBackupFileURI);
            try {
                requestmsg.setCookieParams(originalMessage.getCookieParams());
            } catch (Exception e) {
                if (log.isDebugEnabled())
                    log.debug("Could not set the cookies from the base request:" + e);
            }
            // Do not follow redirects. They're evil. Yep.
            sendAndReceive(requestmsg, false);
            disclosedData = requestmsg.getResponseBody().getBytes();
            int requestStatusCode = requestmsg.getResponseHeader().getStatusCode();
            // If the response is empty it's probably not really a backup

            if (!isEmptyResponse(disclosedData)
                    && ((parentgives404s && requestStatusCode != HttpStatus.SC_NOT_FOUND) || ((!parentgives404s)
                            && nonexistparentmsg.getResponseHeader().getStatusCode() != requestStatusCode
                            && (!Arrays.equals(disclosedData, nonexistparentmsgdata))))) {
                bingo(Alert.RISK_MEDIUM, Alert.CONFIDENCE_MEDIUM,
                        Constant.messages.getString("ascanbeta.backupfiledisclosure.name"),
                        Constant.messages.getString("ascanbeta.backupfiledisclosure.desc"),
                        requestmsg.getRequestHeader().getURI().getURI(), // originalMessage.getRequestHeader().getURI().getURI(),
                        null, // parameter being attacked: none.
                        candidateBackupFileURI.getURI(), // attack
                        originalMessage.getRequestHeader().getURI().getURI(), // new String (disclosedData),  //extrainfo
                        Constant.messages.getString("ascanbeta.backupfiledisclosure.soln"),
                        Constant.messages.getString("ascanbeta.backupfiledisclosure.evidence", originalURI,
                                candidateBackupFileURI.getURI()),
                        requestmsg // originalMessage
                );
            }

            if (isStop()) {
                if (log.isDebugEnabled())
                    log.debug("The scanner was stopped in response to a user request");
                return;
            }
        }

    } catch (Exception e) {
        log.error("Some error occurred when looking for a backup file for '"
                + originalMessage.getRequestHeader().getURI(), e);
        return;
    }
}

From source file:org.zaproxy.zap.extension.ascanrulesBeta.SessionFixation.java

/**
 * scans all GET, Cookie params for Session fields, and looks for SessionFixation
 * vulnerabilities/* w w w  . ja v a 2 s .co  m*/
 */
@Override
public void scan() {

    // TODO: scan the POST (form) params for session id fields.
    try {
        boolean loginUrl = false;

        // Are we dealing with a login url in any of the contexts of which this uri is part
        URI requestUri = getBaseMsg().getRequestHeader().getURI();
        ExtensionAuthentication extAuth = (ExtensionAuthentication) Control.getSingleton().getExtensionLoader()
                .getExtension(ExtensionAuthentication.NAME);

        // using the session, get the list of contexts for the url
        List<Context> contextList = extAuth.getModel().getSession().getContextsForUrl(requestUri.getURI());

        // now loop, and see if the url is a login url in each of the contexts in turn...
        for (Context context : contextList) {
            URI loginUri = extAuth.getLoginRequestURIForContext(context);
            if (loginUri != null && requestUri.getPath() != null) {
                if (requestUri.getScheme().equals(loginUri.getScheme())
                        && requestUri.getHost().equals(loginUri.getHost())
                        && requestUri.getPort() == loginUri.getPort()
                        && requestUri.getPath().equals(loginUri.getPath())) {
                    // we got this far.. only the method (GET/POST), user details, query params,
                    // fragment, and POST params
                    // are possibly different from the login page.
                    loginUrl = true;
                    break;
                }
            }
        }

        // For now (from Zap 2.0), the Session Fixation scanner will only run for login pages
        if (loginUrl == false) {
            log.debug("For the Session Fixation scanner to actually do anything, a Login Page *must* be set!");
            return;
        }
        // find all params set in the request (GET/POST/Cookie)
        // Note: this will be the full set, before we delete anything.

        TreeSet<HtmlParameter> htmlParams = new TreeSet<>();
        htmlParams.addAll(getBaseMsg().getRequestHeader().getCookieParams()); // request cookies only. no response cookies
        htmlParams.addAll(getBaseMsg().getFormParams()); // add in the POST params
        htmlParams.addAll(getBaseMsg().getUrlParams()); // add in the GET params

        // Now add in the pseudo parameters set in the URL itself, such as in the following:
        // http://www.example.com/someurl;JSESSIONID=abcdefg?x=123&y=456
        // as opposed to the url parameters in the following example, which are already picked
        // up by getUrlParams()
        // http://www.example.com/someurl?JSESSIONID=abcdefg&x=123&y=456

        // convert from org.apache.commons.httpclient.URI to a String
        String requestUrl = "Unknown URL";
        try {
            requestUrl = new URL(requestUri.getScheme(), requestUri.getHost(), requestUri.getPort(),
                    requestUri.getPath()).toString();
        } catch (Exception e) {
            // no point in continuing. The URL is invalid.  This is a peculiarity in the Zap
            // core,
            // and can happen when
            // - the user browsed to http://www.example.com/bodgeit and
            // - the user did not browse to http://www.example.com or to http://www.example.com/
            // so the Zap GUI displays "http://www.example.com" as a node under "Sites",
            // and under that, it displays the actual urls to which the user browsed
            // (http://www.example.com/bodgeit, for instance)
            // When the user selects the node "http://www.example.com", and tries to scan it
            // with
            // the session fixation scanner, the URI that is passed is "http://www.example.com",
            // which is *not* a valid url.
            // If the user actually browses to "http://www.example.com" (even without the
            // trailing slash)
            // the web browser appends the trailing slash, and so Zap records the URI as
            // "http://www.example.com/", which IS a valid url, and which can (and should) be
            // scanned.
            //
            // In short.. if this happens, we do not want to scan the URL anyway
            // (because the user never browsed to it), so just do nothing instead.

            log.error("Cannot convert URI [" + requestUri + "] to a URL: " + e.getMessage());
            return;
        }

        // suck out any pseudo url parameters from the url
        Set<HtmlParameter> pseudoUrlParams = getPseudoUrlParameters(requestUrl);
        htmlParams.addAll(pseudoUrlParams);
        if (this.debugEnabled)
            log.debug("Pseudo url params of URL [" + requestUrl + "] : [" + pseudoUrlParams + "]");

        //// for each parameter in turn,
        // int counter = 0;
        for (Iterator<HtmlParameter> iter = htmlParams.iterator(); iter.hasNext();) {

            HttpMessage msg1Final;
            HttpMessage msg1Initial = getNewMsg();

            //// debug logic only.. to do first field only
            // counter ++;
            // if ( counter > 1 )
            //   return;

            HtmlParameter currentHtmlParameter = iter.next();

            // Useful for debugging, but I can't find a way to view this data in the GUI, so
            // leave it out for now.
            // msg1Initial.setNote("Message 1 for parameter "+ currentHtmlParameter);

            if (this.debugEnabled)
                log.debug("Scanning URL [" + msg1Initial.getRequestHeader().getMethod() + "] ["
                        + msg1Initial.getRequestHeader().getURI() + "], [" + currentHtmlParameter.getType()
                        + "] field [" + currentHtmlParameter.getName() + "] with value ["
                        + currentHtmlParameter.getValue() + "] for Session Fixation");

            if (currentHtmlParameter.getType().equals(HtmlParameter.Type.cookie)) {

                // careful to pick up the cookies from the Request, and not to include cookies
                // set in any earlier response
                TreeSet<HtmlParameter> cookieRequestParams = msg1Initial.getRequestHeader().getCookieParams();
                // delete the original cookie from the parameters
                cookieRequestParams.remove(currentHtmlParameter);
                msg1Initial.setCookieParams(cookieRequestParams);

                // send the message, minus the cookie parameter, and see how it comes back.
                // Note: do NOT automatically follow redirects.. handle those here instead.
                sendAndReceive(msg1Initial, false, false);

                /////////////////////////////
                // create a copy of msg1Initial to play with to handle redirects (if any).
                // we use a copy because if we change msg1Initial itself, it messes the URL and
                // params displayed on the GUI.

                msg1Final = msg1Initial;
                HtmlParameter cookieBack1 = getResponseCookie(msg1Initial, currentHtmlParameter.getName());
                long cookieBack1TimeReceived = System.currentTimeMillis(); // in ms.  when was the cookie received?
                // Important if it has a Max-Age directive
                Date cookieBack1ExpiryDate = null;

                HttpMessage temp = msg1Initial;

                int redirectsFollowed1 = 0;
                while (HttpStatusCode.isRedirection(temp.getResponseHeader().getStatusCode())) {

                    // Note that we need to clone the Request and the Response..
                    // we seem to need to track the secure flag now to make sure its set later
                    boolean secure1 = temp.getRequestHeader().isSecure();
                    temp = temp.cloneAll(); // clone the previous message

                    redirectsFollowed1++;
                    if (redirectsFollowed1 > 10) {
                        throw new Exception("Too many redirects were specified in the first message");
                    }
                    // create a new URI from the absolute location returned, and interpret it as
                    // escaped
                    // note that the standard says that the Location returned should be
                    // absolute, but it ain't always so...
                    URI newLocation = new URI(temp.getResponseHeader().getHeader(HttpHeader.LOCATION), true);

                    // and follow the forward url
                    // need to clear the params (which would come from the initial POST,
                    // otherwise)
                    temp.getRequestHeader().setGetParams(new TreeSet<HtmlParameter>());
                    temp.setRequestBody("");
                    temp.setResponseBody(""); // make sure no values accidentally carry from one iteration to
                    // the next
                    try {
                        temp.getRequestHeader().setURI(newLocation);
                    } catch (Exception e) {
                        // the Location field contents may not be standards compliant. Lets
                        // generate a uri to use as a workaround where a relative path was
                        // given instead of an absolute one
                        URI newLocationWorkaround = new URI(temp.getRequestHeader().getURI(),
                                temp.getResponseHeader().getHeader(HttpHeader.LOCATION), true);
                        // try again, except this time, if it fails, don't try to handle it
                        if (this.debugEnabled)
                            log.debug("The Location [" + newLocation
                                    + "] specified in a redirect was not valid. Trying workaround url ["
                                    + newLocationWorkaround + "]");
                        temp.getRequestHeader().setURI(newLocationWorkaround);
                    }
                    temp.getRequestHeader().setSecure(secure1);
                    temp.getRequestHeader().setMethod(HttpRequestHeader.GET);
                    temp.getRequestHeader().setContentLength(0); // since we send a GET, the body will be 0 long
                    if (cookieBack1 != null) {
                        // if the previous request sent back a cookie, we need to set that
                        // cookie when following redirects, as a browser would
                        if (this.debugEnabled)
                            log.debug("Adding in cookie [" + cookieBack1 + "] for a redirect");
                        TreeSet<HtmlParameter> forwardCookieParams = temp.getRequestHeader().getCookieParams();
                        forwardCookieParams.add(cookieBack1);
                        temp.getRequestHeader().setCookieParams(forwardCookieParams);
                    }

                    if (this.debugEnabled)
                        log.debug("DEBUG: Cookie Message 1 causes us to follow redirect to [" + newLocation
                                + "]");

                    sendAndReceive(temp, false, false); // do NOT redirect.. handle it here

                    // handle any cookies set from following redirects that override the cookie
                    // set in the redirect itself (if any)
                    // note that this will handle the case where a latter cookie unsets one set
                    // earlier.
                    HtmlParameter cookieBack1Temp = getResponseCookie(temp, currentHtmlParameter.getName());
                    if (cookieBack1Temp != null) {
                        cookieBack1 = cookieBack1Temp;
                        cookieBack1TimeReceived = System.currentTimeMillis(); // in ms.  record when we got the
                        // cookie.. in case it has a
                        // Max-Age directive
                    }

                    // reset the "final" version of message1 to use the final response in the
                    // chain
                    msg1Final = temp;
                }
                ///////////////////////////

                // if non-200 on the final response for message 1, no point in continuing. Bale
                // out.
                if (msg1Final.getResponseHeader().getStatusCode() != HttpStatusCode.OK) {
                    if (this.debugEnabled)
                        log.debug(
                                "Got a non-200 response code [" + msg1Final.getResponseHeader().getStatusCode()
                                        + "] when sending [" + msg1Initial.getRequestHeader().getURI()
                                        + "] with param [" + currentHtmlParameter.getName()
                                        + "] = NULL (possibly somewhere in the redirects)");
                    continue;
                }

                // now check that the response set a cookie. if it didn't, then either..
                // 1) we are messing with the wrong field
                // 2) the app doesn't do sessions
                // either way, there is not much point in continuing to look at this field..

                if (cookieBack1 == null || cookieBack1.getValue() == null) {
                    // no cookie was set, or the cookie param was set to a null value
                    if (this.debugEnabled)
                        log.debug("The Cookie parameter was NOT set in the response, when cookie param ["
                                + currentHtmlParameter.getName() + "] was set to NULL: " + cookieBack1);
                    continue;
                }

                //////////////////////////////////////////////////////////////////////
                // at this point, before continuing to check for Session Fixation, do some other
                // checks on the session cookie we got back
                // that might cause us to raise additional alerts (in addition to doing the main
                // check for Session Fixation)
                //////////////////////////////////////////////////////////////////////

                // Check 1: was the session cookie sent and received securely by the server?
                // If not, alert this fact
                if ((!msg1Final.getRequestHeader().isSecure())
                        || (!cookieBack1.getFlags().contains("secure"))) {
                    // pass the original param value here, not the new value, since we're
                    // displaying the session id exposed in the original message
                    String extraInfo = Constant.messages.getString(
                            "ascanbeta.sessionidsentinsecurely.alert.extrainfo", currentHtmlParameter.getType(),
                            currentHtmlParameter.getName(), currentHtmlParameter.getValue());
                    if (!cookieBack1.getFlags().contains("secure")) {
                        extraInfo += ("\n" + Constant.messages.getString(
                                "ascanbeta.sessionidsentinsecurely.alert.extrainfo.secureflagnotset"));
                    }

                    // and figure out the risk, depending on whether it is a login page
                    int risk = Alert.RISK_LOW;
                    if (loginUrl) {
                        extraInfo += ("\n" + Constant.messages
                                .getString("ascanbeta.sessionidsentinsecurely.alert.extrainfo.loginpage"));
                        // login page, so higher risk
                        risk = Alert.RISK_MEDIUM;
                    } else {
                        // not a login page.. lower risk
                        risk = Alert.RISK_LOW;
                    }

                    String attack = Constant.messages.getString(
                            "ascanbeta.sessionidsentinsecurely.alert.attack", currentHtmlParameter.getType(),
                            currentHtmlParameter.getName());
                    String vulnname = Constant.messages.getString("ascanbeta.sessionidsentinsecurely.name");
                    String vulndesc = Constant.messages.getString("ascanbeta.sessionidsentinsecurely.desc");
                    String vulnsoln = Constant.messages.getString("ascanbeta.sessionidsentinsecurely.soln");

                    // call bingo with some extra info, indicating that the alert is
                    // not specific to Session Fixation, but has its own title and description
                    // (etc)
                    // the alert here is "Session id sent insecurely", or words to that effect.
                    bingo(risk, Alert.CONFIDENCE_MEDIUM, vulnname, vulndesc,
                            getBaseMsg().getRequestHeader().getURI().getURI(), currentHtmlParameter.getName(),
                            attack, extraInfo, vulnsoln, getBaseMsg());

                    if (log.isDebugEnabled()) {
                        String logMessage = MessageFormat.format(
                                "A session identifier in {2} field: [{3}] may be sent "
                                        + "via an insecure mechanism at [{0}] URL [{1}]",
                                getBaseMsg().getRequestHeader().getMethod(),
                                getBaseMsg().getRequestHeader().getURI().getURI(),
                                currentHtmlParameter.getType(), currentHtmlParameter.getName());
                        log.debug(logMessage);
                    }
                    // Note: do NOT continue to the next field at this point..
                    // since we still need to check for Session Fixation.
                }

                //////////////////////////////////////////////////////////////////////
                // Check 2: is the session cookie that was set accessible to Javascript?
                // If so, alert this fact too
                if (!cookieBack1.getFlags().contains("httponly") && loginUrl) {
                    // pass the original param value here, not the new value, since we're
                    // displaying the session id exposed in the original message
                    String extraInfo = Constant.messages.getString(
                            "ascanbeta.sessionidaccessiblebyjavascript.alert.extrainfo",
                            currentHtmlParameter.getType(), currentHtmlParameter.getName(),
                            currentHtmlParameter.getValue());
                    String attack = Constant.messages.getString(
                            "ascanbeta.sessionidaccessiblebyjavascript.alert.attack",
                            currentHtmlParameter.getType(), currentHtmlParameter.getName());
                    String vulnname = Constant.messages
                            .getString("ascanbeta.sessionidaccessiblebyjavascript.name");
                    String vulndesc = Constant.messages
                            .getString("ascanbeta.sessionidaccessiblebyjavascript.desc");
                    String vulnsoln = Constant.messages
                            .getString("ascanbeta.sessionidaccessiblebyjavascript.soln");

                    extraInfo += ("\n" + Constant.messages
                            .getString("ascanbeta.sessionidaccessiblebyjavascript.alert.extrainfo.loginpage"));

                    // call bingo with some extra info, indicating that the alert is
                    // not specific to Session Fixation, but has its own title and description
                    // (etc)
                    // the alert here is "Session id accessible in Javascript", or words to that
                    // effect.
                    bingo(Alert.RISK_LOW, Alert.CONFIDENCE_MEDIUM, vulnname, vulndesc,
                            getBaseMsg().getRequestHeader().getURI().getURI(), currentHtmlParameter.getName(),
                            attack, extraInfo, vulnsoln, getBaseMsg());

                    if (log.isDebugEnabled()) {
                        String logMessage = MessageFormat.format(
                                "A session identifier in [{0}] URL [{1}] {2} field: "
                                        + "[{3}] may be accessible to JavaScript",
                                getBaseMsg().getRequestHeader().getMethod(),
                                getBaseMsg().getRequestHeader().getURI().getURI(),
                                currentHtmlParameter.getType(), currentHtmlParameter.getName());
                        log.debug(logMessage);
                    }
                    // Note: do NOT continue to the next field at this point..
                    // since we still need to check for Session Fixation.
                }

                //////////////////////////////////////////////////////////////////////
                // Check 3: is the session cookie set to expire soon? when the browser session
                // closes? never?
                // the longer the session cookie is valid, the greater the risk. alert it
                // accordingly
                String cookieBack1Expiry = null;
                int sessionExpiryRiskLevel;
                String sessionExpiryDescription = null;

                // check for the Expires header
                for (Iterator<String> i = cookieBack1.getFlags().iterator(); i.hasNext();) {
                    String cookieBack1Flag = i.next();
                    // if ( this.debugEnabled ) log.debug("Cookie back 1 flag (checking for
                    // Expires): "+ cookieBack1Flag);
                    // match in a case insensitive manner. never know what case various web
                    // servers are going to send back.
                    // if (cookieBack1Flag.matches("(?i)expires=.*")) {
                    if (cookieBack1Flag.toLowerCase(Locale.ENGLISH).startsWith("expires=")) {
                        String[] cookieBack1FlagValues = cookieBack1Flag.split("=");
                        if (cookieBack1FlagValues.length > 1) {
                            if (this.debugEnabled)
                                log.debug("Cookie Expiry: " + cookieBack1FlagValues[1]);
                            cookieBack1Expiry = cookieBack1FlagValues[1]; // the Date String
                            sessionExpiryDescription = cookieBack1FlagValues[1]; // the Date String
                            cookieBack1ExpiryDate = DateUtil.parseDate(cookieBack1Expiry); // the actual Date
                        }
                    }
                }

                // also check for the Max-Age header, which overrides the Expires header.
                // WARNING: this Directive is reported to be ignored by IE, so if both Expires
                // and Max-Age are present
                // and we report based on the Max-Age value, but the user is using IE, then the
                // results reported
                // by us here may be different from those actually experienced by the user! (we
                // use Max-Age, IE uses Expires)
                for (Iterator<String> i = cookieBack1.getFlags().iterator(); i.hasNext();) {
                    String cookieBack1Flag = i.next();
                    // if ( this.debugEnabled ) log.debug("Cookie back 1 flag (checking for
                    // Max-Age): "+ cookieBack1Flag);
                    // match in a case insensitive manner. never know what case various web
                    // servers are going to send back.
                    if (cookieBack1Flag.toLowerCase(Locale.ENGLISH).startsWith("max-age=")) {
                        String[] cookieBack1FlagValues = cookieBack1Flag.split("=");
                        if (cookieBack1FlagValues.length > 1) {
                            // now the Max-Age value is the number of seconds relative to the
                            // time the browser received the cookie
                            // (as stored in cookieBack1TimeReceived)
                            if (this.debugEnabled)
                                log.debug("Cookie Max Age: " + cookieBack1FlagValues[1]);
                            long cookie1DropDeadMS = cookieBack1TimeReceived
                                    + (Long.parseLong(cookieBack1FlagValues[1]) * 1000);

                            cookieBack1ExpiryDate = new Date(cookie1DropDeadMS); // the actual Date the cookie
                            // expires (by Max-Age)
                            cookieBack1Expiry = DateUtil.formatDate(cookieBack1ExpiryDate,
                                    DateUtil.PATTERN_RFC1123);
                            sessionExpiryDescription = cookieBack1Expiry; // needs to the Date String
                        }
                    }
                }
                String sessionExpiryRiskDescription = null;
                // check the Expiry/Max-Age details garnered (if any)

                // and figure out the risk, depending on whether it is a login page
                // and how long the session will live before expiring
                if (cookieBack1ExpiryDate == null) {
                    // session expires when the browser closes.. rate this as medium risk?
                    sessionExpiryRiskLevel = Alert.RISK_MEDIUM;
                    sessionExpiryRiskDescription = "ascanbeta.sessionidexpiry.browserclose";
                    sessionExpiryDescription = Constant.messages.getString(sessionExpiryRiskDescription);
                } else {
                    long datediffSeconds = (cookieBack1ExpiryDate.getTime() - cookieBack1TimeReceived) / 1000;
                    long anHourSeconds = 3600;
                    long aDaySeconds = anHourSeconds * 24;
                    long aWeekSeconds = aDaySeconds * 7;

                    if (datediffSeconds < 0) {
                        if (this.debugEnabled)
                            log.debug("The session cookie has expired already");
                        sessionExpiryRiskDescription = "ascanbeta.sessionidexpiry.timeexpired";
                        sessionExpiryRiskLevel = Alert.RISK_INFO; // no risk.. the cookie has expired already
                    } else if (datediffSeconds > aWeekSeconds) {
                        if (this.debugEnabled)
                            log.debug("The session cookie is set to last for more than a week!");
                        sessionExpiryRiskDescription = "ascanbeta.sessionidexpiry.timemorethanoneweek";
                        sessionExpiryRiskLevel = Alert.RISK_HIGH;
                    } else if (datediffSeconds > aDaySeconds) {
                        if (this.debugEnabled)
                            log.debug("The session cookie is set to last for more than a day");
                        sessionExpiryRiskDescription = "ascanbeta.sessionidexpiry.timemorethanoneday";
                        sessionExpiryRiskLevel = Alert.RISK_MEDIUM;
                    } else if (datediffSeconds > anHourSeconds) {
                        if (this.debugEnabled)
                            log.debug("The session cookie is set to last for more than an hour");
                        sessionExpiryRiskDescription = "ascanbeta.sessionidexpiry.timemorethanonehour";
                        sessionExpiryRiskLevel = Alert.RISK_LOW;
                    } else {
                        if (this.debugEnabled)
                            log.debug("The session cookie is set to last for less than an hour!");
                        sessionExpiryRiskDescription = "ascanbeta.sessionidexpiry.timelessthanonehour";
                        sessionExpiryRiskLevel = Alert.RISK_INFO;
                    }
                }

                if (!loginUrl) {
                    // decrement the risk if it's not a login page
                    sessionExpiryRiskLevel--;
                }

                // alert it if the default session expiry risk level is more than informational
                if (sessionExpiryRiskLevel > Alert.RISK_INFO) {
                    // pass the original param value here, not the new value
                    String cookieReceivedTime = cookieBack1Expiry = DateUtil
                            .formatDate(new Date(cookieBack1TimeReceived), DateUtil.PATTERN_RFC1123);
                    String extraInfo = Constant.messages.getString("ascanbeta.sessionidexpiry.alert.extrainfo",
                            currentHtmlParameter.getType(), currentHtmlParameter.getName(),
                            currentHtmlParameter.getValue(), sessionExpiryDescription, cookieReceivedTime);
                    String attack = Constant.messages.getString("ascanbeta.sessionidexpiry.alert.attack",
                            currentHtmlParameter.getType(), currentHtmlParameter.getName());
                    String vulnname = Constant.messages.getString("ascanbeta.sessionidexpiry.name");
                    String vulndesc = Constant.messages.getString("ascanbeta.sessionidexpiry.desc");
                    String vulnsoln = Constant.messages.getString("ascanbeta.sessionidexpiry.soln");
                    if (loginUrl) {
                        extraInfo += ("\n" + Constant.messages
                                .getString("ascanbeta.sessionidexpiry.alert.extrainfo.loginpage"));
                    }

                    // call bingo with some extra info, indicating that the alert is
                    // not specific to Session Fixation, but has its own title and description
                    // (etc)
                    // the alert here is "Session Id Expiry Time is excessive", or words to that
                    // effect.
                    bingo(sessionExpiryRiskLevel, Alert.CONFIDENCE_MEDIUM, vulnname, vulndesc,
                            getBaseMsg().getRequestHeader().getURI().getURI(), currentHtmlParameter.getName(),
                            attack, extraInfo, vulnsoln, getBaseMsg());

                    if (log.isDebugEnabled()) {
                        String logMessage = MessageFormat.format(
                                "A session identifier in [{0}] URL [{1}] {2} field: "
                                        + "[{3}] may be accessed until [{4}], unless the session is destroyed.",
                                getBaseMsg().getRequestHeader().getMethod(),
                                getBaseMsg().getRequestHeader().getURI().getURI(),
                                currentHtmlParameter.getType(), currentHtmlParameter.getName(),
                                sessionExpiryDescription);
                        log.debug(logMessage);
                    }
                    // Note: do NOT continue to the next field at this point..
                    // since we still need to check for Session Fixation.
                }

                if (!loginUrl) {
                    // not a login page.. skip
                    continue;
                }

                ////////////////////////////////////////////////////////////////////////////////////////////
                /// Message 2 - processing starts here
                ////////////////////////////////////////////////////////////////////////////////////////////
                // so now that we know the URL responds with 200 (OK), and that it sets a
                // cookie, lets re-issue the original request,
                // but lets add in the new (valid) session cookie that was just issued.
                // we will re-send it.  the aim is then to see if it accepts the cookie (BAD, in
                // some circumstances),
                // or if it issues a new session cookie (GOOD, in most circumstances)
                if (this.debugEnabled)
                    log.debug("A Cookie was set by the URL for the correct param, when param ["
                            + currentHtmlParameter.getName() + "] was set to NULL: " + cookieBack1);

                // use a copy of msg2Initial, since it has already had the correct cookie
                // removed in the request..
                // do NOT use msg2Initial itself, as this will cause both requests in the GUI to
                // show the modified data..
                // finally send the second message, and see how it comes back.
                HttpMessage msg2Initial = msg1Initial.cloneRequest();

                TreeSet<HtmlParameter> cookieParams2Set = msg2Initial.getRequestHeader().getCookieParams();
                cookieParams2Set.add(cookieBack1);
                msg2Initial.setCookieParams(cookieParams2Set);

                // resend the copy of the initial message, but with the valid session cookie
                // added in, to see if it is accepted
                // do not automatically follow redirects, as we need to check these for cookies
                // being set.
                sendAndReceive(msg2Initial, false, false);

                // create a copy of msg2Initial to play with to handle redirects (if any).
                // we use a copy because if we change msg2Initial itself, it messes the URL and
                // params displayed on the GUI.
                HttpMessage temp2 = msg2Initial;
                HttpMessage msg2Final = msg2Initial;
                HtmlParameter cookieBack2Previous = cookieBack1;
                HtmlParameter cookieBack2 = getResponseCookie(msg2Initial, currentHtmlParameter.getName());

                int redirectsFollowed2 = 0;
                while (HttpStatusCode.isRedirection(temp2.getResponseHeader().getStatusCode())) {

                    // clone the previous message
                    boolean secure2 = temp2.getRequestHeader().isSecure();
                    temp2 = temp2.cloneAll();

                    redirectsFollowed2++;
                    if (redirectsFollowed2 > 10) {
                        throw new Exception("Too many redirects were specified in the second message");
                    }

                    // create a new URI from the absolute location returned, and interpret it as
                    // escaped
                    // note that the standard says that the Location returned should be
                    // absolute, but it ain't always so...
                    URI newLocation = new URI(temp2.getResponseHeader().getHeader(HttpHeader.LOCATION), true);

                    // and follow the forward url
                    // need to clear the params (which would come from the initial POST,
                    // otherwise)
                    temp2.getRequestHeader().setGetParams(new TreeSet<HtmlParameter>());
                    temp2.setRequestBody("");
                    temp2.setResponseBody(""); // make sure no values accidentally carry from one iteration to
                    // the next

                    try {
                        temp2.getRequestHeader().setURI(newLocation);
                    } catch (Exception e) {
                        // the Location field contents may not be standards compliant. Lets
                        // generate a uri to use as a workaround where a relative path was
                        // given instead of an absolute one
                        URI newLocationWorkaround = new URI(temp2.getRequestHeader().getURI(),
                                temp2.getResponseHeader().getHeader(HttpHeader.LOCATION), true);

                        // try again, except this time, if it fails, don't try to handle it
                        if (this.debugEnabled)
                            log.debug("The Location [" + newLocation
                                    + "] specified in a redirect was not valid. Trying workaround url ["
                                    + newLocationWorkaround + "]");
                        temp2.getRequestHeader().setURI(newLocationWorkaround);
                    }
                    temp2.getRequestHeader().setSecure(secure2);
                    temp2.getRequestHeader().setMethod(HttpRequestHeader.GET);
                    temp2.getRequestHeader().setContentLength(0); // since we send a GET, the body will be 0 long
                    if (cookieBack2 != null) {
                        // if the previous request sent back a cookie, we need to set that
                        // cookie when following redirects, as a browser would
                        // also make sure to delete the previous value set for the cookie value
                        if (this.debugEnabled) {
                            log.debug("Deleting old cookie [" + cookieBack2Previous
                                    + "], and adding in cookie [" + cookieBack2 + "] for a redirect");
                        }
                        TreeSet<HtmlParameter> forwardCookieParams = temp2.getRequestHeader().getCookieParams();
                        forwardCookieParams.remove(cookieBack2Previous);
                        forwardCookieParams.add(cookieBack2);
                        temp2.getRequestHeader().setCookieParams(forwardCookieParams);
                    }

                    sendAndReceive(temp2, false, false); // do NOT automatically redirect.. handle redirects here

                    // handle any cookies set from following redirects that override the cookie
                    // set in the redirect itself (if any)
                    // note that this will handle the case where a latter cookie unsets one set
                    // earlier.
                    HtmlParameter cookieBack2Temp = getResponseCookie(temp2, currentHtmlParameter.getName());
                    if (cookieBack2Temp != null) {
                        cookieBack2Previous = cookieBack2;
                        cookieBack2 = cookieBack2Temp;
                    }

                    // reset the "final" version of message2 to use the final response in the
                    // chain
                    msg2Final = temp2;
                }
                if (this.debugEnabled)
                    log.debug("Done following redirects");

                // final result was non-200, no point in continuing. Bale out.
                if (msg2Final.getResponseHeader().getStatusCode() != HttpStatusCode.OK) {
                    if (this.debugEnabled)
                        log.debug(
                                "Got a non-200 response code [" + msg2Final.getResponseHeader().getStatusCode()
                                        + "] when sending [" + msg2Initial.getRequestHeader().getURI()
                                        + "] with a borrowed cookie (or by following a redirect) for param ["
                                        + currentHtmlParameter.getName() + "]");
                    continue; // to next parameter
                }

                // and what we've been waiting for.. do we get a *different* cookie being set in
                // the response of message 2??
                // or do we get a new cookie back at all?
                // No cookie back => the borrowed cookie was accepted. Not ideal
                // Cookie back, but same as the one we sent in => the borrowed cookie was
                // accepted. Not ideal
                if ((cookieBack2 == null) || cookieBack2.getValue().equals(cookieBack1.getValue())) {
                    // no cookie back, when a borrowed cookie is in use.. suspicious!

                    // use the cookie extrainfo message, which is specific to the case of
                    // cookies
                    // pretty much everything else is generic to all types of Session Fixation
                    // vulnerabilities
                    String extraInfo = Constant.messages.getString(
                            "ascanbeta.sessionfixation.alert.cookie.extrainfo", currentHtmlParameter.getName(),
                            cookieBack1.getValue(), (cookieBack2 == null ? "NULL" : cookieBack2.getValue()));
                    String attack = Constant.messages.getString("ascanbeta.sessionfixation.alert.attack",
                            currentHtmlParameter.getType(), currentHtmlParameter.getName());

                    if (loginUrl) {
                        extraInfo += ("\n" + Constant.messages
                                .getString("ascanbeta.sessionfixation.alert.cookie.extrainfo.loginpage"));
                    }

                    bingo(Alert.RISK_INFO, Alert.CONFIDENCE_MEDIUM,
                            msg2Initial.getRequestHeader().getURI().getURI(), currentHtmlParameter.getName(),
                            attack, extraInfo, msg2Initial);
                    logSessionFixation(msg2Initial, currentHtmlParameter.getType().toString(),
                            currentHtmlParameter.getName());
                }

                continue; // jump to the next iteration of the loop (ie, the next parameter)
            } // end of the cookie code.

            // start of the url parameter code
            // note that this actually caters for
            // - actual URL parameters
            // - pseudo URL parameters, where the sessionid was in the path portion of the URL,
            // in conjunction with URL re-writing
            if (currentHtmlParameter.getType().equals(HtmlParameter.Type.url)) {
                boolean isPseudoUrlParameter = false; // is this "url parameter" actually a url parameter, or was it
                // path of the path (+url re-writing)?
                String possibleSessionIdIssuedForUrlParam = null;
                // remove the named url parameter from the request..
                TreeSet<HtmlParameter> urlRequestParams = msg1Initial.getUrlParams(); // get parameters?
                if (!urlRequestParams.remove(currentHtmlParameter)) {
                    isPseudoUrlParameter = true;
                    // was not removed because it was a pseudo Url parameter, not a real url
                    // parameter.. (so it would not be in the url params)
                    // in this case, we will need to "rewrite" (ie hack) the URL path to remove
                    // the pseudo url parameter portion
                    // ie, we need to remove the ";jsessionid=<sessionid>" bit from the path
                    // (assuming the current field is named 'jsessionid')
                    // and replace it with ";jsessionid=" (ie, we nullify the possible "session"
                    // parameter in the hope that a new session will be issued)
                    // then we continue as usual to see if the URL is vulnerable to a Session
                    // Fixation issue
                    // Side note: quote the string to search for, and the replacement, so that
                    // regex special characters are treated as literals
                    String hackedUrl = requestUrl.replaceAll(
                            Pattern.quote(";" + currentHtmlParameter.getName() + "="
                                    + currentHtmlParameter.getValue()),
                            Matcher.quoteReplacement(";" + currentHtmlParameter.getName() + "="));
                    if (this.debugEnabled)
                        log.debug("Removing the pseudo URL parameter from [" + requestUrl + "]: [" + hackedUrl
                                + "]");
                    // Note: the URL is not escaped. Handle it.
                    msg1Initial.getRequestHeader().setURI(new URI(hackedUrl, false));
                }
                msg1Initial.setGetParams(urlRequestParams); // url parameters

                // send the message, minus the value for the current parameter, and see how it
                // comes back.
                // Note: automatically follow redirects.. no need to look at any intermediate
                // responses.
                // this was only necessary for cookie-based session implementations
                sendAndReceive(msg1Initial);

                // if non-200 on the response for message 1, no point in continuing. Bale out.
                if (msg1Initial.getResponseHeader().getStatusCode() != HttpStatusCode.OK) {
                    if (this.debugEnabled)
                        log.debug("Got a non-200 response code ["
                                + msg1Initial.getResponseHeader().getStatusCode() + "] when sending ["
                                + msg1Initial.getRequestHeader().getURI() + "] with param ["
                                + currentHtmlParameter.getName()
                                + "] = NULL (possibly somewhere in the redirects)");
                    continue;
                }

                // now parse the HTML response for urls that contain the same parameter name,
                // and look at the values for that parameter
                // if no values are found for the parameter, then
                // 1) we are messing with the wrong field, or
                // 2) the app doesn't do sessions
                // either way, there is not much point in continuing to look at this field..

                // parse out links in HTML (assume for a moment that all the URLs are in links)
                // this gives us a map of parameter value for the current parameter, to the
                // number of times it was encountered in links in the HTML
                SortedMap<String, Integer> parametersInHTMLURls = getParameterValueCountInHtml(
                        msg1Initial.getResponseBody().toString(), currentHtmlParameter.getName(),
                        isPseudoUrlParameter);
                if (this.debugEnabled)
                    log.debug("The count of the various values of the [" + currentHtmlParameter.getName()
                            + "] parameters in urls in the result of retrieving the url with a null value for parameter ["
                            + currentHtmlParameter.getName() + "]: " + parametersInHTMLURls);

                if (parametersInHTMLURls.isEmpty()) {
                    // setting the param to NULL did not cause any new values to be generated
                    // for it in the output..
                    // so either..
                    // it is not a session field, or
                    // it is a session field, but a session is only issued on authentication,
                    // and this is not an authentication url
                    // the app doesn't do sessions (etc)
                    // either way, the parameter/url combo is not vulnerable, so continue with
                    // the next parameter
                    if (this.debugEnabled)
                        log.debug("The URL parameter [" + currentHtmlParameter.getName()
                                + "] was NOT set in any links in the response, when "
                                + (isPseudoUrlParameter ? "pseudo/URL rewritten" : "") + " URL param ["
                                + currentHtmlParameter.getName()
                                + "] was set to NULL in the request, so it is likely not a session id field");
                    continue; // to the next parameter
                } else if (parametersInHTMLURls.size() == 1) {
                    // the parameter was set to just one value in the output
                    // so it's quite possible it is the session id field that we have been
                    // looking for
                    // caveat 1: check it is longer than 3 chars long, to remove false
                    // positives..
                    // we assume here that a real session id will always be greater than 3
                    // characters long
                    // caveat 2: the value we got back for the param must be different from the
                    // value we
                    // over-wrote with NULL (empty) in the first place, otherwise it is very
                    // unlikely to
                    // be a session id field
                    possibleSessionIdIssuedForUrlParam = parametersInHTMLURls.firstKey();
                    // did we get back the same value we just nulled out in the original
                    // request?
                    // if so, use this to eliminate false positives, and to optimise.
                    if (possibleSessionIdIssuedForUrlParam.equals(currentHtmlParameter.getValue())) {
                        if (this.debugEnabled)
                            log.debug((isPseudoUrlParameter ? "pseudo/URL rewritten" : "") + " URL param ["
                                    + currentHtmlParameter.getName()
                                    + "], when set to NULL, causes 1 distinct values to be set for it in URLs in the output, but the possible session id value ["
                                    + possibleSessionIdIssuedForUrlParam
                                    + "] is the same as the value we over-wrote with NULL. 'Sorry, kid. You got the gift, but it looks like you're waiting for something'");
                        continue; // to the next parameter
                    }
                    if (possibleSessionIdIssuedForUrlParam.length() > 3) {
                        // raise an alert here on an exposed session id, even if it is not
                        // subject to a session fixation vulnerability
                        // log.info("The URL parameter ["+ currentHtmlParameter.getName() + "]
                        // was set ["+
                        // parametersInHTMLURls.get(possibleSessionIdIssuedForUrlParam)+ "]
                        // times to ["+ possibleSessionIdIssuedForUrlParam + "] in links in the
                        // response, when "+ (isPseudoUrlParameter?"pseudo/URL rewritten":"")+ "
                        // URL param ["+ currentHtmlParameter.getName() + "] was set to NULL in
                        // the request. This likely indicates it is a session id field.");

                        // pass the original param value here, not the new value, since we're
                        // displaying the session id exposed in the original message
                        String extraInfo = Constant.messages.getString(
                                "ascanbeta.sessionidexposedinurl.alert.extrainfo",
                                currentHtmlParameter.getType(), currentHtmlParameter.getName(),
                                currentHtmlParameter.getValue());
                        String attack = Constant.messages
                                .getString("ascanbeta.sessionidexposedinurl.alert.attack",
                                        (isPseudoUrlParameter ? "pseudo/URL rewritten " : "")
                                                + currentHtmlParameter.getType(),
                                        currentHtmlParameter.getName());
                        String vulnname = Constant.messages.getString("ascanbeta.sessionidexposedinurl.name");
                        String vulndesc = Constant.messages.getString("ascanbeta.sessionidexposedinurl.desc");
                        String vulnsoln = Constant.messages.getString("ascanbeta.sessionidexposedinurl.soln");

                        if (loginUrl) {
                            extraInfo += ("\n" + Constant.messages
                                    .getString("ascanbeta.sessionidexposedinurl.alert.extrainfo.loginpage"));
                        }

                        // call bingo with some extra info, indicating that the alert is
                        // not specific to Session Fixation, but has its own title and
                        // description (etc)
                        // the alert here is "Session id exposed in url", or words to that
                        // effect.
                        bingo(Alert.RISK_MEDIUM, Alert.CONFIDENCE_MEDIUM, vulnname, vulndesc,
                                getBaseMsg().getRequestHeader().getURI().getURI(),
                                currentHtmlParameter.getName(), attack, extraInfo, vulnsoln, getBaseMsg());

                        if (log.isDebugEnabled()) {
                            String logMessage = MessageFormat.format(
                                    "An exposed session identifier has been found at "
                                            + "[{0}] URL [{1}] on {2} field: [{3}]",
                                    getBaseMsg().getRequestHeader().getMethod(),
                                    getBaseMsg().getRequestHeader().getURI().getURI(),
                                    (isPseudoUrlParameter ? "pseudo " : "") + currentHtmlParameter.getType(),
                                    currentHtmlParameter.getName());
                            log.debug(logMessage);
                        }
                        // Note: do NOT continue to the next field at this point..
                        // since we still need to check for Session Fixation.
                    } else {
                        if (this.debugEnabled)
                            log.debug((isPseudoUrlParameter ? "pseudo/URL rewritten" : "") + " URL param ["
                                    + currentHtmlParameter.getName()
                                    + "], when set to NULL, causes 1 distinct values to be set for it in URLs in the output, but the possible session id value ["
                                    + possibleSessionIdIssuedForUrlParam
                                    + "] is too short to be a real session id.");
                        continue; // to the next parameter
                    }
                } else {
                    // strange scenario: setting the param to null causes multiple different
                    // values to be set for it in the output
                    // it could still be a session parameter, but we assume it is *not* a
                    // session id field
                    // log it, but assume it is not a session id
                    if (this.debugEnabled)
                        log.debug((isPseudoUrlParameter ? "pseudo/URL rewritten" : "") + " URL param ["
                                + currentHtmlParameter.getName() + "], when set to NULL, causes ["
                                + parametersInHTMLURls.size()
                                + "] distinct values to be set for it in URLs in the output. Assuming it is NOT a session id as a consequence. This could be a false negative");
                    continue; // to the next parameter
                }

                ////////////////////////////////////////////////////////////////////////////////////////////
                /// Message 2 - processing starts here
                ////////////////////////////////////////////////////////////////////////////////////////////
                // we now have a plausible session id field to play with, so set it to a
                // borrowed value.
                // ie: lets re-send the request, but add in the new (valid) session value that
                // was just issued.
                // the aim is then to see if it accepts the session without re-issuing the
                // session id (BAD, in some circumstances),
                // or if it issues a new session value (GOOD, in most circumstances)

                // and set the (modified) session for the second message
                // use a copy of msg2Initial, since it has already had the correct session
                // removed in the request..
                // do NOT use msg2Initial itself, as this will cause both requests in the GUI to
                // show the modified data..
                // finally send the second message, and see how it comes back.
                HttpMessage msg2Initial = msg1Initial.cloneRequest();

                // set the parameter to the new session id value (in different manners,
                // depending on whether it is a real url param, or a pseudo url param)
                if (isPseudoUrlParameter) {
                    // we need to "rewrite" (hack) the URL path to remove the pseudo url
                    // parameter portion
                    // id, we need to remove the ";jsessionid=<sessionid>" bit from the path
                    // and replace it with ";jsessionid=" (ie, we nullify the possible "session"
                    // parameter in the hope that a new session will be issued)
                    // then we continue as usual to see if the URL is vulnerable to a Session
                    // Fixation issue
                    // Side note: quote the string to search for, and the replacement, so that
                    // regex special characters are treated as literals
                    String hackedUrl = requestUrl.replaceAll(
                            Pattern.quote(";" + currentHtmlParameter.getName() + "="
                                    + currentHtmlParameter.getValue()),
                            Matcher.quoteReplacement(";" + currentHtmlParameter.getName() + "="
                                    + possibleSessionIdIssuedForUrlParam));
                    if (this.debugEnabled)
                        log.debug("Changing the pseudo URL parameter from [" + requestUrl + "]: [" + hackedUrl
                                + "]");
                    // Note: the URL is not escaped
                    msg2Initial.getRequestHeader().setURI(new URI(hackedUrl, false));
                    msg2Initial.setGetParams(msg1Initial.getUrlParams()); // restore the GET params
                } else {
                    // do it via the normal url parameters
                    TreeSet<HtmlParameter> urlRequestParams2 = msg2Initial.getUrlParams();
                    urlRequestParams2.add(new HtmlParameter(Type.url, currentHtmlParameter.getName(),
                            possibleSessionIdIssuedForUrlParam));
                    msg2Initial.setGetParams(urlRequestParams2); // restore the GET params
                }

                // resend a copy of the initial message, but with the new valid session
                // parameter added in, to see if it is accepted
                // automatically follow redirects, which are irrelevant for the purposes of
                // testing URL parameters
                sendAndReceive(msg2Initial);

                // final result was non-200, no point in continuing. Bale out.
                if (msg2Initial.getResponseHeader().getStatusCode() != HttpStatusCode.OK) {
                    if (this.debugEnabled)
                        log.debug("Got a non-200 response code ["
                                + msg2Initial.getResponseHeader().getStatusCode() + "] when sending ["
                                + msg2Initial.getRequestHeader().getURI()
                                + "] with a borrowed session (or by following a redirect) for param ["
                                + currentHtmlParameter.getName() + "]");
                    continue; // next field!
                }

                // do the analysis on the parameters in link urls in the HTML output again to
                // see if the session id was regenerated
                SortedMap<String, Integer> parametersInHTMLURls2 = getParameterValueCountInHtml(
                        msg2Initial.getResponseBody().toString(), currentHtmlParameter.getName(),
                        isPseudoUrlParameter);
                if (this.debugEnabled)
                    log.debug("The count of the various values of the [" + currentHtmlParameter.getName()
                            + "] parameters in urls in the result of retrieving the url with a borrowed session value for parameter ["
                            + currentHtmlParameter.getName() + "]: " + parametersInHTMLURls2);

                if (parametersInHTMLURls2.size() != 1) {
                    // either no values, or multiple values, but not 1 value.  For a session
                    // that was regenerated, we would have expected to see
                    // just 1 new value
                    if (this.debugEnabled)
                        log.debug("The HTML has spoken. [" + currentHtmlParameter.getName()
                                + "] doesn't look like a session id field, because there are "
                                + parametersInHTMLURls2.size()
                                + " distinct values for this parameter in urls in the HTML output");
                    continue;
                }
                // there is but one value for this param in links in the HTML output. But is it
                // vulnerable to Session Fixation? Ie, is it the same parameter?
                String possibleSessionIdIssuedForUrlParam2 = parametersInHTMLURls2.firstKey();
                if (possibleSessionIdIssuedForUrlParam2.equals(possibleSessionIdIssuedForUrlParam)) {
                    // same sessionid used in the output.. so it is likely that we have a
                    // SessionFixation issue..

                    // use the url param extrainfo message, which is specific to the case of url
                    // parameters and url re-writing Session Fixation issue
                    // pretty much everything else is generic to all types of Session Fixation
                    // vulnerabilities
                    String extraInfo = Constant.messages.getString(
                            "ascanbeta.sessionfixation.alert.url.extrainfo", currentHtmlParameter.getName(),
                            possibleSessionIdIssuedForUrlParam, possibleSessionIdIssuedForUrlParam2);
                    String attack = Constant.messages.getString("ascanbeta.sessionfixation.alert.attack",
                            (isPseudoUrlParameter ? "pseudo/URL rewritten " : "")
                                    + currentHtmlParameter.getType(),
                            currentHtmlParameter.getName());

                    int risk = Alert.RISK_LOW;
                    if (loginUrl) {
                        extraInfo += ("\n" + Constant.messages
                                .getString("ascanbeta.sessionfixation.alert.url.extrainfo.loginpage"));
                        // login page, so higher risk
                        risk = Alert.RISK_MEDIUM;
                    } else {
                        // not a login page.. lower risk
                        risk = Alert.RISK_LOW;
                    }

                    bingo(risk, Alert.CONFIDENCE_MEDIUM, getBaseMsg().getRequestHeader().getURI().getURI(),
                            currentHtmlParameter.getName(), attack, extraInfo, getBaseMsg());
                    logSessionFixation(getBaseMsg(),
                            (isPseudoUrlParameter ? "pseudo " : "") + currentHtmlParameter.getType(),
                            currentHtmlParameter.getName());

                    continue; // jump to the next iteration of the loop (ie, the next parameter)

                } else {
                    // different sessionid used in the output.. so it is unlikely that we have a
                    // SessionFixation issue..
                    // more likely that the Session is being re-issued for every single request,
                    // or we have issues a login request, which
                    // normally causes a session to be reissued
                    if (this.debugEnabled)
                        log.debug("The " + (isPseudoUrlParameter ? "pseudo/URL rewritten" : "") + " parameter ["
                                + currentHtmlParameter.getName() + "] in url ["
                                + getBaseMsg().getRequestHeader().getMethod() + "] ["
                                + getBaseMsg().getRequestHeader().getURI()
                                + "] changes with requests, and so it likely not vulnerable to Session Fixation");
                }

                continue; // onto the next parameter
            } // end of the url parameter code.
        } // end of the for loop around the parameter list

    } catch (Exception e) {
        // Do not try to internationalise this.. we need an error message in any event..
        // if it's in English, it's still better than not having it at all.
        log.error("An error occurred checking a url for Session Fixation issues", e);
    }
}

From source file:org.zaproxy.zap.extension.ascanrulesBeta.SourceCodeDisclosureSVN.java

/**
 * finds the source code for the given file, using SVN metadata on the server (if this is
 * available)//w  w  w. j av a 2  s .  c  o m
 *
 * @param uri the URI of a file, whose source code we want to find
 * @return Did we find the source code?
 */
private boolean findSourceCodeSVN(HttpMessage originalMessage) throws Exception {

    AlertThreshold alertThreshold = getAlertThreshold();

    // SVN formats 1-10 (format 11 is not used) are supported by this logic.
    // TODO: The SQLite based (and centralised, except for pre-release formats which we don't
    // plan to support) ".svn/wc.db" style used from SVN format 12 through to 31
    // (and possibly later formats) is not yet supported here. It's a work in progress.
    // It is fully supported in the Spider, however.

    URI uri = originalMessage.getRequestHeader().getURI();
    String path = uri.getPath();
    if (path == null)
        path = "";
    // String filename = path.substring( path.lastIndexOf('/')+1, path.length() );
    String urlfilename = uri.getName();

    String fileExtension = null;
    if (urlfilename.contains(".")) {
        fileExtension = urlfilename.substring(urlfilename.lastIndexOf(".") + 1);
        fileExtension = fileExtension.toUpperCase();
    }

    // do not recurse into a Subversion folder... this would cause infinite recursion issues in
    // Attack Mode. (which goes depth first!)
    // in any event, it doesn't make sense to do this.
    if (path.contains("/.svn/") || path.endsWith("/.svn")) {
        if (log.isDebugEnabled())
            log.debug(
                    "Nope. It doesn't make any sense to look for a Subversion repo *within* a Subversion repo");
        return false;
    }

    // Look for SVN < 1.7 metadata (ie internal SVN format < 29) containing source code
    // These versions all store the pristine copies in the the same format (insofar as the logic
    // here is concerned, at least)
    try {
        String pathminusfilename = path.substring(0, path.lastIndexOf(urlfilename));

        HttpMessage svnsourcefileattackmsg = new HttpMessage(new URI(uri.getScheme(), uri.getAuthority(),
                pathminusfilename + ".svn/text-base/" + urlfilename + ".svn-base", null, null));
        svnsourcefileattackmsg.setCookieParams(this.getBaseMsg().getCookieParams());
        // svnsourcefileattackmsg.setRequestHeader(this.getBaseMsg().getRequestHeader());
        sendAndReceive(svnsourcefileattackmsg, false); // do not follow redirects

        int attackmsgResponseStatusCode = svnsourcefileattackmsg.getResponseHeader().getStatusCode();

        if (shouldStop(alertThreshold, attackmsgResponseStatusCode)) {
            return false;
        }

        if (originalMessage.getResponseBody().toString()
                .equals(svnsourcefileattackmsg.getResponseBody().toString())) {
            if (log.isDebugEnabled()) {
                log.debug("Response bodies are exactly the same, so can not be the source code");
            }
        } else if (!UNWANTED_RESPONSE_CODES.contains(attackmsgResponseStatusCode)) { // If the response is wanted (not on the
            // unwanted list)

            String attackFilename = uri.getScheme() + "://" + uri.getAuthority() + pathminusfilename
                    + ".svn/text-base/" + urlfilename + ".svn-base";
            if (log.isDebugEnabled()) {
                log.debug("The contents for request '" + attackFilename
                        + "' do not return 404 or 3**, so we possibly have the source code using SVN < 1.7");
            }
            // check the contents of the output to some degree, if we have a file extension.
            // if not, just try it (could be a false positive, but hey)
            String evidence = findEvidenceForExtension(svnsourcefileattackmsg.getResponseBody().getBytes(),
                    fileExtension);
            if (evidence != null) {
                // if we get to here, is is very likely that we have source file inclusion
                // attack. alert it.
                bingo(Alert.RISK_MEDIUM, getConfidence(attackmsgResponseStatusCode), getName(),
                        getDescription(), getBaseMsg().getRequestHeader().getURI().getURI(), null,
                        attackFilename, getExtraInfo(urlfilename, attackFilename), getSolution(), evidence,
                        svnsourcefileattackmsg);
                // if we found one, do not even try the "super" method, which tries each of the
                // parameters,
                // since this is slow, and we already found an instance
                return true;
            } else {
                if (log.isDebugEnabled())
                    log.debug("The HTML output does not look like source code of type " + fileExtension);
            }
        } else {
            if (log.isDebugEnabled()) {
                log.debug("Got an unsuitable response code "
                        + svnsourcefileattackmsg.getResponseHeader().getStatusCode()
                        + ", so it looks like SVN < 1.7 source code file was not found");
            }
        }
    } catch (Exception e) {
        log.warn("Got an error trying to find source code using the format used by SVN < 1.7", e);
    }

    // try again, by assuming that SVN 1.7 or later is used.  These versions use a different
    // internal format, and store the source code in different locations compared to SVN < 1.7.
    // Note that it's not as simple this time around, because the name of the file that contains
    // the source code is based on a SHA1 hash of the file contents, rather than being based on
    // the source file name.
    // In other words, we can't guess the name of the internal SVN file, and we can't just
    // calculate it from the file name.  The good news is that the file name that we need is
    // contained in the centralised
    // "wc.db" SVN metadata file that is associated with SVN >= 1.7.
    // "wc.db" lives in ".svn/wc.db".  This file contains data for all of the files in the repo
    // (ie, it contains data for the root directory and all subdirectories of the repo).
    // The only real issue we have is the question of where within the web folder structure (or
    // mappings) that the "wc.db" file resides.
    // For instance, the ".svn" directory might have been deployed into
    // "http://www.example.com/.svn",
    // or it *might* have been deployed into "http://www.example.com/dir1/dir2/.svn".
    // If we're looking for the SVN >= 1.7 source for
    // "http://www.example.com/dir1/dir2/login.php", for instance, we need to check for the
    // "wc.db" file in the following locations:
    //   "http://www.example.com/dir1/dir2/.svn/wc.db"
    //   "http://www.example.com/dir1/.svn/wc.db"
    //   "http://www.example.com/.svn/wc.db"
    // ie, we need to traverse all the way back to the web root looking for it.
    // Once we've found the "wc.db" file, we use it as an index, looking up the name of the file
    // for which we're trying to get the source code.
    // That gives us the internal SVN file name (containing the SHA1 value), which we can (in
    // theory) then retrieve. If it works, we will retrieve the source code for the file!
    try {
        String pathminusfilename = path.substring(0, path.lastIndexOf(urlfilename));
        while (!pathminusfilename.equals("/")) {
            HttpMessage svnWCDBAttackMsg = new HttpMessage(
                    new URI(uri.getScheme(), uri.getAuthority(), pathminusfilename + ".svn/wc.db", null, null));
            svnWCDBAttackMsg.setCookieParams(this.getBaseMsg().getCookieParams());
            // svnsourcefileattackmsg.setRequestHeader(this.getBaseMsg().getRequestHeader());
            sendAndReceive(svnWCDBAttackMsg, false); // do not follow redirects

            int svnWCDBAttackMsgStatusCode = svnWCDBAttackMsg.getResponseHeader().getStatusCode();

            if (shouldStop(alertThreshold, svnWCDBAttackMsgStatusCode)) {
                return false;
            }

            if (originalMessage.getResponseBody().toString()
                    .equals(svnWCDBAttackMsg.getResponseBody().toString())) {
                if (log.isDebugEnabled()) {
                    log.debug("Response bodies are exactly the same, so can not be the source code");
                }
            } else if (!UNWANTED_RESPONSE_CODES.contains(svnWCDBAttackMsgStatusCode)) { // If the response is wanted (not on the
                // unwanted list)
                // calculate the path used to access the wc.db, as well as the matching relpath
                // to query the wc.db
                // since the relpath is calculated from the original message URL path, after
                // removing the base used in the wc.db url path
                String wcdbAttackFilename = uri.getScheme() + "://" + uri.getAuthority() + pathminusfilename
                        + ".svn/wc.db";
                String relPath = path.substring(path.indexOf(pathminusfilename) + pathminusfilename.length());
                if (log.isDebugEnabled()) {
                    log.debug("The contents for request '" + wcdbAttackFilename
                            + "' do not return 404 or 3**, so we found the '.svn/wc.db' file for SVN >= 1.7..");
                    log.debug("The relpath to query SQLite is '" + relPath + "'");
                }

                // so we found the wc.db file... handle it.
                // get the binary data, and put it in a temp file we can use with the SQLite
                // JDBC driver
                // Note: File is not AutoClosable, so cannot use a "try with resources" to
                // manage it
                File tempSqliteFile;
                tempSqliteFile = File.createTempFile("sqlite_svn_wc_db", null);
                tempSqliteFile.deleteOnExit();
                OutputStream fos = new FileOutputStream(tempSqliteFile);
                fos.write(svnWCDBAttackMsg.getResponseBody().getBytes());
                fos.close();

                if (log.isDebugEnabled()) {
                    org.sqlite.JDBC jdbcDriver = new org.sqlite.JDBC();
                    log.debug("Created a temporary SQLite database file '" + tempSqliteFile + "'");
                    log.debug("SQLite JDBC Driver is version " + jdbcDriver.getMajorVersion() + "."
                            + jdbcDriver.getMinorVersion());
                }

                // now load the temporary SQLite file using JDBC, and query the file entries
                // within.
                Class.forName("org.sqlite.JDBC");
                String sqliteConnectionUrl = "jdbc:sqlite:" + tempSqliteFile.getAbsolutePath();

                try (Connection conn = DriverManager.getConnection(sqliteConnectionUrl)) {
                    if (conn != null) {
                        Statement pragmaStatement = null;
                        PreparedStatement nodeStatement = null;
                        ResultSet rsSVNWCFormat = null;
                        ResultSet rsNode = null;
                        ResultSet rsRepo = null;
                        try {
                            pragmaStatement = conn.createStatement();
                            rsSVNWCFormat = pragmaStatement.executeQuery("pragma USER_VERSION");

                            // get the precise internal version of SVN in use
                            // this will inform how the scanner should proceed in an efficient
                            // manner.
                            int svnFormat = 0;
                            while (rsSVNWCFormat.next()) {
                                if (log.isDebugEnabled())
                                    log.debug("Got a row from 'pragma USER_VERSION'");
                                svnFormat = rsSVNWCFormat.getInt(1);
                                break;
                            }
                            if (svnFormat < 29) {
                                throw new Exception(
                                        "The SVN Working Copy Format of the SQLite database should be >= 29. We found "
                                                + svnFormat);
                            }
                            if (svnFormat > 31) {
                                throw new Exception("SVN Working Copy Format " + svnFormat
                                        + " is not supported at this time.  We support up to and including format 31 (~ SVN 1.8.5)");
                            }
                            if (log.isDebugEnabled()) {
                                log.debug("Internal SVN Working Copy Format for " + tempSqliteFile + " is "
                                        + svnFormat);
                                log.debug(
                                        "Refer to http://svn.apache.org/repos/asf/subversion/trunk/subversion/libsvn_wc/wc.h for more details!");
                            }

                            // allow future changes to be easily handled
                            switch (svnFormat) {
                            case 29:
                            case 30:
                            case 31:
                                nodeStatement = conn.prepareStatement(
                                        "select kind,local_relpath,'pristine/'||substr(checksum,7,2) || \"/\" || substr(checksum,7)|| \".svn-base\" from nodes where local_relpath = ? order by wc_id");
                                break;
                            }
                            // now set the parameter, and execute the query
                            nodeStatement.setString(1, relPath);
                            rsNode = nodeStatement.executeQuery();

                            // and get the internal name of the SVN file stored in the SVN repo
                            while (rsNode.next()) {
                                if (log.isDebugEnabled())
                                    log.debug("Got a Node from the SVN wc.db file (format " + svnFormat + ")");
                                // String kind = rsNode.getString(1);
                                // String filename = rsNode.getString(2);
                                String svnFilename = rsNode.getString(3);

                                if (svnFilename != null && svnFilename.length() > 0) {
                                    log.debug("Found " + relPath + " in the wc.db: " + svnFilename);

                                    // try get the source, using the internal SVN file path,
                                    // building the path back up correctly
                                    HttpMessage svnSourceFileAttackMsg = new HttpMessage(
                                            new URI(uri.getScheme(), uri.getAuthority(),
                                                    pathminusfilename + ".svn/" + svnFilename, null, null));
                                    svnSourceFileAttackMsg.setCookieParams(this.getBaseMsg().getCookieParams());
                                    // svnsourcefileattackmsg.setRequestHeader(this.getBaseMsg().getRequestHeader());
                                    sendAndReceive(svnSourceFileAttackMsg, false); // do not follow redirects

                                    int svnSourceFileAttackMsgStatusCode = svnSourceFileAttackMsg
                                            .getResponseHeader().getStatusCode();

                                    if (shouldStop(alertThreshold, svnSourceFileAttackMsgStatusCode)) {
                                        return false;
                                    }

                                    if (!UNWANTED_RESPONSE_CODES.contains(svnSourceFileAttackMsgStatusCode)) { // If the
                                        // response is
                                        // wanted (not
                                        // on the
                                        // unwanted
                                        // list)

                                        String attackFilename = uri.getScheme() + "://" + uri.getAuthority()
                                                + pathminusfilename + ".svn/" + svnFilename;
                                        if (log.isDebugEnabled()) {
                                            log.debug("The contents for request '" + attackFilename
                                                    + "' do not return 404 or 3**, so we possibly have the source code using SVN >= 1.7");
                                        }
                                        // check the contents of the output to some degree, if
                                        // we have a file extension.
                                        // if not, just try it (could be a false positive, but
                                        // hey)
                                        String evidence = findEvidenceForExtension(
                                                svnSourceFileAttackMsg.getResponseBody().getBytes(),
                                                fileExtension);
                                        if (evidence != null) {
                                            // if we get to here, is is very likely that we have
                                            // source file inclusion attack. alert it.
                                            bingo(Alert.RISK_MEDIUM,
                                                    getConfidence(svnSourceFileAttackMsgStatusCode), getName(),
                                                    getDescription(),
                                                    getBaseMsg().getRequestHeader().getURI().getURI(), null,
                                                    attackFilename, getExtraInfo(urlfilename, attackFilename),
                                                    getSolution(), evidence, svnSourceFileAttackMsg);
                                            // do not return.. need to tidy up first
                                        } else {
                                            if (log.isDebugEnabled())
                                                log.debug(
                                                        "The HTML output does not look like source code of type "
                                                                + fileExtension);
                                        }
                                    } else {
                                        if (log.isDebugEnabled()) {
                                            log.debug("Got an unsuitable response code "
                                                    + svnSourceFileAttackMsg.getResponseHeader().getStatusCode()
                                                    + ", so it looks like SVN >= 1.7 source code file was not found");
                                        }
                                    }

                                    break; // out of the loop. even though there should be just
                                    // 1 entry
                                }
                            }
                        } catch (SQLException sqlEx) {
                            StringBuilder errorSb = new StringBuilder(300);
                            errorSb.append("Error executing SQL on temporary SVN SQLite database '");
                            errorSb.append(sqliteConnectionUrl);
                            errorSb.append("': ");
                            errorSb.append(sqlEx);
                            errorSb.append("\nThe saved response likely wasn't a SQLite db.");
                            log.debug(errorSb);
                        } catch (Exception e) {
                            log.debug("An error has occurred, related to the temporary SVN SQLite DB. " + e);
                        } finally {
                            // the JDBC driver in use does not play well with "try with
                            // resource" construct. I tried!
                            if (rsRepo != null)
                                rsRepo.close();
                            if (rsNode != null)
                                rsNode.close();
                            if (rsSVNWCFormat != null)
                                rsSVNWCFormat.close();
                            if (pragmaStatement != null)
                                pragmaStatement.close();
                            if (nodeStatement != null)
                                nodeStatement.close();
                        }
                    } else
                        throw new SQLException("Could not open a JDBC connection to SQLite file "
                                + tempSqliteFile.getAbsolutePath());
                } catch (Exception e) {
                    // the connection will have been closed already, since we're used a try with
                    // resources
                    log.error("Error parsing temporary SVN SQLite database " + sqliteConnectionUrl);
                } finally {
                    // delete the temp file.
                    // this will be deleted when the VM is shut down anyway, but better to be
                    // safe than to run out of disk space.
                    tempSqliteFile.delete();
                }

                break; // out of the while loop
            } // non 404, 300, etc for "wc.db", for SVN >= 1.7
              // set up the parent directory name
            pathminusfilename = pathminusfilename.substring(0,
                    pathminusfilename.substring(0, pathminusfilename.length() - 1).lastIndexOf("/") + 1);
        }

    } catch (Exception e) {
        log.warn("Got an error trying to find source code using the format used by SVN >= 1.7", e);
    }
    return false;
}

From source file:org.zaproxy.zap.extension.ascanrulesBeta.UsernameEnumeration.java

/**
 * looks for username enumeration in the login page, by changing the username field to be a
 * valid / invalid user, and looking for differences in the response
 *///w w w  .j a v a2  s  .c om
@Override
public void scan() {

    // the technique to determine if usernames can be enumerated is as follows, using a variant
    // of the Freiling+Schinzel method,
    // adapted to the case where we do not know which is the username field
    //
    // 1) Request the original URL n times. (The original URL is assumed to have a valid
    // username, if not a valid password). Store the results in A[].
    // 2) Compute the longest common subsequence (LCS) of A[] into LCS_A
    // 3) for each parameter in the original URL (ie, for URL params, form params, and cookie
    // params)
    //   4) Change the current parameter (which we assume is the username parameter) to an invalid
    // username (randomly), and request the URL n times. Store the results in B[].
    //   5) Compute the longest common subsequence (LCS) of B[] into LCS_B
    //   6) If LCS_A <> LCS_B, then there is a Username Enumeration issue on the current parameter

    try {
        boolean loginUrl = false;

        // Are we dealing with a login url in any of the contexts of which this uri is part
        URI requestUri = getBaseMsg().getRequestHeader().getURI();

        // using the session, get the list of contexts for the url
        List<Context> contextList = extAuth.getModel().getSession().getContextsForUrl(requestUri.getURI());

        // now loop, and see if the url is a login url in each of the contexts in turn...
        for (Context context : contextList) {
            URI loginUri = extAuth.getLoginRequestURIForContext(context);
            if (loginUri != null) {
                if (requestUri.getScheme().equals(loginUri.getScheme())
                        && requestUri.getHost().equals(loginUri.getHost())
                        && requestUri.getPort() == loginUri.getPort()
                        && requestUri.getPath().equals(loginUri.getPath())) {
                    // we got this far.. only the method (GET/POST), user details, query params,
                    // fragment, and POST params
                    // are possibly different from the login page.
                    loginUrl = true;
                    log.info(requestUri.toString()
                            + " falls within a context, and is the defined Login URL. Scanning for possible Username Enumeration vulnerability.");
                    break; // Stop checking
                }
            }
        }

        // the Username Enumeration scanner will only run for logon pages
        if (loginUrl == false) {
            if (this.debugEnabled) {
                log.debug(requestUri.toString() + " is not a defined Login URL.");
            }
            return; // No need to continue for this URL
        }

        // find all params set in the request (GET/POST/Cookie)
        TreeSet<HtmlParameter> htmlParams = new TreeSet<>();
        htmlParams.addAll(getBaseMsg().getRequestHeader().getCookieParams()); // request cookies only. no response cookies
        htmlParams.addAll(getBaseMsg().getFormParams()); // add in the POST params
        htmlParams.addAll(getBaseMsg().getUrlParams()); // add in the GET params

        int numberOfRequests = 0;
        if (this.getAttackStrength() == AttackStrength.INSANE) {
            numberOfRequests = 50;
        } else if (this.getAttackStrength() == AttackStrength.HIGH) {
            numberOfRequests = 15;
        } else if (this.getAttackStrength() == AttackStrength.MEDIUM) {
            numberOfRequests = 5;
        } else if (this.getAttackStrength() == AttackStrength.LOW) {
            numberOfRequests = 3;
        }

        // 1) Request the original URL n times. (The original URL is assumed to have a valid
        // username, if not a valid password). Store the results in A[].
        // make sure to manually handle all redirects, and cookies that may be set in response.
        // allocate enough space for the responses

        StringBuilder responseA = null;
        StringBuilder responseB = null;
        String longestCommonSubstringA = null;
        String longestCommonSubstringB = null;

        for (int i = 0; i < numberOfRequests; i++) {

            // initialise the storage for this iteration
            // baseResponses[i]= new StringBuilder(250);
            responseA = new StringBuilder(250);

            HttpMessage msgCpy = getNewMsg(); // clone the request, but not the response

            sendAndReceive(msgCpy, false, false); // request the URL, but do not automatically follow redirects.

            // get all cookies set in the response
            TreeSet<HtmlParameter> cookies = msgCpy.getResponseHeader().getCookieParams();

            int redirectCount = 0;
            while (HttpStatusCode.isRedirection(msgCpy.getResponseHeader().getStatusCode())) {
                redirectCount++;

                if (this.debugEnabled)
                    log.debug("Following redirect " + redirectCount + " for message " + i + " of "
                            + numberOfRequests + " iterations of the original query");

                // append the response to the responses so far for this particular instance
                // this will give us a complete picture of the full set of actual traffic
                // associated with following redirects for the request
                responseA.append(msgCpy.getResponseHeader().getHeadersAsString());
                responseA.append(msgCpy.getResponseBody().toString());

                // and manually follow the redirect
                // create a new message from scratch
                HttpMessage msgRedirect = new HttpMessage();

                // create a new URI from the absolute location returned, and interpret it as
                // escaped
                // note that the standard says that the Location returned should be absolute,
                // but it ain't always so...
                URI newLocation = new URI(msgCpy.getResponseHeader().getHeader(HttpHeader.LOCATION), true);
                try {
                    msgRedirect.getRequestHeader().setURI(newLocation);
                } catch (Exception e) {
                    // the Location field contents may not be standards compliant. Lets generate
                    // a uri to use as a workaround where a relative path was
                    // given instead of an absolute one
                    URI newLocationWorkaround = new URI(msgCpy.getRequestHeader().getURI(),
                            msgCpy.getResponseHeader().getHeader(HttpHeader.LOCATION), true);
                    // try again, except this time, if it fails, don't try to handle it
                    if (this.debugEnabled)
                        log.debug("The Location [" + newLocation
                                + "] specified in a redirect was not valid (not absolute?). Trying absolute workaround url ["
                                + newLocationWorkaround + "]");
                    msgRedirect.getRequestHeader().setURI(newLocationWorkaround);
                }
                msgRedirect.getRequestHeader().setMethod(HttpRequestHeader.GET); // it's always a GET for a redirect
                msgRedirect.getRequestHeader().setContentLength(0); // since we send a GET, the body will be 0 long
                if (cookies.size() > 0) {
                    // if a previous request sent back a cookie that has not since been
                    // invalidated, we need to set that cookie when following redirects, as a
                    // browser would
                    msgRedirect.getRequestHeader().setCookieParams(cookies);
                }

                if (this.debugEnabled)
                    log.debug("DEBUG: Following redirect to [" + newLocation + "]");
                sendAndReceive(msgRedirect, false, false); // do NOT redirect.. handle it here

                // handle scenario where a cookie is unset in a subsequent iteration, or where
                // the same cookie name is later re-assigned a different value
                // ie, in these cases, do not simply (and dumbly) accumulate cookie detritus.
                // first get all cookies set in the response
                TreeSet<HtmlParameter> cookiesTemp = msgRedirect.getResponseHeader().getCookieParams();
                for (Iterator<HtmlParameter> redirectSetsCookieIterator = cookiesTemp
                        .iterator(); redirectSetsCookieIterator.hasNext();) {
                    HtmlParameter cookieJustSet = redirectSetsCookieIterator.next();
                    // loop through each of the cookies we know about in cookies, to see if it
                    // matches by name.
                    // if so, delete that cookie, and add the one that was just set to cookies.
                    // if not, add the one that was just set to cookies.
                    for (Iterator<HtmlParameter> knownCookiesIterator = cookies.iterator(); knownCookiesIterator
                            .hasNext();) {
                        HtmlParameter knownCookie = knownCookiesIterator.next();
                        if (cookieJustSet.getName().equals(knownCookie.getName())) {
                            knownCookiesIterator.remove();
                            break; // out of the loop for known cookies, back to the next cookie
                            // set in the response
                        }
                    } // end of loop for cookies we already know about
                      // we can now safely add the cookie that was just set into cookies, knowing
                      // it does not clash with anything else in there.
                    cookies.add(cookieJustSet);
                } // end of for loop for cookies just set in the redirect

                msgCpy = msgRedirect; // store the last redirect message into the MsgCpy, as we
                // will be using it's output in a moment..
            } // end of loop to follow redirects

            // now that the redirections have all been handled.. was the request finally a
            // success or not?  Successful or Failed Logins would normally both return an OK
            // HTTP status
            if (!HttpStatusCode.isSuccess(msgCpy.getResponseHeader().getStatusCode())) {
                log.warn("The original URL [" + getBaseMsg().getRequestHeader().getURI()
                        + "] returned a non-OK HTTP status " + msgCpy.getResponseHeader().getStatusCode()
                        + " (after " + i + " of " + numberOfRequests
                        + " steps). Could be indicative of SQL Injection, or some other error. The URL is not stable enough to look at Username Enumeration");
                return; // we have not even got as far as looking at the parameters, so just
                // abort straight out of the method
            }

            if (this.debugEnabled)
                log.debug("Done following redirects!");

            // append the response to the responses so far for this particular instance
            // this will give us a complete picture of the full set of actual traffic associated
            // with following redirects for the request
            responseA.append(msgCpy.getResponseHeader().getHeadersAsString());
            responseA.append(msgCpy.getResponseBody().toString());

            // 2) Compute the longest common subsequence (LCS) of A[] into LCS_A
            // Note: in the Freiling and Schinzel method, this is calculated recursively. We
            // calculate it iteratively, but using an equivalent method

            // first time in, the LCS is simple: it's the first HTML result.. no diffing
            // required
            if (i == 0)
                longestCommonSubstringA = responseA.toString();
            // else get the LCS of the existing string, and the current result
            else
                longestCommonSubstringA = this.longestCommonSubsequence(longestCommonSubstringA,
                        responseA.toString());

            // optimisation step: if the LCS of A is 0 characters long already, then the URL
            // output is not stable, and we can abort now, and save some time
            if (longestCommonSubstringA.length() == 0) {
                // this might occur if the output returned for the URL changed mid-way. Perhaps
                // a CAPTCHA has fired, or a WAF has kicked in.  Let's abort now so.
                log.warn("The original URL [" + getBaseMsg().getRequestHeader().getURI()
                        + "] does not produce stable output (at " + i + 1 + " of " + numberOfRequests
                        + " steps). There is no static element in the output that can be used as a basis of comparison for the result of requesting URLs with the parameter values modified. Perhaps a CAPTCHA or WAF has kicked in!!");
                return; // we have not even got as far as looking at the parameters, so just
                // abort straight out of the method
            }
        }
        // get rid of any remnants of cookie setting and Date headers in the responses, as these
        // cause false positives, and can be safely ignored
        // replace the content length with a non-variable placeholder
        // replace url parameters with a non-variable placeholder to eliminate tokens in URLs in
        // the output
        longestCommonSubstringA = longestCommonSubstringA.replaceAll("Set-Cookie:[^\\r\\n]+[\\r\\n]{1,2}", "");
        longestCommonSubstringA = longestCommonSubstringA.replaceAll("Date:[^\\r\\n]+[\\r\\n]{1,2}", "");
        longestCommonSubstringA = longestCommonSubstringA.replaceAll("Content-Length:[^\\r\\n]+[\\r\\n]{1,2}",
                "Content-Length: XXXX\n");
        longestCommonSubstringA = longestCommonSubstringA
                .replaceAll("(?<=(&amp;|\\?)[^\\?\"=&;]+=)[^\\?\"=&;]+(?=(&amp;|\"))", "YYYY");

        if (this.debugEnabled)
            log.debug("The LCS of A is [" + longestCommonSubstringA + "]");

        // 3) for each parameter in the original URL (ie, for URL params, form params, and
        // cookie params)
        for (Iterator<HtmlParameter> iter = htmlParams.iterator(); iter.hasNext();) {

            HttpMessage msgModifiedParam = getNewMsg();
            HtmlParameter currentHtmlParameter = iter.next();

            if (this.debugEnabled)
                log.debug("Handling [" + currentHtmlParameter.getType() + "] parameter ["
                        + currentHtmlParameter.getName() + "], with value [" + currentHtmlParameter.getValue()
                        + "]");

            // 4) Change the current parameter value (which we assume is the username parameter)
            // to an invalid username (randomly), and request the URL n times. Store the results
            // in B[].

            // get a random user name the same length as the original!
            String invalidUsername = RandomStringUtils.random(currentHtmlParameter.getValue().length(),
                    RANDOM_USERNAME_CHARS);
            if (this.debugEnabled)
                log.debug("The invalid username chosen was [" + invalidUsername + "]");

            TreeSet<HtmlParameter> requestParams = null;
            if (currentHtmlParameter.getType().equals(HtmlParameter.Type.cookie)) {
                requestParams = msgModifiedParam.getRequestHeader().getCookieParams();
                requestParams.remove(currentHtmlParameter);
                requestParams.add(new HtmlParameter(currentHtmlParameter.getType(),
                        currentHtmlParameter.getName(), invalidUsername.toString())); // add in the invalid username
                msgModifiedParam.setCookieParams(requestParams);
            } else if (currentHtmlParameter.getType().equals(HtmlParameter.Type.url)) {
                requestParams = msgModifiedParam.getUrlParams();
                requestParams.remove(currentHtmlParameter);
                requestParams.add(new HtmlParameter(currentHtmlParameter.getType(),
                        currentHtmlParameter.getName(), invalidUsername.toString())); // add in the invalid username
                msgModifiedParam.setGetParams(requestParams);
            } else if (currentHtmlParameter.getType().equals(HtmlParameter.Type.form)) {
                requestParams = msgModifiedParam.getFormParams();
                requestParams.remove(currentHtmlParameter);
                requestParams.add(new HtmlParameter(currentHtmlParameter.getType(),
                        currentHtmlParameter.getName(), invalidUsername.toString())); // add in the invalid username
                msgModifiedParam.setFormParams(requestParams);
            }

            if (this.debugEnabled)
                log.debug("About to loop for " + numberOfRequests
                        + " iterations with an incorrect user of the same length");

            boolean continueForParameter = true;
            for (int i = 0; i < numberOfRequests && continueForParameter; i++) {

                // initialise the storage for this iteration
                responseB = new StringBuilder(250);

                HttpMessage msgCpy = msgModifiedParam; // use the message we already set up, with the
                // modified parameter value

                sendAndReceive(msgCpy, false, false); // request the URL, but do not automatically follow redirects.

                // get all cookies set in the response
                TreeSet<HtmlParameter> cookies = msgCpy.getResponseHeader().getCookieParams();

                int redirectCount = 0;
                while (HttpStatusCode.isRedirection(msgCpy.getResponseHeader().getStatusCode())) {
                    redirectCount++;

                    if (this.debugEnabled)
                        log.debug("Following redirect " + redirectCount + " for message " + i + " of "
                                + numberOfRequests + " iterations of the modified query");

                    // append the response to the responses so far for this particular instance
                    // this will give us a complete picture of the full set of actual traffic
                    // associated with following redirects for the request
                    responseB.append(msgCpy.getResponseHeader().getHeadersAsString());
                    responseB.append(msgCpy.getResponseBody().toString());

                    // and manually follow the redirect
                    // create a new message from scratch
                    HttpMessage msgRedirect = new HttpMessage();

                    // create a new URI from the absolute location returned, and interpret it as
                    // escaped
                    // note that the standard says that the Location returned should be
                    // absolute, but it ain't always so...
                    URI newLocation = new URI(msgCpy.getResponseHeader().getHeader(HttpHeader.LOCATION), true);
                    try {
                        msgRedirect.getRequestHeader().setURI(newLocation);
                    } catch (Exception e) {
                        // the Location field contents may not be standards compliant. Lets
                        // generate a uri to use as a workaround where a relative path was
                        // given instead of an absolute one
                        URI newLocationWorkaround = new URI(msgCpy.getRequestHeader().getURI(),
                                msgCpy.getResponseHeader().getHeader(HttpHeader.LOCATION), true);
                        // try again, except this time, if it fails, don't try to handle it
                        if (this.debugEnabled)
                            log.debug("The Location [" + newLocation
                                    + "] specified in a redirect was not valid (not absolute?). Trying absolute workaround url ["
                                    + newLocationWorkaround + "]");
                        msgRedirect.getRequestHeader().setURI(newLocationWorkaround);
                    }
                    msgRedirect.getRequestHeader().setMethod(HttpRequestHeader.GET); // it's always a GET for a redirect
                    msgRedirect.getRequestHeader().setContentLength(0); // since we send a GET, the body will be 0 long
                    if (cookies.size() > 0) {
                        // if a previous request sent back a cookie that has not since been
                        // invalidated, we need to set that cookie when following redirects, as
                        // a browser would
                        msgRedirect.getRequestHeader().setCookieParams(cookies);
                    }

                    sendAndReceive(msgRedirect, false, false); // do NOT redirect.. handle it here

                    // handle scenario where a cookie is unset in a subsequent iteration, or
                    // where the same cookie name is later re-assigned a different value
                    // ie, in these cases, do not simply (and dumbly) accumulate cookie
                    // detritus.
                    // first get all cookies set in the response
                    TreeSet<HtmlParameter> cookiesTemp = msgRedirect.getResponseHeader().getCookieParams();
                    for (Iterator<HtmlParameter> redirectSetsCookieIterator = cookiesTemp
                            .iterator(); redirectSetsCookieIterator.hasNext();) {
                        HtmlParameter cookieJustSet = redirectSetsCookieIterator.next();
                        // loop through each of the cookies we know about in cookies, to see if
                        // it matches by name.
                        // if so, delete that cookie, and add the one that was just set to
                        // cookies.
                        // if not, add the one that was just set to cookies.
                        for (Iterator<HtmlParameter> knownCookiesIterator = cookies
                                .iterator(); knownCookiesIterator.hasNext();) {
                            HtmlParameter knownCookie = knownCookiesIterator.next();
                            if (cookieJustSet.getName().equals(knownCookie.getName())) {
                                knownCookiesIterator.remove();
                                break; // out of the loop for known cookies, back to the next
                                // cookie set in the response
                            }
                        } // end of loop for cookies we already know about
                          // we can now safely add the cookie that was just set into cookies,
                          // knowing it does not clash with anything else in there.
                        cookies.add(cookieJustSet);
                    } // end of for loop for cookies just set in the redirect

                    msgCpy = msgRedirect; // store the last redirect message into the MsgCpy, as
                    // we will be using it's output in a moment..
                } // end of loop to follow redirects

                // now that the redirections have all been handled.. was the request finally a
                // success or not?  Successful or Failed Logins would normally both return an OK
                // HTTP status
                if (!HttpStatusCode.isSuccess(msgCpy.getResponseHeader().getStatusCode())) {
                    log.warn("The modified URL [" + msgModifiedParam.getRequestHeader().getURI()
                            + "] returned a non-OK HTTP status " + msgCpy.getResponseHeader().getStatusCode()
                            + " (after " + i + 1 + " of " + numberOfRequests + " steps for ["
                            + currentHtmlParameter.getType() + "] parameter " + currentHtmlParameter.getName()
                            + "). Could be indicative of SQL Injection, or some other error. The URL is not stable enough to look at Username Enumeration");
                    continueForParameter = false;
                    continue; // skip directly to the next parameter. Do not pass Go. Do not
                    // collect $200.
                }

                if (this.debugEnabled)
                    log.debug("Done following redirects!");

                // append the response to the responses so far for this particular instance
                // this will give us a complete picture of the full set of actual traffic
                // associated with following redirects for the request
                responseB.append(msgCpy.getResponseHeader().getHeadersAsString());
                responseB.append(msgCpy.getResponseBody().toString());

                // 5) Compute the longest common subsequence (LCS) of B[] into LCS_B
                // Note: in the Freiling and Schinzel method, this is calculated recursively. We
                // calculate it iteratively, but using an equivalent method

                // first time in, the LCS is simple: it's the first HTML result.. no diffing
                // required
                if (i == 0)
                    longestCommonSubstringB = responseB.toString();
                // else get the LCS of the existing string, and the current result
                else
                    longestCommonSubstringB = this.longestCommonSubsequence(longestCommonSubstringB,
                            responseB.toString());

                // optimisation step: if the LCS of B is 0 characters long already, then the URL
                // output is not stable, and we can abort now, and save some time
                if (longestCommonSubstringB.length() == 0) {
                    // this might occur if the output returned for the URL changed mid-way.
                    // Perhaps a CAPTCHA has fired, or a WAF has kicked in.  Let's abort now so.
                    log.warn("The modified URL [" + msgModifiedParam.getRequestHeader().getURI() + "] (for ["
                            + currentHtmlParameter.getType() + "] parameter " + currentHtmlParameter.getName()
                            + ") does not produce stable output (after " + i + 1 + " of " + numberOfRequests
                            + " steps). There is no static element in the output that can be used as a basis of comparison with the static output of the original query. Perhaps a CAPTCHA or WAF has kicked in!!");
                    continueForParameter = false;
                    continue; // skip directly to the next parameter. Do not pass Go. Do not
                    // collect $200.
                    // Note: if a CAPTCHA or WAF really has fired, the results of subsequent
                    // iterations will likely not be accurate..
                }
            }

            // if we didn't hit something with one of the iterations for the parameter (ie, if
            // the output when changing the parm is stable),
            // check if the parameter might be vulnerable by comparins its LCS with the original
            // LCS for a valid login
            if (continueForParameter == true) {
                // get rid of any remnants of cookie setting and Date headers in the responses,
                // as these cause false positives, and can be safely ignored
                // replace the content length with a non-variable placeholder
                // replace url parameters with a non-variable placeholder to eliminate tokens in
                // URLs in the output
                longestCommonSubstringB = longestCommonSubstringB
                        .replaceAll("Set-Cookie:[^\\r\\n]+[\\r\\n]{1,2}", "");
                longestCommonSubstringB = longestCommonSubstringB.replaceAll("Date:[^\\r\\n]+[\\r\\n]{1,2}",
                        "");
                longestCommonSubstringB = longestCommonSubstringB
                        .replaceAll("Content-Length:[^\\r\\n]+[\\r\\n]{1,2}", "Content-Length: XXXX\n");
                longestCommonSubstringB = longestCommonSubstringB
                        .replaceAll("(?<=(&amp;|\\?)[^\\?\"=&;]+=)[^\\?\"=&;]+(?=(&amp;|\"))", "YYYY");

                if (this.debugEnabled)
                    log.debug("The LCS of B is [" + longestCommonSubstringB + "]");

                // 6) If LCS_A <> LCS_B, then there is a Username Enumeration issue on the
                // current parameter
                if (!longestCommonSubstringA.equals(longestCommonSubstringB)) {
                    // calculate line level diffs of the 2 Longest Common Substrings to aid the
                    // user in deciding if the match is a false positive
                    // get the diff as a series of patches
                    Patch diffpatch = DiffUtils.diff(
                            new LinkedList<String>(Arrays.asList(longestCommonSubstringA.split("\\n"))),
                            new LinkedList<String>(Arrays.asList(longestCommonSubstringB.split("\\n"))));

                    int numberofDifferences = diffpatch.getDeltas().size();

                    // and convert the list of patches to a String, joining using a newline
                    // String diffAB = StringUtils.join(diffpatch.getDeltas(), "\n");
                    StringBuilder tempDiff = new StringBuilder(250);
                    for (Delta delta : diffpatch.getDeltas()) {
                        String changeType = null;
                        if (delta.getType() == Delta.TYPE.CHANGE)
                            changeType = "Changed Text";
                        else if (delta.getType() == Delta.TYPE.DELETE)
                            changeType = "Deleted Text";
                        else if (delta.getType() == Delta.TYPE.INSERT)
                            changeType = "Inserted text";
                        else
                            changeType = "Unknown change type [" + delta.getType() + "]";

                        tempDiff.append("\n(" + changeType + ")\n"); // blank line before
                        tempDiff.append("Output for Valid Username  : " + delta.getOriginal() + "\n"); // no blank lines
                        tempDiff.append("\nOutput for Invalid Username: " + delta.getRevised() + "\n"); // blank line before
                    }
                    String diffAB = tempDiff.toString();
                    String extraInfo = Constant.messages.getString(
                            "ascanbeta.usernameenumeration.alert.extrainfo", currentHtmlParameter.getType(),
                            currentHtmlParameter.getName(), currentHtmlParameter.getValue(), // original value
                            invalidUsername.toString(), // new value
                            diffAB, // the differences between the two sets of output
                            numberofDifferences); // the number of differences
                    String attack = Constant.messages.getString("ascanbeta.usernameenumeration.alert.attack",
                            currentHtmlParameter.getType(), currentHtmlParameter.getName());
                    String vulnname = Constant.messages.getString("ascanbeta.usernameenumeration.name");
                    String vulndesc = Constant.messages.getString("ascanbeta.usernameenumeration.desc");
                    String vulnsoln = Constant.messages.getString("ascanbeta.usernameenumeration.soln");

                    // call bingo with some extra info, indicating that the alert is
                    bingo(Alert.RISK_INFO, Alert.CONFIDENCE_LOW, vulnname, vulndesc,
                            getBaseMsg().getRequestHeader().getURI().getURI(), currentHtmlParameter.getName(),
                            attack, extraInfo, vulnsoln, getBaseMsg());

                } else {
                    if (this.debugEnabled)
                        log.debug("[" + currentHtmlParameter.getType() + "] parameter ["
                                + currentHtmlParameter.getName()
                                + "] looks ok (Invalid Usernames cannot be distinguished from Valid usernames)");
                }
            }
        } // end of the for loop around the parameter list

    } catch (Exception e) {
        // Do not try to internationalise this.. we need an error message in any event..
        // if it's in English, it's still better than not having it at all.
        log.error("An error occurred checking a url for Username Enumeration issues", e);
    }
}

From source file:org.zaproxy.zap.extension.soap.WSDLCustomParser.java

private static void persistMessage(final HttpMessage message, final String opDisplayName) {
    // Add the message to the history panel and sites tree
    final HistoryReference historyRef;

    try {//from w w  w .  j a v  a 2 s .  co  m
        historyRef = new HistoryReference(Model.getSingleton().getSession(), HistoryReference.TYPE_ZAP_USER,
                message);
    } catch (Exception e) {
        LOG.warn(e.getMessage(), e);
        return;
    }

    final ExtensionHistory extHistory = Control.getSingleton().getExtensionLoader()
            .getExtension(ExtensionHistory.class);
    if (extHistory != null) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                extHistory.addHistory(historyRef);

                // FIXME
                /*
                 * Modifies the URI adding the SOAP operation name to avoid overwrites. It's
                 * done after saving it in history so that the original URI is preserved for
                 * scanning tasks.
                 *
                 * URI modification solution is only a workaround since it has size limitations
                 * and, anyway, it is not an elegant solution.
                 */
                try {
                    URI soapURI = message.getRequestHeader().getURI();
                    String soapStringURI = soapURI.getPath();
                    soapURI.setPath(soapStringURI + opDisplayName);
                } catch (URIException e) {
                    LOG.warn(e.getMessage(), e);
                }
                Model.getSingleton().getSession().getSiteTree().addPath(historyRef, message);
            }
        });
    }
}

From source file:org.zaproxy.zap.extension.zest.menu.ZestGenerateScriptFromAlertMenu.java

private void performActionImpl(HttpMessage msg) throws Exception {
    // Build up a Zest script...
    ZestScript sz = new ZestScript();
    // Build up the default tile
    URI uri = msg.getRequestHeader().getURI();
    String pathEnd = uri.getPath();
    if (pathEnd.lastIndexOf("/") > 0) {
        pathEnd = pathEnd.substring(pathEnd.lastIndexOf("/") + 1);
    }/*from w ww .ja v a 2s.  com*/
    String title1 = Constant.messages.getString("zest.alert2script.script.name", uri.getHost(), pathEnd,
            this.lastAlert.getName());

    String title2 = title1;
    int i = 2;
    while (this.extension.getExtScript().getScript(title2) != null) {
        // Keep going until we find a unique one, otherwise it will fail
        title2 = title1 + " " + i++;
    }

    sz.setTitle(title2);
    // Build up the script description from the alert description, other info and solution
    StringBuilder sb = new StringBuilder();
    sb.append(this.lastAlert.getDescription());
    if (this.lastAlert.getOtherInfo() != null) {
        sb.append("\n\n");
        sb.append(this.lastAlert.getOtherInfo());
    }
    if (this.lastAlert.getSolution() != null) {
        sb.append("\n\n");
        sb.append(this.lastAlert.getSolution());
    }
    sz.setDescription(sb.toString());
    // Work out a reasonable prefix
    String prefix = this.lastAlert.getUri();
    int slash = -1;
    if (prefix != null && prefix.length() > 8) {
        // The 8 is to get past "https://" - which is also good for just http://
        slash = prefix.substring(8).indexOf("/");
        if (slash > 0) {
            sz.setPrefix(prefix.substring(0, slash + 8));
        }
    }

    // Add the request
    sz.add(ZestZapUtils.toZestRequest(this.lastAlert.getMessage(), false, extension.getParam()));

    String evidence = this.lastAlert.getEvidence();
    if (evidence != null && evidence.length() > 0) {
        ZestConditional zc = null;
        // We have some evidence, can we find it?
        if (this.lastAlert.getMessage().getResponseHeader().toString().contains(evidence)) {
            // Found in the header
            zc = new ZestConditional(
                    new ZestExpressionRegex(ZestVariables.RESPONSE_HEADER, Pattern.quote(evidence)));
        } else if (this.lastAlert.getMessage().getResponseBody().toString().contains(evidence)) {
            // Found in body
            zc = new ZestConditional(
                    new ZestExpressionRegex(ZestVariables.RESPONSE_BODY, Pattern.quote(evidence)));
        }
        if (zc != null) {
            // Found the evidence, add and fail if its found
            ZestActionFail zaf = new ZestActionFail();
            switch (this.lastAlert.getRisk()) {
            case Alert.RISK_INFO:
                zaf.setPriority(ZestActionFail.Priority.INFO);
                break;
            case Alert.RISK_LOW:
                zaf.setPriority(ZestActionFail.Priority.LOW);
                break;
            case Alert.RISK_MEDIUM:
                zaf.setPriority(ZestActionFail.Priority.MEDIUM);
                break;
            case Alert.RISK_HIGH:
                zaf.setPriority(ZestActionFail.Priority.HIGH);
                break;
            }
            zaf.setMessage(this.lastAlert.getName());
            zc.addIf(zaf);
            sz.add(zc);
        } else {
            // Add a suitable comment - evidence not found
            sz.add(new ZestComment(Constant.messages.getString("zest.alert2script.badevidence.comment")));
        }
    } else {
        // Add a suitable comment - no evidence
        sz.add(new ZestComment(Constant.messages.getString("zest.alert2script.noevidence.comment")));
    }

    ScriptWrapper sw = new ScriptWrapper();
    sw.setName(sz.getTitle());
    sw.setDescription(sz.getDescription());
    sw.setContents(ZestJSON.toString(sz));
    sw.setType(extension.getExtScript().getScriptType(ExtensionScript.TYPE_STANDALONE));
    sw.setEngine(this.extension.getZestEngineWrapper());

    ZestScriptWrapper zsw = new ZestScriptWrapper(sw);
    extension.add(zsw, true);
}