在第一篇 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));
人读起来非常精炼,但是代码审计的难度非常大,如果要使用“词法变量”概念来描述的话,甚至我们连 exec 参数的变量应该是谁都很难表达。更不用说递归调用的问题了。
实际翻译成 SSA 之后,“临时变量”也会自动建立 Use-Def 关系,因此我们可以成功追踪。
我们可以用一个很直观的图来表示这次审计出结果的数据流追踪问题(新版本中追加 --dot):
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
更新记录
Yakit v1.3.4-sp1
1. 重构插件商店交互,将商店、我的和本地进行合并
2. 连接引擎前增加引擎校验
3. 连接引擎增加切换端口功能
4. 优化导入导出插件逻辑
5. 修复websocket包切换时未刷新数据的问题
6. 插件调试页面左侧参数栏可进行拖拽
7. 优化history-网站树的样式
8. 手动劫持增加显示响应延迟时间
9. 增加性能采样功能,卡顿时可点击性能采样采集卡顿数据
10. 修复编辑器高亮显示偏移问题
11. 第三方应用配置优化为后端控制参数和类型
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
长按识别添加工作人员
开启Yakit进阶之旅