某支付APP-JS加密算法分析

关注微信公众号获取更多资讯,第一时间学习最新前沿渗透技术!
在这里插入图片描述

0x01 前言

还是像往常一样对某个APP进行安全测试,由于是支付软件根据安全合规要求APP发送的请求大多是加密的,这点在测试之前已经想好解决方案。根据以前遇到的加密方式,大多都是AES、DES、RSA、SM3、SM4这种单调的加密,秘钥存储在APP客户端或者存储在JS中,只需要通过APP逆向或者JS断点拿到KEY或者公钥,就可以还原加密算法。但是这次遇到的这个支付APP跟往常加密算法存在着很大不同,下文将逐步分析它的加密流程。

0x02 加密分析

打开APP某个功能点,触发HTTP请求,数据包如下,简单的分析一下该请求的结构

从请求头来看,请求头中带有Referer,说明该请求虽然是从APP发出的,但实际还是由APP嵌入的网页发起的请求,利用这点可以通过搜索请求URL找到相关的WEB页面,相关加密算法肯定存在此WEB页面中。这样就不用逆向APP少走弯路。

从请求数据来看,openId是我的手机号;aesKeySign可能是某种签名、加密算法可能与AES有关;encData可能是请求数据;encType是加密类型。
image.png

分析完数据包,就该根据分析结果进行相关操作,先看看该请求的URL是由哪个WEB页面发起的
全局从响应包中搜索personProductInfo

发现相关JS文件
image.png

查看JS文件的Referer即可获取发起请求的网页
image.png

打开该网页
image.png

在该网页中全局搜索encType、aesKeySign相关关键字定位加密算法具体位置

最终在0.d4f14.js文件中发现实现加密的函数
具体代码位置在6755至6815行之间
image.png

逐行分析关键代码

  1. 在6761行调用/gapi/mapi-gateway/auth/getPublicKey接口获取服务端RSA公钥

image.png

相关请求如下
image.png

  1. 在6722行调用clientRsaKeyUpload函数,客户端生成一对RSA公钥与秘钥

image.png

  1. 在6774行至6780行中,创建了几个变量,大致含义如下
var s = {openId: <手机号>, clientRsaPublicKey: <在第二步生成的客户端公钥>}
var a = new JSencrypt; // 创建一个JSencrypt,也就是RSA对象
Ie.serverRsaPublickey = r.result; // 将第一步获取到的服务端公钥,保存到Ie.serverRsaPublickey中
a.setPublickey(r.result); // 对刚刚创建的RSA对象(a)设置公钥,公钥为r.result(也就是服务端公钥)

image.png

  1. 在6781行至6794行中,调用/gapi/mapi-gateway/auth/getAesKey接口获取AesKey

POST的数据为{"encData": a.encrypt(s)}用第三步的a(RSA-服务端公钥)加密了s(openId-手机号、clientRsaPublicKey-客户端公钥)

思考:这里为什么会用服务端公钥来加密手机号以及客户端公钥,发送给服务器
解释:因为将手机号客户端公钥服务端公钥加密后发送给服务端,服务端可以用服务端私钥解密获取客户端公钥手机号,利用用户手机号作为索引存储用户客户端公钥与随机生成的AES KEY,同时将AES KEY客户端公钥加密返回给客户端,这样服务端客户端在互不知道对方RSA私钥的情况下,就完成了数据加密传输,并且每次请求客户端RSA公钥与私钥是不同的,服务端返回的AES KEY也是随机生成的,如果中间存在流量劫持设备,想解密流量获取AES KEY几乎是不可能的
image.png

相关请求如下

明文数据:

encData => {"openId":"<手机号>","clientRsaPublicKey":"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCX/IR8dij6y3JKKTubmhRx19CBEQ7/ijuslmcl5CCWm7OcmQ1sH0HiLQUTpdrX5YCDEpx10P9CEETG3Pxru02GZ+lXmI139XJltmH4zsnipKxiRK9z7QSgMI35APd+l20SG9V6WN2mlr4lhAC2EnknV/SHbkX2R8FIpY+Ka+FKQwIDAQAB"}

result => {"aesKey":"05399b76a2984561bf843d0ef62cabc9","clientRsaPublicKeySign":"nUMY8W897L+Wyi4oAloqVAib2TIG1151A5BtiDiPJ6wNw/9WPov0UGVQ7wTDOVQ6WlCWWwsiOLPJu+q3PiAz7Dg4TJTKrd112ktoZ9ytt1SZO1FeRou6HrISzCywFpcp160Yo8LJnWh0j+HMWPraXoc8Tz8SCm9qpfYeJz+plv1hstKwCbAyF/Z9zDz+bsPa72ZaXnqvkjciJs71wge7yqKGcJHna3NXwl2NUi8gRVpBLb2wquJoqXcYUguW2O4G1bDJYW7ysUZjuvMqd+wYloMRa8uoX2BjkmaVWei6nZ9KREf0vTCj17EoV2x7mB7onLQQp/5AzepEhbnYNL4lvBKg33T9eB/Zfp6ilQUdIles9HSmx8ooT8vdd1J2mDOebGKe0RVMX3OlR0NB8F4hvOlcPOlhcjCXVOh1yfG+czqFSkcX5r1RsUl086BxlLtvUYT1m3RnvXqGSata3rWi8cIWWpNXlgeLPQvBLLwfID0ro4S98GuBxQ5kDr5utaZyMz5/PPnn2QhggM44/BYMFVqBrXg3SRLyF+tvrvsnOyDBvUbxwZR7KP+VoDLt+1yhgdUyz9BkHClzqVhoEkFRQtr7LQOZ0N16AUhL8aAPQv/QbOrd/bcn7AqLCRDTSA0QLHl2vp11qpBvXrRIv/Tozg6CE0DttxUl9hu1KvDqoCo="}

image.png

  1. 在6797行至6803行中,客户端用客户端私钥Ie.clientKey.clientRsaPrivateKey解密了第四步中的响应数据,获取到clientRsaPublicKeySign,使用服务端公钥验证了客户端公钥Ie.clientKey.clientRsaPublicKey没有被篡改。获取到aesKey保存到Ie.aesKey

image.png

  1. 在6804至6816行中
var a = GibberishAES.aesEncrypt(e, Ie.aesKey); //使用Ie.aesKey对用户数据(e)进行加密

o.setPrivateKey(Ie.clientKey.clientRsaPrivateKey); // o(RSA)设置客户端私钥

var c = o.sign(Ie.aesKey, CryptoJS.SHA512, "sha512"); //o(RSA)对Ie.aesKey进行SHA512进行签名

i({result: {openId: t,aesKeySign: c,encData: a,encyType: "C006"},success: !0}) // 发送请求

使用获取到的 Ie.aesKey 对用户数据进行加密
image.png

  1. 服务端收到请求后,利用数据包中的openId(手机号)获取到存储的客户端公钥验证aesKey没有被篡改,再获取之前生成的aesKey解密encData,服务端使用aesKey加密数据返回给客户端,客户端使用aesKey解密,至此加密流程结束

image.png

0x03 总结

  1. 客户端生成RSA公钥以及秘钥
  2. 客户端发送请求获取服务端的公钥
  3. 客户端使用服务端的公钥加密客户端的公钥并发送给服务器
  4. 服务器使用服务器端的私钥解密请求获取客户端公钥,服务端根据请求包中的OpenId生成一个新的AES KEY,并使用客户端公钥加密AES KEY,服务端使用服务端私钥对客户端公钥进行SHA512签名,将以上数据使用客户端公钥加密后返回给客户端
  5. 客户端使用客户端私钥解密数据,获取AES KEY与签名数据,客户端使用之前获取的服务端公钥对签名进行验证,如果验证成功则继续进行加密流程
  6. 客户端使用该KEY加密用户数据,再使用客户端私钥对AES KEY进行签名验证,将结果发送给服务器
  7. 服务器使用AES KEY解密用户数据,并对发送过来的AES KEY的签名进行验证

加密流程分析完成后,还需要将网页中的JS代码扣下来完善成加密脚本以便之后修改参数对APP进行安全测试。然后仔细观察一下加密流程,还是有一处BUG的,用于存储用户aesKey客户端公钥的索引是手机号。当用户的手机号泄露后,攻击者可以根据手机号构造请求无限刷新服务端存储的aesKey客户端公钥,由于这两个变量需要用户客户端服务端保持一致才能正常进行解密流程,当攻击者出现打乱他们之间的同步,就会造成客户端与服务端无法正常解密用户数据的现象。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值