Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sqliplugin: Use Comparable Response instead of Response Matcher #5253

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions addOns/sqliplugin/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Changed
- Update minimum ZAP version to 2.14.0.
- Maintenance changes.
- Changed ResponseMatcher to ComparableResponse for assessing similarity between responses.

## [15] - 2021-10-20
### Fixed
Expand Down
2 changes: 1 addition & 1 deletion addOns/sqliplugin/sqliplugin.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ crowdin {

dependencies {
zapAddOn("commonlib")

testImplementation(project(":testutils"))
implementation("org.jdom:jdom:2.0.2")
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.parosproxy.paros.network.HttpHeader;
import org.parosproxy.paros.network.HttpMessage;
import org.zaproxy.addon.commonlib.CommonAlertTag;
import org.zaproxy.addon.commonlib.http.ComparableResponse;
import org.zaproxy.zap.model.Tech;
import org.zaproxy.zap.model.TechSet;

Expand All @@ -54,6 +55,10 @@ public class SQLInjectionScanRule extends AbstractAppParamPlugin {
// ------------------------------------------------------------------
// Plugin Constants
// ------------------------------------------------------------------

// Similarity ratio required to determine if two responses are similar (used in boolean based
// blind check)
private static final double REQUIRED_SIMILARITY = 0.90;
// Coefficient used for a time-based query delay checking (must be >= 7)
public static final int TIME_STDEV_COEFF = 7;
// Standard deviation after which a warning message should be displayed about connection lags
Expand Down Expand Up @@ -114,7 +119,7 @@ public class SQLInjectionScanRule extends AbstractAppParamPlugin {
private static final Pattern randstrPattern = Pattern.compile("\\[RANDSTR(?:\\d+)?\\]");

// Internal dynamic properties
private final ResponseMatcher responseMatcher;
private ComparableResponse originalResponse;
private final List<Long> responseTimes;

private int lastRequestUID;
Expand All @@ -131,7 +136,6 @@ public class SQLInjectionScanRule extends AbstractAppParamPlugin {
*/
public SQLInjectionScanRule() {
responseTimes = new ArrayList<>();
responseMatcher = new ResponseMatcher();
lastRequestUID = 0;
lastErrorPageUID = -1;
}
Expand Down Expand Up @@ -601,6 +605,7 @@ public void scan(HttpMessage msg, String parameter, String value) {
// each plugin execution, then we have to work
// with message copies and use getNewMsg()
origMsg = getBaseMsg();
originalResponse = new ComparableResponse(origMsg, null);

// Threat the parameter original value according to the
// test's <where> tag
Expand Down Expand Up @@ -639,7 +644,7 @@ public void scan(HttpMessage msg, String parameter, String value) {
// exit the plugin
return;
}

originalResponse = new ComparableResponse(origMsg, payloadValue);
break;

case SQLiPayloadManager.WHERE_REPLACE:
Expand Down Expand Up @@ -678,12 +683,10 @@ public void scan(HttpMessage msg, String parameter, String value) {

// prepare string diff matcher
// cleaned by reflective values
// and according to the replacement
// logic set by the plugin
content = origMsg.getResponseBody().toString();
content = SQLiPayloadManager.removeReflectiveValues(content, payloadValue);
responseMatcher.setOriginalResponse(content);
responseMatcher.setLogic(where);
origMsg.setResponseBody(content);
originalResponse = new ComparableResponse(origMsg, payloadValue);

// -----------------------------------------------
// Check 1: Boolean-based blind SQL injection
Expand All @@ -710,7 +713,7 @@ public void scan(HttpMessage msg, String parameter, String value) {
cmpPayload = payloadValue + cmpPayload;

// Send False payload
// Useful to set first matchRatio on
// Useful to tune original to
// the False response content
tempMsg = sendPayload(parameter, cmpPayload, true);
if (tempMsg == null) {
Expand All @@ -722,9 +725,11 @@ public void scan(HttpMessage msg, String parameter, String value) {
content = tempMsg.getResponseBody().toString();
content =
SQLiPayloadManager.removeReflectiveValues(content, cmpPayload);
responseMatcher.setInjectedResponse(content);
// set initial matchRatio
responseMatcher.isComparable();
tempMsg.setResponseBody(content);

ComparableResponse tempResponse =
new ComparableResponse(tempMsg, cmpPayload);
originalResponse.tuneHeuristicsWithResponse(tempResponse);

// Perform the test's True request
tempMsg = sendPayload(parameter, reqPayload, true);
Expand All @@ -737,13 +742,12 @@ public void scan(HttpMessage msg, String parameter, String value) {
content = tempMsg.getResponseBody().toString();
content =
SQLiPayloadManager.removeReflectiveValues(content, reqPayload);
responseMatcher.setInjectedResponse(content);

tempMsg.setResponseBody(content);
tempResponse = new ComparableResponse(tempMsg, reqPayload);
// Check if the TRUE response is equal or
// at less strongly comparable respect to
// the Original response value
if (responseMatcher.isComparable()) {

if (this.isComparableToOriginal(tempResponse)) {
// Perform again the test's False request
tempMsg = sendPayload(parameter, cmpPayload, true);
if (tempMsg == null) {
Expand All @@ -756,13 +760,14 @@ public void scan(HttpMessage msg, String parameter, String value) {
content =
SQLiPayloadManager.removeReflectiveValues(
content, cmpPayload);
responseMatcher.setInjectedResponse(content);
tempMsg.setResponseBody(content);
tempResponse = new ComparableResponse(tempMsg, cmpPayload);

// Now check if the FALSE response is
// completely different from the
// Original response according to the
// responseMatcher ratio criteria
if (!responseMatcher.isComparable()) {
// required ratio criteria
if (!this.isComparableToOriginal(tempResponse)) {
// We Found IT!
// Now create the alert message
String info =
Expand Down Expand Up @@ -1195,20 +1200,26 @@ public boolean wasLastRequestDBMSError() {
* @param pageContent the content that need to be compared
* @return true if similar, false otherwise
*/
protected boolean isComparableToOriginal(String pageContent) {
responseMatcher.setInjectedResponse(pageContent);
return responseMatcher.isComparable();
}

/**
* Get the page comparison ration against the original content
*
* @param pageContent the content that need to be compared
* @param ComparableResponse that need to be compared
* @return a ratio value for this comparison
*/
protected double compareToOriginal(String pageContent) {
responseMatcher.setInjectedResponse(pageContent);
return responseMatcher.getQuickRatio();
protected double compareToOriginal(ComparableResponse response) {
return originalResponse.compareWith(response);
}

/**
* Check if the response is similar to original Response
*
* @param ComparableResponse that need to be compared
* @return boolean indicating similar (true) or not similar(false)
*/
protected boolean isComparableToOriginal(ComparableResponse otherResponse) {
float similarity = originalResponse.compareWith(otherResponse);
return similarity >= REQUIRED_SIMILARITY;
}

/**
Expand Down
Loading
Loading