目录
什么是栈溢出
堆栈溢出是一种漏洞,当程序向堆栈写入的数据多于分配给堆栈的数据时,就会发生这种情况。这些多余的数据将覆盖相邻的内存空间,从而导致有效数据损坏、控制流中断,甚至可能执行恶意代码。此问题通常是由于使用不对输入执行边界检查的不安全函数而引起的。
这种覆盖的主要问题是,保存的指令指针(EIP/RIP)和返回上一个函数的保存的基指针(EBP/RBP)存储在堆栈中。因此,攻击者将能够覆盖它们并控制程序的执行流程。
该漏洞通常是因为函数在堆栈内复制的字节数多于为其分配的数量,因此能够覆盖堆栈的其他部分。
一些常见的易受此影响的函数包括:strcpy
,,, ...此外,如果指定的长度大于分配的长度,则接受长度参数的函数(如strcat
, &)可能会以易受攻击的sprintf
方式使用。gets
fgets
readmemcpy
例如,以下功能可能存在漏洞:
void vulnerable() {
char buffer[128];
printf("Enter some text: ");
gets(buffer); // This is where the vulnerability lies
printf("You entered: %s\n", buffer);
}
查找 Stack Overflow 偏移量
查找堆栈溢出的最常见方式是给出一个非常大的输入A
s (例如python3 -c 'print("A"*1000)'
),并期望 表示试图访问Segmentation Fault
该地址。0x41414141
此外,一旦发现存在 Stack Overflow 漏洞,你将需要找到偏移量,直到可以覆盖返回地址,为此通常使用De Bruijn 序列。对于给定的大小为k的字母表和长度为n的子序列,它是一个循环序列,其中长度为 _n _** 的每个可能子序列都作为连续子序列出现一次**。
这样,就不需要手动确定需要哪个偏移量来控制 EIP,而是可以使用其中一个序列作为填充,然后找到结束覆盖它的字节的偏移量。
可以使用pwntools来实现这一点:
from pwn import *
# Generate a De Bruijn sequence of length 1000 with an alphabet size of 256 (byte values)
pattern = cyclic(1000)
# This is an example value that you'd have found in the EIP/IP register upon crash
eip_value = p32(0x6161616c)
offset = cyclic_find(eip_value) # Finds the offset of the sequence in the De Bruijn pattern
print(f"The offset is: {offset}")
或者GEF :
#Patterns
pattern create 200 #Generate length 200 pattern
pattern search "avaaawaa" #Search for the offset of that substring
pattern search $rsp #Search the offset given the content of $rsp
Stack Overflow运用
在溢出期间(假设溢出大小足够大),您将能够覆盖堆栈内的局部变量的值,直到到达保存的EBP/RBP 和 EIP/RIP(甚至更多)。滥用此类漏洞的最常见方式是修改返回地址,因此当函数结束时,控制流将被重定向到用户在此指针中指定的任何地方。
然而,在其他情况下,可能只需覆盖堆栈中的某些变量值就足以进行利用(例如在简单的 CTF 挑战中)。
Ret2win
在这种类型的 CTF 挑战中,二进制文件中有一个从未被调用的函数,你需要调用该函数才能获胜。对于这些挑战,你只需要找到偏移量来覆盖返回地址并找到要调用的函数的地址(通常会禁用 ASLR),因此当易受攻击的函数返回时,将调用隐藏函数。
Shellcode
在这种情况下,攻击者可以在堆栈中放置一个 shellcode,并滥用受控的 EIP/RIP 跳转到 shellcode 并执行任意代码。
ROP & Ret2...
该技术是绕过上一个技术的主要保护措施的基本框架:无可执行堆栈 (NX)。它还允许执行其他几种技术(ret2lib、ret2syscall……),这些技术将通过滥用二进制文件中的现有指令来终止执行任意命令。
防御方式
启用Core Files
核心文件是进程崩溃时操作系统生成的一种文件。这些文件捕获崩溃进程终止时的内存映像,包括进程的内存、寄存器和程序计数器状态等详细信息。此快照对于调试和了解崩溃原因非常有用。
启用核心转储生成
默认情况下,许多系统将核心文件的大小限制为 0(即不生成核心文件)以节省磁盘空间。要启用核心文件的生成,你可以使用命令ulimit
(在 bash 或类似的 shell 中)或配置系统范围的设置。
- 使用 ulimit:该命令
ulimit -c unlimited
允许当前 shell 会话创建无限大小的核心文件。这对于调试会话很有用,但在重新启动或新会话后不会持久。
ulimit -c unlimited
- 持久配置:对于更永久的解决方案,你可以编辑
/etc/security/limits.conf
文件以包含类似的行* soft core unlimited
,这允许所有用户生成无限大小的核心文件,而不必在其会话中手动设置 ulimit。
* soft core unlimited
使用 GDB 分析核心文件
要分析核心文件,您可以使用 GDB(GNU 调试器)等调试工具。假设你有一个生成核心转储的可执行文件,并且核心文件名为core_file
,你可以使用以下命令开始分析:
gdb /path/to/executable /path/to/core_file
此命令将可执行文件和核心文件加载到 GDB 中,让您可以检查崩溃时程序的状态。您可以使用 GDB 命令探索堆栈、检查变量并了解崩溃的原因。