长风安全实战能力知识库
http://wave.cf-sec.cn/user/login
一个由实战派推动的安全内容建设平台,传递一线经验,助力实战应用能力提升!
长风安全实战能力知识库
长风安全,公众号:长风安全长风安全实战能力知识库
本文来源于长风安全实战能力知识库,针对JS逆向加解密自动化进行探讨,实现了V_jstools、jsrpc、autodecoder三者强强联动自动加解密💡
强强联动实现自动化加解密
V_jstools、jsrpc、autodecoder
测试环境:http://39.98.108.20:8085/#/login
autoDecoder:https://github.com/f0ng/autoDecoder/
jsRpc:https://github.com/jxhczhl/JsRpc
v_jstools:https://github.com/cilame/v_jstools
首先安装v_jstools,配置如下随便输个账号密码登录下查看数据包,发现请求包和返回包都加密了,并且还有sign、timestamp、requestId,说明可能有签名、时间戳、请求ID等校验,我们下面需要通过js查看对应的关系
输入账号密码,查看控制台并定位加密函数
timestamp是通过r = Date.parse(new Date)来获取,也就是时间戳
requestId是通过i = p()这个函数来获取
sign是通过s = a.a.MD5(n + i + r)
来获取的,其中n= JSON.stringify(v(t.data))
也就是我们输入的账号密码的json字段,然后加上requestId和时间戳,最后再取md5值就是签名
加密函数也就是t.data =l(n)来实现的下面来找解密函数
也是通过v_jstools控制台输出的内容快速定位解密的js文件我们下个断点调试下
在这一步的时候,我们步入函数
发现这一段代码便是实现解密的函数,
t.data
可以输出调试下于是弄清楚了加解密后我们启动jsrpc服务端,配置默认即可
然后我们取消所有断点(一定要取消!),然后再客户端注入js环境
在控制台传入JsEnv_Dev的代码并回车
https://github.com/jxhczhl/JsRpc/blob/main/resouces/JsEnv_Dev.js
然后连接我们的服务端
var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=zzz");
然后我们需要去控制台断点并注册js方法
先是加密的:
// 时间戳window.time = Date.parse;// requestIdwindow.id = function() { return p();};// v函数window.v1 = function(param) { return v(param);};// 签名window.m = function(data) { return a.a.MD5(data).toString();};// 加密window.enc = function(data) { return l(data);};
然后传递函数名进行调用
// 注册函数demo.regAction("req", function(resolve, param) { // 请求头 let timestamp = window.time(new Date()); let requestid = window.id(); let v_data = JSON.stringify(window.v1(param)); let sign = window.m(v_data + requestid + timestamp); // 加密请求体 let encstr = window.enc(v_data); let res = { "timestamp": timestamp, "requestid": requestid, "encstr": encstr, "sign": sign }; resolve(res);});
然后我们控制台打印测试下,没问题
然后我们调用下接口(调用时需要取消断点),也没问题,并且我们需要的参数也都成功返回在data参数中了
接着我们给注册解密的js方法 我们断点到解密函数的位置,并直接把整个方法进行注册
然后传递函数名调用
我们控制台输出测试下,没问题
然后取消断点,试试接口是否能成功解密并返回 也是没问题的
然后我们实现autoDecoder,代码如下
代码实现可以参考flasktestheader.py
,格式的话建议按照它原来的模板来,否则会有很多坑,建议多加print输出并结合burp的logging进行调试
https://github.com/f0ng/autoDecoder/blob/main/flasktestheader.py
import requestsimport jsonfrom flask import Flask, Response, requestimport reapp = Flask(__name__) url = "http://localhost:12080/go"@app.route('/encode', methods=["POST"]) def encrypt(): body = request.form.get('dataBody') # 获取 post 参数 headers = request.form.get('dataHeaders') # 获取 post 参数 reqresp = request.form.get('requestorresponse') # 获取 post 参数 data = { "group": "zzz", "action": "req", "param": body } if headers is not None: # 开启了请求头加密 # 使用正则表达式提取 timestamp、requestid 和 sign timestamp_match = re.search(r'timestamp: (d+)', headers) requestid_match = re.search(r'requestId: ([w-]+)', headers) sign_match = re.search(r'sign: ([w-]+)', headers) # 提取匹配的值 timestamp_before = timestamp_match.group(1) if timestamp_match else None requestid_before = requestid_match.group(1) if requestid_match else None sign_before = sign_match.group(1) if sign_match else None # 发送请求到目标 URL res = requests.post(url, data=data) encry_param = json.loads(res.text)['data'] print(encry_param) # 将 encry_param 转换为字典 encry_param_dict = json.loads(encry_param) encstr = encry_param_dict['encstr'] # 提取新的 timestamp、requestid 和 sign timestamp_new = encry_param_dict['timestamp'] # 提取 timestamp requestid_new = encry_param_dict['requestid'] # 提取 requestid sign_new = encry_param_dict['sign'] # 确保 timestamp_new 是字符串 timestamp_new = str(timestamp_new) # 替换 headers 中的旧值为新值 headers = headers.replace(sign_before, sign_new, 1) headers = headers.replace(requestid_before, requestid_new, 1) headers = headers.replace(timestamp_before, timestamp_new, 1) return f"{headers}rnrnrnrn{encstr}" # 返回值为固定格式,不可更改 必需必需必需,共四个rn # 否则,只返回 encstr return encstr@app.route('/decode', methods=["POST"]) def decrypt(): body = request.form.get('dataBody') # 获取 post 参数 headers = request.form.get('dataHeaders') # 获取 post 参数 reqresp = request.form.get('requestorresponse') # 获取 post 参数 data = { "group": "zzz", "action": "decrypt", "param": body } res = requests.post(url, data=data) body = json.loads(res.text)['data'] print(body) if headers is not None: # 如果需要处理响应头 # 返回值为固定格式,不可更改 必需必需必需,共四个rn return f"{headers}rnrnrnrn{body}" # 否则,只返回 body return bodyif __name__ == '__main__': app.debug = True # 设置调试模式,生产模式的时候要关掉debug app.run(host="0.0.0.0", port="8888")
启动调试在burp中测试也是能正常加解密的
然后配置下这几个地方便可以实现自动加解密了
但这里有点坑,proxy里面是可以正常解密,但在repeater中就返回空白,很奇怪
当我们产生问题的时候可以从burp的logging日志里面分析
这里我们header头以及加密内容都是正确传输的,但为什么返回空白这里有个坑点,我们知道错误代码400是因为客户端请求产生的报错,说明我们脚本里面的请求包构造有问题
不卖关子了,是因为header头下多了个换行,我们这只保留三个rn但如果只保留三个rn,autoDecoder就无法调试了,但在proxy、repeater、intruder就不影响,如果想在autoDecoder中调试还是需要改回四个rn
ok,成功,完美实现在有签名、时间戳、请求id等校验下实现自动加解密
proxy:repeater:
logging:
intruder:
02
关于平台
如何加入
投稿加入: 原创文章投稿,不限类型(SRC实战报告解析、技术知识文章等) 建议:对于原创文章投稿,请确保内容独特、丰富,并且排版整齐。如果你是新手或者尚未深入了解领域知识,请在投稿前慎重考虑是否符合要求。
付费加入: 现价 288 239元,平台2.0上线后价格随成本增加而增长。
更多介绍:
http://cf-sec.cn/wiki
联系vx:SikO316 或扫码 备注来意 ✨
扫码获取联系方式
原文始发于微信公众号(长风安全):JS渗透逆向| V_jstools、Jsrpc、Autodecoder三者强强联动自动加解密
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论