Jamf identified and reproduced an Out-Of-Bounds (OOB) write vulnerability that can be triggered by opening a malformed PDF. This vulnerability reminded us of the FORCEDENTRY vulnerability exploited by NSO/Pegasus according to The Citizen Lab’s blog.
As a brief background, Jamf analyzed several devices of Al-Jazeera journalists in the summer of 2020 and automatically and successfully found compromised devices without relying on any IOC. These attacks were later attributed to NSO/Pegasus.
Also of note, although these two vulnerabilities are different – they are similar and worth a deeper read.
- We reported this vulnerability on September 1st, 2020 – iOS 14 beta was vulnerable at the time.
- The vulnerability was patched on September 14th, 2020 – iOS 14 beta release.
- Apple contacted us on October 20, 2020 – claiming that the bug was already fixed – (“We were unable to reproduce this issue using any current version of iOS 14. Are you able to reproduce this issue using any version of iOS 14? If so, we would appreciate any additional information you can provide us, such as an updated proof-of-concept.”).
- No CVE was assigned.
It is possible that NSO noticed this incremental bug fix and dived deeper into CoreGraphics.
Earlier last year, we obtained a PDF file that cannot be previewed on iOS. The PDF sample crashes
previewUI with a segmentation fault, meaning that a memory corruption was triggered by the PDF. When opening the PDF,
previewUI flashes and shows nothing.
The important question is: how do we find out the source of the memory corruption?
The macOS preview works fine, with no crash. Meaning that it’s the iOS library that might have an issue. We confirmed the assumption with the iPhone Simulator since the crash happened on the iPhone Simulator.
It’s great news since Simulator on macOS provides better debug tools than iOS. However, debug capability is not enough since the process crashes only when the corrupted memory is being used, after the actual memory corruption.
We need to find a way to trigger the crash right at the point the memory corruption happens.
“Guard Malloc is a special version of the malloc library that replaces the standard library during debugging. Guard Malloc uses several techniques to try and crash your application at the specific point where a memory error occurs. For example, it places separate memory allocations on different virtual memory pages and then deletes the entire page when the memory is freed. Subsequent attempts to access the deallocated memory cause an immediate memory exception rather than a blind access into memory that might now hold other data.”
Environment Variables Injection
In this case, we cannot simply add an environment variable with the command line since the previewUI launches on clicking the PDF – which does not launch from the terminal. We need to inject
libgmalloc before the launch.
launchd_sim launches Simulator XPC services with a trampoline process called
xpcproxy_sim launches target processes with a
posix_spawn system call, allowing us to inject environment variables into the target process, in this case,
process attach –name xpcproxy_sim –waitfor allows us to attach xpcproxy_sim and then set a breakpoint on
posix_spawn once it’s launched.
posix_spawn breakpoint is hit, we are able to read the original environment variables by reading the address stored in the
With a few simple
lldb expressions, we are able to overwrite one of the environment variables into
DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib, injection complete.
Continuing execution, the process crashed almost right away.
Analyzing the Crash
Finally, we got the Malloc Guard working as expected, the
previewUI crashes right at the
memmove function that triggers the memory corruption.
libgmalloc injection we have the following backtrace that shows an Out-Of-Bounds write occurs in
With the same method, we can take one step further, with the MallocStackLogging flag
libgmalloc provides, we can track the function call stack at the time of each allocation.
After setting the
MallocStackLoggingNoCompact=1, we got the following backtrace showing that the allocation was inside
The OOB-Write vulnerability happens in the function
CGDataProviderDirectGetBytesAtPositionInternal of the CoreGraphics library. The allocation of the target memory was inside the function
It allocates 16 bytes of memory if the bits_per_pixel equals or less than 1 byte, which is less than copy length.
We came out with a minimal PoC and reported to Apple on September 1st, 2020, the issue was fixed on the iOS 14 release. We will release this POC soon.
Jamf protects against this and numerous other security threats impacting your enterprise fleet.
Have market trends, Apple updates and Jamf news delivered directly to your inbox.