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

文摘   2024-11-13 11:23   北京  


文章导读

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


众亦信安,中意你啊!



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




基本情况

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

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



定位加解密函数



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

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

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




配置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项目地址: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");

提示连接成功。




写MITM脚本,解密成功



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

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

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

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

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

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

下游代理,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, unquote

class Mimitdownstream(): flag = 2 TargetHost = ["127.0.0.1"] 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, unquote
class 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(),]

代码运行情况:

Burp显示效果:

再和之前的对比一下:

嗨嗨安全
提供网络安全资料与工具,分享攻防实战经验和思路。
 最新文章