分享

JS渗透逆向| V_jstools、Jsrpc、Autodecoder三者强强联动自动加解密

 zZ华 2025-04-13 发布于广东

长风安全实战能力知识库

    http://wave./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;

// requestId
window.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 requests
import json
from flask import Flask, Response, request
import re

app = 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}\r\n\r\n\r\n\r\n{encstr}'  # 返回值为固定格式,不可更改 必需必需必需,共四个\r\n

    # 否则,只返回 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:  # 如果需要处理响应头
        # 返回值为固定格式,不可更改 必需必需必需,共四个\r\n
        return f'{headers}\r\n\r\n\r\n\r\n{body}'  

    # 否则,只返回 body
    return body
if __name__ == '__main__':  
    app.debug = True  # 设置调试模式,生产模式的时候要关掉debug  
    app.run(host='0.0.0.0', port='8888')

启动调试图片在burp中测试也是能正常加解密的图片然后配置下这几个地方便可以实现自动加解密了图片图片但这里有点坑,proxy里面是可以正常解密,但在repeater中就返回空白,很奇怪图片当我们产生问题的时候可以从burp的logging日志里面分析

这里我们header头以及加密内容都是正确传输的,但为什么返回空白图片这里有个坑点,我们知道错误代码400是因为客户端请求产生的报错,说明我们脚本里面的请求包构造有问题

不卖关子了,是因为header头下多了个换行,我们这只保留三个\r\n图片但如果只保留三个\r\n,autoDecoder就无法调试了,但在proxy、repeater、intruder就不影响,如果想在autoDecoder中调试还是需要改回四个\r\n图片ok,成功,完美实现在有签名、时间戳、请求id等校验下实现自动加解密

proxy:图片repeater:图片logging:图片intruder:图片



    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多