【声明】本文首发于FreeBuf平台(浅析前端加密后数据包的修改方法 ),属于FreeBuf原创奖励计划,未经许可禁止转载。
在渗透测试过程中,许多系统会直接明文传输账户密码等敏感信息,存在被嗅探的风险。有的开发人员则会对相应的敏感信息在前端借助JS代码进行加密后再传输,使攻击者看到的是“一堆乱码”,从而无法正常进行篡改和其他渗透测试操作……如下图所示。
但是前端加密只能提高攻击者进行攻击的难度,并无法对敏感信息进行有效的保护。因为前端JS加密的代码是公开的(虽然可能会经过混淆和压缩),攻击者可以通过对加密函数的审计,来达到根据自身需要构造测试语句并加密后替换初始密文的目的,从而可以像明文传输的系统一样进行正常的渗透测试。本文将记录对某网站登录页面的前端加密函数的破解过程。
首先来看看该站点的登录页面:
输入账号密码,开启代理和BurpSuite,点击登录,抓包如下:
可见,用户名和密码在传输时均经过前端加密。
接下来进行加密函数的查找,此例借助火狐浏览器F12开发者工具。
前端很多操作都是基于事件绑定的,我们选取“登录”按钮,并查看其绑定的事件。
点击event,可快速定位到这个button点击后的事件逻辑块代码,如下图所示。
在调试器中进一步跟进、查看登录按钮的事件逻辑块代码:
此处附上validateLoginForm()
函数及其关联函数的具体代码:
function validateLoginForm() {
var userName = document.getElementById("userName");
var userPwd = document.getElementById("userPassword");
var inputCode = document.getElementById("validateCode");
if (userName.value.replace(/(^\s*)|(\s*$)/g, "")=='') {
$("#login_info").html("请输入用户名");
userName.focus();
return false;
}else if (userPwd.value.replace(/(^\s*)|(\s*$)/g, "")=='') {
$("#login_info").html("请输入密码");
userPwd.focus();
return false;
}else if (inputCode!=undefined && inputCode.value.replace(/(^\s*)|(\s*$)/g, "")=='') {
$("#login_info").html("请输入验证码");
inputCode.focus();
return false;
}else{
userLogin();
}
}
function userLogin(){
var userName = document.getElementById("userName").value;
var userPwd = document.getElementById("userPassword").value;
var inputCode = document.getElementById("validateCode").value;
var actionName = "";
var submitUserId = document.getElementById("submitUserId").value;
var loginUrl = "/wt/dayeewt/web/index/webUserLogin!userLoginForNormalByCode?operational=260496f91c732845b3918ece535b9c378edde02b7918fe1ab4aefb88debf685012f581ab741e69cc9c3a73bbbf91490a61806b4ea38059f1f30106fd947bf04c67635eb5443d0c163cbaf45030dacf5a3d8fada7db3731a2";
var dataStr = "needDefaultKey=true&submitUserId="+submitUserId+"&userName="+encrypt(userName)+"&password="+encrypt(userPwd)+"&validateCode="+inputCode+"&actionName="+actionName;
$.ajax({
type:"POST",
dataType:"text",
url:loginUrl,
data:dataStr,
success:callBack
});
}
function callBack(data){
var result = eval("("+data+")");
var retCode = result.retCode;
if(retCode=="0"){
var brandCode = '1';
if(brandCode == ''){
brandCode = 1;
}
var url;
url = "/wt/dayeewt/web/index/primaryResume210!listResume?operational=260496f91c732845b3918ece535b9c37b9663b3aaba1be538a89b51cf8e70839552308a0211a4dd69fd52a40c23d85bba51758a03efc5dd80ab70edad5576c64c39cecf29e406ff9"+"&brandCode="+brandCode;
window.location.href=url;
} else {
genNewCode();
document.getElementById("validateCode").value="";
$("#login_info").html(result.retMsg);
}
}
……………………
从以上代码可以看出,userLogin()
函数中调用encrypt()
函数对用户名和密码进行了加密,"&userName="+encrypt(userName)+"&password="+encrypt(userPwd)
。
显然,接下来的任务是继续追踪并查看encrypt()
函数。
直接使用Ctrl+Shift+F
全局搜索encrypt
:
双击查看encrypt()
函数的具体代码:
从代码中可以看到使用了AES加密(ECB模式),密钥key=“1234567887654321”。
具体的加密逻辑代码放在上图所示的crypto-js.js
中,此处不再展开分析了。
打开AES在线加解密站点,如下图所示:
选择加密模式ECB、填充模式pkcs7padding、数据块128位、密码1234567887654321、输出hex,然后输入a123456并进行加密,如下图所示:
得到的加密结果5ec7551fa86d8c8cf5fbe8f153c96dd8
和文章前面拦截获取的登录请求包的密文进行比较:
可见,与password的密文值完全一致。
同时可验证下是否能正常解密,咱们输入数据包中username参数的密文4bc8c4cf302a2615278bdd630ba541bcaa7d6d07dac4c6b68bec23f5fd1d3126
,尝试进行解密,成功。如下图所示:
此案例中整体代码并不复杂,encrypt()
加密函数的查找相对简单,如果遇到复杂的加密情况(经过代码混淆和压缩),我们可能需要借助断点调试确定程序的加密函数的位置(还可以跟踪加密过程中各变量数值的变化)。下面简述下前端JS代码的断点调试过程。
如下图所示,在代码行前面单击即可在改行设置断点:
重新点击“登录”,如果该button的逻辑处理代码调用了断点处的函数,则程序会被暂停到断点处(可初步确定encrypt()
函数就是加密了登录账号信息的函数):
继续逐步往下执行JS代码(可跨步、逐步、回跳),并观察相关变量的数值变化:
一直往下逐行执行代码,最后返回账号([email protected])的密文值:
得到的加密结果4bc8c4cf302a2615278bdd630ba541bcaa7d6d07dac4c6b68bec23f5fd1d3126
和文章前面拦截获取的登录请求包的密文进行比较:
可见,与userName参数的密文值完全一致,故断定该函数即为我们要找的加密函数。
第一种方法上面已经展示完毕,但是该方法局限于前端加密算法很简单(Base64、MD5、密码存储在前端的DES等),如果加密算法特别复杂(如非对称加密RSA算法),无法直接破解(如下图所示),我们就只能通过第二种方法来绕过密文了。
1、来看看本次测试的站点和功能,为某支付平台的交易记录查询功能:
2、首先,从前端JS代码找到该站点的加密函数所在的位置,如下图所示:
3、接着,在该语句设置断点,然后执行程序,如果加密函数寻找正确,那么程序将暂定在该行代码处:
4、程序跳转到执行下一行代码,发现查询请求包的明文数据k,如下图所示:
5、切换到控制台,输入k,控制台将打印k的值,如下图所示:
6、接着关键操作!直接在控制台输入“k=XXXXX”的命令替换掉k的值(此处替换掉查询的卡号,尝试进行越权查询测试),如下图所示:
7、最后,取消断点,放行程序,发现查询的卡号成功被篡改,同时越权查询失败,如下图所示:
【小结】至此,借助F12开发者工具的断点调试功能,我们成功实现了对采用前端加密的站点的数据包篡改。
下面介绍第二种方法,借助Fiddler抓包工具,实现对前端加密站点的JS脚本替换,从而达到任意篡改数据包的目的。
1、首先,将包含加密函数的在线JS脚本文件通过点击鼠标右键,下载保存到本地,如下图所示:
2、在加密函数encryptstring: function (k, g) 中添加JS代码,替换查询请求包中的卡号,如下图所示:
3、接下来打开Fiddler,捕获加密函数所在的JS文件的请求,并将其拖入到右侧AutoResponder模块里,如下图所示:
4、进入AutoResponder模块,勾选以下选项并添加自动替换规则,将本地编辑的JS代码替换用于在线加密的JS代码,如下图所示:
5、刷新该站点,点击该功能模块,其前端JS代码将被我们本地编辑过的JS代码替换,来看下效果:
【小结】至此,我们借助Fiddler的AutoResponder模块模块实现了对采用了前端加密的站点的请求包的篡改。
至此,我们已经可以自由地篡改和替换该网站相应数据包的密文,从而进行正常的渗透测试。另外补充一句,断点调试虽然比JS脚本代码替换简便,但是JS脚本代码替换的方法可以实现的功能更为强大,测试人员可根据实际的需求,选择合适的测试方法。
最后,附上几篇大佬们关于前端加密的文章: