# CVE-2023-26369: Adobe Acrobat PDF Reader RCE when processing TTF fonts

Clement Lecigne, Google Threat Analysis Group

## The Basics

**Disclosure or Patch Date:** September, 12, 2023

**Product:** Adobe Acrobat Reader on Windows and MacOS

**Advisory:** https://helpx.adobe.com/security/products/acrobat/apsb23-34.html

**Affected Versions:** 23.003.20284 and earlier versions

**First Patched Version:** 23.006.20320

**Issue/Bug Report:** N/A

**Patch CL:** N/A (closed source)

**Bug-Introducing CL:** N/A

**Reporter(s):** Anonymous

## The Code

**Proof-of-concept:**

TTF font with the following bitmap tables.

```
EBLC :
     version : 0x20000
     numSizes : 0x1
     [-] 0th bitmapSizeTable
         indexSubTableArrayOffset : 0x38
         indexTablesSize : 0x100
         numberOfIndexSubTables : 0x2
         colorRef : 0x0
         hori : 0b fe 08 01 00 00 00 00 0b fe 00 00
         vert : 00 00 00 00 00 00 00 00 00 00 00 00
         startGlyphIndex : 0x4d
         endGlyphIndex : 0x4e
         ppemX : 0xfa
         ppemY : 0xfa
         bitDepth : 0x1
         flags : 0x1
         [-] 0th subArray
             firstGlyphIndex : 0x4d
             lastGlyphIndex : 0x4d
             additionalOffsetToIndexSubtable : 0x10
             indexFormat : 0x1
             imageFormat : 0x8
             imageDataOffset : 0x4
             offsetArray0 : 0x0
             offsetArray1 : 0x0
         [-] 1th subArray
             firstGlyphIndex : 0x4e
             lastGlyphIndex : 0x4e
             additionalOffsetToIndexSubtable : 0x20
             indexFormat : 0x1
             imageFormat : 0x1
             imageDataOffset : 0x4
             offsetArray0 : 0x0
             offsetArray1 : 0xc
EBDT :
     version : 0x20000
     [-] 0th metrics
         height : 0xa2
         width : 0x61
         BearingX : 0x4
         BearingY : 0x9
         Advance : 0x8
         pad : 0x0
         numComponents : 0x1
         [-] 0th ComponentArray
             glyphCode : 0x4e
             xOffset : 0x20
             yOffset : 0xa3
     [-] 1th metrics
         height : 0xa2
         width : 0x61
         BearingX : 0x4
         BearingY : 0x9
         Advance : 0x8
         pad : 0x0
         numComponents : 0x1
         [-] 0th ComponentArray
             glyphCode : 0x4e
             xOffset : 0x20
             yOffset : 0xa3
```

Note that the font has the following `EBSC` table which prevents the font from
being loaded in many font parsing libraries except libCoolType from Adobe.

```
2. 'EBSC' - checksum = 0x00000000, offset = 0xffffffff, len = 4294967295
```

**Exploit sample:** Not public.

**Did you have access to the exploit sample when doing the analysis?** Yes

## The Vulnerability

**Bug class:** Heap buffer out-of-bounds write

**Vulnerability details:**

An out of bound (OOB) write vulnerability exists in `sfac_GetSbitBitmap` when
parsing a malformed TrueType font in Adobe libCoolType.

By enabling `PageHeap` for Adobe Reader, we get the following crash in
`CoolType!CTCleanup+0x3ed54`:

```
rax=00000000000000ff rbx=0000000000000001 rcx=fffffffff8a29014
rdx=0000000000000004 rsi=000000000000ffff rdi=0000000000000004
rip=00007ffc84736144 rsp=000000ab75f56fa0 rbp=0000000000000000
 r8=0000000000000010  r9=0000027bc9557000 r10=0000027bc1f80010
r11=0000000000000004 r12=000000000000fff3 r13=0000000000000001
r14=0000027b76539ba0 r15=0000000000000001
iopl=0         nv up ei ng nz na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010286
CoolType!CTCleanup+0x3ed54:
00007ffc`84736144 42080409        or      byte ptr [rcx+r9],al ds:0000027b`c1f80014=??
```

The code is attempting to bitwise OR the byte located at `r9[rcx]` with 0xFF
(`al`) and `r9[rcx]` is out of bound.

By tracing the code, we noticed that `r9` is a bitmap buffer which was
previously allocated in `sbit_GetMetrics` using the following formula and data
from the font file.

```
bitmap_size
 = (((EBDT[0].width + 0x1F) >> 3) & 0xFFFC) * EBDT[0].height
 = (((0x61+0x1f)>>3)&0xfffc)*0xa2 = 0xA20
```

The OOB write happens in `sfac_GetSbitBitmap` when glyphs are merged together in
the bitmap buffer. To properly position the glyphs, each glyph has layout
information located in the `EBDT` table, including height, width, advance, X and
Y offsets. `GetSbitComponent` makes recursive calls to `sfac_GetSbitBitmap` to
handle the glyphs one by one. The problem with `sfac_GetSbitBitmap` is that it
merges glyphs without first checking if the X and Y offsets are within the
bounds of the bitmap buffer. For example with the font above, the OOB happens
when positioning the second glyph on the vertical axis (Y offset).

```
index = glyph[curr].yOffset * (glyph[curr].Advance+glyph[curr-1].Advance)
  = 0xa3 * (0x8 + 0x8)
  = 0xa30
```

`0xa30` is outside of the allocated bitmap buffer.

**Patch analysis:**

The vulnerability was patched by adding multiple bound-checks [0] [1] before
entering the loop transforming the bitmaps in `sfac_GetSbitBitmap`. The size
allocated for the bitmap buffer by `sbit_GetMetrics` is also now passed as a new
parameter (`max_sz`) to `sfac_GetSbitBitmap`.

Pseudo-code:

```c
int sfac_GetSbitBitmap(..., uint16 a12_yOffset, uint16 curr_advance, ..., uint32 max_sz)
...
    v25 = a12_yOffset * curr_advance;           // size
    if ( v25 >= max_sz )                        // [0] bound check
    {
        // err
        return 5120;
    }
...
    if (height) n_iter = a12_yOffset + height;
    max_idx = a13_somesize * n_iter;
    if ( max_idx >= lower_bound && max_idx < max_sz )   // [1] bound check
    {
...
        do
        {
...
            do
            {
                *((_BYTE *)data + idx++) |= *(_BYTE *)data;     // OOB before
                data = (unsigned __int16 *)((char *)data + 1);
                n_iter--;
            } while (n_iter)
...
            idx += curr_advance;
            --height;
        }
        while (height);
    }
```

**Thoughts on how this vuln might have been found *(fuzzing, code auditing,
variant analysis, etc.)*:**

3 possible options:

1.  Found via smart TTF fuzzing against the libCoolType shipped with Adobe
    Reader.
1.  Binary diffing various library forks (e.g. both TrueType from MSFT and
    CoolType from Adobe share the `sbit/sfac` functions) to spot discrepancies.
    The vulnerability is very similar to CVE-2011-3402 affecting the TrueType
    font parsing engine in win32k and exploited in the wild by Duqu.
1.  Code auditing around bitmap size calculation and usage.

Out of these options, I consider 1) or 2) to be the most likely ones.

**(Historical/present/future) context of bug:** N/A

## The Exploit

**Exploit strategy (or strategies):**

Through the vulnerability to achieve out-of-bounds write, and corrupting
adjacent EScript objects previously allocated from the PDF.

**Exploit flow:**

1.  Activate LFH allocator on various bucket sizes by allocating multiple
    objects of specific size from Javascript.
2.  Spray a pair of `ArrayBuffer` and `Array` objects on the heap.
3.  Load the malicious font triggering the vulnerability by corrupting one byte
    of the `length` field of the `ArrayBuffer`.
4.  Find corrupted `ArrayBuffer`, allowing limited R/W.
5.  Override `length` of the adjacent `Array` using the corrupted `ArrayBuffer`
    to have complete arbitrary R/W and setup the `addrof` primitive.
6.  Use the arbitrary R/W and `addrof` primitive to compute the ROP chain and
    write the shellcode in memory.
7.  Without CFG, the exploit simply overrides the `TextSize` VTable of a
    `TextBox` object and trigger its call to hijack the execution flow.
8.  With CFG on, the exploit calls `RtlCaptureContext` to leak the stack address
    and then overrides return pointers on the stack to redirect the execution
    flow to their ROP chain.

**Known cases of the same exploit flow:**

Most exploits abusing memory corruption vulnerability in Adobe Reader such as
[CVE-2020-9715](https://www.zerodayinitiative.com/blog/2020/9/2/cve-2020-9715-exploiting-a-use-after-free-in-adobe-reader)
have a similar exploit flow.

**Part of an exploit chain?**

Yes, this bug
[was used](https://blog.google/threat-analysis-group/active-north-korean-campaign-targeting-security-researchers/)
by government backed actors in North Korea as a way to get RCE within Adobe
Reader, we didn't manage to retrieve or get access to the other stages of the
chain like the sandbox escape.

## The Next Steps

### Variant analysis

**Areas/approach for variant analysis (and why):**

1.  Run extensive fuzzing of the libCoolType TTF parsers.
1.  Code auditing around bitmap usage and size computation in TTF parsers.
1.  Analyze differences between Microsoft and Adobe libCoolType to spot any
    discrepancies (e.g. missing bound checks).

**Found variants:**

N/A

### Structural improvements

**Ideas to kill the bug class:**

Rewrite font parsing code in a memory safe language.

**Ideas to mitigate the exploit flow:**

*   Isolate all or some specific ESOBjects into their own dedicated
    heap/partition.
*   Sandbox/isolate font parsing code.
*   Disable javascript processing in Adobe Reader.

### 0-day detection methods

What are potential detection methods for similar 0-days? Meaning are there any
ideas of how this exploit or similar exploits could be detected **as a 0-day**?

N/A

## Other References

*   https://blog.google/threat-analysis-group/active-north-korean-campaign-targeting-security-researchers/
*   https://www.zerodayinitiative.com/blog/2020/9/2/cve-2020-9715-exploiting-a-use-after-free-in-adobe-reader
*   https://exploitshop.wordpress.com/2012/01/18/ms11-087-aka-duqu-vulnerability-in-windows-kernel-mode-drivers-could-allow-remote-code-execution/
*   https://improsec.com/tech-blog/bypassing-control-flow-guard-in-windows-10
