`

Web安全读书笔记9- 缓冲区溢出漏洞

阅读更多
栈溢出
bool CheckLogin(char *username,char * password)
{
char _username[32];
strcpy(_username,username);
}
字符串username超过32个字符,_username缓冲区溢出,攻击者可以覆盖栈上已经保存的返回地址,从而可以执行任意代码

堆溢出
bool CheckLogin(char *username,char * password)
{
char * _username=(char *)malloc(32);
strcpy(_username,username);
}
堆缓冲区旁边是临近其他堆块的控制结构,堆以双向链表的形式存在
可能精心设置的值能够覆写这个双向链表的指针而指向自己所需要的值

一位偏移漏洞

FP帧指针指向帧头  
  SP栈指针指向栈顶  
  大部分现代计算机系统使用栈来给进程传递参数并且存储局部变量。栈是一种在进程映象内存的高地址内的后进先出(LIFO)的缓冲区。当程序调用一个函数时一个新的“栈帧”会被创建。这个栈帧包含着传递给函数的各种参数和一些动态的局部变量空间。“栈指针”记录着当前   栈顶的位置。由于栈指针的值会因为新变量的压入栈顶而经常的变化,许多实现也提供了一种"帧指针"来定位在栈帧的起始位置,以便局部变量可以更容易的被访问。  
  简单的说就是帧指针可用于子例程参数传输,而栈指针则可用于存储子程序调用的返回地址。
 
图3-4  栈中帧结构示意图
对于函数A调用函数B的情况,传递给B的参数包含在A的栈帧中。当A调用B时,函数A的返回地址(调用返回后继续执行的指令地址)被压入栈中,栈中该位置也明确指明了A栈帧的结束处。而B的栈帧则从随后的栈部分开始,即图中保存帧指针(ebp)的地方开始。再随后则用于存放任何保存的寄存器值以及函数的临时值
作为一个例子,我们来观察下面C程序exch.c中函数调用的处理过程。该程序交换两个变量中的值,并返回它们的差值。
1 void swap(int * a, int *b)
2 {
3int c;
4c = *a; *a = *b; *b = c;
5 }
6
7 int main()
8 {
9int a, b;
10a = 16; b = 32;
11swap(&a, &b);
12return (a - b);
13 }
其中函数swap()用于交换两个变量的值。C程序中的主程序main()也是一个函数(将在下面说明),它在调用了swap()之后返回交换后的结果。这两个函数的栈帧结构如图3-5所示。可以看出,函数swap()从调用者main()的栈帧中获取其参数。图中的位置信息相对于寄存器ebp中的帧指针。栈帧左边的数字指出了相对于帧指针的地址偏移值(汇编时候计算地址方便,只要ebp加减偏移量就可以求得变量地址)。在像gdb这样的调试器中,这些数值都用2的补码表示。例如,-4被表示成0xFFFFFFFC,-12会被表示成0xFFFFFFF4。
调用者main()的栈帧结构中包括局部变量a和b的存储空间,相对于帧指针位于-4和-8偏移处。由于我们需要为这两个局部变量生成地址,因此它们必须保存在栈中而非简单地存放在寄存器中。(重要)
 

图3-5  调用函数main和swap的栈帧结构

bool CheckLogin(char *username,char * password)
{
char   _username[32];
int i;
for(int i=0;i<32&&username[i];i++)
{
_username[i]= username[i];
}
_username[i]=0;
}
这段代码复制32B,然后增加终止符。因此,如果用户名为32B或者更长,空字节就会写在缓冲区之外,污染临近区域。
这时候,如果栈上临近区域是调用桢的已保存桢指针(即上图的保存的ebp),那么最后一个溢出的0将导致它指向_username缓冲区(而不是main的ebp的地方,如上图),因而指向攻击者控制的数据,调用函数返回时,保存的ebp被加载到桢指针寄存器ebp上(这个保存的ebp为0),攻击者就可以控制执行流程。


整数漏洞 整数溢出与符号错误
如果应用程序在执行某种缓冲区操作前对一个长度值运用某种算法,却没有考虑整数的特点,可能导致整数方面的漏洞
整数溢出
bool CheckLogin(char *username,char* password)
{
unsigned short len=strlen(username)+1;
char *_username=(char *)malloc(len);
strcpy(_username,username);
}
这段代码应用程序求出用户提交的用户名长度,分配空间+一字节空字节,分配缓冲区。
但是,如果用户提交一个65535个字符的用户名,就会造成整数溢出。unsigned short的范围0-65535,如果再增加1个长度,就会回绕而变为0。于是应用程序分配长度为0的缓冲区,把用户名复制进去,造成堆溢出。

符号错误
bool CheckLogin(char *username,int len,char *password)
{
char _username[32]=’”;
if(len<32)
strncpy(_username,username,len);
}
如果len为负数,那么仍然可以和32比较,执行strncpy的代码,但是strncpy得len只接受无符号数,所以会将len转换为无符号的类型,因而负值被当做一个大的整数处理。如果用户提交的用户名大于32,那么缓冲区就会溢出。
这种攻击需要长度参数由攻击者控制。

格式化字符串漏洞
%n 说明符

int count=43;
int writtern=0;
printf(“The value of count is %d%n.\n”,count,&written);
printf(“%d bytes were printed.\n”,written);
输出
The value of count is 43.
24 bytes were printed.
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics