前言
JSRPC(JavaScript Remote Procedure Call)是一种基于 JavaScript 的远程过程调用协议,用于实现客户端和服务器之间的通信和函数调用。它允许开发人员在客户端 JavaScript 代码中调用远程服务器上的函数,以便获取数据、执行操作或获取服务。
在实际渗透测试的场景中,会遇到很多前端加密、签名校验、返回包加密等等的场景,如果是自己去尝试获取加解密函数,然后自己构造环境去绕过,第一时间成本确实很高,如果js文件进行混淆那种,时间成本还是很高的,第二点对于我这种看看还行,自己本地运行就是纯折磨的来说,jsrpc算是不错的选择。
因为不需要知道完整流程是如何实现的,只需要找到函数然后调用即可,因为没有找到数据包也加密的场景尝试自动解密,本文就拿快手src的登录功能进行演示。
文章实现的逻辑流程图如下:
正文
寻找网站加密函数
演示的站点用快手src,别的不说,快手src的礼物确实多(
直接搜索encrypt
或者password
都可以定位到加密的函数
因为不运行的状态下,浏览器可能不会加载这个js,导致你在console.log(le.encrypt("123",w(w({},0))));
的时候会提示le这个是未定义的,所以我们先设置断点,然后在调试模式下,设置为全局变量
设置完成后,尝试加密一个内容
这部分的内容完成后,就可以尝试构造jsrpc的部分了
安装Jsrpc
这里用到的是sekiro
,jsrpc只是其中的一部分
1
| https://github.com/yint-tech/sekiro-open
|
下载后,按照下面的方式运行
构建完成后,会生成文件夹sekiro-open-demo
,在bin文件夹中根据系统来选择运行.bat还是.sh即可
如果你不想自己构建,可以在下面地址里下载
1
| https://oss.iinti.cn/sekiro/sekiro-demo
|
运行后的样子如下:
这里要说明的是,在网上很多文章文档的地址都不正确(毕竟时间长,难免地址变了),新的地址是https://sekiro.iinti.cn/sekiro-doc/
在文档中可以看到,官方提供了注入的js
但是在.js中,还需要我们自己单独注册一个接口
所以我们把前面的js复制进来后,在底下自己自定义接口就好了(注意:免费版的地址是business-demo)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function guid() { function S4() { return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); } return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4()); } //Modify content group = "kuaishou"; //自定义组织名,一般以网站为单位 registerAction = "encrypt"; //自定义方法名,一般以函数为单位 var client = new SekiroClient("ws://127.0.0.1:5612/business-demo/register?group=" + group + "&clientId=" + guid()); client.registerAction(registerAction, function(request, resolve, reject) { response = le.encrypt(request['text'], w(w({}, o))); //接口返回内容 resolve(response); resolve(response); })
|
写完后,运行就ok了
这个时候我们访问接口,就能请求数据并加密了
设置mitmproxy
在我们的环境中,我们需要调用这个jsrpc来加密后,将处理过的数据包发送到网站中,所以需要一个mitmproxy来加密参数,然后发送
首先安装运行库
接着创建服务器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| import requests import re import json from mitmproxy import ctx
def encrypt(data): url="http://127.0.0.1:5612/business-demo/invoke?group=kuaishou&action=encrypt&text={}".format(data) res=requests.get(url) print(res.text) res = json.loads(res.text) return res['encryptText']
def request(flow): # 获取数据包 body = flow.request.get_text()
data = {} for pair in body.split("&"): key, value = pair.split("=") data[key] = value
# 获取 password password = data.get("password", "") phone = data.get("phone", "") # 调用 encrypt 函数进行加密 encrypted_password = encrypt(password) encrypted_phone = encrypt(phone) # 修改请求的 body 数据为加密后的值 data["password"] = encrypted_password data["phone"] = encrypted_phone
# 构造修改后的 body 数据 modified_body = "&".join([f"{key}={value}" for key, value in data.items()])
# 设置修改后的请求 body 数据 flow.request.set_text(modified_body) ctx.log.warn("加密内容: "+str(flow.request.get_text()))
# 请求后的数据 def response(flow): response = flow.response print(response.text) ctx.log.info(str(response.status_code))
|
注意我这里面的
1 2 3 4
| data = {} for pair in body.split("&"): key, value = pair.split("=") data[key] = value
|
正常来说,网上的例子都是json.loads(),但是我们的数据包并不是json格式的
所以只能自己获取内容后,重新生成这部分的内容,写完后保存,命令运行
1 2
| mitmproxy -v -s <filename> -p <port> mitmweb -v -s <filename> -p <port>
|
这两个都可以,web也会监听端口,但是web的话会多一个web的可视化界面
我们设置一个burpsuite的代理到mitmproxy中,然后尝试发送一个包
burpsuite数据包如下
我们在mitmweb中查看数据包,可以看到内容已经加密了