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中对密钥进行混淆,增加被逆向的难度。当然也可以直接使用非对称的加密算法,相对来说安全性更高。
原文始发于微信公众号(蚁景网络安全):记一次APP加密数据包的解密过程与思路
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论