【好靶场】JS加密逆向分析笔记
字数 1616 2025-11-13 12:06:50
JS加密逆向分析教学文档
0x00 前言
JS加密逆向分析是前端安全与逆向工程领域的核心技术之一,主要针对网页、移动端H5或Electron等基于JavaScript运行的应用中,用于保护数据传输、接口请求、核心逻辑的加密机制,通过技术手段还原加密流程、推导加密算法、破解加密参数的一系列分析过程。
在安全测试中,经常遇到接口存在越权漏洞但数据包被加密的情况,此时需要分析JS进行参数伪造和数据包构造。本文基于真实案例,详细讲解JS加密逆向分析的技术要点。
0x01 JS加密逆向分析反调试对抗
靶场地址
http://www.loveli.com.cn/see_bug_one?id=379
反调试技术分析
反调试手段:
- 禁用快捷键和右键菜单
- 持续触发debugger暂停
- 通过窗口尺寸变化检测DevTools状态
应对方法:
- 页面Ctrl+S保存源码到本地分析
- 浏览器调试器中选择"一律不在此处暂停"
JavaScript逆向分析流程
1. 数据隐藏机制
页面DOMContentLoaded时拼出隐藏节点#__k:
data-x:存储被"插隔符+反转+Base64"处理的公钥数据data-s:存储分隔符(默认::)
2. 加密请求构造
- 默认id=2,用公钥对字符串"2"加密
- 加密结果作为htccheck参数传给后端
3. 公钥还原函数d()的实现
// 还原流程:
// 1. 从data-x用data-s拼接
// 2. 反转字符串
// 3. Base64解码得到PEM文本
4. 加密算法核心
- 算法:RSA-OAEP(SHA-256)
- 优先使用WebCrypto API,回退使用node-forge
- 两条路径算法保持一致
加密算法逆向步骤
PEM公钥还原:
- 从data-x去掉分隔符
:: - 拼接为连续字符串
- 整串反转
- Base64解码得到标准PEM文本
加密流程:
- 对明文"1"执行RSA-OAEP(SHA-256)加密
- 对密文字节进行Base64编码
- 得到htccheck参数值
脚本实现
# htccheck.py 用法:
# 生成id=1:python htccheck.py
# 生成任意id:python htccheck.py --id 2
0x02 JS加密加盐逆向分析案例
代码结构分析
常量定义(main.min.js)
常量数组存储在b中,通过r(idx)取值:
b[0] = "-----BEGIN PUBLIC KEY-----"
b[1] = "-----END PUBLIC KEY-----"
b[2] = "spki"
b[3] = "RSA-OAEP"
b[4] = "SHA-256"
b[5] = "encrypt"
b[6] = "data-x"
b[7] = "data-s"
核心函数分析
1. c()函数 - 回退加载器
- 检测全局forge.pki是否存在
- 依次尝试3个CDN动态加载node-forge
- 成功resolve(true),全部失败reject
2. __resolvePem()函数 - PEM公钥还原
function __resolvePem() {
const element = document.getElementById('__k');
if (!element) return '';
const dataX = element.getAttribute('data-x');
const dataS = element.getAttribute('data-s') || '::';
// 还原流程:
// 1. 去掉分隔符拼接
// 2. 字符串反转
// 3. Base64解码
return atob(dataX.replace(new RegExp(dataS, 'g'), '').split('').reverse().join(''));
}
3. rsaEncryptWithPEM()函数 - 加密主函数
async function rsaEncryptWithPEM(g, h) {
// 明文构造逻辑
h = h || "2"; // 默认值
h = h + "10086"; // 加盐处理
// 加密实现...
}
详细加密流程
WebCrypto加密路径(优先)
步骤1:PEM预处理
k = g.trim()
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replace(/\s+/g, '');
步骤2:Base64转ArrayBuffer
l = Uint8Array.from(atob(k), ch => ch.charCodeAt(0));
const buffer = l.buffer;
步骤3:导入公钥
const publicKey = await crypto.subtle.importKey(
"spki",
buffer,
{
name: "RSA-OAEP",
hash: "SHA-256"
},
false,
["encrypt"]
);
步骤4:明文编码与加密
const encoder = new TextEncoder();
const plaintextBytes = encoder.encode(h);
const ciphertext = await crypto.subtle.encrypt(
{ name: "RSA-OAEP" },
publicKey,
plaintextBytes
);
// 转Base64输出
const uint8Array = new Uint8Array(ciphertext);
const htccheck = btoa(String.fromCharCode(...uint8Array));
node-forge回退路径
步骤1:动态加载检测
if (typeof forge === 'undefined') {
await c(); // 加载node-forge
}
步骤2:PEM解析与加密
const publicKey = forge.pki.publicKeyFromPem(g);
const plaintext = forge.util.encodeUtf8(h);
const cipherBytes = publicKey.encrypt(plaintext, 'RSA-OAEP', {
md: forge.md.sha256.create(),
mgf1: forge.mgf1.create(forge.md.sha256.create())
});
const htccheck = forge.util.encode64(cipherBytes);
完整调用流程
- 获取PEM公钥
const pem = __resolvePem();
- 生成htccheck
const htccheck = await rsaEncryptWithPEM(pem, prefix);
// 不传prefix:默认明文"210086"
// 传"9":明文"910086"
其他文件说明
beta.min.js:生成sessionStorage.k1..k3gamma.min.js:设置window.__zzeta.min.js:设置window.__q- 注:这些文件主要起混淆作用,与核心加密逻辑无关
0x03 关键技术要点总结
1. 反调试对抗技术
- 多维度检测调试环境
- 需要掌握相应的绕过技巧
2. 数据隐藏技术
- 分隔符插入
- 字符串反转
- Base64编码组合使用
3. 加密算法要点
- 算法:RSA-OAEP with SHA-256
- 双路径实现(WebCrypto + node-forge)
- 加盐处理:明文拼接固定字符串"10086"
4. 逆向分析步骤
- 识别加密参数(htccheck)
- 定位加密函数调用
- 分析公钥获取方式
- 还原加密算法流程
- 编写对应加密脚本
5. 脚本开发要点
- 准确还原PEM公钥获取流程
- 实现双加密路径兼容
- 支持参数化输入(不同id值)
通过掌握以上技术要点,可以系统性地分析和逆向JS加密实现,为安全测试和漏洞利用提供技术支撑。