- A+
所属分类:笔记
一.缓冲区分类:
- 按照缓冲区所在内存区域位置分类:
- 栈溢出
- 堆溢出
- 数据段溢出
- 按照导致溢出的内存操作函数分类:
- 字符串操作函数(strcpy)
- 格式化输出函数(sprintf)
- 按照溢出数据修改的关键值分类:
- 修改返回地址
- 修改函数指针
- 修改指针变量
二.栈溢出
- 基于栈溢出的攻击Stack Smashing:将函数返回地址指向注入到缓冲区中的恶意代码,当函数执行结束时会跳转到恶意代码处,进而执行代码。
- Return-into-Libc溢出攻击:不将恶意代码注入到缓冲区中,而是重用已有代码,将系统库函数(如system(),exec()等)作为攻击代码。
- Off-by-one溢出:输入内容恰好超出缓冲区一位数据。
代码示例:
void function(char *input)
{
char buffer[128];
for(int i = 0; i <= 128; i++)
buffer[i] = input[i];
}
三.堆溢出
堆是由程序运行时运用 malloc(⋅)和 free(⋅)等函数动态分配、释放的内存块组成,每一个内存块都包含自身内存大小和指向下一个内存块的指针等信息。虽然堆中没有函数返回地址,但是攻击者可以通过修改堆中的函数指针或者指针变量,进而达到修改程序控制流,执行攻击代码的目的。
四.数据段溢出
数据段中存储的是初始化和未初始化的全局/静态变量。
代码示例:
static char buffer[128];
static int(*fptr)(const char *str);
main(char *str)
{
fptr = (int(*)(const char *str));
strcpy(buffer, str);
(void)(*fptr)(buffer);
}
上述程序中,如果str的长度超过buffer容量就会造成溢出,覆盖函数指针fptr,改变程序的执行流程。
五.格式化字符串溢出
格式化字符串溢出[11,22,23]主要由格式化字符函数如 fprintf、sprintf、snprintf、syslog 等引起。典型的如函数 sprintf(char * str、const char * format,......)是将格式化的数据写入 str 所指的数组中,并添加‘\0’,如果格式化的数据长度超出了数组的容量就会溢出。
六.整数溢出
代码示例:
void copy(char *str)
{
char buffer[80];
unsigned short len; // 0 <= len <= 65535
len = strlen(str);
if(len <= 80)
strcpy(buffer, str);
do_sth_with(buffer);
}
该例子中,参数str被拷贝到长度为80的缓冲区中。虽然程序对缓冲区的写入有边界保护,但当str的长度超过短整型的范围时(也即大于65535),它可能会被截取为一个小于80的值赋值给len(例如str长度为65600时,len的值为64),这样边界保护不再有效。
七.缓冲区溢出攻击特征
len:buff | len(input) > len(buffer) | 输入字符串的长度大于缓冲区所能容纳的最大长度 |
---|---|---|
con:addr | contains(input, type(address)) | 输入中包含地址类信息 |
con:inst | contains(input, type(instruction)) | 输入中包含指令信息 |
con:ctrl | contains(input, type(ctrlvar)) | 输入中包含关键变量 |
mod:radd | modify(retnadd) | 修改返回地址 |
mod:fptr | modify(funcptr) | 修改函数指针 |
mod:cvar | modify(ctrlvar) | 修改关键变量 |
mod:cptr | modify(ctrlptr) | 修改关键指针 |
jmp:stack | jumpe(stack) | 程序执行跳转到栈内存区域 |
jmp:hcap | jumpe(hcap) | 程序执行跳转到堆内存区域 |
exe:stack | execute(stack) | 执行存储在栈中的指令 |
exe:heap | execute(heap) | 执行存储在堆中的指令 |
flow:ctrl | flow(ctrlvar) | 程序执行路径因为关键变量而改变 |