1 | package com.jsql.model.injection.strategy; | |
2 | ||
3 | import com.jsql.model.InjectionModel; | |
4 | import com.jsql.model.accessible.DataAccess; | |
5 | import com.jsql.model.bean.util.Interaction; | |
6 | import com.jsql.model.bean.util.Request; | |
7 | import com.jsql.model.exception.JSqlException; | |
8 | import com.jsql.model.injection.vendor.model.VendorYaml; | |
9 | import com.jsql.model.suspendable.AbstractSuspendable; | |
10 | import com.jsql.model.suspendable.SuspendableGetIndexes; | |
11 | import com.jsql.util.I18nUtil; | |
12 | import com.jsql.util.LogLevelUtil; | |
13 | import org.apache.commons.lang3.StringUtils; | |
14 | import org.apache.logging.log4j.LogManager; | |
15 | import org.apache.logging.log4j.Logger; | |
16 | ||
17 | import java.util.ArrayList; | |
18 | import java.util.Arrays; | |
19 | import java.util.Comparator; | |
20 | import java.util.List; | |
21 | import java.util.regex.Pattern; | |
22 | ||
23 | public class StrategyInjectionNormal extends AbstractStrategy { | |
24 | | |
25 | /** | |
26 | * Log4j logger sent to view. | |
27 | */ | |
28 | private static final Logger LOGGER = LogManager.getRootLogger(); | |
29 | ||
30 | /** | |
31 | * i.e, 2 in "[..]union select 1,2,[..]", if 2 is found in HTML body. | |
32 | */ | |
33 | protected String visibleIndex; | |
34 | ||
35 | /** | |
36 | * HTML body of page successfully responding to | |
37 | * multiple fields selection (select 1,2,3,..). | |
38 | */ | |
39 | protected String sourceIndexesFound = StringUtils.EMPTY; | |
40 | ||
41 | private String performanceLength = "0"; | |
42 | | |
43 | public StrategyInjectionNormal(InjectionModel injectionModel) { | |
44 | super(injectionModel); | |
45 | } | |
46 | ||
47 | @Override | |
48 | public void checkApplicability() throws JSqlException { | |
49 | ||
50 |
1
1. checkApplicability : negated conditional → NO_COVERAGE |
if (this.injectionModel.getMediatorUtils().getPreferencesUtil().isStrategyNormalDisabled()) { |
51 | ||
52 | LOGGER.log(LogLevelUtil.CONSOLE_INFORM, AbstractStrategy.FORMAT_SKIP_STRATEGY_DISABLED, getName()); | |
53 | return; | |
54 | } | |
55 | ||
56 | LOGGER.log( | |
57 | LogLevelUtil.CONSOLE_DEFAULT, | |
58 | AbstractStrategy.FORMAT_CHECKING_STRATEGY, | |
59 |
1
1. lambda$checkApplicability$0 : replaced return value with null for com/jsql/model/injection/strategy/StrategyInjectionNormal::lambda$checkApplicability$0 → NO_COVERAGE |
() -> I18nUtil.valueByKey("LOG_CHECKING_STRATEGY"), |
60 | this::getName | |
61 | ); | |
62 |
1
1. checkApplicability : removed call to com/jsql/model/InjectionModel::setIndexesInUrl → NO_COVERAGE |
this.injectionModel.setIndexesInUrl(new SuspendableGetIndexes(this.injectionModel).run()); |
63 | ||
64 | // Define visibleIndex, i.e, 2 in "[..]union select 1,2,[..]", if 2 is found in HTML body | |
65 |
1
1. checkApplicability : negated conditional → NO_COVERAGE |
if (StringUtils.isNotEmpty(this.injectionModel.getIndexesInUrl())) { |
66 | this.visibleIndex = this.getVisibleIndex(this.sourceIndexesFound); | |
67 | } | |
68 | | |
69 |
1
1. checkApplicability : negated conditional → NO_COVERAGE |
this.isApplicable = StringUtils.isNotEmpty(this.injectionModel.getIndexesInUrl()) |
70 |
2
1. checkApplicability : negated conditional → NO_COVERAGE 2. checkApplicability : changed conditional boundary → NO_COVERAGE |
&& Integer.parseInt(this.injectionModel.getMediatorStrategy().getNormal().getPerformanceLength()) > 0 |
71 |
1
1. checkApplicability : negated conditional → NO_COVERAGE |
&& StringUtils.isNotBlank(this.visibleIndex); |
72 | | |
73 |
1
1. checkApplicability : negated conditional → NO_COVERAGE |
if (this.isApplicable) { |
74 | | |
75 | LOGGER.log( | |
76 | LogLevelUtil.CONSOLE_SUCCESS, | |
77 | "{} [{}] at index [{}] using [{}] characters", | |
78 |
1
1. lambda$checkApplicability$1 : replaced return value with null for com/jsql/model/injection/strategy/StrategyInjectionNormal::lambda$checkApplicability$1 → NO_COVERAGE |
() -> I18nUtil.valueByKey("LOG_VULNERABLE"), |
79 | this::getName, | |
80 |
1
1. lambda$checkApplicability$2 : replaced return value with null for com/jsql/model/injection/strategy/StrategyInjectionNormal::lambda$checkApplicability$2 → NO_COVERAGE |
() -> this.visibleIndex, |
81 |
1
1. lambda$checkApplicability$3 : replaced return value with null for com/jsql/model/injection/strategy/StrategyInjectionNormal::lambda$checkApplicability$3 → NO_COVERAGE |
() -> this.performanceLength |
82 | ); | |
83 |
1
1. checkApplicability : removed call to com/jsql/model/injection/strategy/StrategyInjectionNormal::allow → NO_COVERAGE |
this.allow(); |
84 | | |
85 | } else { | |
86 |
1
1. checkApplicability : removed call to com/jsql/model/injection/strategy/StrategyInjectionNormal::unallow → NO_COVERAGE |
this.unallow(); |
87 | } | |
88 | } | |
89 | ||
90 | @Override | |
91 | public void allow(int... i) { | |
92 | ||
93 |
1
1. allow : removed call to com/jsql/model/InjectionModel::appendAnalysisReport → NO_COVERAGE |
this.injectionModel.appendAnalysisReport( |
94 | "<span style=color:rgb(0,0,255)>### Strategy: " + getName() + "</span>" | |
95 | + this.injectionModel.getReportWithIndexes( | |
96 | this.injectionModel.getMediatorVendor().getVendor().instance().sqlNormal("<span style=color:rgb(0,128,0)><query></span>", "0", true), | |
97 | "metadataInjectionProcess" | |
98 | ) | |
99 | ); | |
100 |
1
1. allow : removed call to com/jsql/model/injection/strategy/StrategyInjectionNormal::markVulnerability → NO_COVERAGE |
this.markVulnerability(Interaction.MARK_NORMAL_VULNERABLE); |
101 | } | |
102 | ||
103 | @Override | |
104 | public void unallow(int... i) { | |
105 |
1
1. unallow : removed call to com/jsql/model/injection/strategy/StrategyInjectionNormal::markVulnerability → NO_COVERAGE |
this.markVulnerability(Interaction.MARK_NORMAL_INVULNERABLE); |
106 | } | |
107 | ||
108 | @Override | |
109 | public String inject(String sqlQuery, String startPosition, AbstractSuspendable stoppable, String metadataInjectionProcess) { | |
110 |
1
1. inject : replaced return value with "" for com/jsql/model/injection/strategy/StrategyInjectionNormal::inject → NO_COVERAGE |
return this.injectionModel.injectWithIndexes( |
111 | this.injectionModel.getMediatorVendor().getVendor().instance().sqlNormal(sqlQuery, startPosition, false), | |
112 | metadataInjectionProcess | |
113 | ); | |
114 | } | |
115 | ||
116 | @Override | |
117 | public void activateWhenApplicable() { | |
118 | ||
119 |
2
1. activateWhenApplicable : negated conditional → NO_COVERAGE 2. activateWhenApplicable : negated conditional → NO_COVERAGE |
if (this.injectionModel.getMediatorStrategy().getStrategy() == null && this.isApplicable()) { |
120 | ||
121 | LOGGER.log( | |
122 | LogLevelUtil.CONSOLE_INFORM, | |
123 | "{} [{}]", | |
124 |
1
1. lambda$activateWhenApplicable$4 : replaced return value with null for com/jsql/model/injection/strategy/StrategyInjectionNormal::lambda$activateWhenApplicable$4 → NO_COVERAGE |
() -> I18nUtil.valueByKey("LOG_USING_STRATEGY"), |
125 | this::getName | |
126 | ); | |
127 |
1
1. activateWhenApplicable : removed call to com/jsql/model/injection/strategy/MediatorStrategy::setStrategy → NO_COVERAGE |
this.injectionModel.getMediatorStrategy().setStrategy(this.injectionModel.getMediatorStrategy().getNormal()); |
128 | ||
129 | var request = new Request(); | |
130 |
1
1. activateWhenApplicable : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE |
request.setMessage(Interaction.MARK_NORMAL_STRATEGY); |
131 |
1
1. activateWhenApplicable : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE |
this.injectionModel.sendToViews(request); |
132 | } | |
133 | } | |
134 | | |
135 | /** | |
136 | * Runnable class, search the most efficient index.<br> | |
137 | * Some indexes will display a lots of characters, others won't, | |
138 | * so sort them by order of efficiency:<br> | |
139 | * find the one that displays the most number of characters. | |
140 | * @return Integer index with most efficiency and visible in source code | |
141 | */ | |
142 | public String getVisibleIndex(String firstSuccessPageSource) { | |
143 | | |
144 | // Parse all indexes found | |
145 | // Fix #4007 (initialize firstSuccessPageSource to empty String instead of null) | |
146 | String regexAllIndexes = String.format(VendorYaml.FORMAT_INDEX, "(\\d+?)"); | |
147 | var regexSearch = Pattern.compile("(?s)"+ regexAllIndexes).matcher(firstSuccessPageSource); | |
148 | | |
149 | List<String> foundIndexes = new ArrayList<>(); | |
150 |
1
1. getVisibleIndex : negated conditional → NO_COVERAGE |
while (regexSearch.find()) { |
151 | foundIndexes.add(regexSearch.group(1)); | |
152 | } | |
153 | ||
154 | String[] indexes = foundIndexes.toArray(new String[0]); | |
155 | ||
156 | // Make url shorter, replace useless indexes from 1337[index]7331 to 1 | |
157 | String regexAllExceptIndexesFound = String.format( | |
158 | VendorYaml.FORMAT_INDEX, | |
159 | "(?!"+ String.join("|", indexes) +"7331)\\d*" | |
160 | ); | |
161 | String indexesInUrl = this.injectionModel.getIndexesInUrl().replaceAll(regexAllExceptIndexesFound, "1"); | |
162 | ||
163 | // Replace correct indexes from 1337(index)7331 to | |
164 | // ==> ${lead}(index)######...###### | |
165 | // Search for index that displays the most # | |
166 | String performanceQuery = this.injectionModel.getMediatorVendor().getVendor().instance().sqlCapacity(indexes); | |
167 | String performanceSourcePage = this.injectionModel.injectWithoutIndex(performanceQuery, "normal#size"); | |
168 | ||
169 | // Build a 2D array of string with: | |
170 | // column 1: index | |
171 | // column 2: # found, so #######...####### | |
172 | regexSearch = Pattern.compile("(?s)"+ DataAccess.LEAD +"(\\d+)(#+)").matcher(performanceSourcePage); | |
173 | | |
174 | List<String[]> performanceResults = new ArrayList<>(); | |
175 | | |
176 |
1
1. getVisibleIndex : negated conditional → NO_COVERAGE |
while (regexSearch.find()) { |
177 | performanceResults.add(new String[]{regexSearch.group(1), regexSearch.group(2)}); | |
178 | } | |
179 | ||
180 |
1
1. getVisibleIndex : negated conditional → NO_COVERAGE |
if (performanceResults.isEmpty()) { |
181 | | |
182 | this.performanceLength = "0"; | |
183 |
1
1. getVisibleIndex : replaced return value with "" for com/jsql/model/injection/strategy/StrategyInjectionNormal::getVisibleIndex → NO_COVERAGE |
return null; |
184 | } | |
185 | | |
186 | // Switch from previous array to 2D integer array | |
187 | // column 1: length of #######...####### | |
188 | // column 2: index | |
189 | var lengthFields = new Integer[performanceResults.size()][2]; | |
190 | | |
191 |
2
1. getVisibleIndex : changed conditional boundary → NO_COVERAGE 2. getVisibleIndex : negated conditional → NO_COVERAGE |
for (var i = 0; i < performanceResults.size(); i++) { |
192 | lengthFields[i] = new Integer[] { | |
193 | | |
194 |
1
1. getVisibleIndex : Replaced integer addition with subtraction → NO_COVERAGE |
performanceResults.get(i)[1].length() + performanceResults.get(i)[0].length(), |
195 | Integer.parseInt(performanceResults.get(i)[0]) | |
196 | }; | |
197 | } | |
198 | ||
199 | // Sort by length of #######...####### | |
200 |
2
1. getVisibleIndex : removed call to java/util/Arrays::sort → NO_COVERAGE 2. lambda$getVisibleIndex$5 : replaced Integer return value with 0 for com/jsql/model/injection/strategy/StrategyInjectionNormal::lambda$getVisibleIndex$5 → NO_COVERAGE |
Arrays.sort(lengthFields, Comparator.comparing((Integer[] s) -> s[0])); |
201 |
1
1. getVisibleIndex : Replaced integer subtraction with addition → NO_COVERAGE |
Integer[] bestLengthFields = lengthFields[lengthFields.length - 1]; |
202 | this.performanceLength = bestLengthFields[0].toString(); | |
203 | ||
204 | // Reduce all others indexes | |
205 | String regexAllIndexesExceptBest = String.format( | |
206 | VendorYaml.FORMAT_INDEX, | |
207 | "(?!"+ bestLengthFields[1] +"7331)\\d*" | |
208 | ); | |
209 | indexesInUrl = indexesInUrl.replaceAll(regexAllIndexesExceptBest, "1"); | |
210 | | |
211 |
1
1. getVisibleIndex : removed call to com/jsql/model/InjectionModel::setIndexesInUrl → NO_COVERAGE |
this.injectionModel.setIndexesInUrl(indexesInUrl); |
212 | | |
213 |
1
1. getVisibleIndex : replaced return value with "" for com/jsql/model/injection/strategy/StrategyInjectionNormal::getVisibleIndex → NO_COVERAGE |
return Integer.toString(bestLengthFields[1]); |
214 | } | |
215 | | |
216 | | |
217 | // Getters and setters | |
218 | | |
219 | @Override | |
220 | public String getPerformanceLength() { | |
221 |
1
1. getPerformanceLength : replaced return value with "" for com/jsql/model/injection/strategy/StrategyInjectionNormal::getPerformanceLength → NO_COVERAGE |
return this.performanceLength; |
222 | } | |
223 | | |
224 | @Override | |
225 | public String getName() { | |
226 |
1
1. getName : replaced return value with "" for com/jsql/model/injection/strategy/StrategyInjectionNormal::getName → NO_COVERAGE |
return "Normal"; |
227 | } | |
228 | ||
229 | public String getVisibleIndex() { | |
230 |
1
1. getVisibleIndex : replaced return value with "" for com/jsql/model/injection/strategy/StrategyInjectionNormal::getVisibleIndex → NO_COVERAGE |
return this.visibleIndex; |
231 | } | |
232 | ||
233 | public void setVisibleIndex(String visibleIndex) { | |
234 | this.visibleIndex = visibleIndex; | |
235 | } | |
236 | ||
237 | public void setSourceIndexesFound(String sourceIndexesFound) { | |
238 | this.sourceIndexesFound = sourceIndexesFound; | |
239 | } | |
240 | } | |
Mutations | ||
50 |
1.1 |
|
59 |
1.1 |
|
62 |
1.1 |
|
65 |
1.1 |
|
69 |
1.1 |
|
70 |
1.1 2.2 |
|
71 |
1.1 |
|
73 |
1.1 |
|
78 |
1.1 |
|
80 |
1.1 |
|
81 |
1.1 |
|
83 |
1.1 |
|
86 |
1.1 |
|
93 |
1.1 |
|
100 |
1.1 |
|
105 |
1.1 |
|
110 |
1.1 |
|
119 |
1.1 2.2 |
|
124 |
1.1 |
|
127 |
1.1 |
|
130 |
1.1 |
|
131 |
1.1 |
|
150 |
1.1 |
|
176 |
1.1 |
|
180 |
1.1 |
|
183 |
1.1 |
|
191 |
1.1 2.2 |
|
194 |
1.1 |
|
200 |
1.1 2.2 |
|
201 |
1.1 |
|
211 |
1.1 |
|
213 |
1.1 |
|
221 |
1.1 |
|
226 |
1.1 |
|
230 |
1.1 |