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

前言

这个作业属于哪个课程:https://edu.cnblogs.com/campus/besti/19attackdefense
这个作业的要求在哪里:https://edu.cnblogs.com/campus/besti/19attackdefense/homework/10723
我在这个课程的目标是:学习网络攻防技术相关知识,掌握相关技能。
这个作业在哪个具体方面帮助我实现目标 :初步学习缓冲区溢出和ShellCode。

1.实践内容

1.1 软件安全概述

1.1.1 软件安全漏洞威胁
  • NIST定义安全漏洞:在系统安全流程、设计、实现或内部控制中所存在的缺陷或弱点,能够被攻击者所利用并导致安全侵害或对系统安全策略的违反。
1.1.2 软件安全困境
  • 复杂性:软件规模越来越大,越来越复杂,也就意味着软件的bug会越来越多。每千行代码中的bug数量会在5~50个之间。
  • 可扩展性:为了支持更加优化的软件架构、支持更好的客户使用感受提供的扩展和交互渠道给攻击者和恶意代码提供了不可预测的扩展方式入侵手段。
  • 连通性:高度的连通性使得极小的软件缺陷也可能造成非常大的影响,也使不需要人为干涉的自动化攻击成为可能
1.1.3 软件安全漏洞类型

  • 内存安全违规类:内存安全违规类漏洞是在软件开发过程中在处理RAM(random-access memory)内存访问时所引入的安全缺陷。C/C++等编程语言由于支持任意的内存分配与归还、任意的指针计算、转换而没有保护确保内存安全导致容易产生此类漏洞。
    • 不安全指针:指在计算机程序中存在的并没有指向适当类型对象的非法指针,对其引用时往往发生不可预期的后果导致程序内存访问错误。
  • 输入验证类:指软件程序在对用户输入进行数据验证存在的错误,没有保证输入数据的正确性、合法性和安全性,从而导致可能被恶意攻击与利用。其包含:格式化字符串、SQL注入、代码注入、远程文件包含、目录遍历、XSS、HTTP Header注入、HTTP响应分割错误。
  • 竞争条件类:通常在涉及多进程或多线程处理的程序中出现,是指处理进程的输出或结果无法预测,并依赖与其他进程事件发生的次序或时间时,所导致的错误。
  • 权限混淆与提升类:指计算机程序由于自身编程疏忽或被第三方欺骗,从而滥用其权限,或赋予第三方不该给予的权限。具体技术形式有:Web应用程序中的跨站请求伪造(Cross-Site Request Forgery,CSRF)、Clickjacking、FTP反弹攻击、权限提升、“越狱”(jailbreak)等

1.2 缓冲区溢出基础概念

1.2.1 缓冲区溢出的基本概念与发展过程
  • 基本概念:在计算机程序向特定缓冲区内填充数据时,超出了缓冲区本身的容量,导致外溢数据覆盖了相邻内存空间的合法数据,从而改变程序执行流程破坏系统运行完整性。
1.2.2 缓冲区溢出攻击背景知识
  • 编译器与调试器的使用

    C/C++编程语言常用GCC。

    • 使用gcc -c test.c进行源码编译生成test.o

    • 使用gcc -o test test.o生成test可执行程序

    • 或使用gcc test.c -o test同时完成编译和连接

    类UNIX平台上进行程序的调试经常用GDB调试器

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

    Windows平台使用Visual Studio、VS.Net、VC++等

  • 汇编语言基础知识

​ 除了IA32架构寄存器,还需要熟悉常用汇编指令,Intel和AT&T两种汇编格式,类UNIX平台常用AT&T,DOS/WIndows平台常用Intel

  • 进程内存管理

  • 函数调用过程

    程序进行函数调用的过程有三个步骤:

    • 调用(call):调用者将函数调用参数、函数调用下一条指令的返回地址压栈,并跳转至被调用函数入口地址
    • 序言(prologue):被调用函数开始执行首先会进入序言阶段,将对调用函数的栈基址进行压栈保存,并创建自身函数的栈结构具体包括将ebp寄存器赋值为当前栈基址,为本地函数局部变量分配栈地址空间,更新esp寄存器为当前栈顶指针等。
    • 返回(return):被调用函数执行完功能将指令控制权返回给调用者之前,会进行返回阶段的操作,通常执行leave和ret命令,及恢复调用者的栈顶与栈底指针,并将之前压栈的返回地址装载至指令寄存器eip中,继续执行调用者在函数调用之后的下一条指令。
1.2.3 缓冲区溢出攻击原理
  • 缓冲区溢出漏洞根据缓冲区在进程内存空间中的位置不同,分为栈溢出、堆溢出和内核溢出三种具体技术形态

    • 栈溢出:存储在栈上的一些缓冲区变量由于存在缺乏边界保护问题,能够被溢出并修改栈上的敏感信息(通常是返回地址),从而导致程序流程的改变。

    • 堆溢出:存储在堆上的一些缓冲区变量缺乏边界保护所遭受溢出攻击的安全问题。

    • 内核溢出:存在于一些内核模块或程序中,由于进程内存空间内核态中存储的缓冲区变量被溢出造成。

1.3 Linux平台上的栈溢出与Shellcode

1.3.1 Linux平台栈溢出攻击技术

Linux平台中的栈溢出攻击按照攻击数据的构造方式不同,主要有NSR、RNS和RS三种模式

  • NSR模式

    适用于被溢出的缓冲区变量比较大,足以容纳Shellcode的情况,其攻击数据从低地址到高地址的构造方式是一堆Nop指令之后填充Shellcode,再加上期望覆盖RET返回地址的跳转地址,从而构成NSR攻击数据缓冲区(Nop-Shellcode-Ret)。

  • RNS模式

    用于被溢出的变量比较小,不足以容纳Shellcode的情况。攻击数据构造方式为RET-Nop-Shellcode。

  • RS模式

    其能够精确定位出Shellcode在目标漏洞程序进程空间中的起始地址,因此无需引入Nop。这种模式将Shellcode放置在目标漏洞程序执行时的环境变量中,由于环境变量位于栈底,其位置固定,计算公式:

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

1.3.2 Linux平台的Shellcode实现技术
  • Linux本地Shellcode实现机制

    Shellcode的通用方法包括5个步骤:

    • 先用高级编程语言编写Shellcode程序
    • 编译并反汇编调试这个Shellcode程序
    • 从汇编语言代码级别分析程序执行流程
    • 整理生成的汇编代码,尽量减小它的体积并使它可注入,并可通过嵌入C语言进行运行测试和调试
    • 提取汇编代码所对应的opcode二进制指令,创建Shellcode指令数组
  • Linux远程Shellcode实现机制与本地Shellcode完全一致

1.4 Windows平台上的栈溢出与Shellcode

1.4.1 Windows平台栈溢出攻击技术
  • Windows平台栈溢出攻击技术机理

    Windows操作系统平台在主要三点上与Linux操作系统具有显著不同的实现机制:

    • 对程序运行过程中废弃栈的处理方式差异:Windows平台会向废弃栈中写入一些随机数据,而Linux不进行任何处理
    • 进程内存空间的布局差异:Windows平台的栈位置处于0x00FFFFFF以下的用户内存空间,而这些内存地址的首字节均为0x00空字节
    • 系统功能调用的实现方式差异:Windows系统通过操作系统中更为复杂的API及内核处理例程调用链来完成系统功能调用,攻击者注入执行的Shellcode在编写时就需要考虑系统功能调用方式的区别。
  • 远程栈溢出攻击示例:

1.4.2 Windows平台Shellcode实现技术
  • 为了使Windows中的Shellcode能够调用操作系统功能以完成攻击目标,并能够在期望注入的不同目标程序中正常运行,需要考虑以下问题:

    • (1)Shellcode必须可以找到所需的Windows32API函数,并生成函数调用表
    • (2)为了能够使用这些API函数,Shellcode必须找出目标程序已加载的函数地址,或者需自行加载所需函数库,获得所需函数在目标程序进程内存空间中的加载地址,并获得他们在目标程序进程内存空间中的加载地址
    • (3)Shellcode需考虑消除空字节,以避免在字符串操作函数中被截断,如果目标程序有更进一步的过滤规则,那么Shellcode在编写时需添加一些编码机制,以顺利通过过滤器
    • (4)Shellcode需确保自己可以正常退出,并使原来的目标程序进程继续运行或终止
    • (5)在目标系统环境存在异常处理和安全防护机制时,Shellcode需进一步考虑如何对抗这些机制
  • Windows本地Shellcode

    编写Shellcode最简单的方式就是使用硬编码的函数地址,比如system()函数在WindowsXP特定版本的目标程序内存空间中的加载地址为0x77bf93c7,那么在Shellcode中使用“Call 0x77bf93c7”指令,让EIP指令寄存器跳转至硬编码的函数入口地址执行。当然由于system()函数是由msvcrt.dll动态链接库所导出的API函数,而攻击目标程序并不一定加载了该DLL,所以一般需要将所需函数的动态链接库装载至目标程序内存中再查询获得该函数的加载地址。

  • Windows远程Shellcode

    Windows远程Shellcode的C语言实现示例代码大致过程如下:

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

1.5 堆溢出攻击

  • 函数指针改写:此攻击需要被溢出的缓冲区临近全局函数指针存储地址,且在其低地址方向。此时如果没有边界判断和控制的话,那么缓冲区溢出后就会自然地覆盖函数指针所在的内存区,从而改写函数指针的指向地址。

  • C++类对象虚函数表改写:编译器为每个包含虚函数的类建立起虚函数表(vtable)、存放虚函数地址,并在每个类对象的内存区放入一个指向虚函数表的指针vptr。对于使用了虚函数机制的C++类,如果它的类成员变量中存在可被溢出的缓冲区,那么就可以进行堆溢出攻击,通过覆盖类对象的虚函数指针,使其指向一个特殊构造的虚函数表,从而转向执行攻击者恶意注入的指令。

  • Linux下堆管理glibc库free()函数本身漏洞:dlmalloc实现的glibc库中的内存块结构如下:

    free()函数在处理内存块回收时,需要将已被释放的空闲块和与之相邻的空闲块进行合并,因此将会把符合条件的空闲块从Bin链表中unlink摘出来,合并之后再将新的空闲块插回链表中。

    如上图,glibc库会把其视作空闲块,试图寻找与这个内存块相邻的空闲块,使用当前空闲块的地址加上当前块的大小来定位后续内存块,此时将会获得sizeA作为下一内存块的开始,然后检测获得下一内存块的PREV_INUSE位,由于所有size都设置了该位,所以将会认为前一个内存块已分配,不会合并;最后检查伪造的下一内存块是否空闲时,使用它的size寻找它的下一块内存块,这里将获得sizeB,检查它的PREV_INUSE位,由于没有置位,所以将会对当前内存块进行unlink操作,试图与前面的空闲块进行合并,当unlink宏被调用时,在“what”位置的值将覆盖到“where”位置上。

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

  • 尝试杜绝溢出的防御技术:使用高级查错程序如fault injection,通过Fuzz注入测试来寻找代码安全漏洞;在编译器上引入针对缓冲区的边界保护检查机制如Jones&Kelly针对gcc的数组边界检查;Compaq C通过对编译器进行改进而杜绝溢出产生;LibSafe函数库修改了不安全的C标准库函数,加入缓冲区边界检查等
  • 允许溢出但不让程序改变执行流程的防御技术:如StackGuard提出对编译器gcc加补丁,使得在函数入口处自动地在栈中返回地址的前面生成一个“Canary”检测标记,在函数调用结束检测该标记是否改变来阻止溢出改变返回地址,从而阻止缓冲区溢出攻击。
  • 无法让攻击代码执行的防御技术:通过栈堆不可执行限制来防御缓冲区溢出攻击。如IA64、AMD64、Alpha等新的CPU硬件体系框架都引入对基于硬件NX保护机制,从硬件上支持对特定内存也设置成不可执行,Windows XP SP2、Linux内核2.6及以后版本都支持硬件NX保护机制,与操作系统配合来提升系统安全性。

2.实践总结

本章初步学习缓冲区溢出和ShellCode,根据教程对于缓冲区溢出攻击有一定了解。

posted on 2020-05-06 22:21  符原  阅读(200)  评论(0编辑  收藏  举报