前端加密攻防实战:深度解析与绕过技巧(下)

admin 2024年11月11日13:16:38评论14 views字数 7042阅读23分28秒阅读模式

点个关注,谨防走丢~

前端加密攻防实战:深度解析与绕过技巧(下)

作者简介

许理想,ID:Drea1v1,具备丰富的甲乙方安全工作经验,主要研究领域:代码审计、漏洞挖掘、红蓝对抗、数据安全等。

前言

本系列文章旨在记录并分享我在前端加密绕过方面的实战经验,系列文章分上下两篇,分别以两个真实案例讲述前端加密安全防护的过程以及绕过方式,同时分别对所使用到的工具做了简单介绍。

在上一篇文章中,我们以某网站为例,介绍了如何绕过前端加签的安全防护,传送门:前端加密攻防实战:深度解析与绕过技巧(上)本篇文章将介绍如何绕过加签及加密双重防护的网站。

安全逻辑分析

先看看报⽂的组成,query参数只有⼀个sign(看到这个参数是不是有点熟悉,⼼⾥已经在笃定有签名校验了),请求体和返回体都是⼀串看不懂的字符串。请求header中有⼀个key为Encrypt的头,暂时不知道有什么⽤。

前端加密攻防实战:深度解析与绕过技巧(下)

好了,带上以上找到的信息,开始分析。现在要找⼀个切⼊点,可以试⼀下全局搜索sign,发现有多达⼏百个结果。

前端加密攻防实战:深度解析与绕过技巧(下)

逐个分析,发现⼀个可疑的地⽅,有出现data关键字以及sign,还有调⽤3DES进⾏加密,尝试在这⾥下断点,看看是否触发

前端加密攻防实战:深度解析与绕过技巧(下)

断点进来了,可以看到e是调⽤的3DES算法对r进⾏加密后的值,右边看到r是⼀个对象,有⼀个packages字段,其中包裹着header和request两个对象,request⼤概率是每个接⼝要传输的参数,可以看到登录输⼊的123323在request中有⼀个userName的key也是该值,所以e就是加密后的请求体。

前端加密攻防实战:深度解析与绕过技巧(下)

加密的密钥是固定的,为M,是⼀个WordArray数组。

前端加密攻防实战:深度解析与绕过技巧(下)

继续往下看,这⾥先把e的值记录起来,后⾯看看是不是就是真正的请求体。

前端加密攻防实战:深度解析与绕过技巧(下)

下⾯将请求参数加密后的内容e和⼀个固定的值globalConfig.transfer作为hex_hmac_md5函数参数,⽣成参数n,将n的值作为参数调⽤匿名函数w.encrypt(t,"base64") ,重新⽣成n,最终将n拼接到url中的?sign= 后⾯

前端加密攻防实战:深度解析与绕过技巧(下)

加密后的数据e赋值给了t.data:

前端加密攻防实战:深度解析与绕过技巧(下)

ok,已经⼤致知道逻辑了,直接放开,查看新发起的报⽂,看⼀下请求体、sign是不是上⾯记录的e的值。从下⾯可以看到,能对应上,没找错地⽅。

前端加密攻防实战:深度解析与绕过技巧(下)

前端加密攻防实战:深度解析与绕过技巧(下)

绕过思路

从上⾯分析可以知道,报⽂加密使⽤的是3DES算法,不排除有魔改的情况,先本地使⽤js通⽤的算法库试,如果没区别,就免去扣取加密代码的步骤。直接让AI给我们⽣成加密、解密代码:

前端加密攻防实战:深度解析与绕过技巧(下)

这⾥有个地⽅要改⼀下,AI给的代码key传的字符串,然后通过CryptoJS.enc.Utf8.parse⽣成⼀个WordArray对象。上⾯我们调试时,获取的就是WordArray对象,这⾥⼿⼯写⼀下代码初始化WordArray就⾏了(也可以将数组转回字符串,实际就是globalConfig.transfer)。整理⼀下,最终如下:

const CryptoJS = require("crypto-js");//对称加密秘钥var words = [1414545744,1346981460,1163019841,1128608304,825762624,556087609];var sigBytes = 24; // 例如,只使用前6个字节(即前1.5个32位整数)var wordArray = CryptoJS.lib.WordArray.create(words, sigBytes);function tripleDesEncrypt(plain) {    encryptContext  =  CryptoJS.TripleDES.encrypt(JSON.stringify(plain),wordArray,{        iv: CryptoJS.enc.Utf8.parse("12345678"),        mode: CryptoJS.mode.ECB,        padding: CryptoJS.pad.Pkcs7    }).toString()    return encryptContext}function tripleDesDecrypt(encText) {    decryptContent = CryptoJS.TripleDES.decrypt(encText,wordArray,{        iv: CryptoJS.enc.Utf8.parse("12345678"),        mode: CryptoJS.mode.ECB,        padding: CryptoJS.pad.Pkcs7    })    return decryptContent.toString(CryptoJS.enc.Utf8)}

先⽤解密算法看看前端⽣成的密⽂和后端返回的密⽂是否能正常解密。稳!!。

前端加密攻防实战:深度解析与绕过技巧(下)

加密算法解决了,还剩下sign,重头戏来了!将加密后的内容e和globalConfig.transfer传⼊hex_hmac_md5函数,⽣成n。

前端加密攻防实战:深度解析与绕过技巧(下)

AI解释可能为⼀个基于HMAC-MD5算法⽣成消息认证码(MAC)。

前端加密攻防实战:深度解析与绕过技巧(下)

同样先试⼀下使⽤通⽤算法⽣成,我就不截图了,最后验证正确。

//hex_hmac_md5的key,已打码key = 'xxxxxxxxxxxx'//参数为加密完的内容function hex_hmac_md5(message) {    var hmac = CryptoJS.HmacMD5(message,key);    // 将结果转换为十六进制字符串    var hex = hmac.toString(CryptoJS.enc.Hex);    return hex;}

⽣成的md5值后,继续继续跟进w.encrypt函数,调⽤this.$$encryptKey

前端加密攻防实战:深度解析与绕过技巧(下)

继续跟进this.$$encryptKey。this.$getDataForEncrypt(e, r)的e为上⾯⽣成的md5,r为空,会⽣成对应字符串的ascii码数组。

前端加密攻防实战:深度解析与绕过技巧(下)

前端加密攻防实战:深度解析与绕过技巧(下)

This.keyPair.encrypt有点复杂就不看了,也不需要知道。

前端加密攻防实战:深度解析与绕过技巧(下)

现在只要调⽤this.$$encryptKey就可以⽣成最终的sign值,但是从上⾯可以看到这个函数是⽐较复杂的,如果要把代码扣下来放到本地执⾏,要分析很多,并且需要补环境(函数执⾏所需的上下⽂)。通过时停⼤法,发现是可以直接调⽤函数的,那有没有办法可以直接获取当前运⾏时,直接执⾏函数呢?答案是有的,可通过JSRPC实现。

前端加密攻防实战:深度解析与绕过技巧(下)

自动化工具JSRPC

JSRPC利⽤RPC(远程过程调⽤)协议实现JavaScript函数的远程调⽤功能。其⼯作原理是在客户端(即浏览器端)中注⼊RPC环境,使客户端与JSRPC服务器之间建⽴起WebSocket连接,以实现双向通信。

此外,⽤户可在客户端注册所需的加解密函数。当RPC服务器向客户端发送请求时,客户端会接收并执⾏相应的函数⽅法,随后将处理结果反馈回RPC服务器。服务器在接收到结果后,会通过接⼝的⽅式返回给⽤户。

下载地址:https://github.com/jxhczhl/JsRpc

计划利用工具可以实现的效果

1、在bp中抓到的请求报⽂、返回报⽂都⾃动解密。

2、可直接在bp上输⼊明⽂的报⽂进⾏重放,插件根据内容⾃动⽣成加密请求体、sign。

3、不能影响原有浏览器上功能的使⽤。

实现思路:原来浏览器请求是转发到bp的,如需要对报⽂内容进⾏解密需要在浏览器和bp之间加⼀层mitmproxydownstream代理,通过execjs模块调⽤本地js函数对报⽂先进⾏解密,解密后再给到bp。在bp修改明⽂报⽂重放后,如直接转发到应⽤服务器,因报⽂为明⽂服务器⽆法识别,所以需要在bp与服务器之间再加⼀层mitmproxyupstream理,⽤于将明⽂的请求报⽂再进⾏加密后转发。当服务器收到请求后,返回加密报⽂会先给到mitmproxy-upstream,需要代理对其报⽂再进⾏解密后给到bp。bp拿到返回报⽂后,为保证前端功能正常,将内容返回给浏览器时,需要再通过mitmproxy-downstream对返回内容进⾏加密。

前端加密攻防实战:深度解析与绕过技巧(下)

说干就干,通过以下7个步骤完成上面的整体流程。

步骤⼀:开启JSRPC服务,监听端⼝和地址可以通过同⽬录下的config.yaml

前端加密攻防实战:深度解析与绕过技巧(下)

步骤⼆:注⼊JS,构建通信环境。

项⽬提供代码JsEnv_Dev.js,复制后在浏览器控制台回⻋运⾏。

前端加密攻防实战:深度解析与绕过技巧(下)

步骤三:注册js⽅法

注册前,需要将想要调⽤的函数赋值给全局变量(window), window.sign1=this.keyPair和window.sign2 = this.$getDataForEncrypt ⽅便调⽤。

注:赋值给全局变量时,需进⼊到调试模式,不然⽆法获取函数执⾏的上下⽂。

前端加密攻防实战:深度解析与绕过技巧(下)

步骤四:在控制台执⾏远程调⽤代码

//连接rpc服务器websocket,group名字可随便起var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=demo1");// 注册一个方法sign,param为需要传入的参数,这里为hex_hmac_md5生成的md5值// 第二个参数为函数,resolve里面的值是想要的值(发送到服务器的)demo.regAction("sign", function (resolve,param) {    console.log(param)    var unitArray = window.sign2(param);    console.log(unitArray)    var unitArray1 = window.sign1.encrypt(unitArray,false);    console.log(unitArray1)    resolve(unitArray1.toString("base64"));})

前端加密攻防实战:深度解析与绕过技巧(下)

步骤五:注册完js函数后,通过访问接⼝http://localhost:12080/go?group=demo1&action=sign&param=123456 调⽤函数,返回内容中data为函数最终⽣成结果。

前端加密攻防实战:深度解析与绕过技巧(下)

步骤六:现在已经能⽣成sign和加解密了,那可以开始写插件

分为两部分,⼀部分是下游代理服务downstream,⼀个是上游代理服务upstream。下游服务器主要是将浏览器到bp之间的报⽂进⾏解密,将bp返回到浏览器的返回报⽂进⾏加密。上游服务器将bp到服务器的请求报⽂进⾏加密和加签,服务器返回报⽂进⾏解密。

下游代理服务downstream.py

from mitmproxy import ctxfrom mitmproxy import httpimport execjsimport json#加载js脚本jsCode = open('xxx.js','r',encoding='utf-8').read()class Downstream():    #解密浏览器到bp的请求报文    def request(self,flow: http.HTTPFlow):        if 'xxx.xxx.com' in flow.request.pretty_url and flow.request.method == 'POST':            #获取请求体            ctx.log.info("浏览器请求加密数据 => "+flow.request.get_text())            #解密请求体            ctx.log.info("浏览器请求明文数据 => "+self.decrypt(flow.request.get_text()))            #将解密后的内容重新给到请求体            flow.request.set_text((self.decrypt(flow.request.get_text())))    #加密bp到浏览器的返回报文    def response(self,flow: http.HTTPFlow):        if 'xxx.xxx.com' in flow.request.pretty_url and flow.request.method == 'POST':            ctx.log.info("响应报文加密前数据 => "+flow.response.get_text())            #加密返回报文            resp = self.encrypt(json.loads(flow.response.get_text()))            ctx.log.info("响应报文加密数据 => "+resp)            #重新将加密报文赋值给返回体            flow.response.set_text(resp)    def encrypt(self,plain):        enc = execjs.compile(jsCode).call('tripleDesEncrypt',plain)        return enc    def decrypt(self,enc):        plain = execjs.compile(jsCode).call('tripleDesDecrypt',enc)        return plainaddons = [Downstream()]

上游代理服务器upstream.py:

import requests,execjsfrom mitmproxy import ctxfrom mitmproxy import httpimport json#加载js脚本jsCode = open('xxx.js','r',encoding='utf-8').read()#jsrpc接口地址url = "http://127.0.0.1:12080/go?group=demo1&action=sign&param="class Upstream:    #加密bp到服务器的请求,并生成新的签名    def request(self,flow: http.HTTPFlow):        if 'xxx.xxx.com' in flow.request.pretty_url and flow.request.method == 'POST':            #req = json.loads(flow.request.get_text())            #加密请求报文            req = self.encrypt(json.loads(flow.request.get_text()))            ctx.log.info("浏览器请求加密数据 => "+ req)            #对新生成的报文进行签名            sign = self.getSign(req)            ctx.log.info("浏览器请求sign => "+ sign)            #将新生成的签名重新赋值给sign            flow.request.query['sign'] = sign            #将加密后的请求报文重新赋值给请求体            flow.request.set_text(req)    #将服务器到bp的返回报文解密    def response(self,flow: http.HTTPFlow):        if 'xxx.xxx.com' in flow.request.pretty_url and flow.request.method == 'POST':            ctx.log.info("响应报文加密数据 => "+flow.response.get_text())            ctx.log.info("响应报文解密数据 => "+self.decrypt(flow.response.get_text()))            #将解密后的返回报文重新赋值到返回            flow.response.set_text((self.decrypt(flow.response.get_text())))    def encrypt(self,plain):        enc = execjs.compile(jsCode).call('tripleDesEncrypt',plain)        return enc    def decrypt(self,enc):        plain = execjs.compile(jsCode).call('tripleDesDecrypt',enc)        return plain    def getSign(self,enc):        #生成md5        md5 = execjs.compile(jsCode).call('hex_hmac_md5',enc)        #调用jsrpc接口        resp = requests.get(url + md5)        #解析jsrpc返回的内容,获取data        jsonStr = json.loads(resp.text)        return jsonStr['data']addons = [Upstream()]

步骤七、运⾏上下游代理

下游服务器启动时需要增加--mode 参数,表示将流量转发到http://127.0.0.1:8080,bp监听的地址根据实际情况配置。--ssl-insecure 忽略ssl证书验证。

mitmdump --listen-port 9999 -s downstream.py --mode upstream:http://127.0.0.1:8080 --ssl-insecure

上游服务器启动

mitmdump --listen-port 6666 -s upstream.py

bp设置上游服务器:

前端加密攻防实战:深度解析与绕过技巧(下)

浏览器代理配置,这⾥需要设置为下游代理的监听地址:127.0.0.1:9999

前端加密攻防实战:深度解析与绕过技巧(下)

效果展示,请求和返回报⽂都是明⽂,直接修改内容⾃动加密解密,同时浏览器功能使⽤正常:

前端加密攻防实战:深度解析与绕过技巧(下)

前端加密攻防实战:深度解析与绕过技巧(下)

总结

本系列文章以两个案例讲解前端加密的过程以及绕过方式,同时分别对所使用到的工具做了简单介绍。mitmproxy功能⾮常强⼤,本⽂也只是介绍了其能⼒的冰⼭⼀⻆。除了可以与jsrpc联动外,还可以和frida联动,实现app的加解密。

⽂章写得⽐较匆忙,有错的地⽅欢迎联系交流。

声明:本文仅供学习参考使用,任何人不得将其用于非法用途,否则后果自行承担,与本公众号无关。

原文始发于微信公众号(安全有术):前端加密攻防实战:深度解析与绕过技巧(下)

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年11月11日13:16:38
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   前端加密攻防实战:深度解析与绕过技巧(下)http://cn-sec.com/archives/3382519.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息