0x01 序言

前端的加解密一直是很多师傅经常遇到过的问题,一个加解密传输,签名,防篡改,导致很多时候没有办法对参数的值进行更改,代码中存在的web漏洞也没办法测试出来,当然这也是一种符合所有场景的防御办法,增加攻击成本,是一个很棒的防御方案。

有的师傅测试遇到时无非会有几种想法:

  • 这个还需要研究js,emm,不太会,以后再学习(然后鸽鸽鸽

  • 这个研究一段时间应该也能尝试一下,以前貌似看到过这种文章,照着文章研究一天应该也可以试试,但是好浪费时间,最后也不一定弄好,弄好也不一定有洞,下次有机会在测吧(然后鸽鸽鸽

  • 好麻烦,还要把用到的js弄下来本地运行,有的是打包的js还不能用,有的还要修改里面的js代码,为什么要这么麻烦,这次测试差不多就得了,摸鱼才是真理(然后摸摸摸

    ps: 有的师傅确实时间紧任务重,可以理解,毕竟如果是半吊子去安排任务大部分都会觉得时间给的很充足~

0x02 JSRPC理解

对于我们来说,jsrpc就是将本地和浏览器,看做是服务端和客户端,二者之间通过 WebSocket 协议进行 RPC 通信,在浏览器中将加密函数暴露出来,在本地直接调用浏览器中对应的加密函数,从而得到加密结果,不必去在意函数具体的执行逻辑,也省去了扣代码、补环境等操作,可以省去大量的逆向调试时间。

0x03 一次完整的某银行网站利用jsrpc解决js加密传输的笔记

1.下载jsrpc,同样用jsrpc技术的项目还有sekiro,我的目标为https站点,注释掉main.go的197行和198行,解除194行的注释,进行编译生成运行

image-20220424235027175

2.打开网站f12的控制台,打开工具里面的jsenv.js,内容全部复制,复制到控制台直接运行,这样这个代码就注入了

image-20220425000931473

3.控制台继续添加js代码,作用是使用js的websocket协议连接上面工具运行开放的12080端口,group和name的值随意,后面使用链接的时候值一样就行,可以当做一句话木马的密码逻辑理解

image-20220425001148163

4.发现网站会每次生成sign放入header中,修改参数及值会导致访问网站失败,所以每次改值就需要生成对应的签名

image-20220424235331639

5.根据sign关键字去js代码中寻找加密点

image-20220424235909158

6.给这个函数末尾添加断点,网站正常运行,发现目标网站存在js反调试,如果网站没有反调试的这步忽略即可,这里利用burp的匹配并替换的功能替换网站2处反调试的js代码,具体原理不多说了,这个不是今天主角

image-20220425001607134
image-20220425001620585 image-20220425001633691

7.网站正常调试,运行至断点处,此时回到控制台,对js中的函数和值进行输出,验证是否满足加密要求,console.log(r),u("aaaa"),发现r变量确实是sign的值,u函数确实是加密函数,根据传入的值生成加密结果存入r

image-20220425002329851
image-20220425002525685

8.断点,yyds,此时控制台能正常调用加密函数了,需要把u函数改为全局函数,意思就是以后不需要断点也能够调用这个函数的意思,使用代码window.test = u,控制台输入即可,关闭调试功能,发现控制台直接调用test函数,就相等于调用u函数了

image-20220425003046058

9.继续控制台注入js代码,hacker2就是action参数的值,看了远程调用的url就明白了,http://127.0.0.1:12080/go?group=zzz&name=hlg&action=hacker2&param=xxxxxxxxxx

demo.regAction("hacker2", function (resolve,param) {
    console.log(param);
    var base666 = test(param); //test就是上面那个加密函数
    resolve(base666);
})

比如想对字符串sven加密,这样就可以直接远程调用浏览器的加密函数了,加密api +1,之后随意你怎么使用都可以了,各显神通,任意发挥即可,或许等autoDecode这个bp插件以后功能更全面了,没准直接能对接上

image-20220425003522476

10.我这里是用mitmdump弄了个py中转,burp的包转到这个代理上,对请求进行一个加工后转发。自动获得我需要的参数和值当做字符串,然后请求调用控制台的api获得加密后的sign值,替换到burp发来的请求包里,再将请求发出去,这样什么工具只要通过burp发送请求,就都会是签名正确的请求了

#encrypt.js
import requests
import json
from urllib.parse import unquote
from urllib.parse import quote

def request(flow):
    print('request url is %s' % flow.request.url)
		/*
		随意对flow的值进行修改即可,同时要注意url编码问题
    sign = json.loads(requests.get("http://127.0.0.1:12080/go?group=zzz&name=hlg&action=hacker2&param={}".format("想要加密的字符串")).text)
    flow.request.headers['Sign'] = sign['data']
		*/
image-20220425004320409
image-20220425004456963

0x04 总结

1.无需太高的js代码水平

2.步骤大多都是机械可重复步骤

3.无论js什么格式,都不影响操作

4.当然也会有一些知识会影响使用理解,比如浏览器调试水平,阅读理解水平,burp使用水平,漏洞逻辑理解水平

5.内卷驱动器