(At least) 4 ways to die opening a PDF

Fri 26 June 2009 by fred

There are several way to trigger events when a PDF is viewed: pushing a button, resizing the document, closing it, reaching a page, when mouse pass on a zone, when an annotation is displayed/hidden, ... but the most interesting from an offensive point of view is when the document is open.

Here, we introduce several ways to trigger a JavaScript - but it could be any kind of action - when the PDF is opened ... A new sample is available in origami with all this code (see sources/samples/open/open.rb)

1. OpenAction

If you have looked at malicious PDF recently, that is PDF exploiting one of the recent Reader's flaw, most were based on issues on the JavaScript engine. So the purpose for the attacker was to write a script, put it in the PDF, then have it executed as soon as possible.

Here come the marvelous /OpenAction. The name speaks for itself.

This event must be placed in the Catalog section. This is one of the most important object in a PDF file as it described many information about the PDF, its pages, some JavaScript can also be registered here, ...

Anyway, back to /OpenAction. It is really easy to use:

1 0 obj
<<
/OpenAction <<
/S /JavaScript
/JS (app.alert\("Method: /OpenAction"\))
>>
/Pages 2 0 R
/Type /Catalog
>>

Adding an /OpenAction is straightforward with origami:

pdf = PDF.read( ARGV[0] )
pdf.onDocumentOpen( action )

2. Special combo: select the 1st page to be seen

You can set in a PDF what page has to be seen at first when the document is opened. But you can also set an action when a page is displayed. 1+1=you win.

When you select this option is PDF readers, there is a trick. In fact, this is done once more on /OpenAction: in order to jump to any page, the OpenAction calls a GoTo action. It is then combined with what is called an Additional Action /AA. This kind of action is associated with many objects in PDF, pages, but also annotations for instance. They have special flags (depending on the kind of the object) to trigger the event, like the one will we use when a page is displayed.

So, in the end, the PDF looks like:

1 0 obj
<<
/OpenAction <<
/S /GoTo
/D [ 6 0 R /Fit ] // Set the destination
>>
/Pages 2 0 R
/Type /Catalog
>>
6 0 obj
<<
...
/AA <<
/O <<    // /O means "when file is opened
/S /JavaScript
/JS (app.alert\("Method: /OpenAction\(/GoTo page\(rand\)\) & /AA on that page"\))
>>
>>
/Parent 2 0 R
/Type /Page
/Contents 7 0 R
>>

Here is the corresponding origami code:

pdf = PDF.read( ARGV[0] )
page = pdf.pages[ rand(pdf.pages.size) ]
page.onOpen( action )
goto = Action::GoTo.new( :D => [ page, :Fit] )
pdf.onDocumentOpen( goto )

3. Using annotations when they become visible

You can add some annotations to the PDF document, hide them or not and so on. As explained with the /AA, you can also bind an action to an annotation, especially when it becomes visible.

So, guess what happens when you put an annotation on the 1st page? Yes, it is visible. So if there is an additional action bound to this annotation, it is automatically triggered. Bingo!

3 0 obj    //This object describes the 1st page
<<
...
/Parent 2 0 R
/Type /Page
/Contents 4 0 R
/Annots [ <<   // Here comes the annotation
/P 3 0 R
/Subtype /Screen
/AA <<
/PV <<   // when page is visible, execute the JavaScript
/S /JavaScript
/JS (app.alert\("Method: /AA when annotation becomes visible on 1st page"\))
>>
>>
/Rect [ 350 700 415 640 ]
>> ]
...
>>

And once more the origami source code:

pdf = PDF.read( ARGV[0] )
annot = Annotation::Screen.new
annot.Rect = Rectangle[:llx => 350, :lly => 700, :urx => 415, :ury => 640]
annot.onPageVisible( action )
pdf.pages[ 0 ].add_annot(annot)

4. Just /Names it

In the /Catalog, there is a special object called /Names. For our experiments, we do not need to understand what it does. All that matters is that when a JavaScript is registered in this section, it is automatically executed when the document is opened:

1 0 obj
<<
/Pages 4 0 R
/Names <<
/JavaScript 2 0 R // Register a JavaScript
>>
/Type /Catalog
>>
2 0 obj //Set the name and content of the script
<<
/Names [ (welcome) 3 0 R ] // Content is set in object 3
>>
3 0 obj //Here comes the script
<<
/S /JavaScript
/JS (app.alert\("Method: Add the script to the document names dictionary"\))
>>

Comparing these methods

When examining how malicious a PDF can be, one can focus on action. There are 2 main objects referring to actions in PDF: /OpenAction and /AA. So, let's compare our 4 methods regarding how they need these 2 commands:

 
  1. OpenAction
  1. Change 1st page
  1. Annotations
  1. Names
/OpenAction 1 1 0 0
/AA 0 1 1 0

When we state PDF is naturally polymorphic, you have here the semantic proof :)