记一次APP加密数据包的解密过程与思路
0x01 前言
针对一次app加密数据包的分析和解密,简单提供下在js中寻找加密方法的思路。
0x02 过程
前几天朋友给我发来个app让我看下
app中的数据包进行了加密,而且还有WAF 动不动就会封IP
这有啥搞得 没啥搞得
虽然返回包加密了,但是请求包没加密,想看看能不能修改请求包做一些其他的操作,但是发现他竟然对请求包的内容进行了验签,更改请求包中的任意值都会提示签名失败
好了,那这下没得搞了,app也找不到js文件,不知道使用了什么加密方式,对称还好些,非对称直接g
到这我基本已经放弃了
过了几天闲来无事,又想看看这个app,实在不行直接反编译app试试
反编译好像有点费劲,去hunter看看能不能搜到什么东西吧
突然就在hunter发现了一个pc.xxxxx.com的子域名,打开一看,哎,这不是网页端吗

但是这个网页端登录还需要开网页端的会员,我的账号登录不上去
网站还对F12做了限制,无法使用F12,如果打开F12去访问网页,网页直接关闭,有点意思,看来还是做了一些防护的
看看burp吧,突然发现一个标绿的js文件
把js文件复制下来,格式化一下,按照常规的思路,找一下有没有解密函数或者key什么的,这5w多行的代码找起来是有点费劲,直接搜索关键词吧
嘿 您猜我发现了什么
竟然把key和iv直接写在了js文件中
使用AES,CBC模式,Pkcs7进行加密
既然这样,那这个加密方式是不是和app一样的呢,本着代码能Ctrl+C绝对不重新写的原则,我猜是一样的
把app加密后的数据包丢进来解密一下,发现可以解密成功
这不就成了吗
返回包加密的难题解决了
那请求包签名校验应该怎么办,理论上来说js文件中应该有相应的签名校验的方法
请求包的内容如下:
phoneType=xxx&appVersion=xxx&apiVersion=xxx&phoneSystem=xxx&netType=xxx&channel=xxx&sign=xxx&deviceId=xxx&platform=xxx×tamp=xxx&token=xxx
虽然参数中有时间戳,但是同一个数据包可以重放多次,意味着没有对时间戳进行校验,也就是这个时间戳可以一直用
参数中的sgin值应该就是签名值,猜测是个MD5,但是这个值怎么来的呢,只能继续寻找js文件
继续发现了这样的一段js
好嘛,t是盐值,把请求的参数以xxx=xxx拼接,然后用&连接成字符串,然后连接成的字符串加上t这个盐值,进行一下MD5,最后就是签名值
但是这里一开始代码Object.keys(e).sort()获取对象e的所有键,并按字典顺序排序,这个排序方式是怎么排序的没弄明白,一开始的时候写了个脚本把参数值各种排序方式的md5给爆破了一遍,发现是通过开头字母顺序进行排序的,其实直接去查这个函数也可以,就是通过首字母进行排序
那么既然知道了签名生成的方式,可以直接写个脚本,生成对应的签名值,
import hashlib def generate_md5_signature(params, salt): sorted_params = sorted(params.items()) raw_string = "&".join(f"{k}={v}" for k, v in sorted_params) + salt # print(raw_string) return hashlib.md5(raw_string.encode()).hexdigest() def update_request(salt): # 参数列表 params = { "phoneType": "xxx", "appVersion": "xxx", "apiVersion": "xxx", "phoneSystem": "xxx", "netType": "xxx", "channel": "xxx", "deviceId": "xxx", "platform": "xxx", "timestamp": "xxx", "token": "xxx" } # 计算新的签名 new_sign = generate_md5_signature(params, salt) # 按照指定顺序输出 output_order = [ "phoneType", "appVersion", "apiVersion", "phoneSystem", "netType", "channel", "sign", "deviceId", "platform", "timestamp", "token" ] # 构建输出字符串 request_string = "&".join( f"{key}={new_sign if key == 'sign' else params[key]}" for key in output_order ) return request_string salt = "xxxxx" result = update_request(salt) print(result)
使用脚本生成的参数请求一下,至此,请求成功~
0x03 总结
这次可能是凑巧app用了对称加密,又凑巧有个网页端,还凑巧把密钥和加密方式写到js中才能够解密数据包,整个过程可能看起来很简单,但是也用了我几天的功夫,同时也给我们提供了一个思路。
站在攻击者的角度来说:在分析加密数据包的过程中,首先要判断加密方式,是对称还是非对称,分析js文件是关键,一般情况下加密的方式都会写在js文件中,如果是对称加密还有的搞,非对称加密基本上是找不到私钥的。
如果是app找不到js文件,可以寻找一下是否有网页端,大概率和app的加密方式相同。
这里可以使用burp的一些插件,这里我用的是UnExInfo,也有其他的很多插件能够检测返回包中的敏感数据进行高亮处理,这样能够帮助我们快速定位到相应的js文件,有些js文件会直接以encrypt命名,如果js代码没有进行格式化不好读,可以使用在线格式化的工具对代码进行格式化,针对可疑的js文件可以搜索关键词key、encrypt、decrypt等,以我的经验来看搜索decrypt解密函数更容易找到相应的密钥信息。
站在开发者的角度来说:加密的方式如果使用对称加密方式,要使用密钥交换算法进行密钥传输,或者在js中对密钥进行混淆,增加被逆向的难度。当然也可以直接使用非对称的加密算法,相对来说安全性更高。
文章来源:Tide安全团队
黑白之道发布、转载的文章中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途及盈利等目的,否则后果自行承担!
如侵权请私聊我们删文
END
华盟君