4.1 缓冲区溢出漏洞
4.1.1 基本概念
(1)漏洞的基本概念
(2)缓冲区溢出漏洞
-
缓冲区定义:
-
缓冲区是一块连续的内存区域,用于存放程序运行时加载到内存的运行代码和数据。
-
-
缓冲区溢出:
-
缓冲区溢出是指程序运行时,向固定大小的缓冲区写入超过其容量的数据,多余的数据 会越过缓冲区的边界覆盖相邻内存空间,从而造成溢出。
-
缓冲区的大小是由用户输入的数据决定的,如果程序不对用户输入的超长数据作长度检 查,同时用户又对程序进行了非法操作或者错误输入,就会造成缓冲区溢出。
-
(3)缓冲区溢出攻击
-
定义:
-
缓冲区溢出攻击是指发生缓冲区溢出时,溢出的数据会覆盖相邻内存空间的返回地址、 函数指针、堆管理结构等合法数据,从而使程序运行失败、或者发生转向去执行其它程序代 码、或者执行预先注入到内存缓冲区中的代码。
-
-
出现的原因:
-
缺乏类型安全功能的程序设计语言(C、C++等)出于效 率的考虑,部分函数不对数组边界条件和函数指针引用等进行边界检查。
-
例如,C 标准库中 和字符串操作有关的函数,像strcpy、strcat、sprintf、gets等函数中,数组和指针都没 有自动边界检查。
-
-
-
要求:程序员必须进行自我检查
4.1.2 栈溢出漏洞
(1)基本概念
-
栈溢出漏洞,即发生在栈区的溢出漏洞。当被调用的子函数中写入数据的长度,大于栈 帧的基址到esp之间预留的保存局部变量的空间时,就会发生栈的溢出。要写入数据的填充 方向是从低地址向高地址增长,多余的数据就会越过栈帧的基址,覆盖基址以上的地址空间。
(2)栈溢出漏洞示例
-
修改返回地址
主函数调用函数f,并没有调用why_here,但是运行结果出现了字符串
#include <stdio.h>;
#include <stdlib.h>;
//Have we invoked this function?
void why_here(void)
{
printf("why u r here?!\n");
exit(0);
}
void f()
{
int buff[1];
buff[2] = (int)why_here;
}
int main(int argc, char * argv[])
{
f();
return 0;
}
2. 覆盖临界变量
4.1.3 堆溢出漏洞
(1)基本概念
-
堆溢出是指在堆中发生的缓冲区溢出。堆溢出后,数据可以覆盖堆区的不同堆块的数据, 带来安全威胁。实验一:==使用OllyDBG运行示例4-1生成的可执行程序,观察栈帧变化情况,特别是返 回地址的数值在调用函数f之前和调用f时候的变化,进一步掌握OllyDBG的使用。
(2)堆溢出漏洞利用
-
堆溢出的实现难度更大,而且往往要求进程在内存中具备特定的组织结 构。然而,堆溢出攻击也已经成为缓冲区溢出攻击的主要方式之一。堆溢出带来的威胁远远 不止上面示例演示的那样,结合堆管理结构,堆溢出漏洞可以在任意位置写入任意数据。
Dword Shoot 攻击:通过堆溢出覆写了一个空闲堆块的块首的前向指针 flink 和后向指针 blink,我们可以精心构造一个地址和一个数据,当这个空闲堆块从链表里卸下 的时候,就获得一次向内存构造的任意地址写入一个任意数据的机会。这种能够向内存任意 位置写入任意数据的机会称为“Arbitrary Dword Reset”(又称 Dword Shoot)。具体如下 图所示。

4.1.4 SEH 结构覆盖
异常处理结构体:是 Windows 异常处理机制所采 用的重要数据结构。
-
SEH结构体存放在栈中,栈中的多个SEH通过链表指针在栈内由栈顶向栈底串成单向链表,
-
位于链表最顶端的SEH通过线程环境块(TEB,Thread Environment Block) 0 字节偏移处的指针标识。
-
每个SEH包含两个DWORD指针:SEH链表指针和异常处理函数句柄, 共8个字节。SEH链表结构图如下图所示。

SEH的作用:
-
线程初始化的时候,自动向栈中安装一个SEH,作为线程默认的异常处理。如果程序 源代码中使用了_try{}_except{}或者Assert宏等异常处理机制,编译器将最终通过向当前函数栈帧中安装一个SEH来实现异常处理。
-
当异常发生时,操作系统会中断程序,并首先从TEB的0字节偏移处取出距离栈顶 最近的SEH,使用异常处理函数句柄所指向的代码来处理异常。当最近的异常处理函 数运行失败时,将顺着SEH链表依次尝试其他的异常处理函数。
-
如果程序安装的所有异常处理函数都不能处理这个异常,系统会调用默认的系统处 理程序,通常显示一个对话框,你可以选择关闭或者最后将其附加到调试器上的调 试按钮。如果没有调试器能被附加于其上或者调试器也处理不了,系统就调用 ExitProcess终结程序。
SEH攻击:
-
通过栈溢出或者其他漏洞,使用精心构造的数据覆盖SEH链表的入口地址、 异常处理函数句柄或链表指针等,实现程序执行流程的控制。
-
因为发生异常的时候,程序会 基于SEH链表转去执行一个预先设定的回调函数,攻击者可以利用这个结构进行漏洞利用攻 击。
-
由于SEH存放在栈中,利用缓冲区溢出可以覆盖SEH;如果精心设计溢出数据,则有可能把SEH中异常处理函数的入口地址更改为恶意程序的入口地址,实现进程的控制。
4.1.5 单字节溢出
单字节溢出是指程序中的缓冲区仅能溢出一个字节。单字节溢出的原理通过下面的样例进行分析。
void single_func(char *src)
{
char buf[256];
int i;
for(i = 0;i <= 256;i++)
buf[i] = src[i];
}
-
缓冲区溢出一般是通过覆盖堆栈中的返回地址,使程序跳转到shellcode或指定程序处 执行。
-
然而在一定条件下,当缓冲区只溢出一个字节时,单字节溢出也是可以利用的,但实 际上利用难度较大。
-
因为它溢出的一个字节必须与栈帧指针紧挨,就是要求必须是函数中首 个变量,一般这种情况很难出现。尽管如此,程序员也应该对这种情况引起重视,因为毕竟 可能造成程序的异常。
4.2 格式化字符串漏洞
1. 格式化字符串的定义
-
printf()函数的一般形式为:printf("format", 输出表列)
format 的结构为:\%标志.精度类型,其中类型有以下常见的几种:
-
%d整型输出
-
%ld长整型输出
-
%o以八进制数形式输出整数
-
%x以十六进制数形式输出整数
-
%u以十进制数输出unsigned型数据(无符号数)
-
%c用来输出一个字符
-
%s用来输出一个字符串
-
%f用来输出实数,以小数形式输出。
-
2. 格式化字符串漏洞的利用-数据泄露
特性一:格式化函数允许可变函数
-
C 语言中的格式化函数(*printf 族函数,包括 printf,fprintf,sprintf,snprintf 等)允许可变参数,它根据传入的格式化字符串获知可变参数的个数和类型,并依据格式化 符号进行参数的输出。
-
调用这些函数时,如果给出了格式化符号串,但没有提供实际对应参数时,这些函数会 将格式化字符串后面的多个栈中的内容弹出作为参数,并根据格式化符号将其输出。当格式 化符号为%x时以16进制的形式输出堆栈的内容,为%s时则输出对应地址所指向的字符串。 利用这个特点,通过提供过多的%x等格式化符号,就可以获得内存中的数据。
void formatstring_func1(char *buf) { char mark[] = “ABCD”; printf(buf); }调用时如果传入”%x%x…%x”,则 printf 会打印出堆栈中的内容,不断增加%x 的个数 会逐渐显示堆栈中高地址的数据,从而导致堆栈中的数据泄漏。
内存泄露利用漏洞
实验四:完成示例4-6的实验,注意观察栈帧结构状态。
#include <stdio.h> int main(void) { int a=1,b=2,c=3; char buf[]="test"; printf("%s %d %d %d\n",buf,a,b,c); return 0; }我们编译之后运行(Debug模式): test 1 2 3 接下来我们做一下测试,我们增加一个printf()的format参数,改为: ==printf("%s %d %d %d %x\n",buf,a,b,c)==, 编译后运行(Debug模式): test 1 2 3 12C62E
-
利用-任意内存数据获取
#include <stdio.h> int main(int argc, char *argv[]) { char str[200]; fgets(str,200,stdin); printf(str); return 0; }
特性二:利用%n格式符写入数据
数据写入
-
更危险的格式化符号是%n,它的作用是将格式化函数输出字符串的长度,写入函数参数
-
指定的位置。%n不向printf传递格式化信息,而是令printf把自己到该点已打出的字符总数放到相应变元指向的整形变量中,比如 printf(“Jamsa%n”, &first_count)将向整型变量first_count 处写入整数5。
int formatstring_func2(int argc,char *argv[])
{
char buffer[100];
sprintf(buffer,argv[1]);
}
Sprintf 函数的作用是把格式化的数据写入某个字符串缓冲区。函数原型为: int sprintf( char *buffer, const char *format, [ argument] … ); 如果调用这段程序时用”aaaabbbbcc%n”作为命令行参数,则最终数值10就会被写入地 址为0x61616161(aaaa)的内存单元。
特性三:自定义打印字符串宽度
#include <stdio.h>
main()
{
int num=66666666;
printf("Before: num = %d\n", num);
printf("%d%n\n", num, &num);
printf("After: num = %d\n", num);
}
#include <stdio.h>
main()
{
int num=66666666;
printf("Before: num = %d\n", num);
printf("%100d%n\n", num, &num); //%100d表示前面的长度至少为100
printf("After: num = %d\n", num);
printf("%0134512640d%n\n", num, &num);
printf("After: num = %x\n", num);
}

4.3 整数溢出漏洞
-
存储溢出
-
存储溢出是使用另外的数据类型来存储整型数造成的。例如,把一个大的变量放入一个 小变量的存储区域,最终是只能保留小变量能够存储的位,其他的位都无法存储,以至于造 成安全隐患。
-
-
运算溢出
-
运算溢出是对整型变量进行运算时没有考虑到其边界范围,造成运算后的数值范围超出 了其存储空间。
-
-
符号问题
-
运算溢出是对整型变量进行运算时没有考虑到其边界范围,造成运算后的数值范围超出了其存储空间。
-
整数溢出一般不能被单独利用,而是用来绕过目标程序中的条件检测,进而实现其他攻击,正如上面的例子,利用整数溢出引发缓冲区溢出。
4.4 攻击 C++虚函数
-
C++虚函数和类在内存中的位置关系如图所示:
-
虚表指针保存在对象的内存空间中,紧接着虚表指针的是其他成员变量;
-
虚函数入口地址被统一存在虚表中。
-

4.5 其他类型漏洞
4.5.1 注入类漏洞
-
SQL注入:将Web页面的原URL、表单域或数据包输入的参数,修改拼接成SQL语句,传递给Web服务器,进而传给数据库服务器以执行数据库命令。
-
操作系统命令注入:操作系统命令注入攻击(OS Command Injection)是指通过Web应用,执行非法的操作 系统命令达到攻击的目的。
-
web脚本语言注入:常用的ASP/PHP/JSP等web脚本解释语言支持动态执行在运行时生成的代码这种特点, 可以帮助开发者根据各种数据和条件动态修改程序代码,
攻击来自下面两个方面
-
合并了用户提交数据的代码的动态执行,构造设计的输入,使得合并用户提交数据后的代码蕴含设定的非正常业务逻辑。通过代码执行来实施特定攻击
-
根据用户提交的数据指定的代码文件的动态包含。多数脚本语言都支持使用包含文件。
-
文件包含允许开发者把可重复执行的代码插入到单个文件中,在需要的时候将他们包含到相关的代码文件中。
-
如果攻击者能修改这个文件中的代码,就让受攻击者执行攻击者的代码
-
-
-
SOAP注入
-
SOAP:简单对象访问协议,是一个简单的XML的协议,让应用程序跨HTTP进行信息交换。主要出现在web服务中,通过浏览器访问的Web应用程序常常使用SOAP在后端应用程序组件之间进行通信
-
由于XML也是一种解释型语言,因此SOAP也易于遭受代码注入攻击。XML元素通过元字 符<>和/以语法形式表示。如果用户提交的数据中包含这些字符,并被直接插入到SOAP消息 中,攻击者就能够破坏消息的结构,进而破坏应用程序的逻辑或造成其他不利影响。
-
4.5.2 权限类漏洞
-
水平越权:相同级别权限的用户或者同一角色的不同用户之间,可以越权访问、 修改或者删除的非法操作。如果出现此类漏洞,那么将可能会造成大批量数据泄露,严重的 甚至会造成用户信息被恶意篡改。
-
垂直越权:向上越权、向下越权

被折叠的 条评论
为什么被折叠?



