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