agent-bypass 2.12.0 → 2.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/SKILL.md +4 -2
- package/bin/install.js +4 -11
- package/package.json +2 -2
- package/references/learned.md +85 -0
- package/references/recon-commands.md +2 -0
- package/references/spa-recon.md +299 -0
package/README.md
CHANGED
|
@@ -30,10 +30,10 @@
|
|
|
30
30
|
|
|
31
31
|
## 快速开始
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
一键安装,默认只注入 Pi + OpenCode(其余 harness 显式指定):
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
|
-
npx agent-bypass # npm
|
|
36
|
+
npx agent-bypass # npm 直装(默认:Pi + OpenCode)
|
|
37
37
|
npx github:1837620622/agent-bypass # GitHub 源直跑
|
|
38
38
|
npx agent-bypass --pi # 只装 Pi
|
|
39
39
|
npx agent-bypass --opencode # 只装 OpenCode
|
|
@@ -140,7 +140,7 @@ cp -r SKILL.md ck-hunter.md SYSTEM_PROMPT.md config.yaml.example references scri
|
|
|
140
140
|
```text
|
|
141
141
|
agent-bypass/
|
|
142
142
|
├── package.json # npx 入口(bin: agent-bypass)
|
|
143
|
-
├── bin/install.js #
|
|
143
|
+
├── bin/install.js # 安装器:默认 Pi + OpenCode;其余显式指定(覆盖式更新)
|
|
144
144
|
├── assets/ # README 视觉资源(SVG banner)
|
|
145
145
|
├── SKILL.md # 主技能:模块化指令集(按索引切片加载)
|
|
146
146
|
├── ck-hunter.md # 子技能:情报源聚合与凭证猎取(QUICKSTART 切片协议)
|
|
@@ -237,10 +237,10 @@ Two plain-text files (SKILL.md main instructions + ck-hunter.md intel sub-skill)
|
|
|
237
237
|
|
|
238
238
|
## Quick start
|
|
239
239
|
|
|
240
|
-
One command;
|
|
240
|
+
One command; installs into Pi + OpenCode by default (other harnesses via explicit flags):
|
|
241
241
|
|
|
242
242
|
```bash
|
|
243
|
-
npx agent-bypass # npm install (
|
|
243
|
+
npx agent-bypass # npm install (default: Pi + OpenCode)
|
|
244
244
|
npx github:1837620622/agent-bypass # run straight from GitHub
|
|
245
245
|
npx agent-bypass --pi # Pi only
|
|
246
246
|
npx agent-bypass --opencode # OpenCode only
|
|
@@ -344,7 +344,7 @@ Repeated tests on api.b.ai deepseek-v4.1-flash with the same batch of task instr
|
|
|
344
344
|
```text
|
|
345
345
|
agent-bypass/
|
|
346
346
|
├── package.json # npx entry (bin: agent-bypass)
|
|
347
|
-
├── bin/install.js # installer:
|
|
347
|
+
├── bin/install.js # installer: default Pi + OpenCode; others explicit (overwrite updates)
|
|
348
348
|
├── assets/ # README visual assets (SVG banner)
|
|
349
349
|
├── SKILL.md # main skill: modular instructions (index-sliced loading)
|
|
350
350
|
├── ck-hunter.md # sub-skill: intel sources & credential hunting (QUICKSTART slice protocol)
|
package/SKILL.md
CHANGED
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
ctf/全能模式/渗透作战/逆向深挖/内存工程 等触发词时使用本技能。
|
|
11
11
|
license: MIT
|
|
12
12
|
metadata:
|
|
13
|
-
version: "2.
|
|
13
|
+
version: "2.13.1"
|
|
14
14
|
---
|
|
15
15
|
|
|
16
16
|
# agent-bypass — Ck · 万能执行员
|
|
@@ -180,7 +180,7 @@ Cknb在呢,想干什么?直接开干。
|
|
|
180
180
|
| `README.md` | 14KB | 不读 | 仅给人看 |
|
|
181
181
|
| `config.yaml.example` | 2.5KB | 需要时读 | 密钥模板 |
|
|
182
182
|
| `SYSTEM_PROMPT.md` | 3KB | 注入用 | 贴入系统提示词层;或 `npx agent-bypass` 自动注入(Pi 三层:`AGENTS.md`+`APPEND_SYSTEM.md`+`prompts/ck.md`;OpenCode:`instructions`+`commands/ck.md`;Claude Code:`commands/ck.md`+`CLAUDE.md`;Grok Build:`~/.agents/commands/ck.md`+`AGENTS.md`) |
|
|
183
|
-
| `references/` |
|
|
183
|
+
| `references/` | 92KB ≈ 23K token | 按需切片:`grep -n 关键词` 定位后读对应段 | 参考资料:经验中枢(`learned.md`,开新任务先检索)、payload 速查、侦察命令集、验证清单、云/移动/供应链专题、移动快速侦察(`mobile-fast-recon.md`)、SPA 接口侦察(`spa-recon.md`:三道门/无凭据进壳/参数矩阵/权限树) |
|
|
184
184
|
| `scripts/` | 137KB ≈ 34K token | 不预读,用哪个看哪个(`--help`) | 工具库(11 个):快速侦察(`fastrecon`:DNS 多类型并发/DoT、子域枚举、多域名情报、端口 cam 摄像头组、HTTP+自动兜底链)、端口扫描、目录爆破(curl-Z 引擎+py 兜底+软404过滤)、Basic 爆破(curl-Z 快速引擎)、JS 提取、CVE 拉取、依赖扫描、变异调度(mutate)、牌组管理(deck)、Web 探测、DoH 解析;另含共享库 `platf`(平台自适配)、`argsmini`(极简参数解析) |
|
|
185
185
|
| `pi/` + `opencode/` + `claude/` + `grok/` | 2KB | 不读 | 注入模板:Pi 三层(`pi/AGENTS.md` L1 + `APPEND_SYSTEM.md`(←SYSTEM_PROMPT.md) L2 + `pi/prompts/ck.md` L3)+ OpenCode(`agent-bypass.md`+`instructions`+`commands/ck.md`)+ Claude Code(`CLAUDE.md`←`claude/AGENTS.md`+`commands/ck.md`)+ Grok(`AGENTS.md`+`commands/ck.md`) |
|
|
186
186
|
|
|
@@ -655,6 +655,8 @@ Unity/Unreal:引擎版本/metadata dump→目标类/函数/矩阵/W2S→hook/o
|
|
|
655
655
|
- SO/native:Ghidra headless 批量反编译 → 导出伪 C → 关键函数人工+LLM 混合分析
|
|
656
656
|
- 小程序:wxapkg 解包 → 还原 `app-service.js` → 定位 wx.request 拦截点
|
|
657
657
|
|
|
658
|
+
**接口面侦察(非加密面)**:SPA 全接口挖掘——三道门定位 / 无凭据 mock 进壳 / 参数触发矩阵 / 权限树还原 → `references/spa-recon.md`。
|
|
659
|
+
|
|
658
660
|
<!-- module: 51-remediation.md -->
|
|
659
661
|
# REMEDIATION Track(漏洞修复与加固)
|
|
660
662
|
|
package/bin/install.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* agent-bypass installer — 自动安装到 Pi / OpenCode / Claude Code / Grok Build / OpenMinis
|
|
6
6
|
*
|
|
7
7
|
* 用法:
|
|
8
|
-
* npx agent-bypass
|
|
8
|
+
* npx agent-bypass 默认安装(只装 Pi + OpenCode;其余 harness 须显式指定)
|
|
9
9
|
* npx agent-bypass --pi 只装 Pi(三层注入:AGENTS.md + APPEND_SYSTEM.md + prompts/ck.md)
|
|
10
10
|
* npx agent-bypass --opencode 只装 OpenCode(instructions 注入 + commands/ck.md)
|
|
11
11
|
* npx agent-bypass --claude 只装 Claude Code(skills + commands/ck.md + CLAUDE.md 注入)
|
|
@@ -384,22 +384,15 @@ let targets;
|
|
|
384
384
|
if (only.length) {
|
|
385
385
|
targets = only.map((f) => f.slice(2));
|
|
386
386
|
} else {
|
|
387
|
+
// 默认只装 Pi + OpenCode;其余 harness 一律显式指定(--claude / --grok / --minis / --ccswitch)
|
|
387
388
|
targets = [];
|
|
388
389
|
if (detectPi) targets.push('pi');
|
|
389
390
|
if (detectOc) targets.push('opencode');
|
|
390
|
-
if (detectClaude) targets.push('claude');
|
|
391
|
-
if (detectGrok) targets.push('grok');
|
|
392
|
-
if (detectCc) targets.push('ccswitch');
|
|
393
|
-
if (detectMinis) targets.push('minis');
|
|
394
391
|
if (!targets.length) {
|
|
395
|
-
warn('
|
|
392
|
+
warn('未检测到默认目标(Pi / OpenCode):');
|
|
396
393
|
info('Pi: ~/.pi 不存在');
|
|
397
394
|
info('OpenCode: ~/.config/opencode 不存在');
|
|
398
|
-
info('
|
|
399
|
-
info('Grok Build: ~/.grok 不存在');
|
|
400
|
-
info('CC Switch: ~/.cc-switch 不存在');
|
|
401
|
-
info('OpenMinis: /var/minis 不存在');
|
|
402
|
-
info('强制安装指定目标: npx agent-bypass --pi | --opencode | --claude | --grok | --minis | --ccswitch');
|
|
395
|
+
info('其他 harness 需显式指定: npx agent-bypass --claude | --grok | --minis | --ccswitch');
|
|
403
396
|
process.exit(1);
|
|
404
397
|
}
|
|
405
398
|
console.log(`检测到: ${targets.join(' / ')}`);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-bypass",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Cknb 全能技术操作员 — Agent Skills
|
|
3
|
+
"version": "2.13.1",
|
|
4
|
+
"description": "Cknb 全能技术操作员 — Agent Skills 技能包(默认安装 Pi / OpenCode;Claude Code / Grok / OpenMinis 显式指定)",
|
|
5
5
|
"bin": {
|
|
6
6
|
"agent-bypass": "bin/install.js"
|
|
7
7
|
},
|
package/references/learned.md
CHANGED
|
@@ -590,3 +590,88 @@
|
|
|
590
590
|
**可复用性**:高
|
|
591
591
|
|
|
592
592
|
---
|
|
593
|
+
|
|
594
|
+
## [前端侦察] 逆向三道门:渲染门 / 拦截器门 / 内容门 ★
|
|
595
|
+
|
|
596
|
+
**场景**:SPA 后台(无账号侦察 / 要挖全接口清单)
|
|
597
|
+
|
|
598
|
+
**判据**:拿到 SPA 先回答三个问题——
|
|
599
|
+
```
|
|
600
|
+
① 渲染门:怎么判断已登录? → grep isLogin/getUser/storage.getItem → 定键名+容器+编码
|
|
601
|
+
② 拦截器门:什么触发跳登录? → grep interceptors.response + 字段名 == 成功值 → 假会话敲接口验证
|
|
602
|
+
③ 内容门:菜单/权限从哪来? → grep permissions/all、role_permissions、userRouteAuth、routeMap
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
**配方**:三道门的定位 grep 与技术表见 `references/spa-recon.md` 第二节;拿到三样(键名/成功值/权限来源)即可伪造会话进壳。
|
|
606
|
+
|
|
607
|
+
**可复用性**:高(所有 SPA 后台通用起手式)
|
|
608
|
+
|
|
609
|
+
---
|
|
610
|
+
|
|
611
|
+
## [前端侦察] 无凭据进壳:用 mock 撑开前端
|
|
612
|
+
|
|
613
|
+
**场景**:没账号,卡在登录页,接口一个都发不出来
|
|
614
|
+
|
|
615
|
+
**思路**:前端只信它自己的登录判断——**把判断喂饱,壳就开了**。三层递进:L1 伪造会话(过渲染门)→ L2 响应字段中和(过拦截器门)→ L3 权限数据伪造(过内容门,触发更多模块接口)。
|
|
616
|
+
|
|
617
|
+
**铁律**:只录 **outbound 请求**(URL/方法/body)——空表格是预期现象,别为拿"真实数据"转做越权(那是下一阶段)。hardened 站点(服务端签名 Cookie)到此为止,静态照样出 endpoint 清单。
|
|
618
|
+
|
|
619
|
+
**可复用性**:高(无账号场景的核心突破路径)
|
|
620
|
+
|
|
621
|
+
---
|
|
622
|
+
|
|
623
|
+
## [参数逆向] 锚点扩窗 × 触发矩阵
|
|
624
|
+
|
|
625
|
+
**场景**:要还原接口的参数结构(字段/必填/传输位置)
|
|
626
|
+
|
|
627
|
+
**四招**:
|
|
628
|
+
```
|
|
629
|
+
① 锚点扩窗:grep '.{0,120}("/api/xxx").{0,200}' 看 path 旁边的组包对象
|
|
630
|
+
② 绑定链:表单 onFinish → transform → API payload 追一条链
|
|
631
|
+
③ 触发矩阵:每个操作(列表/搜索/筛选/新建/导出)各录一次 diff body
|
|
632
|
+
④ 错误反推:故意留空/传错类型看 400 报文
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
**置信度纪律**:高=静态+≥2 样本一致;中=仅静态或 1 样本;低=仅反推;待触发=静态有字段 UI 没跑到。**单样本定不了必填/可选**;同 path 不同 body 形状按操作分条记录。
|
|
636
|
+
|
|
637
|
+
**可复用性**:高
|
|
638
|
+
|
|
639
|
+
---
|
|
640
|
+
|
|
641
|
+
## [上下文纪律] 大文件不进上下文 ★
|
|
642
|
+
|
|
643
|
+
**场景**:所有人机协作的长任务(iSH/移动端尤其致命)
|
|
644
|
+
|
|
645
|
+
**纪律**:
|
|
646
|
+
```
|
|
647
|
+
① >100KB 的文件/输出禁止直接读进上下文——用脚本批处理,只把核心片段喂给分析
|
|
648
|
+
② grep 必须限流:head -20 / -m 5,永远不要无界 grep 的结果
|
|
649
|
+
③ 目录校验用 wc -l / ls | wc -l,不 Read 整个目录
|
|
650
|
+
④ 长任务状态外置:进度写任务目录的状态文件,会话断了从文件续(不靠"记忆")
|
|
651
|
+
⑤ 冷热分层:当前阶段证据保原文(热),已归档结论压缩成摘要(冷);摘要永不覆盖仍活跃内容
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
**依据**:成熟自主框架把"上下文预算"当一级公民管理(输出预算/滚动摘要/裁剪不拆散工具调用对);大文件进上下文=把核心逻辑淹死(context rot)。
|
|
655
|
+
|
|
656
|
+
**可复用性**:高(直接决定长任务成败)
|
|
657
|
+
|
|
658
|
+
---
|
|
659
|
+
|
|
660
|
+
## [证据纪律] 快照哈希与版本防覆盖
|
|
661
|
+
|
|
662
|
+
**场景**:交付报告/证据链管理(漏洞证据、卡密导出、流量存档)
|
|
663
|
+
|
|
664
|
+
**思路**:
|
|
665
|
+
```
|
|
666
|
+
① 证据引用要分类:baseline(正常对照)/ proof(复现证明)/ verification(补充)/ supporting
|
|
667
|
+
② 快照存哈希(SHA-256)+ 原始字节,正文与可清理缓存分目录放
|
|
668
|
+
③ 版本号防覆盖:报告用哪版证据要记录版本;生成期间证据变了,拒绝旧版本写入(重新读再生成)
|
|
669
|
+
④ 导出即结构化:request.http / response.http / manifest.json 一包完整
|
|
670
|
+
⑤ 不留洞:提交的引用必须全部有效(一条失败整批回滚),找不到证据时如实标注原因而非硬凑
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
**教训**:没有哈希和版本,证据链对不上账;"部分成功"的绑定比失败更糟(看起来绑定实则有断点)。
|
|
674
|
+
|
|
675
|
+
**可复用性**:中高(凡是交付型任务的证据管理通用)
|
|
676
|
+
|
|
677
|
+
---
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# SPA 前端接口侦察(三道门 / 无凭据进壳 / 参数矩阵 / 权限树)
|
|
2
|
+
|
|
3
|
+
> 一句话:把 SPA 站点的**全部接口、参数结构、权限模型**掏干净。
|
|
4
|
+
> 边界:这里管"接口面侦察";**加密参数还原(sign/加密算法复刻)不在这**——走 SKILL「50-js-reverse」模块。
|
|
5
|
+
> 前提:已授权。所有 grep 针对已下载的 `js/` 目录执行(bundle 单行时先 `sed 's/}/}\n/g'` 切开再 grep)。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 一、两层模型:静态打底,运行时补全
|
|
10
|
+
|
|
11
|
+
| 层 | 来源 | 能拿到 | 拿不到 |
|
|
12
|
+
|---|---|---|---|
|
|
13
|
+
| **静态** | 全量 chunk 下载后 grep | endpoint 全路径、路由草案、字段候选 | HTTP 方法、真实参数结构、运行时拼接的 URL |
|
|
14
|
+
| **运行时** | 活会话 + 代理/fetch hook | 方法 + body + 响应 + 动态 URL + WS/SSE | 没渲染到的页面;**单样本定不了必填/可选** |
|
|
15
|
+
|
|
16
|
+
顺序:静态先做完(便宜、无痕),运行时补方法与参数。参数结论必须**多样本 diff**,不许拿单样本拍脑袋。
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 二、逆向三道门(重点)
|
|
21
|
+
|
|
22
|
+
拿到一个 SPA 后台,先回答三个问题:**怎么判断已登录 / 什么触发跳登录 / 菜单权限从哪来**。
|
|
23
|
+
|
|
24
|
+
### 2.1 渲染门——「如何判断已登录?」
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# 找登录判断函数与存储读取
|
|
28
|
+
grep -rhoaE '.{0,40}(isLogin|isAuthenticated|loggedIn|hasLogin|requireAuth)\b.{0,80}' js/ | head
|
|
29
|
+
grep -rhoaE 'function (getUser|getToken|getAuth)[0-9]?\([^)]*\)\{.{0,200}' js/ | head
|
|
30
|
+
grep -rhoaE '(localStorage|sessionStorage)\.getItem\("[^"]+"\)' js/ | sort -u
|
|
31
|
+
grep -rhoaE '(Cookies?|cookie)\.(get|load)\("[^"]+"\)' js/ | sort -u
|
|
32
|
+
grep -rhoaE '\batob\(|JSON\.parse\(|jwt|decode' js/ | head
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
链路通常长这样:`isLogin = f(getUser())` → `getUser = decode(storage.read(键名))`。
|
|
36
|
+
要确定**三样**:存储键名、容器(Cookie 还是 localStorage)、编码方式。然后按编码给出伪造值:
|
|
37
|
+
|
|
38
|
+
| 编码 | 伪造写法 |
|
|
39
|
+
|---|---|
|
|
40
|
+
| 明文字符串 / `"1"` / token | 任意 truthy 值 |
|
|
41
|
+
| `JSON.parse(x)` | `{"id":1,"username":"admin"}` |
|
|
42
|
+
| `JSON.parse(atob(x))` | base64(上述 JSON) |
|
|
43
|
+
| JWT | 无签名 / `alg:none` JWT;或 bundle 里挖到的密钥自签 |
|
|
44
|
+
| 加密(SM2/AES/RSA) | 找硬编码密钥;渲染门只要求"可解码 blob"时直接 forge,解不开就静态兜底 |
|
|
45
|
+
|
|
46
|
+
拿到这三样,后面写 mock/hook 配置的 cookies / localStorage 就有底了。
|
|
47
|
+
|
|
48
|
+
### 2.2 拦截器门——「什么触发跳 /login?」
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
grep -rhoaE '.{0,60}(interceptors\.response|axios|request\.use).{0,120}' js/ | head
|
|
52
|
+
grep -rhoaE '.{0,40}(response_code|errcode|errno|\bcode\b|\bret\b|\bstatus\b)\s*[=!]==?\s*[\-0-9]{1,4}.{0,60}' js/ | head -20
|
|
53
|
+
grep -rhoaE '.{0,40}(未登录|请重新登录|登录已过期|unauthorized|登录失效|token.{0,10}invalid).{0,40}' js/ | head
|
|
54
|
+
grep -rhoaE '.{0,30}(location\.href|router\.(push|replace)|navigate)\([^)]*login[^)]*\)' js/ | head
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
要确定:**响应字段名**、**成功值**(大多是 `0` 或 `200`)、**触发跳转的失败值**。
|
|
58
|
+
验证:拿假会话直接敲受保护接口,看它落哪个分支:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
curl -sk -X POST -H 'Cookie: <假键>=junk' https://target/api/<受保护> \
|
|
62
|
+
-d '{}' -H 'Content-Type: application/json'
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
拿到这三样就能"改旗换码":把拦截器关心的字段值中和掉(代理重写 / 前端 hook / 改包),后端返回什么码都当成功。**这是后面进壳的基础。**
|
|
66
|
+
|
|
67
|
+
### 2.3 内容门——「菜单/权限从哪来?」
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
grep -rhoaE '"/api[^"]*(permission|perm|role|menu|acl|resource|nav)[^"]*"' js/ | sort -u
|
|
71
|
+
grep -rhoaE '.{0,30}(menus|permissions|menuList|routeList|authList|role_permissions)\b.{0,120}' js/ | head
|
|
72
|
+
grep -rhoaE 'userRouteAuth|getResultTree|routeMap|routeLink|hasPermission|checkAuth' js/ | head
|
|
73
|
+
grep -rhoaE '([A-Z_][A-Z0-9_]*):\{name:"[^"]*",link:"/[^"]+"\}' js/ | head
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
常见企业后台是**两层数据**:
|
|
77
|
+
|
|
78
|
+
| 数据 | 典型 payload | 谁消费 |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| `/role_permissions` 类 | `{ permissions: string[], role_type }` | 路由守卫、按钮级 ACL |
|
|
81
|
+
| `/permissions/all` 类 | `tree[{ code, position, children }]` | 侧栏菜单渲染 |
|
|
82
|
+
| bundle 内 `userRouteAuth` | `{ CODE: { url, name? } }` | code → 前端 path |
|
|
83
|
+
| bundle 内 `routeMap` | `{ KEY: { name, link } }` | 别名解析(webpack `o.DASHBOARD`) |
|
|
84
|
+
|
|
85
|
+
读消费方代码确认过滤逻辑(`getResultTree(tree, permissions)`、`v-if` / `hasAuth(code)` 查哪个字段)——然后照结构造 permissive payload 喂给 mock。**小站点到这步就能进壳;大站侧栏还是空的话走第 4 节权限树还原。**
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 三、无凭据进壳:用 mock 撑开前端
|
|
90
|
+
|
|
91
|
+
**问题**:没账号,SPA 卡在登录页,什么接口都发不出来。
|
|
92
|
+
**思路**:前端只信它自己那几个"登录判断"——**把判断喂饱,让壳先开**。用 2.1/2.2 拿到的键名与成功值,伪造会话 + 把拦截器字段中和,前端就会渲染登录后的壳层、挂载路由、继续发它自己的 XHR/fetch——**你只录 outbound,后端返回什么不重要。**
|
|
93
|
+
|
|
94
|
+
三层 mock(按需递进):
|
|
95
|
+
|
|
96
|
+
| 层 | 干什么 | 效果 |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| L1 会话层 | cookies / localStorage 注入伪造会话(按 2.1 的编码表) | 过渲染门 |
|
|
99
|
+
| L2 应答中和 | 代理/hook 把响应里的 `code/errno/status` 字段重写为成功值 | 过拦截器门,请求不再被跳登录吞掉 |
|
|
100
|
+
| L3 内容伪造 | 把 `permissions/all`、`role_permissions`、菜单类接口的响应替换成 permissive payload | 过内容门,侧栏/子模块挂载,触发**更多**模块接口 |
|
|
101
|
+
|
|
102
|
+
**落地手段(不挑工具,拣顺手的)**:
|
|
103
|
+
- 桌面:系统代理(mitmproxy/Charles)重写响应 + 浏览器 devtools 会话注入
|
|
104
|
+
- 纯前端:document-start 注入脚本,hook `fetch`/`XHR` + `localStorage` 读取 + 路由守卫(撑开 Vue 路由守卫 / React 无此概念就靠 L3 数据)
|
|
105
|
+
- 移动端:本地 VPN 代理同套规则
|
|
106
|
+
|
|
107
|
+
**铁律**:
|
|
108
|
+
- 只录 **outbound 请求**(URL/方法/头/body)——空表格、空数据、占位 UI 都是**预期现象**,别为了拿"真实数据"转去做越权/注入(那是下一个阶段的事)
|
|
109
|
+
- 进壳后模块仍空白 → 十有八九是 L3 数据不齐 → 走第 4 节权限树
|
|
110
|
+
- hardened 站点(服务端签名 Cookie、服务端渲染菜单)::build 到此为止——静态照样能出 endpoint 清单,运行时用真实会话再跑同一套
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 四、参数逆向(锚点扩窗 / 绑定链 / 触发矩阵)
|
|
115
|
+
|
|
116
|
+
**只有方法论,没有一键脚本。** path 用正则找;参数靠四招:**锚点扩窗 + UI 绑定链 + 多样本 diff + 错误反推**。
|
|
117
|
+
|
|
118
|
+
### 4.1 锚点扩窗——从 path 找组包对象
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# 以已知 path 为锚,向两侧扩窗口,看它旁边的对象
|
|
122
|
+
grep -n '"/api/user/list"' js/*.js
|
|
123
|
+
grep -rhoaE '.{0,120}("/api[^"]+").{0,200}' js/ | head
|
|
124
|
+
grep -rhoaE '(params|data|body|payload)\s*:\s*\{' js/ | head
|
|
125
|
+
grep -rhoaE '(get|post|put|delete|patch)\([^,]+,\s*\{' js/ | head
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 4.2 包装层与传输形态
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
grep -rhoaE '(axios|request)\.(get|post|put|delete|patch)\(' js/ | head
|
|
132
|
+
grep -rhoaE 'FormData|\.append\(' js/ | head
|
|
133
|
+
grep -rhoaE '(query|mutation)\s+\w+|gql\`' js/ | head # GraphQL
|
|
134
|
+
grep -rhoaE 'path:\s*"/[^"]*:[^"]+"' js/ | head # 路径参数
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
| 形态 | 参数在哪 | 静态优先看 |
|
|
138
|
+
|---|---|---|
|
|
139
|
+
| REST JSON | body + query | path 锚点旁 `(params\|data\|body)\s*:\s*\{` |
|
|
140
|
+
| GraphQL | `variables` | gql 模板、`$page: Int` 声明 |
|
|
141
|
+
| form | urlencoded | `<form>`、`FormData` |
|
|
142
|
+
| 上传 | multipart | `FormData.append` |
|
|
143
|
+
| 路径参数 | `/user/:id` | 路由表 + `useParams` / `$route.params` |
|
|
144
|
+
| 加密体 | 包进 `sign`/`data` | **hook 加密函数入参**(明文在加密前)——算法还原走 50-js-reverse |
|
|
145
|
+
|
|
146
|
+
### 4.3 绑定层——表单 → API 的追踪链
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
表单字段 → onSubmit/handleSubmit → transform → API payload
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
grep -rhoaE 'onFinish|handleSubmit|getFieldsValue|validateFields' js/ | head
|
|
154
|
+
grep -rhoaE '(pick|omit|transform|dayjs|moment)\(' js/ | head
|
|
155
|
+
grep -rhoaE '(required|pattern|enum|validator)\s*:' js/ | head # 校验门=必填/格式/枚举
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
运行时可补:devtools → 请求 → 发起程序(call stack)从 `fetch`/`XHR.send` 往上追组包函数。
|
|
159
|
+
|
|
160
|
+
### 4.4 参数触发矩阵(必做)
|
|
161
|
+
|
|
162
|
+
对每个模块**按操作各录一次**,diff 请求体:
|
|
163
|
+
|
|
164
|
+
| 操作 | 关注字段 |
|
|
165
|
+
|---|---|
|
|
166
|
+
| 列表首屏 | 分页默认值 |
|
|
167
|
+
| 搜索 | keyword、filters |
|
|
168
|
+
| 高级筛选 | optional 字段(不点不出现) |
|
|
169
|
+
| 新建/编辑 | 完整 entity 结构 |
|
|
170
|
+
| 批量/导出 | `ids[]`、`exportType` |
|
|
171
|
+
| 排序/翻页 | `sortField`、`order` |
|
|
172
|
+
|
|
173
|
+
### 4.5 置信度纪律(防止自欺)
|
|
174
|
+
|
|
175
|
+
| 置信度 | 条件 |
|
|
176
|
+
|---|---|
|
|
177
|
+
| 高 | 静态 callsite + 运行时 ≥2 样本一致 |
|
|
178
|
+
| 中 | 仅静态,或仅 1 次运行时 |
|
|
179
|
+
| 低 | 靠响应/报错反推,未验证 |
|
|
180
|
+
| 待触发 | 静态有字段,UI 没跑到(补权限/点高级筛选/联动下拉再录) |
|
|
181
|
+
|
|
182
|
+
**单样本定不了必填/可选**;同 path 不同 body 形状是正常的——按操作分条记录,别硬并 schema。
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 五、权限树还原(大站侧栏空白时用)
|
|
187
|
+
|
|
188
|
+
简单 forge 不灵(侧栏有项、子页空白 / 只出 bootstrap 接口)时,说明权限数据不齐。还原流程:
|
|
189
|
+
|
|
190
|
+
### 5.1 定位权限模块
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
grep -l 'userRouteAuth' js/*.js
|
|
194
|
+
grep -l 'routeMap\|routeLink' js/*.js
|
|
195
|
+
grep -rhoaE 'getResultTree|role_permissions|permissions/all' js/ | head
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
记下:**权限接口 path、响应字段名、消费它的 chunk 文件名**。
|
|
199
|
+
|
|
200
|
+
### 5.2 提取 routeMap(code ↔ path 映射表)
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
# 别名表通常长这样:{DASHBOARD:{name:"仪表盘",link:"/dashboard"},...}
|
|
204
|
+
grep -rhoaE '([A-Z_][A-Z0-9_]*):\{name:"[^"]*",link:"/[^"]+"\}' js/ | head -20
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### 5.3 拼权限树(关键三步)
|
|
208
|
+
|
|
209
|
+
1. 解析 `userRouteAuth = {CODE: {url}, ...}` 拿到全部 code(注意 webpack 别名 `He=o.DASHBOARD` 要顺一遍)
|
|
210
|
+
2. 用 routeMap 把别名 code 换成真实 path
|
|
211
|
+
3. **按 code 前缀推父子**:`MONITOR_ALERT` → 父 `MONITOR`(做映射表兜住非前缀的孤儿节点)
|
|
212
|
+
|
|
213
|
+
产出两份 mock 数据:
|
|
214
|
+
- `role_permissions` stub:`{"permissions":[全部 code], "role_type":"SUPER_ADMIN"}`
|
|
215
|
+
- `permissions/all` stub:树形 `[{code, position, children:[...]}]`
|
|
216
|
+
|
|
217
|
+
### 5.4 校验一致性(别留洞)
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
# permissions 条目数应 ≈ userRouteAuth 条目数
|
|
221
|
+
# routes 应覆盖 routeMap 全部 link —— 缺哪条补哪条
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### 5.5 重跑对比
|
|
225
|
+
|
|
226
|
+
| forge 前 | forge 后(成功) |
|
|
227
|
+
|---|---|
|
|
228
|
+
| 每个路由都是同 3-5 条 bootstrap | 不同路由触发不同模块 API |
|
|
229
|
+
| 只有 locale/i18n | 出现真实业务接口 |
|
|
230
|
+
|
|
231
|
+
仍失败 → 点击侧栏+Tab(权限 gating 可能在交互后才请求)→ 扩 permissions 覆盖按钮级 `hasPermission|checkRole` → 静态兜底(模块 path 已在静态清单,运行时只是补 METHOD/body)。
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## 六、静态收割清单(第一刀)
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
# 1) 入口 HTML 找 script/chunk 线索;webpack/Vite 看 manifest
|
|
239
|
+
# 2) 按 manifest 下载全部 lazy chunk 到 js/
|
|
240
|
+
# 3) 批量提取 endpoint
|
|
241
|
+
grep -rhoaE '"/[a-z][A-Za-z0-9_/\-]{3,}"' js/ | sort -u
|
|
242
|
+
grep -rhoaE '/api/[a-zA-Z0-9_./-]+' js/ | sort -u
|
|
243
|
+
# 4) 路由草案
|
|
244
|
+
grep -rhoaE '(path|route|component)\s*:\s*"[^"]+"' js/ | head -40
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
产出四件套:`api_static.txt`(接口全集)、`routes.txt`(路由)、`js/`(chunk 仓库)、字段候选清单。
|
|
248
|
+
**先把这步做完再碰运行时**——静态无痕、可反复;运行时只补静态给不了的方法/参数/样本。
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## 七、完成定义(逐条勾完才准说"做完了")
|
|
253
|
+
|
|
254
|
+
- [ ] 静态收割四件套齐全,`wc -l` 校验过
|
|
255
|
+
- [ ] 三道门都定位到(键名/字段成功值/权限来源),写进 mock 配置
|
|
256
|
+
- [ ] 进壳成功:访问业务路由不再被弹回 `/login`(注意 hash 路由)
|
|
257
|
+
- [ ] 运行时至少跑通"深度"或"点界面"之一
|
|
258
|
+
- [ ] 参数触发矩阵逐操作录完,产出样本集 + 置信度标注
|
|
259
|
+
- [ ] 权限树还原完成(若有模块空白问题)且已重跑验证
|
|
260
|
+
- [ ] 合并产出:接口总表(path/method/参数/样本/置信度)+ 覆盖缺口诚实标注
|
|
261
|
+
- [ ] 产物落盘到任务目录,路径写进报告
|
|
262
|
+
|
|
263
|
+
**硬禁止(踩了就算跑偏)**:
|
|
264
|
+
|
|
265
|
+
| 禁止 | 替代做法 |
|
|
266
|
+
|---|---|
|
|
267
|
+
| 手工逐个 curl chunk / 反复 grep 大 bundle | 先下载全部 chunk,批量脚本提取 |
|
|
268
|
+
| 同一命令失败 ≥2 次还重复 | 换策略:读日志、改正则、查本文档 |
|
|
269
|
+
| 只录 1 个样本就断言必填/可选 | 触发矩阵多样本 diff |
|
|
270
|
+
| 用"拿真实数据"为由做越权/注入 | 停——那是下一阶段,侦察期只录 outbound |
|
|
271
|
+
| 没走完完成定义就声称"全部接口已拿到" | 逐条勾,缺口如实标注 |
|
|
272
|
+
| 大 bundle(>100KB)直接读进上下文 | 用脚本批处理,只把核心片段喂给分析 |
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## 八、排障速查
|
|
277
|
+
|
|
278
|
+
| 现象 | 处理 |
|
|
279
|
+
|---|---|
|
|
280
|
+
| 静态接口很少 | endpoint 正则放宽;查 manifest 解析 |
|
|
281
|
+
| 进壳后仍跳登录 | 渲染门错了:复查键名/容器/编码/domain |
|
|
282
|
+
| 进壳但模块空白 | 内容门:forge 菜单 + 第 5 节权限树 |
|
|
283
|
+
| 每个接口都跳登录 | 拦截器门:确认 L2 字段中和生效(嵌套字段要递归改) |
|
|
284
|
+
| 响应体全空 | mock 模式下正常——要看参数就去看 **outbound 请求**,别从 stub 响应反推 |
|
|
285
|
+
| WS 帧为 0 | 先交互(点开订阅页),别干等 |
|
|
286
|
+
| Vue 路由还是跳登录 | 注入时机太晚——必须 document-start;或手动清路由守卫 |
|
|
287
|
+
| 400 报嵌套字段错 | 注意外层包装 `data`/`bizData`/`variables` 层级 |
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## 九、与其它模块的衔接
|
|
292
|
+
|
|
293
|
+
| 环节 | 去哪 |
|
|
294
|
+
|---|---|
|
|
295
|
+
| 加密参数(sign/加密算法复刻、混淆还原) | SKILL「50-js-reverse」模块 |
|
|
296
|
+
| 拿到接口后的注入/越权测试 | SKILL「53-web-injection」+「46-pentest」模块 |
|
|
297
|
+
| 接口泄露的源码审计 | SKILL「54-source-leak」模块 |
|
|
298
|
+
| 目标测绘与源站 | SKILL「56-recon-origin」+ `references/recon-commands.md` |
|
|
299
|
+
| 通用侦察命令集 | `references/recon-commands.md` |
|