-
-
[原创]Windows主机入侵检测与防御内核技术深入解析(8)
-
发表于: 2024-6-12 07:44 8579
-
本系列文章为看雪星星人为看雪安全爱好者创作的原创免费作品。
欢迎评论、交流、转载,转载请保留看雪星星人署名并勿用于商业盈利。
本人水平有限,错漏在所难免,欢迎批评指正。
在软件开发完成之后,理应进行测试和评估以证明其质量。但安全方面的测试和一般软件的测试不同。
一般的软件通常有一个功能列表,测试的目的是为了确保所有的功能均可达到需求预期的目的,这种预期一般是实在且有限的。但安全方面的需求的预期则和实在和有限截然相反,往往是“虚无且无限”的。
这里所谓的“虚无”是说其预期很难用一个实在的结果来展示。比如我预期的目的是“该软件无法被攻破”,那么我要如何才能展示某个软件无法被攻破呢?
即便我把它挂在网上邀请全世界所有的黑客来攻击,十年内无人攻破也无法证明它是无法攻破的。因为十年内无人攻破并不说明它永远不会有人攻破。更夸张地说,即便地球人无法攻陷它,也无法证明宇宙内没有外星人能攻破它。
所谓“无限”是指我们无法穷举攻击的手段。实际上,因为攻击的手段是有无限多的,安全功能的预期其实是“无穷大”的。实际上,任何不带明确范围限制的安全功能的描述都是不可靠的。如一些安全软件的广告中会描述如下一组功能:
(1)能有效防范rootkit病毒。
(2)保护用户密码不会泄漏。
(3)能具有保护用户机密文件的功能。
以上三条没有任何一条是实在且有限的描述。比如第(1)条,要证明该软件能有效防范rootkit病毒,就必须测试世界上任何可能存在的rootkit病毒。但这是不可能的。rootkit病毒如同生物界不断排列组合DNA进化的真实病毒,是无法穷举的。同样,所谓保护用户密码不会泄漏有如何证实呢?有些泄漏方式(比如严刑拷问)甚至是安全软件根本无法防御的。
因此,尝试对安全功能进行评估时,我们不得不尽力去把“虚无且无限”的需求,变成“实在且有限”的需求。如我们在第3、4章所设计的内核安全模块,如果我们的预期是“任何恶意代码都无法在用户电脑上执行。”这个描述显然是虚无且无限的。我们应该如何明确需求呢?方法不外乎如下几种:
(1)限定明确的目标对象实体
(2)限定明确的执行环境。
(3)将否定性的描述改为肯定性的。
(4)避开任何无法明确的预期。
“恶意代码”本身就是很难界定的实体。我将这个实体改为“PE文件”将会变得明确。因为PE文件的格式是清楚的。当然,因为这个限制的存在,我们的“防御能力”也大大降低了。
Windows上虽然主要的可执行文件是PE文件,但这并不是全部。而且即便Windows只有PE文件能执行,能阻止用户运行任何恶意的PE文件,也无法保证“恶意代码”就无法执行了。
比如有人打开了一个pdf文件,而pdf文件中含有的恶意代码利用pdf阅读器的漏洞成功得到执行权。这个过程用户并没有执行过任何恶意的PE文件(有善意但有漏洞的PE文件被执行),但恶意代码依然能执行(非模块的原生执行被本书归为壳代码执行或利用执行)。
用户不会关心这些细节,但我们自己一定要明确某个安全组件的明确的预期。否则项目的结果将完全无法评估。
在执行环境方面,我们可以限定操作系统的版本和执行权限等级。考虑到Windows内核环境的高权限和复杂的对抗,限定在用户态环境,但有管理员权限是个合理的选择。也就是说,我们防范的是来自有管理员权限的用户态应用程序,而不是从操作系统内核发起的攻击。
环境限定必须是用户可接受且可行的。如我限定了Windows11版本,而用户接受所有的Windows版本都使用Windows11,那么这条限定是用户接受且可行的。同理,如果将来出现Windows12,而用户需要升级,我们再投入人力升级到支持Windows12也无不可。
但有些限制看似合理但实际上用户却不可接受。比如把环境限制为“没有管理员权限的用户态环境”。实际上,非管理员权限的Windows环境在国内用得不多,尤其是进行软件开发工作得时候,非管理员权限会导致很多工作无法进行。因此这样的限定大概率会导致用户无法接受。
另外一些环境条件则是受限于技术而无法去除,比如 “用户态环境”的限制。声称能防范 “即便是来自内核的攻击”是非常酷的。实际上Windows内核已经沦陷的情况下,模块执行的防御没有太大意义。内核无需绕过安全系统去执行模块,本身就已经可以实现任何恶意操作了,甚至可以将我们的内核安全组件破坏掉。
同时,“恶意模块……无法执行”是一个否定性的、无法明确的预期。要证明“无法”就远比证明“能”更难。同事,要界定“恶意模块”又是难上加难。
因此我们把“界定”任务实际留给了用户或者是其他的机制,由用户的安全管理部门或者是其他的机制去界定何为恶意模块。我们提供的功能是一个“能够拦截”并提供“根据界定选择允许或禁止”的能力。这样我们就避开了一堆无法明确的预期。
所以我们的明确需求为:“Windows11用户态包括管理员权限的任何权限环境下任何PE文件的加载执行都能被拦截,并可根据用户的判断选择放过执行或阻止执行。”
要注意这样的预期是仅用于内部项目测试和漏洞分析的,和用于宣传推广的PPT、发给目标用户厂商的宣传单是两回事。用户直接看到这样的内部预期必然会拒绝接受。
但对我们的这个安全组件的测试和漏洞分析来说,这样的预期是是更明确的。只有各个安全组件的预期明确并做出各个安全组件的评估和测试,我们才可能将这些结果的拼图综合到一起,为整个系统的风险做出正确的评估。
在5.1.3节我们会发现,即便是如此明确的预期,在漏洞分析中出现的不可控因素依然是非常多的。也就是说,获得完美的结果依然是不可能的。我们将不得不做出各种妥协,并评估许多残余风险。
当我们有了明确的预期,那么接下来的测试就是要证明能否达成我们的预期。在安全系统测试中,渗透测试是非常有价值和重要的。但一个常见的错误认识是,安全系统开发完毕之后就万事大吉,剩下的交给渗透测试来评估就可以了。
实际上,仅仅进行渗透测试是无法保证安全系统的安全性的。渗透测试的目标是“找出漏洞”,而绝非是“评估所有可能的漏洞”。只需要一个漏洞就可以让渗透测试成功,然后实际的工作流程变成这样:
渗透测试发现漏洞1->修改代码弥补漏洞1->渗透测试发现漏洞2->修改代码弥补漏洞2……如此循环。
因为漏洞无限多,所以该循环可以无休无止。而且聘请渗透测试人员非常昂贵,发现了漏洞就算成功结束。等到打上补丁后下次再进行渗透测试又不知道是猴年马月。因此每次循环的时间也是极长的。
后果就是安全项目长期处于只知道已经修补了少数漏洞,但既不知道还有究竟有多少漏洞、也不知道还需要多久才能达到可用的安全的状况。
问题的根本在渗透测试一般是黑盒攻击,渗透测试人员并不掌握代码。即便提供代码给渗透测试人员,渗透测试人员也未必像开发者一样熟悉这些代码。渗透测试人员的目标是渗透成功,而不是评估代码中所有漏洞。
事实上,安全组件的安全性应该首先由最熟悉代码的开发人员做出评估。在开发过程中,他们最清楚自己留下了什么漏洞。剩下的,他们自己无法想到的,才有理由由专门的渗透测试人员来挖掘。没有由开发者进行过漏洞分析的项目,必定会留下难以评估的风险,做渗透测试完全是浪费成本。
很多项目会在开发结束之后进行起码的漏洞分析。但只是由开发人员简单地编写一份文档就完事了。
在项目中我常常见到各种文档。比如安全模块的说明、安全模块的设计、安全模块的错误码列表等等。有意思的是,大部分文档不是永远被置之高阁无人使用,就是用的时候发现问题百出,和实际情况完全不同。所以如果我作为开发人员,专门为某个我开发的安全模块写了一份名为“漏洞分析”的doc文档,那么其下场也必然如此。
为什么文档永远无法和实际匹配?关键在于文档永远不会运行起来。和实际匹配的只有在运行着的代码和配置[1]。
文档都是各种人员为了应付差事而编写的。他们要么代码水平很好但文字水平很差,要么完全反过来文字水平很好但对代码一窍不通。文档写好整理完毕发布之后极少会再修改,即便修改也往往难以快速更新到所有的阅读者。而代码和配置的修改往往是十万火急而且频繁进行的。因此文档不可能跟得上实际运行的代码和配置。
漏洞分析也是一样。单独撰写的漏洞分析文档没有意义。也许最开始它是符合实际情况的,但数十次修改代码之后,它和实际情况已经不再有任何相似之处,除了误导人之外再没有其他的作用。