如何编写自己的缓冲区溢出利用程序_第1页
如何编写自己的缓冲区溢出利用程序_第2页
如何编写自己的缓冲区溢出利用程序_第3页
如何编写自己的缓冲区溢出利用程序_第4页
如何编写自己的缓冲区溢出利用程序_第5页
已阅读5页,还剩7页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

如何编写自己的缓冲区溢出利用程序如何编写自己的缓冲区溢出利用程序 作者 黑猫黑猫 virtualcat virtualcat 文章出自 内容 本文主要讲解有关 Buffer Overflow 的原理 以及结合实战范例介绍 Linux 和 Solaris 下的漏洞利 用 本文并不介绍如何编写 shell code 要求 读者要有一点 C 和汇编语言基础 目标 希望本文能够尽量做到通熟易懂 使得稍有计算机基础知识的朋友看后能够亲自动手写自己的 Exploit 如果你觉得自己对这些都懂了 就请不要再往下看了 第一部份 概述篇 1 Buffer overflow 是如何产生的 所谓 Buffer overflow 中文译为缓冲区溢出 顾名思意 就是说所用的缓冲区太小了 以至装不下 那么多的东西 多出来的东西跑出来了 就好象是水缸装不了那么多的水 硬倒太多会溢出来一样 那么 在编程过程中为什么要用到 buffer 缓冲区 呢 简单的回答就是做为数据处理的中转站 2 UNIX 下 C 语言函数调用的机制及缓冲区溢出的利用 1 进程在内存中的影像 我们假设现在有一个程序 它的函数调用顺序如下 main func 1 func 2 func 3 即 主函数 main 调用函数 func 1 函数 func 1 调用函数 func 2 函数 func 2 调用函数 func 3 当程序被操作系统调入内存运行 其相对应的进程在内存中的影像如下图所示 内存高址 省略了一些我们不需要关心的区 env strings 环境变量字串 argv strings 命令行字串 env pointers 环境变量指针 SHELL 的环境变量和命令行参数保存区 argv pointers 命令行参数指针 argc 命令行参数个数 main 函数的栈帧 func 1 函数的栈帧 func 2 函数的栈帧 func 3 函数的栈帧 Stack 栈 Heap 堆 Uninitialised BSS data 非初始化数据 BSS 区 Initialised data 初始化数据区 Text 文本区 内存低址 这里需要说明的是 i 随着函数调用层数的增加 函数栈帧是一块块地向内存低地址方向延伸的 随着进程中函数调用层数的减少 即各函数调用的返回 栈帧会一块块地 被遗弃而向内存的高址方向回缩 各函数的栈帧大小随着函数的性质的不同而不等 由函数的局部变量的数目决定 ii 进程对内存的动态申请是发生在 Heap 堆 里的 也就是说 随着系统动态分 配给进程的内存数量的增加 Heap 堆 有可能向高址或低址延伸 依赖于不 同 CPU 的实现 但一般来说是向内存的高地址方向增长的 iii 在 BSS 数据或者 Stack 栈 的增长耗尽了系统分配给进程的自由内存的情况下 进程将会被阻塞 重新被操作系统用更大的内存模块来调度运行 虽然和 exploit 没有关系 但是知道一下还是有好处的 iv 函数的栈帧里包含了函数的参数 至于被调用函数的参数是放在调用函数的栈 帧还是被调用函数栈帧 则依赖于不同系统的实现 它的局部变量以及恢复调用该函数的函数的栈帧 也就是前一个栈帧 所需要的 数据 其中包含了调用函数的下一条执行指令的地址 v 非初始化数据 BSS 区用于存放程序的静态变量 这部分内存都是被初始化为零的 初始化数据区用于存放可执行文件里的初始化数据 这两个区统称为数据区 vi Text 文本区 是个只读区 任何尝试对该区的写操作会导致段违法出错 文本区 是被多个运行该可执行文件的进程所共享的 文本区存放了程序的代码 2 函数的栈帧 函数调用时所建立的栈帧包含了下面的信息 i 函数的返回地址 返回地址是存放在调用函数的栈帧还是被调用函数的栈帧里 取决于不同系统的实现 ii 调用函数的栈帧信息 即栈顶和栈底 iii 为函数的局部变量分配的空间 iv 为被调用函数的参数分配的空间 取决于不同系统的实现 3 缓冲区溢出的利用 从函数的栈帧结构可以看出 由于函数的局部变量的内存分配是发生在栈帧里的 所以如果我们在某一个函数里定义 了缓冲区变量 则这个缓冲区变量所占用的内存空间是在该函数被调用时所建立的栈帧里 由于对缓冲区的潜在操作 比如字串的复制 都是从内存低址到高址的 而内存中所保存 的函数调用返回地址往往就在该缓冲区的上方 高地址 这是由于栈的特性决定的 这 就为复盖函数的返回地址提供了条件 当我们有机会用大于目标缓冲区大小的内容来向 缓冲区进行填充时 就有可以改写函数保存在函数栈帧中的返回地址 从而使程序的执 行流程随着我们的意图而转移 换句话来说 进程接受了我们的控制 我们可以让进程 改变原来的执行流程 去执行我们准备好的代码 这是冯 诺曼计算机体系结构的缺陷 下面是缓冲区溢出利用的示意图 i 函数对字串缓冲区的操作 方向一般都是从内存低址向高址的 如 strcpy s AAA s s 1 s 2 s 3 内存低址 A A A A 内存高址 ii 函数返回地址的复盖 内存高址 调用函数栈帧 0 x41414141 0 x41414141 调用函数的返回地址 s 8 0 x41414141 s 4 被调用函数栈帧 0 x41414141 s 0 x41414141 内存低址 注 字符 A 的十六进制 ASCII 码值为 0 x41 iii 从上图可以看出 如果我们用的是进程可以访问的某个地址而不是 0 x41414141 来改写调用函数的返回地址 而这个地址正好是我们准备好的代码的入口 那么 进程将会执行我们的代码 否则 如果用的是进程无法访问的段的地址 将会导 致进程崩馈 Segment Fault Core dumped 段出错内核转储 如果该地址处有 无效的机器指令数据 将会导致非法指令 Illigal Instruction 错误 等等 4 缓冲区在 Heap 堆 区或 BBS 区的情况 i 如果缓冲区的内存空间是在函数里通过动态申请得到的 如 用 malloc 函数申请 那 么在函数的栈帧中只是分配了存放指向 Heap 堆 中相应申请到的内存空间的指针 这种 情况下 溢出是发生在 Heap 堆中的 想要复盖相应的函数返回地址 看来几乎是不可 能的 这种情况的利用可能性要看具体情形 但不是不可能的 ii 如果缓冲区在函数中定义为静态 static 则缓冲区内存空间的位置在非初始化 BBS 区 和在 Heap 堆 中的情况差不多 利用是可能的 但还有一种特姝情况 就是可以利用它来 复盖函数指针 让进程后来调用相应的函数变成调用我们所指定的代码 3 从缓冲区溢出的利用可以得到什么 从上文我们看到 缓冲区溢出的利用可以使我们能够改写相关内存的内容及函数的返回地址 从而 改变代码的执行流程 让进程去执行我们准备好的代码 但是 进程是以我们当前登录的用户身份来运行的 能够执行我们准备好的代码又怎样呢 我们还 是无法突破系统对当前用户的权限设置 无法干超越权限的事 换句话来说 要想利用缓冲区溢出得到更高的权限 我们还得利用系统的一些特性 对于 UNIX 来讲 有两个特性可以利用 i SUID 及 SGID 程序 UNIX 是允许其他用户可以以某个可执行文件的文件拥有者的用户 ID 或用户组 ID 的身份来执行该 文件的 这是通过设置该可执行文件的文件属性为 SUID 或 SGID 来实现的 也就是说如果某个可执行文件被设了 SUID 或 SGID 那么当系统中其他用户执行该文件时就相当 于以该文件属主的用户或用户组身份来执行该文件 如果某个可执行文件的属主是 root 而这个文件被设了 SUID 那么如果该可执行文件存在可利 用的缓冲区溢出漏洞 我们就可以利用它来以 root 的身份执行我们准备好的代码 没有比让它 为我们产生一个具有超级用户 root 身份的 SHELL 更吸引人了 是不是 ii 各种端口守护 服务 进程 UNIX 中有不少守护 服务 进程是以 root 的身份运行的 如果这些程序存在可利用的缓冲区溢出 那么我们就可以让它们以当前运行的用户身份 root 去执行我们准备被好的代码 由于守护进程已经以 root 的身份在运行 我们并不需要相对应的可执行文件为 SUID 或 SGID 属性 又由于此类利用通常是从远程机器上向目标机器上的端口发送有恶意的数据造成的 所以叫做 远程溢出 利用 4 一个有问题的程序 以下例程纯属虚构 如有雷同 纯属巧合 文件名 p c 编译 gcc o p p c include void vulFunc char s char buf 10 strcpy buf s printf String s n buf main int argc char argv if argc 2 vulFunc argv 1 else printf Usage s n argv 0 这个例程接受用户在命令行的字串输入 然后在标准输出 屏幕 上打印出来 我们可以看出在 vulFunc 这个函数里 定义了一个最多可以装十个字符的缓冲区 buf 如果我们在命令行输入 小于等于十个字符的字串 则一切都很正常 但是 如果我们输入的字串长度大于十呢 情况 会怎样 缓冲区太小装不下了 所以溢出了 答案有待于具体分析一下才知道 对于这个程序在不同操作系统下的分析和模拟攻击 请看第二部份基楚篇 第二部份 基楚篇 5 Linux x86 平台 本文使用了如下 Linux 平台 Red Hat Linux release 6 2 Zoot Kernel 2 2 14 12 on an i586 所使用的编译器及版本 bash gcc v Reading specs from usr lib gcc lib i386 redhat linux egcs 2 91 66 specs gcc version egcs 2 91 66 19990314 Linux egcs 1 1 2 release 注意 不同版本的编译器编译相同代码所生成的机器指令可能不同 1 例程 p c 在 Linux x86 平台下的剖析 i 首先我们编译 p c 并用 gdb 对相关函数进行反汇编 结果见如下清单 bash gcc o p p c bash gdb p GNU gdb 19991004 Copyright 1998 Free Software Foundation Inc GDB is free software covered by the GNU General Public License and you are welcome to change it and or distribute copies of it under certain conditions Type show copying to see the conditions There is absolutely no warranty for GDB Type show warranty for details This GDB was configured as i386 redhat linux gdb disas main Dump of assembler code for function main 0 x804842c push ebp 0 x804842d mov esp ebp 0 x804842f cmpl 0 x2 0 x8 ebp 0 x8048433 jne 0 x8048448 0 x8048435 mov 0 xc ebp eax 0 x8048438 add 0 x4 eax 0 x804843b mov eax edx 0 x804843d push edx 0 x804843e call 0 x8048400 0 x8048443 add 0 x4 esp 0 x8048446 jmp 0 x804845b 0 x8048448 mov 0 xc ebp eax 0 x804844b mov eax edx 0 x804844d push edx 0 x804844e push 0 x80484bb 0 x8048453 call 0 x8048330 0 x8048458 add 0 x8 esp 0 x804845b leave 0 x804845c ret 0 x804845d nop 0 x804845e nop 0 x804845f nop End of assembler dump gdb disas vulFunc Dump of assembler code for function vulFunc 0 x8048400 push ebp 0 x8048401 mov esp ebp 0 x8048403 sub 0 xc esp 0 x8048406 mov 0 x8 ebp eax 0 x8048409 push eax 0 x804840a lea 0 xfffffff4 ebp eax 0 x804840d push eax 0 x804840e call 0 x8048340 0 x8048413 add 0 x8 esp 0 x8048416 lea 0 xfffffff4 ebp eax 0 x8048419 push eax 0 x804841a push 0 x80484b0 0 x804841f call 0 x8048330 0 x8048424 add 0 x8 esp 0 x8048427 leave 0 x8048428 ret 0 x8048429 lea 0 x0 esi esi End of assembler dump 这里我们只对所关心的 main 和 vulFunc 两个函数进行反汇编分析 ii 进程的运行及其在内存中的情况分析 我们用 gdb 来跟踪看看进程是如何在内存中运行的 首先把程序调入 bash gdb p GNU gdb 19991004 Copyright 1998 Free Software Foundation Inc GDB is free software covered by the GNU General Public License and you are welcome to change it and or distribute copies of it under certain conditions Type show copying to see the conditions There is absolutely no warranty for GDB Type show warranty for details This GDB was configured as i386 redhat linux gdb 把断点设到 main 的第一条可执行汇编指令上 gdb b 0 x804842c Breakpoint 1 at 0 x804842c 运行程序 gdb r AAAAAAAA Starting program home vcat p AAAAAAAA Breakpoint 1 0 x804842c in main 在断点处停下来了 看一下这时各寄存器的值 gdb i reg eax 0 x4010b3f8 1074836472 ecx 0 x804842c 134513708 edx 0 x4010d098 1074843800 ebx 0 x4010c1ec 1074840044 esp 0 xbffff6bc 1073744196 ebp 0 xbffff6d8 1073744168 esi 0 x4000ae60 1073786464 edi 0 xbffff704 1073744124 eip 0 x804842c 134513708 eflags 0 x246 582 cs 0 x23 35 ss 0 x2b 43 ds 0 x2b 43 es 0 x2b 43 fs 0 x0 0 gs 0 x0 0 cwd 0 xffff037f 64641 swd 0 xffff0000 65536 twd 0 xffffffff 1 fip 0 x40034d70 1073958256 fcs 0 x35d0023 56426531 fopo 0 xbfffe400 1073748992 fos 0 xffff002b 65493 我们这里关心的是栈底 ebp 栈顶 esp 及指令寄存器 eip 此时 ebp 的值为 0 xbffff6d8 esp 的值为 0 xbffff6bc 相差 28 个字节 eip 的值为 0 x804842c 正好是我们所设的断点 注 这里的值可能会随着程序运行在不同的系统环境而不同 我们再看看当前栈帧里有什么内容 gdb x 8x esp 0 xbffff6bc 0 x400349cb 0 x00000002 0 xbffff704 0 xbffff710 0 xbffff6cc 0 x40013868 0 x00000002 0 x08048350 0 x00000000 也就是说 main 函数刚被调用时进程在内存中的相关部份的影像是这样的 内存高址 00000000 0 xbffff6d8 ebp 调用 main 函数前的 ebp 08048350 00000002 40013868 bffff710 bffff704 00000002 400349cb 0 xbffff6bc esp 调用 main 函数前的 esp 内存低址 我们看看接下来的指令做了些什么 0 x804842c push ebp esp 的值等于 esp 4 因为 ebp 是 32 位 把 ebp 的值放入 esp 所指的 32 位内存单 元 注 这里保存栈底 0 x804842d mov esp ebp ebp 的值等于 esp 的值 注 这里把原来 的栈顶做为新的栈底 运行这两条指令 然后看一下寄存器内容和栈的情况 gdb si 0 x804842d in main gdb si 0 x804842f in main gdb i reg eax 0 x4010b3f8 1074836472 ecx 0 x804842c 134513708 edx 0 x4010d098 1074843800 ebx 0 x4010c1ec 1074840044 esp 0 xbffff6b8 1073744200 ebp 0 xbffff6b8 1073744200 esi 0 x4000ae60 1073786464 edi 0 xbffff704 1073744124 eip 0 x804842f 134513711 eflags 0 x346 838 cs 0 x23 35 ss 0 x2b 43 ds 0 x2b 43 es 0 x2b 43 fs 0 x0 0 gs 0 x0 0 cwd 0 xffff037f 64641 swd 0 xffff0000 65536 twd 0 xffffffff 1 fip 0 x40034d70 1073958256 fcs 0 x35d0023 56426531 fopo 0 xbfffe400 1073748992 fos 0 xffff002b 65493 gdb x 9x esp 0 xbffff6b8 0 xbffff6d8 0 x400349cb 0 x00000002 0 xbffff704 0 xbffff6c8 0 xbffff710 0 x40013868 0 x00000002 0 x08048350 0 xbffff6d8 0 x00000000 此时进程的相关影像为 内存高址 00000000 0 xbffff6d8 调用 main 函数前的 ebp 08048350 00000002 40013868 bffff710 bffff704 00000002 400349cb 0 xbffff6bc 调用 main 函数前的 esp bffff6d8 0 xbffff6b8 ebp esp 内存低址 接下来的两条指令 0 x804842f cmpl 0 x2 0 x8 ebp 2 和 ebp 8 所指向的内存 32 位 4 个字节 里面所放的内容比较 0 x8048433 jne 0 x8048448 如果不等则跳到 0 x08048448 地 址 处继续执行 否则执行下条指 令 这里我们可以看到这是 C 语言语句 if argc 2 else 的等价汇编语句 内存地址 ebp 8 处存放的是 argc 的值 gdb x x ebp 8 0 xbffff6c0 0 x00000002 我们来看看在调用 vulFunc 函数前的指令 0 x

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论