跳到主要内容

工程实践:MITM 热加载 Hook 函数四大场景解析

· 阅读需 12 分钟
Yak Project
网络安全垂直语言团队

最近有很多用户会询问 MITM 中的热加载部分,对于热加载中的 Hook 函数的使用场景了解不充分,本篇将围绕 Hook 函数定义并且给出具体的小例子帮助用户进行理解。

热加载中的 Hook 函数概念

Yakit MITM 的热加载是允许安全测试人员在不中断代理服务的情况下,动态加载、更新和卸载各种安全功能模块。

Yakit MITM 热加载的工作原理基于事件驱动模型,系统在 HTTP 请求和响应的各个关键处理阶段设置了多个"钩子"( Hook 点),当流量经过这些 Hook 点时,系统会触发相应的事件,并执行已加载的处理函数。

根据使用的优先级,给出下表:

在 Yakit MITM 框架里,有三个基本角色,理解清楚它们后,再看各个 Hook 就很直观:

  • MITM 客户端:指把流量导向 Yakit 的那一端,通常是浏览器、移动 App、自动化脚本等。它们把 HTTP/HTTPS 请求发到 Yakit 监听的地址,而不是直接连目标站点,为了方便理解,后文统一称为浏览器。
  • MITM 代理(Yakit):运行在本机或中间服务器的中间人代理。它接收客户端请求、可选地修改或丢弃,再转发给真正的目标服务器。同样地,在收回目标响应后也可以再改动,然后回写给客户端。所有 Hook 都是挂在这个代理内部的事件节点上。
  • 目标服务器:浏览器真正想访问的后端服务,例如 Web 应用的 API、站点服务器等。只有当请求穿过 Yakit 的最后一道关卡后,才会发到这里,响应也是先返回给 Yakit,再由 Yakit 交给浏览器。

Hook 函数场景介绍

Yakit MITM 把 Hook 函数分成三类,分别为:

1、HTTP请求响应劫持类**:同步串行执行的可以动态修改请求数据包与响应,直接影响通信过程;

2、镜像流量类:这类钩子函数都以 mirror... 开头,这类函数一般不涉及流量的修改,在这里编写代码一般只能观察和分析流量,无法对通信产生直接影响。

3、数据包存储处理类:这个阶段的函数只有一个: hijackSaveHTTPFlow,这个钩子函数目的主要是允许用户自定义在流量存储到 History 中的过程

HTTP 请求响应劫持

hijackHTTPRequest - 劫持 HTTP 请求触发时机:

浏览器发起请求后,Yakit 准备将请求发送到目标服务器之前触发。这是最主要的请求拦截和修改点,等同于用代码的方式,完成 Yakit MITM UI 界面中的 "手动劫持请求" 的功能。

使用场景

例如,前端把订单金额、折扣、余额变动这些敏感字段写死在 JS 校验里,浏览器准备提交正常金额时抓包,在 hijackHTTPRequest 把金额改成负数,钱包余额增加。

hijackHTTPResponse / hijackHTTPResponseEx

- 劫持 HTTP 响应触发时机:

在 Yakit 从目标服务器接收到响应后,准备将响应发送回客户端之前触发。这是最主要的响应拦截和修改点。等同于用代码的方式,完成 Yakit MITM UI 界面中的 "手动劫持响应" 的功能。

使用场景

在响应回到浏览器前修改内容,比如某个管理后台的响应包里自带一段 js:先 alert("未授权访问"),然后立刻 window.location.href = "/login"。此时通过 hijackHTTPResponse/hijackHTTPResponseEx里把这段脚本删掉或改成空函数,就能阻止浏览器弹窗和跳转,从而保留在当前页面继续观察 DOM、以及后台页面。

beforeRequest/afterRequest

beforeRequest 和 afterRequest 的概念本身并不难理解,但是有很多初次使用的小伙伴,不太理解这里的 "Request" 概念,容易把这里的 "Request" 和单纯请求发包搞混淆。

Yakit MITM复用了 Burp/Zap 那类代理的传统命名习惯:从MITM 引擎视角出发,请求链路是“先收(request),后回(response)”。

beforeRequest表示请求出站前的最后时机,afterRequest则是“处理完请求以后”——也就是在把响应发还给客户端之前的末尾节点。

换句话说,在这些中间人代理的语境里,它名字里的 “Request” 不光指“浏览器发出去的那段数据”,而是指“浏览器发出请求 → 代理转发 → 代理拿到服务器响应”这一整段往返流程。我们在下面对这两个 Hook 函数给出系统性的总结。

beforeRequest - 请求最终修改Hook触发时机:

在请求即将发送到目标服务器的最后时刻被调用,位于 hijackHTTPRequest 处理完成之后。这是对请求进行最终修改的机会,确保所有修改都能在请求真正发送前生效。

功能说明:

允许用户对经过 hijackHTTPRequest 处理后的请求进行最后一次检查和修改。该函数同时提供原始请求( oreq )和经过 hijack 修改后的请求( req ),用户可以基于这两个版本的对比来决定是否需要进一步修改。

与 hijackHTTPRequest 不同的是,beforeRequest 通过返回值来提交修改后的请求,如果没有返回值则使用 hijack 阶段修改后的请求。

使用说明:

这个钩子函数与其他函数略有区别,它可以直接获取到 hijackHTTPRequest 处理后的结果,允许用户统一对所有请求做最终调整。重要特性:这一步的修改是对用户不可见的 - 如果用户触发手动劫持,在劫持界面看到的内容是 beforeRequest 处理之前的状态,而 beforeRequest 的修改会在用户确认发送后才被应用。这意味着 beforeRequest 中的修改不会影响用户劫持时的可视化界面。

使用场景:

常用于添加全局性的请求头、控制重放频率、统一的身份认证信息、或者不希望用户感知到的底层调整,确保请求的完整性和正确性。

afterRequest - 响应最终修改Hook触发时机:**

在响应即将发送回客户端的最后时刻被调用。位于:

hijackHTTPResponse/hijackHTTPResponseEx 处理完成之后。这是对响应进行最终修改的机会,确保所有修改都能在响应真正返回给客户端前生效。

功能说明:

允许用户对经过:

hijackHTTPResponse/hijackHTTPResponseEx 处理后的响应进行最后一次检查和修改。该函数提供了完整的上下文信息,包括原始请求 (oreq)、hijack 修改后的请求(req)、原始响应 (orsp) 和 hijack 修改后的响应 (rsp),用户可以基于这些信息来决定是否需要进一步修改响应。

与 hijackHTTPResponse 不同的是,afterRequest 通过返回值来提交修改后的响应,如果没有返回值则使用 hijack 阶段修改后的响应。

使用说明:

这个钩子函数是响应处理链的最后一环,它可以直接获取到 hijackHTTPResponse 处理后的结果,允许用户统一对所有响应做最终调整。重要特性:这一步的修改同样对用户不可见 - 如果用户触发手动劫持查看响应,在劫持界面看到的内容是 afterRequest 处理之前的状态,而 afterRequest 的修改会在响应最终返回客户端前才被应用。这意味着 afterRequest 中的修改不会影响用户劫持时的响应查看界面。

使用场景:常用于添加全局性的响应头、统一的安全策略、内容过滤、或者不希望用户感知到的底层调整,确保响应的完整性和安全性。

镜像流量

mirrorHTTPFlow -全流量镜像触发时机

每个 HTTP 请求响应完成后都会触发,无论流量是否被过滤规则拦截。

使用场景

除非用户知道自己在做什么,否则不推荐频繁使用,因为包含大量无关流量(如广告、统计、CDN 资源等),会造成性能开销和数据冗余。

mirrorFilteredHTTPFlow**- 过滤后流量镜像触发时机**:

只有通过 Yakit 过滤规则的 HTTP 流量才会触发。

使用场景

典型场景例如:只关注 api 接口(/api)请求,通过配置过滤器排除.css.js.png等静态资源以及广告域名后,该函数只会捕获真正有价值的业务请求,随后按需处理。

mirrorNewWebsite - 新网站发现镜像触发时机:

当发现一个全新的网站域名时触发,每个域名在整个会话中只会触发一次。

使用场景:

首次遇到新域名时拉起初始化流程,例如自动爆破目录、统计网站等。

**详细解读:**当访问以下 URL 序列时:

https://192.168.1.100:8080/abc/
https://192.168.1.100:8080/abc/12
https://192.168.1.100:8080/abc/13
https://localhost:8080/abc/14?a=2&&c=1

mirrorNewWebsite 只会触发两次调用:

第1次:

首次发现域名 192.168.1.100:8080 时触发;

第2次:

首次发现域名 localhost:8080 时触发;

mirrorNewWebsitePath - 新路径发现镜像触发时机:

当在已知网站上发现新的 URL 路径时触发,相同路径在同一域名下只会触发一次。

使用场景:

该函数用于路径和目录发现。例如,在example.com域名下,首次访问/admin/login/api/users/upload/files等路径时会分别触发一次。这对于目录爆破验证、API 端点发现、以及构建网站结构图谱极其有用,帮助安全研究人员快速了解目标应用的功能模块分布。

详细解读:当访问以下 URL 序列时:

https://192.168.1.100:8080/abc/
https://192.168.1.100:8080/abc/12
https://192.168.1.100:8080/abc/13
https://192.168.1.100:8080/abc/14
https://192.168.1.100:8080/abc/14?a=1
https://192.168.1.100:8080/abc/14?a=2
https://192.168.1.100:8080/abc/14?a=2&&c=1
https://localhost:8080/abc/14?a=2&&c=1

mirrorNewWebsitePath 会触发5 次调用:

第1次

在域名 192.168.1.100:8080 下首次发现路径 /abc/ 时触发。

第2次

在域名 192.168.1.100:8080 下首次发现路径 /abc/12 时触发。

第3次

在域名 192.168.1.100:8080 下首次发现路径 /abc/13 时触发。

第4次

在域名 192.168.1.100:8080 下首次发现路径 /abc/14 时触发。(注意:后续带有参数 ?a=1, ?a=2, ?a=2&&c=1 的请求访问的仍然是 /abc/14 这个路径,因此不会重复触发)。

第5次

在域名 localhost:8080 下首次发现路径 /abc/14 时触发。(虽然 /abc/14 路径在 192.168.1.100:8080 域名下已出现过,但对于新域名 localhost:8080 来说,这是首次发现该路径,因此会触发)。

mirrorNewWebsitePathParams - 新参数组合发现镜像触发时机:

当发现相同路径下的新参数组合时触发,相同参数结构的请求只会触发一次。

使用场景:

该函数专注于参数级别的去重分析。例如,对于路径 /api/user,首次遇到 ?id=123&type=admin 这种参数结构时会触发,但后续的 ?id=456&type=user(相同参数名但不同值)不会重复触发。这对于 API 参数发现、SQL 注入点识别、以及参数污染测试的准备工作非常重要,能够帮助研究人员快速识别所有可能的攻击面而不被重复的参数值干扰。

详细解读:当访问以下 URL 序列时:

https://192.168.1.100:8080/abc/https://192.168.1.100:8080/abc/12https://192.168.1.100:8080/abc/13https://192.168.1.100:8080/abc/14https://192.168.1.100:8080/abc/14?a=1https://192.168.1.100:8080/abc/14?a=1  # 重复https://192.168.1.100:8080/abc/14?a=2  # 结构同上https://192.168.1.100:8080/abc/14?a=2&&c=1https://192.168.1.100:8080/abc/14?a=2&&c=1  # 重复https://localhost:8080/abc/14?a=2&&c=1mirrorNewWebsitePathParams 会触发 7 次调用:

数据包存储

hijackSaveHTTPFlow - 流量存储前处理触发时机

在 HTTP 请求响应完整处理完成后,即将存储到 History 数据库之前触发。这是流量处理链的最后一个环节,所有的请求响应修改都已完成。

功能说明

允许用户在流量入库前对流量数据进行最终的处理和标记,包括修改请求响应内容、添加标签、设置颜色标记。这个函数接收完整的流量结构体,用户可以访问和修改入库(保存到数据库)流量的所有属性。

流量写入数据库之前打标签或删改字段,例如给疑似敏感接口标记“需复测”,或脱敏存储,或明文查看数据包。

使用场景:该函数主要用于流量的后处理和分类标记,常见应用场景包括:

  • 信息标记:检测包含敏感关键字的流量并染色标记
  • 数据解密存储:对加密的通信内容进行解密后存储明文
  • 数据脱敏存储:对数据包进行脱敏处理
  • 流量过滤:根据特定条件丢弃无关流量,减少存储空间占用
  • 自动化标签:根据 URL 模式、响应状态等自动添加分类标签

组合场景:处理前端加密通信

在现代 Web 应用中,前端 JavaScript 经常对敏感数据进行加密后再与服务器通信,这导致 Yakit MITM 直接抓取到的是加密后的密文,用户无法直观地查看和修改请求内容。通过巧妙运用 hijackHTTPRequest beforeRequest 的组合,可以完美解决这一问题,同时还可以使用 hijackSaveHTTPFlow 在 Yakit 中明文查看数据包,具体实战场景,可以翻阅下方文章:

↓ 综合场景靶场实战技巧 ↓Yak,公众号:Yak Project渗透测试高级技巧(三):被前端加密后的漏洞测试


本文首发于 Yak Project 公众号,阅读原文