webfuzzer-hotpatch について
webfuzzer-hotpatch は、Yakit Web Fuzzer の単一タブのリクエスト処理に使う YAK ホットパッチスキルです。リクエストとレスポンスの書き換え、再試行、失敗判定、パラメーター抽出、モック応答に加え、並行処理と自己テストの方法も説明します。
主な機能
- 016 種類の Web Fuzzer フックを説明
- 02リクエストの書き換えとレスポンスの復号
- 03再試行制御と独自の失敗判定
- 04パラメーター抽出と fuzztag 対応
- 05暗号化レシピと自己テスト方法
- 06並行状態の扱い方を説明
活用シーン
- 01Fuzzer で平文リクエストを暗号化・署名
- 02レスポンスを復号して平文を確認
- 03業務応答に応じた再試行や失敗判定
- 04抽出パラメーターを使った複数ステップのテスト
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ yaklang/ yak-skills --skill webfuzzer-hotpatchインストール後、Claude Code で/webfuzzer-hotpatchを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
examples/after-request.yakbefore-request.yakcombo-aescbc-plaintext-roundtrip.yakcombo-rsa-aesgcm-hybrid.yakcombo-sign-and-encrypt.yakcustom-failure-checker.yakfuzztag-handle.yakmirror-http-flow.yakmock-http-request.yakretry-handler.yak詳細
SKILL:Yakit Web Fuzzer ホットリロード(Hot Patch)
AI LOAD INSTRUCTION:これは3層ホットリロード体系の「モジュールレベル Web Fuzzer」特集です。Web Fuzzer のホットリロードは、単一の Fuzzer タブにおけるリクエスト送信フローに作用します。代表的な用途は「イントラネットの暗号化への対処」(リクエストの暗号化、レスポンスの復号、署名の注入)と「自動化された判断」(再試行、ビジネス上の失敗判定)です。まず Hook のシグネチャと戻り値の意味を確認し、次に
examples/の Hook 名に対応したサンプルを見てください。どれもyak <file>でテストできます。「フロントエンドの暗号化に対するさまざまな HACK/ユーザーに平文を表示する」場合は、第5節の組み合わせレシピ(examples/combo-*.yak)を参照してください。ホットリロードを書く前に、競合によるクラッシュを避けるため、必ず第6節「並行処理とグローバル変数」を読んでください。
0. 関連ルート
- 入口と3層体系:yak
- MITM ホットリロード(プロキシ側での横取り/ミラーリング/登録):mitm-hotpatch
- グローバルホットリロード(モジュールの実行前に動作し、MITM/Fuzzer で共有):global-hotpatch
1. Hook のシグネチャと戻り値の意味
Web Fuzzer のホットリロードの Hook は、MITM と重要な違いがあります。横取り系の処理では、戻り値で変更を適用します(forward/drop ではありません)。
| Hook | シグネチャ | 役割 | 適用方法 |
|---|---|---|---|
beforeRequest | (https, originReq, req) -> req | リクエスト送信前の書き換え(暗号化/署名/ヘッダー追加) | 新しいリクエストをreturn |
afterRequest | (https, originReq, req, originRsp, rsp) -> rsp | レスポンス表示前の書き換え(復号/整形) | 新しいレスポンスをreturn |
retryHandler | (https, retryCount, req, rsp, retry) | 再試行時に次の処理を決定 | retry([newReq]) |
customFailureChecker | (https, req, rsp, fail) | カスタムの「失敗」判定 | fail("原因") |
mirrorHTTPFlow | (req, rsp, params) -> params | {{params(name)}} 用のパラメーターを抽出 | map をreturn |
mockHTTPRequest | (https, url, req, mockResponse) | オフライン時/サービスがない場合にローカルレスポンスを使用 | mockResponse(rspStr) |
注意:Web Fuzzer の
mirrorHTTPFlow(req, rsp, params)と MITM のmirrorHTTPFlow(isHttps, url, req, rsp, body)はシグネチャが異なります。混同しないでください。
2. コアシナリオ1:イントラネットの暗号化への対処
最新の Web アプリケーションでは、リクエスト/レスポンスを暗号化または署名することがよくあります。暗号化・復号/署名のロジックをホットリロードに組み込めば、Fuzzer で平文を直接ファジングでき、送信時には自動的に暗号化され、表示時には自動的に復号されます。
flowchart LR
Plain["Fuzzer 里写明文请求"] -->|beforeRequest 加密/签名| Wire["真正出站的密文/带签名请求"]
Wire --> Server["目标服务器"]
Server -->|密文响应| After["afterRequest 解密"]
After --> View["Fuzzer 里看到明文响应"]
| シナリオ | Hook | サンプル |
|---|---|---|
| timestamp+nonce+HMAC 署名の注入 | beforeRequest | examples/before-request.yak |
| AES-CBC レスポンスの復号(Fuzzer に平文を表示) | afterRequest | examples/after-request.yak |
国産暗号(SM4-CBC/SM2/SM3)も同様です。codec.AESCBC* を codec.SM4*/codec.Sm3 に置き換えてください。
3. コアシナリオ2:自動化された判断(再試行/失敗判定)
記事043で導入された2つの関数により、「レスポンスを手動で分析し、その後の処理を決める」作業を自動化し、コード化できます。
| シナリオ | Hook | サンプル |
|---|---|---|
| 405→POST/429 でバックオフ/401 で中止/5xx で再試行 | retryHandler | examples/retry-handler.yak |
| 200 OK でも、本文に失敗キーワードが含まれていれば失敗と判定 | customFailureChecker | examples/custom-failure-checker.yak |
| 対象が未稼働の場合にローカルレスポンスで連携テスト | mockHTTPRequest | examples/mock-http-request.yak |
retryHandlerは、yakit Fuzzer の再試行設定(再試行の有効化と、失敗ステータスコードの指定)と併用する必要があります。customFailureCheckerは、「プロトコル層では200、ビジネス層では失敗」というブルートフォースの結果を絞り込む難題を解決します。mockHTTPRequestを使えば、実際のサービスがなくても、ペイロードのレンダリング/再試行/失敗判定を含む連携テストを実行できます。
4. コアシナリオ3:fuzztag と関連テスト
| シナリオ | 仕組み | サンプル |
|---|---|---|
| ハッシュペイロードを動的に計算 | `{{yak(hash | md5,admin)}}` |
| 複数ステップのリクエストから token/csrf を抽出 | mirrorHTTPFlow が map を返す → {{params(name)}} | examples/mirror-http-flow.yak |
ホットリロードで定義した通常の関数 f = func(param){ return [...] } は、{{yak(f|参数)}} タグとして呼び出せます。返された配列の各要素が1つのペイロードになります。
5. 前端加密对抗组合配方(让用户看到明文)
单个 hook 解决单点问题,真实"前端加密 HACK"往往要 多个 hook 组合:发包侧加密/签名,回包侧解密,让你在 Fuzzer 里始终面对 明文。这正是绕过前端加密做爆破/测试的核心套路。
flowchart LR
P["你写的明文 payload"] -->|beforeRequest / fuzztag 加密+签名| W["出站密文(过验签)"]
W --> S["目标服务器"]
S -->|密文响应| D["afterRequest / mirrorHTTPFlow 解密"]
D --> V["Fuzzer 里看到明文, 一眼判断成功"]
| 组合配方 | 涉及 hook | 对抗的前端防护 | 示例 |
|---|---|---|---|
| AES-CBC 写明文/看明文往返 | beforeRequest + afterRequest | CryptoJS AES-CBC 静态密钥 | examples/combo-aescbc-plaintext-roundtrip.yak |
| 加密 + HMAC 验签 双重防护 | beforeRequest + afterRequest | AES 加密叠加签名防篡改 | examples/combo-sign-and-encrypt.yak |
| RSA-OAEP + AES-GCM 混合加密 | {{yak(...)}} fuzztag + mirrorHTTPFlow | 动态公钥 + 非对称包裹对称密钥 | examples/combo-rsa-aesgcm-hybrid.yak |
要点:
- 请求侧用
beforeRequest(整包加密/签名)或{{yak(fn|...)}}fuzztag(只加密某个字段)。 - 响应侧用
afterRequest(直接把响应改成明文回显)或mirrorHTTPFlow(把解密结果提取成变量,配合序列{{params(name)}},并在结果表里一眼看出成功)。 - 动态密钥/公钥用 Web Fuzzer 序列:Step0 取密钥 → 数据提取器存
publicKey/privateKey→ Step1{{params(publicKey)}}引用(见混合加密示例头注释)。 - 常用算法 API:
codec.AESCBCEncryptWithPKCS7Padding/codec.AESGCMEncryptWithNonceSize12/codec.RSAEncryptWithOAEP/codec.HmacSha256,国密换成codec.Sm4*/codec.SM2*。
关于"明文密码传输"为什么值得测:参见文章 183——前端加密只是抬高门槛,密码本质仍在通信中可还原;把链路解密成明文后,常规注入/爆破/逻辑测试就全部恢复可用。
6. 并发与全局变量:尽量用"只读常量",否则会崩
这是热加载最容易踩的崩溃坑。 MITM / Web Fuzzer 会 并发 地对多个请求调用同一个 hook(同一份脚本实例)。如果你的 hook 去 读写同一个顶层可变全局变量(例如 append 到全局 slice、给全局 map 赋值、累加全局计数器),多个 goroutine 同时写就会触发 data race,轻则结果错乱,重则直接 panic 崩溃。
规则:
- 配置/密钥/IV/规则表等放顶层,但当作"只读常量":加载时赋值一次,hook 内 绝不修改。这种用法天然并发安全(本节所有加密组合示例都是这么写的)。
- 不要在 hook 里写共享可变全局。需要累加/收集时:
- 优先把状态留在 函数局部 或通过返回值传出(
mirrorHTTPFlow返回 map、afterRequest返回包)。 - 确实要跨请求聚合,用并发安全容器/锁:
sync.Map、sync.NewMutex()加锁,或 yakit 的db.*/risk.*落库,而不是裸append全局 slice。
- 优先把状态留在 函数局部 或通过返回值传出(
// 反例(并发会崩): 多个请求同时 append 同一个全局 slice
RESULTS = []
mirrorHTTPFlow = func(req, rsp, params) { RESULTS = append(RESULTS, req); return {} } // 危险!
// 正例: 顶层只放只读常量, 结果通过返回值带出
AES_KEY = codec.DecodeHex("3132...")~ // 只读常量, 不再修改
afterRequest = func(https, oreq, req, orsp, rsp) {
return poc.ReplaceHTTPPacketBody(rsp, decrypt(AES_KEY, poc.GetHTTPPacketBody(rsp)))
}
本库
mirror-*单 hook 示例为了"自测可断言"用了全局 slice/map 累加——那是 单线程命令行自测 的演示写法;放进真实并发的 MITM/Fuzzer 时,请按上面的规则改成只读常量 + 返回值/加锁。
7. 标准写法:hook 函数 + YAK_MAIN 自测
beforeRequest = func(https, originReq, req) {
// 改写 req
return req
}
func runSelfTest() {
// 构造 mock 请求, 调用 hook, assert 返回值
}
if YAK_MAIN {
runSelfTest()
}
YAK_MAIN 区分运行环境:
yak xxx.yak命令行:YAK_MAIN = true,跑自测。- yakit Fuzzer 热加载窗口:
YAK_MAIN = false,仅注册 hook。
各 Hook 的自测 mock 方式
| Hook | 自测验证方式 |
|---|---|
beforeRequest / afterRequest | 构造 mock 请求/响应,断言返回包内容(可对加密结果做反向解密验证 roundtrip) |
retryHandler | 自定义 retry callback(func(args...)),断言调用次数与新请求内容 |
customFailureChecker | 自定义 fail callback,断言是否被标记失败 |
mirrorHTTPFlow | 直接调用,断言返回 map 的提取字段 |
mockHTTPRequest | 自定义 mockResponse callback,验证目标触发 |
8. 常用 codec / poc API 速查
| 用途 | API |
|---|---|
| AES-CBC 加/解密 | codec.AESCBCEncrypt(key, data, iv)~ / codec.AESCBCDecrypt(key, cipher, iv)~ |
| Base64 编/解码 | codec.EncodeBase64(b) / codec.DecodeBase64(s)~ |
| HMAC-SHA256(返回字节) | codec.EncodeToHex(codec.HmacSha256(key, data)) |
| 哈希 | codec.Md5(s) / codec.Sha256(s) / codec.EncodeToHex(codec.Sm3(s)) |
| 状态码 | poc.GetStatusCodeFromResponse(rsp) |
| 改方法 | poc.ReplaceHTTPPacketMethod(req, "POST") |
| 改 body / header | poc.ReplaceHTTPPacketBody / poc.ReplaceHTTPPacketHeader |
| 取请求 method/path | poc.GetHTTPRequestMethod(req) / poc.GetHTTPRequestPath(req) |
9. 验证
cd /Users/v1ll4n/Projects/yaklang
go run common/yak/cmd/yak.go skills/webfuzzer-hotpatch/examples/before-request.yak
# 与 Yakit gRPC 同款执行路径 (MutateHookCaller); 验证 {{yak(...)}} fuzztag:
go build -o /tmp/yak ./common/yak/cmd/yak.go
printf 'GET / HTTP/1.1\r\nHost: t.example.com\r\n\r\n' > /tmp/req.txt
/tmp/yak hotpatch-webfuzzer --script skills/webfuzzer-hotpatch/examples/fuzztag-handle.yak \
--request /tmp/req.txt --fuzztag '{{yak(hash|md5,hello)}}'
每个示例应:10 秒内完成、assert 全过、log 全英文、出现 ... self test passed。
参考来源
- yak-project-public 043 (2025-07-25) Web Fuzzer 新的热加载函数——重试控制与错误处理
- yak-project-public 183 (2023-09-14) 渗透测试高级技巧 分析验签与前端加密(一)(HMAC 验签 + AES-CBC,组合配方来源)
- yak-project-public 177 (2023-10-11) 渗透测试高级技巧(二) 对抗前端动态密钥与非对称加密防护(RSA-OAEP + AES-GCM 混合,混合加密组合来源)
- yak-project-public 084 (2024-12-05) 渗透测试高级技巧(三) 被前端加密后的漏洞测试
- yak-project-public 270 (2022-07-28) 前端 AES-ECB 加密 Web 安全测试实战
- yak-project-public 282/285 (2022-05) Yakit Web Fuzzer 终极能力强化 热加载 Fuzz
- yak-project-public 167 (2023-11-17) Web Fuzzer 进阶
- 引擎实现:
common/yak/script_engine_for_fuzz.go