JS逆向实战-南京大学统一身份认证平台

本文最后更新于 2024年10月22日 晚上

前言:OK,今天开始学习一些基本的JS逆向技术,先达到能逆向出一些简单站点的校验参数、密码等的加密流程,并能写出py脚本爆破密码的程度,话不多说,上案例

加密发现

  1. 打开目标,南京大学统一身份认证平台,如果加密的话那肯定是加密密码参数

image

  1. 输入admin 123456测试看看,F12,抓包,看看登录包载荷

image

果然密码参数被加密了,看样子肯定不是base64等等简单的加密

  1. 本地禁用js,还是输入admin 123456,再次抓登录包,看看载荷

image

密码就以明文形式呈现了,说明加密密码参数的逻辑就写在js代码中,尝试通过分析js逆向出加密流程,这样就可以爆破密码了

逆向加密流程

直接全局搜索 password 参数,定位到加密代码段

image

这里似乎找到了加密password参数的加密点,到底是不是呢?

可以下个断,调试验证一下,在第90行打上断点:

image

再次输入admin 123456,登录,果然在第90行成功下断,这就是password参数的加密点

image

此时还未通过这个加密点,password参数应该是未被加密的,控制台输出一下password的值看看是否如此

image

123456,果然,执行下一步,使password通过加密片段,再次看看password的值

image

可以看到123456已经被加密得面目全非了

确认了加密点,那么下一步就是回到加密点分析加密流程

1
_etd2(password.val(),casLoginForm.find("#pwdDefaultEncryptSalt").val());

可以看到,_etd2函数接收了两个参数,password.val(),casLoginForm.find(“#pwdDefaultEncryptSalt”).val()

意思就是接收password的原始值(val()) 和 从登录表单(casLoginForm)中findpwdDefaultEncryptSalt的值(val()),一起传入**_etd2函数进行加密处理,password的原始值我们知道,于是在分析_etd2函的加密逻辑之前,先要找到pwdDefaultEncryptSalt**的值:

看看登录表单的源码有没有泄露出来?搜索一下

image

好家伙,直接写在源码里了

1
<input type="hidden" id="pwdDefaultEncryptSalt" value="UnejJGj5DiyhEJH7">

现在两个参数的值有了,那就跟进**_etd2**函数,看看加密逻辑

image

1
2
function _etd2(_p0,_p1) {try{var _p2 = encryptAES(_p0,_p1);$("#casLoginForm").find("#passwordEncrypt").val(_p2);}catch(e){$("#casLoginForm").find("#passwordEncrypt").val(_p0);}}
});

image

可以看到**etd2**函数接收的两个参数”password”,”pwdDefaultEncryptSalt”的形参是 分别是” p0”,”_p1”

有两条路径,正常情况下走第一条路径,遇到e(应该是某种错误)走第二条路径

那么这里看第一条路径

1
2
var _p2 = encryptAES(_p0, _p1);
$("#casLoginForm").find("#passwordEncrypt").val(_p2);

接收”_ p0”,”_ p1”两个参数使用”encryptAES”进行AES加密,并赋值为”_ p2”

之后,在ID为casLoginForm的表单元素内,找到ID为passwordEncrypt的子元素,并将其值设置为**_ p2**变量的值

接着,跟进encryptAES

image

其他几处都是调用,只有第一处是定义,跟进

image

1
2
3
4
5
6
7
function encryptAES(data, _p1) {
if (!_p1) {
return data;
}
var encrypted = _gas(_rds(64) + data, _p1, _rds(16));
return encrypted;
}
1
2
var _p2 = encryptAES(_p0, _p1);
$("#casLoginForm").find("#passwordEncrypt").val(_p2);

接收两个参数,”data”就是”_ p0”的形参,即”password”

如果_ p1(即:pwdDefaultEncryptSalt)不存在,就直接返回原始值password

反之,进行加密处理:

1
var encrypted = _gas(_rds(64) + data, _p1, _rds(16));

那么就很简单了,把”_ rds(64)“与”data“进行拼接作为第一个参数,”_ p1“作为第二个参数,”_ rds(16)“作为第三个参数,传入”_ gas“函数

“_ rds”估计就就是生成随机数的函数,同一个密码两次加密结果不同,主要就是这个函数导致的

在控制台运行看看:

image

果然,就是生成某某位数的随机数的函数

那么再跟进”_ gas“函数:

image

1
2
3
4
5
6
7
8
9
10
11
function _gas(data, key0, iv0) {
key0 = key0.replace(/(^\s+)|(\s+$)/g, "");
var key = CryptoJS.enc.Utf8.parse(key0);
var iv = CryptoJS.enc.Utf8.parse(iv0);
var encrypted = CryptoJS.AES.encrypt(data, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.toString();
}

可以看到,”_ gas“函数接收三个值,由上可知,”data“就是”_ rds(64)“+”password“的拼接, “key0“就是”pwdDefaultEncryptSalt“, “iv0“就是”_rds(16)

对key0去除特殊字符

将一个 UTF-8 编码的字符串 (key0) 转换为 CryptoJS 库能够处理的字节数组(WordArray),以便进行加密或解密操作

key=key0

iv=iv0

进行AES加密,传入data,key,iv,指定为CBC模式,Pkcs7填充

返回加密后的字符串

over,有点乱?总结一下数据流:

image

整体来说算很简单的一类了

写脚本批量加密

再自行写脚本批量生成加密后的密码,就可以爆破了