The "Type 2 Charstring Format" specification from 5 May 1998 specified two storage operators which were later removed from the document: `store` and `load`. These operators were responsible for copying data between the transient array (also known as the BuildCharArray) and the so-called "Registry object" (also deprecated by now, but still supported by Adobe implementations).
As the specs state:
"""
The Registry provides more permanent storage for a number of items that have predefined meanings. The items stored in the Registry do not persist beyond the scope of rendering a font. Registry items are selected with an index, thus:
0 Weight Vector
1 Normalized Design Vector
2 User Design Vector
The result of selecting a Registry item with an index outside this list is undefined.
"""
However, the parameter sanity checking implemented for both operators - although present - is flawed. There is an off-by-one error in the verification of the Registry index, allowing for index 3 to be used:
---
.text:0003CA35 cmp eax, 3
.text:0003CA38 ja loc_3BEC4 ; jumptable 0003BF69 case 2
---
Internally, the Registry is implemented as a three-item array of structures similar to the following:
---
struct REGISTRY_ITEM {
int32 size;
void *data;
} Registry[3];
---
Accessing the 4th element of the array results in a reference to uninitialized memory from the Session Paged Pool. If the value of the "size" field happens to be positive, the "data" pointer is used as the "source" parameter of a "memcpy" call in case of the `load` instruction, and as "dest" in case of `store`.
With the ability to perform kernel pool massaging, it is possible to transform the bug into a read/write-what-where primitive by controlling the contents of the uninitialized memory used as source/destination addresses, which then easily leads to elevation of privileges and full system security compromise.
The issue is reproducible with Type1 and OTF fonts -- attached are reproducers for both formats, which can be opened in the standard Windows Font Viewer utility to trigger a bugcheck. Please note that in order for the system to crash, the "size" field must be positive and the "data" pointer must point into kernel-mode memory (ATMFD.DLL silently catches exceptions caused by invalid user-mode references), so it might require a few attempts. On the other hand, a local exploit is believed to be able to take advantage of the vulnerability with near 100% reliability.
If the kernel pool is sprayed such that size=0x55555555 and data=0xaaaaaaaa, the following bugcheck is triggered on a fully patched Windows 8.1 32-bit:
---
PAGE_FAULT_IN_NONPAGED_AREA (50)
Invalid system memory was referenced. This cannot be protected by try-except,
it must be protected by a Probe. Typically the address is just plain bad or it
is pointing at freed memory.
Arguments:
Arg1: aaaaaaaa, memory referenced.
Arg2: 00000001, value 0 = read operation, 1 = write operation.
Arg3: 994f8c00, If non-zero, the instruction address which referenced the bad memory
address.
Arg4: 00000002, (reserved)
---
A full crash log can be found in "crash.txt".
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.