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

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

1.实践内容

软件安全概述

  • 大多数成功攻击都是利用盒破解已经公布单没有修补的软件安全漏洞或者不安全的配置

软件安全漏洞威胁

  • 安全漏洞大致包括两种,一种是安全策略的缺陷,还有一种是软件安全漏洞。其中目前构成安全漏洞主要部分的是软件安全漏洞。软件安全漏洞是指可以被别人利用的安全bug。

软件安全困境

  • 复杂性:现代的计算机软件越来越复杂,代码行数越来越多。而软件越复杂,相应的,产生的bug就会越来越多。一个经过严格的测试的系统大概一千行代码中会有5个bug,没有经过测试的则会更多。大多数的商业软件并没有经过严格的测试。虽然大部分的bug不会直接带来安全问题,但是只要有少数几个bug被利用,就会产生危害。

  • 可拓展性:现代软件为了优化客户体验,都会带着很多的拓展交互渠道。但是很多厂商在退出一个拓展机制的时候并不会很仔细的考虑安全权问题,总是亡羊补牢。不仅如此,即使厂商会考虑安全问题,想要分析可拓展软件的安全性是十分困难的。

  • 连通性: 互联网让全球的软件连通在一起。一个很小的软件缺陷就有可能造成大范围的损失。

软件安全漏洞类型、

  • 书中按照技术将软件安全漏洞分为如下几类

  • 内存安全违规类:软件在开发过程中对RAM储存进行访问的时候引起的安全缺陷,比如缓存区溢出。

  • 输入验证类:软件程序对用户输入进行验证的错误,没有正确的检查出用户输入的合法性、正确性、安全性。又包括和石化字符串、SQL注入、代码注入等。

  • 竞争条件类:这类缺陷是指进程的输出结果无法预知,并且依赖于其他进程的时候,所导致的错误。这使得通过对另一进程进行修改来改变某进程的状态。

  • 权限混淆和提升类:这类漏洞是指计算机由于各种原因滥用权限,或者给了别人不该给的权限。

缓冲区溢出基础概念

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

背景知识

  • 编译器与调试器:高级语言编写的源代码,需要经过编译器和连接器才可以生成在操作系统上可以直接运行的程序代码。调试器则是用于时刻调试和分析程序的工具。
汇编语言基础知识
  • 对于软件安全漏洞分析而言,一般情况下无法得到分析软件的源代码,这时候就需要在反汇编的支持下对汇编语言进行阅读。并且在渗透攻击的代码中,也包含了机器指令形式的shellcode。想要理解shellcode也需要汇编语言的知识。

  • 寄存器和相应的功能:寄存器一般分为四类:通用寄存器、段寄存器、控制寄存器和其它寄存器。通用寄存器主要用于算术运算,保存算术运算中的关键数据,如eax、ebx、ecx、edx。段寄存器一般用作段基址寄存器。控制寄存器用来控制除了力气的执行流程,最关键的是eip,储存着下一条要执行的指令的地址,所以常常受到攻击,如何修改将要输入eip的内容,以及修改为什么,是攻击的关键所在。其他寄存器还有一个eflags寄存器,用于保存标志信息。

  • 汇编格式:在IA32架构的汇编语言中,又分为Intel嗯好AT&T两种汇编格式,类unix下主要是AT&T汇编格式,dos/windos下主要是Intel汇编格式。书上也给出了一些对比,但是我不会汇编所以我都看不懂。前几天的作业中,我也是现学先用的,一知半解。

  • 进程内存管理:软件安全漏洞种最主要的就是内存安全违规类型,该漏洞利用的是堆内存中的重要数据的“改写”和“溢出”,所以掌握内存的基础知识是必须的。linux种程序在执行的时候会有一个虚拟的内存,然后用户将程序加载到内存中。程序一般又.text .bss .data三种类型的段。txt包括程序指令,在内存为一个只读文件,data包含静态的初始化的数据,bss主要是未经初始化的数据,data和bss都是可写的。加载完这些后,系统就会初始化栈和堆。栈是一个先进后出的结构,重要的参数都在栈底,堆是一个先进先出的结构,主要用于储存动态分配的数值。程序开始执行后,就会从text种读取指令,在栈和堆中读写数据。如果我们在程序的关键位置进行修改,然后将恶意指令给寄存器,就会达到恶意目的。

  • 函数调用过程:1、调用:将函数的调用参数、函数调用的指令的地址压栈,然后跳到函数。2、序言:函数开始执行后先进入序言阶段,将ebp寄存器赋值位当前的函数的地址,然后给本地函数的局部变量分配栈空间等等。3、返回:在执行完任务之后,一般会进入返回截断执行eave个ret指令恢复栈的指针,并将之前压栈的地址赋值到eip中,然后继续执行下一条指令。

缓存区溢出攻击原理

  • 缓存区溢出漏洞可以分为栈溢出、堆溢出和内核溢出。栈溢出是指储存在栈上的缓冲区变量由于缺乏边界保护,导致可以被溢出并且修改栈上的信息。如果是很重要的信息的话就会改变函数的流程。堆溢出是储存在堆上的变量由于缺乏边界保护而遭受溢出攻击的安全问题。内核溢出则是存在于一些内核和程序之中,由于内s态储存的缓冲区变量溢出导致的漏洞。

  • 书上给出了栈溢出的例子。

#include <stdio.h>
void return_input(void){
char array[30];
gets (array);
printf("%s\n",array);
int main (void)
{
return_input();
return 0;
}
  • 这是书上给出的代码,还好是用c写的,要不我就看不懂了。

  • 在执行上面的程序的时候,要用户自己手动和输入30个字节来赋值给字符数组。但是万一输入超过了30个字节,就会溢出qrrqy的缓冲区,覆盖到缓冲区上方的EBP和RET,这样就会修改两者的值。

  • 通过上面的实例可以看出,这个实例存在着两个因素。首先是不安全的操作缺乏对缓冲区边界的保护。上述用gets对30字节长度的数组进行赋值就不大行。然后,这个需要时用户可以人为的控制的,用户的输入可以影响到这个函数。如果用户不能控制,就不能被外界利用。

  • 在攻击者想要攻击的时候,就要考虑下面的问题.首先,怎么找到缓冲区溢出要覆盖的重要位置?然后,要将重要位置的数值修改成什么?为了获得控制权,需要通过修改敏感位置的值,然后让程序的下一条的指令变成攻击者想要他执行的指令。然后第三个问题,要执行什么指令?在获得控制权之后,想要实现什么功能?实现这种功能的代码被称作payload。因为一般攻击者想获得一个远程shell,所以也叫shellcode。

linux平台上的栈溢出和shellcode

linux平台栈溢出攻击技术

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

  • NSR模式:这种模式是数据先构造一堆nop空指令,然后填充想要的shellcode,再加上一些期望覆盖RST返回地址的跳转地址,就构成了NSR攻击。这种模式比较适合被溢出的缓冲区变量比较大的情况,最起码足以容纳shellcode。

  • RNS模式:首先填充一些期望覆盖RET的返回地址的跳转地址,然后再是一堆nop,最后是shellcode。这种适合溢出变量较小,不足以容纳shellcode的情况。

  • RS模式:这种情况可以精确的的知道shellcode在程序中的起始地址,所以无需大量nop。将shellcode放在环境变量中。可以使用execve函数将包含shellcode的环境变量数组直接传递到程序的进程之中,然后再攻击缓冲区填充跳到shellcode的地址。

  • linux的远程栈溢出攻击和本地的原理相同,但是用户输入的途径不同。这个也好理解,远程攻击肯定是通过网络传输的,也会利用本地的则可能是命令行输入,文件输入等。还有由于RS模式是通过本地的execve,所以这个模式不适用于远程。还有shellcode也不同,本地攻击的shellcode主要目的是提权并给出shell访问,远程的则是需要将shell与网络连接起来。

llinux平台的shellcode实现

  • shellcode是一段机器指令,在溢出之后改变程序,来执行攻击者想执行的指令,一般会给一个远程或者本地shell访问。

  • 实现机制:本地shellcode通常功能就是位攻击者提供一个命令行shell。一般通过execve()启动/bin/sh提供命令行。但是我们要注入的肯定是二进制指令形式。通过查找Intel opcode参考手册可以找到对的二进制形式的shellcode。

  • 远程shellcode的实现机制:远程shellcode上的实现机制基本一样,最终目的也是二进制指令代码。不过需要让目标程序船舰socket监听指定的端口等待链接,启动一个命令行shell,将该输入和输出与socket绑定,这样就能获得远程shell。Linux中dup2()函数可以做到将命令行的输入输出和socket进行绑定。

windows平台的栈溢出与shellcode

windows平台的栈溢出攻击技术

  • 攻击技术原理:win与linux在这里有很大不同。windows在执行完一个函数的时候,会恢复栈指针,并且还对废弃栈进行一些处理。这样使得攻击者会增加一些限制。比如windows向废弃栈随机写入数据,就可能会破坏shellcode。除此之外,win的内存空间与linux也不同,linux的栈底在0xc00000000,而栈变量一般在oxbfff附近。而win的栈位于0x00ffffff以下的用户内存空间,一般为0x0012,头两个字节都是空字节。第三是系统功能调用的实现方式。windows系统通过更复杂的API和内核处理历程调用链来完成系统功能调用。

shellcode实现技术

  • windows操做系统不提供直接的系统调用,提供API接口函数。所以需要考虑如下的问题

  • shellcode必须找到所需的API函数,生成调用表。

  • 为了能够使用这些API函数,shellcode必须找出目标程序中已经加载的函数地址,或者干脆自己加载函数库。

  • shellcode必须要清除空字节,避免在字符串函数里被截断。

  • 需要保证自己可以正常退出,然后让原来的程序继续。

  • 需要考虑应对异常机制。

本地shellcode
  • 与linux一样,也是需要一个命令行shell,即cmd。在调用system(“command.com”)就可以启动。

  • 写shellcode最简单粗暴的方法就是直接用函数地址。但是这种方法只适用特定程序特定版本的windows。

  • 为了shellcode可以调用正确的函数,一般要将目标函数的动态链接库装在到程序之中,然后查询函数地址.LoadLibrary()和GetProcAddress()函数装在动态链接库和的查询地址的功能。

远程shellcode
  • win的原理和lin一样,不过稍微复杂一些。

  • 首先创建socket,绑定在一个端口上,和linux一样

  • 用ACCEPT()接受网络连接

  • 然后创建子进程运行cmd。

  • 最后创建两个管道,一个夫原则从socket接受命令然后输入到cmd上,一个负责从cmd接受输出然后通过socket发送出去。

堆溢出攻击

  • 由于堆中内存分配和管理比较复杂,不同平台差异也不较大,所以攻击难度也更大。

  • 函数指针改写:在需要被溢出的缓冲区和全局函数的指针的储存的地址接近并且在他的低地址的时候,在没有边界控制的情况下,溢出的值就会覆盖到指针的地址,从而改写指针的指向。让他指向shellcode的入口地址就能在调用改参数的时候执行shellcode。

  • c++类对象虚函数表改写:c++提供了一种late binding的运行过程的绑定机制,为每个虚函数的类建立虚函数表、存放虚函数地址,并在每个类对象的·内存区中放入一个指向虚函数表的指针。如果类成员变量有着能被溢出攻击的缓冲区,就可以进行堆溢出攻击,覆盖虚函数指针,然后让其指向某个指定的虚函数表。

  • linux下glibc库的free()函数的本身漏洞:free()在处理内存块回收的时候,需要将空闲块与之合并进行回收,所以会在bin链表中找出符合条件的块,用完之后再放回去。那么攻击者就可以构造一个像空闲块的块。

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

尝试杜绝溢出

  • 最直接的方法就是写不存在缓冲区溢出的代码。

  • 但是很多效率优先的代码很容易就出现缓冲区溢出。因此研究出了很多工具来帮助经验不足的程序员来编写安全的程序,还有一些查错程序,但是都不能完全检查出所有漏洞。

允许溢出单不允许程序改变进程。

  • 允许溢出的产生,但是对关键的数据进行严格的保护,阻断溢出攻击。

  • 黑客们则想到了绕过的方法,包括c++中的虚函数表指针、覆盖SEH异常处理结构等方法。

无法让攻击代码执行

  • 尝试解决冯诺依曼体系的本质缺陷,通过堆栈不可执行来防御攻击。

  • 相应的,黑客也提出了return-2-libc攻击技术,不是让cpu去执行代码,而是在系统中查找已经存在的代码,然后按顺序组合。

2.实践过程

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

  • 问题1:还是基础知识的问题,对汇编知之甚少
posted @ 2020-05-05 18:04  20199103陈昱帆  阅读(209)  评论(0编辑  收藏  举报