StrategyInjectionNormal.java
package com.jsql.model.injection.strategy;
import com.jsql.model.InjectionModel;
import com.jsql.model.accessible.DataAccess;
import com.jsql.model.bean.util.Interaction;
import com.jsql.model.bean.util.Request;
import com.jsql.model.exception.JSqlException;
import com.jsql.model.injection.vendor.model.VendorYaml;
import com.jsql.model.suspendable.AbstractSuspendable;
import com.jsql.model.suspendable.SuspendableGetIndexes;
import com.jsql.util.I18nUtil;
import com.jsql.util.LogLevelUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Pattern;
public class StrategyInjectionNormal extends AbstractStrategy {
/**
* Log4j logger sent to view.
*/
private static final Logger LOGGER = LogManager.getRootLogger();
/**
* i.e, 2 in "[..]union select 1,2,[..]", if 2 is found in HTML body.
*/
protected String visibleIndex;
/**
* HTML body of page successfully responding to
* multiple fields selection (select 1,2,3,..).
*/
protected String sourceIndexesFound = StringUtils.EMPTY;
private String performanceLength = "0";
public StrategyInjectionNormal(InjectionModel injectionModel) {
super(injectionModel);
}
@Override
public void checkApplicability() throws JSqlException {
if (this.injectionModel.getMediatorUtils().getPreferencesUtil().isStrategyNormalDisabled()) {
LOGGER.log(LogLevelUtil.CONSOLE_INFORM, AbstractStrategy.FORMAT_SKIP_STRATEGY_DISABLED, getName());
return;
}
LOGGER.log(
LogLevelUtil.CONSOLE_DEFAULT,
AbstractStrategy.FORMAT_CHECKING_STRATEGY,
() -> I18nUtil.valueByKey("LOG_CHECKING_STRATEGY"),
this::getName
);
this.injectionModel.setIndexesInUrl(new SuspendableGetIndexes(this.injectionModel).run());
// Define visibleIndex, i.e, 2 in "[..]union select 1,2,[..]", if 2 is found in HTML body
if (StringUtils.isNotEmpty(this.injectionModel.getIndexesInUrl())) {
this.visibleIndex = this.getVisibleIndex(this.sourceIndexesFound);
}
this.isApplicable = StringUtils.isNotEmpty(this.injectionModel.getIndexesInUrl())
&& Integer.parseInt(this.injectionModel.getMediatorStrategy().getNormal().getPerformanceLength()) > 0
&& StringUtils.isNotBlank(this.visibleIndex);
if (this.isApplicable) {
LOGGER.log(
LogLevelUtil.CONSOLE_SUCCESS,
"{} [{}] at index [{}] using [{}] characters",
() -> I18nUtil.valueByKey("LOG_VULNERABLE"),
this::getName,
() -> this.visibleIndex,
() -> this.performanceLength
);
this.allow();
} else {
this.unallow();
}
}
@Override
public void allow(int... i) {
this.injectionModel.appendAnalysisReport(
"<span style=color:rgb(0,0,255)>### Strategy: " + getName() + "</span>"
+ this.injectionModel.getReportWithIndexes(
this.injectionModel.getMediatorVendor().getVendor().instance().sqlNormal("<span style=color:rgb(0,128,0)><query></span>", "0", true),
"metadataInjectionProcess"
)
);
this.markVulnerability(Interaction.MARK_NORMAL_VULNERABLE);
}
@Override
public void unallow(int... i) {
this.markVulnerability(Interaction.MARK_NORMAL_INVULNERABLE);
}
@Override
public String inject(String sqlQuery, String startPosition, AbstractSuspendable stoppable, String metadataInjectionProcess) {
return this.injectionModel.injectWithIndexes(
this.injectionModel.getMediatorVendor().getVendor().instance().sqlNormal(sqlQuery, startPosition, false),
metadataInjectionProcess
);
}
@Override
public void activateWhenApplicable() {
if (this.injectionModel.getMediatorStrategy().getStrategy() == null && this.isApplicable()) {
LOGGER.log(
LogLevelUtil.CONSOLE_INFORM,
"{} [{}]",
() -> I18nUtil.valueByKey("LOG_USING_STRATEGY"),
this::getName
);
this.injectionModel.getMediatorStrategy().setStrategy(this.injectionModel.getMediatorStrategy().getNormal());
var request = new Request();
request.setMessage(Interaction.MARK_NORMAL_STRATEGY);
this.injectionModel.sendToViews(request);
}
}
/**
* Runnable class, search the most efficient index.<br>
* Some indexes will display a lots of characters, others won't,
* so sort them by order of efficiency:<br>
* find the one that displays the most number of characters.
* @return Integer index with most efficiency and visible in source code
*/
public String getVisibleIndex(String firstSuccessPageSource) {
// Parse all indexes found
// Fix #4007 (initialize firstSuccessPageSource to empty String instead of null)
String regexAllIndexes = String.format(VendorYaml.FORMAT_INDEX, "(\\d+?)");
var regexSearch = Pattern.compile("(?s)"+ regexAllIndexes).matcher(firstSuccessPageSource);
List<String> foundIndexes = new ArrayList<>();
while (regexSearch.find()) {
foundIndexes.add(regexSearch.group(1));
}
String[] indexes = foundIndexes.toArray(new String[0]);
// Make url shorter, replace useless indexes from 1337[index]7331 to 1
String regexAllExceptIndexesFound = String.format(
VendorYaml.FORMAT_INDEX,
"(?!"+ String.join("|", indexes) +"7331)\\d*"
);
String indexesInUrl = this.injectionModel.getIndexesInUrl().replaceAll(regexAllExceptIndexesFound, "1");
// Replace correct indexes from 1337(index)7331 to
// ==> ${lead}(index)######...######
// Search for index that displays the most #
String performanceQuery = this.injectionModel.getMediatorVendor().getVendor().instance().sqlCapacity(indexes);
String performanceSourcePage = this.injectionModel.injectWithoutIndex(performanceQuery, "normal#size");
// Build a 2D array of string with:
// column 1: index
// column 2: # found, so #######...#######
regexSearch = Pattern.compile("(?s)"+ DataAccess.LEAD +"(\\d+)(#+)").matcher(performanceSourcePage);
List<String[]> performanceResults = new ArrayList<>();
while (regexSearch.find()) {
performanceResults.add(new String[]{regexSearch.group(1), regexSearch.group(2)});
}
if (performanceResults.isEmpty()) {
this.performanceLength = "0";
return null;
}
// Switch from previous array to 2D integer array
// column 1: length of #######...#######
// column 2: index
var lengthFields = new Integer[performanceResults.size()][2];
for (var i = 0; i < performanceResults.size(); i++) {
lengthFields[i] = new Integer[] {
performanceResults.get(i)[1].length() + performanceResults.get(i)[0].length(),
Integer.parseInt(performanceResults.get(i)[0])
};
}
// Sort by length of #######...#######
Arrays.sort(lengthFields, Comparator.comparing((Integer[] s) -> s[0]));
Integer[] bestLengthFields = lengthFields[lengthFields.length - 1];
this.performanceLength = bestLengthFields[0].toString();
// Reduce all others indexes
String regexAllIndexesExceptBest = String.format(
VendorYaml.FORMAT_INDEX,
"(?!"+ bestLengthFields[1] +"7331)\\d*"
);
indexesInUrl = indexesInUrl.replaceAll(regexAllIndexesExceptBest, "1");
this.injectionModel.setIndexesInUrl(indexesInUrl);
return Integer.toString(bestLengthFields[1]);
}
// Getters and setters
@Override
public String getPerformanceLength() {
return this.performanceLength;
}
@Override
public String getName() {
return "Normal";
}
public String getVisibleIndex() {
return this.visibleIndex;
}
public void setVisibleIndex(String visibleIndex) {
this.visibleIndex = visibleIndex;
}
public void setSourceIndexesFound(String sourceIndexesFound) {
this.sourceIndexesFound = sourceIndexesFound;
}
}