记一次不存在的加解密实战渗透
字数 1882 2025-08-24 07:48:10
前端加密渗透实战分析:从JS加密到明文传输
0x00 背景介绍
在渗透测试过程中,前端JS加密是常见的防护手段。本文记录了一次针对前端加密网站的渗透过程,详细分析了加密机制并实现了明文传输。
0x01 初步分析
1.1 加密参数识别
在抓包过程中发现两个加密参数:
frameUrlSecretParamframeBodySecretParam
1.2 搜索策略
-
直接搜索参数名:
frameUrlSecretParam:存在于混淆加密的JS中,但断点调试发现与业务流程无关frameBodySecretParam:直接搜索不到
-
使用扩展搜索方法:
- 搜索常见加密相关关键词:
encrypt、decrypt、JSON.parse、JSON.stringify、ajax等 - 通过分析找到疑似加密文件:
sm2secrity.js和miniui.js
- 搜索常见加密相关关键词:
-
确定核心加密函数:
encryptAjaxParams是frameBodySecretParam的核心加密函数
0x02 加密机制分析
2.1 基础加密流程
核心代码片段:
return window['\x75\x73\x65\x53\x6d\x32\x45\x6e\x63\x72\x79\x70\x74']
? _0x3a232b = _0xbfd749(0x1f7) + Util[_0xbfd749(0x223)](mini[_0xbfd749(0x210)](_0x4b8958))
: _0x3a232b = Util[_0xbfd749(0x1ee)](mini[_0xbfd749(0x210)](_0x4b8958))
2.1.1 代码解析
-
三目运算符条件部分:
window['\x75\x73\x65\x53\x6d\x32\x45\x6e\x63\x72\x79\x70\x74']解码后为window.useSm2Encrypt- 这是一个布尔值,决定使用哪种加密方式
-
当
useSm2Encrypt为false时的加密流程:_0x3a232b = Util[_0xbfd749(0x1ee)](mini[_0xbfd749(0x210)](_0x4b8958))mini[_0xbfd749(0x210)]对应JSON.stringifyUtil[_0xbfd749(0x1ee)]是加密函数- 整体流程:
加密值 = Util.encrypt(json.stringify(原参数值))
-
实际加密实现:
var _0x747baa = _0x20b204[_0x32df64(0x206)](_0x20b204[_0x32df64(0x1e5)](_0x20b204[_0x32df64(0x206)](_0x4510fe)));- 解码后为:
window["encodeURIComponent"](window["bota"](window["encodeURIComponent"](值))) - 实际加密步骤:URL编码 → Base64编码 → URL编码
- 解码后为:
2.2 SM2加密流程
当window.useSm2Encrypt为true时的加密流程:
_0x3a232b = _0xbfd749(0x1f7) + Util[_0xbfd749(0x223)](mini[_0xbfd749(0x210)](_0x4b8958))
_0xbfd749(0x1f7)解码为字符串"encbody_"Util[_0xbfd749(0x223)]是SM2加密函数- 整体流程:
加密值 = "encbody_" + Util.sm2Encrypt(json.stringify(原数据))
0x03 实现明文传输
3.1 Burp Suite方案
使用Burp的Match and replace功能:
- 匹配加密参数
- 替换为原始明文数据
3.2 mitmproxy自动化方案
from mitmproxy import proxy, options
from mitmproxy.tools.dump import DumpMaster
import json, time
class DLNSProxy:
@staticmethod
def request(flow):
if flow.request.method == 'POST':
datas = flow.request.get_content()
print('#requests path#')
print(flow.request.path)
datas = json.loads(datas.decode())
req_datas = xxx加密算法()
flow.request.raw_content = req_datas
@staticmethod
def response(flow):
if flow.request.method == 'POST':
datas = flow.response.get_content()
try:
datas = json.loads(datas.decode())
except:
pass
body = datas["body"].replace(" ","").split("\u001d")[0]
print("-")
de = aes_decrypt(body)
datas["text"] = de
rsp_data = json.dumps(datas, ensure_ascii=False, separators=(",",":")).encode()
Content_Length = bytes(str(len(rsp_data)), 'utf-8')
headers = flow.response.headers
headers['Content-Length'] = Content_Length
flow.response.raw_content = rsp_data
def start():
bp = DLNSProxy()
opts = options.Options(listen_host='127.0.0.1', listen_port=8888)
pconf = proxy.config.ProxyConfig(opts)
m = DumpMaster(opts)
m.server = proxy.server.ProxyServer(pconf)
m.addons.add(bp)
try:
m.run()
except KeyboardInterrupt:
m.shutdown()
if __name__ == '__main__':
start()
0x04 深入问题探讨
4.1 未解决问题
-
部分数据不明文:
- 可能存在其他数据输入来源
- 需要进一步查找其他加密点或隐藏参数
-
加密方式判断:
- 系统使用两种加密方式(普通加密和SM2)
- 需要动态判断当前使用的加密方式
-
签名问题:
- 如果存在签名验证机制,需要分析签名算法
- 可能需要保持签名与加密数据的同步
4.2 解决方案思路
-
全面搜索:
- 对所有网络请求进行监控
- 查找可能的隐藏接口或WebSocket连接
-
动态判断:
- 通过Hook技术动态检测
window.useSm2Encrypt值 - 根据当前值选择相应的加密/解密方式
- 通过Hook技术动态检测
-
签名分析:
- 使用逆向工程分析签名生成算法
- 可能需要保持原始加密流程只修改关键数据
0x05 技术要点总结
-
JS逆向技巧:
- 通过关键词搜索定位加密函数
- 使用断点调试跟踪数据流
- 解码混淆的十六进制字符串
-
加密分析:
- 识别加密算法(Base64、URL编码、SM2等)
- 分析加密流程和参数传递
-
渗透工具使用:
- Burp Suite的Match and replace功能
- mitmproxy自定义代理实现自动化处理
-
问题解决思路:
- 从简单到复杂的分析方法
- 多种技术组合应用
- 保持对异常情况的敏感度
0x06 防御建议
基于此次渗透经验,对防御方提出以下建议:
-
不要依赖前端加密:
- 前端加密容易被绕过
- 重要数据应在后端进行验证
-
使用更复杂的混淆:
- 更高级的JS混淆技术
- 动态加载关键代码
-
增加完整性校验:
- 对关键参数进行签名
- 使用时间戳防止重放攻击
-
监控异常请求:
- 检测明文传输的异常请求
- 实施请求频率限制
通过这次渗透实战,我们展示了如何分析前端加密机制并实现明文传输的方法,同时也为防御方提供了改进安全性的思路。