|
|
Windows Kernel ATMFD.DLL unlimited out-of-bounds stack manipulation via BLEND operator | ||||||
| Project Member Reported by mjurczyk@google.com, Nov 20 2014 | Back to list | ||||||
The `blend` operator - as described in "The Type 2 Charstring Format, 18 March 1998" - is supposed to take n + 1 parameters from the operand stack, with the 16-bit signed value "n" being passed as the first argument. The implementation of the operator found in the Adobe Type Manager Font Driver (ATMFD.DLL) in the Windows kernel does not expect that value to be negative and mishandles this corner case, leading to catastrophic consequences for system security.
Specifically, the `blend` handler does perform some validation checks against "n" in order to check that the (again, signed) number is not greater than the current size of the stack, or that the stack pointer is not already out of stack bounds when entering the code:
---
case cf2_cmdBLEND:
if ( op_sp < &op_stk[1] || op_sp > &op_end )
...
if ( !master_designs && &op_sp[n] >= &op_end )
...
if ( &op_stk[n * master_designs] > op_sp )
...
op_sp = DoBlend(op_sp,
font->weight_vector,
font->master_designs,
n);
---
A negative 16-bit number passes all of the above checks and is further provided as the last parameter to the "DoBlend" function. When confronted with such a number, that function shifts the operand stack pointer up by the following number of bytes:
-n * (master_designs - 1) * 4
This condition is badly dangerous because of the fact that the 48-item long operand stack is implemented as a regular array on the interpreter function's stack, thus being able to shift the operand stack pointer out of bounds gives the power to manipulate the regular kernel stack in any desired way. An attacker is able to illegally add a maximum value of 32768 * 15 * 4 = 1966080 (0x1E0000) to the stack pointer; on the other hand, by setting "master_designs" to 2 (in case of ATMFD.DLL, this can be achieved by inserting a PostScript array "WeightVector" with two elements into the font dict), one is able to shift the pointer with great accuracy (any multiply of 4). For example, if the return address of the interpreter function is positioned 128 dwords ahead of the "op_stk" array, the two instructions below will set "op_sp" to that address:
-128 blend
At first glance it is not trivial to turn the condition into an arbitrary stack manipulation primitive, because the standard opcodes used for pushing values to the stack do check that "op_sp" has not gone beyond the "op_stk" memory area. However, various operators which write to the stack but don't increase "op_sp" (because they also take parameters from the stack) don't perform such checks, and so can be used to manipulate the data under any "op_sp". These operators are:
1) not
2) neg
3) abs
4) sqrt
5) index
6) div
7) add
8) sub
9) mul
10) get
In particular, a sequence of `put`, `blend`, and several `sqrt` followed by `get` instructions can be used to successfully manipulate the data on stack in any desired way. This is not just about reading and writing - the CharString can perform arithmetic operations such as addition or subtraction, thus it is possible to build a full ROP chain on the kernel stack and reliably hijack ring-0 code execution solely from within the font VM, with no external help. This is very similar to a FreeType vulnerability exploited by comex in his iOS jailbreak [1], only here the attacker gets even more control over the control flow, because the entire kernel stack is fully controlled.
The vulnerability is reproducible with Type1 fonts. It is not reproducible (to our current knowledge) with OpenType fonts, because it requires the number of master designs in the font to be non-zero. The attached testcase ("poc.pfm" + "poc.pfb") crashes a fully patched Windows 8.1 32-bit, by using the following charstring for letter 'a':
/a ## -| { -349 blend exch endchar } |-
together with a "WeightVector" of length 2. This causes the "op_sp" pointer to be shifted forward by 0x574 bytes (4 bytes ahead of the interpreter function's return address), and further dereferenced by the `exch` operator. Therefore, the following crash occurs due to an attempt to execute a non-executable stack address (as the return address and stack frame pointer are exchanged), when the font is opened with the standard Windows Font Viewer utility:
---
ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY (fc)
An attempt was made to execute non-executable memory. The guilty driver
is on the stack trace (and is typically the current instruction pointer).
When possible, the guilty driver's name (Unicode string) is printed on
the bugcheck screen and saved in KiBugCheckDriver.
Arguments:
Arg1: 97ebf6a4, Virtual address for the attempted execute.
Arg2: 11dd2963, PTE contents.
Arg3: 97ebf56c, (reserved)
Arg4: 00000002, (reserved)
---
A full crash log can be found in the "crash.txt" file. All 32-bit versions of Windows up to 8.1 are affected by the vulnerability. While the faulty expression in C code remains the same for both 32 and 64-bit architectures, the "n * master_designs" factor is cast to a 32-bit unsigned integer, causing the entire bounds check expression to evaluate to "true" on 64-bit platforms, if "n" is a negative 16-bit number:
---
if ( (unsigned __int64)((char *)&op_stk + 4 * (unsigned int)(n * master_designs)) > op_sp )
{
v111 = (__int64)"op_stk + inst->lenWeightVector*nArgs <= op_sp";
---
On the other hand, the vulnerability can be successfully exploited in sandboxed environments, if the win32k lock-down option is not in force. Typically installing a font in the system would require file system access; however, it is also possible to achieve the goal through an undocumented win32k!NtGdiAddRemoteFontToDC system call, which can load font resources consisting of multiple files from memory.
References:
[1] http://esec-lab.sogeti.com/post/Analysis-of-the-jailbreakme-v3-font-exploit
Project Member
Comment 1
by
mjurczyk@google.com,
Dec 4 2014
,
Dec 11 2014
,
Feb 6 2015
This bug is subject to a 90 day disclosure deadline. If 90 days elapse without a broadly available patch, then the bug report will automatically become visible to the public. The message was communicated to the vendor while reporting the vulnerability.
,
Mar 24 2015
,
Apr 1 2015
,
Apr 20 2015
,
Jun 12 2015
|
|||||||
| ► Sign in to add a comment | |||||||