• 【web-攻击本地编译性应用程序】(11.1)缓冲区溢出漏洞


    目录

    攻击本地编译性应用程序

    简介:

    一、缓冲区溢出漏洞

    1.1、简介:

    1.2、栈溢出

    1.3、堆溢出

    简述:

    1.4、"一位偏移"漏洞

    简述:

    1.5、查找缓冲区溢出漏洞

    简述:

    过程:


    攻击本地编译性应用程序

    简介:

    1、在本地执行环境中运行的编译型软件一直受到缓冲区溢出与格式化字符串等漏洞的困扰,如今绝大多数的Web应用程序都是使用在托管执行环境中运行的语官和平台编写的, 这个环境中不存在上述典型漏洞。使用C#和Java这类语言的一个主要优点在于,程序员不必再担心缓冲区管理与指针算法等问题;这些问题曾给以本地语言(如C和C++)开发的软件造成重大影响,并且是这些软件中绝大多数严重漏洞的根源所在。

    2、但有时也会遇到用本地代码编写的Web应用程序,而且令许多主要使用托管代码编写的应用程序同样包含本地代码或调用在非托管环境中运行的外部组件,除非确切地知道所针对的应用程序并不包含任何本地代码,否则就有必要对它进行一些基本的检查,查明其中是否存在任何常见的漏洞。

    3、在打印机与交换机等硬件设备上运行的Web应用程序常常使用某种本地代码,其他可能的目标包含:任何其名称(如dll或exe)表示它使用了本地代码的页面或脚本,以及任何已知调用遗留外部组件的功能(如日志机制),如果认为所攻击的应用程序包含大量的本地代码,那么就有必要对应用程序处理的每个用户提交的数据进行测试,包括每个参数的名称与参数值、cookie、请求消息头及其他数据

    4、3种典型的软件漏洞缓冲区溢出、整数漏洞、格式化字符串漏洞



    一、缓冲区溢出漏洞

    1.1、简介:

    如果应用程序将用户可控制的数据复制到一个不足以容纳它们的内存缓存区中,就会出现缓冲区溢出漏洞。由于目标缓冲区溢出,导致邻近的内存被用户数据重写。攻击者可以根据漏洞的特点利用它在服务器上运行任意代码或执行其他未授权操作。

    1.2、栈溢出

    1、如果应用程序在未确定大小固定的缓冲区容量足够大之前,就使用一个无限制的复制操:(如c语言中的strcpy)将一个大小可变的缓冲区复制到另一个大小固定的缓冲区中,往往就会造成缓冲区溢出。

    2、例如,函数将字符串username复制到一个分配到栈上的大小固定的缓冲区中

    如果字符串username超过32个字符,_username缓冲区就会溢出, 攻击者将重写邻近内存中的数据

    bool CheckLogin(char* username,char* password)

    {

            char _username[32];

            strcpy(_username,username);

    ……

    }

    3、在成功利用栈缓冲区溢出漏洞的攻击中,攻击者通常能够重写栈上已保存的返回地址,当调用CheckLogin函数时,处理器将调用函数后执行的指令地址写入栈,结束CheckLogin函数后,处理器从栈中取出这个地址,返回执行这个指令。同时CheckLogin函数分配到栈上已保存的返回地址旁边的_username缓冲区,如果攻击者能够令_username缓冲区溢出,就能用他选择的一个值重写缓冲区已保存的返回地址,让处理器访问这个地址,从而执行任意代码

    1.3、堆溢出

    简述:

    1、从本质上讲,堆缓冲区溢出也是由前面描述的相同危险操作造成的,唯一的不同在于这时溢出的目标缓冲区分配在堆上, 而不是在栈上

    bool CheckLogin(char* username,char* password)

    {

            char* _username = (char*) malloc(32);

            strcpy(_username,username);

    ……

    }

    2、通常在堆缓冲区溢出中,目标缓冲区旁不是已保存的返回地址,而是其他以堆控制结构分隔的堆内存块。堆以一个双向链接表的形式执行,在内存中令每个块的前面是一个控制结构,其中包含块的大小、一个指向堆上前一个块的指针以及一个指向堆上后一个块的指针。当堆缓冲区溢出时,邻近的堆块的控制结构被用户控制的数据重写。

    3、与栈溢出漏洞相比,利用这种漏洞实施攻击要更困难一些,但一种常见的利用方法是在被重写的堆控制结构中写入专门设计的值,以在将来某个时间重写任何一个关键的指针,控制结构已被重写的堆块从内存中释放后、堆管理器需要更新堆块的链接表。要完成这项任务,它需要更新后一个堆块的反向链接指针,并更新前一个堆块的正向链接指针,以便链接表中的这两个指针指向彼此。为此,堆管理器使用被重写的控制结构中的值。具体来说,为更新后一个块的反向链接指针,堆管理器废弃被重写的控制结构中的正向链接指针,并在这个地址的结构中写入被重写的控制结构中的反向链接指针的值。换句话说,它在一个用户控制地址中写入一个用户控制的值。如果攻击者精心设计了他的溢出数据,就能用他选择的值重写内存中的任何指针,其目的是控制指针的执行路径,从而执行任意代码。通常指针重写的主要目标是随后被应用程序调用的函数指针的值,或者是在下次出现异常时被调用的异常处理器的地址

    4、最新的编译器与操作系统已经采取了各种措施对软件进行保护,防止编程错误导致缓冲区溢出。这表示,如今现实中的溢出漏洞往往难以利用

    1.4、"一位偏移"漏洞

    简述:

    1、如果编程错误使得攻击者可以在一个被分配的缓冲区之后写入一个字节(或少数几字节),就会发生一种特殊的溢出漏洞

    2、以下面的代码为例,它在栈上分配一个缓冲区,执行一项计数缓冲区复制操作,然后以空字节结束目标字符串

    bool CheckLogin(char* username,char* password)

    {

            char _username[32];

            int i;

            for(i = 0; username[i] && i<32;i++)

                    _username[i] = username[i];

            username[i]=0;

    ……

    }        

    3、这段代码复制32B, 然后增加空终止符。因此如果用户名为32B或更长,空字节就会写在缓冲区之外,污染邻近的内存,这种条件可被攻击者加以利用,如果栈上邻近的数据是调用帧的已保存的帧指针,那么将低位字节设为零可能会导致它指向_username缓冲区, 因而指向攻击者控制的数据,当调用的函数返回时、攻击者就可以控制执行流程


    4、如果开发者忽略在字符串缓冲区中为一个空字节终止符预留空间,这时也会出现一种与上面的漏洞类似的漏洞。下面以前面堆溢出漏洞的“修复” 代码为例

    bool CheckLogin(char* username,char* password)

    {

            char* _username = (char*) malloc(32);

            strcpy(_username,username,32);

    ……

    }

    5、在这段代码开发中,在堆上建立一个固定大小的缓冲区,然后执行一个计数缓冲区复制操作,旨在确保缓冲区不会溢出。但如果用户名比缓冲区更长,那么缓冲区内就会完全填充用户名中的字符,再没有空间在最后附加一个空字节。因此复制到缓冲区中的字符串就会"丢失"它的空终止符

    6、一些语言(如C)并不单独记录一个字符串的长度,字符串结束部分用一个空字节表示(也就是说, 用零的ASCII字符编码表示),如果一个字符串"丢失"了它的空终止符,它的长度就会培增加,并一直到内存的下一个字节(它碰巧为零)结束,这种无意的结果经常会在应用程序中造成反常行为与漏洞

    7、在一个硬件设备的Web应用程序中出现过漏洞:该应用程序包含一个页面,它接受POST请求的任意参数,并返回HTML表单,其中以隐藏字段的形式包含那些参数的名称与参数值

    8、可能因为某种原因,整个应用程序都需要使用这个页面处理各种用户输入,其中许多为敏感数据。但如果用户提交的数据等于或超过4096B,那么返回的表单中还包括在向页面提出的前一个请求中提交的参数,即使这些参数由另外一名用户提交。

    9、确定这种漏洞后,就可以继续向这个易受攻击的页面提交超长的数据,解析收到的响应,记录其他用户提交给页面的每一个数据,包括登录证书和其他敏感信息。

    10、造成这种漏洞的根本原因是,在4096B的内存块中、用户提交的数据被保存为以空字节终止的字符串。这些数据被复制到一个检验操作中,因此不会直接造成溢出。但如果提交的是超长的输入, 复制操作就会导致空终止符"丢失",因而字符串会流入到内存邻近的数据中。

    11、因此当应用程序解析请求参数时,它会一直解析到下一个空字节为止,因此就会解析出其他用户提交的参数。

    1.5、查找缓冲区溢出漏洞

    简述:

    1、向一个确定的目标发送较长的字符串并监控反常结果是查找缓冲区溢出漏洞的基本方法。有时一些细微的漏洞只有通过发送一个特殊长度或者在较小的长度范围内的超长字符串才能检测出来。但许多时候,只需向应用程序发送一个超出其预计长度的字符串,就可以探查出漏洞。

    2、程序员常常使用十进制或十六进制的约整数(如32 、100 、1024 、4096等)来创建固定大小的缓冲区。在应用程序中探查明显漏洞的一个简单方法就是,向确定的每一个目标数据发送超长字符串, 然后监控服务器对反常输入的响应。


    过程:

    1、向每一个目标数据提交一系列稍大于常用缓冲区大小的长字符串

    2、一次针对一个数据实施攻击,最大程度地覆盖应用程序中的所有代码路径

    3、可以使用Bp中的字符块有效载荷来源自动生成各种大小的有效载荷

    4、监控应用程序的响应,确定所有反常现象。无法控制的溢出几乎可以肯定会在应用程序中引起异常。在远程进程中探测何时出现这种异常相当困难,需要寻找的反常现象包括:

    A、HTTP500状态码或错误消息,这时其他畸形(而非超长)输入不会产生相同的结果

    B、内容详细的消息,表示某个本地代码组件发生故障。

    C、服务器收到一个局部或畸形响应

    D、服务器的TCP连接未返回响应, 突然关闭

    E、整个Web应用程序停止响应

    5、如果一个堆溢出被触发,这可能会在将来而非立即导致系统崩溃。因此必须,进行实验确定一种或几种造成堆"腐化"的测试字符串

    6、"一位偏移"漏洞可能不会造成系统朋溃,但可能会导致反常行为,如应用程序返回意外的数据。


    有时,测试字符串可能会被应用程序自身或其他组件(如Web服务器)头施的输入确认检查所阻止。在URL查询字符串中提交超长数据时通常会出现这种情况,应用程序会在针对每个测试字符串的响应中以"URL过长"之类的常规消息反映这一点,在这种情况下应当进行实验,确定URL允许的最大长度( 一般约为2000个字符),并调整缓冲区大小,以使测试字符串符合这个要求。但即使实施了常规过滤,溢出可能依然存在,因为长度足够短、能够避开这种过滤的字符串也可能触发溢出

    其他情况下,过滤机制可能会限制在一个特定参数中提交的数据类型或字符范围。如当将提交的用户名传送给一个包含溢出漏洞的功能时,应用程序可能会确认该用户名是否仅包含字母数字字符。为实现测试效率最大化,应当设法确保每个测试字符串仅包含相关参数允许的字符。满足这种要求的一个有效方法是,截获一个包含应用程序所接受的数据的正常请求,然后使用其中已经包含的相同类型的字符,创建一个可能通过任何基于内容的过滤的长字符串,再使用这个字符串轮流测试每一个目标参数

  • 相关阅读:
    HI3516DV300 图像输入
    集合框架的线程安全问题ArrayList、HashSet、HashMap
    Spring 的注入
    SpringMVC组件解析
    Day08_DM层建设实战,220625,hm,
    如何在Python中实现找到两个未知特征来从其余样本中聚类已知样本?
    三分钟带你了解内网穿透的什么
    多模态论文阅读之VLMo
    13.Ansible.roles案例
    公众号涨粉思路
  • 原文地址:https://blog.csdn.net/qq_53079406/article/details/126631060