某信用中心之加速乐实战分析
- 某信用中心之加速乐实战分析
- 声明
- 逆向目标
- 逆向分析
- 第一层cookie获取
- 第二层cookie获取
- 调试分析JS文件
- 模拟执行
- 致谢
某信用中心之加速乐实战分析
声明
本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!
逆向目标
逆向分析
通过打开F12抓包查看是否存在加密参数
发现我们需要的数据在xhr请求中
再查看标头和负载,初步看好像没有加密参数,所以我们直接发送request请求尝试能不能获取到数据。
发送请求后发现返回一个js文件
如果有了解过的同学就知道,这是加速乐cookie反爬虫,是知道创宇推出的一款网站CDN加速、网站安全防护平台。
加速乐的特点是访问网站一般有三次请求:
- 第一次请求网站,网站返回的响应状态码为 521,响应返回的为经过 AAEncode 混淆的 JS 代码;
- 第二次请求网站,网站同样返回的响应状态码为 521,响应返回的为经过 OB 混淆的 JS 代码;
- 第三次请求网站,网站返回的响应状态码 200,即可正常访问到网页内容。
我们可以在网页中清除cookie后再尝试抓包查看是否和我们上述所说
接下来就是重点分析这三个cookie文件
第一层cookie获取
直接查看 response 是显示无响应内容的,所以我们通过之前发送请求包中返回的响应包代码进行分析
分析响应包,可以看到第一个xyxx-list.do
返回的响应内容经过 AAEncode 加密,大致内容如下,可以看到一堆颜表情符号,还挺有意思的:
<script>document.cookie=('_')+('_')+('j')+('s')+('l')+('_')+('c')+('l')+('e')+('a')+('r')+('a')+('n')+('c')+('e')+('_')+('s')+('=')+(-~{}+'')+(6+'')+(3+6+'')+(~~[]+'')+(2+4+'')+(2+6+'')+(([2]+0>>2)+'')+((1+[4]>>1)+'')+((1<<2)+'')+(9+'')+('.')+(1+[0]-(1)+'')+((2<<1)+'')+(1+[0]-(1)+'')+('|')+('-')+(-~0+'')+('|')+('I')+('G')+('j')+('P')+(4+4+'')+('l')+('r')+('j')+('q')+('T')+('P')+('y')+('q')+('v')+('O')+('i')+(4+4+'')+('N')+('i')+('n')+('G')+('B')+('O')+('f')+('r')+('k')+('s')+('%')+(1+2+'')+('D')+(';')+('m')+('a')+('x')+('-')+('a')+('g')+('e')+('=')+(1+2+'')+(3+3+'')+(~~[]+'')+(~~{}+'')+(';')+('p')+('a')+('t')+('h')+('=')+('/');location.href=location.pathname+location.search</script>
document.cookie 里的颜表情串实际上是第一次 __jsl_clearance_s 的值,可以直接通过正则提取到加密内容后,使用execjs.eval()
方法即可得到解密后的值:
import re
import execjs
js_clearance = re.findall('cookie=(.*?);location',response.text)[0]
jsl_clearance_s = execjs.eval(js_clearance).split(';')[0]
print(jsl_clearance_s)
// __jsl_clearance_s=1690686292.777|-1|uzTU5s0inLnvhCVdFmuyRXtr69k%3D
第二层cookie获取
第二层cookie获取可以直接查看第一个xyxx-list.do
的响应包,或者通过第一层cookie获取后的__jsl_clearance_s
和__jsluid_s
两个cookie参数发包请求,也能获得对应响应代码,如图所示
将文件复制出来分析后发现是一个经过 OB 混淆的 JS 文件,我们需要对其进行调试分析,所以我们首先需要寻找找这个js代码的加密位置,先清除本地浏览器的cookie。
然后在源代码中勾选住脚本,这样网页执行的每一个js文件都会被我们断住了,当然也可以使用hook注入等方式寻找加密位置
刷新网页,然后不断进行F8往下,我们首先会看到我们之前分析过的第一个cookie文件,继续F8往下走
成功找到我们所需要的js文件
调试分析JS文件
由于网站是cookie加密,所以每次刷新JS文件的一些参数都是在动态变换的,所以我们可以使用本地替换的方式固定一套下来再进行调试。然后在该 JS 文件中通过 CTRL + F 搜索 document,只有一个,在第 596 行打断点调试,选中_0x1739('0x93', '3R@K') + 'ie'
后进入控制台输入会发现这里就是 cookie 经过混淆后的样式:
将等号后面的内容全部选中,进入控制台输入可以发现,这里生成了 Cookie 中 __jsl_clearance_s 参数的值:
至此,我们知道了 Cookie 生成的位置,接下来就需要了解其加密逻辑和加密方法,然后通过 python 对其进行复现了,document 部分完整的代码如下:
document[_0x1739('0x93', '3R@K') + 'ie'] = _0x3fa957[_0x1739('0x9b', '9PBK') + 'W'](_0x3fa957[_0x1739('0x43', '7C)&') + 'W'](_0x3fa957[_0x1739('0xbc', 'ML*i') + 'X'](_0x3fa957[_0x1739('0x37', 'tfb]') + 'X'](_0x5a3a5f['tn'] + '=', _0x4bca4c[0x0]), _0x1739('0x114', '@XV)') + _0x1739('0x10f', ')!ai') + '='), _0x5a3a5f['vt']), _0x1739('0x20', '711Q') + _0x1739('0xda', 'R2lr') + '\x20/');
这里等号后面的内容比较冗杂,其实我们想要获取的是 jsl_clearance_s 参数的值,通过逐步分析调试可以看到其值由(_0x5a3a5f['tn'] + '=', _0x4bca4c[0x0])
生成:
继续分析可知_0x5a3a5f['tn']
对应的部分是__jsl_clearance_s
,而其值是_0x4bca4c[0x0]
,因此我们需要进一步跟踪 _0x4bca4c
生成的位置。
通过搜索,在第 587行可以找到其定义生成的位置,打断点调试可以看到,_0x4bca4c[0x0]
其实就是取了 _0x4bca4c
数组中的第一个位置的值:
我们来进一步分析 _0x4bca4c
后面代码各自的含义,完整代码如下
_0x3fa957[_0x1739('0x11', '3)Nv') + 'V'](_0x29f4ef, _0x5a3a5f['ct'], _0x5a3a5f[_0x1739('0x68', '&GmG')]);
通过控制台可知_0x3fa957[_0x1739('0x11', '3)Nv') + 'V']
取的是后面参数中第一个参数当做函数体,第二和第三个值当做参数传入。
_0x5a3a5f['ct'])
取的是 go 函数传入的字典中 ct 参数的值:
分析可知将_0x5a3a5f[_0x1739('0x68', '&GmG')]
数组中的值按照某种规则进行拼接就是 __jsl_clearance_s
参数的值,并且_0x1739('0x68', '&GmG')
对应字典中 bts 的值:
所以需要进一步跟踪 _0x29f4ef
,可以发现其是个函数体,第 582 行 return 后的返回值就是 __jsl_clearance_s
参数的值:
在第 581 行打断点调试,能知道 hash 后 _0x2cc335
为 __jsl_clearance_s 参数的值:
hash( _0x2cc335)
的值为 _0x2cc335
经过加密后的结果,在本例中,加密结果特征分析很明显能看出是SHA256的加密长度,其实通过查看之前获取的go字典也能看到他写出了用的什么加密方式。
但加密的方法即 hash 方法不全是 SHA256,多刷新几次发现会变化,实际上这个 hash 方法与原来调用 go 函数传入的字典中 ha 的值相对应,ha 即加密算法的类型,一共有 md5、sha1、sha256 三种,所以我们在本地处理的时候,要同时有这三种加密算法,通过 ha 的值来匹配不同算法。
进一步观察这里还有个 for 循环,分析发现每次循环 hash(_0x2cc335)
的值是动态变化的,原因是 _0x2cc335
的值是在动态变化的,_0x2cc335
中只有中间两个字母在变化,不仔细看都看不出来:
跟进 _0x2cc335
生成的位置,分析可知 _0x2cc335
参数的值是由 _0xf7137b
数组的第一个值加上两个字母再加上该数组第二个值组成的结果:
_0x2cc335
的源代码混淆参数很多不好直接分析,建议解混淆后再进行分析
var _0x2cc335 = _0xf7137b[0x0] + _0x5a3a5f[_0x1739('0x89', 'BSbR') + 's'][_0x1739('0xb8', 'jBDG') + 'tr'](_0x107bb7, 0x1) + _0x5a3a5f[_0x1739('0x105', 'kvMu') + 's'][_0x1739('0x6e', 'm%$p') + 'tr'](_0x4ec95b, 0x1) + _0xf7137b[0x1];
解混淆后代码如下
var _0x2cc335 = _0xf7137b[0x0] + _0x5a3a5f['chars']["substr"](_0x107bb7, 0x1) + _0x5a3a5f['chars']["substr"](_0x4ec95b, 0x1) + _0xf7137b[0x1];
中间两个字母是将底下这段写了两次生成的,即 _0x5a3a5f['chars']['substr'][1]
, 取字典中 chars 参数的一个字母,取了两次,这里通过 for 循环在不断取这两个值,直到其值加密后与 _0x1c0bb7
(即 ct)的值相等,则作为返回值传递给 __jsl_clearance_s
参数:
_0x1c0bb7
为ct的值:
最前面_0x3fa957[_0x1739('0xf0', 'oMaE') + 'E']
是个方法,我们进一步跟进过去,看这个方式里面实现了什么样的逻辑:
其内容如下,可以看到这个方法返回的值是两个相等的参数:
模拟执行
综上所述,_0x29f4ef
函数中的逻辑就是判断 _0x2cc335
的值经过 hash
方法加密后的值,是否与 ct 的值相等,若相等则将返回值传递给 __jsl_clearance_s
参数,循环完后还未有成功匹配的值则会执行提示失败,传入参数中 ha 的值是在变化的,即加密算法也是在变化的,有三种加密方式 SHA1
、SHA256
和 MD5
,我们可以扣下三种 hash
方法,也可以直接使用 crypto-js
库来实现:
var CryptoJS = require('crypto-js');
function hash(type, value){
if(type == 'md5'){
return CryptoJS.MD5(value).toString();
}
if(type == 'sha1'){
return CryptoJS.SHA1(value).toString();
}
if(type == 'sha256'){
return CryptoJS.SHA256(value).toString();
}
}
function cookies(_0x5a3a5f){
var _0x26fd06 = new Date();
function _0x29f4ef(_0x1c0bb7, _0xf7137b) {
var _0x5dfb01 = _0x5a3a5f['chars']['length'];
for (var _0x107bb7 = 0x0; _0x107bb7 < _0x5dfb01; _0x107bb7++) {
for (var _0x4ec95b = 0x0; _0x4ec95b < _0x5dfb01; _0x4ec95b++) {
var _0x2cc335 = _0xf7137b[0x0] + _0x5a3a5f['chars']["substr"](_0x107bb7, 0x1) + _0x5a3a5f['chars']["substr"](_0x4ec95b, 0x1) + _0xf7137b[0x1];
if ((hash(_0x5a3a5f['ha'],_0x2cc335) == _0x1c0bb7)) {
return [_0x2cc335, new Date() - _0x26fd06];
}
}
}
}
var _0x4bca4c = _0x29f4ef (_0x5a3a5f['ct'], _0x5a3a5f['bts']);
return {'__jsl_clearance_s' : _0x4bca4c[0]};
}
console.log(cookies({
"bts": ["1690639070.278|0|ihl", "l%2FaY6Y3B%2FKq8I4GT55NZvc%3D"],
"chars": "iMyDvZzWmPBnCGdujVpCAJ",
"ct": "fb9fe0ec006b42f92ffbd372dc71b4612c989e2bf4d095afb8abbfce8ed3d35f",
"ha": "sha256",
"is": false,
"tn": "__jsl_clearance_s",
"vt": "3600",
"wt": "1500"
}))
获取结果如下
python代码只演示部分关键代码,完整代码可私信联系我
def get_first_cookie():
global cookies
resp_first = requests.post(url, headers=headers, data=data)
# 获取 cookie 值 __jsluid_s
cookies.update(resp_first.cookies)
# 获取第一层响应内容, AAEncode 加密
content_first = re.findall('cookie=(.*?);location', resp_first.text)[0]
jsl_clearance_s = execjs.eval(content_first).split(';')[0]
# 获取 cookie 值 __jsl_clearance_s
cookies['__jsl_clearance_s'] = jsl_clearance_s.split("=")[1]
def get_second_cookie():
global cookies
# 通过携带 jsluid_s 和 jsl_clearance_s 值的 cookie 获取第二层响应内容
resp_second = requests.get(url=url, headers=headers, cookies=cookies)
# 获取 go 字典参数
go_params = re.findall(';go\((.*?)\)</script>', resp_second.text)[0]
params = json.loads(go_params)
return params
def get_third_cookie():
with open('jsl.js', 'r', encoding='utf-8') as f:
jsl_js = f.read()
params = get_second_cookie()
# 传入字典
third_cookie = execjs.compile(jsl_js).call('cookies', params)
cookies.update(third_cookie)
def main():
get_first_cookie()
get_third_cookie()
resp_third = requests.post(url, headers=headers,cookies=cookies , data=data)
resp_third.encoding = 'utf-8'
print(resp_third.text)
if __name__ == '__main__':
main()
获取结果如下
致谢
部分思路代码通过以下文章学习:【JS 逆向百例】某网站加速乐 Cookie 混淆逆向详解