Uni-App逆向安全测试方案

不久前,同事给了一个APP样本让我一起分析,借此总结一下Uni-App逆向分析方法,并附上有趣的逆向案例。

Uni-APP

一般情况下,APP 数据加密可以分为 Java 层和 So 层。然而,对于 H5-APP 来说,它通常是在界面中嵌入一个 WebView 来显示网页。在这个网页内,可以使用 JavaScript 进行数据加密。数据交互可以通过 Java 与 JavaScript 的交互实现,也可以直接通过网页进行数据提交。

判断

可以通过 uiautomatorviewer 查看:

但一般情况下通过经验即可判断:

  • 第一种为:APP远程访问 URL 加载 页面信息,此种方式的特征是在抓包时会加载大量的 JScss文件。
  • 第二种为:APP在本地加载页面页面信息,此种方式的特征时在解压apk 安装包后,在 assets 目录下存在 HTMLJSCSS 文件

以上情况不是绝对的,有些 APP 为 移动混合应用程序,即有些地方是 H5 加载,部分地方为原生页面进行渲染。

通杀方案

Uni-APP 的通杀方案 远程调试WebView

环境部署

WebView版本

Chrome WebView 版本要大于 手机端WebView 版本 (chrome更新到最新版)

科学上网

chrome://inspect/#devices 打开空白,则需要下载离线包(开启科学上网),图中出现需要调试的设备。

开启调试

这时候在手机访问 H5 页面,在调试页面是会显示对应记录的:

点击 inspect 即可进入调试,相当于是完成了 APP逆向到JS逆向的转换

如果连 WebView 下的一些信息都没显示出来则代表你没有调试权限 setWebContentsDebuggingEnabled 这个的值必须为 true 才有调试权限,App发布一般都会把这个值设置为 false 禁止调试。

介绍两种方法绕过,原理都是通过设置 setWebContentsDebuggingEnabledtrue开启调试。、

第一种
frida hook 绕过,这里需要根据实际加载的 WebView 进行调整:

Java.perform(function () {
    //实例化一个对象
    var WebView = Java.use('android.webkit.WebView');
    //重写WebView类的重载方法,因为setWebContentsDebuggingEnabled不是静态方法,所以需要一个对象来调用这个方法
    WebView.$init.overload('android.content.Context').implementation = function (a) {
        console.log("WebView.$init is called!1");
        var retval = this.$init(a);
        this.setWebContentsDebuggingEnabled(true);
        return retval;
    }
    WebView.$init.overload('android.content.Context', 'android.util.AttributeSet').implementation = function (a, b) {
        console.log("WebView.$init is called!2");
        var retval = this.$init(a, b);
        this.setWebContentsDebuggingEnabled(true);
        return retval;
    }
    WebView.$init.overload('android.content.Context', 'android.util.AttributeSet', 'int').implementation = function (a, b, c) {
        console.log("WebView.$init is called!3");
        var retval = this.$init(a, b, c);
        this.setWebContentsDebuggingEnabled(true);
        return retval;
    }
    //始终设置为true,打开调试
    WebView.setWebContentsDebuggingEnabled.implementation = function () {
        this.setWebContentsDebuggingEnabled(true);
        console.log("setWebContentsDebuggingEnabled is called!");
    }

});

// frida -U -f package_name -l hook.js

第二种
使用 算法助手 等第三方模块进行绕过:

逆向分析

Uni-App 发送数据包,是可以通过 JS 和 Java 发包的,如果在调试页面的 NETWORK抓取不到请求即为 Java发包。

JS发包

这种是可以在 NETWORK 中抓取数据包,逆向流程和传统的 JS 逆向无异。

案例
以下两个漏洞都是在短时间内无法对 APP 的加解密做分析,遂转向对 H5 页面进行测试时候发现的漏洞,漏洞原理都比较简单,这里就不过多描述了。

JAVA发包

Network 无法抓到数据,这种只有静态分析了。也可以修改本地的 JS 或者远程加载的 JS 来协助我们分析。

案例

本次逆向的主要目的是想明文发,明文获取数据包

抓包发现请求数据和返回数据均为加密:

进入远程调试后发现,NETWORK 没有数据包:

请求包分析

遂开始逆向测试,脱壳后搜索请求包 RECDATA 参数,未发现次数据,解压缩包后在 assets 目录下搜索,发现存在静态 html 文件:

进入具体目录后全局搜索,在 app-service.js 发现我们要找的 RECDATA 参数:

本来想的是想去签后在加密前打印要加密的数据,但发现重新打包后无法正常启动,遂开始静态分析,这里的 METHOD 引起了我的注意:

大致的调用流程就清楚了,通过 METHOD 去找对应的加密前的参数,然后再进行加密:

当然这里也可以选择用工具去签后,打印加密前的值,但是我在试了这个方案后,APP启动异常。

现在只需要还原 sm2Encrypt 即可:

//t是明文数据 e是公钥 i是固定值0
 function sm2Encrypt(t, e, i) {
            i = 0 == i ? i : 1;
            var a = CryptoJS.enc.Utf8.parse(t);
            a = CryptoJS.enc.Base64.stringify(a), a = CryptoJS.enc.Utf8.parse(a);
            var s = e;
            s.length > 128 && (s = s.substr(s.length - 128));
            var r = s.substr(0, 64), n = s.substr(64), o = new SM2Cipher(i), c = o.CreatePoint(r, n);
            a = o.GetWords(a.toString());
            var u = o.Encrypt(c, a);
            return "04" + u
        }

这里有个坑点,找了很多 sm2 在线加密网站,也使用了各种语言的 sm2 加密模块,但是发现加密出来结果都是不是 04 开头的加密结果,直到发现了下面这个网站

RECDATA 替换,成功收到验证码:

这个想要的结果是通过 Burpsuite 明文发 ,明文接收,需要对 sm2Encrypt 加密函数进行模拟,这里使用nodejs来进行模拟:
本地创建 sm2new.js 文件,把网站上 sm2new.js 文件中的 sm2Encrypt() 加密函数复制进来:

function sm2Encrypt(t, e, i) {
    i = 0 == i ? i : 1;
    var a = CryptoJS.enc.Utf8.parse(t);
    a = CryptoJS.enc.Base64.stringify(a), a = CryptoJS.enc.Utf8.parse(a);
    var s = e;
    s.length > 128 && (s = s.substr(s.length - 128));
    var r = s.substr(0, 64), n = s.substr(64), o = new SM2Cipher(i), c = o.CreatePoint(r, n);
    a = o.GetWords(a.toString());
    var u = o.Encrypt(c, a);
    return "04" + u
}

console.log(sm2Encrypt('{"phone":"13122223333","type":"1"}', "042c15790d4d8650d1fcf72fdbb9419da06b1235872d420518225c3256ed2b2d5af63e9d65068fa926c4cd4e06ce62266156b1be5e077c7c136c87bb28c9d3fba9", "0"))

提示 CryptoJS is not defined. 在开头加入 var CryptoJS = require("crypto-js");,继续运行提示ReferenceError: SM2Cipher is not defined

app-service.js 中,复制 SM2Cipher 过去:

提示 TypeError: o.CreatePoint is not a function , CreatePoint 在 SM2Cipher.prototype中,复制过去:

运行提示 ReferenceError: KJUR is not defined:

需要安装 jsrsasign,安装该模块,并且在脚本的开头加入引入语句 var KJUR=require("jsrsasign");,运行后提示 unregistered EC curve name: sm2,在 app-service.js 中复制 KJUR.crypto.ECParameterDB.regist过去:

运行提示 ReferenceError: ECPointFp is not defined

在 导入的模块jsrsasign.js 中是有 ECPointFp 的:

我们将之前的这条语句 var KJUR=require("jsrsasign"); 改为 var jsrsasign=require("jsrsasign"); 。然后再次运行,对运行报错的函数,全局搜索。如果在导入的模块中含有该函数,则在其前面加上 jsrsasign:

运行提示,TypeError: Array.Copy is not a function,根据提示在 app-service.js 复制下来即可:

后面的 SM2CipherModeSM3DigestSM2Cipher引入方式和 SM2Cipher引入方式一致,缺什么就在 app-service.js 中找,最后的整理出来的代码如下:

var CryptoJS = require("crypto-js");
const jsrsasign = require("jsrsasign");

function sm2Encrypt(t, e, i) {
    i = 0 == i ? i : 1;
    var a = CryptoJS.enc.Utf8.parse(t);
    a = CryptoJS.enc.Base64.stringify(a), a = CryptoJS.enc.Utf8.parse(a);
    var s = e;
    s.length > 128 && (s = s.substr(s.length - 128));
    var r = s.substr(0, 64), n = s.substr(64), o = new SM2Cipher(i), c = o.CreatePoint(r, n);
    a = o.GetWords(a.toString());
    var u = o.Encrypt(c, a);
    return "04" + u
}

function SM2Cipher(t) {
    this.ct = 1, this.sm3c3 = this.sm3keybase = this.p2 = null, this.key = Array(32), this.keyOff = 0, this.cipherMode = "undefined" != typeof t ? t : SM2CipherMode.C1C3C2
}

SM2Cipher.prototype = {
    Reset: function () {
        this.sm3keybase = new SM3Digest, this.sm3c3 = new SM3Digest;
        for (var t = this.p2.getX().toBigInteger().toRadix(16); 64 > t.length;) t = "0" + t;
        t = this.GetWords(t);
        for (var e = this.p2.getY().toBigInteger().toRadix(16); 64 > e.length;) e = "0" + e;
        e = this.GetWords(e), this.sm3keybase.BlockUpdate(t, 0, t.length), this.sm3c3.BlockUpdate(t, 0, t.length), this.sm3keybase.BlockUpdate(e, 0, e.length), this.ct = 1, this.NextKey()
    }, NextKey: function () {
        var t = new SM3Digest(this.sm3keybase);
        t.Update(this.ct >> 24 & 255), t.Update(this.ct >> 16 & 255), t.Update(this.ct >> 8 & 255), t.Update(255 & this.ct), t.DoFinal(this.key, 0), this.keyOff = 0, this.ct++
    }, KDF: function (t) {
        var e = Array(t), i = new SM3Digest, a = Array(32), s = 1, r = t / 32;
        t %= 32;
        for (var n = this.p2.getX().toBigInteger().toRadix(16); 64 > n.length;) n = "0" + n;
        n = this.GetWords(n);
        for (var o = this.p2.getY().toBigInteger().toRadix(16); 64 > o.length;) o = "0" + o;
        o = this.GetWords(o);
        for (var c = 0, u = 0; u < r; u++) i.BlockUpdate(n, 0, n.length), i.BlockUpdate(o, 0, o.length), i.Update(s >> 24 & 255), i.Update(s >> 16 & 255), i.Update(s >> 8 & 255), i.Update(255 & s), i.DoFinal(e, c), c += 32, s++;
        for (0 != t && (i.BlockUpdate(n, 0, n.length), i.BlockUpdate(o, 0, o.length), i.Update(s >> 24 & 255), i.Update(s >> 16 & 255), i.Update(s >> 8 & 255), i.Update(255 & s), i.DoFinal(a, 0)), Array.Copy(a, 0, e, c, t), u = 0; u < e.length; u++) e[u] &= 255;
        return e
    }, InitEncipher: function (t) {
        var e = null, i = (e = new KJUR.crypto.ECDSA({curve: "sm2"}), e.generateKeyPairHex()),
            a = new BigInteger(i.ecprvhex, 16);
        e = jsrsasign.ECPointFp.decodeFromHex(e.ecparams.curve, i.ecpubhex);
        return this.p2 = t.multiply(a), this.Reset(), e
    }, EncryptBlock: function (t) {
        this.sm3c3.BlockUpdate(t, 0, t.length);
        for (var e = this.KDF(t.length), i = 0; i < t.length; i++) t[i] ^= e[i]
    }, InitDecipher: function (t, e) {
        this.p2 = e.multiply(t), this.p2.getX().toBigInteger().toRadix(16), this.p2.getY().toBigInteger().toRadix(16), this.Reset()
    }, DecryptBlock: function (t) {
        var e = this.KDF(t.length), i = 0;
        for (i = 0; i < e.length; i++) e[i].toString(16);
        for (i = 0; i < t.length; i++) t[i] ^= e[i];
        this.sm3c3.BlockUpdate(t, 0, t.length)
    }, Dofinal: function (t) {
        for (var e = this.p2.getY().toBigInteger().toRadix(16); 64 > e.length;) e = "0" + e;
        e = this.GetWords(e), this.sm3c3.BlockUpdate(e, 0, e.length), this.sm3c3.DoFinal(t, 0), this.Reset()
    }, Encrypt: function (t, e) {
        var i = Array(e.length);
        Array.Copy(e, 0, i, 0, e.length);
        var a = this.InitEncipher(t);
        this.EncryptBlock(i);
        var s = Array(32);
        this.Dofinal(s);
        var r = a.getX().toBigInteger().toRadix(16);
        for (a = a.getY().toBigInteger().toRadix(16); 64 > r.length;) r = "0" + r;
        for (; 64 > a.length;) a = "0" + a;
        return r += a, i = this.GetHex(i).toString(), 0 != i.length % 2 && (i = "0" + i), s = this.GetHex(s).toString(), a = r + i + s, this.cipherMode == SM2CipherMode.C1C3C2 && (a = r + s + i), a
    }, GetWords: function (t) {
        for (var e = [], i = t.length, a = 0; a < i; a += 2) e[e.length] = parseInt(t.substr(a, 2), 16);
        return e
    }, GetHex: function (t) {
        for (var e = [], i = 0, a = 0; a < 2 * t.length; a += 2) e[a >>> 3] |= parseInt(t[i]) << 24 - a % 8 * 4, i++;
        return new CryptoJS.lib.WordArray.init(e, t.length)
    }, Decrypt: function (t, e) {
        var i = e.substr(0, 64), a = e.substr(0 + i.length, 64),
            s = e.substr(i.length + a.length, e.length - i.length - a.length - 64),
            r = e.substr(e.length - 64);
        return this.cipherMode == SM2CipherMode.C1C3C2 && (r = e.substr(i.length + a.length, 64), s = e.substr(i.length + a.length + 64)), s = this.GetWords(s), i = this.CreatePoint(i, a), this.InitDecipher(t, i), this.DecryptBlock(s), i = Array(32), this.Dofinal(i), this.GetHex(i).toString() == r ? (r = this.GetHex(s), CryptoJS.enc.Utf8.stringify(r)) : ""
    }, CreatePoint: function (t, e) {
        var i = new jsrsasign.KJUR.crypto.ECDSA({curve: "sm2"});
        return jsrsasign.ECPointFp.decodeFromHex(i.ecparams.curve, "04" + t + e)
    }
};

function SM2Cipher(t) {
    this.ct = 1, this.sm3c3 = this.sm3keybase = this.p2 = null, this.key = Array(32), this.keyOff = 0, this.cipherMode = "undefined" != typeof t ? t : SM2CipherMode.C1C3C2
}

SM2Cipher.prototype = {
    Reset: function () {
        this.sm3keybase = new SM3Digest, this.sm3c3 = new SM3Digest;
        for (var t = this.p2.getX().toBigInteger().toRadix(16); 64 > t.length;) t = "0" + t;
        t = this.GetWords(t);
        for (var e = this.p2.getY().toBigInteger().toRadix(16); 64 > e.length;) e = "0" + e;
        e = this.GetWords(e), this.sm3keybase.BlockUpdate(t, 0, t.length), this.sm3c3.BlockUpdate(t, 0, t.length), this.sm3keybase.BlockUpdate(e, 0, e.length), this.ct = 1, this.NextKey()
    }, NextKey: function () {
        var t = new SM3Digest(this.sm3keybase);
        t.Update(this.ct >> 24 & 255), t.Update(this.ct >> 16 & 255), t.Update(this.ct >> 8 & 255), t.Update(255 & this.ct), t.DoFinal(this.key, 0), this.keyOff = 0, this.ct++
    }, KDF: function (t) {
        var e = Array(t), i = new SM3Digest, a = Array(32), s = 1, r = t / 32;
        t %= 32;
        for (var n = this.p2.getX().toBigInteger().toRadix(16); 64 > n.length;) n = "0" + n;
        n = this.GetWords(n);
        for (var o = this.p2.getY().toBigInteger().toRadix(16); 64 > o.length;) o = "0" + o;
        o = this.GetWords(o);
        for (var c = 0, u = 0; u < r; u++) i.BlockUpdate(n, 0, n.length), i.BlockUpdate(o, 0, o.length), i.Update(s >> 24 & 255), i.Update(s >> 16 & 255), i.Update(s >> 8 & 255), i.Update(255 & s), i.DoFinal(e, c), c += 32, s++;
        for (0 != t && (i.BlockUpdate(n, 0, n.length), i.BlockUpdate(o, 0, o.length), i.Update(s >> 24 & 255), i.Update(s >> 16 & 255), i.Update(s >> 8 & 255), i.Update(255 & s), i.DoFinal(a, 0)), Array.Copy(a, 0, e, c, t), u = 0; u < e.length; u++) e[u] &= 255;
        return e
    }, InitEncipher: function (t) {
        var e = null, i = (e = new jsrsasign.KJUR.crypto.ECDSA({curve: "sm2"}), e.generateKeyPairHex()),
            a = new jsrsasign.BigInteger(i.ecprvhex, 16);
        e = jsrsasign.ECPointFp.decodeFromHex(e.ecparams.curve, i.ecpubhex);
        return this.p2 = t.multiply(a), this.Reset(), e
    }, EncryptBlock: function (t) {
        this.sm3c3.BlockUpdate(t, 0, t.length);
        for (var e = this.KDF(t.length), i = 0; i < t.length; i++) t[i] ^= e[i]
    }, InitDecipher: function (t, e) {
        this.p2 = e.multiply(t), this.p2.getX().toBigInteger().toRadix(16), this.p2.getY().toBigInteger().toRadix(16), this.Reset()
    }, DecryptBlock: function (t) {
        var e = this.KDF(t.length), i = 0;
        for (i = 0; i < e.length; i++) e[i].toString(16);
        for (i = 0; i < t.length; i++) t[i] ^= e[i];
        this.sm3c3.BlockUpdate(t, 0, t.length)
    }, Dofinal: function (t) {
        for (var e = this.p2.getY().toBigInteger().toRadix(16); 64 > e.length;) e = "0" + e;
        e = this.GetWords(e), this.sm3c3.BlockUpdate(e, 0, e.length), this.sm3c3.DoFinal(t, 0), this.Reset()
    }, Encrypt: function (t, e) {
        var i = Array(e.length);
        Array.Copy(e, 0, i, 0, e.length);
        var a = this.InitEncipher(t);
        this.EncryptBlock(i);
        var s = Array(32);
        this.Dofinal(s);
        var r = a.getX().toBigInteger().toRadix(16);
        for (a = a.getY().toBigInteger().toRadix(16); 64 > r.length;) r = "0" + r;
        for (; 64 > a.length;) a = "0" + a;
        return r += a, i = this.GetHex(i).toString(), 0 != i.length % 2 && (i = "0" + i), s = this.GetHex(s).toString(), a = r + i + s, this.cipherMode == SM2CipherMode.C1C3C2 && (a = r + s + i), a
    }, GetWords: function (t) {
        for (var e = [], i = t.length, a = 0; a < i; a += 2) e[e.length] = parseInt(t.substr(a, 2), 16);
        return e
    }, GetHex: function (t) {
        for (var e = [], i = 0, a = 0; a < 2 * t.length; a += 2) e[a >>> 3] |= parseInt(t[i]) << 24 - a % 8 * 4, i++;
        return new CryptoJS.lib.WordArray.init(e, t.length)
    }, Decrypt: function (t, e) {
        var i = e.substr(0, 64), a = e.substr(0 + i.length, 64),
            s = e.substr(i.length + a.length, e.length - i.length - a.length - 64),
            r = e.substr(e.length - 64);
        return this.cipherMode == SM2CipherMode.C1C3C2 && (r = e.substr(i.length + a.length, 64), s = e.substr(i.length + a.length + 64)), s = this.GetWords(s), i = this.CreatePoint(i, a), this.InitDecipher(t, i), this.DecryptBlock(s), i = Array(32), this.Dofinal(i), this.GetHex(i).toString() == r ? (r = this.GetHex(s), CryptoJS.enc.Utf8.stringify(r)) : ""
    }, CreatePoint: function (t, e) {
        var i = new jsrsasign.KJUR.crypto.ECDSA({curve: "sm2"});
        return jsrsasign.ECPointFp.decodeFromHex(i.ecparams.curve, "04" + t + e)
    }
};

Array.Copy = function (t, e, i, a, s) {
    for (t = t.slice(e, e + s), e = 0; e < t.length; e++) i[a] = t[e], a++
};

function SM3Digest() {
    this.BYTE_LENGTH = 64,
        this.xBuf = [],
        this.byteCount = this.xBufOff = 0,
        this.DIGEST_LENGTH = 32,
        this.v0 = [1937774191, 1226093241, 388252375, 3666478592, 2842636476, 372324522, 3817729613, 2969243214],
        this.v0 = [1937774191, 1226093241, 388252375, -628488704, -1452330820, 372324522, -477237683, -1325724082],
        this.v = Array(8),
        this.v_ = Array(8),
        this.X0 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        this.X = Array(68),
        this.xOff = 0,
        this.T_00_15 = 2043430169,
        this.T_16_63 = 2055708042,
        0 < arguments.length ? this.InitDigest(arguments[0]) : this.Init()
}

SM3Digest.prototype = {
    Init: function () {
        this.xBuf = Array(4), this.Reset()
    }, InitDigest: function (t) {
        this.xBuf = Array(t.xBuf.length), Array.Copy(t.xBuf, 0, this.xBuf, 0, t.xBuf.length), this.xBufOff = t.xBufOff, this.byteCount = t.byteCount, Array.Copy(t.X, 0, this.X, 0, t.X.length), this.xOff = t.xOff, Array.Copy(t.v, 0, this.v, 0, t.v.length)
    }, GetDigestSize: function () {
        return this.DIGEST_LENGTH
    }, Reset: function () {
        this.xBufOff = this.byteCount = 0, Array.Clear(this.xBuf, 0, this.xBuf.length), Array.Copy(this.v0, 0, this.v, 0, this.v0.length), this.xOff = 0, Array.Copy(this.X0, 0, this.X, 0, this.X0.length)
    }, GetByteLength: function () {
        return this.BYTE_LENGTH
    }, ProcessBlock: function () {
        var t, e = this.X, i = Array(64);
        for (t = 16; 68 > t; t++) e[t] = this.P1(e[t - 16] ^ e[t - 9] ^ this.ROTATE(e[t - 3], 15)) ^ this.ROTATE(e[t - 13], 7) ^ e[t - 6];
        for (t = 0; 64 > t; t++) i[t] = e[t] ^ e[t + 4];
        var a, s, r = this.v, n = this.v_;
        for (Array.Copy(r, 0, n, 0, this.v0.length), t = 0; 16 > t; t++) s = this.ROTATE(n[0], 12), a = Int32.parse(Int32.parse(s + n[4]) + this.ROTATE(this.T_00_15, t)), a = this.ROTATE(a, 7), s ^= a, s = Int32.parse(Int32.parse(this.FF_00_15(n[0], n[1], n[2]) + n[3]) + s) + i[t], a = Int32.parse(Int32.parse(this.GG_00_15(n[4], n[5], n[6]) + n[7]) + a) + e[t], n[3] = n[2], n[2] = this.ROTATE(n[1], 9), n[1] = n[0], n[0] = s, n[7] = n[6], n[6] = this.ROTATE(n[5], 19), n[5] = n[4], n[4] = this.P0(a);
        for (t = 16; 64 > t; t++) s = this.ROTATE(n[0], 12), a = Int32.parse(Int32.parse(s + n[4]) + this.ROTATE(this.T_16_63, t)), a = this.ROTATE(a, 7), s ^= a, s = Int32.parse(Int32.parse(this.FF_16_63(n[0], n[1], n[2]) + n[3]) + s) + i[t], a = Int32.parse(Int32.parse(this.GG_16_63(n[4], n[5], n[6]) + n[7]) + a) + e[t], n[3] = n[2], n[2] = this.ROTATE(n[1], 9), n[1] = n[0], n[0] = s, n[7] = n[6], n[6] = this.ROTATE(n[5], 19), n[5] = n[4], n[4] = this.P0(a);
        for (t = 0; 8 > t; t++) r[t] ^= Int32.parse(n[t]);
        this.xOff = 0, Array.Copy(this.X0, 0, this.X, 0, this.X0.length)
    }, ProcessWord: function (t, e) {
        var i = t[e] << 24;
        i |= (255 & t[++e]) << 16, i |= (255 & t[++e]) << 8, i |= 255 & t[++e];
        this.X[this.xOff] = i, 16 == ++this.xOff && this.ProcessBlock()
    }, ProcessLength: function (t) {
        14 < this.xOff && this.ProcessBlock(), this.X[14] = this.URShiftLong(t, 32), this.X[15] = 4294967295 & t
    }, IntToBigEndian: function (t, e, i) {
        e[i] = Int32.parseByte(this.URShift(t, 24)), e[++i] = Int32.parseByte(this.URShift(t, 16)), e[++i] = Int32.parseByte(this.URShift(t, 8)), e[++i] = Int32.parseByte(t)
    }, DoFinal: function (t, e) {
        this.Finish();
        for (var i = 0; 8 > i; i++) this.IntToBigEndian(this.v[i], t, e + 4 * i);
        this.Reset();
        var a = t.length;
        for (i = 0; i < a; i++) t[i] &= 255;
        return this.DIGEST_LENGTH
    }, Update: function (t) {
        this.xBuf[this.xBufOff++] = t, this.xBufOff == this.xBuf.length && (this.ProcessWord(this.xBuf, 0), this.xBufOff = 0), this.byteCount++
    }, BlockUpdate: function (t, e, i) {
        for (; 0 != this.xBufOff && 0 < i;) this.Update(t[e]), e++, i--;
        for (; i > this.xBuf.length;) this.ProcessWord(t, e), e += this.xBuf.length, i -= this.xBuf.length, this.byteCount += this.xBuf.length;
        for (; 0 < i;) this.Update(t[e]), e++, i--
    }, Finish: function () {
        var t = this.byteCount << 3;
        for (this.Update(128); 0 != this.xBufOff;) this.Update(0);
        this.ProcessLength(t), this.ProcessBlock()
    }, ROTATE: function (t, e) {
        return t << e | this.URShift(t, 32 - e)
    }, P0: function (t) {
        return t ^ this.ROTATE(t, 9) ^ this.ROTATE(t, 17)
    }, P1: function (t) {
        return t ^ this.ROTATE(t, 15) ^ this.ROTATE(t, 23)
    }, FF_00_15: function (t, e, i) {
        return t ^ e ^ i
    }, FF_16_63: function (t, e, i) {
        return t & e | t & i | e & i
    }, GG_00_15: function (t, e, i) {
        return t ^ e ^ i
    }, GG_16_63: function (t, e, i) {
        return t & e | ~t & i
    }, URShift: function (t, e) {
        return (t > Int32.maxValue || t < Int32.minValue) && (t = Int32.parse(t)),
            0 <= t ? t >> e: (t >> e) + (2 << ~e)
    }, URShiftLong: function (t, e) {
        var i;
        if (i = new jsrsasign.BigInteger, i.fromInt(t), 0 <= i.signum()) i = i.shiftRight(e).intValue(); else {
            var a = new jsrsasign.BigInteger;
            a.fromInt(2);
            var s = ~e;
            if (i = "", 0 > s) {
                for (a = 64 + s, s = 0; s < a; s++) i += "0";
                a = new jsrsasign.BigInteger, a.fromInt(t >> e), i = new jsrsasign.BigInteger("10" + i, 2), i.toRadix(10), i = i.add(a).toRadix(10)
            } else i = a.shiftLeft(~e).intValue(), i = (t >> e) + i
        }
        return i
    }, GetZ: function (t, e) {
        var i = CryptoJS.enc.Utf8.parse("1234567812345678"), a = 32 * i.words.length;
        this.Update(a >> 8 & 255), this.Update(255 & a), i = this.GetWords(i.toString()), this.BlockUpdate(i, 0, i.length);
        i = this.GetWords(t.curve.a.toBigInteger().toRadix(16)), a = this.GetWords(t.curve.b.toBigInteger().toRadix(16));
        var s = this.GetWords(t.getX().toBigInteger().toRadix(16)),
            r = this.GetWords(t.getY().toBigInteger().toRadix(16)), n = this.GetWords(e.substr(0, 64)),
            o = this.GetWords(e.substr(64, 64));
        return this.BlockUpdate(i, 0, i.length), this.BlockUpdate(a, 0, a.length), this.BlockUpdate(s, 0, s.length), this.BlockUpdate(r, 0, r.length), this.BlockUpdate(n, 0, n.length), this.BlockUpdate(o, 0, o.length), i = Array(this.GetDigestSize()), this.DoFinal(i, 0), i
    }, GetWords: function (t) {
        for (var e = [], i = t.length, a = 0; a < i; a += 2) e[e.length] = parseInt(t.substr(a, 2), 16);
        return e
    }, GetHex: function (t) {
        for (var e = [], i = 0, a = 0; a < 2 * t.length; a += 2) e[a >>> 3] |= parseInt(t[i]) << 24 - a % 8 * 4, i++;
        return new CryptoJS.lib.WordArray.init(e, t.length)
    }
}, Array.Clear = function (t, e, i) {
    for (var a in t) t[a] = null
}, Array.Copy = function (t, e, i, a, s) {
    for (t = t.slice(e, e + s), e = 0; e < t.length; e++) i[a] = t[e], a++
};

var Int32 = {
    minValue: -parseInt("10000000000000000000000000000000", 2),
    maxValue: 2147483647,
    parse: function (t) {
        if (t < this.minValue) {
            t = new Number(-t), t = t.toString(2), t = t.substr(t.length - 31, 31);
            for (var e = "", i = 0; i < t.length; i++) {
                var a = t.substr(i, 1);
                e = e + ("0" == a ? "1" : "0")
            }
            return t = parseInt(e, 2), t + 1
        }
        if (t > this.maxValue) {
            for (t = Number(t), t = t.toString(2), t = t.substr(t.length - 31, 31), e = "", i = 0; i < t.length; i++) a = t.substr(i, 1), e += "0" == a ? "1" : "0";
            return t = parseInt(e, 2), -(t + 1)
        }
        return t
    },
    parseByte: function (t) {
        if (0 > t) {
            t = new Number(-t), t = t.toString(2), t = t.substr(t.length - 8, 8);
            for (var e = "", i = 0; i < t.length; i++) {
                var a = t.substr(i, 1);
                e = e + ("0" == a ? "1" : "0")
            }
            return parseInt(e, 2) + 1
        }
        return 255 < t ? (t = Number(t), t = t.toString(2), parseInt(t.substr(t.length - 8, 8), 2)) : t
    }
};
SM2CipherMode = {
    C1C2C3: 0,
    C1C3C2: 1
};
jsrsasign.KJUR.crypto.ECParameterDB.regist("sm2", 256, "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", "1", "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", ["sm2", "SM2"]);

console.log(sm2Encrypt('{"phone":"13122223333","type":"1"}', "042c15790d4d8650d1fcf72fdbb9419da06b1235872d420518225c3256ed2b2d5af63e9d65068fa926c4cd4e06ce62266156b1be5e077c7c136c87bb28c9xxxxxx", "0"))

返回数据包
这个比较简单,就是常规的 aesDecrypt 解密:

function aesDecrypt(t) {
            var e = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : "1234567890aaaaaa",
                i = CryptoJS.enc.Utf8.parse(e),
                a = CryptoJS.AES.decrypt(t, i, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7});
            return CryptoJS.enc.Utf8.stringify(a).toString()
        }

明文发明文收
这里用到了 BurpsuiteautoDecoder插件来完成,只用对请求和响应的 body 做处理,设置如下:

只需要在 python 中对请求进行加密,响应进行解密即可:

from flask import Flask, request
import base64
from urllib.parse import parse_qs, urlencode
import subprocess
import json

def call_js_function(function_name, arg):
    node_command = f'node -e "const {{ {function_name} }} = require(\'./demo.js\'); ' \
                   f'console.log(JSON.stringify({function_name}(\'{arg}\')));"'
    try:
        result = subprocess.run(node_command, shell=True, capture_output=True, text=True, encoding='utf-8')
        return json.loads(result.stdout.strip()) if result.returncode == 0 and result.stdout else None
    except Exception as e:
        print(f"Exception occurred: {e}")
        return None

app = Flask(__name__)

@app.route('/encode', methods=["POST"])
def encrypt():
    param = request.form.get('dataBody').rstrip('\r\n')
    if 'request' in request.form.get('requestorresponse'):
        parsed_data = parse_qs(param)
        recdata = base64.b64encode(parsed_data.get('RECDATA', [''])[0].encode('utf-8')).decode('utf-8')
        param = call_js_function('sm2EncryptNew', recdata)
        parsed_data['RECDATA'] = [param]
        return urlencode(parsed_data, doseq=True)
    return param

@app.route('/decode', methods=["POST"])
def decrypt():
    param = request.form.get('dataBody').rstrip('\r\n')
    if 'request' not in request.form.get('requestorresponse'):
        param = call_js_function('aesDecrypt', param)
    return param

if __name__ == '__main__':
    app.run(host="0.0.0.0", port=8888, debug=True)

最后成功达成目标:

后面就是一些常规渗透测试手法,就不做赘述了。

免责声明

免责声明:本博客的内容仅供合法、正当、健康的用途,切勿将其用于违反法律法规的行为。如因此导致任何法律责任或纠纷,本博客概不负责。谢谢您的理解与配合!

本文链接:

https://sanshiok.com/archive/31.html

# 最新文章