主要参考了土爷的博客文章以及一些搜到的其他文章进行的学习 https://lorexxar.cn/2020/09/21/whiteboxaudit https://cloud.tencent.com/developer/article/2235686 https://tttang.com/archive/1375/ https://www.freebuf.com/sectool/290671.html
从挖洞角度来说,人工审计一来在熟练之后逐渐趋向于重复劳动的形式,难以再提升平均效率,二来对于一些复杂的调用情形,人工有时候难以发现,因此就会有需求来进行自动化的代码审计工具,来对项目做安全性的检查,由此就衍生出了AST
的这些安全技术和产品。
在讨论工具的各种实现方案之前,我们就需要对工具的效率做出定义,从挖洞角度来看主要是误报率
、漏报率
、运行效率
这些方面去进行评价,当然背后的还有开发维护成本、使用成本等等因素。
先说AST
,AST
是Application Security Testing
,应用程序安全检测,是一个比较笼统的概念,指用来对应用做安全检测的这么一项技术。
AST
现在又分为SAST(Static Application Security Testing)
、DAST
(Dynamic Application Security Testing)、IAST(Interactive Application Security Testing)
,分别指静态、动态、交互式应用程序安全测试。
SAST
是静态应用程序交互测试,顾名思义,静态指不会实际执行代码发送请求,而是通过“推理”的方式来判断是不是有危险函数以及调用到的可能。这种形式误报率比较高。
SAST
是白盒形式的,根据写好的规则进行正则/AST的匹配,来发现漏洞。
假如从最简单的情形进行思考,如果我们要用代码去检测一段代码是不是存在漏洞,比如说有eval(_GET['a'])这样的情形,那么我们可以利用正则去进行匹配,匹配eval函数的参数中是否有_GET或者
那么如果是如下的代码片段呢
$a = $_GET['a'];
eval($a);
这里的话刚刚的检测手段就检测不到了,如果还是依照先前的方案,误报率低但是漏报率就变高了。
那也许我们可以通过只匹配eval
这样的危险函数的方式,但是这样就会存在误报率高的问题,并没有办法检测到eval
的参数是否可控,只能用人工进行后续的跟进。正则匹配的方案最多算是半自动化,具体正则规则的编写决定了误报率和漏报率的问题,但是很难以这样的方式做到准确、高效、覆盖率全。
正则匹配关键字的策略即便是人工写的再好再复杂,面对本质相同但是形式不同的代码,还是难以匹配,所以基于AST
的检测可以从编译后的结果来看,就可以做到不受代码编写形式的限制。
在词法分析和语法分析之后,代码就变成了抽象语法树(AST
),接下来就是考虑如何分析AST
。
这里我们需要明确三个概念,sink
,source
,information flow
。
information flow
表示数据从source
流动到sink
的过程,以上面的代码为例子,$_GET['a']
就是source
,也就是用户可控的输入,eval
这样的危险函数就是sink
,而从用户输入参数到参数被传递至危险函数内,就是information flow
。
那么如何进行information flow
的分析呢?
核心问题是对于作用域的变动,从主AST语法树到函数的作用域,这里要递归的去分析作用域,那么如何在这个递归的调用中制定规则是比较困难的。
土爷的文章中提到了两点比较困难的情形,第一是函数封装。
对于代码
$a = $_GET['a']
function ee($p){
eval($p);
}
ee($a)
这里危险函数eval
被封装成了新函数ee
进行调用,需要进行作用域上的处理。
这里就需要逆向的进行分析,从eval
为危险函数,进而分析函数参数会直接传递进危险函数,从而判断ee
函数为危险函数。
第二是多重调用链。
假如c
函数是危险函数,a
函数中调用了b
,b
中调用了c
,在存在复杂数据流向时的处理问题,以及如果在多重调用中,存在过滤函数的情形如何去处理,都是比较复杂的点。
IR/CFG
在编译过程中比AST
更底层,相比于基于AST
,有利于获得执行顺序,可以专注于source
到sink
的过程。
codeql
是github
推出的SAST
工具,可以基于规则来定义问题,从而扫描漏洞。
可以参考https://github.com/ASTTeam/CodeQL
,有一些codeql的资料
DAST
是动态应用程序安全测试,简单来说就是黑盒漏洞扫描器。黑盒的形式主要就是运行项目,通过爬虫找到接口并发送请求,根据payload
和回显来判断是否存在漏洞。很显然误报率会比较低,但是漏报率会比较高,只能发现一小部分的漏洞。
黑盒本身也存在一定的限制,毕竟相比于白盒可以看到程序运行的内部逻辑,黑盒在找接口,确定参数,接口返回行为不明确,缺少数据等等方面受到限制。
接下来说一说我认为的DAST
的实现会存在哪些难点。
首先是需要爬虫能够精确的覆盖Web站点的url,并且能够灵活的对于各种特殊参数进行payload的替换,以及针对不同的漏洞类型,做对于漏洞是否存在的检测。
比如说想要对越权进行检测,那么就有水平越权和垂直越权,对于绕过登陆的垂直越权场景,我们可以想到将http
请求的cookie
做替换,来发送不同的请求,并且根据http
响应码和内容,就比较容易判断出来是否存在垂直越权。但是如果对于水平越权,回显的内容非常相似,这时候就比较难以判断,需要能够有比较精准的检测规则,对于口类型的判断,响应的长度以及相似度这些方面做检测,不然就容易误报或者漏报。
IAST
指交互式应用程序安全测试,IAST
的主要原理是在代码的运行过程中发现漏洞,这里可以直接体验一下火线安全开源的IAST
产品洞态https://github.com/HXSecurity/DongTai
,就能对IAST
产品有个概念。
IAST
的核心就是Hook
危险函数/底层api,并且通过前端爬虫判断程序是否运行到了危险函数来判断是否存在漏洞。IAST
的误报率比较低,因为可以通过污点跟踪来判断危险函数是否被运行了。缺陷来说的话,从实现机制上决定了,IAST
是不能做Go
这种静态编译的,没有虚拟机的语言的安全检查的,因为插桩的实现主要依靠runtime agent
在虚拟机上做hook
。
IAST
产品的分类我目前也并不是很能理解,可能是缺少在公司中做iast产品实践的机会,主要靠看文章和看开源产品源码学习,希望之后实习会有机会接触到,下面这段分类主要还是根据前辈的文章做整理学习所得,并不一定准确。
IAST
产品根据场景有比较多的模式,比如说代理扫描,镜像旁路,插桩扫描等等,洞态是被动式插桩实现的IAST
产品。 代理扫描模式是在代理处通过数据包打标进行隔离,打入污点数据后总流量会放大,一定程度导致数据污染和运维的压力。那么就会考虑容器化单独启动测试环境,不过也就需要去单独迭代测试环境造成更多的成本,以及在各种https加密之类的场景下代理模式也会遇到一些困难。 镜像旁路模式是离线分析的方案,在服务器加解密流量后,导向流量清洗集群。这样也会导致在清洗时加重安全侧压力,将污点数据带入QA测试结果等等问题。 插桩扫描模式的实现主要是靠agent
等去hook
关键函数,来确认流量链路。插桩扫描又分主动和被动,区别在于是否有流量输出。
对各种AST
工具的粗浅理解就到这里,之后会想深入研究一下洞态这个被动式插桩的实现,并且再写篇文章讲讲。