JavaScript逆向之通过 Tampermonkey 快速查找 JS加密入口
2022-11-3 08:22:37 Author: Z2O安全攻防(查看原文) 阅读量:36 收藏

免责声明

本文仅用于技术讨论与学习,利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。
只供对已授权的目标使用测试,对未授权目标的测试作者不承担责任,均由使用本人自行承担。

文章正文

本节我们就来介绍一下Tampermonkey插件的使用方法,并结合一个实际案例,介绍下Tampermonkey插件在 JavaScript 逆向分析中的使用。

创建Tampermonkey调试环境

接下来我们创建本地开发功能,借助 Visual Studio Code 实现本地开发.

如上图所介绍的,我们只需要允许 Tampermonkey 插件访问文件网址即可实现本地 js 文件的访问了,然后再通过 @require 引入后,即可实现本地的插件开发了.这里详细介绍一下具体过程.

已经建立好了一个调试环境

并引入了jquery

// ==UserScript==
// @name         tiaoshi
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       You
// @match        https://fofa.info/result*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @require file:///C:\Users\98483\Desktop\1.js
// @require https://code.jquery.com/jquery-2.1.4.min.js
// @grant        none
// ==/UserScript==

安装 Tampermonkey 插件

直接在谷歌应用商店或者edge的商店下载就行

设置 Tampermonkey,使其允许访问文件网址

按照上述步骤打开 Tampermonkey 插件的详细信息

在详细信息中勾选允许访问文件URL

创建本地文件工程

这里演示在 D:\demo作为工程的根目录,在根目录下新建一个 JavaScript 脚本,并命名为 demo01.js,此时这个文件的绝对路径为 D:\demo\demo01.js,对应的文件 URL 为 file:///D:\demo\demo01.js , 这里要留意一下,待会儿我们会用到这个 URL.

在 Visual Studio Code打开这个目录,并打开 demo01.js,编辑如下内容:

(function () {
    console.log("this hello comes from tampermonkey!")
})();

在 Tampermonkey 中创建用户脚本

点击 Tampermonkey 图标,执行以下步骤

编辑相应的内容:

这里最重要的一点是在头部配置中增加一行,表示将从本地引入该文件.

// @require file:///D:\demo\demo01.js

在编辑完成后,使用快捷键 Ctrl + S 保存编辑内容.

运行执行脚本

到此为止,你已经完成了本地开发脚本所需要配置的全部内容,接下来就可以在你要开发的对应页面进行脚本的开发了,这里以百度为例,比如此时我们代开百度页面,然后打开控制台,就会看到我们刚刚开发的代码的运行结果:

实战 JavaScript 逆向

下面我们来通过一个简单的 JavaScript 逆向案例来演示一下 Tampermonkey 的作用。

在 JavaScript 逆向的时候,我们经常会需要追踪某些方法的堆栈调用情况,但很多情况下,一些 JavaScript 的变量或者方法名经过混淆之后是非常难以捕捉的。

但如果我们能掌握一定的门路或规律,辅助以 Tampermonkey,就可以更轻松地找出一些 JavaScript 方法的断点位置,从而加速逆向过程。

在逆向过程中,一个非常典型的技术就是 Hook 技术。Hook 技术中文又叫做钩子技术,它就是在程序运行的过程中,对其中的某个方法进行重写,在原先的方法前后加入我们自定义的代码。相当于在系统没有调用该函数之前,钩子程序就先捕获该消息,钩子函数先得到控制权,这时钩子函数既可以加工处理(改变)该函数的执行行为,还可以强制结束消息的传递。

如果觉得比较抽象,看完下面的 Hook 案例就懂了。

例如,我们接下来使用 Tampermonkey 实现对某个 JavaScript 方法的 Hook,轻松找到某个方法执行的位置,从而快速定位到逆向入口。

接下来我们来这么一个简单的网站:https://scrape.cuiqingcai.com/login1,这个网站结构非常简单,就是一个用户名密码登录,但是不同的是,点击 Submit 的时候,表单提交 POST 的内容并不是单纯的用户名和密码,而是一个加密后的 Token。

页面长这样:

【腾讯云】境外1核2G服务器低至2折,半价续费券限量免费领取!

 https://curl.qcloud.com/MSIFpJMg

我们随便输入用户名密码,点击登录按钮,观察一下网络请求的变化。

可以看到如下结果:

看到实际上控制台提交了一个 POST 请求,内容为:

{"token":"eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJhZG1pbiJ9"}

嗯,确实,没有诸如 username 和 password 的内容了,那这究竟是个啥?我要是做爬虫的话?怎么模拟登录呢?

模拟登录的前提当然就是找到当前 token 生成的逻辑了,那么问题来了,到底这个 token 和用户名、密码什么关系呢?我们怎么来找寻其中的蛛丝马迹呢?

这里我们就可能思考了,本身输入的是用户名和密码,但是提交的时候却变成了一个 token,经过观察 token 的内容还很像 Base64 编码。这就代表,网站可能首先将用户名密码混为了一个新的字符串,然后最后经过了一次 Base64 编码,最后将其赋值为 token 来提交了。所以,初步观察我们可以得出这么多信息。

好,那就来验证下吧,看看网站 JavaScript 代码里面是咋实现的。

接下来我们看看网站的源码,打开 Sources 面板,好家伙,看起来都是 Webpack 打包之后的内容,经过了一些混淆,类似结果如下:

这么多混淆代码,总不能一点点扒着看吧?这得找到猴年马月?那么遇到这种情形,这怎么去找 token 的生成位置呢?

解决方法其实有两种。

Ajax 断点

由于这个请求正好是一个 Ajax 请求,所以我们可以添加一个 XHR 断点监听,把 POST 的网址加到断点监听上面去,在 Sources 面板右侧添加这么一个 XHR 断点,如图所示:

这时候如果我们再次点击登录按钮的时候,正好发起一次 Ajax 请求,就进入到断点了,然后再看堆栈信息就可以一步步找到编码的入口了。

点击 Submit 之后,页面就进入了 Debug 状态停下来了,结果如下:

一步步找,我们最后其实可以找到入口其实是在 onSubmit 方法这里。但实际上,我们观察到,这里的断点的栈顶还会包括了一些 async Promise 等无关的内容,而我们真正想找的是用户名和密码经过处理,再进行 Base64 编码的地方,这些请求的调用实际上和我们找寻的入口是没有很大的关系的。

另外,如果我们想找的入口位置并不伴随这一次 Ajax 请求,这个方法就没法用了。

这个方法是奏效的,但是我们先不讲 onSubmit 方法里面究竟是什么逻辑,下一个方法再来讲。

Hook Function

所以,这里介绍第二种可以快速定位入口的方法,那就是使用 Tampermonkey 自定义 JavaScript 实现某个 JavaScript 方法的 Hook。Hook 哪里呢?最明显的,Hook Base64 编码的位置就好了。

那么这里就涉及到一个小知识点,JavaScript 里面的 Base64 编码是怎么实现的。没错就是 btoa 方法,所以说,我们来 Hook btoa 方法就好了。

好,这里我们新建一个 Tampermonkey 脚本,内容如下:

// ==UserScript==
// @name         HookBase64
// @namespace    https://scrape.cuiqingcai.com/
// @version      0.1
// @description  Hook Base64 encode function
// @author       Germey
// @match       https://scrape.cuiqingcai.com/login1
// @grant        none
// ==/UserScript==
(function () {
    'use strict'
    function hook(object, attr) {
        var func = object\[attr\]
        object\[attr\] = function () {
            console.log('hooked', object, attr)
            var ret = func.apply(object, arguments)
            debugger
            return ret
        }
    }
    hook(window, 'btoa')
})()

首先我们定义了一些 UserScript Header,包括 @name、@match 等等,这里比较重要的就是 @name,表示脚本名称;另外一个就是 @match,代表脚本生效的网址。

脚本的内容如上所示。我们定义了一个 hook 方法,传入 object 和 attr 参数,意思就是 Hook object 对象的 attr 参数。例如我们如果想 Hook 一个 alert 方法,那就把 object 设置为 window,把 attr 设置为 alert 字符串。这里我们想要 Hook Base64 的编码方法,在 JavaScript 中,Based64 编码是用 btoa 方法实现的,那么这里我们就只需要 Hook window 对象的 btoa 方法就好了。

那么 Hook 是怎么实现的呢?我们来看下,首先一句 var func = object[attr],相当于我们先把它赋值为一个变量,我们调用 func 方法就可以实现和原来相同的功能。接着,我们再直接改写这个方法的定义,直接改写 object[attr],将其改写成一个新的方法,在新的方法中,通过 func.apply 方法又重新调用了原来的方法。这样我们就可以保证,前后方法的执行效果是不受什么影响的,之前这个方法该干啥就还是干啥的。但是和之前不同的是,我们自定义方法之后,现在可以在 func 方法执行的前后,再加入自己的代码,如 console.log 将信息输出到控制台,如 debugger 进入断点等等。这个过程中,我们先临时保存下来了 func 方法,然后定义一个新的方法,接管程序控制权,在其中自定义我们想要的实现,同时在新的方法里面再重新调回 func 方法,保证前后结果是不受影响的。所以,我们达到了在不影响原有方法效果的前提下,可以实现在方法的前后实现自定义的功能,就是 Hook 的过程。

最后,我们调用 hook 方法,传入 window 对象和 btoa 字符串,保存。

接下来刷新下页面,这时候我们就可以看到这个脚本就在当前页面生效了,如图所示。

接下来,打开控制台,切换到 Sources 面板,这时候我们可以看到站点下面的资源多了一个叫做 Tampermonkey 的目录,展开之后,发现就是我们刚刚自定义的脚本。

然后输入用户名、密码,点击提交。发现成功进入了断点模式停下来了,代码就卡在了我们自定义的 debugger 这一行代码的位置,如下图所示。

成功 Hook 住了,这说明 JavaScript 代码在执行过程中调用到了 btoa 方法。

看下控制台,如下图所示。

【腾讯云】预热专享1888元早鸟券一键领取,云服务器等爆品抢先购低至4.2元/月

https://url.cn/mOancOap

这里也输出了 window 对象和 btoa 方法,验证正确。

这样,我们就顺利找到了 Base64 编码操作这个路口,然后看看堆栈信息,也已经不会出现 async、Promise 这样的调用,很清晰地呈现了 btoa 方法逐层调用的过程,非常清晰明了了,如图所示。

各个底层的 encode 方法略过,这样我们也非常顺利地找到了 onSubmit 方法里面的处理逻辑:

onSubmit: function() {
  var e = c.encode(JSON.stringify(this.form));
  this.$http.post(a\["a"\].state.url.root, {
    token: e
  }).then((function(e) {
      console.log("data", e)
  }))
}

仔细看看,encode 方法其实就是调用了一下 btoa 方法,就是一个 Base64 编码的过程。

另外堆栈信息中可以清晰地看到 Hook 的方法在执行前传入的参数值,即 arguments。另外执行的之后的结果 ret 也可以轻松地找到了,如图所示:

所以,现在我们知道了 token 和用户名、密码是什么关系了吧。

这里一目了然了,就是对表单进行了 JSON 序列化,然后调用了 encode 也就是 btoa 方法,并赋值为了 token,入口顺利解开。后面,我们只需要模拟这个过程就 OK 了。

所以,我们通过 Tampermonkey 自定义 JavaScript 脚本的方式实现了某个方法调用的 Hook,使得我们快速能定位到加密入口的位置,非常方便。

以后如果观察出来了一些门道,可以多使用这种方法来尝试,如 Hook encode 方法、decode 方法、stringify 方法、log 方法、alert 方法等等,简单而又高效。

以上便是通过 Tampermonkey 实现简单 Hook 的基础操作,当然这个仅仅是一个常见的基础案例,不过从中我们也可以总结出一些 Hook 的基本门道。

技术交流

交流群

关注公众号回复“加群”,添加Z2OBot 小K自动拉你加入Z2O安全攻防交流群分享更多好东西。

知识星球

星球不定时更新最新漏洞复现,手把手教你,同时不定时更新POC、内外网渗透测试骚操作。涉及方向包括Web渗透、免杀绕过、内网攻防、代码审计、应急响应、云安全等

关注我们

关注福利:

回复“app" 获取  app渗透和app抓包教程

回复“渗透字典" 获取 针对一些字典重新划分处理,收集了几个密码管理字典生成器用来扩展更多字典的仓库。

回复“书籍" 获取 网络安全相关经典书籍电子版pdf

往期文章:

CVE-2022-30190 Follina Office RCE分析【附自定义word模板POC】

邮箱附件钓鱼常用技法

入侵痕迹清理

一篇文章带你学会容器逃逸
spring cloud function spel表达式注入RCE复现
利用burp精准定位攻击者
从此 Typora 代码块有了颜色
不会写免杀也能轻松过defender上线CS

文章来源: http://mp.weixin.qq.com/s?__biz=Mzg2ODYxMzY3OQ==&mid=2247489788&idx=1&sn=5a981c1e2a582eca86bbaf066db78db5&chksm=cea8f3bcf9df7aaac3556f164094f6c01bcc0217e64dfcdb7e64452439a06f400721d579f42a#rd
如有侵权请联系:admin#unsafe.sh