如果您的堆栈和堆都是不可执行的,那么您的代码如何运行?

26

我读了一本关于缓冲区溢出的书,它建议处理方法如下:

将堆栈(和堆)设置为不可执行可以为现有程序提供高度保护,以防止许多类型的缓冲区溢出攻击。

但我不理解怎么做 - 如果不在堆或栈上执行,那么执行会发生在哪里?

7个回答

32
如果我正确理解了您的问题,其他答案都没有回答到它。答案是执行发生在代码段中,既不是堆也不是栈。在典型的分页内存系统中,程序文件(例如Windows中的.exe)中的代码被加载到可执行但只读的页面中。为进程分配附加的可写(和可执行)页面用于堆栈和堆。这里的建议是操作系统和硬件应该合作将那些页面标记为可写但不可执行(rgngl的答案解释了如何在Windows中实现这一点)。
即使使用非可执行堆栈和堆仍然可能存在利用返回导向编程的漏洞,如Alexey Frunze的答案中所提到的,但是还有保护技术可以阻止甚至这些漏洞,例如堆栈破坏保护和地址空间布局随机化——见http://en.wikipedia.org/wiki/Return-to-libc_attack#Protection_from_return-to-libc_attacks

1
很好的回答。然而,有一个小注释——(盲)ROP实际上可以打败ASLR。http://ieeexplore.ieee.org/xpl/login.jsp?tp=&arnumber=6956567&url=http%3A%2F%2Fieeexplore.ieee.org%2Fxpls%2Fabs_all.jsp%3Farnumber%3D6956567 - Hack-R

11

有一种被称为“返回导向编程”(ROP)的漏洞利用技术。

攻击者找到了如何将他的恶意代码组合成正在被攻击的程序的不同部分的方法。

攻击者查找可以在返回指令字节之前执行对寄存器或内存执行有用操作,例如将值移动到位置、添加值、比较值等的可用字节序列(指令)。这些是构建漏洞利用的微型子例程。

然后,通过利用代码错误,攻击者迫使程序开始执行这些微小子例程链,从而完成所有恶意工作。

因此,现在优秀代码变成了恶意代码。没有任何东西在堆栈或堆中执行。

还值得注意的是,在指令跨越多个字节且长度可变的CPU上,即使是指令的立即操作数(换句话说,数值常量)也可以成为代码,因此在那里找到可用的字节序列的机会更高。

通常还可以构造恶意代码来改变内存保护,使漏洞利用不再受现有应用程序代码的限制。


4
非常具体的例子:安排返回地址指向 system,并将栈上的下一个槽位(或按寄存器传递的结构中的第一个参数寄存器)设置为指向字符串 "/bin/sh" 的指针。

但是在这种情况下,您不也需要访问控制台吗? - Alexey Frunze
1
不是的。我假设进程的标准输入(stdin)是一个套接字(socket)或终端(terminal),但如果不是,只需将 "/bin/sh" 更改为 "/bin/sh 0<&n",其中 n 是您连接的服务器套接字的文件描述符号码。 - R.. GitHub STOP HELPING ICE
1
当然,您也可以将其更改为执行任何您想要的操作,而无需交互式 shell,例如 "echo 'toor::0:0::/:/bin/sh' >> /etc/passwd" - R.. GitHub STOP HELPING ICE

1

您的代码运行在文本段中,而不是堆栈或堆中(两者都用于数据存储)。因此,组织方式是:

<highest addresses>
stack
...
heap
data section (initialized data & bss - uninitialized data) 
code section (text)
<lowest addresses> 

代码段是可执行但不可变的。这篇维基百科文章有更多细节: https://en.wikipedia.org/wiki/Data_segment

1

1
理论上,你可以自己做到,这本质上只是一堆对mprotect(2)的调用。 - tbert
没问题 :) 只要记住最重要的事情是确保内存不同时可写和可执行。 - tbert

0

你可以跳转到任何可执行段并运行你的恶意代码...

毕竟,任何存储器上的数据都是比特,而比特可以成为指令传递给CPU执行。


这个问题似乎是关于除程序代码以外的所有内容都在非可执行内存中的情况。那么你如何将代码加载到可执行内存中呢?跳转到它可能很容易。 - Alexey Frunze

0

你可以利用溢出来覆盖函数的返回地址,从而跳转到一些已知的带有你代码的地址。但是操作系统的开发者们采取了随机化地址执行代码的方式来应对这种攻击...


首先,如果除了程序代码以外的所有内容都在非可执行内存中,那么你如何将代码加载到可执行位置?仅靠地址空间随机化并不能防止基于ROP的攻击。请参考我的答案。 - Alexey Frunze

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