遇到过很多次数据加密的情况。之前都是看到别人如何前端调试,然后逆向获取密钥解密流量,这一次,给大家实战一下。
我们的目标是某信小程序。
首先还是来看看这个小程序的请求流量。当正常人一眼望去,丝毫理不清传递的是什么,我们基本就可以断定,其对流量进行了加密处理。
然后我们再看看返回请求——依然一头雾水:
经过上面两张图,可以判断这个小程序存在流量加密的情况。至于是全局的流量加密,还是部分流量加密,这个需要经过分析之后才能判断。
如果只是部分流量加密,那么还可以测一测有没有漏网之鱼,如果是全局流量加密的话,就必须对小程序进行逆向获取源代码,再进行分析了。
对某信小程序的逆向采用某神仙模拟器加一个6.X版的某信。因为高版本的安卓和某信不再信任系统的证书,这会造成无法抓包的情况,所以使用安卓5加6.X版本的某信。
再参考网上的文章,从模拟器中脱到了小程序的wxapkg文件。这个文件在我的环境中位于:
/data/data/com.tencent.mm/MicroMsg/7e3e7c**********706/appbrand/pkg
因为小程序比较多,不知道哪一个才是目标文件。通过adb把这几个wxapkg文件全部下载下来,然后就是从wxapkg文件中提取源代码,这里使用wxappUnpacker一键提取:
获取到的目标小程序的源代码文件结构如下,之后便是进入分析阶段了。
【点击查看】
1、网络安全学习路线
2、电子书籍(白帽子)
3、安全大厂内部视频
4、100份src文档
5、常见安全面试题
6、ctf大赛经典题目解析
7、全套工具包
8、应急响应笔记
进入代码审计环节,来分析前端的代码算法。
上面的小程序经过逆向,获取到的源代码结构如下:
因为之前没有分析过某信小程序,所以这里首先学习了一下小程序的一些基本知识。重点有两个,第一个是app.js,这个文件是整个项目的入口文件,通过App()方法来注册一个小程序,并确定了整个程序的生命周期。
第二个便是module.exports,某信小程序的模块化,个人理解就是对外暴露的方法,可以供其他方法调用这个模块,类似于public方法。
了解完上面这两个点,再回到项目的源代码本身,通过浏览整个文件结构,发现对于加密的方法定义都位于/utils文件下。
其中需要关注的是encrypt.js文件,里面定义了关于流量的加解密方法。直接上代码。
从上面的代码中看到encrypt.js通过module.exports对外暴露了encrypt和decrypt两个方法。这两个应该就是用于对流量进行加密和解密的方法了。先来分析encrypt函数:
Encrypt函数传递两个参数e和n,然后将参数n通过r函数处理之后赋值给s,其中r函数内容如下。为了安全里面的字符串,截取长度均已经修改,与原程序不同:
r函数将传递的参数截取一段然后与字符串进行拼接,然后使用SHA256函数签名,再截取中间一部分,将字母转换成大写之后返回。
再回到encrypt函数,变量i存储的AES加密解密的密钥,此处使用硬编码的方式写在代码当中。然后变量u存储格式化之后的s变量,变量c存储格式化之后的参数e。
之后就是加密部分了。
先了解一下AES的加密。对于AES有不同的模式,本程序中使用了CBC的模式,这种模式是先将明文切分成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密。
在使用CBC模式进行加解密过程中,需要定义一个初始向量。初始向量用于随机数据加密,达到的效果是对相同的明文,传递相同的密钥。不同的初始向量可以产生不同的密文。
再回到代码本身。可以看到encrypt函数调用了AES模块的encrypt方法进行加密,传递的参数,依次为c明文,i密钥,然后变量u表示初始向量,采用CBC模式进行加密。
Encrypt函数分析完了,再看decrypt函数,上代码:
Decrypt函数还是传递两个参数e,s然后通过formatString函数将参数e处理一下,其中formString函数具体内容如下,对e参数进行去除换行空格的处理:
变量i还是存储r函数处理后的参数s,变量u存储的还是格式化的密钥,变量c存储格式化后的变量i,最后调用AES模块的decrypt函数进行解密,结果存储在变量o当中。
进行解密的参数传递,e密文,u密钥,c初始化向量,采用CBC模式解密。这里加密和解密的初始化向量都是由r函数进行处理的。
在上面分析中,加密解密传递的都是两个参数,然后其中一个参数是用于生成初始向量的。因为在上面的过程中,这个参数不是固定的,所以需要分析这个参数的来源,这样才能完整的对整个数据进行解密。
再回到项目本身,在app.js中引入了一个request.js文件,在这个文件当中封装了http请求的方法,并对外暴露接口:
在第108行中,有一段代码:
r=(0,n.encrypt)(JSON.stringify(r),d)
这段代码应该就是调用加密函数对请求数据进行了加密。这里传递了两个参数,其中d参数就是加密过程中用于生成iv向量的参数,而在第103行最后可以看到,d参数的来源是函数getRandomUuid。接下来看看这个函数的内容:
这个函数的作用只是生成一个随机的字符串然后返回。但是在上一张图的第105行代码将这个d赋值给了i.uuid,在我们最开始看请求数据包的时候,在http请求的请求体中就存在一个uuid的字段:
所以可以确认,这个uuid的字段就是用于生成iv向量的参数。
然后继续跟进我们的代码,在进行流量加密之后,会调用一个c函数,然后再return。所以我们需要看一下这个c函数的具体内容,因为解密的过程应该在这个c函数当中:
在第61行的位置发送一个http请求,如果请求成功进入回调函数,对数据进行解密,这个解密可以看到传递的参数c.uuid就是请求包中的uuid字段。
整个小程序的分析到这里基本就结束了,接下来只需要根据上面的密钥、iv向量生成、算法等等写一个解密的脚本就可以了。
为了高(tou)效(lan)作(mo)业(yu),使用之前搞开发写的PHP版AES解密脚本改了改,最后的效果如下:
输入uuid参数和密文,点击解密之后返回明文,将明文修改之后点击加密,返回密文。
返回包的解密:
好了,这个小程序的流量加密分析到这就全部结束了。