前言
Yakit
作为基于Yaklang
的安全测试平台,其Web Fuzzer
模块凭借Fuzz Tag
机制已实现基础参数爆破、编码变换等核心功能。
然而,面对复杂业务逻辑(如动态Token校验、多级编码叠加、非对称加密验签)时,传统标签组合常因逻辑嵌套导致编码失效或性能瓶颈。Yakit热加载
模块的诞生,可以很好且有效的解决这一问题。热加载中内置了多个魔术函数(mirrorHTTPFlow
、hijackHTTPRequest
、hijackSaveHTTPFlow
….),通过对这些热加载函数进行重写,可以实现复杂业务逻辑的处理。
下面列出yakit热加载
中的代码模版:
mirrorHTTPFlow = func(isHttps /*bool*/, url /*string*/, req /*[]byte*/, rsp /*[]byte*/, body /*[]byte*/) {
}
mirrorFilteredHTTPFlow = func(isHttps /*bool*/, url /*string*/, req /*[]byte*/, rsp /*[]byte*/, body /*[]byte*/) {
}
mirrorNewWebsite = func(isHttps /*bool*/, url /*string*/, req /*[]byte*/, rsp /*[]byte*/, body /*[]byte*/) {
}
mirrorNewWebsitePath = func(isHttps /*bool*/, url /*string*/, req /*[]byte*/, rsp /*[]byte*/, body /*[]byte*/) {
}
mirrorNewWebsitePathParams = func(isHttps /*bool*/, url /*string*/, req /*[]byte*/, rsp /*[]byte*/, body /*[]byte*/) {
}
hijackHTTPRequest = func(isHttps, url, req, forward /*func(modifiedRequest []byte)*/, drop ) {
}
hijackHTTPResponse = func(isHttps /*bool*/, url /*string*/, rsp /*[]byte*/, forward /*func(modifiedResponse []byte)*/, drop ) { }
hijackHTTPResponseEx = func(isHttps /*bool*/, url /*string*/, req/*[]byte*/, rsp /*[]byte*/, forward /*func(modifiedResponse []byte)*/, drop ) { }
beforeRequest = func(ishttps /*bool*/, oreq /*[]byte*/, req/*[]byte*/){ }
afterRequest = func(ishttps, oreq/*原始请求*/ ,req/*hiajck修改之后的请求*/ ,orsp/*原始响应*/ ,rsp/*hijack修改后的响应*/){ }
hijackSaveHTTPFlow = func(flow /* *yakit.HTTPFlow */, modify /* func(modified *yakit.HTTPFlow) */, drop) { }
|
模块详解
流量劫持
// 请求 -> hijackHTTPRequest -> 前端劫持 -> beforeRequest -> 服务器响应 -> hijackResponse -> 后端劫持 -> afterRequest -> 客户端看到的响应 -> hijackSaveHTTPFlow
|
Yakit
的热加载机制通过多模块协同实现请求-响应全链路劫持与处理,其核心处理流程可细分为以下阶段(按时间顺序排列)
请求处理阶段
hijackRequest劫持(手动劫持场景)
// hijackHTTPRequest 会在过滤后的请求到达Yakit MITM前被调用,可以通过该函数提前将请求修改或丢弃 // isHttps 请求是否为https请求 // url 网站URL // req 请求 // forward(req) 提交修改后的请求,如果未被调用,则使用原始的请求 // drop() 丢弃请求 hijackHTTPRequest = func(isHttps, url, req, forward /*func(modifiedRequest []byte)*/, drop /*func()*/) { // Example: // if str.Contains(string(req), "/should_modify") { // modified = str.ReplaceAll(string(req), "/should_modify", "/modified") // forward(poc.FixHTTPRequest(modified)) // }
// if str.Contains(string(req), "/drop") { // drop() // } }
|
hijackHTTPRequest
相当于请求预处理,可以在客户端的流量到达yakit MITM
之前被调用。它处于请求处理流程的早期阶段,可能用于底层流量的修改或丢弃请求。hijackHTTP模块在请求处理流程中处于客户端与服务端之间的Yakit MITM代理阶段。这可能意味着它用于全局的请求过滤或修改,比如统一添加请求头、修改特定路径,或者根据条件丢弃请求。
使用的场景如下:
- 请求修改
- 针对请求包存在加密的场景,可以使用该模块,对请求数据包做解密,使其明文展示到mitm中
- 替换特定参数,如针对数据包添加固定请求头等(也可以在
beforeRequest
实现)
- 请求丢弃
其实总结一下,hijackHTTPRequest
有点类似于burp的Interpect
模块,可以直接修改请求并将修改后的请求发送到服务端。

beforeRequest预处理(自动劫持场景)
// beforeRequest 会在请求到达服务器之前被调用,可以通过该函数对请求做最后一次修改 // isHttps 请求是否为https请求 // oreq 原始请求 // req hijackRequest修改后的请求 // 返回值: 修改后的请求,如果没有返回值则使用hijackRequest修改后的请求 beforeRequest = func(ishttps /*bool*/, oreq /*[]byte*/, req/*[]byte*/){ // Example: // if str.Contains(string(req), "凝聚磅礴的中国文学力量") { // modified = poc.FixHTTPRequest(str.ReplaceAll(req, "凝聚磅礴的中国文学力量", "AAAAAAAAAAAAAAAA")) // return []byte(modified) // } }
|
通俗来说,就是在流量离开yakit
之前,还可以对流量做修改,适用场景:
- 参数加密:
AES/DES/Base64
等算法处理请求体
- 签名生成:动态基于时间戳/请求体/请求url生成对应签名
- 流量标记:可以添加标识符,用于后续流量分析
服务器交互阶段
hijackResponse响应处理
// hijackHTTPResponse 会在过滤后的响应到达Yakit MITM前被调用,可以通过该函数提前将响应修改或丢弃 // isHttps 请求是否为https请求 // url 网站URL // rsp 响应 // forward(req) 提交修改后的响应,如果未被调用,则使用原始的响应 // drop() 丢弃响应 hijackHTTPResponse = func(isHttps /*bool*/, url /*string*/, rsp /*[]byte*/, forward /*func(modifiedResponse []byte)*/, drop /*func()*/) { // Example: // if str.Contains(string(rsp), "凝聚磅礴的中国文学力量") { // modified = poc.FixHTTPResponse(str.ReplaceAll(rsp, "凝聚磅礴的中国文学力量", "AAAAAAAAAAAAAAAA")) // forward(modified) // } }
|
主要用来动态修改服务器返回的内容,在响应到达Yakit MITM
前被调用。适用场景:
响应处理阶段
afterRequest后处理
// afterRequest 会在响应到达客户端之前被调用,可以通过该函数对响应做最后一次修改 // isHttps 请求是否为https请求 // oreq 原始请求 // req hijackRequest修改后的请求 // orsp 原始响应 // rsp hijackHTTPResponse/hijackHTTPResponseEx修改后的响应 // 返回值: 修改后的响应,如果没有返回值则使用hijackHTTPResponse/hijackHTTPResponseEx修改后的响应 afterRequest = func(ishttps, oreq/*原始请求*/ ,req/*hiajck修改之后的请求*/ ,orsp/*原始响应*/ ,rsp/*hijack修改后的响应*/){ // Example: // if str.Contains(string(rsp), "凝聚磅礴的中国文学力量") { // modified = poc.FixHTTPRequest(str.ReplaceAll(rsp, "凝聚磅礴的中国文学力量", "AAAAAAAAAAAAAAAA")) // return []byte(modified) // } }
|
类比beforeRequest
,主要是在响应到达客户端之前被调用。使用场景:
流量展示
hijackSaveHTTPFlow
// hijackSaveHTTPFlow 会在流量被存储到数据库前被调用,可以通过该函数对入库前的流量进行修改,例如修改请求/响应,添加tag/染色等 // flow 流量结构体,可以通过鼠标悬浮提示查看其拥有的字段并对其进行修改 // modify(modified) 提交修改后的流量结构体,如果未被调用,则使用原始的流量结构体 // drop() 丢弃流量 hijackSaveHTTPFlow = func(flow /* *yakit.HTTPFlow */, modify /* func(modified *yakit.HTTPFlow) */, drop/* func() */) { // flow.Request 转义后的请求 // flow.Response 转义后的响应 // 对于转义后的请求和响应,需要通过以下方式拿到原始的请求/响应 // req = str.Unquote(flow.Request)~ // rsp = str.Unquote(flow.Response)~ // 对于修改后的请求和响应,需要通过以下方式再将其转义回去 // flow.Request = str.Quote(req) // flow.Response = str.Quote(rsp) // flow.AddTag("tag") // 添加tag // flow.Red() // 染红色 // // Example: // responseBytes, _ = codec.StrconvUnquote(flow.Response) // if str.MatchAnyOfRegexp(responseBytes, "/admin/", "accessKey") { // flow.Red(); // modify(flow) // } }
|
hijackSaveHTTPFlow
会在流量被存储到数据库前被调用。他本身不属于流量劫持的某个阶段,所以通过该函数对入库前的数据包修改,既可以显示数据包为明文也不会影响请求(不过这快只能看不能修改,因为不在流量劫持阶段)。适用场景:
数据包明文显示
适用于查看明文数据但不做修改的需求。
添加tag/染色
可以对数据包进行打标签操作,方便识别
流量镜像
这些模块在平常渗透测试中用到的比较少,这款只是简单列一下:
// 请求 -> hijackHTTPRequest -> 前端劫持 -> beforeRequest -> 服务器响应 -> hijackResponse -> 后端劫持 -> afterRequest -> 客户端看到的响应 -> hijackSaveHTTPFlow
// mirrorHTTPFlow 会镜像所有的流量到这里,包括被过滤器过滤的请求 // !!! 一般插件不要实现这个接口 // isHttps 请求是否为https请求 // url 网站URL // req 请求 // rsp 响应 // body 响应体 mirrorHTTPFlow = func(isHttps /*bool*/, url /*string*/, req /*[]byte*/, rsp /*[]byte*/, body /*[]byte*/) {
}
// mirrorFilteredHTTPFlow 会镜像过滤后的流量到这里 // isHttps 请求是否为https请求 // url 网站URL // req 请求 // rsp 响应 // body 响应体 mirrorFilteredHTTPFlow = func(isHttps /*bool*/, url /*string*/, req /*[]byte*/, rsp /*[]byte*/, body /*[]byte*/) {
}
// mirrorNewWebsite 会镜像过滤后的流量到这里,每个网站只会触发一次 // isHttps 请求是否为https请求 // url 网站URL // req 请求 // rsp 响应 // body 响应体 mirrorNewWebsite = func(isHttps /*bool*/, url /*string*/, req /*[]byte*/, rsp /*[]byte*/, body /*[]byte*/) {
}
// mirrorNewWebsitePath 会镜像过滤后的流量到这里,每个网站的相同路径只会触发一次 // isHttps 请求是否为https请求 // url 网站URL // req 请求 // rsp 响应 // body 响应体 mirrorNewWebsitePath = func(isHttps /*bool*/, url /*string*/, req /*[]byte*/, rsp /*[]byte*/, body /*[]byte*/) {
}
// mirrorNewWebsitePathParams 会镜像过滤后的流量到这里,每个网站的参数相同的请求只会触发一次 // isHttps 请求是否为https请求 // url 网站URL // req 请求 // rsp 响应 // body 响应体 mirrorNewWebsitePathParams = func(isHttps /*bool*/, url /*string*/, req /*[]byte*/, rsp /*[]byte*/, body /*[]byte*/) {
}
|
mirrorHTTPFlow
:镜像全流量
mirrorFilteredHTTPFlow
:镜像过滤流量
mirrorNewWebsite
:每新出现一个网站,这个网站的第一个请求,将会在这里被调用!
mirrorNewWebsitePath
:每新出现一个网站路径,关于这个网站路径的第一个请求,将会在这里被传入回调
mirrorNewWebsitePathParams
:每新出现一个网站路径且带有一些参数,参数通过常见位置和参数名去重,去重的第一个 HTTPFlow
在这里被调用
总结
其实语法和使用这块都殊途同归,重要的是要搞清楚各个函数在什么时候使用最合理。