文章总结: 本文介绍了一种结合JsRpc与Yakit热加载技术,绕过前端加密进行渗透测试的方法。通过JsRpc远程调用浏览器中的加密函数,无需还原复杂的运行环境即可生成签名。结合Yakit编写热加载脚本,实现明文参数自动加密发包,大幅提升了对前端加密接口的测试效率与便利性。 综合评分: 83 文章分类: 渗透测试,WEB安全,安全工具,实战经验,逆向分析
JsRpc+Yakit热加载实现明文编辑加密发包
逐影安全
只会看监控的实习生
2026年1月2日 08:00 广东
引言
在渗透测试过程中,常常会遇到前端对请求数据进行加密的情况,增加了分析与利用的难度。传统方法通常依赖于 JavaScript 逆向工程,不仅可能需要搭建复杂的运行环境,即使完整提取了加密代码,也难以在本地准确还原加解密逻辑,严重影响测试效率。
为提升效率,本文将介绍一种基于 JsRpc + Yakit 热加载 的实用方法,实现前端明文数据的直接编辑与自动加密发包。通过 JsRpc 的远程方法调用机制,我们无需还原整个运行环境,只需定位加密函数即可调用,从而快速绕过加密层,专注业务逻辑测试。
本文测试环境基于 Yakit 自带靶场,所用工具及项目地址如下: JsRpc 项目地址:https://github.com/jxhczhl/JsRpc
Yakit 项目地址:https://yaklang.com/products/download_and_install/
操作步骤
安装并启动yakit靶场
如下图所示,在抓包过程中可以观察到请求中包含名为 signature 的参数,即请求的签名字段。
如下图所示,通过分析 JavaScript 代码可以确认,签名(signature)是对 username 和 password 两个参数进行加签生成的。
当然,靶场中的 JavaScript 加密逻辑相对简单,但实际测试的整体思路是通用的。本文不重点讲解如何定位加解密函数——在实际渗透过程中,常会遇到动态 key、接口返回参与签名的参数等情况,这些问题通过多构造几次 yak 函数即可应对。
根据 JavaScript 中的加密逻辑,在浏览器控制台手动执行一次加密操作,生成的 signature 与初始抓包结果一致,说明还原无误。
若加解密函数定义在局部作用域中(如某函数内部或模块作用域),在浏览器控制台中可能无法直接访问,此时可通过将函数挂载到 window 对象的方式,提升为全局可调用函数。在当前靶场环境中,加解密函数已位于全局作用域,因此无需额外处理
JsRpc食用
关于JsRpc的详细解释可移步至https://mp.weixin.qq.com/s/vHoVPINf4GKhR36LSQlDXw,小天师傅讲的很详细。
注入JS,构建通信环境(/resouces/JsEnv_De.js)
将https://github.com/jxhczhl/JsRpc/blob/main/resouces/JsEnv_Dev.js代码复制到控制台
远程调用: 浏览器预先注册js方法 传递函数名调用
resolve(Encrypt(decoded)); 实际上调用的是 JavaScript 文件中的 Encrypt(word) 函数。
// 注入环境后连接通信
var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=Encrypt");
demo.regAction("Encrypt", function (resolve,param) {
var decoded = atob(param);
resolve(Encrypt(decoded));
})
如果目标函数需要传递多个参数,JsRpc 项目中(https://github.com/jxhczhl/JsRpc)提供了“多参数调用”的示例,可供参考。
运行服务端后,控制台会输出日志,并在有客户端连接时显示上线提示信息,方便实时监控调用情况。
通过调用接口并传递参数,可以获取加签后的结果。为了确保参数在传输过程中不被解析异常,建议对其进行 Base64 编码,尤其是在渗透测试过程中频繁修改参数的情况下。如果参数中包含特殊字符,未编码可能导致解析错误或请求失败。
在实际调用中,注入的代码中包含 var decoded = atob(param);,会先对传入的参数进行 Base64 解码,然后将解码后的值传入加签函数进行处理。
http://127.0.0.1:12080/go?group=Encrypt&action=Encrypt¶m=dXNlcm5hbWU9YWRtaW4mcGFzc3dvcmQ9YWRtaW4=
yakit webfuzz热加载
yak runner运行调试
我们可以使用 yak 编写一个脚本,用于获取加签后的结果。脚本示例如下:
# port scan plugin
yakit.AutoInitYakit()
handleCheck = func(target,port,word){
addr = str.HostPort(target, port)
isTls = str.IsTLSServer(addr)
packet = `
GET /go?group=Encrypt&action=Encrypt¶m={{params(word)}} HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cache-Control: no-cache
Connection: keep-alive
Host: {{params(target)}}
`
rsp,req,_ = poc.HTTP(packet,
poc.params({"target":addr,"word":word}),
poc.https(isTls),
poc.redirectTimes(0),
)
println(req)
if len(rsp) > 0 {
body = poc.GetHTTPPacketBody(rsp)
printf("获取加密结果:\n"+string(body))
jsonObj = json.loads(string(body))
// 获取字段值
sign = jsonObj["data"]
return sign
}else{
println("失败")
}
return
}
func base_word(name,passwd){
word = "username=" + name + "&password="+passwd
return codec.EncodeBase64(word)
}
word = base_word("admin","admin")
sign = handleCheck("127.0.0.1",12080,word)
println("\nsign:"+sign)
// signRequest = result => {
// pairs := result.SplitN("|", 2)
// dump(pairs)
// word = base_word(pairs[0], pairs[1])
// sign = handleCheck("127.0.0.1",12080,word)
// return sign
// }
经过运行调试,成功获取到加签结果,验证无误。
热加载脚本
热加载和 Yak Runner 仅有细微差别,脚本可直接按照 Yakit 语法编写。主要加密逻辑集中在 signRequest 函数中;如果加密参数需要动态获取,可通过封装多个辅助函数来实现。
# port scan plugin
yakit.AutoInitYakit()
handleCheck = func(target,port,word){
addr = str.HostPort(target, port)
isTls = str.IsTLSServer(addr)
packet = `
GET /go?group=Encrypt&action=Encrypt¶m={{params(word)}} HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cache-Control: no-cache
Connection: keep-alive
Host: {{params(target)}}
`
rsp,req,_ = poc.HTTP(packet,
poc.params({"target":addr,"word":word}),
poc.https(isTls),
poc.redirectTimes(0),
)
println(req)
if len(rsp) > 0 {
body = poc.GetHTTPPacketBody(rsp)
printf("获取加密结果:\n"+string(body))
jsonObj = json.loads(string(body))
// 获取字段值
sign = jsonObj["data"]
return sign
}else{
println("失败")
}
return
}
func base_word(name,passwd){
word = "username=" + name + "&password="+passwd
return codec.EncodeBase64(word)
}
// word = base_word("admin","admin")
// sign = handleCheck("127.0.0.1",12080,word)
// println("\nsign:"+sign)
signRequest = result => {
pairs := result.SplitN("|", 2)
dump(pairs)
//接收两个参数,按照yakit语法写即可 {{yak(signRequest|admin|admin)}}
word = base_word(pairs[0], pairs[1])
sign = handleCheck("127.0.0.1",12080,word)
return sign
}
调试运行通过后,即可修改参数进行发包操作,批量处理同样支持,效率大幅提升。
参考资料:
https://github.com/jxhczhl/JsRpc
https://yaklang.com/articles/vulnerability_testing_after_being_encrypted_by_the_front-end
https://mp.weixin.qq.com/s/vHoVPINf4GKhR36LSQlDXw
原文链接:https://forum.butian.net/share/4452
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:只会看监控的实习生 逐影安全《JsRpc+Yakit热加载实现明文编辑加密发包》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。








评论