某app的h5应用签名算法分析
背景
对某APP的移动打卡功能进行测试,分析其是否可以实现异地伪造打卡,其中一条测试思路为通过篡改打卡的数据包实现模拟打卡,但其负责人表示存在加密算法,无法攻破,本着试一试的心态,记录下了这篇文章。
前期因为我的测试机太旧,打开APP闪退,只能用安卓模拟器重新配置环境,期间配置中遇到各种乱七八糟的问题,再加上电脑卡顿,同时抓的包存在大量加密的,以及Burp不知道为啥证书有问题(能抓到一部分包),综上误认为是内嵌到APP的打卡,hook了半天都没反应,浪费了大量的时间,后面冷静下来仔细看了下,发现是app内嵌的h5...
过程
先走一遍正常打卡流程,全过程抓包,成功打卡,拿到打卡的数据包,看看用了什么签名。
POST /api/v1/attendance/auth/punch HTTP/1.1
Host: ■■■■■■■■■.cn
Connection: keep-alive
Content-Length: 145
Origin: https://■■■■■■■■■.cn
Content-MD5: 1a3c479c14a23dd7ef9718c6e1a1c1dd
Authorization: ■■■-2:attendanceAuth:dfb0d98fce7bc3309f33935626c4feb61f3bd999
Content-Type: application/json
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Linux; Android 7.1.2; SM-G930K Build/NRD90M.G955NKSU1AQDC; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/75.0.3770.143 Mobile Safari/537.36 android-woa/5.21.0 (inner001) device_id/c020d50c3e12da07774fc58700353e8f appIsPhone
X-DATE: Thu, 05 Jun 2025 09:02:21 GMT
Referer: https://■■■■■■■■■.cn/attendance/?from=■■■■■■■■
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: ■■■■_sid=V02S9a2ny7aWjatGsHH0s4Y-9jxH2js00a71f4b30065673476
X-Requested-With: ■■■.■■■■.■■■
{"location_name":"成都站(建设中)","lng":"104.07360577370704","lat":"30.697122464957793","attendance_type":1,"address_id":25596,"remark":""}
首先映入眼帘的是 Content-MD5
,这个很明显,拿着body直接去md5即可,发现一致。
echo -n '{"location_name":"成都站(建设中)","lng":"104.07360577370704","lat":"30.697122464957793","attendance_type":1,"address_id":25596,"remark":""}' | md5sum
# 输出
1a3c479c14a23dd7ef9718c6e1a1c1dd -
但只改md5,系统会提示InvalidSignature
,说明还有另一个签名验证。
通过分析,另一个签名值为 Authorization
,遂分析这个值是怎么计算来的。
因为是h5,所以签名算法一定是存在某个JS中的,通过搜索关键词■■■-2
成功定位到了加密JS代码,美化后如下。
可见Authorization
的计算规则如下:
sha1( secretKey + ContentMD5 + ContentType + Date)
其中除了secretKey
外,其他值我们都能预测/计算到。因此接下来的任务就是确认secretKey
是怎么来的。
简单看了下其他地方的secretKey
,可见是从AES解密而来,但密文内容是啥、密钥是啥、iv是啥,也不得而知:
如果是在浏览器中,直接下断点就好了,方便又省事,但这里是app中内嵌的h5,也没法通过inspect的方式调试,所以我在想,是否可以直接篡改JS让系统自动把secretKey
主动输出。
思路成立,实践开始,比较通用的输出方式就是alert,修改相关代码如下:
// 原代码
access_id:U,secret_key:H}=JSON.parse(I);mt(s,{accessId:U,secretKey:H})}catch(Ee)
// 修改后
access_id:U,secret_key:H}=JSON.parse(I);mt(s,{accessId:U,secretKey:H});alert(I)}catch(Ee)
可见成功获取到secretKey
但仅限于此还不够,我还要搞清楚secretKey
是从哪里解密来的。继续篡改JS代码:
// 原代码
De=ba.enc.Utf8.parse(Ie);alert(he);
// 修改后
De=ba.enc.Utf8.parse(Ie);alert(JSON.stringify(he));
成功拿到密文、key和iv
而密文来源为:
尝试在线解密,成功。
至此,需要的内容都已集齐,也知道secretKey
怎么来的,构造签名:
sha1( secretKey + ContentMD5 + ContentType + Date)
伪造签名测试:
小插曲
缓存问题
因为h5会默认缓存JS,所以会出现有时候抓不到JS的情况,可以清理掉缓存后再进行抓包。
emmmm
测试完成后,发现该h5在设置完UA,可以直接用浏览器打开调试,md。