20199303 2019-2020-2 《网络攻防实践》第10周作业
1.实践内容
1.1 软件安全概述
1.1.1 软件安全漏洞
定义:被攻击者利用并导致危害的安全缺陷被称为软件安全漏洞
范畴:不限于软件安全漏洞,还包括硬件、个人与组织管理中存在的、能够被攻击者利用来破坏安全策略的弱点
1.1.2 软件安全困境
软件安全“困境三因素”
复杂性:现代软件已经非常复杂,且向着更复杂发展,越发庞大的规模也代表着更多的bug和漏洞等
可扩展性:现代软件为了支持更优化的软件架构和更好的用户感受,会提供扩展和交互渠道,而攻击者可能会用难以预测的方式利用这些渠道进行攻击
连通性:高度的连通性使得很小的软件缺陷也能影响巨大的范围、网络连通性使得不需要人为干涉的自动化攻击成为可能
1.1.3软件安全漏洞类型
从技术上分为:
内存安全违规类:内存安全违规类漏洞是在软件开发过程中在处理RAM (random- access memory)内存访问时所引入的安全缺陷,如缓冲区溢出漏洞和Double Free、Use-after Free等不安全指针。内存安全违规类漏洞主要出现在C\C++等编程语言所编写的软件之中,由于这类语言支持任意内存的分配和归还、任意指针的转换、计算等操作,而这类操作通常都没有内存安全保障。
输入验证类:输入验证类安全漏洞是指软件程序在对用户输入进行数据验证存在的错误,没有保证输入数据的正确性、合法性和安全性,从而导致可能被恶意攻击与利用。输入验证类安全输入验证类是软件程序在对用户进行数据验证存在的错误,主要包含XSS攻击、SQL注入攻击、HTTP响应。
竞争条件类:系统或进程中一类比较特殊的错误,通常在设计多进程或多线程处理的程序中出现,是指处理进程的输出或者结果无法预测,并依赖于其他进程事件发生的次序或时间时,所导致的错误。
权限混淆与提升类:计算机程序由于自身编程疏忽或被第三方欺骗,从而滥用其权限,或赋予第三方不该给予的权限。具体形式:Web应用程序中的跨站请求伪造、Clickjacking、FTP反弹攻击、权限提升、“越狱”等
1.2 缓冲区溢出基础概念
1.2.1 缓冲区溢出的基本概念与发展过程
缓冲区溢出基本概念:缓冲区溢出是计算机程序中存在的一类内存安全违规类漏洞,在计算机程序向特定缓冲区内填充数据时,超出了缓冲区本身的容量,导致外溢数据覆盖了相邻内存空间的合法数据,从而改变程序执行流程破坏系统运行完整性,常见于C/C++的memcpy(),strcpy()等内存与字符串复制函数的引用位置。
1.2.2缓冲区溢出攻击背景知识
编译器与调试器的使用:高级编程语言编写的源码需要通过编译器和连接器才能生成可直接在操作系统平台上运行的可执行程序代码。调试器是程序开发人员在运行时刻调试与分析程序行为的基本工具。常使用的C/C++编程语言,最著名的编译与连接器是GCC。类UNIX平台上进行程序的调试经常使用GDB调试器。Windows平台,常用Visual Studio、VS.net等集成开发环境
汇编语言基础知识:汇编语言是理解软件安全漏洞机理,掌握软件渗透攻击代码技术的底层基础。从应用的角度一般将寄存器分为4类,即通用寄存器、段寄存器、控制寄存器和其他寄存器。通用寄存器主要用于普通的算术运算,保存数据、地址、偏移量、计数值等。段寄存器一般用作段基址寄存器。控制寄存器用来控制处理器的执行流程。其他寄存器中值得关注的是“扩展标志”寄存器,由不同的标志位组成,用于保存指令执行后的状态和控制指令执行流程的标志信息。常见的汇编指令有POP,PUSH,JMP,RET等。
IA32架构中的关键寄存器及功能
进程内存管理
Linux操作系统中的进程内存空间布局和管理机制:程序在执行时,系统在内存中会为程序创建一个虚拟的内存地址空间,在32位机上即4GB的空间大小,用于映射物理内存,并保存程序的指令和数据。内存空间3GB以下为用户态空间,3GB-4GB为内核态空间;操作系统将可执行程序加载到新创建的内存空间中,程序一般包含.text、.bss和.data三种类型的段。
Windows操作系统中的进程内存空间布局和管理机制:内核态地址空间为2GB-4GB,用于映射Windows内核代码和一些核心态DLL,并用于存储一些内核态对象,0GB-2GB为用户态地址空间。
函数调用过程
程序进行函数调用的过程有三个步骤,如下图所示:①调用:调用者将函数调用参数、函数调用下一条指令的返回地址压栈,并跳转至被调用函数入口地址。 ②序言:被调用函数开始执行首先会进入序言阶段,将对调用函数的栈基址进行压栈保存,并创建自身函数的栈结构,具体包括将ebp寄存器赋值为当前栈基址,为本地函数局部变量分配栈地址空间,更新esp寄存器为当前栈顶指针等。 ③返回:被调用函数执行完功能将指令控制权返回给调用者前会进行返回阶段操作,通常执行level 和 ret指令,即恢复调用者的栈顶与栈底指针,并将之前压栈的返回地址装载至指令寄存器eip中,继续执行调用者在函数调用后的下一条指令。
1.2.3 缓冲区溢出攻击原理
缓冲区溢出漏洞根据溢出区在进程内存空间中的位置不同,分为栈溢出、堆溢出和内核溢出这三种。
栈溢出:是指存储在栈上的一些缓冲区变量由于存在缺乏边界保护问题,能够被溢出并修改栈上的敏感信息(通常是返回地址),从而导致程序流程的改变。
堆溢出:是存储在堆上的缓冲区变量缺乏边界保护所遭受溢出攻击的安全问题。
内核溢出:由于进程内存空间内核态中存储的缓冲区变量被溢出造成的。
根本问题:用户输入可控制的缓冲区操作缺乏对目标缓冲区的边界安全保护。
1.3 Linux平台上的栈溢出与Shellcode
1.3.1 Linux平台栈溢出攻击技术
按照攻击数据的构造方式不同,主要有NSR、RNS和RS三种模式
NRS模式:主要适用于被溢出的缓冲区变量比较大,足以容纳Shellcode的情况,其攻击数据是从低地址到高地址的的构造方式是一堆Nop指令(即空操作指令)之后填充Shellcode,再加上一些期望覆盖RET返回地址的跳转地址,从而构成了NRS攻击数据缓冲区
RNS模式:一般用于被溢出的变量比较小,不足于容纳Shellcode的情况。攻击数据从低地址到高地址的构造方式是首先填充一些期望覆盖RET返回地址的跳转地址,然后是一堆Nop指令,最后再是Shellcode。
RS模式:RS模式下能够精确地定位出Shellcode在目标漏洞程序进程空间中的起始地址,因此也就无需引入Nop空指令构建“着陆区”。这种模式是将Shellcode放置在目标漏洞程序执行时的环境变量中,因其位置是固定的,可以通过公式计算ret=0xc0000000-sizeof(void*)-sizeof(FILENAME)-sizeof(Shellcode)
1.3.2Linux平台的shellcode实现技术
Shellcode是一段机器指令,对于我们通常接触的IA32构架平台,Shellcode就是符合Intel 32位指令规范的一串CPU指令,被用于溢出之后改变系统正常流程,转而执行Shellcode以完成渗透测试者的攻击目的,通常是为他提供一个访问系统的本地或远程命令行访问。在Linux操作系统中,程序通过“int 0x80”软中断来执行系统调用,而在Windows操作系统中,则通过核心DLL中提供的API接口来完成系统调用。按照在本地溢出攻击和远程溢出攻击使用场景的不同,又分为本地Shellcode和远程Shellcode。
Linux本地Shellcode实现机制:Linux系统本地Shellcode通常提供的功能就是为攻击者启动一个命令行Shell。在获得汇编语言实现的Shellcode之后,我们可以通过查找Intel opcode指令参考手册,即可获得opcode二进制指令形式的Shellcode。本地Shellcode的产生过程:1.先用高级编程语言,通常用C,来编写Shellcode程序2.编译并反汇编调试这个Shellcode程序3.从编译语言代码级别分析程序执行流程4.整理生成的汇编代码,尽量减小它的体积并使它可注入,并通过嵌入C语言进行运行测试和调试5.提取汇编代码所对应的opcode二进制指令,创建Shellcode指令数组。在Linux本地Shellcode中,往往还会在运行execve()启动shell之前,调用setreuid(0)将程序运行权限提升至Root用户,这样才能利用本地溢出攻击来提升权限,在执行execve()函数之后还需要执行exit()函数,从而在溢出攻击之后能够使程序正常退出。
Linux平台的远程Shellcode实现机制:Linux平台上的远程Shellcode实现机制与本地Shellcode实现机制是一样的,通过系统调用完成指定功能。Linux远程Shellcode需要让攻击目标程序创建socket监听指定的端口等待客户端连接,启动一个命令行Shell,并将命令行的输入输出与socket绑定,这样攻击者就可以通过socket客户端连接目标程序所在主机的开放端口,与服务端socket建立起通信通道,并获得远程访问Shell。
1.4 Windows平台上的栈溢出与Shellcode
1.4.1 Windows平台栈溢出攻击技术
成功攻击应用程序中栈溢出漏洞密切相关的主要有以下三点:
对程序运行过程中废弃栈的处理方式差异:Windows平台会向废弃栈中写入一些随机的数据,而Linux则不进行任何的处理。
进程内存空间的布局差异:Linux进程内存空间中栈底指针在0xc0000000之下,在这些地址中没有空字节。Windows平台的栈位置处于0x00FFFFFF以下的用户内存空间,这些内存地址的首字节均为0x00空字节。
系统功能调用实现方式差异:Linux系统中通过“int 80”中断处理来调用系统功能,而Windows系统则是通过操作系统中更为复杂的API及内核处理例程调用链来完成系统功能调用。
1.4.2 Windows平台Shellcode实现技术
Windows平台Shellcode实现技术
shellcode必须可以找到所需要的Windows32 API函数,并生成函数调用表
为了能够使用API函数,shellcode必须找到目标程序已加载的函数地址
shellcode需考虑消除空字节,以免在字符串操作函数中被截断
shellcode需确保自己可以正常退出,并使原来的目标程序进程继续运行或终止
在目标系统环境存在异常处理和安全防护机制时,shellcode需进一步考虑如何应对这些机制
Windows远程shellcode
创建一个服务器端socket,并在指定的端口上监听
通过accept()接受客户端的网络连接
创建子程序,运行“cmd.exe”,启动命令行
创建两个管道,命令管道将服务器端socket接收(recv)到的客户端通过网络输入的执行命令,连接至cmd.exe的标准输入;然后输出管道将cmd.exe的标准输出连接至服务器端socket的发送(send),通过网络将运行结果反馈给客户端
1.5 堆溢出攻击
堆溢出是缓冲区溢出中第二种类型的攻击方式,由于堆中的内存分配与管理机制较栈更复杂,不同操作系统平台的实现机制具有显著的差异。堆中没有可以直接覆盖的返回地址,因此堆溢出攻击比栈溢出更难
函数指针改写:此种攻击方式要求被溢出的缓冲区临近全局函数指针存储地址,且在其低地址方向上。如果向缓冲区填充数据的时候,如果没有边界控制和判断的话,缓冲区溢出就会自然的覆盖函数指针所在的内存区,从而改写函数指针的指向地址,则程序在使用这个函数指针调用原先的期望函数的时候就会转而执行shellcode
C++类对象虚函数表改写:使用了虚函数机制的C++类,如果它的类成员变量中存在可被溢出的缓冲区,那么就可以进行堆溢出攻击,通过覆盖类对象的虚函数指针,使其指向一个特殊构造的虚函数表,从而转向执行攻击者恶意注入的指令
Linux下堆管理glibc库free()函数本身漏洞:攻击者可以通过精心构造unlinkme内存块进行free()函数堆溢出攻击
1.6 缓冲区溢出攻击的防御技术
尝试杜绝溢出的防御技术:解决缓冲区溢出攻击最根本的方法是编写正确的、不存在缓冲区溢出安全漏洞的软件代码。例如,通过Fuzz测试寻找安全漏洞并修复,在编译器引入针对缓冲区的边界保护检查机制
允许溢出但不让程序改变运行流程的防御技术:允许溢出发生,但对可能影响到程序流程的关键数据结构实施严密的安全保护,不让程序改变其执行流程,从而阻断溢出攻击
无法让攻代码执行的防御技术:尝试解决冯·诺依曼体系的本质缺陷,通过堆栈不可执行限制来防御缓冲区溢出攻击。IA64、AMD64、Alpha 等新的 CPU 硬件体系框架都引入对基于硬件 NX 保护机制,从硬件上支持对特定内存页设置成不可执行,Windows XP SP2、Linux 内核 2.6 及以后版本都支持硬件 NX 保护机制,与橾作系统配合来提升系统的安全性。