缓冲区溢出如何用于攻击计算机?

19

缓冲区溢出如何被用来攻击计算机?

通过引起溢出,一个人如何能够执行任意代码?

我理解程序内存的一部分被覆盖了,但我不明白这怎么会导致一个人执行自己的代码。此外,第三方恶意代码必须是用目标处理器的汇编语言编写的吗?


@ Cody Brocious,感谢您的编辑。 - anon
5个回答

16

3
请注意,堆栈溢出也可能被利用(例如通过在堆栈上覆盖返回地址)。参见例如https://dev59.com/YkXRa4cB1Zd3GeqPucM_。 - sleske
堆栈溢出是绝对可利用的。在Windows2000 /早期XP时代,绝大多数漏洞都是由堆栈溢出引起的,在IE6中也有许多由堆栈溢出引起的已经修补的漏洞。 - SecurityMatt

8

当你跳入一个子程序时,栈中包含数据和返回地址。如果你设法在栈上放置一个特定的地址作为返回地址,就可以强制CPU跳转到一个特定的内存位置,也就是你自己代码所在的位置。这就是缓冲区溢出攻击的原理。堆溢出攻击略有不同,更难利用。

栈溢出只是表明你已经用完了栈空间(通常在内核中更为有限)。


然而,任意代码如何加载到应用程序的地址空间中呢?如果我还记得我的机器架构课程,CPU被设计为不在数据段执行任何操作,以避免此类安全问题。 - Cybis
实际上,确保数据不被执行是操作系统的工作,但可能存在可以利用的漏洞。或者你可以只运行程序的原始代码,但在错误的顺序中,也许只会使其陷入无限循环,以至于不能完成本应该完成的工作。 - Artelius

7
想象一条街上有两栋房子。一栋是你朋友的房子,另一栋是他邪恶多疑的三个门外的邻居的房子。这位邪恶多疑的邻居从来不进出,他的房子紧锁着。
现在,你的朋友是一个好朋友,非常信任你,他会让你把任何东西都存放在他的房子里,从一面墙开始一个接一个地放置盒子。事实上,他是如此好的朋友,他会继续放下盒子,而不检查是否撞到了墙,直到它们在半空中继续前进,最终穿过街道上的另外两栋房子,进入了那个邪恶多疑的邻居的房子。但你的朋友相信你不会这样做,因为他喜欢你(而且他有点天真)。
所以,你可以通过利用你的好朋友的信任将某些东西放入那个邪恶多疑的邻居的房子中。
替换以下术语,你就会看到与缓冲区溢出攻击的类比:
  • "你朋友的房子" --> "程序中不检查缓冲区溢出的一部分"
  • "邪恶多疑的邻居的房子" --> "另一个被认为是安全的程序部分"
  • "盒子" --> "程序中不检查缓冲区溢出的参数/变量"
只有当有人找出安全内存区域的位置,以及必须将什么作为程序的参数传递,才能最终进入安全区域,从而达到期望的效果(无论是数据还是代码,都可以导致攻击者的代码执行)。

5

现代处理器在调用子程序时,几乎都会将返回地址推送到与本地数据(堆栈)相同的区域。对于不检查变量上限的例程(特别是strcpy函数),指令地址重定向(缓冲区溢出)可能会发生。

void make(char *me)
{
    char sandwich[4]; // local data, this is in stack.  the buffer for data is too small
    strcpy(sandwich, me);
    puts(sandwich);

    // implicit "return;" the return instruction(RET on Intel) instructs the processor to implicitly pop an address from stack then resume execution on that address
}

void main()
{
    // calling a subroutine (CALL on Intel) implicitly instructs the processor to push the next instruction's address(getchar line) on stack before jumping to make.
    make("Love Not War"); 
    getchar();

    puts("This will not execute.  The address to next instruction(getchar) gets overwritten with Not War");

}

"另外,第三方的恶意代码必须用目标处理器的汇编语言编写吗?" 是的

堆栈溢出可能会发生在正常运行程序时,例如递归例程(调用自身的函数)忽略了终止条件。堆栈区域将填充大量本地变量和返回地址。


1
通常的方法是在内存中有恶意代码。然后创建一个缓冲区溢出:关键在于不仅要使其溢出,而且还要像你已经提到的那样覆盖程序内存的某些部分。由于堆栈不仅包含变量,而且在调用函数时还包含返回地址,因此尝试使用恶意代码的地址来覆盖此地址。当具有缓冲区溢出的函数返回到其调用者时,该函数不会返回到其原始调用者,而是返回到恶意子例程。由于现在执行的代码通常具有调用代码的权限,因此尝试在具有比恶魔代码更高权限的代码中查找/创建这些溢出(否则您可以通过直接调用恶魔例程来完成)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接