前面发了两篇都是关于C语言缓冲区溢出的文章,有的同行问,是否C#、Java语言有缓冲区溢出问题吗?答案是否定的。由于C#、Java语言需要虚拟机去执行,属于托管语言,虚拟机会自动检查边界。一般不会出现缓冲区溢出。但是通过JNI调用本机代码、以及JVM/CLR/ETC等虚拟机可能出现这些问题。所以缓冲区溢出最突出的语言是C和C++。

下面我们再深一步了解缓冲区溢出的原理。

缓冲区是一块连续的计算机中的内存区域,可保存相同数据类型的多个实例,例如代码段、数据段、堆和栈。栈存放函数变量和实参、堆是提供动态申请内存、静态数据区存放全局或静态数据。C/C++语言中,通常使用字符数组和malloc/new之类内存分配函数申请缓冲区。缓冲区溢出是指数据被添加到分配给该缓冲区的内存块之外。缓冲区溢出是最常见的程序缺陷,严重程序缺陷导致可能被攻击者利用,成为安全漏洞。

按照冯·诺依曼存储程序原理,程序代码是作为二进制数据存储在内存的,同样程序的数据也在内存中,因此直接从内存的二进制形式上是无法区分哪些是数据哪些是代码的,这也为缓冲区溢出攻击提供了可能。 恶意攻击者利用缓冲区溢出攻击的最终目的就是希望系统能执行这块可读写内存中已经被蓄意设定好的恶意代码。

栈帧结构的引入为高级语言中实现函数或过程调用提供直接的硬件支持,但由于将函数返回地址保存在程序员可见的堆栈中,这样给系统安全带来隐患。攻击者可以将函数返回地址修改为指向一段精心安排的恶意代码,则可达到危害系统的目的。此外,堆栈的正确恢复依赖于压栈的EBP值的正确性,但EBP域邻近局部变量,若编程中有意无意地通过局部变量的地址偏移窜改EBP值,则程序的行为将变得非常危险。

栈存取一般是按照向下增长的方式存取的,例如push 0x0021fd67(0x0021fd67是一个变量的值)到栈,按照如图进入栈空间,高字节在高位,低字节在低位,这种方式就是所谓的“大端”方式。为什么栈是这种存取方式呢?其实原因就是当初设计计算机内存如何高效使用时,堆和栈公用相同的空间,最大利用方式是堆从某一个地址增长,而栈从某一个高地址减小,这样向中间增长,保证堆和栈都能先申请先利用,直到最后没有空间(当然这时候发生堆栈溢出,程序会崩溃),来保证最大的内存利用率。

这里补充一下大端和小端的知识。大端/小端就是Big-Endian/Little-Endian问题。大端:高位字节存在高地址上,低位字节存在低地址上
小端:低位字节存在高地址上,高位字节存在低地址上 
有两种常见的方法来判断是大端还是小端,下面代码来自于网络。
方法一:使用指针

int x=1;
if(*(char*)&x==1)
    printf("little-endian\n");
else
    printf("big-endian\n");

方法二:使用联合

union{
    int i;
    char c;
}x;
x.i=1;
if(x.c==1)
    printf("little-endian\n");
else
    printf("big-endian\n");

由于C/C++语言没有数组越界检查机制,当向局部数组缓冲区里写入的数据超过为其分配的大小时,就会发生缓冲区溢出。攻击者可利用缓冲区溢出来窜改进程运行时栈,从而改变程序正常流向,轻则导致程序崩溃,重则系统特权被窃取。

除通过使堆栈缓冲区溢出而更改返回地址外,还可改写局部变量(尤其函数指针)以利用缓冲区溢出缺陷。

缓冲区溢出有常见3种:

(1)激活记录(Activation Records)

这是一种比较常见的溢出方式,每当调用一个函数时,在堆栈中留下一个激活记录,它包含了函数结束时返回的地址。攻击者通过溢出这些自动变量,使这个返回地址指向攻击代码。通过改变程序的返回地址,当函数调用结束时,程序就跳转到攻击者设定的地址,而不是原先的地址。

(2)函数指针(Function Pointers)

在C语言中,void(*foo)()声明了一个返回值为void函数指针的变量foo。函数指针可以用来定位任何地址空间,所以攻击者只需在任何空间内的函数指针附近找到一个能够溢出的缓冲区,然后溢出这个缓冲区来改变函数指针。在某一时刻,当程序通过函数指针调用函数时,程序的流程就按攻击者的意图实现转移。

(3)长跳转缓冲区(Longjmp buffers)

在C语言中包含了一个简单的检验/恢复系统,即setjmp/longjmp,其作用是在检验点设定setjmp(buffer),而用longjmp(buffer)来恢复检验点。但是如果攻击者能够进入缓冲区的空间,longjmp(buffer)实际是跳转到攻击者的代码。和函数指针一样,longjmp缓冲区能够指向任何地方,所以攻击者所要做的就是找到一个可供溢出的缓冲区。

在进行溢出缓冲区攻击时,常常需要在一个字符串里综合了代码植入和激活记录。攻击者需要定位一个可供溢出的自动变量,然后向程序传递一个很大的字符串,在引发缓冲区溢出改变激活记录的同时植入代码。代码植入和缓冲区溢出不一定要在一次动作内完成。攻击者可以在一个缓冲区内放置代码,这时不能溢出缓冲区。然后,攻击者通过溢出另外一个缓冲区来转移程序的指针。这种方法一般用来解决可供溢出的缓冲区不够大的情况。

如果攻击者想使用已经驻留的代码而不是从外部植入代码,就必须先把代码参数化。如在libc中的部分代码段会执行exec(some),其中some就是参数。攻击者使用缓冲区溢出改变程序的参数,再利用另一个缓冲区溢出使程序指针指向libc中特定的代码段

下面举个例子:

// wshell.cpp : 定义控制台应用程序的入口点。

//

#include "stdafx.h"

int fun(void)

{

    int a=10,*p=NULL;

    a=20;

    p=(int*)((char *)&a + 16);

    *p+=16;

    return a;

}

int _tmain(int argc, _TCHAR* argv[])

{

    int b=11;

    b = fun();

    printf("printf aaa\n");

    printf("printf bbb\n");

    return 0;

}

通过VS2012编辑编译、在调试模式下,可以查看到汇编代码如下:

ESP扩展栈指针寄存器,存放的是函数栈顶顶指针。fun函数调用返回地址是0x0021149A,而fun函数执行完成后,返回地址是调用函数指令后跟的指令地址,要跳转到0x002114AA地址,两条指令之间相差16。变量a的地址是0x004bf9b8,fun函数返回地址存放在0x004bf9b8+16=0x004bf9C8中,所以在fun函数中,要取变量a地址,相加16之后,得到返回地址。

由于Vistual  C++编译后会检查指针越界等缺陷,所以会抛出异常,可以忽略掉异常,继续执行,只打你出printf bbb字符串。也就是第一个字符串通过fun函数地址跳转,跳转到第二个打印语句。

如何防范缓冲区溢出呢?

防范缓冲区溢出问题的准则是:

确保对输入数据做边界检查。相对于系统安全,不要担心因为添加检查而影响程序效率。不要为接收数据预留相对过小的缓冲区,大量数据处理应通过malloc/new分配堆空间来解决;

在将数据读入或复制到目标缓冲区前,检查数据长度是否超过缓冲区空间。

检查以确保不会将过大的数据传递给别的程序,尤其是第三方商用软件库——不要设想关于其他人软件行为的任何事情。因为很多商用库也是程序员编写的,早期对于风险防范关注较少,难免出现各种漏洞。

若有可能,改用具备防止缓冲区溢出内置机制的高级语言(Java、C#等)。但许多语言依赖于C库,或具有关闭该保护特性的机制(为速度而牺牲安全性)。

可以借助某些底层系统机制或检测工具(如对C数组进行边界检查的编译器)。许多操作系统(包括Linux和Solaris)提供非可执行堆栈补丁,但该方式不适于这种情况:攻击者利用堆栈溢出使程序跳转到放置在堆上的执行代码。此外,存在一些侦测和去除缓冲区溢出漏洞的静态分析工具(例如北大CoBOT、Checkmarx等)和动态工具,甚至采用grep命令自动搜索源代码中每个有问题函数的实例。

但是即使采用上面这些保护手段,程序员自身也可能犯其他许多错误,导致引入缺陷。例如,当使用有符号数存储缓冲区长度或某个待读取内容长度时,攻击者可将其变为负值,从而使该长度被解释为很大的正值。经验丰富的程序员还容易过于自信地使用某些危险的库函数,如对其添加自己总结编写的检查,或错误地推论出使用的具有潜在危险的函数在某些特殊情况下是"安全"的。

关注安全,关注作者

缓冲区溢出漏洞浅析(三)相关推荐

  1. 浅析缓冲区溢出漏洞的利用与Shellcode编写

    文章目录 前言 汇编语言 寄存器 内存堆栈 CPU指令 函数调用 缓冲区溢出 栈溢出原理 栈溢出攻击 ShellCode 总结 前言 缓冲区溢出(Buffer Overflow)是计算机安全领域内既经 ...

  2. 缓冲区溢出漏洞攻击——Shellcode编写

    一.实验内容 利用一个程序漏洞,编写shellcode,达成效果:蹦出对话框,显示"You have been hacked!(by JWM)" 二.实验原理 因为输入了过长的字符 ...

  3. 缓冲区溢出漏洞攻击之用户登录

    登录程序模拟 在以下程序中,可以使用三种不同的方法检查用户输入的账号和密码是否与存储的用户名和密码匹配.通过编译生成可执行文件,并对其进行测试.该程序会将用户输入的账号.密码与名为password.t ...

  4. Linux实验——缓冲区溢出漏洞实验

    Linux实验--缓冲区溢出漏洞实验 20125121 一.     实验描述 缓冲区溢出是指程序试图向缓冲区写入超出预分配固定长度数据的情况.这一漏洞可以被恶意用户利用来改变程序的流控制,甚至执行代 ...

  5. 分区起始位置参数溢出_IIS6.0缓冲区溢出漏洞深度分析(CVE-2017-7269)

    漏洞描述 开启WebDAV服务的IIS6.0存在缓冲区溢出漏洞可以任意代码执行,目前针对 Windows Server 2003 R2 可以稳定利用.在WebDAV服务的ScStoragePathFr ...

  6. 全网最详细的渗透测试靶机实操步骤——vulnhub靶机实战(七)IMF【包含了sql注入,文件上传,gif图片木马制作,缓冲区溢出漏洞sploit等诸多知识点的靶机,超多干货】

    靶机地址:https://www.vulnhub.com/entry/imf-1,162/ 靶机难度:中级(CTF) 靶机发布日期:2016年10月30日 靶机描述:欢迎使用" IMF&qu ...

  7. 缓冲区溢出漏洞_缓冲区溢出漏洞简介

    缓冲区溢出漏洞 重点 (Top highlight) 缓冲 (Buffer) A buffer is a temporary storage, usually present in the physi ...

  8. 验证本地缓冲区溢出漏洞攻击

    Info:本篇主要是为了验证本地缓冲区溢出,这是理解缓冲区溢出攻击的第一步,有了这一步,才能更深刻的理解到什么是缓冲区漏洞攻击,从而对以后的学习奠定一定的基础(注意:以下请在linux环境下实验) 基 ...

  9. 永恒之蓝ms17-0109(缓冲区溢出漏洞)

    目录 一.漏洞介绍 二.SMB介绍 三.影响版本 四.漏洞原理 五.漏洞利用 六.环境搭建 七.漏洞复现 1.首先对靶机进行端口扫描看看是否开启了445端口 2.启动kali上的metasploit ...

最新文章

  1. AGG第二十二课 conv_contour函数auto_detect_orientation的字体应用
  2. 直播APP开发注意事项汇总
  3. SQL Server 文件路径
  4. VC++开发简易输电线路管理信息系统
  5. Object.create()和new Object()
  6. priority_queue
  7. python处理u开头的字符串
  8. Java并发编程系列之Semaphore详解
  9. always on sql 收缩日志_使用alwayson后如何收缩数据库日志的方法详解
  10. linux 修改docker配置文件,dockerfile动态修改服务配置文件(示例代码)
  11. cors 前后端分离跨域问题_SpringBoot 实现前后端分离的跨域访问(CORS)
  12. 【UNITY3D 游戏开发之三】NGUI HUDTEXT 的练习源码及资源
  13. ERROR: modinfo: could not find module rbd FATAL
  14. 微信支付对账单的详细说明
  15. 三分钟告诉你什么是三层交换机!
  16. 【愚公系列】2021年12月 攻防世界-简单题-MOBILE-008(Ph0en1x-100)
  17. CV学习笔记【1】:transforms
  18. PPT文件不能编辑的原因
  19. bilibili源代码泄露,go-common
  20. 阿里云国际站:java应用提速(速度与激情)

热门文章

  1. 【UE4】TimeLine(蓝图)
  2. 实战技术:提升网站用户体验—WebP 图片的高效使用
  3. 关于ExecuteNonQuery() 方法
  4. 273 块钱购入的域名,值了
  5. phpinfo()函数 写法
  6. 网易2018编程题之游历魔法王国
  7. Pr学习(3)AI CC2017/2018 如何破解?
  8. python爬虫微信e校园签到,用Python爬虫的request方式实现自动签到!
  9. 服务器存储视频文件夹在哪里找,微信视频文件夹存储在什么位置?在哪里能找到...
  10. DXP封装中如何实现开孔