1 | package com.jsql.model.suspendable; | |
2 | ||
3 | import com.jsql.model.InjectionModel; | |
4 | import com.jsql.model.bean.util.Header; | |
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.exception.StoppedByUserSlidingException; | |
9 | import com.jsql.model.injection.strategy.blind.InjectionCharInsertion; | |
10 | import com.jsql.model.injection.vendor.MediatorVendor; | |
11 | import com.jsql.model.injection.vendor.model.Vendor; | |
12 | import com.jsql.model.suspendable.callable.CallablePageSource; | |
13 | import com.jsql.util.I18nUtil; | |
14 | import com.jsql.util.LogLevelUtil; | |
15 | import org.apache.commons.lang3.RandomStringUtils; | |
16 | import org.apache.commons.lang3.StringUtils; | |
17 | import org.apache.logging.log4j.LogManager; | |
18 | import org.apache.logging.log4j.Logger; | |
19 | ||
20 | import java.util.*; | |
21 | import java.util.concurrent.CompletionService; | |
22 | import java.util.concurrent.ExecutionException; | |
23 | import java.util.concurrent.ExecutorCompletionService; | |
24 | import java.util.concurrent.ExecutorService; | |
25 | import java.util.regex.Pattern; | |
26 | import java.util.stream.Collectors; | |
27 | import java.util.stream.Stream; | |
28 | ||
29 | /** | |
30 | * Runnable class, define insertionCharacter to be used during injection, | |
31 | * i.e -1 in "[..].php?id=-1 union select[..]", sometimes it's -1, 0', 0, etc. | |
32 | * Find working insertion char when error message occurs in source. | |
33 | * Force to 1 if no insertion char works and empty value from user, | |
34 | * Force to user's value if no insertion char works, | |
35 | * Force to insertion char otherwise. | |
36 | */ | |
37 | public class SuspendableGetCharInsertion extends AbstractSuspendable { | |
38 | | |
39 | /** | |
40 | * Log4j logger sent to view. | |
41 | */ | |
42 | private static final Logger LOGGER = LogManager.getRootLogger(); | |
43 | ||
44 | private static final String LABEL_PREFIX = "prefix"; | |
45 | ||
46 | public SuspendableGetCharInsertion(InjectionModel injectionModel) { | |
47 | super(injectionModel); | |
48 | } | |
49 | ||
50 | @Override | |
51 | public String run(Object... args) throws JSqlException { | |
52 | | |
53 | String characterInsertionByUser = (String) args[0]; | |
54 | | |
55 | ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableGetInsertionCharacter"); | |
56 | CompletionService<CallablePageSource> taskCompletionService = new ExecutorCompletionService<>(taskExecutor); | |
57 | ||
58 | var charFromBooleanMatch = new String[1]; | |
59 | List<String> charactersInsertion = this.initializeCallables(taskCompletionService, charFromBooleanMatch); | |
60 | | |
61 | var mediatorVendor = this.injectionModel.getMediatorVendor(); | |
62 | ||
63 | LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "Fingerprinting database and character insertion with Order by match..."); | |
64 | ||
65 | String charFromOrderBy = null; | |
66 | | |
67 | int total = charactersInsertion.size(); | |
68 |
2
1. run : negated conditional → NO_COVERAGE 2. run : changed conditional boundary → NO_COVERAGE |
while (0 < total) { |
69 | ||
70 |
1
1. run : negated conditional → NO_COVERAGE |
if (this.isSuspended()) { |
71 | throw new StoppedByUserSlidingException(); | |
72 | } | |
73 | | |
74 | try { | |
75 | CallablePageSource currentCallable = taskCompletionService.take().get(); | |
76 |
1
1. run : Changed increment from -1 to 1 → NO_COVERAGE |
total--; |
77 | String pageSource = currentCallable.getContent(); | |
78 | | |
79 | List<Vendor> vendorsOrderByMatch = this.getVendorsOrderByMatch(mediatorVendor, pageSource); | |
80 | | |
81 |
1
1. run : negated conditional → NO_COVERAGE |
if (!vendorsOrderByMatch.isEmpty()) { |
82 | ||
83 |
1
1. run : removed call to com/jsql/model/suspendable/SuspendableGetCharInsertion::setVendor → NO_COVERAGE |
this.setVendor(mediatorVendor, vendorsOrderByMatch); |
84 | | |
85 | LOGGER.log(LogLevelUtil.CONSOLE_INFORM, "Using [{}]", mediatorVendor.getVendor()); | |
86 | var requestSetVendor = new Request(); | |
87 |
1
1. run : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE |
requestSetVendor.setMessage(Interaction.SET_VENDOR); |
88 | Map<Header, Object> msgHeader = new EnumMap<>(Header.class); | |
89 | msgHeader.put(Header.URL, this.injectionModel.getMediatorUtils().getConnectionUtil().getUrlByUser()); | |
90 | msgHeader.put(Header.VENDOR, mediatorVendor.getVendor()); | |
91 |
1
1. run : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE |
requestSetVendor.setParameters(msgHeader); |
92 |
1
1. run : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE |
this.injectionModel.sendToViews(requestSetVendor); |
93 | | |
94 | // Char insertion | |
95 | charFromOrderBy = currentCallable.getCharacterInsertion(); | |
96 | | |
97 | LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, "Character insertion [{}] matching with Order by and compatible with Error strategy", charFromOrderBy); | |
98 | | |
99 | break; | |
100 | } | |
101 | } catch (InterruptedException e) { | |
102 | | |
103 | LOGGER.log(LogLevelUtil.IGNORE, e, e); | |
104 |
1
1. run : removed call to java/lang/Thread::interrupt → NO_COVERAGE |
Thread.currentThread().interrupt(); |
105 | | |
106 | } catch (ExecutionException e) { | |
107 | LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e); | |
108 | } | |
109 | } | |
110 | ||
111 |
1
1. run : removed call to com/jsql/util/ThreadUtil::shutdown → NO_COVERAGE |
this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor); |
112 | | |
113 |
2
1. run : negated conditional → NO_COVERAGE 2. run : negated conditional → NO_COVERAGE |
if (charFromOrderBy == null && charFromBooleanMatch[0] != null) { |
114 | charFromOrderBy = charFromBooleanMatch[0]; | |
115 | } | |
116 | | |
117 |
1
1. run : replaced return value with "" for com/jsql/model/suspendable/SuspendableGetCharInsertion::run → NO_COVERAGE |
return this.getCharacterInsertion(characterInsertionByUser, charFromOrderBy); |
118 | } | |
119 | ||
120 | private void setVendor(MediatorVendor mediatorVendor, List<Vendor> vendorsOrderByMatch) { | |
121 | if ( | |
122 |
1
1. setVendor : negated conditional → NO_COVERAGE |
vendorsOrderByMatch.size() == 1 |
123 |
1
1. setVendor : negated conditional → NO_COVERAGE |
&& vendorsOrderByMatch.get(0) != mediatorVendor.getVendor() |
124 | ) { | |
125 |
1
1. setVendor : removed call to com/jsql/model/injection/vendor/MediatorVendor::setVendor → NO_COVERAGE |
mediatorVendor.setVendor(vendorsOrderByMatch.get(0)); |
126 |
2
1. setVendor : changed conditional boundary → NO_COVERAGE 2. setVendor : negated conditional → NO_COVERAGE |
} else if (vendorsOrderByMatch.size() > 1) { |
127 |
1
1. setVendor : negated conditional → NO_COVERAGE |
if (vendorsOrderByMatch.contains(mediatorVendor.getPostgresql())) { |
128 |
1
1. setVendor : removed call to com/jsql/model/injection/vendor/MediatorVendor::setVendor → NO_COVERAGE |
mediatorVendor.setVendor(mediatorVendor.getPostgresql()); |
129 |
1
1. setVendor : negated conditional → NO_COVERAGE |
} else if (vendorsOrderByMatch.contains(mediatorVendor.getMysql())) { |
130 |
1
1. setVendor : removed call to com/jsql/model/injection/vendor/MediatorVendor::setVendor → NO_COVERAGE |
mediatorVendor.setVendor(mediatorVendor.getMysql()); |
131 | } else { | |
132 |
1
1. setVendor : removed call to com/jsql/model/injection/vendor/MediatorVendor::setVendor → NO_COVERAGE |
mediatorVendor.setVendor(vendorsOrderByMatch.get(0)); |
133 | } | |
134 | } | |
135 | } | |
136 | ||
137 | private List<Vendor> getVendorsOrderByMatch(MediatorVendor mediatorVendor, String pageSource) { | |
138 | | |
139 |
1
1. getVendorsOrderByMatch : replaced return value with Collections.emptyList for com/jsql/model/suspendable/SuspendableGetCharInsertion::getVendorsOrderByMatch → NO_COVERAGE |
return mediatorVendor.getVendors() |
140 | .stream() | |
141 |
2
1. lambda$getVendorsOrderByMatch$0 : replaced boolean return with true for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getVendorsOrderByMatch$0 → NO_COVERAGE 2. lambda$getVendorsOrderByMatch$0 : negated conditional → NO_COVERAGE |
.filter(vendor -> vendor != mediatorVendor.getAuto()) |
142 |
2
1. lambda$getVendorsOrderByMatch$1 : replaced boolean return with false for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getVendorsOrderByMatch$1 → NO_COVERAGE 2. lambda$getVendorsOrderByMatch$1 : replaced boolean return with true for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getVendorsOrderByMatch$1 → NO_COVERAGE |
.filter(vendor -> StringUtils.isNotEmpty( |
143 | vendor.instance() | |
144 | .getModelYaml() | |
145 | .getStrategy() | |
146 | .getConfiguration() | |
147 | .getFingerprint() | |
148 | .getOrderByErrorMessage() | |
149 | )) | |
150 | .filter(vendor -> { | |
151 | | |
152 | Optional<String> optionalOrderByErrorMatch = Stream.of( | |
153 | vendor.instance() | |
154 | .getModelYaml() | |
155 | .getStrategy() | |
156 | .getConfiguration() | |
157 | .getFingerprint() | |
158 | .getOrderByErrorMessage() | |
159 | .split("[\\r\\n]+") | |
160 | ) | |
161 | .filter(errorMessage -> | |
162 |
2
1. lambda$getVendorsOrderByMatch$2 : replaced boolean return with false for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getVendorsOrderByMatch$2 → NO_COVERAGE 2. lambda$getVendorsOrderByMatch$2 : replaced boolean return with true for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getVendorsOrderByMatch$2 → NO_COVERAGE |
Pattern |
163 | .compile(".*" + errorMessage + ".*", Pattern.DOTALL) | |
164 | .matcher(pageSource) | |
165 | .matches() | |
166 | ) | |
167 | .findAny(); | |
168 | | |
169 |
1
1. lambda$getVendorsOrderByMatch$3 : negated conditional → NO_COVERAGE |
if (optionalOrderByErrorMatch.isPresent()) { |
170 | LOGGER.log( | |
171 | LogLevelUtil.CONSOLE_SUCCESS, | |
172 | String.format("Order by fingerprint matching vendor [%s]", vendor) | |
173 | ); | |
174 | } | |
175 | | |
176 |
2
1. lambda$getVendorsOrderByMatch$3 : replaced boolean return with false for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getVendorsOrderByMatch$3 → NO_COVERAGE 2. lambda$getVendorsOrderByMatch$3 : replaced boolean return with true for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getVendorsOrderByMatch$3 → NO_COVERAGE |
return optionalOrderByErrorMatch.isPresent(); |
177 | }) | |
178 | .collect(Collectors.toList()); | |
179 | } | |
180 | ||
181 | private List<String> initializeCallables(CompletionService<CallablePageSource> taskCompletionService, String[] charFromBooleanMatch) throws JSqlException { | |
182 | | |
183 | List<String> prefixValues = Arrays.asList( | |
184 | RandomStringUtils.random(10, "012"), // to trigger probable failure | |
185 | "1" // to trigger eventual success | |
186 | ); | |
187 | | |
188 | List<String> prefixQuotes = Arrays.asList( | |
189 | LABEL_PREFIX +"'", | |
190 | LABEL_PREFIX, | |
191 | LABEL_PREFIX +"`", // TODO add ITs | |
192 | LABEL_PREFIX +"\"", | |
193 | LABEL_PREFIX +"%bf'" // GBK slash encoding use case | |
194 | ); | |
195 | | |
196 | List<String> prefixParentheses = Arrays.asList(StringUtils.EMPTY, ")", "))"); | |
197 | | |
198 | List<String> charactersInsertion = new ArrayList<>(); | |
199 | | |
200 | LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "Fingerprinting character insertion with Boolean match..."); | |
201 | for (String prefixValue: prefixValues) { | |
202 | for (String prefixQuote: prefixQuotes) { | |
203 | for (String prefixParenthesis: prefixParentheses) { | |
204 |
1
1. initializeCallables : removed call to com/jsql/model/suspendable/SuspendableGetCharInsertion::checkInsertionChar → NO_COVERAGE |
this.checkInsertionChar(charFromBooleanMatch, charactersInsertion, prefixValue, prefixQuote, prefixParenthesis); |
205 | } | |
206 | } | |
207 | } | |
208 | | |
209 | for (String characterInsertion: charactersInsertion) { | |
210 | taskCompletionService.submit( | |
211 | new CallablePageSource( | |
212 | characterInsertion | |
213 | + StringUtils.SPACE // covered by cleaning | |
214 | + this.injectionModel.getMediatorVendor().getVendor().instance().sqlOrderBy(), | |
215 | characterInsertion, | |
216 | this.injectionModel, | |
217 | "prefix#orderby" | |
218 | ) | |
219 | ); | |
220 | } | |
221 | | |
222 |
1
1. initializeCallables : replaced return value with Collections.emptyList for com/jsql/model/suspendable/SuspendableGetCharInsertion::initializeCallables → NO_COVERAGE |
return charactersInsertion; |
223 | } | |
224 | ||
225 | private void checkInsertionChar( | |
226 | String[] charFromBooleanMatch, | |
227 | List<String> charactersInsertion, | |
228 | String prefixValue, | |
229 | String prefixQuote, | |
230 | String prefixParenthesis | |
231 | ) throws StoppedByUserSlidingException { | |
232 | ||
233 | String characterInsertion = prefixQuote.replace(LABEL_PREFIX, prefixValue) + prefixParenthesis; | |
234 | charactersInsertion.add(characterInsertion); | |
235 | | |
236 | // Skipping Boolean match when already found | |
237 |
1
1. checkInsertionChar : negated conditional → NO_COVERAGE |
if (charFromBooleanMatch[0] == null) { |
238 | | |
239 | var injectionCharInsertion = new InjectionCharInsertion( | |
240 | this.injectionModel, | |
241 | characterInsertion, | |
242 | prefixQuote + prefixParenthesis | |
243 | ); | |
244 |
1
1. checkInsertionChar : negated conditional → NO_COVERAGE |
if (injectionCharInsertion.isInjectable()) { |
245 | | |
246 |
1
1. checkInsertionChar : negated conditional → NO_COVERAGE |
if (this.isSuspended()) { |
247 | throw new StoppedByUserSlidingException(); | |
248 | } | |
249 | | |
250 | charFromBooleanMatch[0] = characterInsertion; | |
251 | LOGGER.log( | |
252 | LogLevelUtil.CONSOLE_SUCCESS, | |
253 | "Found character insertion [{}] using Boolean match", | |
254 |
1
1. lambda$checkInsertionChar$4 : replaced return value with null for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$checkInsertionChar$4 → NO_COVERAGE |
() -> charFromBooleanMatch[0] |
255 | ); | |
256 | } | |
257 | } | |
258 | } | |
259 | | |
260 | private String getCharacterInsertion(String characterInsertionByUser, String characterInsertionDetected) { | |
261 | | |
262 | String characterInsertionDetectedFixed = characterInsertionDetected; | |
263 | | |
264 |
1
1. getCharacterInsertion : negated conditional → NO_COVERAGE |
if (characterInsertionDetectedFixed == null) { |
265 | | |
266 | characterInsertionDetectedFixed = characterInsertionByUser; | |
267 | ||
268 | String logCharacterInsertion = characterInsertionDetectedFixed; | |
269 | LOGGER.log( | |
270 | LogLevelUtil.CONSOLE_ERROR, | |
271 | "No character insertion found, forcing to [{}]", | |
272 |
1
1. lambda$getCharacterInsertion$5 : replaced return value with null for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getCharacterInsertion$5 → NO_COVERAGE |
() -> logCharacterInsertion.replace(InjectionModel.STAR, StringUtils.EMPTY) |
273 | ); | |
274 |
1
1. getCharacterInsertion : negated conditional → NO_COVERAGE |
} else if (!characterInsertionByUser.replace(InjectionModel.STAR, StringUtils.EMPTY).equals(characterInsertionDetectedFixed)) { |
275 | | |
276 | String characterInsertionByUserFormat = characterInsertionByUser.replace(InjectionModel.STAR, StringUtils.EMPTY); | |
277 | LOGGER.log( | |
278 | LogLevelUtil.CONSOLE_INFORM, | |
279 | "Using [{}] and [{}]", | |
280 |
1
1. lambda$getCharacterInsertion$6 : replaced return value with null for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getCharacterInsertion$6 → NO_COVERAGE |
() -> this.injectionModel.getMediatorVendor().getVendor(), |
281 |
1
1. lambda$getCharacterInsertion$7 : replaced return value with null for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getCharacterInsertion$7 → NO_COVERAGE |
() -> characterInsertionDetected |
282 | ); | |
283 | LOGGER.log( | |
284 | LogLevelUtil.CONSOLE_DEFAULT, | |
285 | "Add manually the character * like [{}*] to force the value [{}]", | |
286 |
1
1. lambda$getCharacterInsertion$8 : replaced return value with null for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getCharacterInsertion$8 → NO_COVERAGE |
() -> characterInsertionByUserFormat, |
287 |
1
1. lambda$getCharacterInsertion$9 : replaced return value with null for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getCharacterInsertion$9 → NO_COVERAGE |
() -> characterInsertionByUserFormat |
288 | ); | |
289 | } else { | |
290 | LOGGER.log( | |
291 | LogLevelUtil.CONSOLE_INFORM, | |
292 | "{} [{}]", | |
293 |
1
1. lambda$getCharacterInsertion$10 : replaced return value with null for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getCharacterInsertion$10 → NO_COVERAGE |
() -> I18nUtil.valueByKey("LOG_USING_INSERTION_CHARACTER"), |
294 |
1
1. lambda$getCharacterInsertion$11 : replaced return value with null for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getCharacterInsertion$11 → NO_COVERAGE |
() -> characterInsertionDetected.replace(InjectionModel.STAR, StringUtils.EMPTY) |
295 | ); | |
296 | } | |
297 | | |
298 |
1
1. getCharacterInsertion : replaced return value with "" for com/jsql/model/suspendable/SuspendableGetCharInsertion::getCharacterInsertion → NO_COVERAGE |
return characterInsertionDetectedFixed; |
299 | } | |
300 | } | |
Mutations | ||
68 |
1.1 2.2 |
|
70 |
1.1 |
|
76 |
1.1 |
|
81 |
1.1 |
|
83 |
1.1 |
|
87 |
1.1 |
|
91 |
1.1 |
|
92 |
1.1 |
|
104 |
1.1 |
|
111 |
1.1 |
|
113 |
1.1 2.2 |
|
117 |
1.1 |
|
122 |
1.1 |
|
123 |
1.1 |
|
125 |
1.1 |
|
126 |
1.1 2.2 |
|
127 |
1.1 |
|
128 |
1.1 |
|
129 |
1.1 |
|
130 |
1.1 |
|
132 |
1.1 |
|
139 |
1.1 |
|
141 |
1.1 2.2 |
|
142 |
1.1 2.2 |
|
162 |
1.1 2.2 |
|
169 |
1.1 |
|
176 |
1.1 2.2 |
|
204 |
1.1 |
|
222 |
1.1 |
|
237 |
1.1 |
|
244 |
1.1 |
|
246 |
1.1 |
|
254 |
1.1 |
|
264 |
1.1 |
|
272 |
1.1 |
|
274 |
1.1 |
|
280 |
1.1 |
|
281 |
1.1 |
|
286 |
1.1 |
|
287 |
1.1 |
|
293 |
1.1 |
|
294 |
1.1 |
|
298 |
1.1 |