代码审计技术在过去的一段时间的发展中,似乎受到了一个魔咒,安全从业人员的很难摆脱 *QL的影响,毕竟编译成数据库,然后通过 QL 来查询。或者通过把 AST 解析进 neo4j,通过 neo4j 的 QL: Cypher 或 API 来查询。甚至于我们在不使用 *QL 式的辅助工具的时候,也会通过 IDE 的 Query(关键字查询,定义查询,文件搜索)。甚至于我们也会戏称“搜索”是“代码审计”的核心操作。现状确实是这样,但是以搜索为切入点也会遇到其他的问题:
如何搜索一个变量是函数调用?
“代码分析”与“数理证明“
这一段话非常抽象,如果阅读这个抽象描述有问题的话,可以阅读以下 ChatGPT-4 的解读。 这段话概述了使用符号执行和约束求解技术(特别是在 SMT 或 SAT 求解器上下文中)来进行代码分析的基本过程。这里提供了对这一技术的高层次描述,并说明了其在识别代码中的安全漏洞(如 source 到 sink 的数据流问题)方面的应用。以下是对这一技术描述的几个关键点的评价: 正确性和准确性 1. 转化代码问题为符号运算和定理证明问题:描述准确地反映了符号执行技术的核心理念,即将程序的执行路径转化为符号路径,然后通过逻辑约束来进行推理和验证。这是静态代码分析中常见的方法,用于分析程序可能的执行行为而无需实际运行程序。 2. 使用约束求解算法找到符号解:这正确地指出了在符号执行中的一个关键步骤,即利用如 SMT (Satisfiability Modulo Theories) 或 SAT (Boolean Satisfiability Problem) 求解器来解决这些约束。如果求解器能够找到满足所有约束的解,这意味着存在一种特定的输入或程序状态,可以使程序执行到某一特定路径。 应用的实用性 3. 把符号解转化成具体的代码位置:这一步骤非常关键,因为它将抽象的符号结果转化为可以操作的具体信息。例如,在安全性分析中,这可以帮助开发者准确地定位到可能存在漏洞的代码行或路径。 4. 找到 source 和 sink 的交汇点:在安全性分析中,识别数据从潜在的不安全源(source)流向敏感汇(sink)的路径是至关重要的。这种分析可以帮助识别例如 SQL 注入、跨站脚本(XSS)等常见的安全漏洞。 技术限制 5. 可能无法求解:这一点正确地指出了符号执行和约束求解中的一个主要挑战,即在某些情况下,由于路径爆炸问题、复杂的约束或资源限制,求解器可能无法找到解或在合理时间内完成求解。 结论 这段描述提供了一个对使用符号执行和约束求解进行代码分析的有效概述。它正确地捕捉了这种技术的工作原理和在实际代码安全性分析中的应用。同时,也简要提到了在实施这种分析时可能遇到的挑战,如复杂性和求解的可行性问题。这种技术是静态代码分析领域的重要组成部分,尤其是在寻求自动化安全审计和缺陷识别方面。
构建符号化系统的两层理解
这两种理解不论是哪种都并不亲民,而且据我们所知,大部分漏洞并不需要约束求解,甚至很多就是普通的过滤,或者数据流到了危险函数中的参数了,如果有清晰的数据流可达性判断,就可以得到正确的结果,并不需求解什么内容。
“代码分析”与“图算法“
可选的跨过程分析需要从某一个节点扩展出去。
OOP 实现中,object.method
和 object
往往应该被视为有某种关联的对象。
闭包噩梦:在闭包系统中,捕获变量(逃逸变量)可能并不是单纯数据流或者图可以表达得出来的。
...
新的技术方案:SyntaxFlow
1.编译SSA IR
@Controller
@RequestMapping("/home/rce")
public class RuntimeExec {
@RequestMapping("/runtime")
public String RuntimeExec(String cmd, Model model) {
StringBuilder sb = new StringBuilder();
String line;
try {
Process proc = Runtime.getRuntime().exec(cmd);
InputStream fis = proc.getInputStream();
InputStreamReader isr = new InputStreamReader(fis, "GBK");
BufferedReader br = new BufferedReader(isr);
while ((line = br.readLine()) != null) {
sb.append(line).append(System.lineSeparator());
}
} catch (IOException e) {
e.printStackTrace();
sb.append(e);
}
model.addAttribute("results", sb.toString());
return "basevul/rce/runtime";
}
}
❯ tree /tmp/javatest
/tmp/javatest
└── a1
└── test.java
2 directories, 1 file
❯ yak ssa -t /tmp/javatest --program sf1
2.编写 SyntaxFlow 审计语句
7| String line;
8|
9| try {
10| Process proc = Runtime.getRuntime().exec(cmd);
11|
12| InputStream fis = proc.getInputStream();
13| InputStreamReader isr = new InputStreamReader(fis, "GBK");
❯ yak ssa --program sf1 --sf 'getRuntime()'
[INFO] 2024-06-13 15:04:18 [ssacli:86] using syntaxflow rule will skip compile
[INFO] 2024-06-13 15:04:18 [ssa:38] init ssa database: /Users/v1ll4n/yakit-projects/default-yakssa.db
......
......
......
[INFO] 2024-06-13 15:04:18 [ssacli:146] syntax flow query result:
[INFO] 2024-06-13 15:04:18 [ssacli:148] ===================== Variable:_ ===================
[INFO] 2024-06-13 15:04:18 [ssacli:174] /tmp/javatest/a1/test.java:10:35 - 10:47: getRuntime()
IR: 335234: Undefined-Runtime.getRuntime(valid)()
7| String line;
8|
9| try {
10| Process proc = Runtime.getRuntime().exec(cmd);
11|
12| InputStream fis = proc.getInputStream();
13| InputStreamReader isr = new InputStreamReader(fis, "GBK");
.getRuntime 表示所有成员包含 getRumtime 为名字的值.*Runtime 和 ./.*?Runtime/ 正则和 Glob 匹配也是符合要求的 | ||
foo.bar 表示寻找所有 foo 作为 object,bar 作为成员的调用情况 | ||
getRuntime() 表示所有名字为 getRuntime 的符号被调用的位置。 | ||
exec(*) 意思是把 exec 所有的参数都作为审计对象,嵌套从参数开始进行审计。 | ||
call(,*,) 表示call这个函数调用的第二个参数开始审计,从第几个逗号开始匹配说明是第几个参数。 | ||
getRumtime() -{depth: 3}-> 设置一个深度为 3 的 UD 链递归向下查询。 | ||
exec(* as $sink) 把所有 exec 的参数保存为 $sink 变量。 |
❯ yak ssa --program sf1 --sf 'getRuntime().exec(*)'
[INFO] 2024-06-13 15:28:43 [ssacli:86] using syntaxflow rule will skip compile
[INFO] 2024-06-13 15:28:43 [ssa:38] init ssa database: /Users/v1ll4n/yakit-projects/default-yakssa.db
.........
.........
.........
[INFO] 2024-06-13 15:28:43 [ssacli:146] syntax flow query result:
[INFO] 2024-06-13 15:28:43 [ssacli:148] ===================== Variable:_ ===================
[INFO] 2024-06-13 15:28:43 [ssacli:174] /tmp/javatest/a1/test.java:5:30 - 5:40: String cmd
IR: 335223: Parameter-cmd
2| @RequestMapping("/home/rce")
3| public class RuntimeExec {
4| @RequestMapping("/runtime")
5| public String RuntimeExec(String cmd, Model model) {
6| StringBuilder sb = new StringBuilder();
7| String line;
8|
3.这是碰巧的吗?
public class RuntimeExec {
@RequestMapping("/runtime")
public String RuntimeExec(
String cmd,
Model model) {
StringBuilder sb = new StringBuilder();
String line;
try {
any runtimeInstance = Runtime.getRuntime();
Process proc = runtimeInstance.exec(cmd);
InputStream fis = proc.getInputStream();
InputStreamReader isr = new InputStreamReader(fis, "GBK");
BufferedReader br = new BufferedReader(isr);
while ((line = br.readLine()) != null) {
sb.append(line).append(System.lineSeparator());
}
❯ yak ssa --program sf3 --sf 'getRuntime().exec(*)'
[INFO] 2024-06-13 15:33:55 [ssacli:86] using syntaxflow rule will skip compile
[INFO] 2024-06-13 15:33:55 [ssa:38] init ssa database: /Users/v1ll4n/yakit-projects/default-yakssa.db
...
...
[INFO] 2024-06-13 15:33:55 [ssacli:146] syntax flow query result:
[INFO] 2024-06-13 15:33:55 [ssacli:148] ===================== Variable:_ ===================
[INFO] 2024-06-13 15:33:55 [ssacli:174] /tmp/javatest/a1/test.java:8:2 - 8:12: String cmd
IR: 335419: Parameter-cmd
5| public String RuntimeExec(
6|
7|
8| String cmd,
9|
10| Model model) {
11| StringBuilder sb = new StringBuilder();
未来已来
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