3838import org .parosproxy .paros .network .HttpHeader ;
3939import org .parosproxy .paros .network .HttpMessage ;
4040import org .zaproxy .addon .commonlib .CommonAlertTag ;
41+ import org .zaproxy .addon .commonlib .http .ComparableResponse ;
4142import org .zaproxy .zap .model .Tech ;
4243import org .zaproxy .zap .model .TechSet ;
4344
@@ -54,6 +55,10 @@ public class SQLInjectionScanRule extends AbstractAppParamPlugin {
5455 // ------------------------------------------------------------------
5556 // Plugin Constants
5657 // ------------------------------------------------------------------
58+
59+ // Similarity ratio required to determine if two responses are similar (used in boolean based
60+ // blind check)
61+ private static final double REQUIRED_SIMILARITY = 0.90 ;
5762 // Coefficient used for a time-based query delay checking (must be >= 7)
5863 public static final int TIME_STDEV_COEFF = 7 ;
5964 // Standard deviation after which a warning message should be displayed about connection lags
@@ -114,7 +119,7 @@ public class SQLInjectionScanRule extends AbstractAppParamPlugin {
114119 private static final Pattern randstrPattern = Pattern .compile ("\\ [RANDSTR(?:\\ d+)?\\ ]" );
115120
116121 // Internal dynamic properties
117- private final ResponseMatcher responseMatcher ;
122+ private ComparableResponse originalResponse ;
118123 private final List <Long > responseTimes ;
119124
120125 private int lastRequestUID ;
@@ -131,7 +136,6 @@ public class SQLInjectionScanRule extends AbstractAppParamPlugin {
131136 */
132137 public SQLInjectionScanRule () {
133138 responseTimes = new ArrayList <>();
134- responseMatcher = new ResponseMatcher ();
135139 lastRequestUID = 0 ;
136140 lastErrorPageUID = -1 ;
137141 }
@@ -601,6 +605,7 @@ public void scan(HttpMessage msg, String parameter, String value) {
601605 // each plugin execution, then we have to work
602606 // with message copies and use getNewMsg()
603607 origMsg = getBaseMsg ();
608+ originalResponse = new ComparableResponse (origMsg , null );
604609
605610 // Threat the parameter original value according to the
606611 // test's <where> tag
@@ -639,7 +644,7 @@ public void scan(HttpMessage msg, String parameter, String value) {
639644 // exit the plugin
640645 return ;
641646 }
642-
647+ originalResponse = new ComparableResponse ( origMsg , payloadValue );
643648 break ;
644649
645650 case SQLiPayloadManager .WHERE_REPLACE :
@@ -678,12 +683,10 @@ public void scan(HttpMessage msg, String parameter, String value) {
678683
679684 // prepare string diff matcher
680685 // cleaned by reflective values
681- // and according to the replacement
682- // logic set by the plugin
683686 content = origMsg .getResponseBody ().toString ();
684687 content = SQLiPayloadManager .removeReflectiveValues (content , payloadValue );
685- responseMatcher . setOriginalResponse (content );
686- responseMatcher . setLogic ( where );
688+ origMsg . setResponseBody (content );
689+ originalResponse = new ComparableResponse ( origMsg , payloadValue );
687690
688691 // -----------------------------------------------
689692 // Check 1: Boolean-based blind SQL injection
@@ -710,7 +713,7 @@ public void scan(HttpMessage msg, String parameter, String value) {
710713 cmpPayload = payloadValue + cmpPayload ;
711714
712715 // Send False payload
713- // Useful to set first matchRatio on
716+ // Useful to tune original to
714717 // the False response content
715718 tempMsg = sendPayload (parameter , cmpPayload , true );
716719 if (tempMsg == null ) {
@@ -722,9 +725,11 @@ public void scan(HttpMessage msg, String parameter, String value) {
722725 content = tempMsg .getResponseBody ().toString ();
723726 content =
724727 SQLiPayloadManager .removeReflectiveValues (content , cmpPayload );
725- responseMatcher .setInjectedResponse (content );
726- // set initial matchRatio
727- responseMatcher .isComparable ();
728+ tempMsg .setResponseBody (content );
729+
730+ ComparableResponse tempResponse =
731+ new ComparableResponse (tempMsg , cmpPayload );
732+ originalResponse .tuneHeuristicsWithResponse (tempResponse );
728733
729734 // Perform the test's True request
730735 tempMsg = sendPayload (parameter , reqPayload , true );
@@ -737,13 +742,12 @@ public void scan(HttpMessage msg, String parameter, String value) {
737742 content = tempMsg .getResponseBody ().toString ();
738743 content =
739744 SQLiPayloadManager .removeReflectiveValues (content , reqPayload );
740- responseMatcher . setInjectedResponse (content );
741-
745+ tempMsg . setResponseBody (content );
746+ tempResponse = new ComparableResponse ( tempMsg , reqPayload );
742747 // Check if the TRUE response is equal or
743748 // at less strongly comparable respect to
744749 // the Original response value
745- if (responseMatcher .isComparable ()) {
746-
750+ if (this .isComparableToOriginal (tempResponse )) {
747751 // Perform again the test's False request
748752 tempMsg = sendPayload (parameter , cmpPayload , true );
749753 if (tempMsg == null ) {
@@ -756,13 +760,14 @@ public void scan(HttpMessage msg, String parameter, String value) {
756760 content =
757761 SQLiPayloadManager .removeReflectiveValues (
758762 content , cmpPayload );
759- responseMatcher .setInjectedResponse (content );
763+ tempMsg .setResponseBody (content );
764+ tempResponse = new ComparableResponse (tempMsg , cmpPayload );
760765
761766 // Now check if the FALSE response is
762767 // completely different from the
763768 // Original response according to the
764- // responseMatcher ratio criteria
765- if (!responseMatcher . isComparable ( )) {
769+ // required ratio criteria
770+ if (!this . isComparableToOriginal ( tempResponse )) {
766771 // We Found IT!
767772 // Now create the alert message
768773 String info =
@@ -1195,20 +1200,26 @@ public boolean wasLastRequestDBMSError() {
11951200 * @param pageContent the content that need to be compared
11961201 * @return true if similar, false otherwise
11971202 */
1198- protected boolean isComparableToOriginal (String pageContent ) {
1199- responseMatcher .setInjectedResponse (pageContent );
1200- return responseMatcher .isComparable ();
1201- }
12021203
12031204 /**
12041205 * Get the page comparison ration against the original content
12051206 *
1206- * @param pageContent the content that need to be compared
1207+ * @param ComparableResponse that need to be compared
12071208 * @return a ratio value for this comparison
12081209 */
1209- protected double compareToOriginal (String pageContent ) {
1210- responseMatcher .setInjectedResponse (pageContent );
1211- return responseMatcher .getQuickRatio ();
1210+ protected double compareToOriginal (ComparableResponse response ) {
1211+ return originalResponse .compareWith (response );
1212+ }
1213+
1214+ /**
1215+ * Check if the response is similar to original Response
1216+ *
1217+ * @param ComparableResponse that need to be compared
1218+ * @return boolean indicating similar (true) or not similar(false)
1219+ */
1220+ protected boolean isComparableToOriginal (ComparableResponse otherResponse ) {
1221+ float similarity = originalResponse .compareWith (otherResponse );
1222+ return similarity >= REQUIRED_SIMILARITY ;
12121223 }
12131224
12141225 /**
0 commit comments