白盒系列之变量追踪引擎(一)
字数 1367 2025-08-15 21:30:59
白盒扫描之变量追踪引擎实现详解
一、白盒扫描概述
白盒扫描通常在软件开发生命周期的三个阶段进行:
- 开发阶段:使用IDE插件进行本地扫描(效果最佳但实际应用较少)
- 集成阶段:代码提交时进行扫描
- 发布阶段:强制卡点扫描(目前最普遍)
理想状态是在开发阶段进行扫描,但由于IDE插件性能限制(可能导致IDE卡死),实际应用效果不佳。
二、变量追踪引擎架构
变量追踪引擎实现包含6个关键节点:
- 文件语法结构解析及提取
- 解析后语法结构的合理存储
- 危险函数(Sink)和污染数据(Source)的定义
- 调用流图的构建
- 代码的模拟执行
- 误报优化
三、语法结构解析
3.1 抽象语法树(AST)的选择
相比直接分析源代码或字节码,AST具有以下特点:
优势:
- 更易分析代码结构(如区分TemplateLiteral、Identifier等)
- 标准化结构便于自动化处理
劣势:
- 语法结构复杂(简单代码行可能解析为上百行AST)
- 节点类型繁多(JavaScript约150多种指令集)
3.2 AST解析工具
推荐工具:
- JavaScript:Babel(支持ES5/ES6、TypeScript、JSX)
- Java:Eclipse JDT、Soot
- Python:ast.parse
- Go:go/parser包
Babel配置示例:
this.config = {
loc: true,
strictMode: false,
sourceType: 'module',
allowImportExportEverywhere: true,
allowUndeclaredExports: true,
errorRecovery: true,
plugins: [
'typescript',
'classProperties',
'dynamicImport',
'decorators-legacy',
'jsx',
'optionalChaining',
'exportDefaultFrom'
]
};
3.3 AST节点学习资源
- ESTree:JavaScript规范定义
- babel-spec:Babel特定节点扩展定义
- babel-types:TypeScript/JSX节点参考
四、关键信息提取
4.1 快速定位关键节点
使用Babel的@babel/traverse:
const babelTraverse = require('@babel/traverse').default;
babelTraverse(astData, infoCollect(results));
function infoCollect(results) {
return {
VariableDeclarator({node}) {
// 处理变量声明节点
},
// 其他节点类型...
}
}
4.2 单个节点信息解析
采用递归解析方法:
- 设定需要解析的关键信息(如CallExpression的callee)
- 递归遍历子节点,匹配目标属性(如callee→Identifier→name)
- 返回符合条件的节点信息
五、污染源与危险函数定义
5.1 污染源(Source)定义
Web应用中用户可控数据来源:
Express框架:
MemberExpression: [
['req', 'params'],
['req', 'body'],
['req', 'cookies'],
['req', 'query']
],
CallExpression: [
['req', 'get'],
['req', 'param']
]
Koa框架:
MemberExpression: [
['ctx', 'header'],
['ctx', 'headers'],
['ctx', 'origin'],
['ctx', 'href'],
['ctx', 'query'],
['ctx', 'querystring'],
['ctx', 'cookies'],
['ctx', 'body'],
['ctx', 'params']
],
CallExpression: [
['ctx', 'query'],
['ctx', 'query', 'get'],
['ctx', 'request', 'get'],
['ctx', 'cookies', 'get'],
['ctx', 'get']
]
5.2 危险函数(Sink)定义
格式:{index: 参数位置, value: 函数路径}
命令执行:
CallExpression: [
{index: 0, value: ['exec']},
{index: 0, value: ['execSync']},
{index: 0, value: ['child_process', 'exec']},
{index: 0, value: ['child_process', 'execSync']},
{index: 0, value: ['eval']},
{index: 0, value: ['shell', 'exec']}
]
SQL注入:
CallExpression: [
{index: 0, value: ['connection', 'query']},
{index: 0, value: ['conn', 'query']},
{index: 0, value: ['db', 'query']},
{index: 0, value: ['mysql', 'createConnection', 'query']},
{index: 0, value: ['sequelize', 'query']},
{index: 0, value: ['db', 'driver', 'execQuery']}
]
SSRF:
CallExpression: [
{index: 0, value: ['http', 'get']},
{index: 0, value: ['http', 'request']},
{index: 0, value: ['axios']},
{index: 0, value: ['fetch']},
{index: 0, value: ['request']},
{index: 0, value: ['request', 'get']},
{index: 0, value: ['request', 'post']}
]
六、调用链构建
6.1 调用流图选择
全局调用流图:
- 解析所有文件及其引用关系
- 构建完整调用关系图
- 耗时长,不适合实时扫描
局部调用流图:
- 以HTTP请求处理方法为起点
- 按需构建调用关系
- 速度快,适合实时扫描(小型项目秒级,大型项目1分钟左右)
6.2 调用树构建示例
- 以路由处理方法为起点(Source方法)
- 递归解析方法内的调用语句
- 为每个方法体创建节点,记录:
- 方法体信息
- 调用点信息
6.3 调用链路获取
- 遍历调用树的所有叶子节点
- 识别符合Sink定义的节点
- 逆向查找还原完整调用链路
七、核心实现要点总结
- 性能优化:局部调用流图显著优于全局调用流图
- 准确性:递归解析确保不遗漏关键节点
- 可扩展性:通过配置文件定义Source/Sink,便于规则更新
- 误报处理:参数位置(index)的精确指定减少误报
八、后续方向
下一篇将深入讲解基于状态机的模拟执行实现,这是变量追踪引擎最核心的部分。