HOW DO YOU ACTUALLY FIND BUGS?(译文)

本文是根据具有12年从业经验的安全研究人员Mark Dowd在今年的OffensiveCon大会上面的主题演讲整理而成,主要讲解了他自己在挖掘软件漏洞方面的方法论。

简介

就目前来说,大部分关于安全主题的演讲,都是关于“我发现了一个很牛的漏洞”,或“我发现了一种更牛的漏洞利用方法”,但是,几乎没有介绍“我是如何在软件中挖掘安全漏洞的”。而本演讲的主旨,并非为读者介绍某种魔术手法,而是探讨如何成为魔术师。所以,这里讲到的不是技术细节,而是一些原则性的东西,或者称为方法论:实际的漏洞挖掘的一般过程是什么,需要涉及哪些方面,等等。

首先,对于非从业者来说,通过本演讲可以了解漏洞研究者是什么样子的,要处理哪些事情;对于这个领域的年轻人来说,则可以从这里得到一些具有可操作性的建议;对于资深的研究者来说,也可以跟自己的方法做个比较,并鼓励他们分享自己的经验。

心理建设

在我继续谈论实际的审计工作之前,我想先谈一谈与漏洞研究相关的性格和心态因素,因为我认为这是区分一个人是否擅长漏洞研究的关键因素,还是容易精疲力竭、情绪低落的关键因素。这些心里因素包括:

  • 性格
  • 直面挫折
  • 勇往直前
  • 动机
  • 信心
  • 偏见与假设

心理素质

1.png

首先,在性格方面,对于漏洞研究人员来说要具有好奇心以及非常注重细节的天性已经是最基本的要求了。但我认为更重要的一点是,当你在做漏洞研究时,随着对代码的不断深入挖掘,经常就会发现:本以为了如指掌的技术,实际上并没有真正理解,或者,至少理解程度没有自认为的那么高,更有甚者,我们原来的理解是完全错误的——这时候,能够以积极的心态接受新信息就格外重要。实际上,在进行漏洞研究过程中,失败和挫折才是常态,要想成功,不仅要勇敢面对,更要以积极心态加以处理——这是漏洞研究者的最重要的技能之一。

学会如何应对挫败感

1.png

这里说一下我个人的做法:当我进行漏洞研究时,我的手头上总是两件事情同时推进。比如,它们可以是来自同一份代码的两个不同的组件,或者,完全不同的两份代码中的两个组件。然后,在研究的过程中,时刻观察自己;如果发现自己无法取得进展,或者效率骤降,说明当前在这个漏洞研究项目上的新想法已经被榨干,或者在理解关键组件上遇到阻力。这时候,不仅会效率地下,并且通常会非常令人沮丧,注意力难以集中,等等。因此,一旦发觉出现这种情况,就要立即切换到另一个项目上,将遇阻的项目暂时搁置一边;随着注意力的切换,相关的挫折感也会随之消散,同时,这也有助于振作精神。

另外,通过冷处理一段时间,比如一天或一周,有时候,我们会突然茅塞顿开。经验表明,最棒的想法,往往不是出现在苦思冥想的时候,而是在不经意间冒出来的。如果手头上的第二个项目是个程序开发类型的,那就更好了:与漏洞研究不同,这些任务通常可以稳步推进,并且易于衡量进度,所以,很容易让人获得成就感,并重新获得动力。这时候,我们就可以切回之前的项目上去了。

砥砺前行,适可而止

1.png

当然,执行这个策略也是需要毅力的。有时候,我们需要折腾好几个来回,才能发现一个安全漏洞。有时候,来回折腾好几次后,还是一直碰壁,没有取得丝毫进展——这对积极性和信心的打击会很大,并且难以消除:我花了这么多时间来了解这个代码库,尽管我已经对它了如指掌,但是连根漏洞的毛都没找到!!!实际上,千万不要有这种想法。即使我们暂时将其搁置起来,这也不是浪费时间;相反,我们不妨把它看成是理解这个代码库的启动成本。一段时间后,当它添加了新的功能需要进行审查,或者遇到类似的项目时,我们可以很快上手。退一万步讲,至少可以看作是磨砺了我们的安全研究技能。

总之,我们既要砥砺前行,也要适可而止,千万不要被沉默成本所左右!

动机——满足好奇心

1.png

关于如何持续保持对代码的关注这方面,我实在没什么好讲的。就像前面说过的那样,大部分研究安全漏洞的人的好奇心本来就比较重。我想说的是,除了寻找漏洞之外,很多时候我只是想了解技术是如何运作的。以这种心态阅读代码,会让事情变得有趣起来:尤其是发现一个全新的技术是如何工作的时候;或者自认为再熟悉不过的东西,却发现了新的细节,尤其这些细节只有通过深入研究自己不熟悉的代码库才能真正发现的时候。所以,你对技术越有兴趣,就越不会觉得审查代码是件枯燥的事情。

另外,对于同样的问题,我对别人在解决它所使用的不同算法也很好奇。在此过程中,我会发现一些非常酷的编程技巧,这时我就会想,下次我也在自己的代码中使用该技巧。偷师学艺,这本身就是一件非常刺激的事情!

动机——心有不甘

关于研究漏洞的动机方面,有一件事情不得不提。那就是,自己发现的漏洞,还没来得及提交,就被修复了——一方面,这种经历会让人很沮丧,另一方面,这说明找到的真是一个安全漏洞,在这个方向上挖洞是正确的!这时,很容易获得继续前进的动力,因为你已经熟悉漏洞及其上下文环境,并且挖洞方向也得到了验证!

我曾在chrome中发现了两个漏洞,所以,感觉很爽,但是好景不长,两天后这些都就被修复了。于是,我一气之下挖出了另一个漏洞,并为其编写了相应的漏洞利用代码,为此,我非常嘚瑟。但一周后,这个漏洞又被修复了——遇到这种超级沮丧的事情,建议大家练练拳击,或者做点其他能让自己变积极的事情。我仔细阅读了谷歌的安全通告,发现这些漏洞都是由同一个人的提交的,于是,我就雇佣了这个家伙,让他给我干活,也算是报了一箭之仇,哈哈。小提示:创办一家漏洞研究公司,然后,设法将所有研究人员都收于麾下!

动机——漏洞补丁

1.png

实际上,安全补丁是一把双刃剑,因为它除了能够修复安全漏洞之外,对于熟悉代码库的人来说,还是非常好的文档。当您考察安全补丁时,不仅能获得灵感,联想到该漏洞的其他变体或模式,还可以帮助您了解漏洞在这个特定代码库的上下文中可能的位置,漏洞类型和成因等。

信心

1.png

另外,自信也是非常重要的。在许多人眼里,安全漏洞研究是一个相当令人生畏的领域,即使对某个领域有着丰富经验的研究人员来说,如果要跳到一个全新的领域,比如从内核领域跳到浏览器领域,也是很怵头的。

1.png

但我要告诉你们的是,许多安全研究人员(当然不是所有的研究人员),其中包括你们非常尊敬的一些安全研究人员,也有同样的自我怀疑情绪,尤其是初级安全研究人员,在切换技术时会反复出现这种情况。实际上,当我着手某项漏洞研究时,也会变得非常不自信:对于像我这样一个几乎没有经验或知识的人来说,代码还没有下载下来,就想着在某个互联网大神编写并部署在数百万台机器上的代码中找茬,似乎有点“狂妄”。虽然心里这么想,但我还是这么做了,因为没有人知道我在搞事,因此,这只是在暗地里撒野。之后,我会被它的复杂性淹没,嘟囔这该死的代码到底想干啥,甚至怀疑人生,或自己学的汇编是不是假的。出现这种情况时,我会尝试理解要求最少背景知识的最小组件,一旦我理解了这一小部分,我就会有动力继续搞另一个组件。在这个过程中,相关的背景知识会不断丰富,一段时间后,你开始意识到,哦,自己对组件的整体运作已经有点了解了,这就有点门道了。

然后,你开始发现一些小问题,比如,代码好像不应该在这里清理这个东西,因为之前已经清理过了。就这样,从小问题开始,就能逐渐发现更大的问题,这对培养信心是非常有帮助的。

需要说明的另一件事是,当研读某些漏洞研究报告时,新手经常冒出这样的疑问:他们怎么可能做出这么逆天的漏洞研究,或这么牛掰的漏洞利用?我过去曾经勉励初学者:你们所看到的很多作品,都是那些人花了几十万个小时练习的结果,这足以让他们的技术变得成熟,对技术和漏洞利用的理解变得到位,所以,按照他们的水平来说,做出这样的成果是很正常的事情。而你才刚入行,自然觉得这种事情是不可思议的。

1.png

不过,有一件事是有用的:如果你回顾他们过去发布的一些东西,比如一年前、三年前或五年前的东西,你会发现他们自己基本上也一步一个台阶走过来的。所以,你可能不理解他们今天做的事情,但你可以看看他们五年前做的事情。回顾高手的成长之路,可以帮助自己规划一张路线图,如果你准备投入同样多的精力,也能达到同样的水平。所以,这实际上是关于发展的心态,而不是面对某个东西说我做不到,不是做不到,而是目前还没有做到。

偏见与假设

我在这里想谈的最后一件事是,关于偏见和假设。我认为,当你审查代码时,找到的大部分漏洞都是程序员的许多假设所致,这是显而易见的。但我认为,安全研究社区实际上也存在许多偏见和假设,你可能会不时成为牺牲品,让你错失一些潜在的优质漏洞。

1.png

在我职业生涯早期,遇到的第一个偏见,源自关于开源和闭源软件谁更安全的大辩论:支持开源的人认为,由于代码是开源的,有那么多双眼睛在盯着呢,即使有漏洞,也很快被揪出来。后来,事实证明这种假设是错误的。我意识到,对于某些最主要的应用程序来说,根本就没有人在审查它们,因为每个人都认为其他人都在盯着它们;并且有人在盯着,我相信大部分情况下也只是浮光掠影式的检查,比如看看是否存在整数溢出漏洞,而真正花时间来充分理解代码的复杂性,并试图挖掘更复杂的安全漏洞的情况是很少发生的,至少我是这么认为的。所以,一旦我发现这个偏见,整个职业生涯的早期的工作,就是在所有这些主流的应用程序中找漏洞;在这个过程中,人们对于我经常在大路边捡到漏洞的情况感到无比惊讶!但据我所知,实际上很少有人在检查这些代码!

1.png

据我所知,还有另一个倾向,我认为有些人即使不时被某些问题绊倒,还是依然乐此不彼。实际上,我就经常被绊倒,比如发现的许多漏洞都是无法利用的。所以,那里在有段时间里,整个研究社区基本上都从挖掘服务器端漏洞转向客户端漏洞。当然,这与ASLR等相关缓解措施的引入有关,但是却并非唯一的原因:人们开始意识到,与客户端广泛的攻击面相比,服务器端的攻击面通常要小得多;另外,还有一种感觉,即使我们在服务器端发现了一些漏洞,要想进行可靠的利用也是非常困难的,有时候简直就是不可能的。鉴于此,在很长一段时间以来,安全社区一直都忙着研究客户端的漏洞,直到几年前永恒蓝色出现为止。之所以出现这种情况,主要是ASLR造成的:所有东西在内存中都是随机的,我们无法确定它们到底在哪里;如果能够在固定地址分配内存,这对漏洞利用将非常有用,实际上,永恒的蓝色就利用了这样的机制。之后,人们开始继续挖掘其他服务器端漏洞,Windows DNS和HTTP模块中的漏洞;以及无交互型漏洞,比如ios的即时通讯软件。事实证明,在很多情况下,某些缓解措施的确很有用,但很多时候,我们却可以通过可靠的方式绕过它们。

下一个问题是,我们会认为某个攻击面已经被挖掘得差不多了,没有其他东西了。但是,当我开始研究浏览器漏洞时,却发现有些漏洞就像一条射线,能看到起点,却看不到终点。我的记忆中,每年都有两三个新的漏洞从同一个模块中冒出,并且每个主要的浏览器都用到这些模块。之所以出现这种情况,是因为最初发现了一个非常简单的漏洞,并进行了相应的修复,然后一个更微妙问题被引入或发现,然后继续得到修复,然后又一个更微妙的漏洞版本再次出现……。所以,就算你发现某个模块中已经被挖出了许多漏洞,也不要轻易认为那里没有其他漏洞了,因为这些代码库通常会不断发生变化,所以还是非常值得一看的。

我想在这里提到的最后一件事是,很多时候你可能会对协议或说明文档中介绍的东西存在潜意识的偏见。而当您考察的某个特定事物,这些偏见对你的想象力的限制的影响是惊人的!

1.png

而且您知道,开发人员经常犯这种错误,而这正是许多漏洞的成因之一。比如,许多协议或其他东西的实现代码,实际允许您做的事情,要比协议规范所规定的要多。所以,过去有一个非常有用的经验:故意不阅读任何关于我正在研究的技术的文档,直到我读完一半代码的时候,再返回头来看文档。我发现,自认为根本不应该发生的事情,程序却实现了,而我之前根本就没有想到——我经常惊讶于自己到底有多笨,竟然不知道如何正确使用协议。所以,我现在特意这样做,以避免偏见。这样,通过代码与文档对照着读,可以解决许多歧义问题。

关于心理方面,我们就先介绍到这里了;实际上,还有许多方面没有讲,如果都囊括进来,恐怕四个小时也讲不完。

1.png

审计过程

1.png

努力理解代码

1.png

这似乎是一个愚蠢的事情,因为这个显然是漏洞研究过程中比不可少的阶段,但在我的职业生涯中,我注意到很多人试图缩短这个过程,并在这方面投入了大量的精力,并借助于诸如fuzzer、静态分析软件等工具。虽然这些工具对于漏洞分类等工作非常有用,但它们并不是这个过程的全部。即使这些工具已经相当先进,通常也只能找到低悬的果子,最多也就能够到中层的果子。去年年底,来自Project Zero的Tavis Ormindy在Mozilla维护的SSL库找到了一个安全漏洞,当然,这只是一个简单的堆溢出漏洞;同时,他们还专门为此撰写了一篇文章,介绍了在这个过程中静态分析工具所发挥的作用,以及它们的不足之处。

1.png

我发现,现在的漏洞发现过程,很多都是从一个不引人注意的功能或一个奇怪的API开始的,因为它可能就是一个容易被滥用的接口。在找出具有安全隐患的漏洞之前,你首先需要把这些想法连在一起;作为一个安全研究人员,你的优势是能够在安全上下文中理解它们,并能够以一种创造性的方式思考问题,这可能是工具无法做到的。例如,Natalie Silvanovic去年在聊天软件Signal中发现了一个安全漏洞;我们知道,这是一款非常流行的软件,因此,许多安全研究人员都花了大量时间来审查其代码,并通过安全测试工具对其进行考察。不过,她发现的这个漏洞,是需要精通状态机方面的知识的。实际上,她花了许多时间考察WebRTC和SD SDP后,发现对于其中的状态机来说,通过打开麦克风或摄像头就能达到某种状态,而无需经历发起呼叫的调用状态——在这个特定的上下文中,这就构成了一个安全问题。事实证明,状态机比您想象的要复杂得多,所以,她能够找到一种看起来不可能,但事实上的确可能的情况。

其次,您看到的每种缓解机制绕过方法,也是一种特定于上下文的漏洞:寻找针对某种缓解机制的绕过方法的过程,类似于挖洞过程,只是要挖的不是传统的内存损坏之类的漏洞,大多数时候,我们要找的是破坏特定的完整性验证算法的方法。举例来说,在设法绕过PAC机制时,我们通常会寻找伪造指针的方法,或者寻找可以修改指针但PAC却检测不到的方法,或者类似的方法。要找到这种性质的漏洞,您需要了解PAC的相关知识,知道它的运行机制,这样你就可以有的放矢了。

1.png

实际上,当我们对研究对象的背景知识非常了解时,有时只需向前迈出一步,就能发现一个非常有趣的漏洞,而无需深入太多的细节。下面,我们以去年研究人员在iOS中发现的一个漏洞为例,进行说明。当时的情况是,他发现了一个api,并觉得它相当古怪。具体的讲,这是一个函数,旨在接受端口对象,并向其添加引用计数。他注意到,在一个特定的情况下,它实际上不会进行引用计数,而是保持不变。然后,他发现的第二件事情是,该函数在无法让引用计数递增时,会发送信号。但在某种情况下,该函数被调用时,它们实际上从未检查过引用计数;而在这种情况下,不检查引用计数的理由是,端口基本上不可能使进入这种特殊的状态。于是,他利用iOS内核相关子系统的相关知识,成功构造了一个竞态条件;在此条件下,他可以使端口进入被认为是“不可能的状态”,并触发UAF漏洞。实际上,很多漏洞都是这样的——它们并不是简单的整数溢出漏洞,而是要结合各种背景知识,才能知道如何触发和利用它们。

关于阅读代码的另一方面的作用,当然是利用漏洞。目前,对于大多数通用漏洞利用技术来说,在很多情况下都不再真正起作用,我们不仅需要通过真正理解代码来寻找漏洞,还需要根据这个漏洞所在的具体环境来利用它。

我曾经在flash中发现了一个漏洞,可以将受控的整数写到一个受控的位置,但是,这个位置必须是四字节对齐的,所以不能做错位写入。另外,这个整数也只能是一个非常小的数值。所以,熟悉漏洞利用的人就会明白,这不是一个很好的原语。不过,从另一方面说,将任何小整数写入一个表中是没有任何问题的。设想一下,如果写入ActionScript虚拟机所使用的表中,会发生什么?该虚拟机会将其解释为ActionScript指令,并为其分配一个ActionScript函数,也就是说,我们的整数变成了指令,并能可靠地执行任意代码。看到这里,有人认为这是一个惊人的逻辑飞跃:发现这个漏洞根本与ActionScript无关,最后呢,却利用它让平庸的漏洞得到了质的提升。实际上,事实正好相反,作为漏洞研究的一部分,我逆向分析了ActionScript虚拟机大部分代码,所以,我对它非常熟悉。当我发现这个漏洞时,我就想到了,前面研究的虚拟机对它非常有用。

所以,我认为这是一个非常普遍的策略,特别是像我说过的那样,因为很多通用的漏洞利用技术已经失效了,无论如何,对熟悉待审计的程序是必不可少的。因此,我的格言是,对一个程序的工作原理了解得越多,就越有可能找到漏洞,并利用它们。

我是如何理解代码的

1.png

顺便说一下,了解一个东西是如何运作的最好方法,就是向别人解释它,无论是做演讲,还是写文章。我有很多次的经历,原本以为了如指掌的东西,但在写论文的时候,却发现自己有很多盲点,因为当你真正写出来之前,通常会走一些心理捷径,尽管当时还没有发觉;当你被迫写下来或者向别人解释的时候,通常会问一些自己还没意识到尚未搞懂的问题,这样的话,这些盲点就暴露出来了。

实际上,我之前发现的一个远程发送邮件漏洞,就是在撰写安全专著的过程中发现的。当时,我正在写关于信号处理的章节。这一章基本上是非常简单的,不过有一个API,在Linux中与BSD系统上存在细微差别,这些我早就知道了。但在写这一章的过程中,我开始思考,信号处理漏洞是否会导致有趣的安全问题。另外,当时阅读过OpenSSH和Sendmail库的介绍,我知道它们都使用了信号处理,所以,我想看看我的想法是否适用于那些代码库。于是,所以我首先阅读了OpenSSH库的代码,并在其中发现了一个漏洞,虽然可能会引发崩溃,但基本上是不可利用的,而且它位于Kerberos模块中的,当时默认情况下该模块是关闭的,并除了在朋友面前吹牛之外,这个漏洞并没有真正的利用价值。研究完OpenSSH之后,我开始转向Sendmail库。实际上,这个库我以前看过很多次了,但从来没有非常密切地关注过信号处理,因为我认为自己对它已经非常了解了。但是这一次,却在其中发现了一个非常酷的漏洞。

我们真正应该关注的东西:攻击面与复杂性

1.png

当你做漏洞研究时,要想真正挖出好洞,必须要擅长阅读代码,因为这是一项与编写代码截然不同的技能。而且我认为阅读不同的代码库涉及很多事情,即使是高级开发人员,我的意思是他们中的很多人,也没有我们想象中的那样擅长阅读其他人的代码。对于某些代码库,我会持续跟踪,但本质上我真正寻找的是可用的攻击面和复杂性,这两者都是漏洞猎人最好的朋友。

1.png

关于攻击面,当然是越大越好。但是,当我在看过最明显的攻击面之后,我的感觉是,大部分攻击面都是间接的。所以,让我们回到之前的浏览器示例。您知道,诸如array.concat方法之类的代码,可能是攻击面中首先被攻击的部分,然后,才是jit生成的代码。但是,当前发现的许多漏洞,都出现在间接攻击面上,或者说是二级攻击面上。您可能会说,即使是缓解措施也是一个攻击面,虽然它们的出现是为了防止漏洞被利用,但它们同时也会引入复杂性。实际上,近几年发现的一些劲爆的攻击面都位于缓解措施中。

有时候,我们会遇到一个不起眼的,也从未听别人提起过的攻击面,这时候,简直就是找到宝藏了。刚开始的时候,人们很容易把新的攻击面误认为是一个漏洞,但是,漏洞只是一个细枝末节而已;当有人发现一个新的攻击面,然后在接下来的两个月里,很可能会有上百个漏洞相继被发现,这就是找到新攻击面的证据。

1.png

对于漏洞挖掘来说,复杂性也是件好事。实际上,程序本身就是涉及到很复杂的东西,从CPU层到应用层,每一层都很复杂。但是除此之外,还有很多复杂性,就多少有点没必要了,但是,却无形中增加了漏洞猎人的攻击面。首先,有些复杂性来自功能驱动。有些产品为了超过竞争对手,会不断增加新功能,比如浏览器产品。这些年,每当我开始担心无法在浏览器中挖掘更多漏洞时,W3C都会适时引进一些新功能,从而带来一些新的攻击面。

复杂性的另一个来源,是对遗留功能的支持。但是,向后兼容性总是会增加许多不必要的复杂性,这就为降级攻击带来了机会!另外,当人们为某种东西(包括软件和硬件)设计解决方案时,对他们来说,创建更复杂的东西比更简单的东西会更便宜、更高效,因为与重新发明轮子,从头开始编写所有代码以实现他们想要的最低限度的服务相比,直接使用现成的代码库要省钱省力得多——只需把操作系统或任何已经存在的东西,组合在一起,这样就不必自己做所有的工作了。

但是这是有代价的:许多代码库实现的功能,比您实际需要的功能要复杂得多,因为它们是通用的,所以,您在引入您想要的功能的同时,也会引入一大堆您用不到的功能,并且有时很难禁用这些功能,或者根本无法禁用,更有甚者,我们都不知道这些功能的存在。比如,Log4j漏洞就是一个很好的例子,基本上每个人都在使用这个库。相对于大家所需的功能来说,它的功能过于丰富了;我们只知道它能正确地记录错误消息,却不知道它提供了额外的格式化功能,甚至可以用来下载和运行任意代码——对于这个库的大部分用户来说,根本就不需要这个功能,特别是远程下载代码,可能相当多的用户甚至没有意识到该功能的存在。因为这些程序的实现者认为,既然有人已经写了一个日志子系统,我为什么还要重复造轮子呢?所以,这些错误真的应该引起我们的关注。

1.png

实际上,Diff工具和Bug追踪器对于理解代码也非常有用,它们能够帮助了解特定的代码库中曾经出现过什么样的bug,其中有些是安全bug,但有些只是普通的bug。其中,有些bug还没有被我们认识到其安全隐患,或者也许当时没有安全影响,但现在情况不同了,也许已经成为安全漏洞了。另外,如果你认为某个开发人员的提交中经常出现bug的话,也可以自动方式跟踪他们提交的所有代码。同时,它们也是可以bug的重要来源,帮助我们了解某段特点的代码为什么存在的原因等。不仅如此,它们还能带来许多启发,比如这些漏洞会有哪些变种,其他代码库中是否存在同样的代码,等等。

另外,对于代码库中的注释,也是相当有用的,因为他们记录了一些你可能从未想过的东西。

随手记下自己的发现

我发现,许多漏洞研究人员都没有随手做记录的习惯。实际上,我们真的要养成这种好习惯,在研究代码的时候,不妨随手记下当时的点子,潜在的漏洞,有什么不对劲的地方,相关的数据结构以及算法等等。这样的话,能够帮我们迅速的切换回当时的研究状态中。

1.png

同时,记录失败的想法真的很重要,至少跟记录成功的思路同等重要。我曾经浪费了很多时间,因为我切换回代码库后,看到一些东西,我觉得这看起来像一个漏洞,但没过多久我就意识到,我以前也看过这个东西,当时并认为它并不是一个漏洞,但并没有写下这么认为的具体原因。所以,上次已经搞定的事情,如今又重复了一遍过程,花了几个小时的时间,才重新搞清楚它为什么不是一个漏洞,这简直就是浪费时间!所以,写下那些失败的想法和失败的原因是非常有用的,这样当再次检查代码的时候,只需检查那些特定的条件是否已经改变就行了。

1.png

所以,如果你花了大量的时间研究某个特定的代码库,这些笔记就是无价之宝。为了发挥它们的最大威力,建议看看实现同样功能的代码库,看看它们是否会犯同样的错误。事实上,它们经常的确如此,退一步讲,即使它们没有犯同样的错误,但是小心翼翼地做了大量的工作来避免这件事情,那就说明,这的确是一个非常容易犯的错误。所以,很有必要再考察其他类似的代码库,大概率会有惊喜。

每当我遇到一些复杂的组件,并发现一些眉目的时候,我就会转而考察其他类似的组件,看看它们是否犯了不同的错误。顺便说一句,当我很难理解一个特定的组件在做什么时,有时我去看一个不同的代码库,也许它们的代码是以一种更容易理解的方式实现的,所以,这种做法是很有用的。

1.png

这方面的例子,就是前面Natalie发现Signal漏洞的过程。实际上,她投入了许多时间来了解WebRTC和状态机等知识,这些就是她的启动成本。当她在Signal程序中发现一个漏洞后,为了最大化投入产出比,于是考察了类似的即时通讯软件,并在其中发现了同样的漏洞。

经常回顾代码库和尚未成为漏洞的bug

1.png

众所周知,代码库不是静态的,因为代码可能被重写,功能可能会添加或修改,并且环境也不是静态的,此外,缓解机制等也会发生变化。因此,随着环境的变化,也许回顾它们的时候能够有新的收获。

分析失败案例

1.png

前面讲随手记录的习惯时,讲过要连同“失败的漏洞”一同记录下来。关于这方面,我有过一个经历。当时发现了一个漏洞,类似数据结构中的溢出漏洞,但是被覆盖的字段根本没有用,也无法溢出到足够远的相邻内存中以完成有用的事情,所以,基本上这个漏洞没多大价值。但一年后,我再次“光顾”同一个代码库时,发现添加了一些与我发现的漏洞完全无关的新功能,但是他们添加这些新功能的结果是,在该数据结构中添加了更多的成员,以至于可以溢出到这些成员中,因此,这个之前无用的漏洞,突然变成香饽饽了。因为我能够使用这些新的字段来做一些事情,做一些触发安全问题的事情。

几年前,我曾经发现了一个漏洞,刚发现它时,我非常兴奋,但是,当我触发它时,在相同的代码路径上触发了一个空指针解引用,指向感兴趣的漏洞所在的位置,并且没有办法绕过它,这显然很令人非常沮丧。但一段时间后,再一次查看那个代码库时,发现他们修复了空指针解引用问题,但没有修复另一个漏洞,所以,了解失败原因,然后检查这些条件在新版本的代码库中是否仍然成立,这可能是非常有用。

复盘失败案例时,有时也会捡漏:比如,它本来就是一个真正的安全漏洞,只不过当时遗漏了某些东西;或者,你现在已经掌握了某些新技能,通过组合这些技能,使得原先以为无法利用的东西,现在已经不在话下了;或者,代码库添加的新功能,可以使得原来的bug变成了一个正在的安全漏洞。无论如何,通过复盘,至少可以总结教训,查缺补漏——任何时候,失败都是一个不可多得的学习机会。

善于借助工具

1.png

借助于合适的工具以及交互式测试技术,可以节省大量的时间和精力。不过,还需结合你对特定问题的理解,才能将工具的威力发挥到极致。但在我的职业生涯中,我从未用过这些技术,因为我超级懒;同时,对于阅读代码,通常是为了满足自己的好奇心,而不仅仅为了是寻找bug。如果您自己在利用工具方面不是很擅长,可以跟喜欢工具化的人合作,互通有无。

1.png

关于工具,我觉得有必要提示一点,尤其是对于第一次进入漏洞研究的年轻人来说,它可能是一个巨大的兔子洞,它会让你永远无法真正掌握理解代码所需的技能,比如能够阅读其他人的代码并理解上下文的能力。因为即使自己打造的工具,也是编写代码,而不是阅读他人编写的代码。所以,如果你是一个热衷于创造工具的人,我会建议你考虑与一个非常专注于漏洞研究进行合作,这样更容易获得成果。因为我在职业生涯中见过很多次,有些人刚开始是做漏洞研究,可做着做着,就一头扎进工具上面去了。渐渐地,他们忘记了他们创造工具的初衷,而是醉心于超级复杂的程序,尽管这些程序并不是特别有用;或者沉溺于自认为很优雅的东西,但是只能在他们的头脑中运行,但实际上并没有实践。所以,通过与一个非常专注于寻找漏洞的人合作,你会得到持续的、真实的反馈。因为你认为很酷的东西,可能实际上根本行不通;而你认为不太优雅的想法,实际上却非常有效。

我曾经遇到一些人,为了研究漏洞,他们会花大量的时间,比如几个月,甚至几年来打造史诗般的工具;但是,我想说的是,如果直接阅读代码,恐怕早就能挖出大量的漏洞了。所以,这种情况下与不同兴趣的人互相合作,更容易取得成果。

1.png

小结

下面,我们总结几条挖洞的原则,具体如下图所示:

1.png

我们知道,漏洞研究虽然很难,却是一种可以习得的技能。不过,挖洞过程中,积极调整心态,合理安排手头上的项目,持续关注新事物,也是不可或缺的。最后,特别强调一下,阅读代码是一种有别于编写代码的技能,它也需要大量的练习。但是,大多数编写代码的人通常有种误解,那就是能写代码的人,自然擅长阅读别人的代码,但事实并非如此。所以,你必须努力成为阅读代码的好手,才能找到真正的安全漏洞。

视频地址:https://www.youtube.com/watch?v=7Ysy6iA2sqA&list=PLYvhPWR_XYJnPvrhXE4RYvwZhV26nYTIp&index=3

免责声明:文章内容不代表本站立场,本站不对其内容的真实性、完整性、准确性给予任何担保、暗示和承诺,仅供读者参考,文章版权归原作者所有。如本文内容影响到您的合法权益(内容、图片等),请及时联系本站,我们会及时删除处理。查看原文

为您推荐