日常工作中,正好碰到针对银行类系统的安全测试工作,通过Burp抓包发现传输的信息都是一串加密的数据,因此,就针对其中一个系统进行代码分析,了解银行一般是通过何种方式来实现数据的加密,顺便巩固一下JS调试技巧。
1、为了模拟手机访问直销银行登陆页面,利用Chrome F12开发工具,点击左上角"toggle device toolbar",模拟手机访问直销银行主页(www.mszxyh.com)
2、设置Burp代理工具,能够捕捉到用户直销银行账号登陆时的加密字符串,初步可以判断用户输入账号密码后,是在前端对输入的信息进行算法加密。
利用Chrome F12开发工具里面的“network”模块,本地模拟一次登录状态,并捕捉相应的通信流量。
我们可以猜想用户在网页上输入账号密码后,会在前端利用JS进行加密,然后将加密数据以POST的方式提交到认证服务器,因此,如果想要分析加密算法,需要去分析当前主页加载的所有JS文件。
F4w8pLTVHDMq3ExNFPV+GJsc9x55Zc+LIUekAsxqWzCUzZRJac/Fp3MmmnlNBna5inw1KLfcwakWp1ARdIcTAMW4GkOjkkRDul2FXmFVPfhQZbN1TexKORsPEiYPxSCOEVqlsMMTahnxZU+krTRhgdSpGCwDopaVnc+AaAl+2XAZpccY3elnAMUOWdPxx2I0noC5S63Jax6s8KhuMsx8Arx//iYQIHReQZxxuB2f59cDFozgY6HIaMsFGWGhVlDtT9g+LGQdGispdzUWESxne5cyU8+c4hjYHWOzu38IdL/jC+koy+RJe6SA17UYgRuNK+jfvnHbZzVkVmyY3TluD4TYX8RvKYXffAofPB0PeOEuakPKkcpQcB34phrE8mfWefq9ODk/YKNvWgJyfjD9zBpujmwXOb4hURd6FfAKVk4MWJuJLSybko2caUPDZVnoMHMSK9GPRM/cY7wTJRBKlDIB3tYylOzFgTMj/YwhJAHFtrhyTVRVgGqb8qSXFooquD7czpur00c=
根据加密字符,可以判断当前最后一次的加密算法是base64,因此,在JS文件中搜索“base64”关键字,在all.min.js文件中,定位到下面的功能函数:
service("crypto", function() {
var f = "2KL89TM3"; //某个常量
var e = CryptoJS.enc.Utf8.parse(f); //对f这个常量做了一个变形
return {
DESEncrypt: function(i, g) {
var j = CryptoJS.enc.Utf8.parse(g);
var h = CryptoJS.DES.encrypt(i, j, {
iv: e, //分析之后其实就是DES中的偏移量
mode: CryptoJS.mode.CBC, //DES加密模式
padding: CryptoJS.pad.Pkcs7 //DES填充
});
return h.ciphertext.toString(CryptoJS.enc.Base64)
}
}
上段代码中DES算法的偏移量e的值可以直接在Chrome F12开发工具的console接口中计算出来:
DESEncrypt函数有2个参数i,g,结合CryptoJS.DES.encrypt,可以初步判断i为一串需要加密的字符串,g经过一串变形生成j,也就是DES算法的key值,那么结合“DESEncrypt”搜索关键字,定位到如下代码位置:
....
if (H && d.REQUESTENCRYPT && vx.indexOf(a, D) > -1) {
var J = t.params.random;
H = p.DESEncrypt(vx.toJson(H), J);
F.request = {
JData: H
}
.....
也就是加密的原始数据 vx.toJson(H),key值未J,但是具体是啥没法确认,但是可以认为当前all.min.js文件仅仅是一个存放功能函数的文件,我们先研究到这一步。
我们从登陆主界面开始,右击查看源码,发现点击登陆按钮后,数据将被提交到doIt()函数中,搜索“doIt()”字符串,定位到login.js文件
doIt()部分代码如下:
var DeviceType = $os.type,
loginUserId = hasSaveUserId && !hasChangeUserId ? userId : $scope.UserId;
var formData = {
"UserId": loginUserId,
"Password": $scope["_Password"], //软键盘输入的密码
"RandNum": window.Channel.H5?"NA":$scope.RandNum,
"_vTokenName": $scope.vTokenName,
"BankId": "9999",
"LoginType": "E",
"_locale": "zh_CN",
"CustIdentiType": "1",
"ChannelId": window.ChannelId,
"DeviceType": DeviceType
};
var staData = {
"UserId": loginUserId
};
$scope.post2SRV("thirdEwebLoginTrans.json",formData, function(data){ //发送数据
if($scope.rememberUser){
$localStorage.setItem("CMBCUserId",loginUserId);
}else{
$localStorage.setItem("CMBCUserId","");
}
$rootScope.CIFNAME=data.userInfo.CIFNAME;
User.saveUser(data);
//indexFactory.init(); //清除之前保存的首页如意宝、定活宝、民生金信息
//登录之后重新获取首页菜单数据,首页是我的账户不用更新首页菜单
if(window.actionId!="act"){
indexFactory.getIndexPageInfo(true).then(function(data){
$rootScope.indexMenuList = data.indexMenuList;
});
}
Statistics.reportClickEvent("W000101-01","登录",staData);
}
我们就在var formData 处下断点,并输入账号密码,运行:
当F10单步到$scope.post2SRV函数时,从字面意思就可看出该函数时发送认证信息到服务器,因此按F11进入post2SRV函数,根据调试步入的文件名来确认是否进入all.min.js文件。
通过下方Local环境变量可以查看all.min.js文件中各个变量的赋值情况,回到之前DESEncrypt加密函数中vx.toJson(H), J 这个两个参数,来看看DES加密中的key也就是J是怎么来的,一路F10,直到J有具体的赋值。
此时J的值为“C8AD3AD7”,通过分析该值是未登录前,客户端请求get3DesRandomTrans.json文件,服务器返回的一串随机字符串,也就是DES加密的密钥是存储在服务器端,每次打开的时候,客户端主动向服务器请求DES的key值,然后利用key值在客户端对用户输入的账号密码进行加密。
由于直销银行服务器端有超时的设置,因此其中完整的一次成功的解密如下图所示:
直销银行前端主要用的是DES算法加密,利用系统自身的超时设置+存储在服务器端的随机密钥在前端实现对用户输入数据的算法加密,针对用户软件盘输入的密码又有一层加密,利用JS动态调试,应该也可以分析清楚,太困了,睡觉去了。