搞定那个加密的公众号:一次有点折腾的密钥固定实战
那天接到一个测试任务,目标看起来挺正经的微信公众号。一上来就给了我个下马威——Burp里抓到的请求,全是一堆加密的乱码。
文章作者:先知社区(adman )
文章来源:https://xz.aliyun.com/news/91362
1►
测试过程
流量长这熊样:

典型的“加密三件套”:加密的密钥、加密的数据、外加一个签名。心里大概有数了——八成是前端随机生成个AES密钥,用RSA公钥加密后传过去,数据用AES加密,最后再算个MD5签名。
绕道走:浏览器里看代码
微信里调试是不用想了,听说乱开调试可能封号。不过我发现这个公众号的H5页面,是不是可以直接在浏览器里打开?
从Burp历史记录里翻出地址:/******h5/index.html,往浏览器里一输,还真加载出来了。
F12一开,代码清清楚楚。找到关键处:
// AES 密钥生成function uid() { return "xxxxxxxxxxxxxxxxxxxxxxxxxxxx".replace(/x/g, function() { var r = 16 * Math.random() | 0; return r.toString(16); });};var securityInterceptor = function(chain) { var appSecret = "c13w5*****7421"; // 签名盐值 var key = uid(); // 生成随机AES密钥 // RSA加密AES密钥 var keyEncrypt = rsa.encrypt(key); // AES加密业务数据(ECB模式,PKCS7填充) var contentEncrypt = aes.encrypt(data, keyHex, { "mode": mode_ecb_default.a, "padding": pad_pkcs7_default.a }).toString(); // 生成签名 var sign = md5(contentEncrypt + appSecret).toString(); return { "encrypt": 2, "key": keyEncrypt, "content": contentEncrypt, "sign": sign };};
再看加密逻辑:
- uid() 生成随机的32位字符串(就是AES密钥)
- RSA加密这个密钥 → 变成key字段
- 用这个密钥使用AES算法加密业务数据 → 变成content字段
- md5(content + "盐值") → 变成sign字段
逻辑是清楚了,但问题来了:在浏览器里能分析代码,却登录不了——需要微信的openid。
异想天开:用CE改内存?
记得之前看过篇文章,有人用Cheat Engine改小程序的内存,达到密钥固定的效果。我想着能不能也这么干。
思路很直接:找到Math.random()那行,让它永远返回0,这样uid()每次都生成同一个字符串。
打开CE,挂上微信进程,搜索……然后就卡住了。JS代码在内存里怎么定位,我真不太会。搜了半天,不是找到一堆无关字符串,就是找到地址但改了不起作用。折腾一小时,无果。突然想到一句名言“世上无难事只要肯放弃”,放弃干饭去。
柳暗花明:Burp里改JS
吃完饭回来,突然冒出个想法:这JS文件是从测试对象服务器加载的吧?如果我能改服务器返回的JS……
马上打开Burp的过滤器设置,让其显示js文件。重新打开页面(需要重启微信),果然在历史记录里找到了那个关键的JS文件。
接下来就简单了——在Burp里配一个自动替换规则:

成了!
规则生效后,重新在微信里操作公众号。配置autoDecoder,用固定的密钥,成功解密content并显示。


回头看:为什么能成?
- 前端加密的命门:只要代码在用户端执行,就有被篡改的可能
- JS文件没保护:没签名没校验,中间人想改就改
给开发提个醒
如果你在开发类似功能:
- 别太相信前端加密:它防君子不防小人,只能增加攻击成本
- JS文件加个签名:用SRI(Subresource Integrity)至少能防篡改
- 关键逻辑放服务端:前端能做的基本都能被绕过
最后说两句
这次测试让我再次确认:安全是个系统工程。单个环节再强,链条有弱点还是白搭。
对测试人员来说,有时候就得“不按套路出牌”。直接调试不行,就找别的入口;内存修改不会,就试流量层面。条条大路通罗马,就看能不能找到那条能走通的路。
不过得强调一句:所有测试都得在合法授权下进行,别乱来。
文章来源:先知社区
华盟君