如何向外行解释缓冲区溢出

信息安全 缓冲区溢出
2021-08-18 11:35:25

每隔一段时间(当我大声思考而人们无意中听到我的声音时)我不得不解释什么是缓冲区溢出。因为我真的想不出一个好的比喻,我最终花了大约 10 分钟来解释(易受攻击的)程序如何工作和内存分配,然后用大约 2 句话来说明实际的漏洞利用(“所以缓冲区溢出会填满缓冲区废话并覆盖指针以指向我希望它指向的任何内容”)。到这个时候,大多数人已经自杀了……有什么好的方法可以向外行解释缓冲区溢出?如果可能的话,请包含一个“溢出”组件,但至少还要说明为什么这意味着攻击者可以获得他想要的东西。请记住,平均(和低于平均水平)智力的人应该能够理解我在说什么,

PS,一个相关的问题用技术术语解释缓冲区溢出的作用:什么是缓冲区溢出?

4个回答

想象一下,你有一张你欠债的人的名单。

姓名 |  欠款

此外,当您覆盖某些内容时,它会替换之前的内容,而不是覆盖它的顶部。(这个类比在这里有点不成立,因为你的笔在现实生活中不是这样工作的,但是计算机内存可以)

你向某人支付了 500 美元的 500 美元押金购买一辆 5000 美元的汽车,所以你现在欠他们 4500 美元。他们告诉你他们的名字是约翰史密斯。您在表格中写下金额 (4500) 和姓名 (John Smith)。您的表格现在看起来像这样:

约翰·史密斯 |  4500

稍后您的餐桌会提醒您还款。您支付 4500 美元(加上利息)并将其从表中删除,所以现在您的表又是空白的。

然后你从别人那里得到 1000 美元的贷款。他们告诉你他们的名字是“John Smithxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx9999999999”。您在表中写下金额 (1000) 和名称 (John Smithxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx9999999999)。您的表格现在看起来像这样:

约翰·史密斯xxxxxxxxxxxxxxxxxxxxxxxx|x99999999990

(1000 中的最后一个 0 没有被覆盖。这不重要。)

写名字的时候,一直到“名字”一栏的末尾才停下来,一直往“欠款”一栏写!这是缓冲区溢出。

稍后,您的餐桌提醒您欠 John Smithxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 99999999990 美元。你再次找到他并支付他近 1000 亿美元。

使用比给定更多空间的想法,因此溢出到不同的领域很简单,可以想象。但可能尚不清楚这如何导致坏人运行自己的代码。

如果你理解得足够好,这很容易解释。只要确保你击中了重要的背景。或多或少按此顺序:

  • “堆栈”是您可以存储临时信息的地方。“堆栈指针”确定堆栈的末尾在哪里。当一个函数运行时,它移动堆栈指针以给自己内存以供使用,当它完成时,它将指针移回它找到它的位置。

  • 堆栈向后增长。因此,要在堆栈上给自己 100 个字节,请从堆栈指针中减去100,而不是添加它。如果前一个函数的堆栈从 1000 开始并且我想要 100 个字节,那么我的堆栈从 900 开始。

  • 这意味着,如果您使用的空间比给自己的空间多,您将不会继续写入空白空间,您实际上会开始覆盖以前的堆栈值。

  • 当我的函数启动时,前一个函数为我留在堆栈上的最高值是函数完成后我应该去的返回地址。

  • 这意味着如果我的函数超出了它的堆栈,它首先要覆盖的是返回地址。如果攻击者小心他用什么填充堆栈,他可以指定他想要的任何返回地址。

  • 当我的函数存在时,该返回地址处的任何代码都是接下来将执行的代码。

简单示例

Smashing the Stack for Fun and Profit中,最初描述了这种技术,介绍了最简单直接的技术。想象一下这个函数读取你的名字然后返回。所以你的堆栈看起来像这样:

Stack Pointer                                      Prev. Stack Ptr
+----------------------------------+--------------+................
| Your Name Here                   | Return Addr  |  Old stack ...
+----------------------------------+--------------+................

但是这个坏人的名字足够长,以至于溢出了空间。不仅如此,他没有输入真实姓名,而是输入了一些邪恶代码、一些填充物和那个邪恶代码的地址。

+----------------------------------+--------------+................
| [ Evil Code ]xxxxxxxxxxxxxxxxxxxxxxEvil Address |  Old stack ...
+----------------------------------+--------------+................
  ▲──────────────────────────────────┘

现在不是返回到前一个调用者,而是直接跳转到[Evil Code]. 现在你正在运行他的代码而不是你的程序。从那里它几乎是游戏结束。

缓解和其他技术

用于降低堆栈粉碎效率的两种技术是 DEP 和 ASLR。

DEP(“数据执行保护”)通过将堆栈标记为不可执行来工作。这意味着[Evil Code]堆栈上的 不会运行,因为不再允许在堆栈上运行代码。为了解决这个问题,攻击者会找到大量现有代码,这些代码将执行他想要的零碎工作。而不是仅仅覆盖他自己的返回地址,他通过堆栈为他想要依次运行的所有函数创建一个返回地址链。他们称之为“面向返回的编程”或 ROP。返回链称为“ROP 链”。这真的很难做到。但是有一些工具可以提供帮助。

ASLR(“地址空间布局随机化”)通过随机化所有有趣函数的位置来工作。现在创建 ROP 链并不容易——每次程序运行时,所有地址都在不同的位置。所以当攻击者去用自己的邪恶地址覆盖返回地址时,他将不知道使用什么数字,因为代码总是在不同的地方。

DEP 和 ASLR 本身都不能提供太多保护,但两者一起使成功利用变得非常困难。虽然有时存在一些规避措施,但没有一种解决方法可以在任何地方都有效。如果你能绕过 DEP+ASLR,那就是一次性的成功。

其他答案仍然非常技术性,所以我提供这个。

假设你有一个幼儿园班。每个学生都有放鞋的小房间。每个小房间都有一只鞋子。因此,您为每个学生提供两个小房间。

每个学生被分配两个相邻的小房间。然后老师随机叫学生把他们的鞋子放到他们被分配到的小房间里。

当老师打电话给坏比利时,坏比利想惹愚蠢的莎莉比利的小房间是数字56,而莎莉的小房间是数字78比利把他的节目放进去56然后超出了他定义的限制,把一只黏糊糊的蟾蜍放进了莎莉的小房间里7

由于老师没有对相邻顺序中使用小房间的定义限制执行任何保护,比利能够超出他的限制并弄乱莎莉的存储空间。现在,当莎莉去拿她的鞋子时,她会得到一只黏糊糊的蟾蜍,而不是恶心!


+-------------------+--------------------+-------------------+--------------------+
|      CUBBY 5      |       CUBBY 6      |      CUBBY 7      |       CUBBY 8      |
+-------------------+--------------------+-------------------+--------------------+
|                   |                    |                   |                    |
| Billy's Left Shoe | Billy's Right Shoe | Sally's Left Shoe | Sally's Right Shoe |
+-------------------+--------------------+-------------------+--------------------+

比利在定义的地方输入了三个项目,他应该只放入 2,这就是堆栈溢出在高级别上的工作方式,有人在弄乱他们未经授权的存储,然后当该存储被读取时,它不是你原来的样子期待。

+-------------------+--------------------+------------+--------------------+
|      CUBBY 5      |       CUBBY 6      |   CUBBY 7  |       CUBBY 8      |
+-------------------+--------------------+------------+--------------------+
|                   |                    |            |                    |
| Billy's Left Shoe | Billy's Right Shoe | Slimy Toad | Sally's Right Shoe |
+-------------------+--------------------+------------+--------------------+

如果教师更加注意并确保每个学生只使用预期的存储量,则可以防止缓冲区溢出。

我会在不使用任何类比的情况下尝试这个。

计算机基本上都是内存,这是重要的部分,内存的内容是指令,它告诉计算机要做什么,以及数据,指令利用并可以使用或修改。通常需要存储具有可变长度的数据。例如,如果程序必须跟踪某人的电子邮件地址,该地址可能非常短 (a@b.com) 或非常长 (first-name.middle-name.last-name@mycustomdomainname.co.uk)。有些程序不能很好地跟踪其数据记录的最大长度。因此,如果一个程序被设计为一个电子邮件地址最多包含 100 个字符,并且有人给了它一个超过 100 个字符的电子邮件地址,那么程序将继续在内存中写入地址的其余部分,直到其结束。 -分配的空间。

确切地知道该程序如何工作的人可以给它一个非常精心制作的电子邮件地址,该地址很长,并且在它的末尾有特殊字符。这个想法是,当程序将电子邮件地址存储在内存中时,它会盲目地将这些特殊字符写入程序认为其他部分所在的内存的一部分,然后当它开始运行这些部分时,它会改为运行任何用计算机代码对翻译成的特殊字符进行编程。这样一来,只要仔细制作他们提供给程序的数据,就可以让计算机运行他们想要的任何东西。