关于后端:从前端JS逆向到发现后端越权漏洞的渗透测试之旅

63次阅读

共计 5520 个字符,预计需要花费 14 分钟才能阅读完成。

前言

本篇文章首发先知社区,作者为本公众号。

前端剖析

首先搜寻申请接口,未发现要害加密点

依据申请参数进行搜寻

在 js 文件中找到 aes 加密 key、iv

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;
        k = [function(e) {return r[e] || e
        }
        ];
        e = function() {return '[0-9a-x]'
        }
        ;
        c = 1
    }
    ;while (c--)
        if (k)
            p = p.replace(new RegExp('\\b' + e(c) + '\\b','g'), k);
    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("xxxxx");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, {}))

持续跟进,发现了两个加密函数:RsaEncrypt、AesEncrypt

搜寻这两个函数没有太多信息。咱们持续浏览混同的 js 代码。

('(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("xxxxx");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, {}))

这里在进行 RSA 加密的时候,调用了 JSEncrypt() 类。

该 js 为 vue 的加密库。在该 js 文件中搜寻 Publickey、encode 字段,发现了几个函数。

然而咱们在相干函数下断点并不会通过。最初咱们在 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;}
        }
        ;
        JSEncrypt.version = "3.0.0-rc.1";
        return JSEncrypt;
    }());

在这里下断点

断点跟进,能够发现先进行了 RSA 加密,失去十六进制进行 base64 编码。

持续跟进断点,发现跳转到 VM 虚拟机中。

两个加密函数高深莫测

function RsaEncrypt(message) {var encrypt = new JSEncrypt();
    encrypt.setPublicKey(window.publicKey);
    var encrypted = encrypt.encryptLong(message);
    return encrypted.toString()}
function AesEncrypt(message) {var key = CryptoJS.enc.Utf8.parse("xxxxx");
    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()}

外围代码为

(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);

1、dynamicKey(); 动静生成 key。

2、将 keyScript 赋值为 aes、rsa 加密的后果。

opt.data.keyScript = AesEncrypt(RsaEncrypt(JSON.stringify(isEncrypt)))

3、dynamicKey 函数如下

function dynamicKey() {if (window.publicKey == undefined || window.publicKey == null || window.publicKey == '') {
        $.ajax({
            type: 'GET',
            async: false,
            url: Globals.ctx + "/xxx/xxxx",
            data: {nowDate: new Date()
            },
            success: function(data) {if (data.success) {window.publicKey = data.body}
            }
        })
    }
}

一开始的 key 是通过动静调试获取的

然而后续浸透发现,每次获取一个 key 都会发动一个申请包。

致此加密形式曾经剖析结束。

原始数据获取

因为是通过 RSA 加密,咱们无奈拿到私钥无奈进行解密。因为咱们不晓得申请发送的原始数据。只能依据公钥来加密数据,因而接下来就是获取原始数据。

之前咱们发现一个函数 JSEncrypt.prototype.encryptLong,承受一个字符串,而后对该字符串进行加密,最初跳转到了 VM 虚拟机中的代码。

所以咱们打印该 strings,查看是否是原始数据。

在 burp 中应用插件,将 var k=this.getKey(); 替换为 var k = this.getKey();console.log(string);。最终的成果是能够在控制台中打印出原始数据。

数据加密

接下来就是对数据进行加密,加密逻辑很简略: 获取公钥 -RSA 加密 -AES 加密。AES 晓得 iv、加密形式、key 比较简单。比拟难得就是 RSA 加密。

首先网站应用了 vue 的 JSEncrypt 库。咱们将整个 JSEncrypt 文件拷贝下来,应用工具对数据进行加密。

window = this;
navigator = this;
// 以上两行次要是避免代码报错
(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\"}"))

成果如图

自动化加密

在自动化加密的时候,须要调用 python 与 js。这里应用 execjs 库。最终代码如下

本文由 mdnice 多平台公布

正文完
 0