Skip to main content

数据流运算:追踪定义和使用

在静态代码分析和安全审计中,Use-Def 链(Use-Definition Chain,简称 UD 链)和 Def-Use 链(Definition-Use Chain,简称 DU 链)的追踪是至关重要的技术。它们能够帮助审计员精准地追踪变量或函数的使用与定义关系,从而有效识别潜在的安全漏洞。SyntaxFlow 支持对 UD 链和 DU 链的追踪,充分利用 SSA(静态单赋值)形式的优势,实现对数据流的精确追踪。本文将详细介绍 SyntaxFlow 中 Use-Def 链追踪的运算定义、语法规则、使用实例以及实战中的注意事项。

简介

Use-Def 链Def-Use 链是数据流分析中的基本概念。

  • Use-Def 链(UD 链):描述一个变量的使用位置与其定义位置之间的关系。
  • Def-Use 链(DU 链):描述一个变量的定义位置与其后续使用位置之间的关系。

通过追踪这些链,审计员可以精准地识别数据在代码中的流向,从而发现潜在的安全漏洞,如 SQL 注入、命令注入、跨站脚本(XSS)等。

SyntaxFlow 提供了强大的语法规则,支持对 UD 链和 DU 链的递归追踪,以实现高效、精确的代码审计。

Use-Def 链追踪语法定义

SyntaxFlow 中,Use-Def 链追踪通过特定的运算符来实现。这些运算符可以简化复杂的链式调用追踪,使得规则编写更加简洁和高效。以下是相关的语法定义和运算符解释。

运算符概览

  1. ->-->(使用链追踪)

    • ->:追踪到下一个使用该变量或函数的地方。这是追踪变量“一级”使用的基本方式。
    • -->:追踪直到使用链的结束。这个运算符将继续追踪,直到没有更多的使用,即完全展开整个使用链。
  2. #>#->(定义链追踪)

    • #>:追踪到变量或函数的直接定义点。通常指变量被赋值或函数被声明的地方。
    • #->:追踪直到定义链的起点。使用这个运算符可以追踪到变量或函数的最初定义,穿过所有中间的定义点。
  3. -{}-{...}->(定制追踪深度或上下文)

    • -{}:用于在追踪时设置上下文或参数,如追踪深度。
    • -{depth: 5}->:表示追踪使用链,且追踪深度限制为5层。

语法结构

filterItem
: filterItemFirst # First
...
| '->' # NextFilter
| '#>' # DefFilter
| '-->' # DeepNextFilter
| '-{' (recursiveConfig)? '}->' # DeepNextConfigFilter
| '#->' # TopDefFilter
| '#{' (recursiveConfig)? '}->' # TopDefConfigFilter
...
;
  • filterItemFirst:定义过滤项的起始。
  • ->:向下追踪一级使用链节点。
  • -->:向下追踪使用链节点直到链结束。
  • #>:向上追踪一级定义链节点。
  • #->:向上追踪定义链直到链结束。
  • -{...}->:在追踪过程中设置参数,如追踪深度。

使用实例与解释

通过具体的代码案例,结合 SyntaxFlow 的语法规则,可以更直观地理解 Use-Def 链追踪的应用。

案例一:审计针对 ProcessBuilder 的 RCE

审计代码示例

import java.io.*;

public class TestRCE {
public static void main(String[] args) {
String cmd = "ping example.com";

// 这行代码将被上面的规则捕获
ProcessBuilder pb = new ProcessBuilder(cmd.split(" "));
pb.start();
}
}

SyntaxFlow 规则案例

// 审计潜在的远程代码执行风险
desc(title: "rce")
// 捕获创建 ProcessBuilder 对象时的所有参数
ProcessBuilder(* as $cmd) as $builder
// 追踪 ProcessBuilder 对象启动进程的方法调用
$builder.start() as $execBuilder
// 检查是否成功执行了 start 方法,如果未执行,则可能存在漏洞
check $execBuilder then "fine" else "rce 2 SyntaxFlow error"

执行效果

执行以下命令进行审计:

yak ssa -t . --program rce && yak sf --program rce rce.sf

输出示例:

[INFO] 2024-06-26 14:11:10 [ssacli:132] start to compile file: .
[INFO] 2024-06-26 14:11:10 [ssacli:148] compile save to database with program name: rce
[INFO] 2024-06-26 14:11:10 [ssa:42] init ssa database: /Users/v1ll4n/yakit-projects/default-yakssa.db
[INFO] 2024-06-26 14:11:10 [language_parser:46] parse project in fs: *filesys.LocalFs, localpath: .
...
...
[INFO] 2024-06-26 14:11:10 [ssacli:272] syntax flow query result:
rule md5 hash: d04b7fc1476e8957cdad9f8ba36214a6
rule preview: // 审计潜在的远程代码执行风险 desc(title: "rc...e" else "rce 2 SyntaxFlow error"
description: {title: "title", $execBuilder: "fine"}
Result Vars:
cmd:
t1283661: Undefined-ProcessBuilder
Sample.java:8:32 - 8:62
t1283666: Undefined-cmd.split(" ")
Sample.java:8:51 - 8:61
builder:
t1283662: Undefined-ProcessBuilder
Sample.java:8:32 - 8:62

解释

  • ProcessBuilder(* as $cmd) as $builder:捕获 ProcessBuilder 构造函数的所有参数,并存储在 $cmd 中,同时将 ProcessBuilder 对象存储在 $builder 中。
  • $builder.start() as $execBuilder:追踪 ProcessBuilder 对象调用 start() 方法,并将结果存储在 $execBuilder 中。
  • check $execBuilder then "fine" else "rce 2 SyntaxFlow error":检查 start() 方法是否成功执行,若未执行则报告潜在的 RCE 漏洞。

案例二:GroovyShell 代码执行漏洞

审计代码示例

package org.vuln.javasec.controller.basevul.rce;
import groovy.lang.GroovyShell;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/home/rce")
public class GroovyExec {

@GetMapping("/groovy")
public String groovyExec(String cmd, Model model) {
GroovyShell shell = new GroovyShell();
try {
shell.evaluate(cmd);
model.addAttribute("results", "执行成功!!!");
} catch (Exception e) {
e.printStackTrace();
model.addAttribute("results", e.toString());
}
return "basevul/rce/groovy";
}
}

SyntaxFlow 规则案例

// 审计通过 GroovyShell 实例执行代码的情况
desc(title: "groovy shell eval")

// 捕获 GroovyShell 的 evaluate 方法调用时传递的所有参数
GroovyShell().evaluate(* as $cmd)
// 追踪参数 $cmd 的定义来源,直到最初的定义点
$cmd #-> * as $target

// 检查是否能追踪到 $cmd 的源头,若无法追踪,则可能存在代码注入风险
check $target then "fine" else "not found groovyShell.evaluate parameter"

执行效果

执行以下命令进行审计:

yak ssa -t . --program groovy && yak sf --program groovy rce.sf

输出示例:

...
...
[INFO] 2024-06-26 14:24:11 [ssacli:272] syntax flow query result:
rule md5 hash: 17476c2166f26d3dfc5fe3f1c116451b
rule preview: // 审计通过 GroovyShell 实例执行代码的情况 de... groovyShell.evaluate parameter"
description: {title: "title", $target: "fine"}
Result Vars:
cmd:
t1323355: Parameter-cmd
Groovy.java:13:29 - 13:39
_:
t1323363: Undefined-shell.evaluate(valid)
Groovy.java:16:18 - 16:31
target:
t1323355: Parameter-cmd
Groovy.java:13:29 - 13:39

解释

  • GroovyShell().evaluate(* as $cmd):捕获 GroovyShell 实例调用 evaluate 方法时传递的所有参数,并存储在 $cmd 中。
  • $cmd #-> * as $target:追踪 $cmd 的定义来源,直到最初的定义点,将结果存储在 $target 中。
  • check $target then "fine" else "not found groovyShell.evaluate parameter":检查 $cmd 的定义点是否可追踪,若无法追踪则提示潜在的代码注入风险。

实战中的注意事项

在实际应用中,使用 Use-Def 链追踪时需要注意以下几点:

1. 上下文敏感

理解代码的上下文非常重要,尤其是在处理复杂逻辑或大型代码库时。使用 -{}-> 运算符来设置适当的追踪参数,可以帮助维护追踪的可管理性。

2. 性能考虑

在大型项目中,使用 -->#-> 可能会导致性能开销。建议在可能的情况下,限制追踪的深度或明确追踪的起始点和终点,以提高审计效率。

3. 追踪的精确性

使用运算符时,需要确保理解每个符号的具体含义,以便精确地捕捉到所需的数据流和定义点。避免误匹配和漏匹配。

4. 处理递归和循环

在遇到递归函数或循环结构时,需谨慎设置追踪深度和条件,以防止无限追踪或遗漏重要的追踪点。

重要性与优势

Use-Def 链Def-Use 链的追踪在代码审计和安全分析中具有重要意义,主要优势包括:

1. 精准的数据流追踪

通过追踪变量或函数的使用与定义关系,能够精准地识别数据在代码中的流向,发现潜在的安全漏洞。

2. 充分利用 SSA 优势

SSA 形式的代码确保每个变量在生命周期内只被赋值一次,简化了数据流分析,提升了追踪的准确性和效率。

3. 简化规则编写

SyntaxFlow 提供的运算符和语法规则,使得复杂的数据流追踪过程更加简洁高效,减少了手动编写繁琐规则的工作量。

4. 提升审计覆盖率

通过递归追踪,确保无论目标变量或函数通过多少层链式调用被使用,都能被准确捕捉到,提升审计的全面性。

最佳实践

为了充分发挥 SyntaxFlow 中 Use-Def 链追踪的优势,建议遵循以下最佳实践:

1. 明确目标方法或变量

确保目标方法或变量名准确无误,以避免误匹配和漏匹配。例如,在定义规则时,仔细检查方法名和变量名的拼写。

2. 合理设置追踪深度

根据代码的复杂度和实际需求,合理设置追踪深度或使用限定条件,避免过度追踪导致性能问题。

3. 结合其他过滤条件

Use-Def 链追踪可以与其他过滤条件(如参数类型、返回值等)结合使用,提升规则的精准度。例如,可以结合方法参数的类型或调用环境,进一步筛选潜在的风险点。

4. 模块化编写规则

将复杂的审计任务分解为多个模块,每个模块专注于特定的链式调用层级,提升规则的可读性和可维护性。

5. 定期审查和更新规则

随着代码库的演进,定期审查和更新审计规则,确保其始终适用于最新的代码结构和调用模式,保持审计的有效性。

总结

Use-Def 链Def-Use 链的追踪是静态代码分析和安全审计中的核心技术。通过 SyntaxFlow 提供的运算符和语法规则,审计员能够高效、精准地追踪变量和函数的使用与定义关系,发现潜在的安全漏洞。结合实际案例的解析,本文展示了如何在实际审计中应用这些规则,从而提升审计效率和覆盖率。

掌握并应用本文介绍的 Use-Def 链追踪方法和最佳实践,能够帮助开发和安全团队更有效地进行代码审计和安全分析,保障代码的整体安全性与稳定性。