
某药web长后缀type__1241加密分析
某药web长后缀type__1241加密分析
前言
此次是被分派到公司的一个需求,要求获取一些数据,具体什么不多说了,整体网站没有加密,只有params处,有一个非常长的type__1241开头的后缀,如果我们不带这个,或者伪造,网站会返回一个html包含一大串的js代码。此次目的就是分析长后缀是怎么来的了,至于为什么说是长后缀,因为ali还有个很短的,不过听说是旧版本了。
分析
由于是公司需求,另加药物类,网址就不放了,阿里系后缀网站有很多,某雪球就是同样套路结果。有心有缘分总能找到一两个网站。
无限debugger
这里我们打开控制台,进行翻页操作,触发请求,映入眼帘的是一个无限debugger,这个debugger真的非常有意思和新颖啊,不仅用了正则表达式检测是否格式化,还new 了SVGAnimateString
进行类型检测,所以当我们打开控制台,追两下就能看到TypeError
我们利用ai来解释一下这些代码吧,毕竟又好用的工具,何必自己苦苦分析
对于这个无限debugger的bypass呢,大家就各显神通吧,暂不提供代码了,在此给出一下思路,
生成定位
我们通过启动器进入 send函数
我们看到send函数中也包含几个函数,这里我们在if和else处下断,看看走哪些内容
然后进行翻页操作
发现停在了这个断点处,且this指向XHR,这里我们不妨猜测,代码里重写了XHR内容,后续可能进行了加密处理。我们继续单步运行
发现进入了一个新的函数,且js也由jquery变为VM虚拟机,这里同样,在if和else处打上断点。
这里发现了最终的type__1241参数是在这里生成,不过函数经过变种ob混淆,只能先提取出来,然后依次运行输出,最终得到,是经过g函数后,得到了最终的加密结果。
AST处理
Switch 分析
经过ast处理后的js已经很的非常直观了,逻辑也清晰可见,不过可惜的是,无法有更好的方法替换在浏览器中,所以采用静态分析AST后的代码,浏览器继续动态调试混淆后的。
g函数中传递的参数则是三个,分别是//www.xxxxx.com/pc/search/v2/listProducts
url路径
queryWord=%E7%BE%8E%E6%9E%97&categoryFirstId....
data参数
post
请求方法
1
这里在switch中所有case分支下的continue下断点.
继续分析,由于case的分支不多,这里我们一个个看就足够了。
看到了一个数组,且数组的第一位也正好对应我们的case走的第一个分支,所以这个数组应该是执行顺序无疑了。
case 6
var OR = y['X'];
发现只是设置了window的操作,可以跳过
case 7
有部分混淆,我们就对照着我们的解完混淆后的代码看,发现是进行转小写操作,传参则是我们的请求方法(GET或者POST)
case 8
1
2
3
4
5
6
case '8':
var Ok = z(t(O3)),
OP = e(Ok, !(-815 + kS), !(-815 + kS)),
Oh = OP,
Oy = Oh = y['X']["encodeURIComponent"](OR["_waf_bd8ce2ce37"] + O5 + "110279f682e" + Oh);
continue;
这里发现做的内容操作很简单,只是进行encodeURIComponent
函数操作,其中_waf_bd8ce2ce37
是携带错误的type值,会返回一串js代码,其中返回的结果,而O5post
Oh是urlpath ,都是传递来的参数,最后进行url编码即可.
case 1
1
2
3
4
5
6
7
8
9
10
case '1':
if (O4 && -1 == ["get", "head"]["indexOf"](O5)) {
if (y['X']["Uint8Array"] && O4 instanceof y['X']["Uint8Array"]) {
for (var O9 = '', OO = -815 + kS; OO < O4["byteLength"]; OO++)
O9 += v[O4[OO]];
Oh += O9;
} else
h += y['X']["encodeURIComponent"](O4);
}
continue;
这里的if判断是fasle 所以不会走,所以会经过else的内容,即对上文的内容,在进行一次url编码。
附上ai对于上述代码的解释
case 0
1
2
3
4
case '0':
y['X']["Math"]["random"] = T(a);
continue;
// a 固定参数
这个是重之之重了,由于当时没在意,错踩坑,卡了一会时间,最后才发现,实际上是伪随机数。
这里分析一下T函数,至于a我还真找不到是怎么生成的,但所幸多次测试发现是固定的,不然还要卡很久。
有了AST后的代码。整体直接扣下来运行即可。
case 4
1
2
3
4
case '4':
var Os = F(Oh),
OX = Os;
continue;
这里同样用ai去解释,省时省力,方便分析
根据ai分析的,即可就分析出来算法,调用验证即可。
1
2
3
4
5
function F(O3){
for (var O4 = CryptoJS.SHA256(O3).toString(), O5 = [], O6 = 8; O6 < 24; ++O6)
O5["push"](O4[O6 % O4["length"]]);
return O5;
}
发现完美成功
case 3
1
2
3
case '3':
Oh != Oy && (OX = F(Oy));
continue;
发现算法完全和case4一样,所以相同办法处理,只是传递的参数不同,不携带了data请求的内容了。
case 2
1
2
3
4
case '2':
var ON = "110279f682e-" + W(Os = [G(Os), G(OX), Z(), new y['X']["Date"]()["getTime"]() + '', OR["_waf_bd8ce2ce37"], OR["_waf_a86dfdc5f2"], C()]["join"]('|')),
OE = {};
continue;
应该就是最终生成地方了,看到了最先拼接的110279f682e-
内容,W函数应该就是最终生成地方,这里依次分析
G函数
同样附上解释,这里知道大概即可,我们最终还是要把代码给扣出来使用。
然后扣出来的结果和浏览器验证即可一下,防止出错。
Z函数
只能说ai给出了大概的内容,实际上Z函数的作用,是获取浏览器的环境和指纹,最终通过算法动态生成位掩码
这里一样扣出来即可。
C函数
可自己生成或者使用浏览器的结果,都不影响。
W函数
也是重之之中了
得知是个LZW压缩算法的变种,也是一样扣出来调用即可。这里我们查看最终拼接进入W函数的参数是什么,
G函数得到的两个结果 拼接Z函数,加上时间戳 加上 js第一次乱码返回的_waf_bd8ce2ce37
+_waf_a86dfdc5f2(也是时间戳)
拼接C函数结果。
最终我们把传递参数传递给我们扣出来的W验证结果即可。
至此就完结撒花了。
其实还剩一个case5分支,但是经过代码解释,发现只是更改了浏览器的location的属性,对于我们扣算法来说,用不上,也就不分析,不过还是附上一下ai的解释的图
结尾
验证结果
文章仅此学习记录。
参考文章
感谢恩师七神,一手AST直接带我秒杀,经过此次需求和猿人学验证码比赛案例,会AST也显得额外重要了。