SyntaxFlow 快速入门指南
前置条件
在开始使用 SyntaxFlow 之前,请确保您的系统上已安装 Yaklang 环境。设置此环境的最简单方法是使用 Yaklang 的预编译环境:
bash <(curl -sS -L http://oss.yaklang.io/install-latest-yak.sh)
安装完成后,运行以下命令验证版本:
yak version
要使用 SyntaxFlow 的最新功能,建议使用 Yaklang 版本 1.3.7 或更高版本。
虽然安装基本的执行环境是必要的,但拥有编程概念的基础知识也将大有裨益。熟悉 PHP、JavaScript 或 Java 等语言将有助于理解我们将要探讨的示例。
开始使用
假设您对 SSA(静态单赋值形式) 和 SyntaxFlow 的语法都不熟悉,让我们从最基础的开始。
步骤 1:克隆项目仓库
首先,将 syntaxflow-zero-to-hero
仓库克隆到您的本地机器上,并进入 lesson-1-hello-world
目录:
git clone https://github.com/yaklang/syntaxflow-zero-to-hero
cd syntaxflow-zero-to-hero/lesson-1-hello-world/
步骤 2:编译 Hello World 程序
与传统的编程语言中编写 println("Hello World")
不同,SyntaxFlow 的操作方式有所不同。要使用 SyntaxFlow,我们需要将要分析的代码编译成特定的 SSA 格式。
在 lesson-1-hello-world
目录下,执行以下命令:
yak ssa -t . --program lesson1
注意: --program lesson1
选项将您的程序命名为 lesson1
。在后续的命令中,您将引用此程序名称,因此请记住它。
执行后,您应看到显示编译过程的输出:
[INFO] 2024-06-25 22:57:36 [ssacli:131] start to compile file: .
[INFO] ...(更多日志)...
[INFO] 2024-06-25 22:57:37 [ssacli:162] finished compiling..., results: 1
消息 finished compiling..., results: ...
确认编译已成功完成。
理解源码
我们正在分析的源代码被有意设计得简单,以展示 SyntaxFlow 的功能:
@Controller
@RequestMapping("/home/rce")
public class RuntimeExec {
@RequestMapping("/runtime")
public String RuntimeExec(@GetParam(value="id") String cmd, Model model) {
StringBuilder sb = new StringBuilder();
String line;
try {
var runtimeInstance = Runtime.getRuntime();
Process proc = runtimeInstance.exec(cmd);
} catch (IOException e) {
// 处理异常
}
return "basevul/rce/runtime";
}
}
此代码片段表示一个可能根据用户输入执行系统命令的 Java 控制器。请注意,为了简化,一些典型的 Java 类组件(如 import
语句和包声明)被省略。尽管有这些省略,Yaklang SSA 编译器仍然可以处理该代码。
执行 SyntaxFlow 规则
现在我们已经编译了程序,接下来我们将执行一个 SyntaxFlow 规则 来分析它。在相同的目录下,有一个名为 lesson-1.sf
的规则文件,内容如下:
desc(title: "这是 SyntaxFlow 的 Hello World 示例,简单而有效!")
Runtime.getRuntime().exec(* #-> * as $source) as $sink;
check $source then "找到命令执行参数来源(依赖)" else "未找到参数来源";
check $sink then "找到命令执行点" else "未检测到命令执行";
此规则使用 SyntaxFlow 的查询语言来检测 Java 代码中的命令执行漏洞。它的重点是:
- 识别命令执行的 源头(用户输入)。
- 定位命令执行发生的 位置(汇点)。
此规则的关键行是:
Runtime.getRuntime().exec(* #-> * as $source) as $sink;
理解 #->
运算符
#->
运算符用于追踪一个值的 支配者。- 它回答的问题是:“是什么影响了这个特定的值?”
- 它遵循 Use-Def 链,帮助分析一个值的来源和使用方式。
- 这对于理解数据流和识别潜在的安全问题至关重要。
运行 SyntaxFlow 规则
要执行规则,请运行:
yak sf --program lesson1 lesson-1.sf
确保 --program
参数与您在编译期间使用的名称(lesson1
)一致。
执行后,您应看到类似以下的输出:
[INFO] 2024-06-25 23:35:24 [ssacli:221] start to use SyntaxFlow rule: lesson-1.sf
[INFO] ...(更多日志)...
[INFO] 2024-06-25 23:35:24 [ssacli:272] syntax flow query result:
rule md5 hash: 389009d4257afd3ee509af4749936a3b
rule preview: desc(title: "这是 SyntaxFlow 的 Hello World...then "找到命令执行点" else "未检测到命令执行";
description: {title: "这是 SyntaxFlow 的 Hello World 示例,简单而有效!", $source: "找到命令执行参数来源(依赖)", $sink: "找到命令执行点"}
Result Vars:
source:
t2612544: Parameter-cmd
HelloWorld.java:5:30 - 5:62
sink:
t2612569: Undefined-runtimeInstance.exec(valid)
HelloWorld.java:12:43 - 12:52
分析结果
输出提供了有价值的信息:
-
描述信息:
"找到命令执行参数来源(依赖)"
"找到命令执行点"
-
结果变量:
$source
: 指示潜在的危险输入来源于何处。$sink
: 显示输入在关键操作(命令执行)中被使用的位置。
将结果映射到代码
从输出中:
-
源(
$source
):- 变量:
Parameter-cmd
- 位置:
HelloWorld.java:5:30 - 5:62
- 这指出了方法参数
String cmd
,它来自用户输入。
- 变量:
-
汇点(
$sink
):- 操作:
runtimeInstance.exec(valid)
- 位置:
HelloWorld.java:12:43 - 12:52
- 这是用户输入在
exec
方法中被使用的地方,该方法执行系统命令。
- 操作:
理解规则检查
在规则文件中,我们有:
check $source then "找到命令执行参数来源(依赖)" else "未找到参数来源";
check $sink then "找到命令执行点" else "未检测到命令执行";
这些检查基于是否找到 $source
和 $sink
来输出消息。
总结
在这个快速入门指南中,您已经学会了:
-
将代码编译为 SSA 形式:
- 使用
yak ssa -t ./project-path --program name
- 为使用 SyntaxFlow 分析代码做好准备。
- 使用
-
编写 SyntaxFlow 规则:
- 创建包含特定查询和检查的
rule.sf
文件。 - 定义您希望在代码中检测的内容。
- 创建包含特定查询和检查的
-
执行 SyntaxFlow 规则:
- 运行
yak sf --program name rule.sf
- 执行分析并输出结果。
- 运行
关键要点
- SyntaxFlow 是用于静态代码分析的强大工具,特别适用于安全审计。
- 理解像
#->
这样的运算符对于追踪数据流和识别漏洞至关重要。 - 该过程包括编译代码、编写规则和分析结果。
- 即使是简单的示例也能发现关键的安全问题,例如命令注入漏洞。
下一步
现在您已经完成了本快速入门指南:
- 探索更多 SyntaxFlow 的功能和运算符。
- 编写您自己的规则来检测不同类型的漏洞。
- 分析更复杂的代码库以获得更深入的见解。
- 参考 SyntaxFlow 文档 以获取高级用法和最佳实践。
通过遵循本指南,您已经迈出了利用 SyntaxFlow 进行有效代码分析和安全审计的第一步。继续尝试和学习,以进一步提升您的技能!