在第一篇 SyntaxFlow 引入文章 中,我们介绍了基本 SyntaxFlow 可以解决的问题,虽然我们屏蔽了大量的技术细节,但是很多读者并不只局限在“介绍”中,提出了很多比较经典的教科书中的问题。在接下来的系列文章中,我们将会花一些篇幅为大家介绍 SyntaxFlow 和 SSA IR 是如何解决教科书中的“高级话题”的。
当然,上篇文章中缺失了一些基本的引导,我们创建了
github.com/yaklang/syntaxflow-zero-to-hero 代码仓库,感兴趣的用户可以率先尝鲜~
什么是跨函数(跨过程)分析
跨函数分析我们在教科书中经常叫它“跨过程”分析,其核心是让数据流分析可以穿透一个函数执行过程。当然,我们已经默认现有的 IR 格式已经解决了数据流的问题。所以我们可以基于已经被理清楚的数据流进行分析(如果你有疑问,请先阅读《编译拾遗》两篇)。
我们用一个简单的 MVP 来说明跨过程分析的最核心的用途,大家一看便知。
在上述内容中,我们很轻易地发现了,Runtime.getRuntime().exec(actualCmd)
和 cmd
参数其实看起来通过 crossFunction 进行了链接(从语法上连接成立),但是仔细观察就会发现
public String crossFunction(String cmd) {
return "echo 'Hello World'";
}
我们的 crossFunction 的返回值却和 cmd 没有半毛钱关系,那么从数据上的连接就是不成立的,这是一个数据流问题。
从上面的这个案例来思考分析跨过程分析的意义,如果说我们可以洞悉 crossFunction,那么得出的结论是:“这命令执行的点不可控”,如果我们不知道 crossFunction 的实现,则得到的结论是“cmd”参数会影响 exec,这是一个命令执行漏洞点。如果不具备跨过程能力,得到的结论是不正确的,会有大量误报。
YAK
自动跨过程的表现形式
在进行下面的实验中,如果用户对
https://github.com/yaklang/syntaxflow-zero-to-hero 有一定了解,并尝试过 Hello World,会减轻很大部分的操作负担。
在保证安装了 yak 命令行之后,可以在
yak ssa-compile -t lesson-2 --program lesson2
编译后,执行审计代码
yak ssa --program lesson2 --sf 'getRuntime().exec(* #-> * as $source)'
这个含义是,审计 getRuntime() 这个成员的 exec 调用,追踪 exec 参数的最顶级定义(自动跨过程)。
我们会看到执行结果为
❯ yak ssa --program lesson2 --sf 'getRuntime().exec(* #-> * as $source)'
[INFO] 2024-06-20 11:49:15 [ssacli:117] using syntaxflow rule will skip compile
[INFO] 2024-06-20 11:49:15 [ssa:42] init ssa database: /Users/v1ll4n/yakit-projects/default-yakssa.db
[INFO] 2024-06-20 11:49:15 [database_marshal:410] load scope from id: 210331 when loading basic block
[INFO] 2024-06-20 11:49:15 [database_marshal:410] load scope from id: 210317 when loading basic block
[INFO] 2024-06-20 11:49:15 [database_marshal:410] load scope from id: 210315 when loading basic block
[INFO] 2024-06-20 11:49:15 [database_marshal:410] load scope from id: 210314 when loading basic block
[WARN] 2024-06-20 11:49:15 [ssa_predefined:115] this value function package is nil
[ERRO] 2024-06-20 11:49:15 [ssa_predefined:314] SetType: value is not Value but is 1190522
[INFO] 2024-06-20 11:49:15 [ssacli:183] syntax flow query result:
[INFO] 2024-06-20 11:49:15 [ssacli:188] ===================== Variable:source ===================
[INFO] 2024-06-20 11:49:15 [ssacli:214] lesson-2/runtime_cross.java:3:15 - 3:35: "echo 'Hello World'"
IR: 1190497: "echo 'Hello World'"
1| public class External {
2| public String crossFunction(String cmd) {
3| return "echo 'Hello World'";
4| }
5| }
6| public class RuntimeExecCrossFunction {
我们虽然从语法上观察到了这个结果的可能受控制,但是代码分析并不应该给出控制位点,这个印证了我们自动跨过程分析的技术实现效果。
YAK
扫盲:变量追踪与数据流追踪
很多人会觉得上面这个跨过程案例比较难写代码审计,实际上是陷入了“变量”的陷阱,实际上我们在 SyntaxFlow 分析追踪数据流的过程中,并不知道变量是何物。我们只知道“值”什么时候存在,什么时候消亡。
上面的代码看起来压缩成了一行
Process proc = Runtime.getRuntime().exec(extern.crossFunction(cmd));
yak ssa --program lesson2 --sf 'getRuntime().exec(* #-> * as $source)' --dot
将会得到输出中包含:
[INFO] 2024-06-20 11:57:55 [ssacli:195] ===================== DOT ===================
strict digraph {
rankdir = "BT";
n1 [label="t1190497: \"echo 'Hello World'\""]
n2 [label="t1190523: #1190514.crossFunction(t1190514,cmd)"]
n2 -> n1 [label=""]
}
我们使用 dot 渲染图可以看最后一个目标数据流是什么,同样的,我们可以观察别的数据流
YAK
复杂数据流
我们决定观察一个数据流叫 fis,那我们如何获取这个 fis 的数据流?可以直接使用 fis #-> * as $data 来执行。
yak ssa --program lesson2 --sf 'fis #-> * as $data' --dot
执行这个语句,将会在输出结果中看到:
[INFO] 2024-06-20 12:02:53 [ssacli:195] ===================== DOT ===================
strict digraph {
rankdir = "BT";
n4 [label="#1190518.exec"]
n5 [label="t1190497: \"echo 'Hello World'\""]
n6 [label="t1190527: fis=#1190524.getInputStream()"]
n15 [label="t1190524: proc=#1190518.exec(t1190523)"]
n18 [label="t1190523: #1190514.crossFunction(t1190514,cmd)"]
n1 [label="#1190524.getInputStream"]
n2 [label="Runtime"]
n3 [label="#1190515.getRuntime"]
n11 [label="t1190518: #1190515.getRuntime()"]
n11 -> n3 [label=""]
n1 -> n3 [label=""]
n15 -> n4 [label=""]
n1 -> n4 [label=""]
n15 -> n18 [label=""]
n18 -> n5 [label=""]
n1 -> n5 [label=""]
n6 -> n1 [label=""]
n4 -> n2 [label=""]
n1 -> n2 [label=""]
n4 -> n3 [label=""]
n3 -> n2 [label=""]
}
渲染成图之后将会看到
我们通过底层 t1190527号 SSA 指令对应的数据流,直接追踪到了 Hello World。用人能听的描述可以说:“getInputStream() 的执行结果和 Runtime 还有 "echo Hello World" 有关(支配关系)”。
总结
本文描述的内容中包含了 SyntaxFlow 的技术追踪跨过程数据流的能力,并且提供了在上一次文章的基础上,提供可以供用户使用的训练指引,后续我们将会逐步完善这些能力的细节实现并且逐步向大家揭示 SyntaxFlow 技术的技术实现:github.com/yaklang/syntaxflow-zero-to-hero
END
更新记录
YAK官方资源
Yak 语言官方教程:
https://yaklang.com/docs/intro/
Yakit 视频教程:
https://space.bilibili.com/437503777
Github下载地址:
https://github.com/yaklang/yakit
Yakit官网下载地址:
https://yaklang.com/
Yakit安装文档:
https://yaklang.com/products/download_and_install
Yakit使用文档:
https://yaklang.com/products/intro/
常见问题速查:
https://yaklang.com/products/FAQ
超级牛的日常碎碎念:
https://space.bilibili.com/3546645784430965?spm_id_from=333.999.0.0