JS前端调试——一个静态加密网站

发布于 2022-01-18  471 次阅读


写在前面

  在这次之前我是没有接触过前端JS调试,师父临时接了一个项目,说是几个人大家一起看一下,调一下前端JS。因为项目需求嘛,这就开始边学边做,然后还有个同事之前有做过这个,刚好也可以请教一下。
  从这里开始基本上就可以踏上前端JS调试之路了,在我这没有一点JS代码基础的前提下开始,路远且长,脑壳大,不过也算是一个全新的内容了,之后的渗透测试应该也能够做的更加全面了。

发现加密

  在测试过程中使用BurpSuite进行抓包,发现抓到的包为如下类型:

image-20220118112040328

  网站POST地址都是为网站的一级路由,真实的路径都在某些字段中,加密内容为__xml的参数值,在网站的开发者工具中也可以找到这个。

image-20220118112420965

  可以看到网站前端的JS文件进过了混淆,对于解密工作还是有一定的难度,就是看起来比较烦。

AES解密

  那么下面就是解密工作,首先找找网站是怎么加密的。一般加密算法都是写在JS文件中,翻翻JS文件。

image-20220118112942974

  直接用开发者工具打开时没有格式化的JS代码,这样的代码阅读起来十分困难,首先要格式化代码以便阅读,下面是几种格式化代码方式。

  1. 谷歌Chrome浏览器开发者工具自带格式化功能
  2. Sublime Text工具——html-css-js prettify,需要注意的是需要node.js的支持
  3. note pad++,安装JSTool插件进行支持
  4. 其他代码格式化工具,比如"鬼鬼JS"等等

  格式完代码之后,全局搜索"encrypt"找寻加密方式,这个搜索内容不一定就是这个字段,要根据网站内容去设置,碰巧我这边就是"encrypt"这个字段。这里发现网站是使用的AES.CBC的加密方式,因为前端JS进行了函数名的一定的代码混淆,分析后确定AESKeyStr是变量t,偏移量iv是变量r

image-20220118143631869

  我这次调的网站相对来说会比较简单一些,AESKeyStriv是通过标准的JS库生成的,我这里理解的有点类似于SSH登录中通过一段字符串生成AES密钥这个过程,因为生成的字符串是固定的,所以这里就是固定的密钥和偏移量。因此这个网站的keyiv在后面的调试过程中就使用crypto-js自动生成即可。
  找完密钥和偏移量之后就是对接口进行调试,调试哪个接口这个就随意了。我这里选择了登录的接口进行调试。

image-20220118153728514

  通过接口地址在JS文件中找到相应的内容。

image-20220118153830445

  这里就可以看出this.$refs[e].validate(function(e)控制这个接口的数据包,而在这上面一段就是一些变量的加密处理方式。

image-20220118154629199

  打断点进行登录操作也可以看到,上图的这部分就是负责变量内容的加密。

image-20220118154520471

  这里就可以放弃这个断点重新执行,将断点重新设置在3510行,然后在重新执行,其实不看执行也可以,这一块的代码还比较好理解。

handleSubmit: function(e) {
    this.loading = !0;
    var t = this
      , r = this.formInline.user
      , n = CryptoJS.MD5(this.formInline.password).toString()
      , i = this.formInline.vaildcode
      , a = new this.HashMap;
    a.put("loginName", r),
    a.put("password", n),
    a.put("vaildcode", i);
    var o = JSON.stringify(a)
      , s = this.encrypt(o)
      , c = {
        __xml: s,
        __type: "extTrans"
    };
代码 对应内容 含义
r = this.formInline.user username 格式化username并赋值给变量r
n = CryptoJS.MD5(this.formInline.password).toString() password 格式化password并进行MD5加密,转换成字符串类型后赋值给变量n
i = this.formInline.vaildcode vaildcode 验证码
a = new this.HashMap list 等同于其他代码中的字典

  然后通过put()函数将变量rni以键值对的形式传入a中,然后通过JSON.stringify()a按照JSON格式进行格式化,最后对变量o进行加密处理,这就是整体的逻辑。知道这个逻辑就可以将这部分以自己理解的代码重新复写出来。

//脚本编写自Muyu

function test(){

var CryptoJS = require("crypto-js");
var username = "188888888888@163.com";
var password = CryptoJS.MD5("123456").toString();
var vaildcode = "zzzz";
var a = {"loginName":username,"password":password,"vaildcode":vaildcode};
var o = JSON.stringify(a);

// Encrypt
var key = CryptoJS.enc.Utf8.parse("[keystring]");
//console.log(key);
var iv = CryptoJS.enc.Utf8.parse("[keystring]");
var  n = CryptoJS.enc.Utf8.parse(o);

var ciphertext = CryptoJS.AES.encrypt(n, key, { iv: iv, mode: CryptoJS.mode.CBC}).toString();
console.log(ciphertext);

// Decrypt
var bytes  = CryptoJS.AES.decrypt(ciphertext, key, { iv: iv, mode: CryptoJS.mode.CBC});
var originalText = bytes.toString(CryptoJS.enc.Utf8);
//console.log(originalText); // 'my message'
}
test();

  这个脚本仅仅只是针对于登录的这个接口,后面没有验证适用性,不过一般来说一个网站中的接口应该都是同一套加密算法,然后再根据不同的需求更改参数和参数值应该就OK了。

其他内容

  有一点要明确一下,这次调试的算是一个静态加密的网站,难度相对来说比较低,部分网站会使用动态密钥来进行加密,所以这部分网站需要单独去再进行研究,本文中的方法并不能涵盖到所有的站点。
  除了动态密钥加密的网站,还有部分网站会将前端JS进行混淆,不单单是上文中的网站仅对部分函数、变量名进行混淆,在遇到这种网站时,我是不确定还能不能继续做下去,有待研究。
  关于是否要进行动态调试的问题,因为我做的这个网站最后确定是静态加密,并且文章中并没有过多的说动态调试的过程,但是在询问了师父之后,他给出的意见是——不论对方是否是动态加密,都应该进行动调,这样能够保证你自己写的解密加密的脚本的正确性,以及网站加密算法理解的准确性。

改包

  改包这个部分就直接使用上面复写的代码进行针对性更改,然后运行就可以了,这里就不再进行记录了。

写在最后

  之前其实有遇到过前端、数据包内容加密的渗透测试场景,并没有学习前端调试的我就没有办法进行这样的测试,也没能通过这样的项目学习到这部分内容,也主要是之前并没有想到可以这样做。这次也算是把这部分内容学习到了,后面测试内容也可以更全面了。