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

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

前言

问题 解答
这个作业属于哪个课程 《网络攻防实践》
这个作业的要求在哪里 <作业要求>
我在这个课程的目标是 熟练掌握网络攻防知识,学习第十章 软件安全攻防--缓冲区溢出和Shellcode
这个作业在哪个具体方面帮助我实现目标 学习Linux操作命令以及虚拟机的应用;学习网络攻防知识,为以后研究打下基础
作业正文 如下
其他参考文献 见正文最后

1. 实践内容

1.1 软件安全概述

1.1.1 软件安全困境(三要素)

  • 复杂性:软件规模越来越大,越来越复杂,导致软件bug越来越多
  • 可扩展性:为了支持更好的客户使用感受,会提供一些扩展和交互渠道,使得安全保证更困难
  • 连通性:互联网的普及使得全球更多的软件系统联通在一起

1.1.3 软件安全漏洞类型

  • 从技术上分为以下几类:
    • 内存安全违规类
      内存安全违规类漏洞主要出现在C\C++等编程语言所编写的软件之中,由于这类语言支持任意内存的分配和归还、任意指针的转换、计算,而这类操作通常都没有内存安全保障;缓冲区溢出漏洞是一种最基础的没存问题;不安全指针是指在计算机程序中存在的,并没有指向适当类型对象的非法指针,引用这些指针会导致程序内存访问错误。
    • 输入验证类
      输入验证类安全漏洞是指软件程序在对用户输入进行数据验证存在的错误,无法保证输入数据的正确性、合法性和安全性。此漏洞被利用的方式不同,主要包含格式化字符串、XSS、SQL注入、代码注入、远程文件包含、目录遍历......
    • 竞争条件类
      竞争条件类缺陷是系统或进程中一类比较特殊的错误,通常在涉及多进程和多线程处理程序中出现
      一类竞争条件漏洞是TOCTTOU,程序检查一个谓词条件之后通过另一进程对谓词条件进行修改,使得检查时刻和使用时刻的条件状态不一致
      另一类竞争条件漏洞是符号链接竞争问题,主要是程序以不安全的方式创建文件而产生的漏洞,使得可以插入恶意用户所期望的内容。
    • 权限混淆与提升类
      权限混淆与提升漏洞是指计算机程序由于自身编程疏忽或被第三方欺骗,从而滥用其权限
      此类漏洞的具体技术主要有:Web应用程序中的跨站请求伪造、Clickjacking、FTP反弹攻击、权限提升、越狱等。

1.2 缓冲区溢出基本概念

  • 缓冲区溢出基本概念
    缓冲区溢出是计算机程序中存在的一类内存安全违规类漏洞,在计算机程序向特定缓冲区内填充数据时,超出了缓冲区本身的容量,导致外溢数据覆盖了相邻内存空间的合法数据,从而改变程序执行流程破坏系统运行完整性,通常多见于C/C++语言程序中,因为对复制的目标缓冲区普遍没有进行严格的边界安全保护。
  • 缓冲区溢出攻击背景知识
    • 编译器与调试器的使用
      对于高级编程语言编写的源码,需要通过编译器和连接器才能生成可直接在操作系统平台上运行的可执行程序代码。而调试器则是程序开发人员在运行时刻调试与分析程序行为的基本工具。对于通常使用的C/C++编程语言,最著名的编译与连接器是GCC。类UNIX平台上进行程序的调试经常使用GDB调试器。
    • 汇编语言基础知识
      汇编语言,特别是IA32(intel32位)构架下的汇编语言,是理解软件安全漏洞机理,掌握软件渗透攻击代码技术的底层基础。
      寄存器分为4类通用寄存器、段寄存器、控制寄存器和其他寄存器。通用寄存器主要用于普通的算术运算,保存数据、地址、偏移量、计数值等。段寄存器一般用作段基址寄存器。控制寄存器用来控制处理器的执行流程,其中最关键的是eip(指令指针),它保存了下一条即将执行的机器指令的地址。其他寄存器中值得关注的是“扩展标志”寄存器,由不同的标志位组成,用于保存指令执行后的状态和控制指令执行流程的标志信息。常见的汇编指令有POP,PUSH,JMP,RET等。
      类UNIX平台下,使用AT&T汇编格式,而在DOS/Windows平台下,则主要使用Intel汇编格式
  • 进程内存管理
    • LINXU操作系统中的进程内存空间布局和管理机制,程序在执行时,系统在内存中会为程序创建一个虚拟的内存地址空间,在32位机上4GB的空间大小,用于映射物理内存,并保存程序的指令和数据。内存空间3GB以下为用户态空间,3GB-4GB为内核态空间。
    • “栈”是一种后进后出的数据结构,最重要的是(临时)保存函数调用时所需要的的维护信息;其他地址空间从高地址向低地址增长。
    • “堆”则是一种先进先出的数据结构,用于保护程序分配的数据和变量,其他地址从低地址向高地址增长。

  • Windows操作系统的进程中,内存空间2GB-4GB为内核态地址空间,被用于映射Windows内核代码和一些核心态DLL,并用于存储一些内核态对象;0GB-2GB为用户态地址空间。

  • Windows操作系统的进程中,“堆”是从低地址向高地址增长,但是“栈”也是从低地址向高地址增长

  • 函数调用过程

    • 栈溢出攻击是针对函数调用过程中返回地址在栈中的存储位置,进行缓冲区溢出,从而改写返回地址,达到让处理器指令寄存器跳转至攻击者指定位置执行恶意代码的目的。
    • 是一种最基本的LIFO后进先出抽象数据结构,主要被用于实现程序中的函数或过程调用,在栈中会保存函数的调用参数、返回地址、调用者栈基址、函数本地局部变量等数据。两个与栈密切相关的寄存器为ebp和esp,分别保存当前运行函数的栈底地址和栈顶地址。两个重要指令为push和pop,分别是将数据压入栈,及将栈顶数据弹出至特定寄存器。
    • 程序进行函数调用的过程有如下三个步骤:1.调用(call):调用者将函数调用参数、函数调用下一条指令的返回地址压栈,并跳转至被调用函数入口地址。2.序言(prologue):对调用函数的栈基址进行压栈保存,并创建自身函数的栈结构。3.返回(return):通常执行leave或ret
    • 函数调用时的栈结构如下图:
  • 缓冲区溢出攻击原理:分为栈溢出、堆溢出和内核溢出这三种

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

1.3 Linux平台上的栈溢出和Shellcode

1.3.1 Linux平台上的栈溢出攻击技术

  • 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)。

1.3.2 Linux平台的shellcode实现技术

  • shellcode是一段用于利用软件漏洞而执行的代码,shellcode为16进制的机器码,因为经常让攻击者获得shell而得名。shellcode常常使用机器语言编写。 可在暂存器eip溢出后,塞入一段可让CPU执行的shellcode机器码,让电脑可以执行攻击者的任意指令,改变系统正常流程。
  • Linux本地Shellcode实现机制
    Linux系统本地Shellcode通常提供的功能就是为攻击者启动一个命令行shell。但是我们无法将C语言代码作为注入攻击负载,给目标程序执行,所以必须提供二进制指令形式存在的Shellcode(即C语言转换为汇编代码,再查表转换为opcode二进制指令代码)。
  • 本地Shellcode的产生过程
    (1)先用高级编程语言,通常用C,来编写Shellcode程序
    (2)编译并反汇编调试这个Shellcode程序
    (3)从编译语言代码级别分析程序执行流程
    (4)整理生成的汇编代码,尽量减小它的体积并使它可注入,并通过嵌入C语言进行运行测试和调试
    (5)提取汇编代码所对应的opcode二进制指令,创建Shellcode指令数组。
    在Linux本地Shellcode中,往往还会在运行execve()启动shell之前,调用setreuid(0)将程序运行权限提升至Root用户,这样才能利用本地溢出攻击来提升权限,在执行execve()函数之后还需要执行exit()函数,从而在溢出攻击之后能够使程序正常退出。

1.3.3 Linux远程Shellcode实现机制

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

1.4 Windows平台上的栈溢出和Shellcode

1.4.1 Windows平台上的栈溢出攻击技术

  • Windows操作系统平台在很多方面与Linux操作系统具有显著不同的实现机制,在这些差异中,与成功攻击应用程序中栈溢出漏洞密切相关的主要有以下三点:
    • 对程序运行过程中废弃栈的处理方式差异
      Windows平台会向废弃栈中写入一些随机的数据,而Linux则不进行任何的处理。
    • 进程内存空间的布局差异
      Linux进程内存空间中栈底指针在0xc0000000之下,在这些地址中没有空字节。Windows平台的栈位置处于0x00FFFFFF以下的用户内存空间,这些内存地址的首字节均为0x00空字节。
    • 系统功能调用的实现方式差异
      Linux系统中通过“int 80”中断处理来调用系统功能,而Windows系统则是通过操作系统中更为复杂的API及内核处理例程调用链来完成系统功能调用,对应用程序直接可见的是应用层中如Kernel32.dll\User32.dll等系统动态链接库中导出的一些系统API接口函数。

1.4.2 Windows平台Shellcode实现技术

  • 由于Windows操作系统并不提供直接的系统调用,而是提供一系列的API接口函数,因此Windows平台上的Shellcode实现比Linux系统具有一些差异。

  • 对于Windows中的Shellcode,我们还需考虑如下问题:

    • shellcode必须可以找到所需要的Windows32 API函数,并生成函数调用表
    • 为了能够使用API函数,shellcode必须找出目标程序已加载的函数地址,或者需自行加载所需函数库
    • shellcode需考虑消除空字节,以免在字符串操作函数中被截断,如果目标程序有进一步过滤规则,shellcode在编写时还需添加一些编码机制,以便顺利通过过滤器。
    • shellcode需确保自己可以正常退出,并使原来的目标程序进程继续运行或终止
    • 在目标系统环境存在异常处理和安全防护机制时,shellcode需进一步考虑如何应对这些机制
  • Windows本地Shellcode:启动一个命令行Shell,即“command.com”或者“cmd.exe”。Windows32的系统API中提供了system()函数调用,故可以调用system(“command.com”)之后即可启动命令行程序。

  • Windows远程Shellcode

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

1.5 堆溢出攻击

  • 堆溢出攻击
    堆溢出是缓冲区溢出中第二种类型的攻击方式,由于堆中的内存分配与管理机制较栈更复杂,不同操作系统平台的实现机制具有显著的差异。
    堆溢出比栈溢出难,是因为堆溢出并没有直接覆盖并修改指令寄存器指针的返回指针,而是利用堆中一些会影响程序执行流程的关键变量,如函数指针、C++类对象中的虚函数表,或者挖掘堆中进行数据操作时可能存在的向指定内存地址改写内容的漏洞机会。
    • 函数指针改写
      在没有边界判断和控制的话,缓冲区溢出之后就覆盖函数指针所在的内存区,从而改写函数指针的指向地址,当攻击者将函数指针地址指向恶意的shellcode入口地址,在程序使用函数指针调用原来期望的函数时,就会转向执行恶意的shellcode。
    • 对象虚函数表改写
      C++类通过虚函数提供了一种Late binding运行过程绑定的机制,编译器为每个包含虚函数的类建立起虚函数表(vtable)、存放虚函数的地址,并在每个类对象的内存区中放入一个指向虚函数表的指针,通常被称为虚函数指针vptr。

对于使用了虚函数机制的C++类,如果它的类成员变量中存在可被溢出的缓冲区,那么就可以进行堆溢出攻击,通过覆盖类对象的虚函数指针,使其指向一个特殊构造的虚函数表,从而转向执行攻击者恶意注入的指令。

  • Linux下堆管理glibc库free()函数本身漏洞(理解起来有点晕。。。。。)
    Linux操作系统中的对管理是通过gilc库实现
    unlink me内存块结构

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

  • 缓冲区溢出攻击的防御技术:要是缓冲区溢出攻击奏效,攻击者通常先通过溢出植入攻击代码,然后通过修改关键数据结构改变执行流程,最后让攻击代码执行。

  • 尝试杜绝溢出的防御技术
    (解决缓冲区溢出攻击最根本的方法就是编写正确的、不存在缓冲区溢出安全漏洞的软件代码)
    研究人员开发一些工具和技术帮助程序员编写安全的程序。还有一些分析工具来侦测缓冲区漏洞是否存在。另外还有在编译器上引入针对缓冲区的边界保护检查机制。

  • 允许溢出但不让程序改变执行流程的防御技术
    允许溢出发生,但不让程序改变其执行流程,对编译器gcc加补丁,使得函数入口处自动地在栈返回地址前面生成一个"Cannry"(金丝雀)检测标记,在函数调用结束后检测这个标记是否发生改变,以此来阻止溢出改变返回地址,从而阻止缓冲区溢出攻击。

  • 无法让攻击代码执行的防御技术
    解决冯若依曼体系的本质缺陷,通过堆栈不可执行限制来防御缓冲区溢出攻击

2.实践过程

3.学习中遇到的问题及解决

问题1:但是信息安全的网课里面有一章节,有讲到栈溢出的知识还有汇编指令啊,但也就是简单的了解。没有学习过汇编和linux,故对于某些知识点的理解有点晕。

4.实践总结

参考资料

posted on 2020-05-06 22:26  20199116xjq  阅读(203)  评论(0编辑  收藏  举报