安全矩阵

 找回密码
 立即注册
搜索
查看: 5058|回复: 0

H5页面漏洞挖掘之路-混淆篇

[复制链接]

991

主题

1063

帖子

4315

积分

论坛元老

Rank: 8Rank: 8

积分
4315
发表于 2020-10-27 09:05:04 | 显示全部楼层 |阅读模式
原文链接:H5页面漏洞挖掘之路-混淆篇

前言
针对上次我们提交漏洞之后,我们再次查看JS代码,定位加密函数和解密函数的位置,发现已经不是赤裸裸没有任何防护,而是已经进行的了JS混淆,接下来我们针对遇到JS混淆后,我们该如何破解JS混淆后的代码进行加解密,继续进行渗透测试。笔者在这里提供一个思路和方法。

前置知识
首先我们先了解下代码混淆的具体原理是什么?其实很简单,就是去除代码中尽可能多的有意义的信息,比如注释、换行、空格、代码负号、变量重命名、属性重命名(允许的情况下)、无用代码的移除等等。因为代码是公开的,我们必须承认没有任何一种算法可以完全不被破解,所以,我们只能尽可能增加攻击者阅读代码的成本。
我将混淆类型分为两类:
变量名混淆
将变量名混淆成阅读比较难阅读的字符,增加代码阅读难度,而现在大部分厂商的混淆,都会将其混淆成16进制变量名。
效果如下:
  1. `var test = 'helloworld';`
复制代码
混淆后:
  1. `var _0x7deb = 'helloworld';`
复制代码
常量提取
将JS中的常量提取到数组中,调用的时候用数组下标的方式调用,这样的话直接读懂基本不可能了,要么反AST处理下,要么一步一步调试,工作量大增。
以上面的代码为例:
  1. `var test = 'helloworld';`
复制代码
复制代码混淆过后:
  1. var _0x9d2b = ['helloworld'];
  2. var _0xb7de = function (_0x4c7513) {
  3.     var _0x96ade5 = _0x9d2b[_0x4c7513];
  4.     return _0x96ade5;
  5. };
  6. var test = _0xb7de(0);
复制代码

常量混淆
每个文件开头会有一个很长的字符数组,然后会有一段代码对这个数组进行加工,然后还有一个函数接收一个或两个参数输出一个字符串,这个字符串更接近原始的代码。将常量进行加密处理,上面的代码中,虽然已经是混淆过后的代码了,但是helloworld字符串还是以明文的形式出现在代码中,例如将关键字进行Unicode16进制编码。如下:
  1. `var test = 'helloworld';`
复制代码

结合常量提取得到混淆结果:
  1.   var _0x9d2b = ['\x68\x65\x6c\x6c\x6f'];
  2.    
  3.     var _0xb7de = function (_0x4c7513) {
  4.         _0x4c7513 = _0x4c7513 - 0x0;
  5.         var _0x96ade5 = _0x9d2b[_0x4c7513];
  6.         return _0x96ade5;
  7.     };
  8.    
  9.     var test = _0xb7de('0x0');
复制代码

案例
第一部分: 变量名称存储数组
这里存储了一些在函数中用到的变量和字符串。
  1. var _0x2ec2 = [
  2.   'UGtjczc=',
  3.   'dG9TdHJpbmc=',
  4.   'ZGVjcnlwdA==',
  5.   'c3RyaW5naWZ5',
  6.   'xxxx',
  7.    'bW9kZQ==',
  8.   'Q0JD',
  9.   'cGFk'
  10. ];
复制代码
第二部分 数组处理函数
  1. /**
  2. * params _0x167407: 上面的字符串数组
  3. * params _0x353595: 计数个数
  4. * 把前 _0x353595 +1 个元素放到数组末尾
  5. */
  6. (function (_0x167407, _0x353595) {
  7.   var _0x52a3ae = function (_0x3fbe47) {
  8.     while (--_0x3fbe47) {
  9.       _0x167407['push'](_0x167407['shift']());
  10.     }
  11.   };
  12.   _0x52a3ae(++_0x353595);
  13. }(_0x2ec2, 312));
复制代码

第三部分 数组字符串处理函数
  1. // 这个是数组内容解码的函数, 实际上第二个参数是没有用到的
  2. var _0x523d = function (_0x4c10d0, _0x393bf7) {
  3.   _0x4c10d0 = _0x4c10d0 - 0; // 这里第一个参数是通过字符串传进来, 因此这里起到类型转换的作用
  4.   var _0x70d87b = _0x2ec2[_0x4c10d0]; // 这里 _0x70d87b 保存的是数组下标对应的值, 也就是, 解密第几个字符串
  5.   // 接下来判断有没有进行过初始化操作, 如果没有的话, 先初始化
  6.   if (_0x523d['CuFQcU'] === undefined) {
  7.     (function () {
  8.       var _0x5b57a4 = function () {
  9.         var _0x29e588;
  10.         try {
  11.           _0x29e588 = Function('return (function() ' + '{}.constructor("return this")( )' + ');') ();
  12.         } catch (_0x4956c9) {
  13.           _0x29e588 = window;
  14.         }
  15.         return _0x29e588;
  16.       };
  17.       var _0x2b121a = _0x5b57a4(); // 这里实际上返回的是 Window 对象
  18.       var _0x6c99b9 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
  19.       // 下面这个是判断Window有没有atob这个函数, 如果没有的话生成一个进去.
  20.       _0x2b121a['atob'] || (_0x2b121a['atob'] = function (_0x13f6f4) {
  21.         var _0x901f5e = String(_0x13f6f4) ['replace'](/=+$/, '');
  22.         for (var _0x240979 = 0, _0x43e3e8, _0x42ec25, _0x6ec31e = 0, _0x1c0a86 = ''; _0x42ec25 = _0x901f5e['charAt'](_0x6ec31e++); ~_0x42ec25 && (_0x43e3e8 = _0x240979 % 4 ? _0x43e3e8 * 64 + _0x42ec25 : _0x42ec25, _0x240979++ % 4) ? _0x1c0a86 += String['fromCharCode'](255 & _0x43e3e8 >> ( - 2 * _0x240979 & 6))  : 0) {
  23.           _0x42ec25 = _0x6c99b9['indexOf'](_0x42ec25);
  24.         }
  25.         return _0x1c0a86;
  26.       });
  27.     }());
  28.    
  29.    
  30.     _0x523d['ZEesoG'] = function (_0x1de802) {
  31.       var _0x216ff1 = atob(_0x1de802);
  32.       var _0x42331f = [
  33.       ];
  34.       for (var _0x3a392f = 0, _0x2319db = _0x216ff1['length']; _0x3a392f < _0x2319db; _0x3a392f++) {
  35.         _0x42331f += '%' + ('00' + _0x216ff1['charCodeAt'](_0x3a392f) ['toString'](16)) ['slice']( - 2);
  36.       }
  37.       return decodeURIComponent(_0x42331f);
  38.     };
  39.     // 到这里完成初始化操作, 置CuFQcU为true, 添加VgXLDn属性, 这个相当于是一个字典表, 如果已经解密过的东西就存进去, 下次就不用在解密了.
  40.     _0x523d['VgXLDn'] = {};
  41.     _0x523d['CuFQcU'] = !![];
  42.   }
  43.   
  44.    // 后面这段是先判断之前有没有对传入的参数进行解密过, 如果解密过的话, 那么就不再解密了.
  45.   var _0x22ee7f = _0x523d['VgXLDn'][_0x4c10d0];
  46.   if (_0x22ee7f === undefined) {
  47.     _0x70d87b = _0x523d['ZEesoG'](_0x70d87b);
  48.     _0x523d['VgXLDn'][_0x4c10d0] = _0x70d87b;
  49.   } else {
  50.     _0x70d87b = _0x22ee7f;
  51.   }
  52.   return _0x70d87b;
  53. };
复制代码

第四部分 加解密函数
  1. function encrypt(_0xd0a5dd) {
  2.   var _0x2d682e = CryptoJS[_0x523d('0x0')][_0x523d('0x1')][_0x523d('0x2')](_0x523d('0x3'));
  3.   var _0x2d053c = CryptoJS[_0x523d('0x0')][_0x523d('0x1')][_0x523d('0x2')](_0x523d('0x4'));
  4.   var _0xa5c781 = CryptoJS[_0x523d('0x0')][_0x523d('0x1')][_0x523d('0x2')](_0xd0a5dd);
  5.   var _0x17d14e = CryptoJS[_0x523d('0x5')][_0x523d('0x6')](_0xa5c781, _0x2d682e, {
  6.     'iv': _0x2d053c,
  7.     'mode': CryptoJS[_0x523d('0x7')][_0x523d('0x8')],
  8.     'padding': CryptoJS[_0x523d('0x9')][_0x523d('0xa')]
  9.   });
  10.   return _0x17d14e[_0x523d('0xb')]();
  11. }
  12. function decrypt(_0x363945) {
  13.   var _0x41412c = CryptoJS[_0x523d('0x0')][_0x523d('0x1')][_0x523d('0x2')](_0x523d('0x3'));
  14.   var _0xf43728 = CryptoJS[_0x523d('0x0')][_0x523d('0x1')][_0x523d('0x2')](_0x523d('0x4'));
  15.   var _0x2f2c26 = CryptoJS[_0x523d('0x5')][_0x523d('0xc')](_0x363945, _0x41412c, {
  16.     'iv': _0xf43728,
  17.     'mode': CryptoJS[_0x523d('0x7')][_0x523d('0x8')],
  18.     'padding': CryptoJS[_0x523d('0x9')][_0x523d('0xa')]
  19.   });
  20.   return CryptoJS[_0x523d('0x0')][_0x523d('0x1')][_0x523d('0xd')](_0x2f2c26) [_0x523d('0xb')]();
  21. }
复制代码

当我们分析整个混淆后的代码后, 我们可以手动断点调试, 来看看具体的解密之后每参数是什么。我们首先将整个混淆后的js代码copy下来,定义main()函数,调用加密encrypt和decrypt解密这两个函数,在浏览器下调试运行。


代码完美运行,在第三部分数组字符串处理函数的位置我们手动断点F10进行调试。
密钥key成功拿到:

向量IV成功拿到:

得知加密算法为AES:

AES加密算法使用的模式:mode=CBC

AES加密算法使用的填充方式:Pkcs7

至此混淆后的加密算法已破解,拿到加密算法的明文,我们可以编写python加解密脚本,加解密结果一致。

总结
JS混淆在安全对抗中必不可少,一是对保护前端页面的代码逻辑,二是对前端登陆的算法密钥和向量IV进行保护。而我们通过反混淆还原代码或者直接调用混淆后的JS代码进行调试,获取密钥和向量IV,从而达到解密密文,篡改数据包继续进行漏洞挖掘。
参考
  1. https://www.52pojie.cn/thread-1104122-1-1.html#29856570_%E5%A3%B0%E6%98%8E
复制代码







回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-9-20 15:29 , Processed in 0.014544 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表