530 likes | 873 Views
北京大学计算机科学技术研究所. 缓冲区溢出攻击技术 -栈溢出攻击技术. XXX The Artemis Project 2006-5-31. 内容. 缓冲区溢出攻击简介 基本概念及发展历史 背景知识 (Linux & win32) 缓冲区溢出攻击的原理 栈溢出攻击如何工作? Linux: 栈溢出 , shellcode Win32: 栈溢出 , shellcode 总结. 缓冲区溢出攻击的威胁. 缓冲区溢出攻击的基本概念. 向缓冲区中填入过多的数据,超出边界 导致数据外溢,覆盖了相邻的内存空间 利用缓冲区溢出改写数据、改变程序执行流程
E N D
北京大学计算机科学技术研究所 缓冲区溢出攻击技术 -栈溢出攻击技术 XXX The Artemis Project 2006-5-31
内容 • 缓冲区溢出攻击简介 • 基本概念及发展历史 • 背景知识 (Linux & win32) • 缓冲区溢出攻击的原理 • 栈溢出攻击如何工作? • Linux: 栈溢出, shellcode • Win32: 栈溢出, shellcode • 总结
缓冲区溢出攻击的基本概念 • 向缓冲区中填入过多的数据,超出边界 • 导致数据外溢,覆盖了相邻的内存空间 • 利用缓冲区溢出改写数据、改变程序执行流程 • 干扰系统运行,破坏系统完全性,任意执行恶意代码
缓冲区溢出攻击的发展历史 • 1980s • Morris蠕虫-fingerd缓冲区溢出攻击 • 1996 • Aleph One, Smashing the Stack for Fun and Profit, Phrack 49 • 1998 • Dildog: 提出利用栈指针的方法完成跳转 • The Tao of Windows Buffer Overflows • 1999 • Dark Spyrit: 提出使用系统核心DLL中的Jmp ESP指令完成跳转, Phrack 55 • M. Conover: 基于堆的缓冲区溢出教程
缓冲区溢出攻击背景知识与技巧 • 编译器、调试器的使用 • Linux: gcc+gdb • Win32: VC6.0+OllyDbg • 进程内存空间结构 • 汇编语言基本知识 • 栈的基本结构 • 函数调用过程
GCC编译器基础 • 最著名的GNU的Ansi c/c++编译器 • gcc [options] [filenames] • 编译: gcc -c test.c 生成 test.o • 连接: gcc -o test test.o • 同时搞定: gcc test.c -o test • make: 用于控制编译过程 • Makefile How To
GDB调试器的使用 • 断点相关指令 • break/clear, disable/enable/delete • watch –表达式值改变时,程序中断 • 执行相关指令 • run/continue/next/step • attach –调试已运行的进程 • finish/return • 信息查看相关指令 • info reg/break/files/args/frame/functions/… • backtrace –函数调用栈 • print /f exp –显示表达式的值 • x /nfu addr –显示指定内存地址的内容 • list –列出源码 • disass func –反汇编指定函数
VC6.0命令行 • 环境变量 • 我的电脑-属性-高级-环境变量 • PATH: • C:\Program Files\Microsoft Visual Studio\VC98\Bin; • C:\Program Files\Microsoft Visual Studio\Common\MSDev98\Bin; • INCLUDE: • C:\Program Files\Microsoft Visual Studio\VC98\Include • LIB: • C:\Program Files\Microsoft Visual Studio\VC98\Lib • 命令行指令 • cl sourcefilename –编译并链接
Win32平台调试器 • OllyDbg1.10汉化版 • 32-bit assembler level analysing debugger by Oleh Yuschuk • Free • 支持插件机制 • OllyUni: 查找跳转指令功能 • Softice • IDA Pro
背景知识-进程内存空间 0xc0000000 ESP EIP 0x80000000 Linux Win32
Linux进程内存空间 • Highest zone (0xc0000000-3G) • 进程环境参数: env strings & pointers • 进程参数: argv strings & pointers, argc • 栈 • 存储函数参数、本地参数和栈状态变量 (返回地址, …) • LIFO, 向低地址增长 • 堆 • 动态分配变量 (malloc) • 向高地址增长 • .bss: uninitialized data • .data: static initialized data • .text(0x80000000): 指令, 只读数据 • Example: ./linux/memory/memory.c
Win32进程内存空间 • 系统核心内存区间 • 0xFFFFFFFF~0x80000000 (4G~2G) • 为Win32操作系统保留 • 用户内存区间 • 0x00000000~0x80000000 (2G~0G) • 堆: 动态分配变量(malloc), 向高地址增长 • 静态内存区间: 全局变量、静态变量 • 代码区间: 从0x00400000开始 • 栈: 向低地址增长 • 单线程进程: (栈底地址: 0x0012FFXXXX) • 多线程进程拥有多个堆/栈 • Example: ./win32/background/memory.c
汇编语言基础知识-汇编指令 • AT&T 格式与Intel 格式
栈的基本结构 • 栈-LIFO抽象数据结构 • 用于实现函数或过程调用 • 相关寄存器 • BP (Base Pointer) = FP (Frame Pointer): 当前栈底指针 • SP (Stack Pointer): 当前栈顶指针 • 相关操作 • PUSH: 压栈 • POP: 弹栈
函数调用过程 • 函数调用过程的三个步骤 • prologue: 保存当前的栈基址 (ebp). • call: 调用参数和返回地址(eip)压栈,跳转到函数入口 • return (or epilogue): 恢复调用者原有栈
func ebp main ebp esp esp esp esp esp esp esp esp esp esp 函数调用过程示例 Stack frame … 2 1 Ret-add ebp retVal
缓冲区溢出攻击的本源 • 没有内嵌支持的边界保护 • User funcs • Ansi C/C++: strcat(), strcpy(), sprintf(), vsprintf(), bcopy(), gets(), scanf()… • 程序员安全编程技巧和意识 • 可执行的栈(堆) • 给出Shell或执行任意的代码
栈溢出攻击的原理 • 当调用函数时 • Call指令会将返回地址(Call指令下一条指令地址)压入栈 • Ret指令会把压栈的返回地址弹给EIP • 栈溢出攻击的原理 • 通过缓冲区溢出修改栈中的返回地址 • 当函数调用返回,EIP获得被修改后的返回地址,并执行Shellcode • 挑战1: 将修改后的返回地址填到正确的位置 • 挑战2: 返回地址能正确地指向Shellcode
… Return add … Return add Return add Return add 栈溢出攻击示例 高地址 Para1 char shellcode[] = "\xeb\x1f\x……"; char large_string[128]; int main(int argc, char **argv){ char buffer[96]; int i; long *long_ptr = (long *) large_string; for (i = 0; i < 32; i++) *(long_ptr + i) = (int) buffer; for (i = 0; i < (int) strlen(shellcode); i++) large_string[i] = shellcode[i]; strcpy(buffer, large_string); return 0; } Para2 Return add Buffer (96bytes) shellcode i long_ptr 低地址
Linux系统下的栈溢出攻击 • 栈溢出攻击 • NSR模式 • NRS模式 • R.S模式 • Shellcode • 真实世界中的栈溢出攻击
NSR溢出模式 NOP … NOP NOP shellcode … RET … RET RET Low Address High Address vulnerable1.c: stackexploit1.c:
RNS溢出模式 RET … RET RET NOP … NOP NOP shellcode … Low Address High Address vulnerable2. c: stackexploit2.c:
R.S溢出模式-利用环境变量 RET … RET RET … shellcode FILE NULL 0xc0000000 Low Address High Address vulnerable2.c: stackexploit3.c:
栈溢出模式分析 • 挑战 • 溢出点 (在哪改写返回地址?) • Shellcode地址 (将返回地址改写成什么?) • NSR模式 • 最经典的方法 –Alpha One • 需要漏洞程序有足够大的缓冲区 • RNS模式 • 能够适合小缓冲区情况,更容易计算返回地址 • R.S模式 • 最新的方法: execve (filename, argv [], envp[]); • Ret = 0xc0000000 – 4 – strlen (FILENAME) – strlen (shellcode), 不需要任何NOP • 但对远程缓冲区溢出攻击不适用
Shellcode 汇编版本 shellcode_asm.c shellcode_asm_fix.c 去除’\0’
Shellcode Opcode版本 • 31 d2 xor %edx,%edx • 52 push %edx • 68 6e 2f 73 68 push $0x68732f6e • 68 2f 2f 62 69 push $0x69622f2f • 89 e3 mov %esp,%ebx • 52 push %edx • 53 push %ebx • 89 e1 mov %esp,%ecx • 8d 42 0b lea 0xb(%edx),%eax • cd 80 int $0x80
渗透防火墙的Shellcode • fcntl设置socket状态 • scz@nsfocus • 发送OOB数据 • bkbll@cnhonke • 利用ioctl函数的FIONREAD选项 • eyas@xfocus • 文件上传下载功能
在真实Linux世界中的栈溢出攻击 • 2003/09 - /usr/sbin/chat stack overflow • 寻找溢出点 • gdb /usr/sbin/chat • (gdb) run `perl -e 'print "A" x 1056'` • * eip 0x41414141 0x41414141 • (gdb) run `perl -e 'print "A" x 1000'` • Program exited with code 03. • 返回地址 • ret=0xc0000000-strlen(shellcode)-strlen("/usr/sbin/chat")-sizeof(void *); • Here we go!(实战训练) • char *env[]={"HOME=/root",shellcode,NULL}; • for(i=1000;i<1056;i+=4) • *(long *)&buf[i]=ret; • execve(p[0],p,env); • Why not root??? - (not setuid program)
Win32系统下的栈溢出攻击 • 栈溢出攻击 • 本地栈溢出示例 • 远程栈溢出攻击 • Shellcode • 真实Win32世界中的栈溢出攻击
如何利用?与Linux平台有何不同? • Win32平台与Linux平台的不同之处 • 如何进行系统调用-shellcode方式不同 • Win32: 通过调用系统DLL提供的接口函数 • Linux: 通过中断进行系统调用 • 对废弃栈的处理-NSR模式不适用于Win32 • Win32: 写入一些随机的数据 • Linux: 不进行任何处理 • 进程内存空间的分布-NRS与R.S模式同样不适用于Win32 • Win32: 栈在1G以下的用户空间,地址中有空字节 • Linux: 栈在3G(0xc0000000)附近,地址中没有空字节
Win32对废弃栈的处理 调用者的栈 • 如何以Shellcode地址覆盖返回地址? • NSR模式 R指向了Shellcode地址, 但执行“mov esp,ebp”恢复调用者栈信息时,Win32会在被废弃的栈中填入一些随机数据。 WE LOST SHELLCODE!!! 调用参数 返回地址 R SSSS SSSS NNNN NNNN Shellcode 地址 EBP EBP ESP 局部变量 %%%% %%%% %%%% ESP
如何以Shellcode地址覆盖返回地址? NRS模式 栈在1G(~0x00FFFFFF)以下 如果R直接指向Shellcode,则在R中必然含有空字节‘\0’. Shellcode将被截断 we lost shellcode AGAIN!! R.S模式 Win32平台无SUID机制,本地溢出没有意义 同样会由于R中含空字节被截断 Win32栈地址含有空字节 调用者的栈 地址R中含空字节 调用参数 SSSS Return address R Shellcode 地址 EBP NNNN NNNN 局部变量
如何解决? • 通过Jmp/Call ESP指令跳转 • 1998: Dildog-提出利用栈指针的方法完成跳转 • 1999: Dark Spyrit-提出使用系统核心DLL中的Jmp ESP指令完成跳转 • 跳转指令在哪? • OllyUni插件提供Overflow Return Address功能 • 代码页里的地址: 不受任何系统版本及SP影响,但受语言区域选择影响。 • 应用程序加载的用户DLL,取决于具体的应用程序,可能较通用。 • 系统未变的DLL,特定发行版本里不受SP影响,但不同语言版本加载基址可能会不同。
通过跳转指令执行Shellcode • 如何利用跳转指令让漏洞程序正确执行我们的Shellcode 0040100F |. E8 0C000000 CALL 00401014 |. 83C4 08 ADD ESP,8 00401017 |. 8BE5 MOV ESP,EBP 00401019 |. 5D POP EBP 0040101A \. C3 RETN NOW ESP POINTS TO SHELLCODE!! 调用者的栈 调用参数 SSSS ESP Return address R JMP ESP ESP EBP NNNN NNNN EBP ESP 局部变量 ESP
Win32远程缓冲区溢出 • 远程缓冲区溢出演示 • 缓冲区溢出漏洞服务器端程序:server.cpp • 远程缓冲区溢出攻击代码:exploit.c • 结构化异常处理-SEH • server_seh.cpp进行了异常处理,exploit时返回前异常触发,程序流进入异常处理 • SEH保存在栈中,对其进行覆盖 • XP和2003的增强异常处理-VEH向量化异常处理 • 异常处理次序: 调试器 VEH SEH • VEH保存在堆里 • David Litchfield: “Defeating the Stack Based Buffer Overflow Prevention Mechanism of Microsoft Windows 2003 Server”
Win32完整的本地Shellcode • shellcode_asm_full.c - 三个API调用过程: • LoadLibrary("msvcrt.dll"); • system("command.com"); • exit(0); • 平台相关的API入口地址 • system() and exit() • 使用LoadLibrary()和GetProcAddress() 获取其他API函数入口地址 • GetProcAddress()和LoadLibrary()的地址可以在漏洞程序的Import Address Table找到 • GetProcAddress()和LoadLibrary()的地址对于一个特定版本的Win32平台是固定的-从Kernel32.dll中获取其地址
从Kernel32.dll获取地址 • 获取Kernel32.dll加载基址 • 从PEB(进程环境块)获取 • 获取Windows API地址 • Hash算法减少API名字长度4字节 • h=((h<<25)|(h>>7))+c • 通过PE结构e_lfanew找到PE头 • PE基址偏移0x78引出表目录指针DataDirectory,其前两个元素分别对应ExportDirectory和ImportDirectory • 引出ExportDirectory中的每个函数名称,做hash计算,与原先保存的hash值进行比较,相等则找到对应API入口地址
实用的Win32 Shellcode • Xor编码消除空字节 • 给出远程连接 • Create server and listen • Accept client connection • Create a child process to run “cmd.exe” • Create two pipes and links the shell with socket • Command: Client send >> recv Server write >> pipe2 >> stdin Cmd.exe • Output: Client recv << send Server read << pipe1 << stdout Cmd.exe
穿透防火墙的Shellcode • 端口复用技术 • 重新绑定原端口 • Getpeername查找socket • 字串匹配查找socket • Hook系统的recv调用 • 文件上传下载功能的实现
真实Win32世界中的栈溢出攻击 • DCOM RPC WIN32 remote exploit by Lordy • E:\bof_codes\win32\real>dcomrpc_magickey_win -h 127.0.0.1 -t6
总结 • 缓冲区溢出攻击的基本简介 • 背景知识 • 基本概念 • 发展历史 • 基本原理 • Linux平台下的栈溢出攻击 • Win32平台下的栈溢出攻击