记一次使用JSRPC半自动化逆向数据包的过程

admin 2024年11月11日14:52:28评论10 views字数 8194阅读27分18秒阅读模式

文章导读

声明:文中涉及到的技术和工具,仅供学习使用,禁止从事任何非法活动,如因此造成的直接或间接损失,均由使用者自行承担责任。

众亦信安,中意你啊!

点不了吃亏,点不了上当,设置星标,方能无恙!

记一次使用JSRPC半自动化逆向数据包的过程

基本情况

日子一天比一天难熬啊,如图所示老板给渗透的站点数据包全加密了。

记一次使用JSRPC半自动化逆向数据包的过程

前车之鉴,生怕让我卷铺盖走人,没办法头发掉点就掉点吧。

记一次使用JSRPC半自动化逆向数据包的过程

定位加解密函数

找到关键字CipherText,直接F12全局搜索打上断点,1分钟定位(实际60秒)

加密函数: (0, u.getSm2DataHexByObject)

解密函数: (0, u.getSm2DataByString) 

记一次使用JSRPC半自动化逆向数据包的过程

记一次使用JSRPC半自动化逆向数据包的过程

配置JsRpc

    由于加解密函数无法全局调用,所以要先把它们通过window变量代理到全局,以便全局调用。

    debug到加密或解密函数的时候,在控制台分别输入下面的代码

    这里有个小重点,由于加密函数的入参是一个object,也就是json对象,但是json对象不利于写代码传输,所以这里的mysm2enc函数的入参我改成了base64编码后的字符串,然后通过atob和JSON.parse对字符串进行解析(类比反序列化)

window.mysm2enc = function(encodedStr) {    try {        console.log("+++加密+++  ",encodedStr)        // 解码 base64 加密后的字符串        var decodedStr = decodeURIComponent(atob(encodedStr));        var jsonObj = JSON.parse(decodedStr);        // 调用 getSm2DataHexByObject 函数并返回结果        var result = (0,u.getSm2DataHexByObject)(jsonObj);        console.log("---加密---  ",result)        return result;    } catch (error) {        console.error("Error encrypt or parsing:", error);        return null;    }};window.mysm2dec = function(encodedStr) {    try {        console.log("+++解密+++  ",encodedStr)        var result = (0,u.getSm2DataByString)(encodedStr);        console.log("---解密---  ",result)        return result;    } catch (error) {        console.error("Error decrypt :", error);        return null;    }};

然后在非debug情况下,测试一下函数的可用性。

记一次使用JSRPC半自动化逆向数据包的过程

加解密函数准备好后,通过在控制台执行以下代码,配置并连接jsrpc

jsrpc项目地址:https://github.com/jxhczhl/JsRpc

function Hlclient(wsURL) {    this.wsURL = wsURL;    this.handlers = {        _execjs: function (resolve, param) {            var res = eval(param)            if (!res) {                resolve("没有返回值")            } else {                resolve(res)            }        }    };    this.socket = undefined;    if (!wsURL) {        throw new Error('wsURL can not be empty!!')    }    this.connect()}Hlclient.prototype.connect = function () {    console.log('begin of connect to wsURL: ' + this.wsURL);    var _this = this;    try {        this.socket = new WebSocket(this.wsURL);        this.socket.onmessage = function (e) {            _this.handlerRequest(e.data)        }    } catch (e) {        console.log("connection failed,reconnect after 10s");        setTimeout(function () {            _this.connect()        }, 10000)    }    this.socket.onclose = function () {        console.log('rpc已关闭');        setTimeout(function () {            _this.connect()        }, 10000)    }    this.socket.addEventListener('open', (event) => {        console.log("rpc连接成功");    });    this.socket.addEventListener('error', (event) => {        console.error('rpc连接出错,请检查是否打开服务端:', event.error);    });};Hlclient.prototype.send = function (msg) {    this.socket.send(msg)}Hlclient.prototype.regAction = function (func_name, func) {    if (typeof func_name !== 'string') {        throw new Error("an func_name must be string");    }    if (typeof func !== 'function') {        throw new Error("must be function");    }    console.log("register func_name: " + func_name);    this.handlers[func_name] = func;    return true}//收到消息后这里处理,Hlclient.prototype.handlerRequest = function (requestJson) {    var _this = this;    try {        var result = JSON.parse(requestJson)    } catch (error) {        console.log("catch error", requestJson);        result = transjson(requestJson)    }    //console.log(result)    if (!result['action']) {        this.sendResult('', 'need request param {action}');        return    }    var action = result["action"]    var theHandler = this.handlers[action];    if (!theHandler) {        this.sendResult(action, 'action not found');        return    }    try {        if (!result["param"]) {            theHandler(function (response) {                _this.sendResult(action, response);            })            return        }        var param = result["param"]        try {            param = JSON.parse(param)        } catch (e) {}        theHandler(function (response) {            _this.sendResult(action, response);        }, param)    } catch (e) {        console.log("error: " + e);        _this.sendResult(action, e);    }}Hlclient.prototype.sendResult = function (action, e) {    if (typeof e === 'object' && e !== null) {        try {            e = JSON.stringify(e)        } catch (v) {            console.log(v)//不是json无需操作        }    }    this.send(action + atob("aGxeX14") + e);}function transjson(formdata) {    var regex = /"action":(?<actionName>.*?),/g    var actionName = regex.exec(formdata).groups.actionName    stringfystring = formdata.match(/{..data..:.*..w+..:s...*?..}/g).pop()    stringfystring = stringfystring.replace(/\"/g, '"')    paramstring = JSON.parse(stringfystring)    tens = `{"action":` + actionName + `,"param":{}}`    tjson = JSON.parse(tens)    tjson.param = paramstring    return tjson}//连接var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=firefox");

提示连接成功。

记一次使用JSRPC半自动化逆向数据包的过程

记一次使用JSRPC半自动化逆向数据包的过程

写MITM脚本,解密成功

这里要写两个MITM代理脚本,

下游代理: 位于浏览器和burp之间,监听端口设置为7070,转发到burp的8080。

负责将密文请求数据包解密,明文发给burp,以及重新加密明文响应,以便浏览器可以正常显示。

上游代理: 位于burp和服务器之间,监听端口设置为9090,设置burp的数据包转发到9090。

负责将明文请求数据包重新加密发给服务器,以及将服务器返回的密文响应解密,明文发给burp。

如图:(图片来源:https://xz.aliyun.com/t/13218)

记一次使用JSRPC半自动化逆向数据包的过程

下游代理,MITM-downstream.py

mitmdump -p 7070 -s MITM-downstream.py --mode upstream:http://127.0.0.1:8080 --ssl-insecure
from mitmproxy import flowfilter,ctxfrom mitmproxy.http import HTTPFlowfrom mitmproxy import flowfilterfrom mitmproxy.http import HTTPFlowimport base64import jsonimport timeimport requestsfrom urllib.request import quote, unquoteclass Mimitdownstream():    flag = 2    TargetHost = ["222.190.116.142"]    group = "new1"    def mysm2enc(self,data):        jscode = """mysm2enc111("{}")""".format(data)        url = "http://localhost:12080/execjs"        data = {            "group": self.group,            "code": jscode        }        res = requests.post(url, data=data)        return json.loads(res.text)['data']    def mysm2dec(self,data):        jscode = "mysm2dec111("{}")".format(data)        url = "http://localhost:12080/execjs"        data = {            "group": self.group,            "code": jscode        }        res = requests.post(url, data=data)        return json.loads(res.text)['data']    def request(self,flow):        if flow.request.host in self.TargetHost:            print("======处理下游请求======")            # 获取请求头 header            req_header = flow.request.headers            # ctx.log.info(req_header)                # 获取请求体 body            req = flow.request.get_text()            print(req)            # 修改请求头            req_json = json.loads(req)            if self.flag == 1:                req_json['new-down'] = "下游请求"                CipherText = req_json['CipherText']                # 进行JsRpc SM2解密                PlaintText = self.mysm2dec(CipherText)                ctx.log.info(PlaintText)                req_json['PlaintText'] = PlaintText            elif self.flag == 2:                  # 进行JsRpc SM2解密                CipherText = req_json['CipherText']                PlaintText = self.mysm2dec(CipherText)                ctx.log.info(PlaintText)                req_json = json.loads(PlaintText)            # 处理json.dumps中文问题 ensure_ascii=False            new_req_str = json.dumps(req_json,ensure_ascii=False)            ctx.log.info(new_req_str)            # 设置修改过后的请求体            flow.request.set_text(new_req_str)    def response(self,flow):        # if '{"CipherText"' in flow.response.text:        if 1:            print("======处理下游响应======")            # 获取请求头 header            rep_header = flow.response.headers            ctx.log.info(rep_header)            # 获取请求体 body            rep = flow.response.get_text()            ctx.log.info(rep)            # 修改请求头            rep_json = {}            if self.flag == 1:                rep_json = json.loads(rep)                pass            elif self.flag == 2:                CipherText = self.mysm2enc(base64.b64encode(quote(rep).encode()).decode())                rep_json['CipherText'] = CipherText            ctx.log.info(rep_json)            # 处理json.dumps中文问题 ensure_ascii=False            new_rep_str = json.dumps(rep_json,ensure_ascii=False)            ctx.log.info(new_rep_str)            # 设置修改过后的请求体            flow.response.set_text(new_rep_str)addons = [Mimitdownstream(),]

上游代理  MITM-upstream.py

mitmweb -p 9090 -s MITM-upstream.py --ssl-insecure
from mitmproxy import flowfilter,ctxfrom mitmproxy.http import HTTPFlowfrom mitmproxy import flowfilterfrom mitmproxy.http import HTTPFlowimport base64import jsonimport requestsfrom urllib.request import quote, unquoteclass Mimitupstream():    flag = 2    TargetHost = ["222.190.116.142"]    group = "new1"    def mysm2enc(self,data):        jscode = """mysm2enc111("{}")""".format(data)        url = "http://localhost:12080/execjs"        data = {            "group": self.group,            "code": jscode        }        res = requests.post(url, data=data)        return json.loads(res.text)['data']    def mysm2dec(self,data):        jscode = "mysm2dec111("{}")".format(data)        url = "http://localhost:12080/execjs"        data = {            "group": self.group,            "code": jscode        }        res = requests.post(url, data=data)        return json.loads(res.text)['data']    def request(self,flow):        if flow.request.host in self.TargetHost:            print("======处理上游请求======")            # 获取请求头 header            req_header = flow.request.headers            ctx.log.info(req_header)            # 获取请求体 body            req = flow.request.get_text()            ctx.log.info(req)            # 修改请求头            # req_json = json.loads(req)            if self.flag == 1:                pass            elif self.flag == 2:                print("======",req,type(req))                CipherText = self.mysm2enc(base64.b64encode(quote(req).encode()).decode())                req_json = {}                req_json['CipherText'] = CipherText                # req_json['new-up'] = "上游请求"                print(req_json)                print("=======")            ctx.log.info(req_json)             # 处理json.dumps中文问题 ensure_ascii=False            new_req_str = json.dumps(req_json,ensure_ascii=False)            ctx.log.info(new_req_str)            # 设置修改过后的请求体             flow.request.set_text(new_req_str)    def response(self,flow):        if '{"CipherText"' in flow.response.text:            print("======处理上游响应======")            # 获取请求头 header            rep_header = flow.response.headers            ctx.log.info(rep_header)            # 获取请求体 body  (str)            rep = flow.response.get_text()            ctx.log.info(rep)            # 修改请求头            rep_json = json.loads(rep)            if self.flag == 1:                rep_json['new-up'] = "上游响应"                pass            elif self.flag == 2:                PlaintText = self.mysm2dec(rep_json['CipherText'])                ctx.log.info(PlaintText)                rep_json = json.loads(PlaintText)            ctx.log.info(rep_json)            # 处理json.dumps中文问题 ensure_ascii=False            new_rep_str = json.dumps(rep_json,ensure_ascii=False)            ctx.log.info(new_rep_str)            # 设置修改过后的请求体            flow.response.set_text(new_rep_str)addons = [Mimitupstream(),]

代码运行情况:

记一次使用JSRPC半自动化逆向数据包的过程

Burp显示效果:

记一次使用JSRPC半自动化逆向数据包的过程

再和之前的对比一下:

记一次使用JSRPC半自动化逆向数据包的过程

往这里看

点点关注不迷路,每周不定时持续分享各种干货。可关注公众号回复"进群",也可添加管理微信拉你入群。

记一次使用JSRPC半自动化逆向数据包的过程

原文始发于微信公众号(众亦信安):记一次使用JSRPC半自动化逆向数据包的过程

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年11月11日14:52:28
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   记一次使用JSRPC半自动化逆向数据包的过程http://cn-sec.com/archives/3383399.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息