麻省理工学院的课程“计算机系统安全”。 第2课:“控制黑客攻击”,第1部分

麻省理工学院。 讲座课程#6.858。 “计算机系统的安全性。” Nikolai Zeldovich,James Mickens。 2014年


计算机系统安全是一门有关开发和实施安全计算机系统的课程。 讲座涵盖了威胁模型,危害安全性的攻击以及基于最新科学研究的安全技术。 主题包括操作系统(OS)安全性,功能,信息流管理,语言安全性,网络协议,硬件安全性和Web应用程序安全性。

第1课:“简介:威胁模型” 第1 部分 / 第2 部分 / 第3部分
第2课:“控制黑客攻击”, 第1 部分 / 第2 部分 / 第3部分

James Mickens:在上一讲中,我们了解了有关缓冲区溢出攻击的所有知识,今天我们将继续讨论启动这些攻击的一些方法。 缓冲区溢出攻击背后的基本思想如下。



首先,我注意到这些攻击会影响几种不同的情况。 他们使用的第一种情况是系统软件通常是用C编写的。

系统软件是指数据库,编译器,网络服务器等。 您可以回想起您喜欢的命令外壳之类的东西。 所有这些“软件”通常都是用C编写的。为什么要用C编写? 因为,首先,它速度更快,其次,C被认为是最适合各种硬件平台需求的高级汇编器。 因此,所有关键系统都以这种低级编程语言编写。 用C编写的软件的问题在于它确实使用原始内存地址,并且没有任何工具或软件模块来检查它们。 在某些情况下,这可能会导致灾难性的后果。

为什么C中没有数组索引检查,即没有边界检查? 原因之一是硬件没有。 用C语言编写的人们通常希望最快的程序执行速度。 另一个原因是,正如我们稍后将讨论的那样,在C语言中,实际上很难定义指针的含义以及指针应在何种程度上起作用。 因此,在某些情况下,在C中自动化软件过程将非常困难。
让我们讨论一些实际上试图创建某种类型的自动内存管理的技术。 但是,正如我们将看到的,这些方法都不是完全“防弹”的。

另外,缓冲区溢出攻击利用了x86的体系结构知识,例如,堆栈朝着哪个方向增长。 什么是函数调用约定? 访问函数C时,堆栈是什么样的? 当您在堆上选择一个对象时,这些主要选择的结构是什么样的?

让我们看一个简单的例子。 这与您在上一堂课中看到的非常相似。 因此,这里有一个标准的读取请求,然后得到一个缓冲区,然后是规范的int ,然后是臭名昭著的gets命令。 下面还有其他必要的事情。



因此,正如我们在上周的讲座中所讨论的那样,这是有问题的,对吗? 因为此获取操作不会检查缓冲区边界。 如果用户用数据填充缓冲区,并且我们在此使用了此不安全的函数,则实际上我们可能会使缓冲区溢出。 我们可以重写堆栈的全部内容。 让我提醒您它的外观。

最底部是数组“ i”。 缓冲区位于其上方,其第一个地址位于底部,最后一个地址位于顶部。 无论如何,在缓冲区上方,我们都有间隙指示器的保存值-保存值EBP。 它的上方是函数的返回地址,甚至更高的是前一帧中的某些内容。

而且不要忘了,这里的底部在“ i”的左侧,我们有一个ESP堆栈指针插入其中,并且在保存的EBP部分中出现了新的中断指针。 返回地址包括ESP,而前一帧的其余部分包括断点。



让我提醒您,堆栈溢出的方式是沿向右箭头的方向向上累积数据。 启动gets操作时,我们开始将字节写入缓冲区,最后,它将开始覆盖位于上游的所有内容。 基本上,一切对您来说都应该很熟悉。

攻击者会采取什么措施来利用此优势? 基本上,它输入很长的数据序列。 因此,关键思想是可以使用这种技术进行攻击。

并且,如果攻击者捕获了返回地址,则他可以确定函数在溢出后将跳转到何处。 也就是说,黑客唯一能做的就是拦截返回地址并跳转到他想要的任何地方。 多数情况下,攻击者使用具有特权的代码来控制拦截过程。

因此,例如,如果此过程是高优先级的,则它是以root或admin身份运行的,无论我们如何称呼您喜欢的操作系统的超级用户,现在由攻击者控制的该程序都可以使用此优先级的特权来执行所需的操作。 因此,如果黑客损坏了邮件服务器,则可以读取文件或发送垃圾邮件。 它甚至可以击败防火墙,因为防火墙的思想是在防火墙后面有“好”机器,而外面有“坏”机器。 通常,防火墙内的计算机彼此“信任”,如果您设法入侵受防火墙保护的网络内的至少一台计算机,那就太好了。 因为现在您可以跳过这些计算机通常针对“外来”计算机所做的许多检查,因为它们会认为您是受信任的人。

作为学生,您必须考虑一件事:

“太好了,他们向我们展示了如何使缓冲区溢出,但是为什么操作系统不能停止它呢? 她不是像银河护卫队那样保护周围环境中发生的邪恶事件的人吗?”

重要的是要注意,操作系统不会持续监视您。 硬件观察到,它提取指令并解密,然后执行许多类似的操作。 但是首先,操作系统是做什么的? 它基本上设置了允许应用程序运行的页表,例如,如果您要求操作系统发送网络数据包,或者要发出某种IPC请求或类似的内容,则将向OS寻求帮助。 但是操作系统不会遵循您的应用程序执行的每条指令。 换句话说,当此缓冲区已满时,操作系统根本不会监视该堆栈的内存使用情况。 作为进程的发起者,所有这些地址空间都属于您,而这不适用于OS。 您可以执行此操作,而操作系统无法帮助您解决问题。

稍后,我们将讨论OS在硬件方面可以做的一些事情,以防御此类攻击。 让我再次提醒您-实际上,只有硬件可以监视您的工作并对其做出反应。 因此,您可以利用某些特殊类型的保护,我们将进一步讨论。

这就是缓冲区溢出的样子。 我们将如何解决所有这些问题?

防止缓冲区溢出的一种方法是简单地避免C代码中的错误,这是一种建设性的方法,因为如果您的程序没有错误,则攻击者无法使用它们。 但是,这说起来容易做起来难。 程序员可以做一些非常简单的事情来提供“卫生”的安全性。 例如,我们现在知道的诸如get之类的功能可以称为“ gos-tos”或“捕获操作系统”,这是安全漏洞。

因此,当您使用现代编译器(例如GCC或Visual Studio)编译代码时,它们将指出此类功能的缺点。 他们会说:“嘿,您在使用危险的东西,最好考虑使用fgets函数或其他可以真正跟踪边界合规性的函数。” 这是程序员可以做的简单的事情之一。

但是请注意,许多应用程序实际上在不使用所有这些功能的情况下操纵缓冲区。 这在网络服务器中很常见,网络服务器定义了自己的解析过程,然后确保以所需的方式从缓冲区中检索数据。 因此,仅仅将自己限制在正确命令功能的选择上,将不可能完全解决问题。

使这种方法更难以解决的另一种情况是,问题并非总是很明显是由C编写的程序中的错误引起的。如果您曾经使用C编写的大型程序,则知道,就像在void *指针上方具有18个星号的函数标识符一样。 我认为只有宙斯知道这意味着什么,对吧? 使用C之类的语言,即使是程序员,也很难理解是否发生了错误。

通常,我们讲座的主要主题之一是C语言是魔鬼的产物。 我们之所以使用它,是因为我们总是想比其他所有人都快,对吧? 但是,随着硬件变得越来越快,我们使用更高级的语言来编写大量的系统代码。 但是,即使您认为这样会更快,但编写C代码并不总是有意义。 稍后我们将讨论这个问题。



因此,解决该问题的第一种方法是避免C程序代码中的错误,第二种方法是创建可帮助程序员发现此类错误的工具。 这种工具的一个示例是静态代码分析。 稍后我们将详细讨论它,现在我要说的是静态分析是一种甚至在程序源代码启动之前就对其进行分析的方法,它有助于检测潜在的问题。

假设您有这样一个函数,我们将其称为void foo(int,* p) ,它包含整数数据和一个指针。 假设它声明了一个整数偏移值int off 。 此函数声明另一个指针并向其添加偏移量: int * z = p + off 。 即使现在,在编写函数时,静态代码分析也可以告诉我们该偏移量变量尚未初始化。



因此,通过分析程序,可以回答我们的功能是否正常工作的问题。 在此示例中,很简单,因为没有偏移初始化,所以看到的答案是“不,不会”。 静态分析是一种软件,当您使用流行的编译器构建代码时,它将告诉您:“嘿,伙计,这件事没有初始化。 您确定要这样做吗?” 这是使用静态分析的最简单示例之一。

另一个例子考虑了当我们有一个函数的一个分支,即在一定条件下执行它的情况。



因此,如果偏移量大于8, 如果(off> 8) ,则会导致对某个功能的调用(off) 。 因此,这种情况告诉我们偏移值是多少。 即使忽略偏移量未初始化的事实,在分析函数的此分支时,我们仍然发现它可以大于8。因此,当我们开始对bar进行静态分析时,我们发现偏移量只能采用某些值。 我再次指出,这是对静态分析的非常肤浅的介绍,稍后我们将更详细地考虑该工具。 但是此示例说明了即使不执行代码也可以检测到某些类型的错误。

因此,您可能要考虑的另一件事是,它与静态分析具有相同的作用。 这是软件模糊测试。 他的想法是,您将程序代码中的所有功能都纳入其中,然后在其中输入随机值。 因此,代码值和格式的所有选项都重叠。 也就是说,模糊测试是一种通过将无效数据或格式错误的数据提交给程序输入来自动搜索漏洞的工具。 例如,您在单元测试中输入值2、4、8和15,并且会收到一条消息,提示数字15可能是错误的,因为所有数字都是偶数,但它是奇数。

实际上,您需要查看整个程序中有多少分支会影响您的测试代码,因为这些分支通常是隐藏“错误”的地方。 程序员不会考虑这种“麻烦”,因此他们通过了一些单元测试,可以说出其中的大多数测试。 但是,他们没有检查程序的所有“细节”,这就是静态分析可以提供帮助的地方。 同样,使用类似限制的概念。 例如,在我们的程序部分中,这是一个分支函数的条件,该函数定义的偏移量大于8。 因此,我们可以发现该位移是静态的。 并且,如果我们基于此限制使用自动模糊处理生成输入数据,则可以确保偏移量的输入值之一小于8,一个值等于8,一个值大于8。



因此,这是创建工具以帮助程序员发现错误的概念的主要思想。 使用C语言时,即使是部分代码分析也非常有用,我们将要研究的许多工具都可用来防止缓冲区溢出或检查变量的初始化,这些工具无法检测程序代码的所有问题。 但是它们在提高这些程序的安全性方面可能具有实际用途。 这些工具的缺点是它们不完整。 预期进度不是完整进度。 因此,您需要在用C编写的程序和其他程序中积极探索防止漏洞利用的问题。 我们研究了两种解决缓冲区溢出保护问题的方法,但是还有其他方法。

因此,第三种方法是使用内存安全语言或确保内存安全的语言。 这些语言包括Python,Java,c#。 我不想让Perl与他们相提并论,因为它被“坏人”使用。 这样,您可以使用内存安全语言,这似乎是您可以做的最明显的事情。 我刚刚向您解释了C基本上是高级汇编编码器,但是它提供了原始指针和其他不需要的东西,那么为什么不只使用这些高级编程语言之一呢?

这有几个原因。 首先,在这些语言中,有许多从C继承的代码元素。如果您启动一个新项目并使用一种高级语言来确保其安全性,那么一切都很好。 但是,如果给了您一个大型的二进制文件或使用C编写并维护了10-15年的大型源代码发行版,那将是一个世代相传的项目,我的意思是即使我们的孩子也将继续从事此工作? 在这种情况下,您将无法说:“我只是用C#重写了所有内容并改变了世界!”。

问题不仅仅在于C语言,还有一些系统您更应该担心,因为它们使用的是内战中的Fortran和COBOL代码。 为什么会这样呢? 因为,作为工程师,我们想认为我们可以自己构建所有东西,这将是非常棒的,它将是我想要的,而我将根据需要调用我的变量。

但是在现实世界中,这不会发生。 您出现在工作中,并且已经有这个系统,并且查看代码的基础并思考为什么它不执行所需的操作? 然后他们对您说:“听着,我们会做您想做的一切,但只能在该程序的第二个版本中进行,现在您必须做出我们必须做的事情,否则客户会收回他们的钱。”

那么,我们如何处理强制使用遗留代码的巨大问题呢? 如您所知,错误定义边界的系统的优点之一是它们可以与该过时的代码完美配合。 这就是为什么您仅通过切换到提供安全内存使用的语言就无法摆脱缓冲区溢出问题的原因之一。



如果我们需要对硬件的低级访问怎么办? 例如,更新驱动程序等。

因此,如果您需要对设备的低级别访问,则会出现另一个问题,这是在为某些设备编写驱动程序时发生的。 在这种情况下,您确实需要C提供的优点,例如,查看寄存器和类似功能元素的能力。

此外,当您担心系统性能时,就需要使用C。 , , , . , , . , , memory-safe . , JIT. , Java, Java Script. , , «». , . , «» x86.

, , -. , , JVM, - Java. , - . , - JVM , . , , . . , , .

, , 86. JIT- , . JIT- , .

, JavaScript , , «» 32- , . JIT-, «» . , , JIT-, , , .



«» , asm.js – JavaScript, , , . , , , JavaScript , . JavaScript, JavaScript, C ++.

, -, IO. . , , , , . «» , .

. , - . , C C++, , . Python, , , . . .

, , , .

, . , . , . , «» , . , C C++, .

, ? , , ? ?

. , – . , - , . , . , -, , . IP , , . , - . , , . , , «» .

, , , , , , . , - , . , , , . , , , , , , .



, . stack canaries, « », , . « » , , , . , , .

让我们绘制一个堆栈图。我们需要确保攻击者在到达寄信人地址之前首先“进入金丝雀”。而且,如果我们可以在从函数返回之前检测到这一点,那么我们可以检测到“邪恶”。

28:30分钟

继续:

麻省理工学院的课程“计算机系统安全”。 第2课:“控制黑客攻击”,第2部分


该课程的完整版本可在此处获得

感谢您与我们在一起。 你喜欢我们的文章吗? 想看更多有趣的资料吗? 通过下订单或将其推荐给您的朋友来支持我们, 为我们为您发明的入门级服务器的独特模拟,为Habr用户提供30%的折扣: 关于VPS(KVM)E5-2650 v4(6核)的全部真相10GB DDR4 240GB SSD 1Gbps从$ 20还是如何划分服务器? (RAID1和RAID10提供选件,最多24个内核和最大40GB DDR4)。

戴尔R730xd便宜2倍?在荷兰和美国,我们有2台Intel Dodeca-Core Xeon E5-2650v4 128GB DDR4 6x480GB SSD 1Gbps 100电视(249美元起) 阅读有关如何构建基础架构大厦的信息。 使用价格为9000欧元的Dell R730xd E5-2650 v4服务器的上等课程?

Source: https://habr.com/ru/post/zh-CN414505/


All Articles