JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

admin 2024年8月7日15:42:05评论33 views字数 10471阅读34分54秒阅读模式
此文章只为学习而生,请勿干违法违禁之事,本公众号只在技术的学习上做以分享,通过现实的案例来实战锻炼水平,所有行为与本公众号无关。

02

前言

针对请求接口加密/响应需要解密的情况,在使用Burp测试时无法对数据进行操作,需要确认传输都为明文我们才可以进行常规测试。这块有点JS基础和Python基础基本都可以通杀。(JsRpc也是FridaRpc的前置,主要都是用到了mitmproxy,技术这东西一通百通,大家卷起来,呆哥躺平。)

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

▲ JSRPC请求处理逻辑

03

核心项目

01

mitmproxy

项目地址:https://github.com/mitmproxy/mitmproxy

安装证书:在连接mitmproxy之后,手机或设备需要设置代理,输入http://mitm.it/安装证书

代理配置:使用--mode upstream:来设Suite,加上--ssl-insecure忽略 SSL 证书验证:

mitmdump-p6666-s main.py --mode upstream:http://127.0.0.1:6662 --ssl-insecure

这里upstream就是burp的端口

mitm request属性:https://docs.mitmproxy.org/stable/api/mitmproxy/http.html#Request

def__init__(self,host:str, // 主机port:int, // 端口method:bytes, // HTTP请求方法,例如“GET”。scheme:bytes, // HTTP请求方案,应为“http”或“https”。authority:bytes, // HTTP 请求权限path:bytes, // 路径http_version:bytes,headers:Headers | tuple[tuple[bytes, bytes], ...],content:bytes | None,trailers:Headers | tuple[tuple[bytes, bytes], ...] | None,timestamp_start:float,timestamp_end:float | None):

mitm response属性:https://docs.mitmproxy.org/stable/api/mitmproxy/http.html#Response

def __init__(        self,        http_version: bytes,        status_code: int,        reason: bytes,        headers: Headers | tuple[tuple[bytes, bytes], ...],        content: bytes | None,        trailers: None | Headers | tuple[tuple[bytes, bytes], ...],        timestamp_start: float,        timestamp_end: float | None,)
02

JSRPC

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

用法说明:双击JSRPC.exe启动监听服务,然后在命令行执行以下脚本创建Hlclient对象,然后再通过对其实例化进行连接服务,连接上之后可以正常使用。

04

RPC正题

这里只做基础的加解密函数定位,研究型文章不做过多的分析,JS逆向基础差的可以爬楼找最上面的几篇文章,只做JsRpc演示防止被恶意利用,这个做过金融项目的大概率遇到过。

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

01

JS逆向找到加解密函数

发现请求被加密了,开始加入XHR调试拦截:

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

然后一层层找调用栈。

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

然后往下调试找加密点。

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

▲ 加密函数

经过一点点调试发现由该位置对请求request进行一次加密。

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

▲ l就是传入的json对象

然后我们找response解密函数,一步步跟栈调用也是同理,M$e(e.data)这个解密函数就被找到了。

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

▲ 解密函数位置

02

RPC流程

> 我们已知加密函数z9(),解密函数M$e(e.data)

现在开始RPC调用,第一步启动服务:

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

▲ 双击下载下来的JsRPC.exe,监听在12080端口

客户端在浏览器控制台注入以下代码,创建类定义:https://github.com/jxhczhl/JsRpc/blob/main/resouces/JsEnv_Dev.js

functionHlclient(wsURL){this.wsURL = wsURL;this.handlers = {_execjs:function(resolve, param){varres =eval(param)if(!res) {resolve("没有返回值")}else{resolve(res)}}};this.socket =undefined;if(!wsURL) {thrownewError('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 =newWebSocket(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(typeoffunc_name !=='string') {thrownewError("an func_name must be string");}if(typeoffunc !=='function') {thrownewError("must be function");}console.log("register func_name: "+ func_name);this.handlers[func_name] = func;returntrue}//收到消息后这里处理,Hlclient.prototype.handlerRequest =function(requestJson){var_this =this;try{varresult =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}varaction = result["action"]vartheHandler =this.handlers[action];if(!theHandler) {this.sendResult(action,'action not found');return}try{if(!result["param"]) {theHandler(function(response){_this.sendResult(action, response);})return}varparam = 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(typeofe ==='object'&& e !==null) {try{e =JSON.stringify(e)}catch(v) {console.log(v)//不是json无需操作}}this.send(action + atob("aGxeX14") + e);}functiontransjson(formdata){varregex =/"action":(?<actionName>.*?),/gvaractionName = regex.exec(formdata).groups.actionNamestringfystring = 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 = paramstringreturntjson}

不过为了方便就小小修改两个地方就可以不用重复输入了。

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

把函数用let定义,然后直接点击右下角的三角形运行即可。(此处在页面加载后就可以开始了)

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本▲ 在Sources->Snippets下面新建一个脚本,然后把它复制进去

然后启动RPC链接:

var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=zzz");

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

然后回到原始代码中,我们可以看到这里断点还在,我们将其用window.hook=z9代出来。

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

▲ 代理加密函数

我们甚至可以直接看出来他的加密方式,然后Hook完成后将断点放开。

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

这样我们就可以看到这个函数被我们代理到全局window中了,然后我们进行调用。

demo.regAction("encode", function (resolve,param) {    //这样添加了一个param参数,http接口带上它,这里就能获得    var ret = window.hook(JSON.stringify(param))    resolve(ret);})

注入加密方法定义。

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

▲ 注册好了

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

▲ 正常显示加密

然后写python调用一下。

curl 'http://127.0.0.1:12080/go?group=zzz&action=encode&param='curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36' -d 'action=encode&group=zzz&param=' 'http://127.0.0.1:12080/go'

既可以用GET也可以用POST,考虑到传入参数可能多样性,所以我这里会用POST来做代码层面处理,GET用于测试。

然后访问这个网页:https://curlconverter.com/http/

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

import requestsheaders = {    'Content-Type': 'application/x-www-form-urlencoded'}data = {    'action': 'encode',    'group': 'zzz',    'param': '',}response = requests.post('http://127.0.0.1:12080/go', headers=headers, data=data)

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

发现调用成功了,然后修改脚本为通用版本。

import requestsdef getRpc(param,method):    headers = {        'Content-Type': 'application/x-www-form-urlencoded'    }    data = {        'action': method,        'group': 'zzz',        'param': param    }    resJson = requests.post('http://127.0.0.1:12080/go', headers=headers, data=data).json()    return resJson["data"]def encode(param):    return getRpc(param, 'encode')def decode(param):    return getRpc(param, 'decode')print(encode('{"username":"admin","password":"123456","code":"2","uuid":"xxxxx"}'))

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

开始整合mitm进行改写。

import requestsimport mitmproxyfrom mitmproxy import http,ctxfrom mitmproxy import flowfilterdef encode(param):    return getRpc(param, 'encode')def decode(param):    return getRpc(param, 'decode')def getRpc(param,method):    headers = {        'Content-Type': 'application/x-www-form-urlencoded'    }    data = {        'action': method,        'group': 'zzz',        'param': param    }    resJson = requests.post('http://127.0.0.1:12080/go', headers=headers, data=data).json()    return resJson["data"]# mitm核心处理逻辑class Interceptor:    def __init__(self):        pass    def request(self, flow: mitmproxy.http.HTTPFlow):        request = flow.request        # 直接将请求体进行一次加密处理        request.content = encode(request.content).encode('utf-8')        # 日志模块        ctx.log.info(request.content)    def response(self, flow: mitmproxy.http.HTTPFlow):        passaddons = [    Interceptor()]

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

加密解决了,现在对解密函数进行处理。

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

▲ 加解密是一套

代理到window.decode里面了。

demo.regAction("decode", function (resolve,param) {    //这样添加了一个param参数,http接口带上它,这里就能获得    var ret = window.decode(param)    resolve(ret);})

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

import requestsimport mitmproxyfrom mitmproxy import http,ctxfrom mitmproxy import flowfilterinfo = ctx.log.info# 加密方法def encode(param):    return getRpc(param, 'encode')# 解密方法def decode(param):    return getRpc(param, 'decode')# 获取RPC调用方法def getRpc(param,method):    headers = {        'Content-Type': 'application/x-www-form-urlencoded'    }    data = {        'action': method,        'group': 'zzz',        'param': param    }    resJson = requests.post('http://127.0.0.1:12080/go', headers=headers, data=data).json()    return resJson["data"]class Hook:    def __init__(self):        pass    # 处理request请求    def request(self, flow: mitmproxy.http.HTTPFlow):        request = flow.request        info("=======Request修改前:" + str(request.content) + "=======")        request.content = encode(request.content).encode('utf-8')        info("=======Request修改后:" + str(request.content) + "=======")    # 处理response请求    def response(self, flow: mitmproxy.http.HTTPFlow):        response = flow.response        info("=======Response修改前:" + response.text + "=======")        response.text = decode(response.text)        info("=======Response修改后:" + response.text + "=======")addons = [    Hook()]

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

这里请求/响应都处理完成了。

如果请求可以解密的话,先前置一个mitm将加密请求解密之后发送给burp这样就可以直接被动扫接受时自动处理。

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

▲ 类似这样

把response的处理pass掉,如果请求有加密,并且被我们解密了,这个作为直接和客户端交互的第一步所以也可以在这里配置响应加密返回给客户端正常显示,这样到burp的数据都是明文的。

处理逻辑就是:客户端->beforeRpc-mitm(请求解密) ->burpsuit(明文请求)->totalRpc-mitm(请求加密/响应解密)->burpsuit(明文响应)->beforeRpc-mitm(响应加密)

前置mitm启动方式:‍

mitmdump -p 6666 -s beforeRpc.py  --mode upstream:http://127.0.0.1:65530 --ssl-insecure

命令输入这个即可,意思是启动666端口监听,但是上游是burp的端口65530,然后burp的上游设置7777的加解密RPC。

03

通用RPC模板

 写了个通用模板方便使用:

import jsonimport requestsimport mitmproxyfrom mitmproxy import http,ctxfrom mitmproxy import flowfilterinfo = ctx.log.info# 工具方法def is_valid_json(text):    try:        json.loads(text)        return True    except json.JSONDecodeError:        return False# signdef rpcSign(param):    return getRpc(param, 'sign')# 加密方法def rpcEncode(param):    return getRpc(param, 'encode')# 解密方法def rpcDecode(param):    return getRpc(param, 'decode')# 获取RPC调用方法def getRpc(param,method):    headers = {        'Content-Type': 'application/x-www-form-urlencoded'    }    data = {        'action': method,        'group': 'zzz',        'param': param    }    resJson = requests.post('http://127.0.0.1:12080/go', headers=headers, data=data).json()    return resJson["data"]# 处理Param模式def changeRequestParam(reqObj):    reqObj.query.set_all("要修改的key", ["要修改的任意类型,又可以改为encode(xxx)"])    info(reqObj.query)# 处理Form模式def changeRequestForm(reqObj):    reqObj.urlencoded_form["要修改的key"] = "要修改的任意类型,又可以改为encode(xxx)"    info(reqObj.urlencoded_form)# 处理JSON模式def changeRequestJson(reqObj):    if reqObj.headers["Content-Type"].find("application/json") != -1:        if len(str(reqObj.content)) != 0:            if is_valid_json(reqObj.content):                # 说明传入的是JSON                jsonObj = json.loads(reqObj.content)                jsonObj["要修改的key"] = "要修改的任意类型,又可以改为encode(xxx)"                reqObj.content = json.dumps(jsonObj).encode('utf-8')                info(reqObj.content)# 处理整个请求体都为加密时def changeRquestBody(reqObj):     reqObj.content = rpcEncode(reqObj.content).encode('utf-8')     info(reqObj.content)# 处理请求头加密时候def changeRequestHeaders(reqObj):    reqObj.headers["Sign1"] = "要修改的任意类型,又可以改为sign(xxx)"# 响应全加密时候def changeResponseBody(reqObj):    reqObj.text = rpcDecode(reqObj.text)    info(reqObj.text)# 响应为json且部分加密的时候def changeResponseJson(reqObj):    if reqObj.headers["Content-Type"].find("application/json") != -1:         if len(str(reqObj.content)) != 0:              if is_valid_json(reqObj.content):                    jsonObj = json.loads(reqObj.content)                    jsonObj["要修改的key"] = "要修改的任意类型,又可以改为encode(xxx)"                    reqObj.content = json.dumps(jsonObj).encode('utf-8')                    info(reqObj.content)# 请求处理最终控制器(只需要改这里启用)def totalRequest(reqObj):    # changeRquestBody(reqObj)    pass# 响应处理最终控制器(只需要改这里启用)def totalResponse(reqObj):    # changeResponseJson(reqObj)    passclass Hook:    def __init__(self):        pass    # 处理request请求    def request(self, flow: mitmproxy.http.HTTPFlow):        request = flow.request        totalRequest(request)    # 处理response请求    def response(self, flow: mitmproxy.http.HTTPFlow):        response = flow.response        totalResponse(response)addons = [    Hook()]

只需要修改要启用的操作方法,并且totalResponsetotalRequest里面将其注册进去即可,小白都能用,读一下就懂了。

这段代码如果不想复制我就把他放进网盘里:【阿呆攻防】软件分享->脚本->JSRPC

JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

 

原文始发于微信公众号(阿呆攻防):JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年8月7日15:42:05
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   JSRPC|看不懂加密方式?稳了!以金融为例且提供最终样板脚本https://cn-sec.com/archives/3042786.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息