一、简介
目前许多网站前端源码都趋于模块化,参数值通过webpack的模块进行加解密,这对于爬虫和渗透都是一个拦路虎,因此写下该博客,记录自己的学习思路。
二、目标站点
这里分析的是某动某站前端的js代码
http://www.xxx.com/
三、算法分析
3.1 正常发送请求包
打开开发者工具,点击network,输入账号密码,点击登录。
查看响应请求的内容,其中password的参数值被加密了,这里我输入的是123456,密文看着很像md5,但是通过解密发现,密文又似乎不是md5。
发送第二个登录包。
password的值没变,但是sign的值发生了变化,较大几率sign值在生成的时候,使用了时间戳。
此时寻找该参数加密点。
3.2 寻找password、sign加密相关的模块与函数
全局搜索该登录请求的接口
并成功找到相关的参数。
3.2.1 分析password
通过分析发现,password的值是由n决定的。
s = {
phone: e.phone,
password: n,
type: t,
sign: "",
timestamp: d
}
并在第1886行,找到了有关n的声明
此时发现password被md5加密后,与pwdkey的值,进行字符串拼接后,在进行一次md5加密。
直接搜索pwdkey,并在第27589行,找到了该值,发现该值是不变的。
这里使用python3,写一个简单的password的加密demo。
import hashlib
def md5Encode(str):
m = hashlib.md5(str.encode(encoding='utf-8'))
return m.hexdigest()
password = "123456"
pwdkey = "kla5ra8h9s"
encrypt_password = md5Encode(md5Encode(password)+pwdkey)
print(encrypt_password)
发现该值,与我在前端登录框处输入密码123456后,经前端webpack模块加密后的值是一样的,说明前面的分析思路是正确的。
3.2.2 分析sign
sign的生成方式在第1866行进行的。
d.sign = this.$md5Sign(n)
sign的值是通过md5Sign进行加密后得到的,因此这里需要分析两个点。分别是md5Sign的算法流程,以及l的值。
3.2.2.1 分析l
在第1858行发现
l = "" + e.phone + n + t + d
这里我下个断点,传值进来方便查看。
l的值是由手机号+密码密文+1+时间戳生成的。
即l=phone+md5(md5(password)+pwdkey)+1+时间戳
3.2.2.2 分析md5Sign
此时分析md5Sign的算法。
通过断点追踪发现,md5Sign和函数mn()有关。
有点奇怪,为什么不应该是直接追到md5Sign的函数声明去吗?怎么会是mn()的声明。
此时我直接搜索了mn()
发送mn的值又赋值给了md5Sign
因此此处mn和md5Sign可以理解是等价的关系。
此时来分析mn的函数流程。
sn = a("8237"),
cn = a.n(sn);
function mn(e) {
var i = "",
a = "6d9fkhj33rk8sa5fc";
return i = e + a,
i = cn()(i).toLowerCase(),
i
}
此处并设置断点传值。
并连续调试到下一步
此时分析可知
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()转换成小写字母。
sn = a("8237");
cn = a.n(sn);
为了方便调试,这里我打开了编辑器,准备扣前端js代码下来,本地执行调试了。
先把前面的流程进行整理,这里我把所有时间戳写死了,方便等下观察值是否一样。
当时间戳为1635925676时,sign的值为32138108f605a5122cc6f6c1bc54c7b3。(之前的截图)
var password = "9ac13f8bad58389388ecc1604eaed320"
var phone = "15112341234"
var timestamp = "1635925676"
var a = "6d9fkhj33rk8sa5fc"
var sn = a("8237");
var cn = a.n(sn);
var i = phone + password + '1' + timestamp + a;
var sign = cn()(i).toLowerCase();
console.log(sign)
此时只需要分析a
断点跟进
在这里找到了a
此时分析确定了分发器
我们将这段代码全部扣下来。
声明一个flag,用于接收分发器模块的值。
将l赋值给flag,并要把原先的a("8237")和a.n(sn)修改成flag("8237")和flag.n(sn)
运行之后,报错。
提示window没有被定义,这里就把window声明一下。
var window = global;
将这个声明放在最上面,和flag的声明放在一起。
此时再次运行。报错,提示call没有被定义。原因是因此flag中的8237模块没有被调用。
这里全局搜索8237
在此js文件中找到了有关8237的定义
将这个js代码全部复制至本地的2.js中,并在1.js中进行调用。
并声明一下window
并再次运行
和预期值不一样?猜测时间戳填错了。
因此此处重新抓一个时间戳的数据包。
并修改timestamp的值
此时本地调试的结果,与前端的结果一致,因此该sign的签名算法分析完成。
四、使用python3完成此次签名算法
import hashlib
import time
def md5Encode(str):
m = hashlib.md5(str.encode(encoding='utf-8'))
return m.hexdigest()
if __name__ == "__main__":
phone = "15112341234"
password = "123456"
pwdkey = "kla5ra8h9s"
#timestamp = "1635927403"
timestamp = int(time.time())
a = "6d9fkhj33rk8sa5fc"
n = md5Encode(md5Encode(password)+pwdkey)
i = phone + n + "1" + str(timestamp) + a
print("时间戳是:"+str(timestamp))
print(md5Encode(i))
使用系统时间戳
五、总结
遇到前端加密不要慌,作为一个安服仔,应该直接干就完了,反正js代码已经默认开源给你了,慢慢调试分析算法逻辑,并调用相应的模块即可。
自主学习时,解决问题应有多样性,尽量还是多增加自己的知识层面。
实际解决问题时,应考虑效率。
所以此处我提供了javascript和python3的两种解决问题的方式方法。
点击上方"蓝字"关注我们吧!此文为哈工大安天联合CERT实验室发布的针对挖矿木马的简要技术分析,首发于”高校信息化应用“公众号,安天公众号现对其进行转载,以便读者进一步了解挖矿木马,同时为读者提供处置建议。挖矿木马概述互联网的虚拟货币,如比特币(BTC)、门罗…
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论