Example usage for java.net SocketException getClass

List of usage examples for java.net SocketException getClass

Introduction

In this page you can find the example usage for java.net SocketException getClass.

Prototype

@HotSpotIntrinsicCandidate
public final native Class<?> getClass();

Source Link

Document

Returns the runtime class of this Object .

Usage

From source file:org.zaproxy.zap.extension.ascanrules.TestSQLInjection.java

/** scans for SQL Injection vulnerabilities */
@Override//from w  w  w.  j  a  v  a 2 s  .c  om
public void scan(HttpMessage msg, String param, String origParamValue) {
    // Note: the "value" we are passed here is escaped. we need to unescape it before handling
    // it.
    // as soon as we find a single SQL injection on the url, skip out. Do not look for SQL
    // injection on a subsequent parameter on the same URL
    // for performance reasons.
    // reinitialise each parameter.
    sqlInjectionFoundForUrl = false;
    sqlInjectionAttack = null;
    refreshedmessage = null;
    mResBodyNormalUnstripped = null;
    mResBodyNormalStripped = null;

    try {
        // reinitialise the count for each type of request, for each parameter.  We will be
        // sticking to limits defined in the attach strength logic
        countErrorBasedRequests = 0;
        countExpressionBasedRequests = 0;
        countBooleanBasedRequests = 0;
        countUnionBasedRequests = 0;
        countOrderByBasedRequests = 0;

        // Check 1: Check for Error Based SQL Injection (actual error messages).
        // for each SQL metacharacter combination to try
        for (int sqlErrorStringIndex = 0; sqlErrorStringIndex < SQL_CHECK_ERR.length && !sqlInjectionFoundForUrl
                && doSpecificErrorBased
                && countErrorBasedRequests < doErrorMaxRequests; sqlErrorStringIndex++) {

            // work through the attack using each of the following strings as a prefix: the
            // empty string, and the original value
            // Note: this doubles the amount of work done by the scanner, but is necessary in
            // some cases
            String[] prefixStrings;
            if (origParamValue != null) {
                // ZAP: Removed getURLDecode()
                prefixStrings = new String[] { "", origParamValue };
            } else {
                prefixStrings = new String[] { "" };
            }
            for (int prefixIndex = 0; prefixIndex < prefixStrings.length
                    && !sqlInjectionFoundForUrl; prefixIndex++) {

                // new message for each value we attack with
                HttpMessage msg1 = getNewMsg();
                String sqlErrValue = prefixStrings[prefixIndex] + SQL_CHECK_ERR[sqlErrorStringIndex];
                setParameter(msg1, param, sqlErrValue);

                // System.out.println("Attacking [" + msg + "], parameter [" + param + "] with
                // value ["+ sqlErrValue + "]");

                // send the message with the modified parameters
                try {
                    sendAndReceive(msg1, false); // do not follow redirects
                } catch (SocketException ex) {
                    if (log.isDebugEnabled())
                        log.debug("Caught " + ex.getClass().getName() + " " + ex.getMessage()
                                + " when accessing: " + msg1.getRequestHeader().getURI().toString());
                    continue; // Something went wrong, continue to the next prefixString in the
                    // loop
                }
                countErrorBasedRequests++;

                // now check the results against each pattern in turn, to try to identify a
                // database, or even better: a specific database.
                // Note: do NOT check the HTTP error code just yet, as the result could come
                // back with one of various codes.
                for (RDBMS rdbms : RDBMS.values()) {
                    if (getTechSet().includes(rdbms.getTech())
                            && checkSpecificErrors(rdbms, msg1, param, sqlErrValue)) {
                        sqlInjectionFoundForUrl = true;
                        // Save the attack string for the "Authentication Bypass" alert, if
                        // necessary
                        sqlInjectionAttack = sqlErrValue;
                        break;
                    }
                    // bale out if we were asked nicely
                    if (isStop()) {
                        log.debug("Stopping the scan due to a user request");
                        return;
                    }
                } // end of the loop to check for RDBMS specific error messages

                if (this.doGenericErrorBased && !sqlInjectionFoundForUrl) {
                    Iterator<Pattern> errorPatternIterator = RDBMS.GenericRDBMS.getErrorPatterns().iterator();

                    while (errorPatternIterator.hasNext() && !sqlInjectionFoundForUrl) {
                        Pattern errorPattern = errorPatternIterator.next();
                        String errorPatternRDBMS = RDBMS.GenericRDBMS.getName();

                        // if the "error message" occurs in the result of sending the modified
                        // query, but did NOT occur in the original result of the original query
                        // then we may may have a SQL Injection vulnerability
                        StringBuilder sb = new StringBuilder();
                        if (!matchBodyPattern(getBaseMsg(), errorPattern, null)
                                && matchBodyPattern(msg1, errorPattern, sb)) {
                            // Likely a SQL Injection. Raise it
                            String extraInfo = Constant.messages.getString(
                                    MESSAGE_PREFIX + "alert.errorbased.extrainfo", errorPatternRDBMS,
                                    errorPattern.toString());
                            // raise the alert, and save the attack string for the
                            // "Authentication Bypass" alert, if necessary
                            sqlInjectionAttack = sqlErrValue;
                            bingo(Alert.RISK_HIGH, Alert.CONFIDENCE_MEDIUM,
                                    getName() + " - " + errorPatternRDBMS, getDescription(), null, param,
                                    sqlInjectionAttack, extraInfo, getSolution(), sb.toString(), msg1);

                            // log it, as the RDBMS may be useful to know later (in subsequent
                            // checks, when we need to determine RDBMS specific behaviour, for
                            // instance)
                            getKb().add(getBaseMsg().getRequestHeader().getURI(), "sql/" + errorPatternRDBMS,
                                    Boolean.TRUE);

                            sqlInjectionFoundForUrl = true;
                            continue;
                        }
                        // bale out if we were asked nicely
                        if (isStop()) {
                            log.debug("Stopping the scan due to a user request");
                            return;
                        }
                    } // end of the loop to check for RDBMS specific error messages
                }
            } // for each of the SQL_CHECK_ERR values (SQL metacharacters)
        }

        // ###############################
        // Check 4
        // New!  I haven't seen this technique documented anywhere else, but it's dead simple.
        // Let me explain.
        // See if the parameter value can simply be changed to one that *evaluates* to be the
        // same value,
        // if evaluated on a database
        // the simple check is to see if parameter "1" gives the same results as for param
        // "2-1", and different results for param "2-2"
        // for now, we try this for integer values only.
        // ###############################
        // Since the previous checks are attempting SQL injection, and may have actually
        // succeeded in modifying the database (ask me how I know?!)
        // then we cannot rely on the database contents being the same as when the original
        // query was last run (could be hours ago)
        // so to work around this, simply re-run the query again now at this point.
        // Note that we are not counting this request in our max number of requests to be issued
        refreshedmessage = getNewMsg();
        try {
            sendAndReceive(refreshedmessage, false); // do not follow redirects
        } catch (SocketException ex) {
            if (log.isDebugEnabled())
                log.debug("Caught " + ex.getClass().getName() + " " + ex.getMessage() + " when accessing: "
                        + refreshedmessage.getRequestHeader().getURI().toString());
            return; // Something went wrong, no point continuing
        }

        // String mResBodyNormal = getBaseMsg().getResponseBody().toString();
        mResBodyNormalUnstripped = refreshedmessage.getResponseBody().toString();
        mResBodyNormalStripped = this.stripOff(mResBodyNormalUnstripped, origParamValue);

        if (!sqlInjectionFoundForUrl && doExpressionBased
                && countExpressionBasedRequests < doExpressionMaxRequests) {

            // first figure out the type of the parameter..
            try {
                // is it an integer type?
                // ZAP: removed URLDecoding because on Variants
                // int paramAsInt =
                // Integer.parseInt(TestSQLInjection.getURLDecode(origParamValue));
                int paramAsInt = Integer.parseInt(origParamValue);

                if (this.debugEnabled) {
                    log.debug("The parameter value [" + origParamValue + "] is of type Integer");
                }
                // This check is implemented using two variant PLUS(+) and MULT(*)
                try {
                    // PLUS variant check the param value "3-2" gives same result as original
                    // request and param value "4-2" gives different result if original param
                    // value is 1
                    // set the parameter value to a string value like "3-2", if the original
                    // parameter value was "1"
                    int paramPlusTwo = addWithOverflowCheck(paramAsInt, 2);
                    String modifiedParamValueForAdd = String.valueOf(paramPlusTwo) + "-2";
                    // set the parameter value to a string value like "4-2", if the original
                    // parameter value was "1"
                    int paramPlusThree = addWithOverflowCheck(paramAsInt, 3);
                    String modifiedParamValueConfirmForAdd = String.valueOf(paramPlusThree) + "-2";
                    // Do the attack for ADD variant
                    expressionBasedAttack(param, origParamValue, modifiedParamValueForAdd,
                            modifiedParamValueConfirmForAdd);
                    // bale out if we were asked nicely
                    if (isStop()) {
                        log.debug("Stopping the scan due to a user request");
                        return;
                    }
                    // MULT variant check the param value "2/2" gives same result as original
                    // request and param value "4/2" gives different result if original param
                    // value is 1
                    if (!sqlInjectionFoundForUrl && countExpressionBasedRequests < doExpressionMaxRequests) {
                        // set the parameter value to a string value like "2/2", if the original
                        // parameter value was "1"
                        int paramMultTwo = multiplyWithOverflowCheck(paramAsInt, 2);
                        String modifiedParamValueForMult = String.valueOf(paramMultTwo) + "/2";
                        // set the parameter value to a string value like "4/2", if the original
                        // parameter value was "1"
                        int paramMultFour = multiplyWithOverflowCheck(paramAsInt, 4);
                        String modifiedParamValueConfirmForMult = String.valueOf(paramMultFour) + "/2";
                        // Do the attack for MULT variant
                        expressionBasedAttack(param, origParamValue, modifiedParamValueForMult,
                                modifiedParamValueConfirmForMult);
                        // bale out if we were asked nicely
                        if (isStop()) {
                            log.debug("Stopping the scan due to a user request");
                            return;
                        }
                    }
                } catch (ArithmeticException ex) {
                    if (this.debugEnabled) {
                        log.debug("Caught " + ex.getClass().getName() + " " + ex.getMessage()
                                + "When performing integer math with the parameter value [" + origParamValue
                                + "]");
                    }
                }
            } catch (Exception e) {
                if (this.debugEnabled) {
                    log.debug("The parameter value [" + origParamValue + "] is NOT of type Integer");
                }
                // TODO: implement a similar check for string types?  This probably needs to be
                // RDBMS specific (ie, it should not live in this scanner)
            }
        }

        // Check 2: boolean based checks.
        // the check goes like so:
        // append " and 1 = 1" to the param.  Send the query.  Check the results. Hopefully they
        // match the original results from the unmodified query,
        // *suggesting* (but not yet definitely) that we have successfully modified the query,
        // (hopefully not gotten an error message),
        // and have gotten the same results back, which is what you would expect if you added
        // the constraint " and 1 = 1" to most (but not every) SQL query.
        // So was it a fluke that we got the same results back from the modified query? Perhaps
        // the original query returned 0 rows, so adding any number of
        // constraints would change nothing?  It is still a possibility!
        // check to see if we can change the original parameter again to *restrict* the scope of
        // the query using an AND with an always false condition (AND_ERR)
        // (decreasing the results back to nothing), or to *broaden* the scope of the query
        // using an OR with an always true condition (AND_OR)
        // (increasing the results).
        // If we can successfully alter the results to our requirements, by one means or
        // another, we have found a SQL Injection vulnerability.
        // Some additional complications: assume there are 2 HTML parameters: username and
        // password, and the SQL constructed is like so:
        // select * from username where user = "$user" and password = "$password"
        // and lets assume we successfully know the type of the user field, via SQL_OR_TRUE
        // value '" OR "1"="1' (single quotes not part of the value)
        // we still have the problem that the actual SQL executed would look like so:
        // select * from username where user = "" OR "1"="1" and password = "whateveritis"
        // Since the password field is still taken into account (by virtue of the AND condition
        // on the password column), and we only inject one parameter at a time,
        // we are still not in control.
        // the solution is simple: add an end-of-line comment to the field added in (in this
        // example: the user field), so that the SQL becomes:
        // select * from username where user = "" OR "1"="1" -- and password = "whateveritis"
        // the result is that any additional constraints are commented out, and the last
        // condition to have any effect is the one whose
        // HTTP param we are manipulating.
        // Note also that because this comment only needs to be added to the "SQL_OR_TRUE" and
        // not to the equivalent SQL_AND_FALSE, because of the nature of the OR
        // and AND conditions in SQL.
        // Corollary: If a particular RDBMS does not offer the ability to comment out the
        // remainder of a line, we will not attempt to comment out anything in the query
        //            and we will simply hope that the *last* constraint in the SQL query is
        // constructed from a HTTP parameter under our control.

        if (this.debugEnabled) {
            log.debug("Doing Check 2, since check 1 did not match for "
                    + getBaseMsg().getRequestHeader().getURI());
        }

        // Since the previous checks are attempting SQL injection, and may have actually
        // succeeded in modifying the database (ask me how I know?!)
        // then we cannot rely on the database contents being the same as when the original
        // query was last run (could be hours ago)
        // so to work around this, simply re-run the query again now at this point.
        // Note that we are not counting this request in our max number of requests to be issued
        refreshedmessage = getNewMsg();
        try {
            sendAndReceive(refreshedmessage, false); // do not follow redirects
        } catch (SocketException ex) {
            if (log.isDebugEnabled())
                log.debug("Caught " + ex.getClass().getName() + " " + ex.getMessage() + " when accessing: "
                        + refreshedmessage.getRequestHeader().getURI().toString());
            return; // Something went wrong, no point continuing
        }

        // String mResBodyNormal = getBaseMsg().getResponseBody().toString();
        mResBodyNormalUnstripped = refreshedmessage.getResponseBody().toString();
        mResBodyNormalStripped = this.stripOff(mResBodyNormalUnstripped, origParamValue);

        // boolean booleanBasedSqlInjectionFoundForParam = false;

        // try each of the AND syntax values in turn.
        // Which one is successful will depend on the column type of the table/view column into
        // which we are injecting the SQL.
        for (int i = 0; i < SQL_LOGIC_AND_TRUE.length && !sqlInjectionFoundForUrl && doBooleanBased
                && countBooleanBasedRequests < doBooleanMaxRequests; i++) {
            // needs a new message for each type of AND to be issued
            HttpMessage msg2 = getNewMsg();
            // ZAP: Removed getURLDecode()
            String sqlBooleanAndTrueValue = origParamValue + SQL_LOGIC_AND_TRUE[i];
            String sqlBooleanAndFalseValue = origParamValue + SQL_LOGIC_AND_FALSE[i];

            setParameter(msg2, param, sqlBooleanAndTrueValue);

            // send the AND with an additional TRUE statement tacked onto the end. Hopefully it
            // will return the same results as the original (to find a vulnerability)
            try {
                sendAndReceive(msg2, false); // do not follow redirects
            } catch (SocketException ex) {
                if (log.isDebugEnabled())
                    log.debug("Caught " + ex.getClass().getName() + " " + ex.getMessage() + " when accessing: "
                            + msg2.getRequestHeader().getURI().toString());
                continue; // Something went wrong, continue to the next item in the loop
            }
            countBooleanBasedRequests++;

            // String resBodyAND = msg2.getResponseBody().toString();
            String resBodyANDTrueUnstripped = msg2.getResponseBody().toString();
            String resBodyANDTrueStripped = stripOffOriginalAndAttackParam(resBodyANDTrueUnstripped,
                    origParamValue, sqlBooleanAndTrueValue);

            // set up two little arrays to ease the work of checking the unstripped output, and
            // then the stripped output
            String normalBodyOutput[] = { mResBodyNormalUnstripped, mResBodyNormalStripped };
            String andTrueBodyOutput[] = { resBodyANDTrueUnstripped, resBodyANDTrueStripped };
            boolean strippedOutput[] = { false, true };

            for (int booleanStrippedUnstrippedIndex = 0; booleanStrippedUnstrippedIndex < 2; booleanStrippedUnstrippedIndex++) {
                // if the results of the "AND 1=1" match the original query (using either the
                // stipped or unstripped versions), we may be onto something.
                if (andTrueBodyOutput[booleanStrippedUnstrippedIndex]
                        .compareTo(normalBodyOutput[booleanStrippedUnstrippedIndex]) == 0) {
                    if (this.debugEnabled) {
                        log.debug("Check 2, "
                                + (strippedOutput[booleanStrippedUnstrippedIndex] ? "STRIPPED" : "UNSTRIPPED")
                                + " html output for AND TRUE condition [" + sqlBooleanAndTrueValue
                                + "] matched (refreshed) original results for "
                                + refreshedmessage.getRequestHeader().getURI());
                    }
                    // so they match. Was it a fluke? See if we get the same result by tacking
                    // on "AND 1 = 2" to the original
                    HttpMessage msg2_and_false = getNewMsg();

                    setParameter(msg2_and_false, param, sqlBooleanAndFalseValue);

                    try {
                        sendAndReceive(msg2_and_false, false); // do not follow redirects
                    } catch (SocketException ex) {
                        if (log.isDebugEnabled())
                            log.debug("Caught " + ex.getClass().getName() + " " + ex.getMessage()
                                    + " when accessing: "
                                    + msg2_and_false.getRequestHeader().getURI().toString());
                        continue; // Something went wrong, continue on to the next item in the
                        // loop
                    }
                    countBooleanBasedRequests++;

                    // String resBodyANDFalse =
                    // stripOff(msg2_and_false.getResponseBody().toString(),
                    // SQL_LOGIC_AND_FALSE[i]);
                    // String resBodyANDFalse = msg2_and_false.getResponseBody().toString();
                    String resBodyANDFalseUnstripped = msg2_and_false.getResponseBody().toString();
                    String resBodyANDFalseStripped = stripOffOriginalAndAttackParam(resBodyANDFalseUnstripped,
                            origParamValue, sqlBooleanAndFalseValue);

                    String andFalseBodyOutput[] = { resBodyANDFalseUnstripped, resBodyANDFalseStripped };

                    // which AND False output should we compare? the stripped or the unstripped
                    // version?
                    // depends on which one we used to get to here.. use the same as that..

                    // build an always false AND query.  Result should be different to prove the
                    // SQL works.
                    if (andFalseBodyOutput[booleanStrippedUnstrippedIndex]
                            .compareTo(normalBodyOutput[booleanStrippedUnstrippedIndex]) != 0) {
                        if (this.debugEnabled) {
                            log.debug("Check 2, "
                                    + (strippedOutput[booleanStrippedUnstrippedIndex] ? "STRIPPED"
                                            : "UNSTRIPPED")
                                    + " html output for AND FALSE condition [" + sqlBooleanAndFalseValue
                                    + "] differed from (refreshed) original results for "
                                    + refreshedmessage.getRequestHeader().getURI());
                        }

                        // it's different (suggesting that the "AND 1 = 2" appended on gave
                        // different results because it restricted the data set to nothing
                        // Likely a SQL Injection. Raise it
                        String extraInfo = null;
                        if (strippedOutput[booleanStrippedUnstrippedIndex]) {
                            extraInfo = Constant.messages.getString(
                                    MESSAGE_PREFIX + "alert.booleanbased.extrainfo", sqlBooleanAndTrueValue,
                                    sqlBooleanAndFalseValue, "");
                        } else {
                            extraInfo = Constant.messages.getString(
                                    MESSAGE_PREFIX + "alert.booleanbased.extrainfo", sqlBooleanAndTrueValue,
                                    sqlBooleanAndFalseValue, "NOT ");
                        }
                        extraInfo = extraInfo + "\n" + Constant.messages
                                .getString(MESSAGE_PREFIX + "alert.booleanbased.extrainfo.dataexists");

                        // raise the alert, and save the attack string for the "Authentication
                        // Bypass" alert, if necessary
                        sqlInjectionAttack = sqlBooleanAndTrueValue;
                        bingo(Alert.RISK_HIGH, Alert.CONFIDENCE_MEDIUM, getName(), getDescription(), null, // url
                                param, sqlInjectionAttack, extraInfo, getSolution(), "", msg2);

                        sqlInjectionFoundForUrl = true;

                        break; // No further need to loop through SQL_AND

                    } else {
                        // the results of the always false condition are the same as for the
                        // original unmodified parameter
                        // this could be because there was *no* data returned for the original
                        // unmodified parameter
                        // so consider the effect of adding comments to both the always true
                        // condition, and the always false condition
                        // the first value to try..
                        // ZAP: Removed getURLDecode()
                        String orValue = origParamValue + SQL_LOGIC_OR_TRUE[i];

                        // this is where that comment comes in handy: if the RDBMS supports
                        // one-line comments, add one in to attempt to ensure that the
                        // condition becomes one that is effectively always true, returning ALL
                        // data (or as much as possible), allowing us to pinpoint the SQL
                        // Injection
                        if (this.debugEnabled) {
                            log.debug("Check 2 , "
                                    + (strippedOutput[booleanStrippedUnstrippedIndex] ? "STRIPPED"
                                            : "UNSTRIPPED")
                                    + " html output for AND FALSE condition [" + sqlBooleanAndFalseValue
                                    + "] SAME as (refreshed) original results for "
                                    + refreshedmessage.getRequestHeader().getURI()
                                    + " ### (forcing OR TRUE check) ");
                        }
                        HttpMessage msg2_or_true = getNewMsg();
                        setParameter(msg2_or_true, param, orValue);
                        try {
                            sendAndReceive(msg2_or_true, false); // do not follow redirects
                        } catch (SocketException ex) {
                            if (log.isDebugEnabled())
                                log.debug("Caught " + ex.getClass().getName() + " " + ex.getMessage()
                                        + " when accessing: "
                                        + msg2_or_true.getRequestHeader().getURI().toString());
                            continue; // Something went wrong, continue on to the next item in
                            // the loop
                        }
                        countBooleanBasedRequests++;

                        // String resBodyORTrue =
                        // stripOff(msg2_or_true.getResponseBody().toString(), orValue);
                        // String resBodyORTrue = msg2_or_true.getResponseBody().toString();
                        String resBodyORTrueUnstripped = msg2_or_true.getResponseBody().toString();
                        String resBodyORTrueStripped = stripOffOriginalAndAttackParam(resBodyORTrueUnstripped,
                                origParamValue, orValue);

                        String orTrueBodyOutput[] = { resBodyORTrueUnstripped, resBodyORTrueStripped };

                        int compareOrToOriginal = orTrueBodyOutput[booleanStrippedUnstrippedIndex]
                                .compareTo(normalBodyOutput[booleanStrippedUnstrippedIndex]);
                        if (compareOrToOriginal != 0) {

                            if (this.debugEnabled) {
                                log.debug("Check 2, "
                                        + (strippedOutput[booleanStrippedUnstrippedIndex] ? "STRIPPED"
                                                : "UNSTRIPPED")
                                        + " html output for OR TRUE condition [" + orValue
                                        + "] different to (refreshed) original results for "
                                        + refreshedmessage.getRequestHeader().getURI());
                            }

                            // it's different (suggesting that the "OR 1 = 1" appended on gave
                            // different results because it broadened the data set from nothing
                            // to something
                            // Likely a SQL Injection. Raise it
                            String extraInfo = null;
                            if (strippedOutput[booleanStrippedUnstrippedIndex]) {
                                extraInfo = Constant.messages.getString(
                                        MESSAGE_PREFIX + "alert.booleanbased.extrainfo", sqlBooleanAndTrueValue,
                                        orValue, "");
                            } else {
                                extraInfo = Constant.messages.getString(
                                        MESSAGE_PREFIX + "alert.booleanbased.extrainfo", sqlBooleanAndTrueValue,
                                        orValue, "NOT ");
                            }
                            extraInfo = extraInfo + "\n" + Constant.messages
                                    .getString(MESSAGE_PREFIX + "alert.booleanbased.extrainfo.datanotexists");

                            // raise the alert, and save the attack string for the
                            // "Authentication Bypass" alert, if necessary
                            sqlInjectionAttack = orValue;
                            bingo(Alert.RISK_HIGH, Alert.CONFIDENCE_MEDIUM, getName(), getDescription(), null, // url
                                    param, sqlInjectionAttack, extraInfo, getSolution(), "", msg2);

                            sqlInjectionFoundForUrl = true;
                            // booleanBasedSqlInjectionFoundForParam = true;  //causes us to
                            // skip past the other entries in SQL_AND.  Only one will expose a
                            // vuln for a given param, since the database column is of only 1
                            // type

                            break; // No further need to loop
                        }
                    }
                } // if the results of the "AND 1=1" match the original query, we may be onto
                  // something.
                else {
                    // andTrueBodyOutput[booleanStrippedUnstrippedIndex].compareTo(normalBodyOutput[booleanStrippedUnstrippedIndex])
                    // the results of the "AND 1=1" do NOT match the original query, for
                    // whatever reason (no sql injection, or the web page is not stable)
                    if (this.debugEnabled) {
                        log.debug("Check 2, "
                                + (strippedOutput[booleanStrippedUnstrippedIndex] ? "STRIPPED" : "UNSTRIPPED")
                                + " html output for AND condition [" + sqlBooleanAndTrueValue
                                + "] does NOT match the (refreshed) original results for "
                                + refreshedmessage.getRequestHeader().getURI());
                        Patch diffpatch = DiffUtils.diff(
                                new LinkedList<String>(Arrays
                                        .asList(normalBodyOutput[booleanStrippedUnstrippedIndex].split("\\n"))),
                                new LinkedList<String>(Arrays.asList(
                                        andTrueBodyOutput[booleanStrippedUnstrippedIndex].split("\\n"))));

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

                        // and convert the list of patches to a String, joining using a newline
                        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 Unmodified parameter: " + delta.getOriginal() + "\n");
                            tempDiff.append("Output for   modified parameter: " + delta.getRevised() + "\n");
                        }
                        log.debug("DIFFS: " + tempDiff);
                    }
                }
                // bale out if we were asked nicely
                if (isStop()) {
                    log.debug("Stopping the scan due to a user request");
                    return;
                }
            } // end of boolean logic output index (unstripped + stripped)
        }
        // end of check 2

        // check 2a: boolean based logic, where the original query returned *no* data. Here we
        // append " OR 1=1" in an attempt to extract *more* data
        // and then verify the results by attempting to reproduce the original results by
        // appending an " AND 1=2" condition (ie "open up first, then restrict to verify")
        // this differs from the previous logic based check since the previous check assumes
        // that the original query produced data, and tries first to restrict that data
        // (ie, it uses "restrict first, open up to verify" ).
        for (int i = 0; i < SQL_LOGIC_OR_TRUE.length && !sqlInjectionFoundForUrl && doBooleanBased
                && countBooleanBasedRequests < doBooleanMaxRequests; i++) {
            HttpMessage msg2 = getNewMsg();
            String sqlBooleanOrTrueValue = origParamValue + SQL_LOGIC_OR_TRUE[i];
            String sqlBooleanAndFalseValue = origParamValue + SQL_LOGIC_AND_FALSE[i];

            setParameter(msg2, param, sqlBooleanOrTrueValue);
            try {
                sendAndReceive(msg2, false); // do not follow redirects
            } catch (SocketException ex) {
                if (log.isDebugEnabled())
                    log.debug("Caught " + ex.getClass().getName() + " " + ex.getMessage() + " when accessing: "
                            + msg2.getRequestHeader().getURI().toString());
                continue; // Something went wrong, continue on to the next item in the loop
            }
            countBooleanBasedRequests++;

            String resBodyORTrueUnstripped = msg2.getResponseBody().toString();

            // if the results of the "OR 1=1" exceed the original query (unstripped, by more
            // than a 20% size difference, say), we may be onto something.
            // TODO: change the percentage difference threshold based on the alert threshold
            if ((resBodyORTrueUnstripped.length() > (mResBodyNormalUnstripped.length() * 1.2))) {
                if (this.debugEnabled) {
                    log.debug("Check 2a, unstripped html output for OR TRUE condition [" + sqlBooleanOrTrueValue
                            + "] produced sufficiently larger results than the original message");
                }
                // if we can also restrict it back to the original results by appending a " and
                // 1=2", then "Winner Winner, Chicken Dinner".
                HttpMessage msg2_and_false = getNewMsg();
                setParameter(msg2_and_false, param, sqlBooleanAndFalseValue);
                try {
                    sendAndReceive(msg2_and_false, false); // do not follow redirects
                } catch (SocketException ex) {
                    if (log.isDebugEnabled())
                        log.debug("Caught " + ex.getClass().getName() + " " + ex.getMessage()
                                + " when accessing: " + msg2_and_false.getRequestHeader().getURI().toString());
                    continue; // Something went wrong, continue on to the next item in the loop
                }
                countBooleanBasedRequests++;

                String resBodyANDFalseUnstripped = msg2_and_false.getResponseBody().toString();
                String resBodyANDFalseStripped = stripOffOriginalAndAttackParam(resBodyANDFalseUnstripped,
                        origParamValue, sqlBooleanAndFalseValue);

                // does the "AND 1=2" version produce the same as the original (for
                // stripped/unstripped versions)
                boolean verificationUsingUnstripped = resBodyANDFalseUnstripped
                        .compareTo(mResBodyNormalUnstripped) == 0;
                boolean verificationUsingStripped = resBodyANDFalseStripped
                        .compareTo(mResBodyNormalStripped) == 0;
                if (verificationUsingUnstripped || verificationUsingStripped) {
                    if (this.debugEnabled) {
                        log.debug("Check 2, " + (verificationUsingStripped ? "STRIPPED" : "UNSTRIPPED")
                                + " html output for AND FALSE condition [" + sqlBooleanAndFalseValue
                                + "] matches the (refreshed) original results");
                    }
                    // Likely a SQL Injection. Raise it
                    String extraInfo = null;
                    if (verificationUsingStripped) {
                        extraInfo = Constant.messages.getString(MESSAGE_PREFIX + "alert.booleanbased.extrainfo",
                                sqlBooleanOrTrueValue, sqlBooleanAndFalseValue, "");
                    } else {
                        extraInfo = Constant.messages.getString(MESSAGE_PREFIX + "alert.booleanbased.extrainfo",
                                sqlBooleanOrTrueValue, sqlBooleanAndFalseValue, "NOT ");
                    }
                    extraInfo = extraInfo + "\n" + Constant.messages
                            .getString(MESSAGE_PREFIX + "alert.booleanbased.extrainfo.datanotexists");

                    // raise the alert, and save the attack string for the "Authentication
                    // Bypass" alert, if necessary
                    sqlInjectionAttack = sqlBooleanOrTrueValue;
                    bingo(Alert.RISK_HIGH, Alert.CONFIDENCE_MEDIUM, getName(), getDescription(), null, // url
                            param, sqlInjectionAttack, extraInfo, getSolution(), "", msg2);

                    sqlInjectionFoundForUrl = true;

                    break; // No further need to loop
                }
            }
        }
        // end of check 2a

        // Check 3: UNION based
        // for each SQL UNION combination to try
        for (int sqlUnionStringIndex = 0; sqlUnionStringIndex < SQL_UNION_APPENDAGES.length
                && !sqlInjectionFoundForUrl && doUnionBased
                && countUnionBasedRequests < doUnionMaxRequests; sqlUnionStringIndex++) {

            // new message for each value we attack with
            HttpMessage msg3 = getNewMsg();
            String sqlUnionValue = origParamValue + SQL_UNION_APPENDAGES[sqlUnionStringIndex];
            setParameter(msg3, param, sqlUnionValue);
            // send the message with the modified parameters
            try {
                sendAndReceive(msg3, false); // do not follow redirects
            } catch (SocketException ex) {
                if (log.isDebugEnabled())
                    log.debug("Caught " + ex.getClass().getName() + " " + ex.getMessage() + " when accessing: "
                            + msg3.getRequestHeader().getURI().toString());
                continue; // Something went wrong, continue on to the next item in the loop
            }
            countUnionBasedRequests++;

            // now check the results.. look first for UNION specific error messages in the
            // output that were not there in the original output
            // and failing that, look for generic RDBMS specific error messages
            // TODO: maybe also try looking at a differentiation based approach?? Prone to false
            // positives though.
            for (RDBMS rdbms : RDBMS.values()) {
                if (getTechSet().includes(rdbms.getTech()) && checkUnionErrors(rdbms, msg3,
                        mResBodyNormalStripped, refreshedmessage.getRequestHeader().getURI(), param,
                        origParamValue, sqlUnionValue)) {
                    sqlInjectionFoundForUrl = true;
                    // Save the attack string for the "Authentication Bypass" alert, if
                    // necessary
                    sqlInjectionAttack = sqlUnionValue;
                    break;
                }
                // bale out if we were asked nicely
                if (isStop()) {
                    log.debug("Stopping the scan due to a user request");
                    return;
                }
            } // end of the loop to check for RDBMS specific UNION error messages
        } //// for each SQL UNION combination to try
          // end of check 3

        // ###############################

        // check for columns used in the "order by" clause of a SQL statement. earlier tests
        // will likely not catch these

        // append on " ASC -- " to the end of the original parameter. Grab the results.
        // if the results are different to the original (unmodified parameter) results, then
        // bale
        // if the results are the same as for the original parameter value, then the parameter
        // *might* be influencing the order by
        //   try again for "DESC": append on " DESC -- " to the end of the original parameter.
        // Grab the results.
        //   if the results are the same as the original (unmodified parameter) results, then bale
        //   (the results are not under our control, or there is no difference in the ordering,
        // for some reason: 0 or 1 rows only, or ordering
        //   by the first column alone is not sufficient to change the ordering of the data.)
        //   if the results were different to the original (unmodified parameter) results, then
        //      SQL injection!!

        // Since the previous checks are attempting SQL injection, and may have actually
        // succeeded in modifying the database (ask me how I know?!)
        // then we cannot rely on the database contents being the same as when the original
        // query was last run (could be hours ago)
        // so to work around this, simply re-run the query again now at this point.
        // Note that we are not counting this request in our max number of requests to be issued
        refreshedmessage = getNewMsg();
        try {
            sendAndReceive(refreshedmessage, false); // do not follow redirects
        } catch (SocketException ex) {
            if (log.isDebugEnabled())
                log.debug("Caught " + ex.getClass().getName() + " " + ex.getMessage() + " when accessing: "
                        + refreshedmessage.getRequestHeader().getURI().toString());
            return; // Something went wrong, no point continuing
        }

        // String mResBodyNormal = getBaseMsg().getResponseBody().toString();
        mResBodyNormalUnstripped = refreshedmessage.getResponseBody().toString();
        mResBodyNormalStripped = this.stripOff(mResBodyNormalUnstripped, origParamValue);

        if (!sqlInjectionFoundForUrl && doOrderByBased && countOrderByBasedRequests < doOrderByMaxRequests) {

            // ZAP: Removed getURLDecode()
            String modifiedParamValue = origParamValue + " ASC " + SQL_ONE_LINE_COMMENT;

            HttpMessage msg5 = getNewMsg();
            setParameter(msg5, param, modifiedParamValue);

            try {
                sendAndReceive(msg5, false); // do not follow redirects
            } catch (SocketException ex) {
                if (log.isDebugEnabled())
                    log.debug("Caught " + ex.getClass().getName() + " " + ex.getMessage() + " when accessing: "
                            + msg5.getRequestHeader().getURI().toString());
                return; // Something went wrong, no point continuing
            }
            countOrderByBasedRequests++;

            String modifiedAscendingOutputUnstripped = msg5.getResponseBody().toString();
            String modifiedAscendingOutputStripped = stripOffOriginalAndAttackParam(
                    modifiedAscendingOutputUnstripped, origParamValue, modifiedParamValue);

            // set up two little arrays to ease the work of checking the unstripped output, and
            // then the stripped output
            String normalBodyOutput[] = { mResBodyNormalUnstripped, mResBodyNormalStripped };
            String ascendingBodyOutput[] = { modifiedAscendingOutputUnstripped,
                    modifiedAscendingOutputStripped };
            boolean strippedOutput[] = { false, true };

            for (int booleanStrippedUnstrippedIndex = 0; booleanStrippedUnstrippedIndex < 2; booleanStrippedUnstrippedIndex++) {
                // if the results of the modified request match the original query, we may be
                // onto something.
                if (ascendingBodyOutput[booleanStrippedUnstrippedIndex]
                        .compareTo(normalBodyOutput[booleanStrippedUnstrippedIndex]) == 0) {
                    if (this.debugEnabled) {
                        log.debug("Check X, "
                                + (strippedOutput[booleanStrippedUnstrippedIndex] ? "STRIPPED" : "UNSTRIPPED")
                                + " html output for modified Order By parameter [" + modifiedParamValue
                                + "] matched (refreshed) original results for "
                                + refreshedmessage.getRequestHeader().getURI());
                    }
                    // confirm that a different parameter value generates different output, to
                    // minimise false positives

                    // use the descending order this time
                    // ZAP: Removed getURLDecode()
                    String modifiedParamValueConfirm = origParamValue + " DESC " + SQL_ONE_LINE_COMMENT;

                    HttpMessage msg5Confirm = getNewMsg();
                    setParameter(msg5Confirm, param, modifiedParamValueConfirm);

                    try {
                        sendAndReceive(msg5Confirm, false); // do not follow redirects
                    } catch (SocketException ex) {
                        if (log.isDebugEnabled())
                            log.debug("Caught " + ex.getClass().getName() + " " + ex.getMessage()
                                    + " when accessing: " + msg5Confirm.getRequestHeader().getURI().toString());
                        continue; // Something went wrong, continue on to the next item in the
                        // loop
                    }
                    countOrderByBasedRequests++;

                    String confirmOrderByOutputUnstripped = msg5Confirm.getResponseBody().toString();
                    String confirmOrderByOutputStripped = stripOffOriginalAndAttackParam(
                            confirmOrderByOutputUnstripped, origParamValue, modifiedParamValueConfirm);

                    // set up two little arrays to ease the work of checking the unstripped
                    // output or the stripped output
                    String confirmOrderByBodyOutput[] = { confirmOrderByOutputUnstripped,
                            confirmOrderByOutputStripped };

                    if (confirmOrderByBodyOutput[booleanStrippedUnstrippedIndex]
                            .compareTo(normalBodyOutput[booleanStrippedUnstrippedIndex]) != 0) {
                        // the confirm query did not return the same results.  This means that
                        // arbitrary queries are not all producing the same page output.
                        // this means the fact we earlier reproduced the original page output
                        // with a modified parameter was not a coincidence

                        // Likely a SQL Injection. Raise it
                        String extraInfo = null;
                        if (strippedOutput[booleanStrippedUnstrippedIndex]) {
                            extraInfo = Constant.messages.getString(
                                    MESSAGE_PREFIX + "alert.orderbybased.extrainfo", modifiedParamValue, "");
                        } else {
                            extraInfo = Constant.messages.getString(
                                    MESSAGE_PREFIX + "alert.orderbybased.extrainfo", modifiedParamValue,
                                    "NOT ");
                        }

                        // raise the alert, and save the attack string for the "Authentication
                        // Bypass" alert, if necessary
                        sqlInjectionAttack = modifiedParamValue;
                        bingo(Alert.RISK_HIGH, Alert.CONFIDENCE_MEDIUM, getName(), getDescription(), null, // url
                                param, sqlInjectionAttack, extraInfo, getSolution(), "", msg5);

                        sqlInjectionFoundForUrl = true;
                        break; // No further need to loop
                    }
                }
                // bale out if we were asked nicely
                if (isStop()) {
                    log.debug("Stopping the scan due to a user request");
                    return;
                }
            }
        }

        // ###############################

        // if a sql injection was found, we should check if the page is flagged as a login page
        // in any of the contexts.  if it is, raise an "SQL Injection - Authentication Bypass"
        // alert in addition to the alerts already raised
        if (sqlInjectionFoundForUrl) {
            boolean loginUrl = false;
            // log.debug("### A SQL Injection may lead to auth bypass..");

            // are we dealing with a login url in any of the contexts?
            ExtensionAuthentication extAuth = (ExtensionAuthentication) Control.getSingleton()
                    .getExtensionLoader().getExtension(ExtensionAuthentication.NAME);
            if (extAuth != null) {
                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;
                            // DEBUG only
                            // log.debug("##### The right login page was found");
                            break;
                        } else {
                            // log.debug("#### This is not the login page you're looking for");
                        }
                    } else {
                        // log.debug("### This context has no login page set");
                    }
                }
            }
            if (loginUrl) {
                // log.debug("##### Raising auth bypass");
                // raise the alert, using the custom name and description
                String vulnname = Constant.messages.getString(MESSAGE_PREFIX + "authbypass.name");
                String vulndesc = Constant.messages.getString(MESSAGE_PREFIX + "authbypass.desc");

                // raise the alert, using the attack string stored earlier for this purpose
                bingo(Alert.RISK_HIGH, Alert.CONFIDENCE_MEDIUM, vulnname, vulndesc,
                        refreshedmessage.getRequestHeader().getURI().getURI(), // url
                        param, sqlInjectionAttack, "", getSolution(), "", getBaseMsg());
            } // not a login page
        } // no sql Injection Found For Url

    } catch (InvalidRedirectLocationException e) {
        // Not an error, just means we probably attacked the redirect location
    } 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 SQL Injection vulnerabilities", e);
    }
}

From source file:org.zaproxy.zap.extension.ascanrules.TestSQLInjection.java

private void expressionBasedAttack(String param, String originalParam, String modifiedParamValue,
        String modifiedParamValueConfirm) throws IOException {
    // those of you still paying attention will note that if handled as expressions (such as by
    // a database), these represent the same value.
    HttpMessage msg = getNewMsg();/*from  ww  w.  j av  a 2  s .com*/
    setParameter(msg, param, modifiedParamValue);

    try {
        sendAndReceive(msg, false); // do not follow redirects
    } catch (SocketException ex) {
        if (log.isDebugEnabled())
            log.debug("Caught " + ex.getClass().getName() + " " + ex.getMessage() + " when accessing: "
                    + msg.getRequestHeader().getURI().toString());
        return; // Something went wrong, no point continuing
    }
    countExpressionBasedRequests++;

    String modifiedExpressionOutputUnstripped = msg.getResponseBody().toString();
    String modifiedExpressionOutputStripped = stripOffOriginalAndAttackParam(modifiedExpressionOutputUnstripped,
            originalParam, modifiedParamValue);
    String normalBodyOutput = mResBodyNormalStripped;

    if (!sqlInjectionFoundForUrl && countExpressionBasedRequests < doExpressionMaxRequests) {
        // if the results of the modified request match the original query, we may be onto
        // something.
        if (modifiedExpressionOutputStripped.compareTo(normalBodyOutput) == 0) {
            if (this.debugEnabled) {
                log.debug("Check 4, STRIPPED html output for modified expression parameter ["
                        + modifiedParamValue + "] matched (refreshed) original results for "
                        + refreshedmessage.getRequestHeader().getURI().toString());
            }
            // confirm that a different parameter value generates different output, to minimise
            // false positives
            // this time param value will be different to original value and mismatch is
            // expected in responses of original and this value
            // Note that the two values are NOT equivalent, and the param value is different to
            // the original
            HttpMessage msgConfirm = getNewMsg();
            setParameter(msgConfirm, param, modifiedParamValueConfirm);

            try {
                sendAndReceive(msgConfirm, false); // do not follow redirects
            } catch (SocketException ex) {
                if (log.isDebugEnabled())
                    log.debug("Caught " + ex.getClass().getName() + " " + ex.getMessage() + " when accessing: "
                            + msgConfirm.getRequestHeader().getURI().toString());
                return; // Something went wrong
            }
            countExpressionBasedRequests++;

            String confirmExpressionOutputUnstripped = msgConfirm.getResponseBody().toString();
            String confirmExpressionOutputStripped = stripOffOriginalAndAttackParam(
                    confirmExpressionOutputUnstripped, originalParam, modifiedParamValueConfirm);

            if (confirmExpressionOutputStripped.compareTo(normalBodyOutput) != 0) {
                // the confirm query did not return the same results.  This means that arbitrary
                // queries are not all producing the same page output.
                // this means the fact we earier reproduced the original page output with a
                // modified parameter was not a coincidence

                // Likely a SQL Injection. Raise it
                String extraInfo = Constant.messages
                        .getString(MESSAGE_PREFIX + "alert.expressionbased.extrainfo", modifiedParamValue, "");

                // raise the alert, and save the attack string for the "Authentication Bypass"
                // alert, if necessary
                sqlInjectionAttack = modifiedParamValue;
                bingo(Alert.RISK_HIGH, Alert.CONFIDENCE_MEDIUM, getName(), getDescription(), null, // url
                        param, sqlInjectionAttack, extraInfo, getSolution(), "", msg);
                // SQL Injection has been found
                sqlInjectionFoundForUrl = true;
                return;
            }
        }
        // bale out if we were asked nicely
        if (isStop()) {
            return;
        }
    }
}

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

/**
 * scans for SQL Injection vulnerabilities, using Hypersonic specific syntax. If it doesn't use
 * specifically Hypersonic syntax, it does not belong in here, but in SQLInjection
 *//*from w  w w.j a  va2s  .com*/
@Override
public void scan(HttpMessage originalMessage, String paramName, String paramValue) {

    // DEBUG only
    // log.setLevel(org.apache.log4j.Level.DEBUG);
    // this.debugEnabled = true;

    try {
        // Timing Baseline check: we need to get the time that it took the original query, to
        // know if the time based check is working correctly..
        HttpMessage msgTimeBaseline = getNewMsg();
        long originalTimeStarted = System.currentTimeMillis();
        try {
            sendAndReceive(msgTimeBaseline, false); // do not follow redirects
        } catch (java.net.SocketTimeoutException e) {
            // to be expected occasionally, if the base query was one that contains some
            // parameters exploiting time based SQL injection?
            if (this.debugEnabled)
                log.debug("The Base Time Check timed out on [" + msgTimeBaseline.getRequestHeader().getMethod()
                        + "] URL [" + msgTimeBaseline.getRequestHeader().getURI().getURI() + "]");
        } catch (SocketException ex) {
            if (this.debugEnabled)
                log.debug("Caught " + ex.getClass().getName() + " " + ex.getMessage() + " when accessing: "
                        + msgTimeBaseline.getRequestHeader().getURI().toString() + " for Base Time Check");
            return; // No need to keep going
        }
        long originalTimeUsed = System.currentTimeMillis() - originalTimeStarted;
        // end of timing baseline check

        int countUnionBasedRequests = 0;
        int countTimeBasedRequests = 0;

        if (this.debugEnabled)
            log.debug("Scanning URL [" + getBaseMsg().getRequestHeader().getMethod() + "] ["
                    + getBaseMsg().getRequestHeader().getURI() + "], field [" + paramName + "] with value ["
                    + paramValue + "] for SQL Injection");

        // Hypersonic specific time based SQL injection checks
        for (int timeBasedSQLindex = 0; timeBasedSQLindex < SQL_HYPERSONIC_TIME_REPLACEMENTS.length
                && doTimeBased && countTimeBasedRequests < doTimeMaxRequests; timeBasedSQLindex++) {
            HttpMessage msgAttack = getNewMsg();
            String newTimeBasedInjectionValue = SQL_HYPERSONIC_TIME_REPLACEMENTS[timeBasedSQLindex]
                    .replace(ORIG_VALUE_TOKEN, paramValue).replace(SLEEP_TOKEN, Integer.toString(sleep));

            setParameter(msgAttack, paramName, newTimeBasedInjectionValue);

            // send it.
            long modifiedTimeStarted = System.currentTimeMillis();
            try {
                sendAndReceive(msgAttack, false); // do not follow redirects
                countTimeBasedRequests++;
            } catch (java.net.SocketTimeoutException e) {
                // this is to be expected, if we start sending slow queries to the database.
                // ignore it in this case.. and just get the time.
                if (this.debugEnabled)
                    log.debug("The time check query timed out on ["
                            + msgTimeBaseline.getRequestHeader().getMethod() + "] URL ["
                            + msgTimeBaseline.getRequestHeader().getURI().getURI() + "] on field: [" + paramName
                            + "]");
            } catch (SocketException ex) {
                if (this.debugEnabled)
                    log.debug("Caught " + ex.getClass().getName() + " " + ex.getMessage() + " when accessing: "
                            + msgTimeBaseline.getRequestHeader().getURI().toString() + " for time check query");
                return; // No need to keep going
            }
            long modifiedTimeUsed = System.currentTimeMillis() - modifiedTimeStarted;

            if (this.debugEnabled)
                log.debug("Time Based SQL Injection test: [" + newTimeBasedInjectionValue + "] on field: ["
                        + paramName + "] with value [" + newTimeBasedInjectionValue + "] took "
                        + modifiedTimeUsed + "ms, where the original took " + originalTimeUsed + "ms");

            if (modifiedTimeUsed >= (originalTimeUsed + sleep)) {
                // takes more than 5 (by default) extra seconds => likely time based SQL
                // injection. Raise it
                String extraInfo = Constant.messages.getString(
                        "ascanbeta.sqlinjection.alert.timebased.extrainfo", newTimeBasedInjectionValue,
                        modifiedTimeUsed, paramValue, originalTimeUsed);
                String attack = Constant.messages.getString("ascanbeta.sqlinjection.alert.booleanbased.attack",
                        paramName, newTimeBasedInjectionValue);

                // raise the alert
                bingo(Alert.RISK_HIGH, Alert.CONFIDENCE_MEDIUM, getName() + " - Time Based", getDescription(),
                        getBaseMsg().getRequestHeader().getURI().getURI(), // url
                        paramName, attack, extraInfo, getSolution(), msgAttack);

                if (log.isDebugEnabled()) {
                    log.debug("A likely Time Based SQL Injection Vulnerability has been found with ["
                            + msgAttack.getRequestHeader().getMethod() + "] URL ["
                            + msgAttack.getRequestHeader().getURI().getURI() + "] on field: [" + paramName
                            + "]");
                }
                return;
            } // query took longer than the amount of time we attempted to retard it by
        } // for each time based SQL index
          // end of check for time based SQL Injection

    } catch (InvalidRedirectLocationException e) {
        // Not an error, just means we probably attacked the redirect location
    } 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 Hypersonic SQL Injection vulnerabilities", e);
    }
}

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

/**
 * scans for SQL Injection vulnerabilities, using MySQL specific syntax. If it doesn't use
 * specifically MySQL syntax, it does not belong in here, but in TestSQLInjection
 *///from   w  w w.jav  a2 s. c o m
@Override
public void scan(HttpMessage originalMessage, String paramName, String originalParamValue) {

    try {
        // Timing Baseline check: we need to get the time that it took the original query, to
        // know if the time based check is working correctly..
        HttpMessage msgTimeBaseline = getNewMsg();
        long originalTimeStarted = System.currentTimeMillis();
        try {
            sendAndReceive(msgTimeBaseline, false); // do not follow redirects
        } catch (java.net.SocketTimeoutException e) {
            // to be expected occasionally, if the base query was one that contains some
            // parameters exploiting time based SQL injection?
            if (this.debugEnabled)
                log.debug("The Base Time Check timed out on [" + msgTimeBaseline.getRequestHeader().getMethod()
                        + "] URL [" + msgTimeBaseline.getRequestHeader().getURI().getURI() + "]");
        } catch (SocketException ex) {
            if (this.debugEnabled)
                log.debug("Caught " + ex.getClass().getName() + " " + ex.getMessage() + " when accessing: "
                        + msgTimeBaseline.getRequestHeader().getURI().toString());
            return; // No need to keep going
        }
        long originalTimeUsed = System.currentTimeMillis() - originalTimeStarted;
        // if the time was very slow (because JSP was being compiled on first call, for
        // instance)
        // then the rest of the time based logic will fail.  Lets double-check for that scenario
        // by requesting the url again.
        // If it comes back in a more reasonable time, we will use that time instead as our
        // baseline.  If it come out in a slow fashion again,
        // we will abort the check on this URL, since we will only spend lots of time trying
        // request, when we will (very likely) not get positive results.
        if (originalTimeUsed > sleep * 1000) {
            long originalTimeStarted2 = System.currentTimeMillis();
            try {
                sendAndReceive(msgTimeBaseline, false); // do not follow redirects
            } catch (java.net.SocketTimeoutException e) {
                // to be expected occasionally, if the base query was one that contains some
                // parameters exploiting time based SQL injection?
                if (this.debugEnabled)
                    log.debug(
                            "Base Time Check 2 timed out on [" + msgTimeBaseline.getRequestHeader().getMethod()
                                    + "] URL [" + msgTimeBaseline.getRequestHeader().getURI().getURI() + "]");
            } catch (SocketException ex) {
                if (this.debugEnabled)
                    log.debug("Caught " + ex.getClass().getName() + " " + ex.getMessage() + " when accessing: "
                            + msgTimeBaseline.getRequestHeader().getURI().toString());
                return; // No need to keep going
            }
            long originalTimeUsed2 = System.currentTimeMillis() - originalTimeStarted2;
            if (originalTimeUsed2 > sleep * 1000) {
                // no better the second time around.  we need to bale out.
                if (this.debugEnabled)
                    log.debug("Both base time checks 1 and 2 for ["
                            + msgTimeBaseline.getRequestHeader().getMethod() + "] URL ["
                            + msgTimeBaseline.getRequestHeader().getURI().getURI()
                            + "] are way too slow to be usable for the purposes of checking for time based SQL Injection checking.  We are aborting the check on this particular url.");
                return;
            } else {
                // phew.  the second time came in within the limits. use the later timing
                // details as the base time for the checks.
                originalTimeUsed = originalTimeUsed2;
                originalTimeStarted = originalTimeStarted2;
            }
        }
        // end of timing baseline check

        int countTimeBasedRequests = 0;

        if (this.debugEnabled)
            log.debug("Scanning URL [" + getBaseMsg().getRequestHeader().getMethod() + "] ["
                    + getBaseMsg().getRequestHeader().getURI() + "], [" + paramName + "] with value ["
                    + originalParamValue + "] for SQL Injection");

        // MySQL specific time-based SQL injection checks
        for (int timeBasedSQLindex = 0; timeBasedSQLindex < SQL_MYSQL_TIME_REPLACEMENTS.length && doTimeBased
                && countTimeBasedRequests < doTimeMaxRequests; timeBasedSQLindex++) {
            HttpMessage msg3 = getNewMsg();
            String newTimeBasedInjectionValue = SQL_MYSQL_TIME_REPLACEMENTS[timeBasedSQLindex]
                    .replace(ORIG_VALUE_TOKEN, originalParamValue)
                    .replace(SLEEP_TOKEN, Integer.toString(sleep));
            setParameter(msg3, paramName, newTimeBasedInjectionValue);

            // send it.
            long modifiedTimeStarted = System.currentTimeMillis();
            try {
                sendAndReceive(msg3, false); // do not follow redirects
                countTimeBasedRequests++;
            } catch (java.net.SocketTimeoutException e) {
                // to be expected occasionally, if the contains some parameters exploiting time
                // based SQL injection
                if (this.debugEnabled)
                    log.debug("The time check query timed out on [" + msg3.getRequestHeader().getMethod()
                            + "] URL [" + msg3.getRequestHeader().getURI().getURI() + "] on field: ["
                            + paramName + "]");
            } catch (SocketException ex) {
                if (this.debugEnabled)
                    log.debug("Caught " + ex.getClass().getName() + " " + ex.getMessage() + " when accessing: "
                            + msg3.getRequestHeader().getURI().toString());
                return; // No need to keep going
            }
            long modifiedTimeUsed = System.currentTimeMillis() - modifiedTimeStarted;

            if (this.debugEnabled)
                log.debug("Time Based SQL Injection test: [" + newTimeBasedInjectionValue + "] on field: ["
                        + paramName + "] with value [" + newTimeBasedInjectionValue + "] took "
                        + modifiedTimeUsed + "ms, where the original took " + originalTimeUsed + "ms");

            // add some small leeway on the time, since adding a 5 (by default) second delay in
            // the SQL query will not cause the request
            // to take a full 5 (by default) seconds longer to run than the original..
            if (modifiedTimeUsed >= (originalTimeUsed + (sleep * 1000) - 200)) {
                // takes more than 5 (by default) extra seconds => likely time based SQL
                // injection. Raise it

                // Likely a SQL Injection. Raise it
                String extraInfo = Constant.messages.getString(
                        "ascanbeta.sqlinjection.alert.timebased.extrainfo", newTimeBasedInjectionValue,
                        modifiedTimeUsed, originalParamValue, originalTimeUsed);

                // raise the alert
                bingo(Alert.RISK_HIGH, Alert.CONFIDENCE_MEDIUM, getName(), getDescription(),
                        getBaseMsg().getRequestHeader().getURI().getURI(), // url
                        paramName, newTimeBasedInjectionValue, extraInfo, getSolution(), msg3);

                if (this.debugEnabled)
                    log.debug("A likely Time Based SQL Injection Vulnerability has been found with ["
                            + msg3.getRequestHeader().getMethod() + "] URL ["
                            + msg3.getRequestHeader().getURI().getURI() + "] on field: [" + paramName + "]");

                return;
            } // query took longer than the amount of time we attempted to retard it by
              // bale out if we were asked nicely
            if (isStop()) {
                if (this.debugEnabled)
                    log.debug("Stopping the scan due to a user request");
                return;
            }
        } // for each time based SQL index
          // end of check for MySQL time based SQL Injection

    } catch (InvalidRedirectLocationException e) {
        // Not an error, just means we probably attacked the redirect location
    } 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 MySQL SQL Injection vulnerabilities", e);
    }
}

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

/**
 * scans for SQL Injection vulnerabilities, using POSTGRES specific syntax. If it doesn't use
 * specifically POSTGRES syntax, it does not belong in here, but in SQLInjection
 *///w w  w  .  j a  va 2  s . c  om
@Override
public void scan(HttpMessage originalMessage, String paramName, String paramValue) {

    // DEBUG only
    // log.setLevel(org.apache.log4j.Level.DEBUG);
    // this.debugEnabled = true;

    try {
        // Timing Baseline check: we need to get the time that it took the original query, to
        // know if the time based check is working correctly..
        HttpMessage msgTimeBaseline = getNewMsg();
        long originalTimeStarted = System.currentTimeMillis();
        try {
            sendAndReceive(msgTimeBaseline, false); // do not follow redirects
        } catch (java.net.SocketTimeoutException e) {
            // to be expected occasionally, if the base query was one that contains some
            // parameters exploiting time based SQL injection?
            if (this.debugEnabled)
                log.debug("The Base Time Check timed out on [" + msgTimeBaseline.getRequestHeader().getMethod()
                        + "] URL [" + msgTimeBaseline.getRequestHeader().getURI().getURI() + "]");
        } catch (SocketException ex) {
            if (this.debugEnabled)
                log.debug("Caught " + ex.getClass().getName() + " " + ex.getMessage() + " when accessing: "
                        + msgTimeBaseline.getRequestHeader().getURI().toString());
            return; // No need to keep going
        }
        long originalTimeUsed = System.currentTimeMillis() - originalTimeStarted;
        // end of timing baseline check

        int countTimeBasedRequests = 0;

        if (this.debugEnabled)
            log.debug("Scanning URL [" + getBaseMsg().getRequestHeader().getMethod() + "] ["
                    + getBaseMsg().getRequestHeader().getURI() + "], field [" + paramName
                    + "] with original value [" + paramValue + "] for SQL Injection");

        // POSTGRES specific time based SQL injection checks
        for (int timeBasedSQLindex = 0; timeBasedSQLindex < SQL_POSTGRES_TIME_REPLACEMENTS.length && doTimeBased
                && countTimeBasedRequests < doTimeMaxRequests; timeBasedSQLindex++) {
            HttpMessage msgAttack = getNewMsg();
            String newTimeBasedInjectionValue = SQL_POSTGRES_TIME_REPLACEMENTS[timeBasedSQLindex]
                    .replace(ORIG_VALUE_TOKEN, paramValue).replace(SLEEP_TOKEN, Integer.toString(sleep));

            setParameter(msgAttack, paramName, newTimeBasedInjectionValue);

            // send it.
            long modifiedTimeStarted = System.currentTimeMillis();
            try {
                sendAndReceive(msgAttack, false); // do not follow redirects
                countTimeBasedRequests++;
            } catch (java.net.SocketTimeoutException e) {
                // this is to be expected, if we start sending slow queries to the database.
                // ignore it in this case.. and just get the time.
                if (this.debugEnabled)
                    log.debug("The time check query timed out on ["
                            + msgTimeBaseline.getRequestHeader().getMethod() + "] URL ["
                            + msgTimeBaseline.getRequestHeader().getURI().getURI() + "] on field: [" + paramName
                            + "]");
            } catch (SocketException ex) {
                if (this.debugEnabled)
                    log.debug("Caught " + ex.getClass().getName() + " " + ex.getMessage() + " when accessing: "
                            + msgTimeBaseline.getRequestHeader().getURI().toString());
                return; // No need to keep going
            }
            long modifiedTimeUsed = System.currentTimeMillis() - modifiedTimeStarted;

            if (this.debugEnabled)
                log.debug("Time Based SQL Injection test: [" + newTimeBasedInjectionValue + "] on field: ["
                        + paramName + "] with value [" + newTimeBasedInjectionValue + "] took "
                        + modifiedTimeUsed + "ms, where the original took " + originalTimeUsed + "ms");

            if (modifiedTimeUsed >= (originalTimeUsed + (sleep * 1000))) {
                // takes more than 5 (by default) extra seconds => likely time based SQL
                // injection. Raise it
                String extraInfo = Constant.messages.getString(
                        "ascanbeta.sqlinjection.alert.timebased.extrainfo", newTimeBasedInjectionValue,
                        modifiedTimeUsed, paramValue, originalTimeUsed);
                String attack = Constant.messages.getString("ascanbeta.sqlinjection.alert.booleanbased.attack",
                        paramName, newTimeBasedInjectionValue);

                // raise the alert
                bingo(Alert.RISK_HIGH, Alert.CONFIDENCE_MEDIUM, getName() + " - Time Based", getDescription(),
                        getBaseMsg().getRequestHeader().getURI().getURI(), // url
                        paramName, attack, extraInfo, getSolution(), msgAttack);

                if (log.isDebugEnabled()) {
                    log.debug("A likely Time Based SQL Injection Vulnerability has been found with ["
                            + msgAttack.getRequestHeader().getMethod() + "] URL ["
                            + msgAttack.getRequestHeader().getURI().getURI() + "] on field: [" + paramName
                            + "]");
                }
                return;
            } // query took longer than the amount of time we attempted to retard it by
        } // for each time based SQL index
          // end of check for time based SQL Injection

    } catch (InvalidRedirectLocationException e) {
        // Not an error, just means we probably attacked the redirect location
    } 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 POSTGRES SQL Injection vulnerabilities", e);
    }
}