As per discussions with
On macOS, gaining shellcode execution from arbitrary memory read/write in a WebKit renderer only requires finding and writing to the JIT region. However, on iOS a combination of APRR [1] and PAC [2] protect the JIT region from an attacker with arbitrary read/write.
WebKit has support for in-process signal handling. This is for example used by some JIT optimizations in JSC [3]. The main signal handler is `catch_mach_exception_raise_state` in Signals.cpp [4], which will traverse a linked list of handlers and call each one of them. If any of the handlers returns success, the signal is treated as handled and the thread will continue.
This enables the following attack:
1. The linked list of handlers is turned into a cycle, causing
`catch_mach_exception_raise_state` to loop infinitely upon catching
a signal
2. A crash is triggered in another thread, for example in a
WebWorker. A GCD thread is now \"stuck\" in
`catch_mach_exception_raise_state`
3. The main thread searches the stacks for the stackframe of
`catch_mach_exception_raise_state`. Once found, it has access to
the reply mach message of `catch_mach_exception_raise_state` and
with that to the context (registers + stack) of the crashed thread.
It can modify them arbitrarily except for PC which is protected by
PAC. After modifying the state and marking the exception as
property handled in the reply message, it fixes the linked list of
handlers, causing `catch_mach_exception_raise_state` in the other
thread to finish
4. The crashed thread now resumes execution with
attacker-controlled registers and/or stack content
It should also be possible to catch multiple signals following each other by first making a copy of the handlers list/cycle, then swapping the \"active\" and \"inactive\" exception hander lists before repairing the now inactive handler list. The current exception handler will then return, but if a new exception is immediately raised, the handling thread will again be stuck in `catch_mach_exception_raise_state` as it uses the other list which is still a cycle. It is also worth noting that it should be possible to modify the global `activeExceptions` variable in Signals.cpp prior to the installation of signal handlers, thus allowing the attacker to control which exceptions are handled.
This \"debugger\" now immediately allows brute-forcing PACs as
PAC mostly relies on conventional access violations when failing.
Moreover, it allows PAC to be bypassed trivially for some pointers,
namely in cases where the authentication and use are two separate
instructions, with the second instruction triggering a crash. The
PoC demonstrates this by bypassing the PAC protecting a
TypedArray's backing storage pointer: first, a TypedArray's backing
storage pointer in a worker is corrupted, then accessed. This will
cause the AUTDB instruction to fail, leaving the pointer clobbered
and causing a crash when the pointer is subsequently accessed.
Next, this crash is \"handled\" with the debugger and the register
containing the
clobbered pointer is replaced with an arbitrary pointer. The worker
then continues and re-executes the access instruction which now
succeeds and thus accesses an address of the attacker's
choosing.
With this, it should now be possible to achieve arbitrary native
code execution (i.e. bypassing the JIT hardening). Possible ideas
for that are:
- Corrupt the AssemblerBuffer so arbitrary instructions are copied
into the JIT region by the LinkBuffer. This will cause the computed
hashes to mismatch and the linker to crash, but that only happens
after the instructions have been copied and the crash can then
simply be caught
- Crash during one of the writes into the JIT region in
LinkBuffer::copyCompactAndLinkCode (by corrupting the destination
pointer prior to that) and change the content of the source
register so that an arbitrary instruction is written into the JIT
region while the original instruction is used for the hash
computation
- Crash during LinkBuffer::copyCompactAndLinkCode and resume
execution somewhere else. This should leave the JIT region writable
(although not executable) for that thread
- Brute-force a PAC code (e.g. by repeatedly accessing, crashing,
and then changing a PAC protected pointer), then JOP into one of
the functions into which performJITMemcpy is inlined
[1] https://siguza.github.io/APRR/
[2]
https://github.com/apple/llvm-project/blob/apple/master/clang/docs/PointerAuthentication.rst
[3]
https://github.com/WebKit/webkit/blob/015fb86d51851fc3e13f05898c85d62d0b1bae8f/Source/JavaScriptCore/runtime/OptionsList.h#L466
[4]
https://github.com/WebKit/webkit/blob/4ceb36e525b55b9d49aed0b400507d522953e025/Source/WTF/wtf/threads/Signals.cpp#L137
This bug is subject to a 90 day disclosure deadline. After 90
days elapse,
the bug report will become visible to the public. The scheduled
disclosure
date is 2020-08-13. Disclosure at an earlier date is possible
if
agreed upon by all parties.
Related CVE Numbers: CVE-2020-9910
Found by:
Read more https://packetstormsecurity.com/files/158870/GS20200814160719.txt

