导语:教你如何在挖洞过程中寻找灵感。
前言
我会在本文中把平时在应用程序中发现漏洞的方法,例如属于软件安全漏洞,而不是配置错误或补丁管理问题等一些技巧和大家一起分享。
本文分三大部分,我将首先回顾一下我认为比较高水平的挖漏洞方法,然后讨论在寻找安全漏洞时需要了解和执行的内容。最后,我将讨论一些其他的挖漏洞思想和经验,这些方法并不常见。
什么是漏洞发现过程?
从某种角度来说,漏洞发现过程可以被比作发现迷宫的出口一样。
1.你不会立刻看清楚迷宫的的全貌;
2.通过探索逐渐看清全貌;
3.有多个位置并不确定的起始点和端点;
4.虽然最终你并不会100%搞明白迷宫的全部地图,但从迷宫走出去是不成问题的。
转换成挖漏洞的思维就是:
1.枚举入口点即与应用程序交互的方式;
2.想一想应用程序会在哪里出现不安全的状态即漏洞;
3.使用指定的入口点操作应用程序,看是否会出现不安全的状态。
在这个过程中,迷宫就好比是应用程序,地图是你对它的预期理解,起点是你的入口点,终点是你发现不安全的状态。
入口点可以是UI中明显可修改的参数,也可以是对最终用户来说比较模糊或透明的交互(例如IPC)。对于安全研究员来说,还有一些更有趣的入口点:
1.老旧代码区域,例如,从遗留系统中过渡的痕迹;
2.划分团队或个人,例如互操作性的开发工作的交叉部分;
3.调试或测试代码,这些代码被从开发部门带到实际运行中;
4.客户机调用的api与服务器公开的api之间的差别;
5.不打算受到最终用户直接影响的内部请求,例如FormField对象所组成的集合。
我所考虑的漏洞类型可以分为两类:普通型和上下文型。一般的漏洞,例如,RCE、SQLi、XSS等,我通常可以在不了解其业务逻辑的情况下在许多应用程序中找到漏洞。而上下文漏洞,如未经授权的设备暴露或篡改,则需要更多的业务逻辑、信任级别和信任边界规则的知识。
我的经验是,优先考虑什么会对利益相关者产生最大的影响。而利用轻量级的威胁模型,如一种基于STRIDE威胁模型,也可以帮助弄清楚到底出了什么问题。
以下是一个web应用程序示例,以及一个桌面应用程序示例。
假设这个应用程序是一个金融门户网站的单页Web应用(single page web application,SPA),我已经对它进行了验证,但是没有服务器端源代码或二进制文件。当我在枚举入口点时,我可以探索站点的不同特性,以了解它们的运行目的,看看我的HTTP代理中有哪些请求,并将一些清晰的信息组合成初步的地图。我还可以查看客户端JavaScript,以获取SPA所调用的RESTful api的列表。没有服务器端代码的一个限制是,我不能看到由SPA调用的api和由服务器公开的api之间的差别。
然后可以操纵被识别的入口点,试图到达我正在寻找的不安全的状态。当我考虑到表面的漏洞时,我应该构建适合应用程序的技术堆栈和业务逻辑的测试用例列表。如果没有,我就会去尝试那些永远无法工作的测试用例(例如,当后端使用Postgres时,尝试使用xp_cmdshell),而不需要对应用程序进行更深入的了解(例如,在外汇请求的货币参数中找到验证差距)。
在桌面应用程序中,通过确定的入口点来寻找漏洞的基本过程仍然适用,但也有一些不同之处。与web应用程序最大的不同之处在于,它需要一组不同的主题知识和方法来执行。前10 位“OWASP”工具将不会有多大帮助,将应用程序连接到一个HTTP代理来检查网络流量可能不会产生同样的运行水平。这是因为入口点更多样化,包含了本地向量。
与黑盒方法相比,当你访问源代码时,不确定性会少一些。特别是在针对弱的代码路径和效载荷方面。你可以从一个脆弱接收器(Sink)开始,然后倒着到达一个入口点,而不是通过一个入口点来发送有效载荷。而利用白盒方法,你会受到固定思维的限制。
挖漏洞需要具备哪些知识?
漏洞研究所需的知识是广泛的、随时间变化的,并且根据应用的类型不同而有所不同。然而,知识的领域往往保持不变,可以分为四种:
1.应用程序的技术:也就是说开发人员应该知道如何构建目标应用程序的技术,包括编程语言、系统内部构件、设计范例或模式、协议、框架或库等等。如果一个研究人员拥有与他们的目标相适应的经验,则更好。
2.进攻和防御的概念:从基本的安全原则到不断演变的技术,进攻和防御概念的结合可以引导研究人员发现漏洞,同时绕过漏洞。
3.工具和方法:要有效地将概念付诸实践,就要花时间学习如何使用工具并配置它们,优化重复的任务,建立你自己的工作流程。了解相关的工具是如何工作的,如何开发它们,以及如何为不同的用例重新设计它们非常重要。
面向过程的方法比以工具为导向的方法更有价值,当使用的工具被限制时,研究人员不应该停止探索。我的方法主要来自于阅读书籍并亲自动手实践,以及在不断地试错中学习。
4.目标应用程序:重要的是能够更好地理解应用程序的安全属性,而不是它的开发和维护。这不仅要考虑应用程序的安全特性,还涉及到获取其用例的上下文,枚举所有入口点,并能够假设适合其业务逻辑和技术堆栈的漏洞。
下面的表格展示了一个示例,说明了在web应用程序和Windows桌面应用程序中研究漏洞所需要的知识。
为了获得这些知识,我花了成千上万个小时,遭遇了几百次错误,无数个不眠之夜。这些知识领域能极大的增加挖到漏洞的可能性。
执行的是什么活动?
在分析应用程序时,我使用了下面的四种“分析模式”,每当我遇到困难时,就会不断地从一种模式切换到另一种模式。这不是一个线性或循环的过程。我不确定这个模型是否详尽,但它确实能帮助我进行跟踪。
每个模式中都包含了主动和被动的活动,主动活动需要与应用程序或其环境进行一定程度的交互,而被动活动则不需要。也就是说,描述并不总是明确的,每个活动都有其目的:
1.了解有关安全性的假设;
2.假设如何破坏他们;
3.试图破坏他们。
用例分析是了解应用程序的用途以及它服务的目的,这通常是我在新申请的时候做的第一件事。与应用程序的工作版本进行交互并阅读一些高级文档有助于巩固我对其特性和预期边界的理解,这可以帮助我更快地提出测试用例。另外,我会尽最大可能请求内部文档(威胁模型、开发人员文档等)以便获得更全面的理解。
这可能不像做深度用例测试那样有趣,但该方法确实为我节省了很多时间。比如Oracle Opera示例,通过阅读用户手册,我能够快速发现哪些数据库表存储了有价值的加密数据。
实现分析(Implementation analysis)是用于理解应用程序所在的环境的,这可能包括在被动活动中检查网络和主机配置,并在主动活动中执行端口或漏洞扫描。
这可能是一个系统服务,它安装在可执行文件的ACL上,允许低权限用户修改它从而导致本地特权升级。另一个例子可能是一个web应用程序,它在同一个主机上有一个暴露的匿名FTP服务器,可能会导致源代码和其他敏感文件的暴露。这些问题并不是应用程序本身固有的,而是如何在它们的环境中实现的。
通信分析(Communications analysis )的目的是了解目标如何与其他流程和系统交换信息,通过监控或积极地通过不同的入口点发送精心设计的请求,并检查响应是否产生不安全状态,以发现漏洞,许多web应用程序漏洞都是这样发现的。如果有漏洞的话,网络和数据流图有助于了解到更多的信息。
虽然这种模式需要了解应用程序特定的通信协议,但是对于应用程序的内部工作原理的理解却不是这样的。在这个分析模式中,用户影响的数据是如何被传递和转换的,在一个系统中或多或少被视为一个黑盒方法,其重点是监控和发送请求,已进行最后的结果分析。
还是以上面假设的金融门户为例,我可能会考虑到网站允许客户以不同的货币购买预付信用卡的功能,作为一个人为操作的例子。假设购买请求采用的是以下参数:
1. fromAccount:取款的账户,用来购买预付卡;
2. fromAmount:获取账户的金额;
3. cardType:购买的信用卡类型,如USD,GBP;
4. currencyPair:fromAccount和cardType的货币对(如CADUSD,CADGBP)。
我要做的第一件事就是发送一个标准请求,这样我就知道了正常响应应该是什么样子的。如下所示,就是从一个CAD帐户购买82美元的信用卡的请求和回应:
虽然我可能不知道幕后到底发生了什么,但它是由状态属性指定的。现在,如果我将fromAmount更改为负数,或者将fromAccount调整到另一个人的帐户,则这些返回指示可能正在执行验证的错误响应。如果我将CADUSD到CADJPY的currencyPair的值进行修改,我将看到toAmount的范围从82.20到8863.68,而cardType仍然是USD。我可以通过使用更有利的汇率来获得更大的回报,而设定的卡片类型保持不变。
如果我能够访问后端代码,就可以更容易地知道用户输入了什么,并提出了更彻底的测试用例,从而更精确地寻找到不安全的状态。我猜测,客户端没有公开的另一个请求参数可能会以潜在的恶意方式改变预期的行为。
代码和二进制分析能帮助我理解对用户影响的输入是如何在目标应用程序中进行传递和转换的,通过对实际的恶意软件进行分析,我发现如果把最后三种分析模式比作外部的分析,那么这种模式就是内容分析的开始。
静态和动态分析可以执行各种各样的挖漏洞活动:
1.数据流分析:这对于监控入口点和了解数据如何流向潜在的不安全状态非常有用,当我试图在通信分析的环境中获得有效载荷时,我会用不同的方式来尝试去实现我所假设的不安全状态。我可以先检查一下这个不安全的状态是否确实存在,如果存在,弄清楚如何使我的载荷更精确地到达那里。正如前面所提到的,这种模式的好处之一是能够找到不安全的状态,并能够通过倒推获得相应的入口点的载荷。
静态和动态分析在这里是紧密相连的,如果你想从入口点到达不安全的状态。那么静态分析就像阅读地图一样,动态分析就像是对交通和天气状况进行实时分析一样。从静态分析中得到的应用程序的广泛而抽象的理解,可以通过动态分析具体化。
2.进口分析(Imports analysis):分析导入的api可以深入了解应用程序的各个函数以及它如何与操作系统交互,特别是在上下文缺失的情况下。例如,使用加密函数可以表明某些有价值的东西正在受到保护。你可以通过追踪交互信息来弄清楚它在保护什么,以及它是否受到了适当的保护。如果创建了一个流程,你可以查看用户输入是否可以影响它。了解该软件如何与操作系统交互,这可以为你提供与它交互的入口点(例如,网络监听器、可写文件、IOCTL请求)。
3.字符串分析:在分析导入时,字符串可以对程序的功能提供一些见解。我倾向于寻找诸如调试语句、密钥或令牌之类的东西,以及任何看起来可疑的东西,因为它们都有可能在正常函数运行时出现意外。有趣的字符串可以追溯到其用法,并查看是否有从入口点可到达的代码路径。要注意的是,把核心程序中的字符串和作为静态导入库一部分的字符串区分是很重要的。
4.安全扫描诊断:自动化的源代码扫描工具可能有助于找到通用的低级漏洞,但在发现基于上下文或基于设计的漏洞时实际上是无用的。我通常不认为这个办法最有效,因为误报因素的数量非常多。
5.相关分析(Dependency Analysis):这涉及到对已知漏洞的相关分析,例如,开源组件,或者发现可以在目标应用程序上下文中使用的公开未知的漏洞。一个现代的大型应用程序通常建立在许多外部相关项之上。这些相关性的某些环节说不定就存在漏洞,这样利用这些漏洞再间接的进入到主应用程序,并最终开发一个入口点。常见的例子包括heartUNK、Shellshock和各种Java反序列化漏洞。
6.库的分析:如果你有访问代码库的权限,那么这有可能有助于识别一些特别的漏洞。除了拥有比单独使用二进制文件所带来的好处之外,还更容易找到老代码区域,这些代码在很长一段时间内并没有发生变化。
与其他模式相比,代码和二进制分析通常花费更长的时间,这就可能会让分析变得更加困难。因为此模式下,研究人员经常需要理解对应用程序有很深的了解。而在实践中,这很难做到。
在进攻端,发现漏洞需更有敏锐洞察力,并且还要适应多种攻击环境。而在防御方面,可以提供特殊的补救建议,以针对代码级别的漏洞。
类似与所需的知识,你可以积极地将这种分析模式与其他模式结合在一起,以便增加找到漏洞的可能性。
其他的想法和教训
本节讨论一些不常见的其它想法。
脆弱的复杂性
漏洞的复杂性各不相同,一方面,有一些很明显的漏洞,它们能够被轻而易举地发现,因为这些漏洞的代码很容易出现在常见的重点监控领域,例如经典的SQLi认证绕过。另一方面,系统元素之间没有预期的交互作用的结果,它们本身既不安全,也没有被特殊设计,但组合在一起时,会导致一个漏洞,例如,Intel处理器Memory Sinkhole漏洞。
通过团队协调挖漏洞
向你的团队坦率地介绍你所知道的和不知道的事情通常是有帮助的,这样你就可以在专业领域上的互补。比如:
1.小明探测到了入口点和它们的参数,而阿龙则寻找到了带有漏洞的接收器。
2.小明将有效载荷从一个脆弱的接收器中取出,而阿龙则将协议的意义放在了接收器对应的入口点上。
3.小明通过动态分析客户机请求来恢复结构,而阿龙则分析如何静态地接收它们。
4.小明在一个网络上寻找可访问的文件共享,而阿龙则通过它们获取有价值的信息。
总的来说,安全团队可以提高工作效率,但要做到这一点很不容易。
总结
感谢你花时间阅读这篇文章,我希望这能让你获得挖漏洞的灵感。总的来说,挖漏洞就是一个持续不断的学习过程。