Stack Overflow
什么是栈溢出
栈溢出是一种漏洞,当程序向栈写入的数据超过其分配的空间时发生。这些多余的数据将覆盖相邻的内存空间,导致有效数据的损坏,控制流的中断,以及潜在地执行恶意代码。这个问题通常是由于使用不执行输入边界检查的不安全函数而引起的。
这种覆盖的主要问题在于保存的指令指针(EIP/RIP)和保存的基指针(EBP/RBP)用于返回到前一个函数的位置被存储在栈上。因此,攻击者可以覆盖这些内容并控制程序的执行流程。
这种漏洞通常是因为一个函数在栈内复制的字节数超过了为其分配的数量,因此能够覆盖栈的其他部分。
一些常见的容易受到影响的函数包括:strcpy
、strcat
、sprintf
、gets
... 同样,像**fgets
、read
和memcpy
这样带有长度参数**的函数,如果指定的长度大于分配的长度,可能会以易受攻击的方式使用。
例如,以下函数可能存在漏洞:
寻找栈溢出偏移量
发现栈溢出最常见的方法是输入大量的 A
(例如 python3 -c 'print("A"*1000)')并期望出现
Segmentation Fault,表明尝试访问地址
0x41414141`。
此外,一旦发现存在栈溢出漏洞,就需要找到偏移量,直到可以覆盖返回地址,通常会使用De Bruijn序列。对于大小为 k 的字母表和长度为 n 的子序列,De Bruijn序列是一个循环序列,其中长度为 n 的每个可能子序列都恰好出现一次**作为一个连续的子序列。
这样,不需要手动找出控制 EIP 所需的偏移量,可以使用其中一个这些序列作为填充,然后找到最终覆盖它的字节的偏移量。
可以使用pwntools来实现这一点:
或者 GEF:
利用栈溢出
在溢出时(假设溢出大小足够大),您将能够覆盖栈内部局部变量的值,直到达到保存的EBP/RBP和EIP/RIP(甚至更多)。 滥用这种类型的漏洞最常见的方式是通过修改返回地址,因此当函数结束时,控制流将被重定向到用户在指针中指定的位置。
然而,在其他情况下,也许只是覆盖栈中一些变量的值就足以进行利用(就像在简单的CTF挑战中)。
Ret2win
在这种类型的CTF挑战中,二进制文件中有一个从未被调用的函数,您需要调用该函数才能获胜。对于这些挑战,您只需要找到覆盖返回地址的偏移量,并找到要调用的函数的地址(通常会禁用ASLR),因此当易受攻击的函数返回时,隐藏函数将被调用:
栈内代码
在这种情况下,攻击者可以在栈中放置一个shellcode,并滥用受控的EIP/RIP跳转到shellcode并执行任意代码:
ROP和Ret2...技术
这种技术是绕过前一技术的主要保护**不可执行栈(NX)**的基本框架。它允许执行几种其他技术(ret2lib、ret2syscall...),通过滥用二进制文件中现有指令最终执行任意命令:
堆溢出
溢出并不总是发生在栈中,它也可能发生在堆中,例如:
保护类型
有几种保护机制试图防止利用漏洞,可以在以下位置查看它们:
最后更新于