在这个web系统里,登陆接口的密码进行了加密后传递给后端,换取token。
而在接口自动化测试中,如果没能知道加密算法计算出加密后的password,则后续很难展开。
本文对登陆接口:https:/api/v1/user/ocean/phone/password/login-auth做一个密码逆向分析

测试账户/密码:huangjinlong@zhizh.com/1122

登录加密.png

浏览器打开该系统的登录页:https://**/portal/login
我们知道刚才提交的数据中密码字段为password,我们现在找到请求发送的代码位置以便后面做分析
并按F12来到Sources面板,ctrl+shift+F在所有文件中搜索password

搜索代码.png

在含有关键字password文件中,可能在index.js或login.js中。在index.js文件搜索password并没有发现提交数据相关的,而在login.js中,很快就发现了这段代码:

g({
  fromSystem: 8,
  loginId: Z.email,
  loginType: 2,
  password: M(Z.pass),
  reqType: "登录"
})

这个结构跟login接口发送的数据完全一致,能确定就是这里。
密码加密调用了M(Z.pass),再搜索M函数,看到新建了一个对象k,M调用了k的方法setPublicKey()l.encrypt(e.trim())返回作为password。
其实这里已经能够看出RSA加密了,因为使用了公钥对消息进行加密

加密处.png

我们在return l.setPublicKey(F),l.encrypt(e.trim())处打断点

打断点.png

接下来在页面使用正确的账户密码,点击Sign In

登录页.png

断点起作用,停留在return l.setPublicKey(F)并给出了l函数的参数,这里的秘钥长度为1024,使用的指数为010001,这个信息看不出来也没关系,主要是验证前面的猜想,这两个参数都符合RSA加密的规则

加密.png

按F11进入下一个回调函数,来到了element-plus.js,这里的key不一定是公钥,因为还没执行完

进入调用方法1.png

一路next,直到函数回到login.js,在l.encrypt(e.trim())停下,这意味着前面的l.setPublicKey(F),已执行完毕,这个时候在右侧Scope面板展开Module,看到F的值,这个就是公钥。

找到公钥.png

把公钥复制出来

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA88FgfNpx6fAdD34QWdlgo12G1w8b36nenjVDUOEoaYaE8d6r3ks0vi9z30uN7PMY824VX**HjHaVCcZ7CCTle0zMTAAttTF0eEUXEvamRKI8cV0NZaGQSIshWeGrOe/0iPVF4kps5s6eru8JGS27JjsCQ9kqKDnFLS6NsWX1eJDJSs5lOVbK5DxZrCuSwEDMZMXiyfL09dHoTODXTINIBBrxdDZGoyYDnZR0EasTO/hiDaKf0UbsdxJC975QA/y9k1y/nTksRXS5cTAOGqO6KffxFnwIDAQAB

现在有了公钥,我们就能python,使用公钥对密码进行加密
这里加密用的是pycryptodome

from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5
from Crypto.PublicKey import RSA
import base64

def js_encrypt(text):
    # 通过拿到js中的RSA公钥,构造完整的公钥部分
    key = """-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA88FgfNpx6fAdD34QWdlgo12G1w8b36nenjVDUOEoaYaE8d6r3ks0vi9z30uN7PMY824VX****HjHaVCcZ7CCTle0zMTAAttTF0eEUXEvamRKI8cV0NZaGQSIshWeGrOe/0iPVF4kps5s6eru8JGS27JjsCQ9kqKDnFLS6NsWX1eJDJSs5lOVbK5DxZrCuSwEDMZMXiyfL09dHoTODXTINIBBrxdDZGoyYDnZR0EasTO/hiDaKf0UbsdxJC975QA/y9k1y/nTksRXS5cTAOGqO6KffxFnwIDAQAB
    -----END PUBLIC KEY-----"""

    rsakey = RSA.importKey(key)
    cipher = Cipher_pkcs1_v1_5.new(rsakey) 
    cipher_text = base64.b64encode(cipher.encrypt(text.encode(encoding="utf-8")))
    # print(cipher_text)
    value = cipher_text.decode('utf8')
    return value

# 传入密码:1122
print("加密后的密码为:"+js_encrypt("1122"))

拿到加密后的密文

加密后的密文.png

拿到postman里验证一下

postman测试.png

换取到了token,至此,逆向分析完毕


遇到的一些坑

一开始我是没想着加密算法的,因为对密码加密的整个过程,断点停在element-plus.js里,为此把element-plus.js后掐掉不需要的内容,如图的顶部引用、底部和加密无关的内容

删除多余代码1.png

删除多余代码2.png

然后加上自己的代码

// 用秘钥加密密码
function getPwd(pwd){
  var key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA88Fgf***0eEUXEvamRKI8cV0NZaGQSIshWeGrOe/0iPVF4kps5s6eru8JGS27JjsCQ9kqKDnFLS6NsWX1eJDJSs5lOVbK5DxZrCuSwEDMZMXiyfL09dHoTODXTINIBBrxdDZGoyYDnZR0EasTO/hiDaKf0UbsdxJC975QA/y9k1y/nTksRXS5cTAOGqO6KffxFnwIDAQAB"
  var exponent = "010001"
  St.prototype.setPublicKey(key,exponent);
  return St.prototype.encrypt(pwd);
};
// 传入密码
getPwd("1122")

组成的完整代码如下(因为代码太长且无法收起,所以只简单展示一下截图)

图像 039.png

打开浏览器控制台,把完整的JS代码粘贴进去运行

运行结果.png

得到秘钥后在postman验证通过(这里就不展示了),
本想着借助python运行JS的库PyExecJS计算密码密文,这样可以省去看JS代码的时间,但是实际这里花的时间非常多,包括找不到windows对象,这个因为是浏览器对象,引擎直接运行JS找不到很正常,看了代码发现跟加密无关就删除解决了;然后再是运行提示globalThis未定义问题,排查到需要Nodejs引擎运行;引擎问题解决了,然后是加密结果总是False。个人对JS不是很懂,所以这个方案页只能分析到这里为止。
如果有大佬明白其中的问题,请多多指点。

奇怪的问题.png