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

实践内容

10.1软件安全概述

攻击者能够轻易地对系统和网络实施攻击,很大程度上是因为安全漏洞在软件中的大规模存在,攻击者可以利用这些漏洞来违背系统和网络的安全属性。安全漏洞的类型多种多样,从最基本的缓冲区溢出,到格式化字符串漏洞,竞态条件漏洞,整数溢出,以及流行的XSS跨站脚本、SQL注入等。而针对安全漏洞的渗透利用技术也在与安全防护机制的博弈中日新月异,层出不穷,从最简单的栈溢出,到更难操控的堆溢出,内核溢出,对抗DEP(数据执行保护)机制的ret2libc攻击、对抗ASLR(地址空间布局随机化)机制的Heap Spraying攻击,最新的能够同时对抗DEP和ASLR的JIT Spraying攻击,在软件代码与内存空间上演着精彩的对弈。

软件安全漏洞的定义:软件自从诞生之日起,就和bug形影不离,而其中可以被攻击者利用并导致危害的安全缺陷被称为软件安全漏洞。

安全漏洞的三元素:系统的脆弱性或缺陷、攻击者对缺陷的可访问性,以及攻击者对缺陷的可利用性。

软件缺陷和安全漏洞是计算机系统最致命的弱点,并在现在的大量软件中非常普遍地存在,软件缺陷和安全漏洞会导致巨大的经济损失,甚至于灾难性的后果。

软件安全困境三要素:复杂性,可扩展性和连通性

  • 复杂性:现代软件复杂,软件源代码行数越来越多。
  • 可扩展性:大多数现代操作系统通过动态装载设备驱动和模块支持可扩展性,,客户端通过运行时编译或解释执行的虚拟机允许运行移动代码。
  • 连通性:网络连通性使得不需要人为干涉的自动化攻击成为可能

CVE:软件安全漏洞标准目录

软件安全漏洞类型从技术上主要包括如下几类:
1.内存安全违规类:是在软件开发过程中在处理RAM内存访问时所引入的安全缺陷,如缓冲区溢出漏洞等。不安全指针是指在计算机程序中存在的并没有指向适当类型对象的非法指针,在对这些指针进行引用时,往往发生一些不可预期的后果,导致程序内存访问错误,而一旦攻击者可以控制这些指针指向的内存内容,那他们就可以利用这些问题构造出恶意攻击,获得软件的控制权。
2.输入验证类:软件程序在对用户输入进行数据验证存在的错误,没有保证输入数据的正确性、合法性和安全性,从而导致可能恶意攻击与利用
3.竞争条件类:处理进程的输出或者结果无法预测,并依赖于其他进程事件发生的次序或时间时,所导致的错误
4.权限混淆与提升类:滥用权限所导致的漏洞,权限提升漏洞通常被攻击者用于获取系统管理员或系统开发者预期之外的更高访问权限,用于执行内核级别的操作

10.2缓冲区溢出基础概念

缓冲区溢出:在计算机程序向特定缓冲区内填充数据时,超出了缓冲区本身的容量,导致外溢数据覆盖了相邻内存空间的合法数据,从而改变程序执行流程破坏系统运行完整性。理想情况下,程序应检查每个输入缓冲区的数据长度,并不允许输入超出缓冲区本身分配的空间容量,但是大量程序总是假设数据长度是与所分配的存储空间相匹配的,因而很容易产生缓冲区溢出漏洞。

缓冲区溢出攻击发生的根本原因,可以认为是现代计算机系统的基础架构--冯诺依曼体系存在本质的安全缺陷,即采用了存储程序原理,计算机程序的数据和指令都在同一内存中进行存储,而没有严格的分离。这一缺陷使得攻击者可以将输入的数据,通过利用缓冲区溢出漏洞,覆盖修改程序在内存空间中与数据区相邻存储的关键指令,从而达到使程序执行恶意注入指令的攻击目的

类UNIX平台GDB调试器小结:

  • break/clear:设置或移除断点
  • enable/disable:启用或禁用断点
  • watch:设置监视表达式值改变时的程序中断
  • run:运行程序
  • attach:调试已运行进程
  • continue:继续运行
  • next:单步代码执行并不进入函数调用
  • nexti:单步指令执行并不进入函数调用
  • step:单步代码执行并跟入函数调用
  • stepi:单步指令并跟入函数调用
  • info:查看各种信息
  • backtrace:显示调用栈
  • x:限制指定地址内容
  • print:显示表达式值
  • list:列出程序源码,需调试程序带符号编译
  • disass:反汇编指定函数

汇编小结:

  • 通用寄存器:eax,ebx,ecx,edx主要用于普通的算术运算,保存数据、地址、偏移量、计数值等
  • 段寄存器:在IA32架构中是16位的,一般用作段基址寄存器
  • 控制寄存器:用来控制处理器的执行流程,其中最关键的是eip,也被称为指令指针,它保存了下一条即将执行的机器指令的地址
  • 其他寄存器:值得关注的是“扩展标志”eflags寄存器,由不同的标志位组成,用于保存指令执行后的状态和控制指令执行流程的标志信息。

IA32架构中的关键寄存器及其功能如下图:


IA32架构汇编语言中,又分为Intel和AT&T两种具有很多差异的汇编格式,在类UNIX平台下,通常使用AT&T汇编格式,而在DOS/Windows平台下,则主要使用Intel汇编格式。

Intel汇编格式与AT&T汇编格式如下:

程序在执行时,系统在内存中会为程序创建一个虚拟的内存地址空间,在32位机上即4GB的空间大小,用于映射物理内存,并保存程序的指令和数据

Linux进程内存空间布局如下图所示,其中,3GB以下为用户态空间,3GB-4GB为内核态空间,操作系统将可执行程序加载到新创建的内存空间中,程序一般包含.text,.bss和.data三种类型的段

  • .text段包含程序指令,在内存中被映射为只读
  • .data段主要包含静态初始化的数据
  • .bss段则主要包含未经初始化的数据

是一种后进先出的数据结构,其地址空间从高地址向低地址增长,Linux程序运行的环境变量env,运行参数argv、运行参数数量argc都被放置在栈底,然后是主函数及调用栈中各个函数的临时保存信息

是一种先进先出的数据结构,用于保存程序动态分配的数据和变量,其地址空间从低地址向高地址增长。与栈是相反的

程序执行时,就会按照程序逻辑执行.text中的指令,并在堆和栈中保存和读取数据,然而程序并不能正确地区分指令和数据,所以当我们通过修改内存空间中影响程序执行逻辑的敏感位置,并将恶意数据作为指令提交给处理器时,它仍会很高兴地执行这些指令,正是冯体系的本质缺陷和软件中存在的安全漏洞,才使得破解目标程序控制其执行流程成为可能。

Windows操作系统的进程内存空间布局如下图所示:

2GB-4GB为内核态地址空间,用于映射Windows内核代码和一些核心态DLL,并用于存储一些内核态对象
0GB-2GB为用户态地址空间,高地址段映射了一些大量应用进程所共同使用的系统DLL,如kernel32.dll,User32.dll等

程序进行函数调用的过程如下:分为三个步骤,调用,序言,返回

函数调用过程代码实例:

缓冲区溢出漏洞根据缓冲区在进程内存空间中的位置不同,又分为栈溢出、堆溢出和内核溢出三种具体技术形态。

  • 栈溢出:存储在栈上的一些缓冲区变量由于存在缺乏边界保护问题,能够被溢出并修改栈上的敏感信息,从而导致程序流程的改变
  • 堆溢出:存储在堆上的缓冲区变量缺乏边界保护所遭受溢出攻击的安全问题
  • 内核溢出:由于进程内存空间内核态中存储的缓冲区变量被溢出造成的
10.3Linux平台上的栈溢出与Shellcode

Linux平台中的栈溢出攻击按照攻击数据的构造方式不同,主要有NSR,RNS,RS三种模式
NSR模式:
适用于被溢出的缓冲区变量比较大,足以容纳Shellcode的情况,其攻击数据从低地址到高地址的构造方式是一堆Nop指令(即空操作指令)之后填充Shellcode,再加上一些期望覆盖RET返回地址的跳转地址,从而构成了NSR攻击数据缓冲区
RNS模式:
一般用于被溢出的变量比较小,不足以容纳Shellcode的情况,攻击数据从低地址到高地址的构造方式是首先填充一些期望覆盖RET返回地址的跳转地址,然后是一堆Nop指令填充出着陆区,最后再是Shellcode,在溢出攻击之后,攻击数据将在RET区段即溢出了目标漏洞程序的小缓冲区,并覆盖了栈中的返回地址,然后跳转至Nop指令所构成的着陆区,并最终指向Shellcode
RS模式:
精确定位出shellcode在目标漏洞程序进程空间中的起始地址,因此也就无须引入Nop空指令构建着陆区。这种模式将shellcode放置在目标漏洞程序执行时的环境变量中,由于环境变量是位于Linux进程空间的栈底位置,因而不会受到各种变量内存分配与对齐因素的影响,其位置是固定的可通如下公式计算:

  ret=0xc0000000-sizeof(void *)-sizeof(FILENAME)-sizeof(Shellcode)

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

Linux系统中一个最简单的本地Shellcode的产生过程:
1.先用高级编程语言,通常是C,编写Shellcode程序
2.编译并反汇编调试这个SHellcode程序
3.从汇编语言代码级别分析程序执行流程
4.整理生成的汇编代码,尽量减小它的体积并使它可注入,并使得他可注入,并可通过嵌入C语言进行运行测试和调试
5.提取汇编代码所对应的opcode二进制指令,创建Shellcode指令数组

Linux远程Shellcode与之类似,不过它需要让攻击目标程序创建socket监听指定的端口等待客户端连接,启动一个命令行shell,并将命令行的输入输出与socket绑定,这样攻击者就可以通过socket客户端连接目标程序所在主机的开放端口,与服务端socket建立起通信通道,并获得远程访问shell

10.4:Windows平台上的栈溢出与Shellcode

Windows与Linux具有显著不同的实现机制,而在这些差异中,与成功攻击应用程序中栈溢出漏洞密切相关的主要有如下三点
1.对程序运行过程中废弃栈的处理方式差异
2.进程内存空间的布局差异
3.系统功能调用的实现方式差异

Windows远程Shellcode大致过程如下:
1.创建一个服务器端socket,并在指定的端口上监听;
2.通过accept()接受客户端的网络连接;
3.创建子进程,运行“cmd.exe”,启动命令行;
4.创建两个管道,命令管道将服务器端socket接收(recv)到的客户端通过网络输入的执行命令,连接至cmd.exe的标准输入;然后输出管道将cmd.exe的标准输出连接至服务器端socket的发送(send),通过网络将运行结果反馈给客户端。

10.5:堆溢出攻击

堆溢出之所以较栈溢出具有更高的难度,最重要的原因在于堆中并没有可以直接覆盖并修改指令寄存器指针的返回地址,因此往往需要利用在堆中一些会影响程序执行流程的关键变量,如函数指针、C++类对象中的虚函数表,或者挖掘出堆中进行数据操作时可能存在的向指定内存地址改写内容的漏洞机会。
攻击案例有函数指针改写,C++类对象虚函数表gaixie,Linux下堆管理glibc库free()函数本身漏洞。
C++类对象虚函数表改写:
C++类通过虚函数提供了一种Late binding运行过程绑定的机制,编译器为每个包含虚函数的类建立起虚函数表,存放虚函数的地址,并在每个类对象的内存区放入一个指向虚函数表的指针,通常称为虚函数指针vptr。对于使用了虚函数机制的C++类,如果它的类成员变量中存在可被溢出的缓冲区,那么就可以进行堆溢出攻击,通过覆盖类对象的虚函数指针,使其指向一个特殊构造的虚函数表,从而转向执行攻击者恶意注入的指令。
Linux下堆管理glibc库free()函数本身漏洞:
glibc库的free函数为我们提供了这样的机会,free函数在处理内存块回收时,需要将已被释放的空闲块和与之相邻的空闲块进行合并,因此将会把符合条件的空闲块从Bin链表中unlink摘出来,合并之后再将新的空闲块插回链表中

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

1.尝试杜绝溢出的防御技术
2.允许溢出但不让程序改变执行流程的防御技术
3.无法让攻击代码执行的防御技术

学习感想和体会

这次学习虽然没有了实践部分,但是整体来说难度还是有点大的,因为涉及到了汇编部分,所以看起来还是很吃力的。另外就是对于堆溢出以及windows平台的shellcode编写流程还是有点半知半解。

学习中遇到的问题及解决

等待补充

参考文献

原文地址:https://www.cnblogs.com/funmary/p/12837612.html