Skip to content

Commit 7897ddb

Browse files
committed
ascanrules: SQLi SQLite split timing tests to new scan rule
Signed-off-by: kingthorin <kingthorin@users.noreply.github.com>
1 parent f5929ec commit 7897ddb

File tree

6 files changed

+242
-410
lines changed

6 files changed

+242
-410
lines changed

addOns/ascanrules/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1212
- SQL Injection - MsSQL
1313
- SQL Injection - MySQL
1414
- SQL Injection - Hypersonic
15+
- SQL Injection - SQLite
1516
- SQL Injection - PostgreSQL
1617

1718
### Added
Lines changed: 8 additions & 284 deletions
Large diffs are not rendered by default.

addOns/ascanrules/src/main/javahelp/org/zaproxy/zap/extension/ascanrules/resources/help/contents/ascanrules.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -426,10 +426,10 @@ <H2 id="id-40022">SQL Injection - PostgreSQL (Time Based)</H2>
426426
<br>
427427
Alert ID: <a href="https://www.zaproxy.org/docs/alerts/40022/">40022</a>.
428428

429-
<H2 id="id-40024">SQL Injection - SQLite</H2>
430-
This active scan rule attempts to inject SQLite specific commands into parameter values and analyzes the server's responses to see if the commands were effectively executed on the server (indicating a successful SQL injection attack).
429+
<H2 id="id-40024">SQL Injection - SQLite (Time Based)</H2>
430+
This active scan rule attempts to inject SQLite specific commands into parameter values and analyzes the timing of server responses to see if the commands were effectively executed on the server (indicating a successful SQL injection attack).
431431
<p>
432-
Latest code: <a href="https://github.com/zaproxy/zap-extensions/blob/main/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/SqlInjectionSqLiteScanRule.java">SqlInjectionSqLiteScanRule.java</a>
432+
Latest code: <a href="https://github.com/zaproxy/zap-extensions/blob/main/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/SqlInjectionSqLiteTimingScanRule.java">SqlInjectionSqLiteTimingScanRule.java</a>
433433
<br>
434434
Alert ID: <a href="https://www.zaproxy.org/docs/alerts/40024/">40024</a>.
435435

addOns/ascanrules/src/main/resources/org/zaproxy/zap/extension/ascanrules/resources/Messages.properties

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,10 @@ ascanrules.sqlinjection.postgres.name = SQL Injection - PostgreSQL (Time Based)
190190
ascanrules.sqlinjection.refs = https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
191191
ascanrules.sqlinjection.soln = Do not trust client side input, even if there is client side validation in place.\nIn general, type check all data on the server side.\nIf the application uses JDBC, use PreparedStatement or CallableStatement, with parameters passed by '?'\nIf the application uses ASP, use ADO Command Objects with strong type checking and parameterized queries.\nIf database Stored Procedures can be used, use them.\nDo *not* concatenate strings into queries in the stored procedure, or use 'exec', 'exec immediate', or equivalent functionality!\nDo not create dynamic SQL queries using simple string concatenation.\nEscape all data received from the client.\nApply an 'allow list' of allowed characters, or a 'deny list' of disallowed characters in user input.\nApply the principle of least privilege by using the least privileged database user possible.\nIn particular, avoid using the 'sa' or 'db-owner' database users. This does not eliminate SQL injection, but minimizes its impact.\nGrant the minimum database access that is necessary for the application.
192192
ascanrules.sqlinjection.sqlite.alert.errorbased.extrainfo = The following known SQLite error message was provoked: [{0}].
193-
ascanrules.sqlinjection.sqlite.alert.timebased.extrainfo = The query time is controllable using parameter value [{0}], which caused the request to take [{1}] milliseconds, parameter value [{2}], which caused the request to take [{3}] milliseconds, when the original unmodified query with value [{4}] took [{5}] milliseconds.
193+
ascanrules.sqlinjection.sqlite.alert.timing.extrainfo = The query time is controllable using parameter value [{0}], which caused the request to take [{1}] milliseconds, parameter value [{2}], which caused the request to take [{3}] milliseconds, when the original unmodified query with value [{4}] took [{5}] milliseconds.
194194
ascanrules.sqlinjection.sqlite.alert.versionnumber.extrainfo = Using a UNION based SQL Injection attack, and by exploiting SQLite's dynamic typing mechanism, the SQLite version was determined to be [{0}].\nWith string-based injection points, full SQLite version information can be extracted, but with numeric injection points, only partial SQLite version information can be extracted.\nMore information on SQLite version [{0}] is available at https://www.sqlite.org/changes.html
195195
ascanrules.sqlinjection.sqlite.name = SQL Injection - SQLite
196+
ascanrules.sqlinjection.sqlite.timing.name = SQL Injection - SQLite (Time Based)
196197

197198
ascanrules.ssti.alert.otherinfo = Proof found at [{0}]\ncontent:\n[{1}]
198199
ascanrules.ssti.desc = When the user input is inserted in the template instead of being used as argument in rendering is evaluated by the template engine. Depending on the template engine it can lead to remote code execution.

addOns/ascanrules/src/test/java/org/zaproxy/zap/extension/ascanrules/SqlInjectionSQLiteScanRuleUnitTest.java

Lines changed: 4 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,17 @@
1919
*/
2020
package org.zaproxy.zap.extension.ascanrules;
2121

22-
import static fi.iki.elonen.NanoHTTPD.newFixedLengthResponse;
2322
import static org.hamcrest.MatcherAssert.assertThat;
2423
import static org.hamcrest.Matchers.equalTo;
25-
import static org.hamcrest.Matchers.hasKey;
2624
import static org.hamcrest.Matchers.is;
27-
import static org.hamcrest.Matchers.not;
28-
import static org.hamcrest.Matchers.startsWith;
2925

30-
import fi.iki.elonen.NanoHTTPD.IHTTPSession;
31-
import fi.iki.elonen.NanoHTTPD.Response;
3226
import java.util.Map;
3327
import org.junit.jupiter.api.Test;
34-
import org.parosproxy.paros.core.scanner.Alert;
3528
import org.parosproxy.paros.core.scanner.Plugin;
36-
import org.parosproxy.paros.network.HttpMessage;
3729
import org.zaproxy.addon.commonlib.CommonAlertTag;
3830
import org.zaproxy.addon.commonlib.PolicyTag;
3931
import org.zaproxy.zap.model.Tech;
4032
import org.zaproxy.zap.model.TechSet;
41-
import org.zaproxy.zap.testutils.NanoServerHandler;
4233

4334
/** Unit test for {@link SqlInjectionSqLiteScanRule}. */
4435
class SqlInjectionSQLiteScanRuleUnitTest extends ActiveScannerTest<SqlInjectionSqLiteScanRule> {
@@ -56,11 +47,11 @@ protected int getRecommendMaxNumberMessagesPerParam(Plugin.AttackStrength streng
5647
return NUMBER_MSGS_ATTACK_STRENGTH_LOW;
5748
case MEDIUM:
5849
default:
59-
return NUMBER_MSGS_ATTACK_STRENGTH_MEDIUM + 2;
50+
return NUMBER_MSGS_ATTACK_STRENGTH_MEDIUM;
6051
case HIGH:
61-
return NUMBER_MSGS_ATTACK_STRENGTH_HIGH + 10;
52+
return NUMBER_MSGS_ATTACK_STRENGTH_HIGH;
6253
case INSANE:
63-
return NUMBER_MSGS_ATTACK_STRENGTH_INSANE + 22;
54+
return NUMBER_MSGS_ATTACK_STRENGTH_INSANE + 126;
6455
}
6556
}
6657

@@ -84,114 +75,6 @@ void shouldNotTargetNonSqLiteSQLTechs() throws Exception {
8475
assertThat(targets, is(equalTo(false)));
8576
}
8677

87-
@Test
88-
void shouldAlertIfSqlErrorReturned() throws Exception {
89-
String test = "/shouldReportSqlErrorMessage/";
90-
91-
this.nano.addHandler(
92-
new NanoServerHandler(test) {
93-
@Override
94-
protected Response serve(IHTTPSession session) {
95-
String name = getFirstParamValue(session, "name");
96-
String response = "<html><body></body></html>";
97-
if (name != null && name.contains(" randomblob(")) {
98-
response =
99-
"<html><body>SQL error: no such function: randomblob</body></html>";
100-
}
101-
return newFixedLengthResponse(response);
102-
}
103-
});
104-
105-
HttpMessage msg = this.getHttpMessage(test + "?name=test");
106-
107-
this.rule.init(msg, this.parent);
108-
109-
this.rule.scan();
110-
111-
assertThat(alertsRaised.size(), equalTo(1));
112-
assertThat(alertsRaised.get(0).getEvidence(), equalTo("no such function: randomblob"));
113-
assertThat(alertsRaised.get(0).getParam(), equalTo("name"));
114-
assertThat(
115-
alertsRaised.get(0).getAttack(),
116-
equalTo("case randomblob(100000) when not null then 1 else 1 end "));
117-
assertThat(alertsRaised.get(0).getRisk(), equalTo(Alert.RISK_HIGH));
118-
assertThat(alertsRaised.get(0).getConfidence(), equalTo(Alert.CONFIDENCE_MEDIUM));
119-
assertThat(alertsRaised.get(0).getTags(), not(hasKey(CommonAlertTag.TEST_TIMING.getTag())));
120-
}
121-
122-
@Test
123-
void shouldAlertIfRandomBlobTimesGetLonger() throws Exception {
124-
String test = "/shouldReportSqlTimingIssue/";
125-
126-
this.nano.addHandler(
127-
new NanoServerHandler(test) {
128-
private int time = 100;
129-
130-
@Override
131-
protected Response serve(IHTTPSession session) {
132-
String name = getFirstParamValue(session, "name");
133-
String response = "<html><body></body></html>";
134-
if (name != null && name.contains(" randomblob(")) {
135-
try {
136-
Thread.sleep(time);
137-
} catch (InterruptedException e) {
138-
// Ignore
139-
}
140-
time += 100;
141-
}
142-
return newFixedLengthResponse(response);
143-
}
144-
});
145-
146-
HttpMessage msg = this.getHttpMessage(test + "?name=test");
147-
148-
this.rule.init(msg, this.parent);
149-
this.rule.setExpectedDelayInMs(90);
150-
151-
this.rule.scan();
152-
153-
assertThat(alertsRaised.size(), equalTo(1));
154-
assertThat(
155-
alertsRaised.get(0).getEvidence(),
156-
startsWith("The query time is controllable using parameter value"));
157-
assertThat(alertsRaised.get(0).getParam(), equalTo("name"));
158-
assertThat(alertsRaised.get(0).getAttack(), startsWith("case randomblob(100"));
159-
assertThat(alertsRaised.get(0).getRisk(), equalTo(Alert.RISK_HIGH));
160-
assertThat(alertsRaised.get(0).getConfidence(), equalTo(Alert.CONFIDENCE_MEDIUM));
161-
assertThat(alertsRaised.get(0).getTags(), is(hasKey(CommonAlertTag.TEST_TIMING.getTag())));
162-
}
163-
164-
@Test
165-
void shouldNotAlertIfAllTimesGetLonger() throws Exception {
166-
String test = "/shouldReportSqlTimingIssue/";
167-
168-
this.nano.addHandler(
169-
new NanoServerHandler(test) {
170-
private int time = 100;
171-
172-
@Override
173-
protected Response serve(IHTTPSession session) {
174-
String response = "<html><body></body></html>";
175-
try {
176-
Thread.sleep(time);
177-
} catch (InterruptedException e) {
178-
// Ignore
179-
}
180-
time += 100;
181-
return newFixedLengthResponse(response);
182-
}
183-
});
184-
185-
HttpMessage msg = this.getHttpMessage(test + "?name=test");
186-
187-
this.rule.init(msg, this.parent);
188-
this.rule.setExpectedDelayInMs(90);
189-
190-
this.rule.scan();
191-
192-
assertThat(alertsRaised.size(), equalTo(0));
193-
}
194-
19578
@Test
19679
void shouldReturnExpectedMappings() {
19780
// Given / When
@@ -201,7 +84,7 @@ void shouldReturnExpectedMappings() {
20184
// Then
20285
assertThat(cwe, is(equalTo(89)));
20386
assertThat(wasc, is(equalTo(19)));
204-
assertThat(tags.size(), is(equalTo(8)));
87+
assertThat(tags.size(), is(equalTo(7)));
20588
assertThat(
20689
tags.containsKey(CommonAlertTag.OWASP_2021_A03_INJECTION.getTag()),
20790
is(equalTo(true)));
@@ -212,7 +95,6 @@ void shouldReturnExpectedMappings() {
21295
tags.containsKey(CommonAlertTag.WSTG_V42_INPV_05_SQLI.getTag()), is(equalTo(true)));
21396
assertThat(tags.containsKey(CommonAlertTag.HIPAA.getTag()), is(equalTo(true)));
21497
assertThat(tags.containsKey(CommonAlertTag.PCI_DSS.getTag()), is(equalTo(true)));
215-
assertThat(tags.containsKey(CommonAlertTag.TEST_TIMING.getTag()), is(equalTo(true)));
21698
assertThat(tags.containsKey(PolicyTag.QA_FULL.getTag()), is(equalTo(true)));
21799
assertThat(tags.containsKey(PolicyTag.PENTEST.getTag()), is(equalTo(true)));
218100
assertThat(

0 commit comments

Comments
 (0)