乱杀之你的密钥被我看见了 | 技术精选0126


本文约2600字,阅读约需7分钟。


最近在工作的过程中,遇到了几次数据加密的情况。之前都是看大佬们如何前端调试,然后逆向获取密钥解密流量,这一次遇到了,便好好实战一下,抄起我的“乱杀三式”。


1

逆向式

我们的目标是某微信小程序。

首先还是来看看这个小程序的请求流量。当正常人一眼望去,丝毫理不清传递的是什么,我们基本就可以断定,其对流量进行了加密处理。


然后我们再看看返回请求——依然一头雾水:


经过上面两张图,可以判断这个小程序存在流量加密的情况。至于是全局的流量加密,还是部分流量加密,这个需要经过分析之后才能判断。

如果只是部分流量加密,那么还可以测一测有没有漏网之鱼,如果是全局流量加密的话,就必须对小程序进行逆向获取源代码,再进行分析了。

对微信小程序的逆向采用某神仙模拟器加一个6.X版的微信。因为高版本的安卓和微信不再信任系统的证书,这会造成无法抓包的情况,所以使用安卓5加6.X版本的微信。

再参考网上的文章,从模拟器中脱到了小程序的wxapkg文件。这个文件在我的环境中位于:

/data/data/com.tencent.mm/MicroMsg/7e3e7c**********706/appbrand/pkg


因为小程序比较多,不知道哪一个才是目标文件。通过adb把这几个wxapkg文件全部下载下来,然后就是从wxapkg文件中提取源代码,这里使用wxappUnpacker一键提取:


获取到的目标小程序的源代码文件结构如下,之后便是进入分析阶段了。



2

分析式

进入代码审计环节,来分析前端的代码算法。

上面的小程序经过逆向,获取到的源代码结构如下:


因为之前没有分析过微信小程序,所以这里首先学习了一下小程序的一些基本知识。重点有两个,第一个是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字段。

3

解密式

整个小程序的分析到这里基本就结束了,接下来只需要根据上面的密钥、iv向量生成、算法等等写一个解密的脚本就可以了。

为了高(tou)效(lan)作(mo)业(yu),使用之前搞开发写的PHP版AES解密脚本改了改,最后的效果如下:

输入uuid参数和密文,点击解密之后返回明文,将明文修改之后点击加密,返回密文。


返回包的解密:


好了,这个小程序的流量加密分析到这就全部结束了。

收工,干饭!!!


- END -


往期推荐

记一次卑微的渗透测试

pwn入门之栈入门

MYSQL另类利用方式

长按下方图片即可关注

点击下方阅读原文,加入社群,读者作者无障碍交流
免责声明:文章内容不代表本站立场,本站不对其内容的真实性、完整性、准确性给予任何担保、暗示和承诺,仅供读者参考,文章版权归原作者所有。如本文内容影响到您的合法权益(内容、图片等),请及时联系本站,我们会及时删除处理。查看原文

为您推荐