渗透测试-前端加密测试

【声明】本文首发于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代码的断点调试过程。

扫描二维码关注公众号,回复: 8704472 查看本文章

如下图所示,在代码行前面单击即可在改行设置断点:
在这里插入图片描述重新点击“登录”,如果该button的逻辑处理代码调用了断点处的函数,则程序会被暂停到断点处(可初步确定encrypt()函数就是加密了登录账号信息的函数):
在这里插入图片描述继续逐步往下执行JS代码(可跨步、逐步、回跳),并观察相关变量的数值变化:
在这里插入图片描述一直往下逐行执行代码,最后返回账号([email protected])的密文值:
在这里插入图片描述
得到的加密结果4bc8c4cf302a2615278bdd630ba541bcaa7d6d07dac4c6b68bec23f5fd1d3126和文章前面拦截获取的登录请求包的密文进行比较:
在这里插入图片描述
可见,与userName参数的密文值完全一致,故断定该函数即为我们要找的加密函数。

密文绕过

在这里插入图片描述对经过前端加密的数据包的修改,有两种思路:

  1. 从网站的前端JS代码中查找加密函数,有些站点的加密方式较为简单,通过审计前端JS逻辑代码可以直接破解,从而对密文进行解密后做修改,最后再重新加密并发送;
  2. 中断程序的执行,直接在前端JS代码对明文数据进行加密前,对明文数据进行更改。

第一种方法上面已经展示完毕,但是该方法局限于前端加密算法很简单(Base64、MD5、密码存储在前端的DES等),如果加密算法特别复杂(如非对称加密RSA算法),无法直接破解(如下图所示),我们就只能通过第二种方法来绕过密文了。

断点拦截

1、来看看本次测试的站点和功能,为某支付平台的交易记录查询功能:
在这里插入图片描述

2、首先,从前端JS代码找到该站点的加密函数所在的位置,如下图所示:
在这里插入图片描述
3、接着,在该语句设置断点,然后执行程序,如果加密函数寻找正确,那么程序将暂定在该行代码处:
在这里插入图片描述
4、程序跳转到执行下一行代码,发现查询请求包的明文数据k,如下图所示:
在这里插入图片描述
5、切换到控制台,输入k,控制台将打印k的值,如下图所示:
在这里插入图片描述
6、接着关键操作!直接在控制台输入“k=XXXXX”的命令替换掉k的值(此处替换掉查询的卡号,尝试进行越权查询测试),如下图所示:
在这里插入图片描述
7、最后,取消断点,放行程序,发现查询的卡号成功被篡改,同时越权查询失败,如下图所示:
在这里插入图片描述

【小结】至此,借助F12开发者工具的断点调试功能,我们成功实现了对采用前端加密的站点的数据包篡改。

JS脚本替换

下面介绍第二种方法,借助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脚本代码替换的方法可以实现的功能更为强大,测试人员可根据实际的需求,选择合适的测试方法。

最后,附上几篇大佬们关于前端加密的文章:

  1. 前端加解密方案探讨
  2. 前端加密后的一次安全测试
  3. 前端加密之使用Firefox来解密
  4. 两次前端绕过渗透小结
  5. 一探前端开发中的JS调试技巧
发布了117 篇原创文章 · 获赞 84 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/weixin_39190897/article/details/103358609