从前端JS逆向到发现后端越权漏洞的渗透测试之旅
字数 1221 2025-08-23 18:31:18

从前端JS逆向到发现后端越权漏洞的渗透测试教程

1. 前言

本教程将详细讲解如何通过前端JS逆向分析,最终发现后端越权漏洞的完整过程。内容包括前端加密分析、JS逆向技巧、加密流程还原、自动化加密实现以及漏洞挖掘方法。

2. 前端加密分析

2.1 初始分析

  1. 首先搜索请求接口,寻找关键加密点
  2. 在JS文件中发现AES加密的key和iv
  3. 发现混淆的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 加密流程

  1. 调用dynamicKey()动态生成RSA公钥
  2. 将需要加密的数据分为两部分:
    • 长度>460的字段不加密
    • 其他字段进行加密
  3. 加密流程:
    • 先RSA加密:RsaEncrypt(JSON.stringify(isEncrypt))
    • 再AES加密:AesEncrypt(RSA加密结果)
    • 最终赋值给opt.data.keyScript

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 完整加密流程

  1. 获取RSA公钥:通过/xxxx/xxxxx接口
  2. 准备要加密的数据:
    • 长度≤460的字段:需要加密
    • 长度>460的字段:不加密
  3. 加密步骤:
    • 将需要加密的数据JSON序列化
    • 使用RSA公钥加密
    • 使用AES加密RSA加密结果
    • 将AES加密结果赋值给keyScript字段

5.2 加密示例

原始数据:

{"id":"138652"}

加密过程:

  1. RSA加密:RsaEncrypt('{"id":"138652"}')
  2. AES加密:AesEncrypt(RSA加密结果)
  3. 最终请求体:
{
    "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 测试方法

  1. 登录后台系统
  2. 通过前端获取加密前的原始数据
  3. 使用Python脚本模拟加密过程
  4. 构造并发送加密后的请求
  5. 测试是否存在越权漏洞

7.2 发现的两个越权漏洞

  1. 垂直越权:低权限用户通过构造特定请求访问高权限功能
  2. 水平越权:用户A通过修改ID参数访问用户B的数据

8. 总结

本教程详细介绍了从JS逆向到漏洞挖掘的完整流程,关键点包括:

  1. 前端加密逻辑分析
  2. 混淆JS代码的解密与理解
  3. 加密流程的完整还原
  4. 自动化加密的实现
  5. 基于加密理解的漏洞挖掘方法

通过这种方法,可以有效地分析前端加密逻辑,进而发现后端可能存在的安全漏洞。

从前端JS逆向到发现后端越权漏洞的渗透测试教程 1. 前言 本教程将详细讲解如何通过前端JS逆向分析,最终发现后端越权漏洞的完整过程。内容包括前端加密分析、JS逆向技巧、加密流程还原、自动化加密实现以及漏洞挖掘方法。 2. 前端加密分析 2.1 初始分析 首先搜索请求接口,寻找关键加密点 在JS文件中发现AES加密的key和iv 发现混淆的JS代码,包含加密相关函数 2.2 关键加密函数 发现两个主要加密函数: RsaEncrypt :使用RSA加密 AesEncrypt :使用AES加密 3. JS逆向过程 3.1 代码混淆分析 原始混淆代码示例: 3.2 解密后的核心逻辑 3.3 加密流程 调用 dynamicKey() 动态生成RSA公钥 将需要加密的数据分为两部分: 长度>460的字段不加密 其他字段进行加密 加密流程: 先RSA加密: RsaEncrypt(JSON.stringify(isEncrypt)) 再AES加密: AesEncrypt(RSA加密结果) 最终赋值给 opt.data.keyScript 3.4 动态密钥获取 3.5 RSA加密函数 3.6 AES加密函数 4. 调试技巧 4.1 关键断点设置 在 JSEncrypt.prototype.encryptLong 函数中设置断点: 4.2 获取原始数据技巧 在Burp中使用插件,将 var k=this.getKey(); 替换为: 这样可以在控制台中打印出原始数据。 5. 加密流程还原 5.1 完整加密流程 获取RSA公钥:通过 /xxxx/xxxxx 接口 准备要加密的数据: 长度≤460的字段:需要加密 长度>460的字段:不加密 加密步骤: 将需要加密的数据JSON序列化 使用RSA公钥加密 使用AES加密RSA加密结果 将AES加密结果赋值给 keyScript 字段 5.2 加密示例 原始数据: 加密过程: RSA加密: RsaEncrypt('{"id":"138652"}') AES加密: AesEncrypt(RSA加密结果) 最终请求体: 6. 自动化加密实现 6.1 Python与JS交互 使用 execjs 库调用JS加密函数: 6.2 完整加密脚本 7. 漏洞挖掘 7.1 测试方法 登录后台系统 通过前端获取加密前的原始数据 使用Python脚本模拟加密过程 构造并发送加密后的请求 测试是否存在越权漏洞 7.2 发现的两个越权漏洞 垂直越权 :低权限用户通过构造特定请求访问高权限功能 水平越权 :用户A通过修改ID参数访问用户B的数据 8. 总结 本教程详细介绍了从JS逆向到漏洞挖掘的完整流程,关键点包括: 前端加密逻辑分析 混淆JS代码的解密与理解 加密流程的完整还原 自动化加密的实现 基于加密理解的漏洞挖掘方法 通过这种方法,可以有效地分析前端加密逻辑,进而发现后端可能存在的安全漏洞。