缓冲区溢出和Shellcode
1. 实践内容
1.1 软件安全概述
- 安全漏洞的基本元素:系统的脆弱性,对缺陷的可访问性,对缺陷的可利用性。
- 软件安全困境三要素:复杂性,可扩展性,连通性。
- 软件安全漏洞技术分类:
- 内存安全违规类 :不安全指针等。
- 输入验证类 :SQL注入,格式化字符串等。
- 竞争条件类 :进程间通信的同异步问题?
- 权限混淆与提升类 :越狱,FTP反弹攻击等。
1.2 缓冲区溢出基础概念
- 缓冲区溢出基本概念 :内存安全违规类错误;C/C++程序在访问内存时未进行边界检查,可能导致缓冲区溢出,从而修改或覆盖相邻存储的关键指令,使得一些恶意指令攻击得以执行。
- 缓冲区溢出攻击背景知识 :
- gcc编译。
- gdb调试。
- 汇编语言基础:
- 寄存器:通用寄存器(eax 累加, ebx栈底指针, ecx 计数, edx除法)、段寄存器(存储段基址)、控制寄存器(指令指针寄存器,程序计数器?)和其他寄存器(扩展标志寄存器) 。
- 基础汇编指令:PUSH,,POP,JMP(mov eip addr),CALL,LEAVE,RET(pop eip)。
- 进程内存管理:linux下将执行程序加载到新创建的内存空间中,程序一般包含 .text, .bss ,.data 三种段,前者包含程序指令,在内存中映射为只读,后两者包含未初始化的数据和初始化的静态数据,被映射为可写。加载完成后,初始化程序的参数栈和数据堆,其中栈的栈底为环境变量,运行参数及参数数量,然后是主函数及调用的临时信息;堆存储动态分配的数据。
- 函数调用:
- 调用:将函数调用参数,函数调用下一条指令返回地址压栈,并跳转至被调函数入口地址。
- 序言:被调函数将调用函数的栈基址进行压栈保存,并创建自身函数栈结构。
- 返回:在被调函数执行玩指令后将指令控制权返回给调用者之前,恢复调用者的栈顶与栈底指针(ebp,esp),并将之前压栈的返回地址装在至指令寄存器eip中执行。
- 缓冲区溢出攻击原理:
- 栈溢出,堆溢出,内核溢出。
- 取消linux系统对抗缓冲区溢出(缓冲区溢出实验):
- 取消“栈上数据不可执行”保护。
- 取消”地址空间随机化“保护。
- 编译时取消‘’/GS‘’保护。
- 溢出攻击以获取权限的三个挑战:
- 找出缓冲区溢出要覆盖和修改的敏感位置。
- 将敏感位置修改为何值。
- 攻击指令代码(payload,shellcode)。
1.3 Linux平台上的栈溢出与Shellcode
- linux栈溢出攻击的三种模式:
- NSR:适用于被溢出的缓冲区变量比较大,足以容纳Shellcode的情况 。数据构造方式为从低地址到高地址分别是Nop+shellcode+覆盖返回地址的期望返回地址。
- RNS:一般用于被溢出的变量比较小,不足于容纳Shellcode的情况。 数据构造方式为从低地址到高地址分别是覆盖到RET返回的地址的跳转地址+Nop填充出着陆区+shellcode。
- RS:这种模式能精确定位出shellcode在目标漏洞程序进程空间中的起始地址,所以无需引入Nop指令填充出着陆区。将shellcode放置在漏洞程序执行的环境变量中,也就是在栈底,因而位置是固定的,返回位置为ret=0xc0000000-sizeof(void*)-sizeof(FILENAME)-sizeof(shellcode)。
- 前两种模式适合远程栈溢出,RS只能用于本地缓冲区溢出攻击。
- shellcode实现技术(通常是溢出后通过调用execve()函数启动/bin/sh提供命令行):
- 使用高级语言(C)编写shellcode。
- 编译并反汇编调试该程序。
- 从汇编代码级别分析程序执行流程。
- 将获取的汇编代码嵌入C中进行测试。
- 提取汇编代码所对应的opcode二进制指令,创建shellcode数组。
1.4 windows上的栈溢出和shellcode
- windows相对linux在栈溢出攻击上的差异:
- windows会对废弃栈进行随机数插入处理,而linux不做任何处理,故以linux栈溢出攻击方式攻击windows会有一定限制,导致溢出位置shellcode指令失效。
- 进程内存空间分布不同,导致linux下RNS模式攻击不可用。
- windows系统功能调用通过系统API及内核处理例程调用,而linux通过“int80”中断处理。
- windows上栈溢出攻击通过系统核心DLL中的“JMP ESP”指令完成流程跳转,将溢出返回地址改写为只想“JMP ESP"的高位地址,并同时是ESP寄存器指向Nop和shellcode的位置。
- windows shellcode实现
- shellcode需要找到所需的API函数(system(),exit()),生成函数调用表。
- 获得函数的内存加载地址。
- 确保推出。
- windows远程shellcode
- 创建一个服务器端socket,并在指定的端口上监听 。
- 通过accept()接受客户端的网络连接 。
- 创建子进程,运行“cmd.exe”,启动命令行 。
- 创建两个管道,命令管道将服务器端socket接收(rccv)到的客户端通过网络输入的执行命令,连接至cmd.exe的标准输入;然后输出管道将cmd.exe的标准输出连接至服务器端socket的发送(send),通过网络将运行结果反馈给客户端 。
1.5 堆溢出攻击
- 堆中并没有可以直接覆盖并修改指令寄存器指针的返回地址,故堆溢出更难。需要借助函数指针、C++类对象中的虚函数表等关键变量挖掘漏洞。
- 指针改写:将函数指针指向shellcode入口地址(需要溢出的缓冲区临近全局函数指针存储地址,且在其低地址方向 )。
- C++类对象虚函数表改写。
- glibc库free()函数漏洞 。
1.6 缓冲区溢出攻击的防御
- 代码漏洞查错(fuzz注入测试)。
- 在编译器上引入边界检查。
- 设置允许溢出但不执行保护。
- 设置堆栈不可执行(一些堆栈上)保护。
2. 实践过程
3. 学习问题及解决
- gdb调试命令不太清楚。解决:慢慢实践。
4. 实践总结
参考
posted @
2020-05-06 13:52
chlei233
阅读(
662)
评论()
编辑
收藏
举报