
今天分享的主题是开源软件漏洞挖掘实践,主要讲对于企业项目、开源项目审计的认识以及做代码审计的经验。

关于我在代码安全这一块的经历,主要分为三个阶段。

阶段一,主要挖PHP写的内容管理系统的一些漏洞,作为我刚入门的一个阶段;
阶段二,是在百度云计算网络等业务线的安全评估,里面也会涉及到一些代码审计相关的东西;
阶段三,目前在火线安全做云原生相关的安全研究。

第二部分:介绍一下开源软件安全评估的一些手段,主要是两个自动化相关的手段。

目前可以看到市面上的很多开源软件都集成了fuzz功能,可以大概理解成我们给程序随机的数据,然后去看这个程序有没有崩溃,如果程序崩溃,它就可能存在比如IP指针被覆盖的安全问题,然后研究人员根据这些崩溃的案例去进一步的看有没有安全漏洞。
这些开源软件怎么集成fuzz,举一个例子就是CPython的一个项目,就像上面PPT画的。CPython下面有一个目录,这个目录的文件里面有类似单元测试的代码,它会去调用那个CPython的已实现的API接口,想去测试这些API接口,单元测试会被fuzz工具自动化的去看这些API有没有安全漏洞。
从公开的一些报道来看的话,现在国内也有一些厂商,比如说腾讯,是已经这个fuzz集成到他们的流程里。

第二点,开源软件有很多借助静态代码扫描的手段去自动化检测漏洞。在安全里面最出名的自动化的静态扫描工具应该是Code QL。
比如说PPT右边展示的这个配置文件,他们做了一些配置,然后github就会根据这个配置去对仓库做一个漏洞扫描,扫描结果也可以在github上看到,就像左边的这四个漏洞,他标记的都是高风险类型。
开源软件作者或者安全研究者,可以借助这些工具来保证软件的安全性和漏洞挖掘。
第三点,想分享一下之前在企业里面做代码审计的经验。我在没有去百度做代码审计和安全评估之前,我以为企业的代码审计可能跟开源的代码审计差不多,主要就是看看代码挖漏洞这种,但实际上不是这样。

实际上企业里代码审计跟开源的目标不一样,企业里面代码审计的目标主要是为了防守、降低漏洞代码带来的安全风险。目标是挖掘和修复所有漏洞,并且覆盖到所有对外的线上业务。基于这个目标的话,工作内容就会发生变化。
跟开源软件相比,就不会太注重那个漏洞分析这一块儿。举个例子,比如我们做SDL时需要去分析fastjson漏洞的原理、各种利用链是怎么回事吗?就我的工作经历来看的话,其实是不太需要的。为什么呢?因为我们要审计的目标很多,但是资源、人力、工具都是很有限的。在这么一个情况下去防守,去推动业务完成漏洞的防御,所以说,我们可能就不会优先去投入很多的资源去分析这个漏洞。
还有一点相比于开源来说,它多了漏洞的修复。我们需要让研发来修复,那么就需要要提供详细的修复方案给研发,还要不断的去催研发修这个漏洞,这个事情比较花时间。

另外来说就是我们的一些工作思路,这个还是以我个人的经验来做分享,我假设攻击者是没有拿到源码和访问不了内网系统,在这种情况下,我不太关注特别复杂的情况。
比如从黑盒的角度来看的话,你拿不到这个token,你就访问不了API,这也干不了什么事儿。但是实际的情况不太一样,举一个例子,我在挖掘一个安全厂商的漏洞的时候发现了这个漏洞,从github上泄露了源码,拿到源码以后,我看到了代码里面的这个key,这个key是用来做身份证的,我有了这个Key以后,就能访问这个服务的所有的API,也能拿到很多的数据,特别是其中有一个API还是存在一些SQL注入的问题。
另外一些不同点,企业里面的文档相对来说没有大型的成熟的开源项目那么完善,从文档里面你很难看出业务架构、业务技术栈以及业务逻辑。第二个是企业的代码相对来说更复杂一些,包括他用的技术栈和依赖的组件,还有一些业务形态。就比如说云上的一些Paas平台,它可能还要考虑到一些多租户的这种情况,相对来说可能会更复杂。
第三个是从我在企业里面做代码审计的经验来看,你很难在本地去搭建一个开发环境出来。
第四部分我想跟大家分享一些之前总结出来的方法论。

我们在做代码审计的时候到底是在关注哪些目标?看右图,把关注目标分成三个层级。每一层理解成一个业务。
我们在审计的时候总是假设在底层它的实现是正确的。我们在这一层的服务角度上来讲,去看一下有没有安全问题。三层里面每一层它其实都算一种。这些业务,你用不同语言的不同框架都有可能出现类似的漏洞。
经验经常是出现在最底层的误用这一块儿,比如说语言层或者库层,都会提供命令执行或者是文件操作这些功能,我们服务出现制造命令执行漏洞,或者是任意文件读写这种漏洞,其实都算是对底层的一种误用。根据这个经验,我审计的时候遵循几个原则,审计的目标是自上而下的,我假设Java的日志库是安全的。文件读写这种上层的业务,如果说我在这儿上传业务,在我已经觉得比较安全的情况下还想继续发掘漏洞,可能审计目标就会变成下一层,我会去看框架层有没有漏洞。审计之前我也会去学习同类业务的历史漏洞。
最后,审计时我会以那个黑盒视角去找到风险业务,做安全评估比较多的人,可能就是较容易知道哪些业务经常出现问题,就比如说身份认证这种功能,它经常可能就会出现一些奇奇怪怪的问题,如果一旦出现问题,他可能就会造成API有一个未授权访问。

最后,给大家分享两个案例,第一个案例Bytebase身份认证伪造的漏洞,他是CNCF基金下的一个项目,主要是用来做数据库的版本管理。这个项目的身份认证是依赖jwt,本身生成的签名需要有一个key跟固定的算法去生成一个签名。
如果说我们能拿到这个key的话,我们就可以伪造这个签名,伪造这个签名的话,我们也就可以去修改jwt的payload,也就是这些里面的数据,比如可以用户名来判断你当前的请求是哪个用户,另一方面是随机数的生成,需要一个API接口,它这里是一个伪随机数算法,就是说它的随机数的一个序列是可以被猜测到的,所以我们是可以猜到它的key。
对于这个案例,其实是相当于把业务分层,它的身份证业务其实是依赖jwt的实现,或者说这个规范的实现。关于随机数的生成,如果你把它也当做一个业务的话,生成的方式有两种,一种就是说你用库提供的rand api去生成一个随机数,这种情况下,它有可能分真随机跟伪随机。第二种就是有一些业务会拿uid来当做一个随机数,但用uid这种东西,它本身也是有规律的,也不是说完全随机的。

第二个案例是yaml解析的一个安全问题,这个是我在好早之前挖一个厂商的漏洞的时候遇到的。
Snake Yaml在解析用户提供的恶样本数据时存在一个反序列化漏洞,但是它的利用链是说你需要能够从外网去拿到一个字节码,然后它才会根据你这个字节码去实例化对象,然后造成命令执行。但是我怎么在不出网的环境下去利用,因为我对Snake Yaml其实也不也不熟,我当时的想法是根据用户提供的对象名还有构造参数来实例化一个对象,fastjson之前被爆过一些漏洞利用链是可以不出网实现任意文件读写。那么,我这个能不能把fastjson的这个这个链能移到这个Snake Yaml上来,结果证明是可以的。
做完这个漏洞挖掘之后,我的想法是说Snake Yaml是Java语言的一个库,那其他语言是否可以?类似Java关于yaml的解析,业务都是类似的,都可能是根据用户提供的对象跟参数值去实例化,造成命令执行。我稍微测试了一下,像python也是可以根据用户的输入直接造成命令执行的。
所以可以看得出来,相似的业务总有相似的漏洞利用链。

因为我们的资源都是有限的,时间也是有限的,但我们的审计目标都是很多的。我们总是想要提高效率,主要有几个方面。
一个就是高危函数的正则,一个是建立标准的审计流程,关于审计流程的话,因人而异。第三个就是通过git日志,可以知道最近的审计目标对象,它新增了哪些业务代码,然后再去针对性的审计。

在审计过程中,我也遇到了很多问题,总结一些比较常见的问题,比如说怎么阅读源码?关于这个你可以去看一些日志或者文档,去理解你的审计目标,从而更好的避免直接陷入代码里面的细节。
举个例子,就是我之前在审计Snake Yaml的时候,我一开始看就觉得很迷糊,但它实际上用的是一个状态机,如果说文档里面提到了状态机这个背景知识的话,我可以不用看他那一块儿的逻辑,因为确实也很绕。这样的话我可以省去一些时间,从而提高自己的效率。
第二个是学习语言是不是都是差不多的?我之前也会听到一个说法,语言都是一通百通。但是做审计会接触到很多的技术栈,很多应用可能是用不同的语言写的,我们都要去审计,这种情况下,我们花费很多的精力是在学习他的语言和应用构建。实际上,如果我们把语言当做一种业务来理解,它总要实现一些共同性的东西,比如很多语言在运行时都要支持并发、支持垃圾回收等等。
然后第三个是怎么快速去掌握一个新的框架?我们审计的时候,拿到一个新的应用,它可能是用到我们之前不认识或者从没接触过的web框架。那这个时候我们怎么去掌握?可以分两点:
第一点是API怎么使用。
第二点是它的web框架怎么实现。
以上两点都要掌握的。怎么掌握呢?一个是概念,比如说容器跟web框架的关系,还有请求、上下文这些东西。另外一个就是框架的设计思想,比如springboot的具体配置,我们可能更容易去理解一个新的框架。

参考资料:
1.聊聊 Golang 的 Web 框架
https://mp.weixin.qq.com/s/1mnlMddHzZHMfmfpE5-yhw
2.代码安全审计之道-有价值炮灰
https://www.bilibili.com/video/BV15L411P7cM?spm_id_from=333.1007.top_right_bar_window_history.content.click
3.持续Fuzzing在DevSecOps中的应用
https://mp.weixin.qq.com/s/1mnlMddHzZHMfmfpE5-yhw
4.漏洞挖掘
https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzU0MzgzNTU0Mw==&action=getalbum&album_id=1363100069033066496&scene=173&from_msgid=2247484124&from_itemidx=1&count=3&nolastread=1#wechat_redirect
5.如何高效地学习开源项目 | “华仔,放学别走!”
https://time.geekbang.org/column/article/10022
6.使用Github CodeQL进行0day漏洞挖掘
https://mp.weixin.qq.com/s/KYyW2-JElofG1--k3vnxOg
上图所展示的部分是我在做这个PPT时候参考的一些资料和相关知识,目前的工作主要偏向漏洞,后面也会给大家分享一些漏洞相关的东西。