20199113 2019-2020-2 《网络攻防实践》第十周作业

20199113 2019-2020-2 《网络攻防实践》第十周作业

1.实践内容

软件安全概述

安全漏洞在软件开发周期的各个环节(包括设计、编码、发布等)中都可能被引入,而只有软件设计与开发人员充分认识到安全漏洞的危害,掌握安全漏洞机理,以及如何避免漏洞的安全编程经验,并在软件厂商的软件开发生命周期中切实执行安全设计开发的流程,才有可能尽量减少发布软件中的安全漏洞数量,降低它们对网络与现实世界所带来的影响与危害。


软件安全漏洞
美国国家标准技术研究院NIST将安全漏洞定义为:在系统安全流程、设计、实现或内部控制中所存在的缺陷或弱点,能够被攻击者所利用并导致安全侵害或对系统安全策略的违反。
三个基本元素:
1)系统的脆弱性或缺陷
2)攻击者对缺陷的可访问性
3)攻击者对缺陷的可利用性


软件安全困境
困境三要素:复杂性、可扩展性、连通性
复杂性:软件规模越来越大,越来越复杂,也就意味着软件的bug会越来越多。
可扩展性:攻击者可能以不可预测的扩展方式来入侵软件和系统,且可扩展性软件的的安全分析要比分析一个完全不能更改的软件要困难得多。
连通性:高度的连通性使小小的缺陷可能影响非常大的范围。网络的连通性使得不需要人为干涉的自动化攻击成为可能。


软件安全漏洞类型

  • 内存安全违规类:

在软件开发过程中在处理RAM内存访问时所引入的安全缺陷 缓冲区溢出漏洞是一种最基础的内存安全问题

  • 输入验证类:
    输入验证类安全漏洞是指软件程序在对用户输入进行数据验证存在的错误,没有保证输入数据的正确性、合法性和安全性,从而导致可能被恶意攻击与利用。
    XSS、SQL注入、远程文件包含、HTTP Header注入等

  • 竞争条件类:
    竞争条件类缺陷是系统或进程中一类比较特殊的错误,通常在涉及多进程或多线程处理的程序中出现,是指处理进程的输出或结果无法预测,并依赖于其他进程事件发生的次序或时间时,所导致的错误。

  • 权限混淆与提升类:
    权限混淆与提升漏洞是指计算机程序由于自身编程疏忽或被第三方欺骗,从而滥用其权限,或赋予第三方不该给予的权限。
    跨站请求伪造、FTP反弹攻击、权限提升、“越狱"等
    缓冲区溢出基础概念



缓冲区溢出基础概念

缓冲分区溢出基本概念:缓冲区溢出是一种内存安全违规类的漏洞。程序在往缓冲区输入数据的时候超出了缓冲区的最大容量,导致了一处数据覆盖了相邻的内存。该漏洞最常见于内存和字符串的复制函数。理想的情况下应该检查每个输入缓冲区的数据的长度,但是程序员一般没有足够的安全知识与意识。


缓冲区溢出攻击背景知识

  • 编译器与调试器的使用:对于高级编程语言编写的源码,需要通过编译器和连接器才能生成可直接在操作系统平台上运行的可执行程序代码。而调试器则是程序开发人员在运行时刻调试与分析程序行为的基本工具。对于通常使用的C/C++编程语言,最著名的编译与连接器是GCC。类UNIX平台上进行程序的调试经常使用GDB调试器。
  • 汇编语言基础知识:汇编语言是理解软件安全漏洞机理,掌握软件渗透攻击代码技术的底层基础。从应用的角度一般将寄存器分为4类,即通用寄存器、段寄存器、控制寄存器和其他寄存器。通用寄存器主要用于普通的算术运算,保存数据、地址、偏移量、计数值等。段寄存器一般用作段基址寄存器。控制寄存器用来控制处理器的执行流程。其他寄存器中值得关注的是“扩展标志”寄存器,由不同的标志位组成,用于保存指令执行后的状态和控制指令执行流程的标志信息。常见的汇编指令有POP,PUSH,JMP,RET等。
  • 进程内存管理:LINXU操作系统中的进程内存空间布局和管理机制,程序在执行时,系统在内存中会为程序创建一个虚拟的内存地址空间,在32位机上4GB的空间大小,用于映射物理内存,并保存程序的指令和数据。内存空间3GB以下为用户态空间,3GB-4GB为内核态空间。Windows操作系统的进程内存空间2GB-4GB为内核态地址空间,用于映射Windows内核代码和一些核心态DLL,并用于存储一些内核态对象,0GB-2GB为用户态地址空间。
  • 函数调用过程:栈溢出攻击是针对函数调用过程中返回地址在栈中的存储位置,进行缓冲区溢出,从而改写返回地址,达到让处理器指令寄存器跳转至攻击者指定位置执行恶意代码的目的。栈是一种最基本的LIFO后进先出抽象数据结构,主要被用于实现程序中的函数或过程调用,在栈中会保存函数的调用参数、返回地址、调用者栈基址、函数本地局部变量等数据。两个与栈密切相关的寄存器为ebp和esp,分别保存当前运行函数的栈底地址和栈顶地址。两个重要指令为push和pop,分别是将数据压入栈,及将栈顶数据弹出至特定寄存器。程序进行函数调用的过程有如下三个步骤:
    1.调用:调用者将函数调用参数、函数调用下一条指令的返回地址压栈,并跳转至被调用函数入口地址。
    2.序言:对调用函数的栈基址进行压栈保存,并创建自身函数的栈结构。
    3.返回:通常执行leave或ret
    代码:
#include <stdio.h>
int func(int a, int b){
   int retVal = a+ b;
   printf("b: 0x%08x\n",&b);
   printf("a: 0x%08x\n",&a);
   printf("ret addr here: 0x%08x\n",&a-1);
   printf("stored ebp here: 0x%08x\n",&a-2);
   printf("retVal: 0x%08x\n\n", &retVal);
   return retVal;
}
int main(int argc, char* argv[])
{
   int result = func(1, 2);
   return 0;
}

缓冲区溢出攻击原理:分为栈溢出、堆溢出和内核溢出这三种。

  • 栈溢出:是指存储在栈上的一些缓冲区变量由于存在缺乏边界保护问题,能够被溢出并修改栈上的敏感信息(通常是返回地址),从而导致程序流程的改变。
  • 堆溢出:是存储在堆上的缓冲区变量缺乏边界保护所遭受溢出攻击的安全问题.
  • 内核溢出:是由于进程内存空间内核态中存储的缓冲区变量被溢出造成的。

Linux平台上的栈溢出与shellcode

Linux平台栈溢出攻击技术
按照攻击数据的构造方式不同,主要有NSR、RNS和RS三种模式。NSR和RNS模式适用于本地缓冲区溢出和远程栈溢出攻击,而RS模式只能用于本地缓冲区溢出攻击。

  • ​NSR模式:NSR模式主要适用于被溢出的缓冲区变量比较大,足以容纳Shellcode的情况,其攻击数据从低地址到高地址的构造一堆Nop指令填充Shellcode,加上一些期望覆盖RET返回地址的跳转地址,从而构成了NSR攻击数据缓冲区。

  • RNS模式:概括来说,RNS模式一般用于被溢出的变量比较小,不足于容纳Shellcode的情况。攻击数据从低地址到高地址的构造方式是首先填充一些期望覆盖RET返回地址的跳转地址,然后是一堆Nop指令填充出“着陆区”,最后再是Shellcode。

  • RS模式:RS模式下能够精确地定位出Shellcode在目标漏洞程序进程空间中的起始地址,因此也就无需引入Nop空指令构建“着陆区”。这种模式是将Shellcode放置在目标漏洞程序执行时的环境变量中,由于位置是固定的,可以通过公式计算ret=0xc0000000-sizeof(void*)-sizeof(FILENAME)-sizeof(Shellcode)。


Linux平台的shellcode实现技术
Shellcode就是符合Intel 32位指令规范的一串CPU指令,被用于溢出之后改变系统正常流程,转而执行Shellcode以完成渗透测试者的攻击目的,通常是为他提供一个访问系统的本地或远程命令行访问。


  • Linux本地shellcode实现机制:Linux系统本地Shellcode通常提供的功能就是为攻击者启动一个命令行Shell。
  • Shellcode的通用方法:
    1.先用高级编程语言,通常用C,来编写Shellcode程序;
    2.编译并反汇编调试这个Shellcode程序;
    3.从汇编语言代码级别分析程序执行流程;
    4.整理生成的汇编代码,尽量减小它的体积并使它可注入,并可通过嵌入C语言进行运行测试和调试;
    5.提取汇编代码所对应的opcode二进制指令,创建Shellcode指令数组。

  • Linux远程Shellcode实现机制:实现原理与本地Shellcode完全一致,也是通过执行一系列的系统调用来完成指定的功能。实现方法步骤也基本一样。Linux远程Shellcode需要让攻击目标程序创建socket监听指定的端口等待客户端连接,启动一个命令行Shell,并将命令行的输入输出与socket绑定,这样攻击者就可以通过socket客户端连接目标程序所在主机的开放端口,与服务端socket建立起通信通道,并获得远程访问Shell。

Windows平台上栈溢出与Shellcode

1栈溢出攻击技术:与Linux的攻击技术的差异主要体现在三个方面

  • 对程序运行中废弃栈的处理方式的差异:程序运行过程中拥有大量的函数调用,当一个函数调用完返回至调用者执行下一条指令前会有恢复栈基和栈顶指针的操作,Windows会向废弃栈中写入一些随机的数据,Linux则不做任何处理。这导致Windows平台中构建缓冲区数据在栈中植入恶意指令时,会面临限制。

  • 进程内存空间的布局差异:Linux系统进程内存空间中栈底指针在0xc0x0000000之下,这些地址中没有空字节;Windows系统栈位置处于0x00FFFFFF以下的用户内存空间一般为0x0012****地址附近,而这些地址的首字节均为0x00空字节。

  • 系统功能调用的实现方式的差异:Windows通过操作系统中更为复杂的API及内核处理例程调用链来完成系统功能调用。攻击者在执行shellcode就要考虑系统功能调用的区别。

Windows系统上典型的Shellcode是启动一个命令行shell,即“command.com”或“cmd.exe”,API中提供了system()函数调用,可以用于启动指定程序或特定命令。

  • 远程Shellcode的过程:

1.创建一个服务器端socket,并在指定端口上监听;
2.通过accept接受网络连接;
3.创建子进程,运行cmd.exe启动命令行;
4.创建两个管道,命令管道和输出管道。


堆溢出攻击

  • 更难的堆溢出:堆中并没有可以直接覆盖并修改指令寄存器指针的返回地址,因此往往需要利用在堆中一些会影响程序执行流程的关键变量,如函数指针、C++类对象中的虚函数表,或挖掘岀堆中进行数据操作时可能存在的向指定内存地址改写内容的漏洞机会。
  • 函数指针改写:如果需要被溢出的缓冲区临近全局函数指针存储地址,且在其低地址方向。那么当向缓冲区填充数据时,如果没有边界判断和控制的话,那么缓冲区溢出之后就会自然地覆盖函数指针所在的内存区,从而改写函数指针的指向地址。而攻击者只需将指针指向hellcode入口地址。
  • C++类对象虚函数表改写:使用虚函数机制的C++类的类成员变量中存在可被溢出的缓冲区,那么可以通过覆盖类对象的虚函数指针,使其指向一个特殊构造的虚函数表,从而执行注入指令。
  • Linux下堆管理glibc库free()函数本身漏洞:Linux操作系统中的堆管理是通过glibc库实现的,使用的内存管理算法被称为dlmalloc,使用了被称为Bin的双向循环链表来存储内存空闲块信息。glibc库中的free()函数提供了将4字节值写入任意内存地址的机会。free()函数在处理内存块回收时,需要将己被释放的空闲块和与之相邻的空闲块进行合并,因此将会把符合条件的空闲块从Bin链表中unlink摘出来,合并之后再将新的空闲块插回链表中。攻击者可以利用此特性构造空闲块,达成覆盖。

缓冲区溢出攻击的防御技术

  • 尝试杜绝溢出的防御技术:根本方法编写正确的、不存在缓冲区溢出安全漏洞的软件代码。依靠fault injection等查错程序、Fuzz注入测试寻找漏洞、在编译器上引入针对缓冲区的边界保护检查机制。
  • 允许溢出但不让程序改变执行流程的防御技术:Canary、SafeSEH技术等。
  • 无法让攻击代码执行的防御技术:通过堆栈不可执行限制来防御。


2.实践总结

本章内容不仅涉及到高级语言、汇编语言很多方面的知识,同时还拓展了一些操作系统底层的内容,有很大的难度,需要反复研究和学习。

posted @ 2020-05-06 11:37  姚泽  阅读(201)  评论(0编辑  收藏  举报