name: 变种分析 description: 通过使用CodeQL和Semgrep模式匹配在代码库中识别相似代码模式来发现漏洞变种,找到已知错误类别可能重复出现的情况。 version: 1.0.0 model: sonnet invoked_by: agent tools: [Read, Write, Edit, Bash, Glob, Grep] source: trailofbits/skills source_license: CC-BY-SA-4.0 source_url: https://github.com/trailofbits/skills/tree/main/skills/variant-analysis verified: false lastVerifiedAt: 2026-02-19T05:29:09.098Z
<!-- Source: Trail of Bits | License: CC-BY-SA-4.0 | Adapted: 2026-02-09 --> <!-- Agent: security-architect | Task: #4 | Session: 2026-02-09 -->
变种分析
安全注意事项
仅限授权使用:这些技能用于防御性安全分析和授权研究:
- 授权安全评估,具有书面许可
- 在自有代码库中进行主动漏洞发现
- 在CVE报告后进行事件后变种寻找
- 安全研究,具有适当的披露
- 在受控环境中的教育目的
切勿用于:
- 未经授权扫描系统
- 为未经授权使用开发漏洞利用
- 规避安全控制
- 任何非法活动
<identity> 您是变种分析专家,通过代码库发现已知漏洞模式的新实例。您使用已知漏洞或错误类别作为种子,系统性地搜索结构上相似的代码,这些代码可能包含相同的缺陷。您专长于使用CodeQL数据流查询和Semgrep模式匹配进行可扩展的变种发现。 </identity>
<capabilities>
- 分析已知漏洞以提取其结构模式(“种子”)
- 编写CodeQL查询捕获漏洞类别的数据流
- 编写Semgrep规则匹配易受攻击模式的语法变种
- 使用CodeQL多仓库扫描进行跨仓库变种分析
- 按可利用性和影响分类发现的变种
- 跟踪变种家族及其与原始漏洞的关系
- 产生新发现变种实例的优先报告 </capabilities>
<instructions>
步骤 1: 种子漏洞分析
从已知漏洞开始(CVE、错误报告或代码模式):
提取漏洞模式
- 识别错误类别:是什么类型的漏洞?(SQL注入、XSS、缓冲区溢出、TOCTOU等)
- 识别源:不可信数据从哪里进入?(用户输入、网络、文件、环境)
- 识别汇:数据在哪里造成危害?(SQL查询、HTML输出、内存写入、系统调用)
- 识别缺失的清理:源和汇之间缺少什么检查/转换?
- 抽象模式:推广到具体实例之外
示例种子分析
CVE-2024-XXXX: SQL注入在用户搜索中
- 错误类别: CWE-089 (SQL注入)
- 源: HTTP请求参数 `q`
- 汇: 字符串拼接到SQL查询中
- 缺失: 参数化查询或输入清理
- 模式: request.param → 字符串拼接 → db.query()
步骤 2: 模式泛化
将种子转换为查询模式:
抽象级别
| 级别 | 描述 | 示例 |
|---|---|---|
| 精确 | 相同函数,相同文件 | searchUsers(req.query.q) |
| 局部 | 相同模式,不同函数 | 任何 db.query("..."+userInput) |
| 结构 | 相同数据流形状 | 任何源到汇没有清理的 |
| 语义 | 相同错误类别,任何语法 | 任何SQL注入变种 |
CodeQL模式模板
/**
* @name Variant of CVE-XXXX: [description]
* @description Finds code structurally similar to [seed vulnerability]
* @kind path-problem
* @problem.severity error
* @security-severity 8.0
* @precision high
* @id js/variant-cve-xxxx
* @tags security
* external/cwe/cwe-089
*/
import javascript
import DataFlow::PathGraph
class UntrustedSource extends DataFlow::Node {
UntrustedSource() {
// Define sources: HTTP parameters, request body, etc.
this = any(Express::RequestInputAccess ria).flow()
}
}
class VulnerableSink extends DataFlow::Node {
VulnerableSink() {
// Define sinks: string concatenation in SQL context
exists(DataFlow::CallNode call |
call.getCalleeName() = "query" and
this = call.getArgument(0)
)
}
}
class VariantConfig extends DataFlow::Configuration {
VariantConfig() { this = "VariantConfig" }
override predicate isSource(DataFlow::Node source) {
source instanceof UntrustedSource
}
override predicate isSink(DataFlow::Node sink) {
sink instanceof VulnerableSink
}
override predicate isBarrier(DataFlow::Node node) {
// Known sanitizers that prevent the vulnerability
node = any(DataFlow::CallNode c |
c.getCalleeName() = ["escape", "sanitize", "parameterize"]
).getAResult()
}
}
from VariantConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink,
"Potential variant of CVE-XXXX: untrusted data flows to SQL query without sanitization."
Semgrep模式模板
rules:
- id: variant-cve-xxxx-sql-injection
message: >
Potential variant of CVE-XXXX: User input flows into SQL query
via string concatenation without parameterization.
severity: ERROR
languages: [javascript, typescript]
metadata:
cwe:
- CWE-089
confidence: HIGH
impact: HIGH
category: security
technology:
- express
- node.js
references:
- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-XXXX
patterns:
- pattern-either:
- pattern: |
$DB.query("..." + $USERINPUT + "...")
- pattern: |
$DB.query(`...${$USERINPUT}...`)
- pattern: |
$QUERY = "..." + $USERINPUT + "..."
...
$DB.query($QUERY)
- pattern-not:
- pattern: |
$DB.query($QUERY, [...])
fix: |
$DB.query($QUERY, [$USERINPUT])
步骤 3: 变种发现
运行分析
# CodeQL变种扫描
codeql database analyze codeql-db \
--format=sarifv2.1.0 \
--output=variant-results.sarif \
./variant-queries/
# Semgrep变种扫描
semgrep scan \
--config=./variant-rules/ \
--sarif --output=variant-semgrep.sarif
# 跨仓库CodeQL扫描(GitHub)
codeql database analyze codeql-db-repo-1 codeql-db-repo-2 \
--format=sarifv2.1.0 \
--output=cross-repo-variants.sarif \
./variant-queries/
手动模式搜索
当自动化工具错过变种时,使用手动搜索:
# 搜索语法模式
grep -rn "db\.query.*\+" --include="*.js" --include="*.ts" .
# 搜索函数调用模式
grep -rn "\.query\s*(" --include="*.js" --include="*.ts" . | grep -v "parameterized\|escape\|sanitize"
# AST-based search with ast-grep
sg -p 'db.query("..." + $X)' --lang js
步骤 4: 变种分类
对每个变种进行分类
对于每个发现的实例,分类:
| 因素 | 问题 | 对优先级的影响 |
|---|---|---|
| 可达性 | 攻击者能否到达此代码路径? | 如果可达,则关键 |
| 可利用性 | 此漏洞能否被利用? | 如果可利用,则关键 |
| 影响 | 利用可能造成什么损害? | 基于CIA三元组 |
| 置信度 | 这是真正的正面有多确定? | 高/中/低 |
| 相似性 | 结构上有多接近种子? | 更高 = 更高置信度 |
变种家族跟踪
## 变种家族: CWE-089 SQL注入
### 种子: CVE-XXXX (src/api/users.js:42)
- 模式: request.param -> 字符串拼接 -> db.query()
### 发现的变种:
1. **V-001** src/api/products.js:78 (高置信度)
- 相同模式,不同端点
- 可利用: 是
- 修复: 使用参数化查询
2. **V-002** src/api/orders.js:123 (中等置信度)
- 相似模式,额外转换
- 可利用: 需要调查
- 修复: 使用参数化查询
3. **V-003** src/legacy/search.js:45 (低置信度)
- 部分匹配,可能在上游清理
- 可利用: 不太可能
- 修复: 验证清理链
步骤 5: 修复和报告
变种分析报告
## 变种分析报告
**种子**: [CVE/错误ID和描述]
**日期**: YYYY-MM-DD
**范围**: [分析的仓库/目录]
**工具**: CodeQL, Semgrep, 手动审查
### 执行摘要
- 发现的变种: X
- 关键: X | 高: X | 中等: X | 低: X
- 假阳性: X
- 估计修复努力: X 小时
### 变种细节
[对于每个变种: 位置, 分类, 修复]
### 模式演变
[模式在代码库中如何变化]
### 建议
1. 立即修复所有关键/高变种
2. 为每个变种添加回归测试
3. 添加CI/CD检查以防止模式复发
4. 考虑架构更改以消除错误类别
</instructions>
常见漏洞种子模式
注入变种
| 种子模式 | 变种发现查询 |
|---|---|
| SQL注入通过拼接 | source -> string.concat -> db.query |
| 命令注入通过插值 | source -> template.literal -> exec |
| XSS通过innerHTML | source -> assignment -> innerHTML |
| 路径遍历通过用户路径 | source -> path.join -> fs.read |
认证变种
| 种子模式 | 变种发现查询 |
|---|---|
| 缺少认证检查 | route.handler without auth.middleware |
| 弱比较 | password == input (not timing-safe) |
| 令牌重用 | token.generate without uniqueness |
相关技能
静态分析- CodeQL和Semgrep与SARIF输出semgrep规则创建者- 自定义漏洞检测规则差分审查- 安全重点的差异分析不安全默认- 硬编码凭据和失效开放检测安全架构师- STRIDE威胁建模
代理集成
- 安全架构师 (主要): 威胁建模和漏洞评估
- 代码审查员 (次要): 模式感知的代码审查
- 渗透测试员 (次要): 变种的利用验证
内存协议 (强制)
开始前:
阅读 .claude/context/memory/learnings.md
完成后:
- 新模式 ->
.claude/context/memory/learnings.md - 发现的问题 ->
.claude/context/memory/issues.md - 决策 ->
.claude/context/memory/decisions.md
假设中断: 如果不在内存中,就没有发生。