假如业务的JS文件被外部人员控制,并且是针对性利用(多种bypass监控),对业务来说是一次灾难,对最近CDN供应链事件进行了分析(艰难)。
之前也分析过类似事件:BootCDN供应链攻击分析与应对
这里使用python批量请求,然后变换User-Agent 以及Referer(当然必要时候挂代理),然后观察长度变化,不一样的说明有问题,然后可以对比分析了。
import time
import requests
headers ={
'sec-ch-ua':'"Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"',
'Referer':'https://bbs.test.com/',
'sec-ch-ua-mobile':'?1',
'User-Agent':'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36',
'sec-ch-ua-platform':'"Android"',
}
for i in range(1,100):
response = requests.get('https://cdn.staticfile.net/jquery/3.7.1/jquery.js', headers=headers)
print('jsfile/{}.js'.format(i),len(response.text))
with open('jsfile/{}.js'.format(i),'w')as f:
f.write(response.text)
time.sleep(0.5)
成功获取到有问题的JS。
可以看到在文件后面增加了一段js代码
0
获取的是第一个js loader https://pastebin.com/raw/kWhveDQi,这里比较简单,直接创建一个script标签加载url https://api.bdustatic.com/jquery.min-4.0.12.js。
1
js文件大概356kb,但是实际功能比较少,全部用于混淆了:https://pastebin.com/raw/ryFnmJve
我下载代码进行格式化,发现网页卡死,发现是陷入死循环,于是我使用未修改的代码发现一切正常。于是提前打开devtools,然后进入网站调试,然后中断。
2
根据栈回溯,发现这个while循环。
3
那他如何检测代码被改动呢?通过打印这个异常,点击对应的绿色字跳转到对应函数,同时观察这个函数相邻的函数,发现有一个字符串相关的操作,很容易想到就是检测源代码改动的地方了,并且我对比能够正常加载的中间值发现不一样,那么极可能是这里了。
4
于是对此处下断点,发现通过function.toString()打印了函数的源代码,进行进行正则判断。
`\\w+ *\\(\\) *{\\w+ *['|"].+['|"];? *}`
正则用于检测是否符合function () {xxx; }这种宽松代码格式,因为原来的代码都是压缩的,如果为宽松则说明是改动原代码。
5
后面我把返回值改回-1还是陷入死循环,还有其他地方有检测。不过我插入alert(1),没有任何空格之类的,是不会陷入死循环。就不在此处纠结了。
整体函数如下,其中核心函数为:check_tiaozhuan
6
这里的混淆更比我之前分析的js都为严重,对字符串进行加密,使用了大量具有上下文的运算,通过简单python execjs计算结果替换明显不太可能,但是好在可以通过devtool进行动态运算然后替换。
7
这里手动替换明显不太可能(太多了),但是可以通过cdp控制chrome进行运算,最后结果如下,可实现半自动化结果还不错。
8
来到最后一行,但是发现是中文函数,并且check_tiaozhuan是核心函数
9
对加密的字符串,半自动化解密,成功解密部分。
10
直接来到比较关键的地方,会根据两个_0xf9b5a5、_0x180b50变量内容进行嵌套的if判断。
11
经过分析,发现会根据_0xf9b5a5的事件选择不同的url,而_0x180b50是一个随机数用于偶发性。
12
在第一行的if时候,在console上进行修改
_0xf9b5a5 = 3 // 设置凌晨3点的事件
_0x180b50 = 2 // 随机数小于10才触发
// 用于绕过PC检测
Object.defineProperty(navigator,'platform',{get:function(){return 'iPhone';}});
接下来还有一层检测
13
14
_0x43e1c8、_0x3b3cbf、_0x17e501、_0x50dac3,其实最终调用checkKeywords函数,通过半自动化的解密,发现checkKeywords函数获取document['body']['outerHTML']的内容,很容易判断获取网页内容有上面打码的关键词,然后选择不同url触发。
15
这里我在html设置了一个关键词,触发成功。
16
17
然后就是调用vfed_update,参数为如上图的url
18
vfed_update(_0x30f8ed),其实最终会根据_0x30f8ed这个参数跳转
19
但是报错了,因为还需要进行加载js,看起来加载这个这个js补齐了变量,就可以跳转了。
20
我们要如何去防御这种类型的供应链攻击?
1、使用CDN,使用自主可控的JS链接代替;
存放到本地或者自己的云服务
2、使用SRI,对引入的外部文件进行hash计算,判断是否修改。
不过要注意的是:启用 SRI 策略后,浏览器会对资源进行 CORS 校验,这就要求被请求的资源必须同域,或者配置了 Access-Control-Allow-Origin 响应头
21
总的来说,分析起来很曲折,好在里面的真正的代码比较简单并且少,能短时间内分析个大概(也学到不少技巧)。