本质
JS的对抗 本质无非就是开发者通过一系列加密算法防止用户恶意爆破,攻击者通过逆向,推测与实践实现自加密逻辑爆破的过程。

环境靶场:
0ctDay/encrypt-decrypt-vuls: 加解密逻辑漏洞靶场 (github.com)
手把手js逆向断点调试&js逆向前端加密对抗&企业SRC实战分享
网站架构
关于加解密也引出许多思考,第一是实现HTTP加解密是一对一实现的吗?第二是如果把数据包加密了,那安全设备是不是也失效了,这里带着几个疑问简单了解一下加解密的实现流程。
这里以请求为例

用户输入明文消息在被JS提取后, 通过JS中定义的加密方法进行加密并制作请求包进行请求。 发送到后端后,一般来说由加解密网关进行解密后, 发送到真正提供服务的服务器上。
加解密网关本质其实就是一种反向代理,除了本身转发数据包的功能以外,还对数据包进行解密。
按照这种部署模式,有两个优势:
- 如果新增分布式系统, 无需重复实现加解密功能, 直接使用作为该网关的下游服务即可。
- 在springcloud gateway和 分布式系统之间部署安全设备, 可以检测明文流量识别攻击行为。
改包的防范
先汇总一下目前流行的防止改包方式
主要是这么几个方面
1. 请求参数和路径的加密
如果原始请求是GET请求,或防止访问者获取请求路径,通常会将用户实际的请求路径和GET请求参数封装都封装为POST请求的请求体,通过加解密网关再还原为原始GET请求传入后端分布式服务上。 在APP中比较常见。
表现的形式通常为: 抓包后发现访问任何功能都是同一路径,并且请求全为密文
2. 请求体的加密
- 这类在纯web中最常见, 通常仅仅加密接口请求的请求体内容,但有以下几类加密问题。
- 使用固定密钥 — 顾名思义, 这种情况一般JS中会存储密钥, 属于最简单的一种
- 使用动态密钥 — JS中不存储,一般用户第一次请求后将密钥加密写入COOKIE或本地存储中, 这类加密追踪难度较大。
- 对称加密 — 加解密数据包内容同一套密钥
- 非对称加密 — 加密一套解密一套
- 算法 — 算法就不是特别固定了, 常见的诸如AES RSA等, 也遇到过使用国密算法或一些冷门算法。
3. 签名
- 签名的应用也十分广泛,app,小程序和现在许多web中均存在,签名的构成主要是以下几点
- RequestId — 为了防止重放攻击, 客户端生成随机RequestId 服务端接收后保存至Redis中, 如果再次接收到此RequestID, 则视为非法请求
- 时间戳 — 添加时间戳的超时时间, 一旦超时, 原始数据包失效
- 签名本身 — 通过 requestId + 原始请求体或请求参数 + 时间戳 + 盐值合并生成哈希值 从而保证以上参数的有效性和唯一性
JS逆向
需要懂得基础的JS知识,和调试方法
JS调试基础
本质就是以浏览器的Devtools控制与调试js前端代码

其中里面的作用域,调用堆栈,XHR断点这三个功能需要了解认识下

一、作用域(Scope)
作用域是指变量、函数和对象在代码中可访问的范围,决定了标识符(变量名、函数名)的可见性。
主要类型:
全局作用域:在所有函数和代码块之外声明的变量,在整个程序中都可访问。
函数作用域:在函数内部声明的变量,仅在该函数内部可访问。
块级作用域:由 {} 包裹的代码块(如 if、for、while)中用 let/const 声明的变量,仅在块内可访问。
和大部分语言变量作用域概念的思路共通
二、调用堆栈(Call Stack)
调用堆栈是JavaScript引擎用于管理函数调用顺序的一种数据结构(遵循“后进先出”原则)。
工作原理:
当函数被调用时,引擎会为其创建一个“执行上下文”并压入栈顶
函数执行完毕后,其执行上下文从栈顶弹出,控制权回到之前的函数。
栈顶始终是当前正在执行的函数。
三 、XHR断点(XHR Breakpoint)
XHR断点是浏览器开发者工具中的一种调试功能,用于在发送AJAX请求(XMLHttpRequest 或 Fetch)时暂停代码执行,方便调试网络请求相关逻辑。
使用场景:
调试接口请求参数是否正确
查看请求发送时机和触发条件
分析请求被拦截或修改的逻辑
四、 js基础断点调试
我们这里随便输入一个电话号码以及密码,直接看这个网络这里,可以看到账户输入的账户、密码都被进行了加密
其中我们常见的加密内容是md5、base64加密的,但是下面这个系统加密的一看就不是常见的加密方式

像这个,我们要是想要在我们输入账户密码的后,在传输到服务器端中可以将其加密的字段截取,然后进行分析,看看这个网站是使用什么类型进行加密的,就可以进行破解了,这就是后面我需要讲的js断点调试。
认识下这几个按钮工具
工具栏作为断点调试的操作工具,包含了 6 个按钮:
按钮 1:让代码继续执行,运行到下一个断点会中断执行,如果没有设置断点会直接运行完代码
按钮 2:跳过下一个函数调用。即不遇到函数时,执行下一步;遇到函数时,不进入函数直接执行下一步
按钮 3:跳进下一个函数上下文。即不遇到函数时,执行下一步;遇到函数时,进入函数上下文,查看函数具体内容
按钮 4:跳出当前函数调用
按钮 5:单步调试,当前断点的下一步
按钮 6:停用/激活全部断点

情景1

这里直接搜索Vip/LoginResult接口关键字,因为我们断点调试的话需要点击web端端某个个功能点,出发我们后面打的断点才可以成功,这里我们开始点击登陆,直接请求的是这个接口,所有这里我们可以先搜索这个Vip/LoginResult关键词看看
直接打开源代码 ctrl+shift+f全局检索

找到这个有关登陆功能处的代码,然后打断点

点击登陆按钮,就可以成功执行断点了,右边那个按钮就是执行到下一个断点

可以看到图中,代码断点运行成功了,在控制台输入logindata,就可以显示对应的加密数据了(手机号、密码)

情景2
老规矩还是直接输入**手机号和密码,然后看F12中的网络数据包,**可以看到这里的密码也是进行了加密

找到登陆口附近的js代码,寻找publickey关键字

这里先随机设置一个断点
设置好断点后,点击登录,即可触发断点,进行 js 断点的调试。

可以看到点击完登陆,出现了这个功能

常见的js逆向加密搜索关键字:
1 | encrypt |
直接进行js断点调试,在控制台输入dataJson.password,就可以看到密码的js加密数据
情景3
标签断点法

F12,然后点击1,然后把鼠标选中2(登陆功能上)

然后右键选中“登陆”标签,将子树修改,和属性修改都勾选上。然后我们随便输入一个账号密码点击登录

这样就可以成功 把运行的js代码给断下来了,但是目前这个网站今天写文章的时候已经找不到加密函数了,可能是已经修复了这个系统

五、针对算法对症下药
AES固定Key
第一种比较简单,key和iv写死,抓包发现数据传输被加密了

这里直接定位搜索“encryptedData”加密字段定位到算法位置

简单的分析,就是一个固定key 和iv 的aes加密,直接还原明文数据
1 | {"username":"admin","password":"123456"} |

这里阐述一个解密工具
autoDecoder
1 | 工具下载链接:https://github.com/f0ng/autoDecoder |

AES服务端获取key
key和iv没有写在前端,直接使用bp抓包即可


这里使用bp插件autoDecoder,先配置自带的方法,红框的配置要注意配置好

主页面配置,仅登录所以关键字写了password

然后进入intruder进行爆破登录尝试
成功爆破

非对称加密-RSA 加解密
如何快速判定 RSA 呢?
RSA 只能加密短小的数据,如果数据太大,会直接报错,因此可以入超长数值,看看是否报错!

提示了这个错误,显示加密失败,说明就是非对称加密- RSA加密了

对于非对称加密,他需要设置公钥,因此一般全局搜索:
1 | setpublickey、encrypt |
Sign 签名校验绕过
直接使用bp抓包,数据包的参数如下:username、password、nonce、timestamp、signature

演示下这个signature参数的由来
登陆界面,密码输入1234567,返回包提示密码错误

这个靶场的密码是123456,那么在bp数据包中,直接这里就把密码修改成123456,看看返回包
显示signature校验不正确,因为前面username、password、nonce、timestamp参数会生成signature传入到后台,后台就回和我们这里输入到signature进行匹配,要是不一样,就会进行报错
搜索关键字:signature,去断点看js代码
1 | function sendDataWithNonce(url) { |

那么直接根据datatoSign拼接出data字符串+ 使用CryptoJS库的HmacSHA256方法,用secretKey(be56e057f20f883e)对拼接后的字符串进行 HMAC-SHA256 加密,生成signature(签名)
再替换回到数据包中即可成功登陆了