use of com.jsql.model.bean.util.Request in project jsql-injection by ron190.
the class ManagerFile method readFile.
/**
* Attempt to read files in parallel by their path from the website using injection.
* Reading file needs a FILE right on the server.
* The user can interrupt the process at any time.
* @param pathsFiles List of file paths to read
* @throws JSqlException when an error occurs during injection
* @throws InterruptedException if the current thread was interrupted while waiting
* @throws ExecutionException if the computation threw an exception
*/
public void readFile(List<ItemList> pathsFiles) throws JSqlException, InterruptedException, ExecutionException {
if (!MediatorHelper.model().getResourceAccess().isReadingAllowed()) {
return;
}
var countFileFound = 0;
ExecutorService taskExecutor = Executors.newFixedThreadPool(10, new ThreadFactoryCallable("CallableReadFile"));
CompletionService<CallableFile> taskCompletionService = new ExecutorCompletionService<>(taskExecutor);
for (ItemList pathFile : pathsFiles) {
var callableFile = new CallableFile(pathFile.toString(), MediatorHelper.model());
taskCompletionService.submit(callableFile);
MediatorHelper.model().getResourceAccess().getCallablesReadFile().add(callableFile);
}
List<String> duplicate = new ArrayList<>();
int submittedTasks = pathsFiles.size();
int tasksHandled;
for (tasksHandled = 0; tasksHandled < submittedTasks && !MediatorHelper.model().getResourceAccess().isSearchFileStopped(); tasksHandled++) {
var currentCallable = taskCompletionService.take().get();
if (StringUtils.isNotEmpty(currentCallable.getSourceFile())) {
var name = currentCallable.getPathFile().substring(currentCallable.getPathFile().lastIndexOf('/') + 1, currentCallable.getPathFile().length());
String content = currentCallable.getSourceFile();
String path = currentCallable.getPathFile();
var request = new Request();
request.setMessage(Interaction.CREATE_FILE_TAB);
request.setParameters(name, content, path);
MediatorHelper.model().sendToViews(request);
if (!duplicate.contains(path.replace(name, StringUtils.EMPTY))) {
LOGGER.log(LogLevel.CONSOLE_INFORM, "Shell might be possible in folder {}", () -> path.replace(name, StringUtils.EMPTY));
}
duplicate.add(path.replace(name, StringUtils.EMPTY));
countFileFound++;
}
}
// Force ongoing suspendables to stop immediately
for (CallableFile callableReadFile : MediatorHelper.model().getResourceAccess().getCallablesReadFile()) {
callableReadFile.getSuspendableReadFile().stop();
}
MediatorHelper.model().getResourceAccess().getCallablesReadFile().clear();
taskExecutor.shutdown();
taskExecutor.awaitTermination(5, TimeUnit.SECONDS);
MediatorHelper.model().getResourceAccess().setSearchFileStopped(false);
var result = String.format("Found %s file%s%s on %s files checked", countFileFound, countFileFound > 1 ? 's' : StringUtils.EMPTY, tasksHandled != submittedTasks ? " of " + tasksHandled + " processed " : StringUtils.EMPTY, submittedTasks);
if (countFileFound > 0) {
LOGGER.log(LogLevel.CONSOLE_SUCCESS, result);
} else {
LOGGER.log(LogLevel.CONSOLE_ERROR, result);
}
var request = new Request();
request.setMessage(Interaction.END_FILE_SEARCH);
MediatorHelper.model().sendToViews(request);
}
use of com.jsql.model.bean.util.Request in project jsql-injection by ron190.
the class InjectionModel method testStrategies.
/**
* Find the insertion character, test each strategy, inject metadata and list databases.
* @param isParamByUser true if mode standard/JSON/full, false if injection point
* @param isJson true if param contains JSON
* @param parameter to be tested, null when injection point
* @return true when successful injection
* @throws JSqlException when no params' integrity, process stopped by user, or injection failure
*/
// TODO Merge isParamByUser and parameter: isParamByUser = parameter != null
private boolean testStrategies(boolean isParamByUser, boolean isJson, SimpleEntry<String, String> parameter) throws JSqlException {
// Define insertionCharacter, i.e, -1 in "[..].php?id=-1 union select[..]",
LOGGER.trace(I18n.valueByKey("LOG_GET_INSERTION_CHARACTER"));
// Test for params integrity
String characterInsertionByUser = ParameterUtil.checkParametersFormat(false, isParamByUser, parameter);
// Force to insertion char otherwise.
if (parameter != null) {
String charInsertion = new SuspendableGetCharInsertion().run(characterInsertionByUser, parameter, isJson);
LOGGER.info(I18n.valueByKey("LOG_USING_INSERTION_CHARACTER") + " [" + charInsertion.replace(InjectionModel.STAR, "") + "]");
}
// Fingerprint database
this.vendor = new SuspendableGetVendor().run();
// Test each injection strategies: time, blind, error, normal
StrategyInjection.TIME.instance().checkApplicability();
StrategyInjection.BLIND.instance().checkApplicability();
StrategyInjection.ERROR.instance().checkApplicability();
StrategyInjection.NORMAL.instance().checkApplicability();
// Choose the most efficient strategy: normal > error > blind > time
if (StrategyInjection.NORMAL.instance().isApplicable()) {
StrategyInjection.NORMAL.instance().activateStrategy();
} else if (StrategyInjection.ERROR.instance().isApplicable()) {
StrategyInjection.ERROR.instance().activateStrategy();
} else if (StrategyInjection.BLIND.instance().isApplicable()) {
StrategyInjection.BLIND.instance().activateStrategy();
} else if (StrategyInjection.TIME.instance().isApplicable()) {
StrategyInjection.TIME.instance().activateStrategy();
} else if (PreferencesUtil.isEvasionEnabled() && this.stepSecurity < 3) {
// No injection possible, increase evasion level and restart whole process
this.stepSecurity++;
LOGGER.warn("Injection failed, testing evasion level " + this.stepSecurity + "...");
Request request = new Request();
request.setMessage(Interaction.RESET_STRATEGY_LABEL);
this.sendToViews(request);
// sinon perte de insertionCharacter entre 2 injections
// ConnectionUtil.setQueryString(ConnectionUtil.getQueryString() + this.charInsertion);
this.beginInjection();
return false;
} else {
throw new InjectionFailureException("No injection found");
}
if (!this.isScanning) {
if (!PreferencesUtil.isNotInjectingMetadata()) {
DataAccess.getDatabaseInfos();
}
DataAccess.listDatabases();
}
return true;
}
use of com.jsql.model.bean.util.Request in project jsql-injection by ron190.
the class InjectionModel method testParameters.
/**
* Verify if injection works for specific Method using 3 modes: standard (last param), injection point
* and full params injection. Special injections like JSON and SOAP are checked.
* @param methodInjection currently tested (Query, Request or Header)
* @param paramsAsString to verify if contains injection point
* @param params from Query, Request or Header as a list of key/value to be tested for insertion character ;
* Mode standard: last param, mode injection point: no test, mode full: every params.
* @return true if injection didn't failed
* @throws JSqlException when no params' integrity, process stopped by user, or injection failure
*/
public boolean testParameters(MethodInjection methodInjection, String paramsAsString, List<SimpleEntry<String, String>> params) throws JSqlException {
boolean hasFoundInjection = false;
// or method is selected by user.
if (!PreferencesUtil.isCheckingAllParam() && ConnectionUtil.getMethodInjection() != methodInjection) {
return hasFoundInjection;
}
// Force injection method of model to current running method
ConnectionUtil.setMethodInjection(methodInjection);
// Default injection: last param tested only and no injection point
if (!methodInjection.isCheckingAllParam() && !paramsAsString.contains(InjectionModel.STAR)) {
// Injection point defined on last parameter
params.stream().reduce((a, b) -> b).ifPresent(e -> e.setValue(e.getValue() + InjectionModel.STAR));
// Will check param value by user.
// Notice options 'Inject each URL params' and 'inject JSON' must be checked both
// for JSON injection of last param
hasFoundInjection = this.testStrategies(IS_PARAM_BY_USER, !IS_JSON, params.stream().reduce((a, b) -> b).get());
// Injection by injection point
} else if (paramsAsString.contains(InjectionModel.STAR)) {
LOGGER.info("Checking single " + methodInjection.name() + " parameter with injection point at *");
// Will keep param value as is,
// Does not test for insertion character (param is null)
hasFoundInjection = this.testStrategies(!IS_PARAM_BY_USER, !IS_JSON, null);
// Injection of every params: isCheckingAllParam() == true.
// Params are tested one by one in two loops:
// - inner loop erases * from previous param
// - outer loop adds * to current param
} else {
// inner loop will erase mark * otherwise
for (SimpleEntry<String, String> paramBase : params) {
// For standard value mark * is simply added to the end of its value.
for (SimpleEntry<String, String> paramStar : params) {
if (paramStar == paramBase) {
// Will test if current value is a JSON entity
Object jsonEntity = null;
try {
// Test for JSON Object: {...}
jsonEntity = new JSONObject(paramStar.getValue());
} catch (JSONException exceptionJSONObject) {
try {
// Test for JSON Array: [...]
jsonEntity = new JSONArray(paramStar.getValue());
} catch (JSONException exceptionJSONArray) {
// Not a JSON entity
}
}
// Define a tree of JSON attributes with path as the key: root.a => value of a
List<SimpleEntry<String, String>> attributesJson = JsonUtil.loopThroughJson(jsonEntity, "root", null);
// Marks * are erased between each tests.
if (PreferencesUtil.isCheckingAllJSONParam() && !attributesJson.isEmpty()) {
// Loop through each JSON values
for (SimpleEntry<String, String> parentXPath : attributesJson) {
// Erase previously defined *
JsonUtil.loopThroughJson(jsonEntity, "root", null);
// Add * to current parameter's value
JsonUtil.loopThroughJson(jsonEntity, "root", parentXPath);
// Replace param value by marked one.
// paramStar and paramBase are the same object
paramStar.setValue(jsonEntity.toString());
try {
LOGGER.info("Checking JSON " + methodInjection.name() + " parameter " + parentXPath.getKey() + "=" + parentXPath.getValue().replace(InjectionModel.STAR, ""));
// Test current JSON value marked with * for injection
// Keep original param
hasFoundInjection = this.testStrategies(IS_PARAM_BY_USER, IS_JSON, paramBase);
// Injection successful
break;
} catch (JSqlException e) {
// Injection failure
LOGGER.warn("No " + methodInjection.name() + " injection found for JSON " + methodInjection.name() + " parameter " + parentXPath.getKey() + "=" + parentXPath.getValue().replace(InjectionModel.STAR, ""), e);
} finally {
// Erase * at the end of each params
params.stream().forEach(e -> e.setValue(e.getValue().replaceAll(Pattern.quote(InjectionModel.STAR) + "$", "")));
// Erase * from JSON if failure
if (!hasFoundInjection) {
paramStar.setValue(paramStar.getValue().replace("*", ""));
}
}
}
// Standard non JSON injection
} else {
// Add * to end of value
paramStar.setValue(paramStar.getValue() + InjectionModel.STAR);
try {
LOGGER.info("Checking " + methodInjection.name() + " parameter " + paramBase.getKey() + "=" + paramBase.getValue().replace(InjectionModel.STAR, ""));
// Test current standard value marked with * for injection
// Keep original param
hasFoundInjection = this.testStrategies(IS_PARAM_BY_USER, !IS_JSON, paramBase);
// Injection successful
break;
} catch (JSqlException e) {
// Injection failure
LOGGER.warn("No " + methodInjection.name() + " injection found for parameter " + paramBase.getKey() + "=" + paramBase.getValue().replace(InjectionModel.STAR, "") + " (" + e.getMessage() + ")", e);
} finally {
// Erase * at the end of each params
params.stream().forEach(e -> e.setValue(e.getValue().replaceAll(Pattern.quote(InjectionModel.STAR) + "$", "")));
}
}
}
}
// If injection successful then add * at the end of value
if (hasFoundInjection) {
paramBase.setValue(paramBase.getValue().replace("*", "") + "*");
break;
}
}
}
return hasFoundInjection;
}
use of com.jsql.model.bean.util.Request in project jsql-injection by ron190.
the class DataAccess method listDatabases.
/**
* Get database names and table counts and send them to the view.<br>
* Use readable text (not hexa) and parse this pattern:<br>
* => hh[database name 1]jj[table count]hhgghh[database name 2]jj[table count]hhggh...hi<br>
* Data window can be cut before the end of the request but the process helps to obtain
* the rest of the unreachable data. The process can be interrupted by the user (stop/pause).
* @return list of databases found
* @throws JSqlException when injection failure or stopped by user
*/
public static List<Database> listDatabases() throws JSqlException {
LOGGER.trace(I18n.valueByKey("LOG_FETCHING_DATABASES"));
List<Database> databases = new ArrayList<>();
String resultToParse = "";
try {
String[] sourcePage = { "" };
resultToParse = new SuspendableGetRows().run(MediatorModel.model().getVendor().instance().sqlDatabases(), sourcePage, true, 0, null);
} catch (SlidingException e) {
LOGGER.warn(e.getMessage(), e);
// Get pieces of data already retreived instead of losing them
if (!"".equals(e.getSlidingWindowAllRows())) {
resultToParse = e.getSlidingWindowAllRows();
} else if (!"".equals(e.getSlidingWindowCurrentRows())) {
resultToParse = e.getSlidingWindowCurrentRows();
}
} catch (Exception e) {
LOGGER.warn(e.getMessage(), e);
}
// Parse all data we have retrieved
Matcher regexSearch = Pattern.compile(MODE + ENCLOSE_VALUE_RGX + CELL_TABLE + ENCLOSE_VALUE_RGX).matcher(resultToParse);
if (!regexSearch.find()) {
throw new InjectionFailureException();
}
regexSearch.reset();
// Build an array of Database objects from the data we have parsed
while (regexSearch.find()) {
String databaseName = regexSearch.group(1);
String tableCount = regexSearch.group(2);
Database newDatabase = new Database(databaseName, tableCount);
databases.add(newDatabase);
}
Request request = new Request();
request.setMessage(Interaction.ADD_DATABASES);
request.setParameters(databases);
MediatorModel.model().sendToViews(request);
return databases;
}
use of com.jsql.model.bean.util.Request in project jsql-injection by ron190.
the class DataAccess method listTables.
/**
* Get tables name and row count and send them to the view.<br>
* Use readable text (not hexa) and parse this pattern:<br>
* => hh[table name 1]jj[rows count]hhgghh[table name 2]jj[rows count]hhggh...hi<br>
* Data window can be cut before the end of the request but the process helps to obtain
* the rest of the unreachable data. The process can be interrupted by the user (stop/pause).
* @param database which contains tables to find
* @return list of tables found
* @throws JSqlException when injection failure or stopped by user
*/
public static List<Table> listTables(Database database) throws JSqlException {
// Reset stoppedByUser if list of Databases is partial
// and some Tables are still reachable
MediatorModel.model().setIsStoppedByUser(false);
List<Table> tables = new ArrayList<>();
// Inform the view that database has just been used
Request requestStartProgress = new Request();
requestStartProgress.setMessage(Interaction.START_PROGRESS);
requestStartProgress.setParameters(database);
MediatorModel.model().sendToViews(requestStartProgress);
String tableCount = Integer.toString(database.getChildCount());
String resultToParse = "";
try {
String[] pageSource = { "" };
resultToParse = new SuspendableGetRows().run(MediatorModel.model().getVendor().instance().sqlTables(database), pageSource, true, Integer.parseInt(tableCount), database);
} catch (SlidingException e) {
LOGGER.warn(e.getMessage(), e);
// Get pieces of data already retreived instead of losing them
if (!"".equals(e.getSlidingWindowAllRows())) {
resultToParse = e.getSlidingWindowAllRows();
} else if (!"".equals(e.getSlidingWindowCurrentRows())) {
resultToParse = e.getSlidingWindowCurrentRows();
}
} catch (Exception e) {
LOGGER.warn(e.getMessage(), e);
}
// Parse all the data we have retrieved
Matcher regexSearch = Pattern.compile(MODE + ENCLOSE_VALUE_RGX + CELL_TABLE + ENCLOSE_VALUE_RGX).matcher(resultToParse);
Request requestEndProgress = new Request();
requestEndProgress.setMessage(Interaction.END_PROGRESS);
requestEndProgress.setParameters(database);
MediatorModel.model().sendToViews(requestEndProgress);
if (!regexSearch.find()) {
throw new InjectionFailureException();
}
regexSearch.reset();
// Build an array of Table objects from the data we have parsed
while (regexSearch.find()) {
String tableName = regexSearch.group(1);
String rowCount = regexSearch.group(2);
Table newTable = new Table(tableName, rowCount, database);
tables.add(newTable);
}
Request requestAddTables = new Request();
requestAddTables.setMessage(Interaction.ADD_TABLES);
requestAddTables.setParameters(tables);
MediatorModel.model().sendToViews(requestAddTables);
return tables;
}
Aggregations