CVE-2022-28672 _ Foxit PDF Reader UAF RCE
🦊

CVE-2022-28672 _ Foxit PDF Reader UAF RCE

⚠️ [ ORIGIN SOURCE ]
https://hacksys.io/blogs/foxit-reader-uaf-rce-jit-spraying-cve-2022-28672
📅 [ Archival Date ]
Dec 18, 2022 10:46 PM
🏷️ [ Tags ]
FoxitPDFWindows
✍️ [ Author ]

Krishnakant Patil, Ashfaq Ansari

💣 [ PoC / Exploit ]
https://crash.link/cve-2022-28672

Overview

In the first part of the PDF Reader series, we shared details about an exploitable bug that we found in Adobe Acrobat Reader. This bug, which was an Out of Bounds Read caused by treating ANSI strings as Unicode, allowed us to leak sensitive information from the sandboxed Adobe Reader process.

Adobe Reader - XFA - ANSI - Unicode Confusion Information Leak

In the second part of the series, we will be discussing another vulnerability that we discovered while assessing the security of popular PDF readers. This time, we found a use-after-free vulnerability and several other crashes in Foxit PDF Reader during fuzz testing.

We were able to successfully exploit this vulnerability to gain Remote Code Execution in the context of Foxit PDF Reader.

Zero Day Initiative (ZDI) purchased this exploit, despite it being a bug collision.

Advisory

CVE-2022-28672

Testbed

  • Host OS: Windows 10 Pro 20H2 19042.804
  • Product: Foxit PDF Reader 11.1.0.52543 (x86)Product
  • URL: https://www.foxitsoftware.com/downloads/

Crash State

A quick verification using the !heap command reveals that this is a use-after-free vulnerability.

Proof of Concept

The test case includes static form fields in PDF and javascript action to manipulate them, causing a crash.

Static PDF fields

Faulting Javascript

Root Cause Analysis

The code crashes when trying to access an object using this pointer.

int __thiscall sub_1734610(_DWORD *this)
{
  int v1; // eax
  bool v2; // cl

  v1 = this[11];  // CRASH while deferencing this pointer
  v2 = 0;
  if ( v1 )
    v2 = *(_DWORD *)v1 != 0;
  if ( v2 && v1 )
    return *(_DWORD *)v1;
  else
   return 0
}

Stack trace analysis reveals that the sub_1729070 function allocates a Widget related object of size 0x64 when the setFocus method is called on this.getField("field_10").setFocus() in the javascript.

This allocation occurs when the sub_1729070 function is called, which returns different-sized objects based on a type check. In this case, the switch case condition 5 is satisfied, resulting in an object of size 0x64 being returned.

This can be verified by using a debugger.

The closeDoc function invokes the calculate handler on field_15. Inside the calculate callback, we set the items property of the choice list field f1, which has a registered format callback. Setting the items property invokes its format callback, which deletes the 0th page and potentially deletes the target object.

When the document is closed, the sub_172B3A0 function is called.

The sub_1729070 function returns the target object, which was previously created during the setFocus call. This object is then passed to the indirect call (*(unsigned __int8 (__thiscall **)(int, _DWORD ))((_DWORD *)v4 + 80))(v4, a2).

This code path then invokes the format callback registered on field_12. Within the format callback, the target object is freed when this.deletePages function is called.

The sub_173A900 function is responsible for freeing the target object of size 0x64, which is later accessed by sub_1734610 causing the program to crash.

Exploitation

If we can control and reallocate the same size allocation, we may be able to gain direct control over code execution using the call instruction inside sub_172ADA0. This is shown below:

eax=41414141 ebx=0e847e88 ecx=0e8c7960 edx=00000000 esi=0e8c7960 edi=00000002
eip=01c2ade1 esp=080fa8dc ebp=080fa910 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x277e41:
01c2ade1 ff5074          call    dword ptr [eax+74h]  ds:002b:414141b5=????????

The following script can be used to groom the heap to crash the Foxit Reader process at a controlled location. During testing, it was discovered that support for ArrayBuffer was disabled in Foxit Reader. This is likely done as a preventative measure to prevent exploitation using common javascript exploit primitives such as heap spraying and out-of-bound read/write. However, it was found that SharedArrayBuffer was not disabled and could be used for the same purpose.

In the sprayed memory blocks, the eax register points to the start of the memory buffer, and an indirect call at an offset of 0x74 indicates that it is a C++ object inside Foxit where a virtual method is being invoked. This can be used to execute arbitrary code in the context of the Foxit process.

By carefully controlling the heap spraying process using the provided script, it is possible to crash Foxit Reader at a specific location when a virtual method is invoked. This allows the attacker to control the state of the object and potentially execute arbitrary code in the context of the Foxit process.

Bypassing Mitigations

Data Execution Prevention (DEP)

One way to bypass DEP and execute user-controlled code in memory is to use return-oriented programming (ROP). This involves chaining together short sequences of code, called gadgets, that are already present in the program's memory. By carefully selecting gadgets and arranging them in a specific order, it is possible to execute arbitrary code without needing to directly call the sprayed shellcode. This can be difficult to achieve, but there are tools and resources available to help with the process.

Our bug is user-after-free of an object on the heap which allows us to call arbitrary addresses in memory using a virtual function call. Although heap spraying with user-controlled data is possible, the heap memory does not have execute permissions. Hence, we cannot call shellcode sprayed using heap-spraying.

To bypass DEP, we need to have an arbitrary read/write primitive and ROP chain to create an executable memory range which we don't have.

Control Flow Guard (CFG)

Control Flow Guard (CFG) is a mitigation technique that is designed to prevent attackers from calling arbitrary call sites. CFG is used to protect indirect calls and is present in most modern software. However, in the case of Foxit, the software was not compiled with CFG support, which means that attackers can call any memory address within the Foxit address space. This lack of CFG support makes Foxit vulnerable to exploitation by attackers.

Address Space Layout Randomization (ASLR)

Foxit PDF Reader has Address Space Layout Randomization (ASLR) enabled, which means that we cannot use any hardcoded addresses in the exploit to call the shellcode. To bypass ASLR, we need some kind of heap-leaking primitive (info-leak), but we do not have one available.

JIT Spraying to rescue! Bypassing DEP, ASLR at once.

JIT spraying is a technique that can be used to bypass both Data Execution Prevention (DEP) and Address Space Layout Randomization (ASLR) at the same time. Foxit, a popular PDF viewer, ships with the Google V8 javascript engine as a backend for processing javascript within PDF files. Testing revealed that Foxit is vulnerable to JIT spraying.

JIT, or Just In Time Compilation, is commonly used within javascript engines to improve performance by converting javascript bytecode into native architecture-specific code. To do this, the JIT compiler must create a memory with read-write-execute permissions to store the compiled code. There are various ways to invoke the JIT compiler within a script engine.

rh0dev has done excellent research on JIT spraying, particularly on the use of the asm.js feature of javascript for JIT spraying. This technique allows the attacker to spray encoded shellcode using asm.js, enabling them to bypass DEP and ASLR protections.

  1. The Return of the JIT
  2. Github Repository

After several attempts, we were able to create a JIT spray for v8 inside Foxit.

We can confirm the shellcode spraying by looking at the base of any allocation above it.

0:000> u 18ca0000
18ca0000 e97b470000      jmp     18ca4780
18ca0005 e956470000      jmp     18ca4760
18ca000a cc              int     3
18ca000b cc              int     3
18ca000c cc              int     3

The first jump is to the generated code at 18ca4780, which in our case contains our encoded shellcode.

The JIT spraying script has been stripped for readability. The full source can be found in the exploit on GitHub.

In this case, we are using the best-educated guess of 0x19b40000 for the shellcode execution, where one of our JIT sprays is located.

0:025> u 19b40000
19b40000 e97b470000      jmp     19b44780
19b40005 e956470000      jmp     19b44760
19b4000a cc              int     3
19b4000b cc              int     3
19b4000c cc              int     3

In the JIT spray script, a hardcoded address derived from the assumed base address of 0x19b447a2|0, // using predicated 19b40000 base is used as the starting point for the shellcode. This address is referenced by a call instruction, as can be verified using a debugger. This allows us to execute the shellcode at a known location in memory, bypassing DEP and ASLR protections.

0:025> ? 19b44729+74
Evaluate expression: 431245213 = 19b4479d

0:025> dd 19b4479d
19b4479d  19b447a2 90909068 c93168a8 6a68a890
19b447ad  68a85830 a8008b64 0c408b68 708b68a8
19b447bd  ad68a814 68a8ad96 a810588b 3c538b68

The execution of the shellcode must start from 19b447a2. This can be verified in the debugger.

By analyzing the decoded shellcode, it is possible to see that it contains a series of valid instructions that carry out the desired actions. This indicates that the JIT spraying technique was successful in allowing us to execute our shellcode in the context of the Foxit process.

Conclusion

In conclusion, this research shows that if Foxit Reader had been compiled with Control Flow Guard (CFG) support, the discovered bug would have been more difficult to exploit. However, the lack of CFG support allowed the attacker to use JIT spraying to bypass existing mitigations such as ASLR and DEP. This highlights the importance of using multiple layers of defense to protect against attacks.

Exploit Repository

https://github.com/hacksysteam/CVE-2022-28672

Demo Video