Semgrep代码审计体验
2022-4-24 19:4:49 Author: blog.xlab.app(查看原文) 阅读量:9 收藏

最近有一些代码审计需要,研究了一下自动化审计工具

考虑了一些备选工具

  1. Kunlun-M

    LoRexxar大佬的审计工具,文档似乎比较老旧,学习成本比较高,试用了一下就放着了

  2. CodeQL

    GitHub搞得审计工具,QL逐渐流行起来,很多师傅写过文章

  3. Semgrep

    很早之前看过介绍文章,简单的规则编写让我印象非常深刻

刚好有需求,决定研究学习一下Semgrep

官方提供了非常棒的教程,强烈推荐学习一下 https://semgrep.dev/learn

本文是在学习和尝试实践后,个人的一些体验分享(基于Semgrep 0.88.0

ltdr

可以学习,暂时不推荐使用,但未来可期 跳到总结部分

像写代码一样写规则

这个我觉得是最厉害的地方,官网上也提到了

Write rules that look like your code

No painful and complex DSL

没有痛苦和复杂的DSL完全就是在说CodeQL

Semgrep的“DSL”部分很少,也很容易记,非常符合编程思维,学习成本可以说极低,30分钟从入门到精通,比如

...是任意代码段

"..."是任意字符串

$xx变量

学习完learn教程真的就可以去实践,写自己的规则

不像CodeQL,看完教程好像是懂了,但想要去写检测一个新的洞,文档翻烂,才拼拼凑凑写一个规则出来(CodeQL大佬带带我有啥技巧吗)

数据流分析

代码审计逃不开数据流分析,Semgrep当然也是支持的

官网例子 https://semgrep.dev/s/P8oz

1
2
3
4
5
6
7
8
9
10
11
12
13
14
rules:
- id: taint-example
languages:
- python
message: Found dangerous HTML output
mode: taint
pattern-sanitizers:
- pattern: sanitize_input(...)
pattern-sinks:
- pattern: html_output(...)
- pattern: eval(...)
pattern-sources:
- pattern: get_user_input(...)
severity: WARNING
1
2
3
4
5
6
7
8
9
10
def route1():
data = get_user_input()
data = sanitize_input(data)

return html_output(data)

def route2():
data = get_user_input()

return html_output(data) // 检测到这里

看着非常简单,基本上定义好sourcesinksanitizers就行

但是稍微变形一下就不行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def route1():
data = get_user_input()
data = sanitize_input(data)

return html_output(data)

def route2():
data = get_user_input()

return html_output(data) // 检测到

def html_output_wrap(data):
return html_output(data)

def route3():
data = get_user_input()
return html_output_wrap(data) // 无法检测到

污点传播分析还是比较弱,另外还有一些case也检测不到,就不一一列举了

规则复用

这个在实践中还是非常重要的,规则复用更像是知识的积累

Semgrep当然也是支持的,叫join模式,也有例子文档 https://semgrep.dev/docs/experiments/join-mode/overview/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
rules:
- id: flask-likely-xss
mode: join
join:
refs:
- rule: flask-user-input.yaml
as: user-input
- rule: unescaped-template-extension.yaml
as: unescaped-extensions
- rule: any-template-var.yaml
renames:
- from: '$...EXPR'
to: '$VAR'
as: template-vars
on:
- 'user-input.$VAR == unescaped-extensions.$VALUE'
- 'unescaped-extensions.$VAR == template-vars.$VAR'
- 'unescaped-extensions.$PATH > template-vars.path'
message: |
Detected a XSS vulnerability: '$VAR' is rendered
unsafely in '$PATH'.
severity: ERROR

导入flask-user-input.yaml获取用户输入(source

导入any-template-var.yaml获取模板渲染(sink

最后用神奇的on语法,把sourcesink连起来

看起来很棒不是,但是这个on语法没有污点分析,也就说虽然上面的污点跟踪有些弱,但这里甚至都没有

join模式线上用不了,我本地写个例子,检测eval一个变量的场景

1
2
3
4
5
6
7
8
9
10
11
// eval.yaml
rules:
- id: eval
patterns:
- pattern: eval($X)
- pattern-not: eval("...")
message: $X
languages:
- js
- ts
severity: WARNING

检测取location

1
2
3
4
5
6
7
8
9
// location.yaml
rules:
- id: location
languages:
- js
- ts
severity: INFO
message: $VAR
pattern: $VAR = location.$X

合并起来就是检测eval(location.$X),很显然会有xss,有以下代码

1
2
3
4
5
6
7
8
9
10
11
12
let p1 = location.hash;
let p2 = p1;

function param_wrap(param) {
return param;
}

let p3 = param_wrap(p2);

eval(p1);
eval(p2);
eval(p3);

使用join模式写规则

1
2
3
4
5
6
7
8
9
10
11
12
13
rules:
- id: eval-join
mode: join
join:
refs:
- rule: location.yaml
as: user-input
- rule: eval.yaml
as: eval
on:
- user-input.$VAR == eval.$X
message: eval-join
severity: ERROR

只能检测到eval(p1),但是如果使用taint跑污点分析,都能检测出来

1
2
3
4
5
6
7
8
9
10
11
12
rules:
- id: eval-taint
mode: taint
pattern-sources:
- pattern: location.$X
pattern-sinks:
- pattern: eval(...)
message: eval-taint
severity: ERROR
languages:
- js
- ts

总结

Semgrep是一个非常年轻的开源项目(似乎20年才出现的),功能还不是很完善

  1. 污点分析弱,漏报率太高,意味着不能很好的挖洞

  2. 规则复用弱,知识不好积累,意味着难以形成工程

这两个连起来,可能适合比较小型,调用关系不复杂的源码场景,以及个人使用

比如js编译,混淆,调用关系复杂,中大型项目,团队合作就没法用了

如果能解决上面的问题,规则编写的简单将是巨大的优势


文章来源: https://blog.xlab.app/p/809ccc3c/
如有侵权请联系:admin#unsafe.sh