分析前端加密的意义?
正常来说,我们在做测试的时候,一般都是明文数据,并且服务端不会对传入的参数进行检测。这个时候的测试就是一番风顺。
如下图,请求参数会带一个sign=xx,后端会对这个sign进行校验,此处的校验规则是:将请求参数进行拆分,并且按照ascii顺序排序之后进行md5(query + “字符常量”)的加密方式进行校验
基本操作
找断点——通过搜索敏感关键词
通过请求参数发现关键词sign=
通过devtools进行sources的搜索
找断点——通过网络请求的调用栈
找一个看起来更容易定位的函数,这里的函数其实可以随便找,因为在调用栈中,可以回溯/跟进每个函数。
跟踪加密的方法是s=Object(ht.b)(o,_dyn$.t(622)),可以确认加密函数是s=Object(ht.b), 所以可以在上图3245行进行断点跟踪,找到加密方法
mitmdump的基本使用(addons编写)
1.简单的demo
class AutoDecoderClass(object):
def request(self, flow: http.HTTPFlow):
pass
def response(self, flow: http.HTTPFlow):
pass
addons = [
AutoDecoderClass()
]
给mitmdump运行, 加了一些参数,仅供参考
如果勾选了Do DNS lookups over SOCKs Proxy, 脚本能获取的url信息是包含域名的,如果不勾选的话,获取到的url信息是IP的,因为经过了一层解析。
手写加密算法
如下图所示的加密校验(此处我修改了3c为2c,为了是将报错作为参考系)
可以看出sign=xxx是整个数据包发送到后端之后提供给后端校验的数据
通过debug查看加密算法
signData为请求GET请求参数ethod=edit&roleid=5985&operatorid=13223232324&_=1675850145395
getQuery方法(这部分不用细看,想手动还原算法分也行,不过此处的getQuery运行一下就可以得到结果从而可以判断出算法是啥样的)
function getQuery(_0x25e05e) {
var _0x59335d = {
'tMUuu': function(_0x5c4842, _0x4a44ef) {
return _0x5c4842(_0x4a44ef);
},
'FukXq': function(_0x68f762, _0x5c9bb) {
return _0x68f762(_0x5c9bb);
},
'IAwqi': function(_0x4c0bd0, _0x4ae967, _0x16eeab) {
return _0x4c0bd0(_0x4ae967, _0x16eeab);
},
'RYViz': _0xfa96('b9', 'aosW')
};
var _0x4c50e2 = [];
var _0x8ddc6f = _0x25e05e['split']('&');
for (var _0xe55c87 = 0x0; _0xe55c87 < _0x8ddc6f['length']; _0xe55c87++) {
if (_0x8ddc6f[_0xe55c87]['split']('=')[0x0]) {
_0x4c50e2[_0xfa96('ba', 'b#9z')]({
'name': _0x8ddc6f[_0xe55c87]['split']('=')[0x0],
'value': _0x59335d[_0xfa96('bb', 'SKqK')](decodeURIComponentSafely, _0x59335d['FukXq'](decodeURIComponentSafely, _0x59335d[_0xfa96('bc', '4q@0')](getCaption, _0x8ddc6f[_0xe55c87], _0x8ddc6f[_0xe55c87][_0xfa96('bd', 'oc$h')]('=')[0x1])))
});
}
}
return _0x4c50e2[_0xfa96('be', '%866')](_0x59335d['FukXq'](createComprisonFunction, _0x59335d['RYViz']));
}
从运行结果看得出,是将所有的参数分隔之后将key和value都取出来以{name: X, value: Y}的方式存入数组并返回,同时也可以看出此处的排序规则为ASCII从小到大排序
getSign签名算法, 和上面的函数一样,可以手动还原 但是可能没啥必要。还原算法的时候可以配合debug的变量作用域来编写代码。比如_0xfa96(‘13d’, ‘3nwA’)其实就是一个length
getSign签名算法, 和上面的函数一样,可以手动还原 但是可能没啥必要。还原算法的时候可以配合debug的变量作用域来编写代码。比如_0xfa96(‘13d’, ‘3nwA’)其实就是一个length
function getSign(_0x2c8a1a) {
var _0x388511 = {
'DWutz': function(_0x1bf78a, _0x23f0a5) {
return _0x1bf78a + _0x23f0a5;
},
'ldLIf': function(_0x41a705, _0x25b696) {
return _0x41a705 - _0x25b696;
},
'pwfjR': _0xfa96('13c', 'Yb@s')
};
var _0x2f1bba = '';
for (var _0x4d9213 = 0x0; _0x4d9213 < _0x2c8a1a[_0xfa96('13d', '3nwA')]; _0x4d9213++) {
if (_0x2c8a1a[_0x4d9213] && _0x2c8a1a[_0x4d9213][_0xfa96('13e', 'eoTh')]) {
_0x2f1bba += _0x388511[_0xfa96('13f', 'eoTh')](_0x2c8a1a[_0x4d9213][_0xfa96('140', 'Q5pM')] + '=', _0x2c8a1a[_0x4d9213][_0xfa96('141', 'hlCR')]);
if (_0x388511['ldLIf'](_0x2c8a1a['length'], 0x1) > _0x4d9213) {
_0x2f1bba += '&';
}
}
}
console[_0xfa96('142', 'o9yX')](_0x2f1bba);
return md5(_0x388511[_0xfa96('143', 'SKqK')](_0x2f1bba, _0x388511['pwfjR']));
}
跟踪进入getSign查看md5加密的值
其中,_0x2f1bba是重新拼接之后的值, _0x388511[‘pwfjR’])为一个常量salteBrkhGPrugSZqXEwB6YnX7m49VIQYObJ。
最后组合成为 md5(排序参数组合+salt)
梳理加密流程 获取参数 –> 排序参数 -> 组合参数 -> 末尾拼接salt -> md5 -> 拼接sign= -> 发送
mitmproxy脚本实现
class AutoDecode4Finance(AutoDecoderClass):
# 排序字符串 重组
def new_string(self, params):
params_sorted = sorted(params.split("&"))
for i, v in enumerate(params_sorted):
# 每个参数url解码一次
params_sorted[i] = urllib.parse.unquote(v)
return "&".join(params_sorted)
def request(self, flow: http.HTTPFlow):
# 跳过drw的url, 已经带sign=的不在计算
if flow.request.url.endswith(".dwr") or '&sign=' in flow.request.url:
return
if flow.request.method.lower() == "get":
# 获取get链接的参数
query = urllib.parse.urlparse(flow.request.url).query
else:
# 获取 post 链接的参数
query = flow.request.content.decode("utf-8")
new_string = self.new_string(query)
new_string += "eBrkhGPrugSZqXEwB6YnX7m49VIQYObJ"
flow.request.query['sign'] = md5hash(new_string)
效果
去掉sign参数可以正常请求,因为mitmdump已经自动完成参数的拼接了
最后
本文是举了一个比较简单的算法例子,还有其他的如des、aes、sm4等算法都可以用此办法进行加密中转。
但是如果遇到复杂的算法,比如自带了一些自己写的算法,如果在代码篇幅不多的情况下,其实手动还原会好点,如果引用了数不清的莫名其妙的东西,可以采用其他方法进行加密算法的还原或者是调用。比如python的execjs来局部调用一些js的算法(不想还原js算法的情况下)。
原创文章,作者:guozi,如若转载,请注明出处:https://www.sudun.com/ask/81924.html