从前端JS逆向到发现后端越权漏洞的渗透测试之旅
字数 1221 2025-08-23 18:31:18
从前端JS逆向到发现后端越权漏洞的渗透测试教程
1. 前言
本教程将详细讲解如何通过前端JS逆向分析,最终发现后端越权漏洞的完整过程。内容包括前端加密分析、JS逆向技巧、加密流程还原、自动化加密实现以及漏洞挖掘方法。
2. 前端加密分析
2.1 初始分析
- 首先搜索请求接口,寻找关键加密点
- 在JS文件中发现AES加密的key和iv
- 发现混淆的JS代码,包含加密相关函数
2.2 关键加密函数
发现两个主要加密函数:
RsaEncrypt:使用RSA加密AesEncrypt:使用AES加密
3. JS逆向过程
3.1 代码混淆分析
原始混淆代码示例:
eval(function(p,a,c,k,e,r){e=function(c){return c.toString(36)};if('0'.replace(0,e)==0){while(c--)r[e(c)]=k[c];k=[function(e){return r[e]||e}];e=function(){return'[0-9a-x]'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(3($){e();2 j=$.f;$.f=3(1){5(1.0!=\'\'&&1.0!=g){2 h=9 k();2 a=9 k();5(1.contentType=="application/json"){a=1.0}l{for(2 m in 1.0){2 4=m;5(1.0[4]!=g&&1.0[4].length>460){a[4]=1.0[4]}l{h[4]=1.0[4]}}}1.0=a;1.0.keyScript=n(o(JSON.stringify(h)))}2 p=$.extend(1,{beforeSend:3(jqXHR,settings){}});i j(p)}})(jQuery);3 e(){5(6.7==g||6.7==null||6.7==\'\'){$.f({type:\'GET\',async:false,url:Globals.ctx+"/e/generate",0:{nowDate:9 Date()},q:3(0){5(0.q){6.7=0.body}}})}}3 o(b){2 c=9 JSEncrypt();c.setPublicKey(6.7);2 d=c.encryptLong(b);i d.r()}3 n(b){2 s=8.t.u.v("x x x x x x");2 w=8.t.u.v(b);2 d=8.AES.c(w,s,{x:8.x.ECB,padding:8.pad.Pkcs7});i d.r()}',[],34,'data|opt|var|function|arrayName|if|window|publicKey|CryptoJS|new|noEncrypt|message|encrypt|encrypted|dynamicKey|ajax|undefined|isEncrypt|return|_ajax|Object|else|index|AesEncrypt|RsaEncrypt|_opt|success|toString|key|enc|Utf8|parse|srcs|mode'.split('|'),0,{}))
3.2 解密后的核心逻辑
(function($){
dynamicKey();
var _ajax = $.ajax;
$.ajax = function(opt){
if(opt.data != '' && opt.data != undefined){
var isEncrypt = new Object();
var noEncrypt = new Object();
if(opt.contentType == "application/json"){
noEncrypt = opt.data
}else{
for(var index in opt.data){
var arrayName = index;
if(opt.data[arrayName] != undefined && opt.data[arrayName].length > 460){
noEncrypt[arrayName] = opt.data[arrayName]
}else{
isEncrypt[arrayName] = opt.data[arrayName]
}
}
}
opt.data = noEncrypt;
opt.data.keyScript = AesEncrypt(RsaEncrypt(JSON.stringify(isEncrypt)))
}
var _opt = $.extend(opt,{beforeSend:function(jqXHR,settings){}});
return _ajax(_opt)
}
})(jQuery);
3.3 加密流程
- 调用
dynamicKey()动态生成RSA公钥 - 将需要加密的数据分为两部分:
- 长度>460的字段不加密
- 其他字段进行加密
- 加密流程:
- 先RSA加密:
RsaEncrypt(JSON.stringify(isEncrypt)) - 再AES加密:
AesEncrypt(RSA加密结果) - 最终赋值给
opt.data.keyScript
- 先RSA加密:
3.4 动态密钥获取
function dynamicKey(){
if(window.publicKey == undefined || window.publicKey == null || window.publicKey == ''){
$.ajax({
type:'GET',
async:false,
url:Globals.ctx+"/xxxx/xxxxx",
data:{nowDate:new Date()},
success:function(data){
if(data.success){
window.publicKey = data.body
}
}
})
}
}
3.5 RSA加密函数
function RsaEncrypt(message){
var encrypt = new JSEncrypt();
encrypt.setPublicKey(window.publicKey);
var encrypted = encrypt.encryptLong(message);
return encrypted.toString()
}
3.6 AES加密函数
function AesEncrypt(message){
var key = CryptoJS.enc.Utf8.parse("xxxxxxxxxxx");
var srcs = CryptoJS.enc.Utf8.parse(message);
var encrypted = CryptoJS.AES.encrypt(srcs,key,{mode:CryptoJS.mode.ECB,padding:CryptoJS.pad.Pkcs7});
return encrypted.toString()
}
4. 调试技巧
4.1 关键断点设置
在JSEncrypt.prototype.encryptLong函数中设置断点:
JSEncrypt.prototype.encryptLong = function(string){
var k = this.getKey();
try{
var ct = "";
var bytes = new Array();
bytes.push(0);
var byteNo = 0;
var len, c;
len = string.length;
var temp = 0;
for(var i=0;i<len;i++){
c = string.charCodeAt(i);
if(c>=0x010000 && c<=0x10FFFF){
byteNo +=4;
}else if(c>=0x000800 && c<=0x00FFFF){
byteNo +=3;
}else if(c>=0x000080 && c<=0x0007FF){
byteNo +=2;
}else{
byteNo +=1;
}
if((byteNo%117)>=114 || (byteNo%117)==0){
if(byteNo-temp>=114){
bytes.push(i);
temp = byteNo;
}
}
}
if(bytes.length>1){
for(var i=0;i<bytes.length-1;i++){
var str;
if(i==0){
str = string.substring(0,bytes[i+1]+1);
}else{
str = string.substring(bytes[i]+1,bytes[i+1]+1);
}
var t1 = k.encrypt(str);
ct += t1;
};
if(bytes[bytes.length-1]!=string.length-1){
var lastStr = string.substring(bytes[bytes.length-1]+1);
ct += k.encrypt(lastStr);
}
return hex2b64(ct);
}
var t = k.encrypt(string);
var y = hex2b64(t);
return y;
}catch(ex){
return false;
}
};
4.2 获取原始数据技巧
在Burp中使用插件,将var k=this.getKey();替换为:
var k = this.getKey();console.log(string);
这样可以在控制台中打印出原始数据。
5. 加密流程还原
5.1 完整加密流程
- 获取RSA公钥:通过
/xxxx/xxxxx接口 - 准备要加密的数据:
- 长度≤460的字段:需要加密
- 长度>460的字段:不加密
- 加密步骤:
- 将需要加密的数据JSON序列化
- 使用RSA公钥加密
- 使用AES加密RSA加密结果
- 将AES加密结果赋值给
keyScript字段
5.2 加密示例
原始数据:
{"id":"138652"}
加密过程:
- RSA加密:
RsaEncrypt('{"id":"138652"}') - AES加密:
AesEncrypt(RSA加密结果) - 最终请求体:
{
"keyScript": "AES加密后的字符串"
}
6. 自动化加密实现
6.1 Python与JS交互
使用execjs库调用JS加密函数:
import execjs
# 加载JS加密代码
with open('encrypt.js', 'r', encoding='utf-8') as f:
js_code = f.read()
# 创建JS环境
ctx = execjs.compile(js_code)
# 加密数据
data = {"id": "138652"}
encrypted = ctx.call('RsaEncrypt', json.dumps(data))
6.2 完整加密脚本
window = this;
navigator = this;
// 以上两行主要是防止代码报错
// JSEncrypt文件内容
// ... 完整JSEncrypt库代码 ...
// 加密函数直接使用网站自带的
function RsaEncrypt(message){
var encrypt = new JSEncrypt();
encrypt.setPublicKey("-----BEGIN PUBLIC KEY-----\nxxxxxx\n-----END PUBLIC KEY-----");
var encrypted = encrypt.encryptLong(message);
return encrypted.toString()
}
console.log(RsaEncrypt("{\"id\":\"138652\"}"))
7. 漏洞挖掘
7.1 测试方法
- 登录后台系统
- 通过前端获取加密前的原始数据
- 使用Python脚本模拟加密过程
- 构造并发送加密后的请求
- 测试是否存在越权漏洞
7.2 发现的两个越权漏洞
- 垂直越权:低权限用户通过构造特定请求访问高权限功能
- 水平越权:用户A通过修改ID参数访问用户B的数据
8. 总结
本教程详细介绍了从JS逆向到漏洞挖掘的完整流程,关键点包括:
- 前端加密逻辑分析
- 混淆JS代码的解密与理解
- 加密流程的完整还原
- 自动化加密的实现
- 基于加密理解的漏洞挖掘方法
通过这种方法,可以有效地分析前端加密逻辑,进而发现后端可能存在的安全漏洞。