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 com.jsql.util.StringUtil;
14 import org.apache.commons.lang3.StringUtils;
15 import org.apache.logging.log4j.LogManager;
16 import org.apache.logging.log4j.Logger;
17
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.Comparator;
21 import java.util.List;
22 import java.util.regex.Pattern;
23
24 public class StrategyInjectionUnion extends AbstractStrategy {
25
26
27
28
29 private static final Logger LOGGER = LogManager.getRootLogger();
30
31
32
33
34 protected String visibleIndex;
35
36
37
38
39
40 protected String sourceIndexesFound = StringUtils.EMPTY;
41 private int nbIndexesFound = 0;
42
43 private String performanceLength = "0";
44
45 public StrategyInjectionUnion(InjectionModel injectionModel) {
46 super(injectionModel);
47 }
48
49 @Override
50 public void checkApplicability() throws JSqlException {
51 if (this.injectionModel.getMediatorUtils().getPreferencesUtil().isStrategyUnionDisabled()) {
52 LOGGER.log(LogLevelUtil.CONSOLE_INFORM, AbstractStrategy.FORMAT_SKIP_STRATEGY_DISABLED, this.getName());
53 return;
54 }
55
56 this.logChecking();
57
58 this.injectionModel.setIndexesInUrl(new SuspendableGetIndexes(this.injectionModel).run());
59
60
61 if (StringUtils.isNotEmpty(this.injectionModel.getIndexesInUrl())) {
62 this.visibleIndex = this.getVisibleIndex(this.sourceIndexesFound);
63 }
64
65 this.isApplicable = StringUtils.isNotEmpty(this.injectionModel.getIndexesInUrl())
66 && Integer.parseInt(this.injectionModel.getMediatorStrategy().getUnion().getPerformanceLength()) > 0
67 && StringUtils.isNotBlank(this.visibleIndex);
68
69 if (this.isApplicable) {
70 LOGGER.log(
71 LogLevelUtil.CONSOLE_SUCCESS,
72 "{} [{}] at index [{}] showing [{}] characters",
73 () -> I18nUtil.valueByKey("LOG_VULNERABLE"),
74 this::getName,
75 () -> this.visibleIndex,
76 () -> this.performanceLength
77 );
78 this.allow();
79 } else {
80 this.unallow();
81 }
82 }
83
84 @Override
85 public void allow(int... i) {
86 this.injectionModel.appendAnalysisReport(
87 StringUtil.formatReport(LogLevelUtil.COLOR_BLU, "### Strategy: " + this.getName())
88 + this.injectionModel.getReportWithIndexes(
89 this.injectionModel.getMediatorVendor().getVendor().instance().sqlUnion(StringUtil.formatReport(LogLevelUtil.COLOR_GREEN, "<query>"), "0", true),
90 "metadataInjectionProcess"
91 )
92 );
93 this.markVulnerability(Interaction.MARK_UNION_VULNERABLE);
94 }
95
96 @Override
97 public void unallow(int... i) {
98 this.markVulnerability(Interaction.MARK_UNION_INVULNERABLE);
99 }
100
101 @Override
102 public String inject(String sqlQuery, String startPosition, AbstractSuspendable stoppable, String metadataInjectionProcess) {
103 return this.injectionModel.injectWithIndexes(
104 this.injectionModel.getMediatorVendor().getVendor().instance().sqlUnion(sqlQuery, startPosition, false),
105 metadataInjectionProcess
106 );
107 }
108
109 @Override
110 public void activateWhenApplicable() {
111 if (this.injectionModel.getMediatorStrategy().getStrategy() == null && this.isApplicable()) {
112 LOGGER.log(
113 LogLevelUtil.CONSOLE_INFORM,
114 "{} [{}]",
115 () -> I18nUtil.valueByKey("LOG_USING_STRATEGY"),
116 this::getName
117 );
118 this.injectionModel.getMediatorStrategy().setStrategy(this.injectionModel.getMediatorStrategy().getUnion());
119
120 var request = new Request();
121 request.setMessage(Interaction.MARK_UNION_STRATEGY);
122 this.injectionModel.sendToViews(request);
123 }
124 }
125
126
127
128
129
130
131
132
133 public String getVisibleIndex(String firstSuccessPageSource) {
134
135
136 String regexAllIndexes = String.format(VendorYaml.FORMAT_INDEX, "(\\d+?)");
137 var regexSearch = Pattern.compile("(?s)"+ regexAllIndexes).matcher(firstSuccessPageSource);
138
139 List<String> foundIndexes = new ArrayList<>();
140 while (regexSearch.find()) {
141 foundIndexes.add(regexSearch.group(1));
142 }
143
144 String[] indexes = foundIndexes.toArray(new String[0]);
145
146
147 String regexAllExceptIndexesFound = String.format(
148 VendorYaml.FORMAT_INDEX,
149 "(?!"+ String.join("|", indexes) +"7331)\\d*"
150 );
151 String indexesInUrl = this.injectionModel.getIndexesInUrl().replaceAll(regexAllExceptIndexesFound, "1");
152
153
154
155
156 String performanceQuery = this.injectionModel.getMediatorVendor().getVendor().instance().sqlCapacity(indexes);
157 String performanceSourcePage = this.injectionModel.injectWithoutIndex(performanceQuery, "union#size");
158
159
160
161
162 regexSearch = Pattern.compile("(?s)"+ DataAccess.LEAD +"(\\d+)("+ VendorYaml.CALIBRATOR_SQL +"+)").matcher(performanceSourcePage);
163 List<String[]> performanceResults = new ArrayList<>();
164 while (regexSearch.find()) {
165 performanceResults.add(new String[]{regexSearch.group(1), regexSearch.group(2)});
166 }
167
168 if (performanceResults.isEmpty()) {
169 this.performanceLength = "0";
170 return null;
171 }
172
173
174
175
176 var lengthFields = new Integer[performanceResults.size()][2];
177
178 for (var i = 0; i < performanceResults.size(); i++) {
179 lengthFields[i] = new Integer[] {
180 performanceResults.get(i)[1].length() + performanceResults.get(i)[0].length(),
181 Integer.parseInt(performanceResults.get(i)[0])
182 };
183 }
184
185
186 Arrays.sort(lengthFields, Comparator.comparing((Integer[] s) -> s[0]));
187 Integer[] bestLengthFields = lengthFields[lengthFields.length - 1];
188 this.performanceLength = bestLengthFields[0].toString();
189
190
191 String regexAllIndexesExceptBest = String.format(
192 VendorYaml.FORMAT_INDEX,
193 "(?!"+ bestLengthFields[1] +"7331)\\d*"
194 );
195 indexesInUrl = indexesInUrl.replaceAll(regexAllIndexesExceptBest, "1");
196
197 this.injectionModel.setIndexesInUrl(indexesInUrl);
198
199 return Integer.toString(bestLengthFields[1]);
200 }
201
202
203
204
205 @Override
206 public String getPerformanceLength() {
207 return this.performanceLength;
208 }
209
210 @Override
211 public String getName() {
212 return "Union";
213 }
214
215 public String getVisibleIndex() {
216 return this.visibleIndex;
217 }
218
219 public void setVisibleIndex(String visibleIndex) {
220 this.visibleIndex = visibleIndex;
221 }
222
223 public void setSourceIndexesFound(String sourceIndexesFound) {
224 this.sourceIndexesFound = sourceIndexesFound;
225 }
226
227 public int getNbIndexesFound() {
228 return this.nbIndexesFound;
229 }
230
231 public void setNbIndexesFound(int nbIndexesFound) {
232 this.nbIndexesFound = nbIndexesFound;
233 }
234 }