简介
目前许多网站前端源码都趋于模块化,参数值通过webpack的模块进行加解密,这对于爬虫和渗透都是一个拦路虎,因此写下该博客,记录自己的学习思路。
目标站点
这里分析的是某动某站前端的js代码
1 | aHR0cDovLzVnbWF0Y2guZ3guY2hpbmFtb2JpbGUuY29tL2hvbWVwYWdlLyMvbG9naW4= |
算法分析
正常发送请求包
打开开发者工具,点击network
,输入账号密码,点击登录。
查看响应请求的内容,其中password
的参数值被加密了,这里我输入的是123456
,密文看着很像md5
,但是通过解密发现,密文又似乎不是md5
。
发送第二个登录包。
password
的值没变,但是sign
的值发生了变化,较大几率sign
值在生成的时候,使用了时间戳
。
此时寻找该参数加密点。
寻找password、sign加密相关的模块与函数
全局搜索该登录请求的接口
并成功找到相关的参数。
分析password
通过分析发现,password
的值是由n
决定的。
1 | s = { |
并在第1886行,找到了有关n
的声明
此时发现password
被md5加密后,与pwdkey
的值,进行字符串拼接后,在进行一次md5加密。
直接搜索pwdkey
,并在第27589行,找到了该值,发现该值是不变的。
这里使用python3
,写一个简单的password
的加密demo
。
1 | import hashlib |
发现该值,与我在前端登录框处输入密码123456
后,经前端webpack
模块加密后的值是一样的,说明前面的分析思路是正确的。
分析sign
sign
的生成方式在第1866行进行的。
1 | d.sign = this.$md5Sign(n) |
sign
的值是通过md5Sign
进行加密后得到的,因此这里需要分析两个点。分别是md5Sign
的算法流程,以及l
的值。
分析l
在第1858行发现
1 | l = "" + e.phone + n + t + d |
这里我下个断点,传值进来方便查看。
l
的值是由手机号+密码密文+1+时间戳
生成的。
即l=phone+md5(md5(password)+pwdkey)+1+时间戳
分析md5Sign
此时分析md5Sign
的算法。
通过断点追踪发现,md5Sign
和函数mn()
有关。
有点奇怪,为什么不应该是直接追到md5Sign
的函数声明去吗?怎么会是mn()
的声明。
此时我直接搜索了mn()
发送mn
的值又赋值给了md5Sign
因此此处mn
和md5Sign
可以理解是等价的关系。
此时来分析mn
的函数流程。
1 | sn = a("8237"), |
此处并设置断点传值。
并连续调试到下一步
此时分析可知
e = phone +password的密文+1+时间戳
e = 151123412349ac13f8bad58389388ecc1604eaed32011635925676
a = 6d9fkhj33rk8sa5fc
i = e + a
i = phone +password的密文 +1 + 时间戳 + 6d9fkhj33rk8sa5fc
即i = 151123412349ac13f8bad58389388ecc1604eaed320116359256766d9fkhj33rk8sa5fc
这里为了方便查看看,就将i最后的值拆分一下
phone | md5(md5(password)+pwdkey) | 1 | 时间戳 | a |
---|---|---|---|---|
15112341234 | 9ac13f8bad58389388ecc1604eaed320 | 1 | 1635925676 | 6d9fkhj33rk8sa5fc |
i
的值再被cn
函数加密,加密后的值,全部通过toLowerCase()
转换成小写字母。
1 | sn = a("8237"); |
为了方便调试,这里我打开了编辑器,准备扣前端js代码下来,本地执行调试了。
先把前面的流程进行整理,这里我把所有时间戳写死了,方便等下观察值是否一样。
当时间戳为1635925676
时,sign的值为32138108f605a5122cc6f6c1bc54c7b3
。(之前的截图)
1 | var password = "9ac13f8bad58389388ecc1604eaed320" |
此时只需要分析a
断点跟进
在这里找到了a
此时分析确定了分发器
我们将这段代码全部扣下来。
声明一个flag,用于接收分发器模块的值。
将l
赋值给flag
,并要把原先的a("8237")
和a.n(sn)
修改成flag("8237")
和flag.n(sn)
运行之后,报错。
提示window没有被定义,这里就把window声明一下。
1 | var window = global; |
将这个声明放在最上面,和flag的声明放在一起。
此时再次运行。报错,提示call没有被定义。原因是因此flag中的8237模块没有被调用。
这里全局搜索8237
在此js
文件中找到了有关8237
的定义
将这个js代码全部复制至本地的2.js中,并在1.js中进行调用。
并声明一下window
并再次运行
和预期值不一样?猜测时间戳填错了。
因此此处重新抓一个时间戳的数据包。
并修改timestamp
的值
此时本地调试的结果,与前端的结果一致,因此该sign的签名算法分析完成。
使用python3完成此次签名算法
1 | import hashlib |
使用系统时间戳
总结
遇到前端加密不要慌,作为一个安服仔,应该直接干就完了,反正js代码已经默认开源给你了,慢慢调试分析算法逻辑,并调用相应的模块即可。
自主学习时,解决问题应有多样性,尽量还是多增加自己的知识层面。
实际解决问题时,应考虑效率。
所以此处我提供了javascript和python3的两种解决问题的方式方法。