问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
JsRpc+Yakit热加载实现明文编辑加密发包
渗透测试
引言 在渗透测试过程中,常常会遇到前端对请求数据进行加密的情况,增加了分析与利用的难度。传统方法通常依赖于 JavaScript 逆向工程,不仅可能需要搭建复杂的运行环境,即使完整提取了加密代...
**引言** ====== 在渗透测试过程中,常常会遇到前端对请求数据进行加密的情况,增加了分析与利用的难度。传统方法通常依赖于 JavaScript 逆向工程,不仅可能需要搭建复杂的运行环境,即使完整提取了加密代码,也难以在本地准确还原加解密逻辑,严重影响测试效率。 为提升效率,本文将介绍一种基于 **JsRpc + Yakit 热加载** 的实用方法,实现前端明文数据的直接编辑与自动加密发包。通过 JsRpc 的远程方法调用机制,我们无需还原整个运行环境,只需定位加密函数即可调用,从而快速绕过加密层,专注业务逻辑测试。 本文测试环境基于 Yakit 自带靶场,所用工具及项目地址如下: JsRpc 项目地址:<https://github.com/jxhczhl/JsRpc> Yakit 项目地址:[https://yaklang.com/products/download\_and\_install/](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)**)** 将[https://github.com/jxhczhl/JsRpc/blob/main/resouces/JsEnv\_Dev.js](https://github.com/jxhczhl/JsRpc/blob/main/resouces/JsEnv_Dev.js)代码复制到控制台  ### **远程调用: 浏览器预先注册js方法 传递函数名调用** `resolve(Encrypt(decoded));` 实际上调用的是 JavaScript 文件中的 `Encrypt(word)` 函数。 ```js // 注入环境后连接通信 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&param=dXNlcm5hbWU9YWRtaW4mcGFzc3dvcmQ9YWRtaW4=](http://127.0.0.1:12080/go?group=Encrypt&action=Encrypt¶m=InVzZXJuYW1lPWFkbWluJnBhc3N3b3JkPWFkbWluIg==)   **yakit webfuzz热加载** -------------------- ### **yak runner运行调试** 我们可以使用 `yak` 编写一个脚本,用于获取加签后的结果。脚本示例如下: ```js # 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` 函数中;如果加密参数需要动态获取,可通过封装多个辅助函数来实现。 ```js # 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://yaklang.com/articles/vulnerability_testing_after_being_encrypted_by_the_front-end) <https://mp.weixin.qq.com/s/vHoVPINf4GKhR36LSQlDXw>
发表于 2025-07-14 14:03:47
阅读 ( 215 )
分类:
渗透测试
4 推荐
收藏
0 条评论
请先
登录
后评论
all
2 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!