2023年5月30日超星登录加密算法分析

前言

不知道什么时候超星又修改了他的登录加密算法,研究一下,本文仅供学习交流,转载请注明出处

算法比较简单,就不慢慢下断点逆向了,直接干

正文

老规矩打开Chrome或者你电脑上一切能打开开发者工具的浏览器,输入i.chaoxing.com,来到登录页面,打开开发这工具,随便输入账号密码,这里我输入的账号是13900000000,密码是123456789,点击登录,看网络请求

可以看到,uname加密了,password也加密了,那毫无疑问uname就是手机号码,password就是密码,老规矩看一下登录按钮按下之后做了什么,右键检查按钮这个元素

可以清晰看到,当点击按钮的时候,执行了一个js方法,名为loginByPhoneAndPwd()

那我们就在刚才的网络请求那里找一下这个方法

把这个方法先拿出来分析

function loginByPhoneAndPwd(){
	util.showMsg(false,"phoneMsg","",true);
	util.showMsg(false,"pwdMsg","",true);
	util.showMsg(false,"err-txt","");
	var phone = $("#phone").val().trim();
	var pwd = $("#pwd").val();
	var fid = $("#fid").val();
	var refer = $("#refer").val();
	var forbidotherlogin = $("#forbidotherlogin").val();
	if(util.isEmpty(phone)){
		util.showMsg(true,"phoneMsg",please_input_phone,true);
		return;
	}/*else if(!util.isInterPhone(phone) && (phone.length > 50 || !util.checkEmail(phone))){
		util.showMsg(true,"phoneMsg","手机号格式错误",true);
		return;
	}*/
	if(util.isEmpty(pwd)){
		util.showMsg(true,"pwdMsg",please_input_pwd,true);
		return;
	}
	var t = $("#t").val();
	if(t == "true"){
		var transferKey =  "u2oh6Vu^HWe4_AES";
		pwd = encryptByAES(pwd, transferKey);
		//pwd = $.base64.btoa(pwd,"UTF-8");
	}

	if(capInstance == null || $("#needVcode").val() != "1"){
		//容错
		loginByPhoneAndPwdSubmit();
	}else{
		capInstance && capInstance.popUp();
	}
}

我们需要关注的是这一小段

if(t == "true"){
	var transferKey =  "u2oh6Vu^HWe4_AES";
	pwd = encryptByAES(pwd, transferKey);
	//pwd = $.base64.btoa(pwd,"UTF-8");
}

这一小段可以给出的信息是,加密密钥是transferKey

加密过程就是encryptByAES(pwd, transferKey);

老方法(就是在网络请求那里查找)查找一下对应的方法,可以找到在loginByPhoneAndPwd() 方法的上面有一个方法

function encryptByAES(message, key){
	let CBCOptions = {
		iv: CryptoJS.enc.Utf8.parse(key),
		mode:CryptoJS.mode.CBC,
		padding: CryptoJS.pad.Pkcs7
	};
	let aeskey = CryptoJS.enc.Utf8.parse(key);
	let secretData = CryptoJS.enc.Utf8.parse(message);
	let encrypted = CryptoJS.AES.encrypt(
		secretData,
		aeskey,
		CBCOptions
	);
	return CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
}

这个时候我们可以抽离出来,放到IDE里,需要补一下CryptoJS的依赖,这里需要安装Node.js,这里就不废话补充了

npm i crypto-js

补充完依赖后,我们就可以新建一个js文件,然后把下面的代码放到IDE里,放心,我会解释代码的意思

const CryptoJS = require("crypto-js");

function encryptByAES(message, key) {
    let CBCOptions = {
        iv: CryptoJS.enc.Utf8.parse(key),
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    };
    let aeskey = CryptoJS.enc.Utf8.parse(key);
    let secretData = CryptoJS.enc.Utf8.parse(message);
    let encrypted = CryptoJS.AES.encrypt(
        secretData,
        aeskey,
        CBCOptions
    );
    return CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
}

let transferKey = "u2oh6Vu^HWe4_AES", pwd = "123456789";
console.log(encryptByAES(pwd, transferKey));

第一行是引入CryptoJS的依赖

然后就是我们刚才拿到的encryptByAES 方法,原封不动

接着就是创建一个变量放密钥,这个密钥就是刚才我们找到的 u2oh6Vu^HWe4_AES ,不知道怎么来的?往上面翻翻,刚说完

接着pwd 就是模拟的密码,刚才测试的时候输入的密码是123456789,最后一行就是加密过程,不知道怎么来的?往上面翻翻,原封不动拿下来的

得出结果8La9l9FwD0SxCAYAbGmVFQ==

对比一下刚才我们通过官方加密后的结果

// 官方: 8La9l9FwD0SxCAYAbGmVFQ==
// 我们: 8La9l9FwD0SxCAYAbGmVFQ==

OK,到这里,密码加密就完毕了,但还有一个问题,手机号码,uname还没处理好,用同样的方法试试,把pwd改成13900000000试试

得出结果是96OlDUDY7HsmBfaMHg2CMg== ,而我们从官方得出的结果也是96OlDUDY7HsmBfaMHg2CMg== ,虽然如此,但我们还是得分析一波为什么

还记得我们一开始就得到的信息loginByPhoneAndPwd 方法吗,忘记了?没关系,再给你看一次

function loginByPhoneAndPwd(){
	util.showMsg(false,"phoneMsg","",true);
	util.showMsg(false,"pwdMsg","",true);
	util.showMsg(false,"err-txt","");
	var phone = $("#phone").val().trim();
	var pwd = $("#pwd").val();
	var fid = $("#fid").val();
	var refer = $("#refer").val();
	var forbidotherlogin = $("#forbidotherlogin").val();
	if(util.isEmpty(phone)){
		util.showMsg(true,"phoneMsg",please_input_phone,true);
		return;
	}/*else if(!util.isInterPhone(phone) && (phone.length > 50 || !util.checkEmail(phone))){
		util.showMsg(true,"phoneMsg","手机号格式错误",true);
		return;
	}*/
	if(util.isEmpty(pwd)){
		util.showMsg(true,"pwdMsg",please_input_pwd,true);
		return;
	}
	var t = $("#t").val();
	if(t == "true"){
		var transferKey =  "u2oh6Vu^HWe4_AES";
		pwd = encryptByAES(pwd, transferKey);
		//pwd = $.base64.btoa(pwd,"UTF-8");
	}

	if(capInstance == null || $("#needVcode").val() != "1"){
		//容错
		loginByPhoneAndPwdSubmit();
	}else{
		capInstance && capInstance.popUp();
	}
}

注意"容错"这两个字下面有一个方法loginByPhoneAndPwdSubmit()

巧合的是,我们得到的loginByPhoneAndPwd 方法下面就是loginByPhoneAndPwdSubmit 方法,可能有点绕,看图就很清晰了

还是把它拿下来看吧

function loginByPhoneAndPwdSubmit() {
	let phone = $("#phone").val().trim();
	let pwd = $("#pwd").val();
	let fid = $("#fid").val();
	let refer = $("#refer").val();
	let forbidotherlogin = $("#forbidotherlogin").val();
	let t = $("#t").val();
	let _blank = $("#_blank").val();
	let doubleFactorLogin = $("#doubleFactorLogin").val();
	let independentId = $("#independentId").val();
	if(t == "true"){
		let transferKey = "u2oh6Vu^HWe4_AES";
		pwd = encryptByAES(pwd, transferKey);
		phone = encryptByAES(phone, transferKey);
	}
	let validate = $("#validate").val();
	if(undefined == validate){
		validate = "";
	}
	$.ajax({
		url: _CP_+"/fanyalogin",
		type:"post",
		dataType : 'json',
		data: {
			'fid': fid,
			'uname': phone,
			'password': pwd,
			'refer': refer,
			't': t,
			'forbidotherlogin': forbidotherlogin,
			'validate': validate,
			'doubleFactorLogin': doubleFactorLogin,
			'independentId': independentId
		},
		success: function(data){
			if(data.status){
				let url = "";
				if(isChaoxingReader()){
					let path= window.location.protocol+'//' + window.location.host ;
					url = path+_CP_+"/towriteother?name="+encodeURIComponent(data.name)+"&pwd="+encodeURIComponent(data.pwd)+"&refer="+data.url;
				}else{
					url = decodeURIComponent(data.url);
				}
				//跳转到双因子登录页面
				if (data.containTwoFactorLogin) {
					let toTwoFactorLoginPCUrl = data.twoFactorLoginPCUrl;
					location.href = toTwoFactorLoginPCUrl + "&_blank=" + _blank + "&refer=" + encodeURIComponent(url);
					return false;
				}

				if (top.location != self.location && _blank == "1") {
					top.location = url;
				} else {
					window.location = url;
				}
			} else {
				if (data.weakpwd) {
					window.location =_CP_+ "/v11/updateweakpwd?uid=" + data.uid + "&oldpwd=" + encodeURIComponent($("#pwd").val()) + "&_blank=" + $("#_blank").val() + "&refer=" + refer;
				} else {
					let msg = util.isEmpty(data.msg2) ? "登录失败" : data.msg2;
					msg = ("密码错误" == msg || "用户名或密码错误" == msg) ? "手机号或密码错误" : msg;
					util.showMsg(true,"err-txt",msg);
				}
			}
		}
	});
}

可以注意到这三行代码

let transferKey = "u2oh6Vu^HWe4_AES";
pwd = encryptByAES(pwd, transferKey);
phone = encryptByAES(phone, transferKey);

可以得出的信息是,密钥,加密算法都是一样的,通过这样,我们就能证明刚才我们的猜想是对的

到这里,我们就完成了所有的分析,最终代码如下

const CryptoJS = require("crypto-js");

function encryptByAES(message, key) {
    let CBCOptions = {
        iv: CryptoJS.enc.Utf8.parse(key),
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    };
    let aeskey = CryptoJS.enc.Utf8.parse(key);
    let secretData = CryptoJS.enc.Utf8.parse(message);
    let encrypted = CryptoJS.AES.encrypt(
        secretData,
        aeskey,
        CBCOptions
    );
    return CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
}

let transferKey = "u2oh6Vu^HWe4_AES", phone = "13900000000", pwd = "123456789";
console.log(`phone: ${encryptByAES(phone, transferKey)}`);
console.log(`pwd: ${encryptByAES(pwd, transferKey)}`);

 

版权声明:
作者:X1a0He
链接:https://www.x1a0he.com/cxnewsign_230530
来源:X1a0He's Blog
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>