软件安全实验——pre6(整数溢出堆溢出栈溢出漏洞预习)
Posted 大灬白
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了软件安全实验——pre6(整数溢出堆溢出栈溢出漏洞预习)相关的知识,希望对你有一定的参考价值。
这里写目录标题
一级目录
1、阅读Basic Integer Overflows 这篇文章,大概描述整数溢出的原因和危害。
http://www.phrack.org/issues.html?issue=60&id=10#article
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>
void catcher(int a)
{
setresuid(geteuid(),geteuid(),geteuid());
printf("WIN!\\n");
system("/bin/sh");
exit(0);
}
int main(int argc, char **argv)
{
if (argc != 3 || !atoi(argv[2]))
return 1;
signal(SIGFPE, catcher);
return atoi(argv[1]) / atoi(argv[2]);
}
上面这段代码如何才能执行到system("/bin/sh")处?
整数溢出的原因:一个整数是一个固定的长度 ,它能存储的最大值是固定的,当尝试去存储一个大于这个固定的最大值时,将会导致一个整数溢出。整数溢出将会导致"不能确定的行为",也就是说编译器遵从了这个的规则,那就是完全忽略溢出而退出这个程序。很多编译器似乎忽略了这个溢出,结果是一个意想不到的错误值被存储。
危害:整数溢出是不能被立即察觉,因此没有办法去用一个应用程序来判断先前计算的结果是否实际上也是正确的。如果是用来计算缓冲区的大小或者计算数组索引排列的距离,这会变的危险.。当然很多整数溢出并不是都是可利用的,因为并没有直接改写内存,但是有时,他们可导致其他类型的bugs,缓冲区溢出等。而且,整数溢出很难被发现,因此,就算是审核过的代码也会产生意外。
要让这段代码执行到system("/bin/sh")处,我们先看一下
(1)signal(SIGFPE, catcher);
指的是出现信号码SIGFPE (Signal Floating-Point Exception) 算术运算出错,如除数为 0 或溢出(不一定是浮点运算)。的时候就会调用catcher函数
(2)argc != 3要求argc为3,即argc是命令行中参数的个数是3个参数
(3)!atoi(argv[2])要求第三个参数为负数,atoi把字符串转换成int整数
(4)溢出点就在atoi(argv[1]) / atoi(argv[2]),也就是我们输入的两个参数做完除法之后再转换成整数的时候,因为int无法存储计算结果而发生整数溢出,从而触发signal函数调用catcher函数执行到system("/bin/sh")处。
2、阅读堆溢出的文章Once upon a free()…
http://www.phrack.org/issues.html?issue=57&id=9#article。比较堆溢出和栈溢出的异同。
答:主要的区别有:
1、管理方式不同:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生内存泄漏。
2、空间大小不同:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的。
3、碎片问题:对于堆来讲,频繁的新建和删除势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出。
4、生长方向不同:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。
5、分配方式不同:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
6、分配效率不同:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。
3、(一定要做)上网搜索利用jmp esp或者call esp来进行栈溢出的文章并且仔细阅读,解释这种技术的优点。
答:利用JMP ESP的方式
其利用格式是NNNNNNRSSSSS,这里N=NOP,R=RET(jmp esp的地址),S=ShellCode。就是把缓冲区一直覆盖成NOP(空指令,什么都不做),直到原来的EIP位置时,我们填入系统中某个核心dll中的jmp esp的地址,紧跟后面才是我们的ShellCode。 正常情况下,函数返回时,执行RET指令,这等于POP EIP,会把保存的原来程序的EIP的值恢复,从而完成中断的返回。但在这里,我们把保存的EIP的值覆盖了,改写成了jmp esp的地址。这样,POP EIP后,EIP = jmp esp的地址,而堆栈指针ESP会往下走,指向ShellCode的开始。程序继续执行,此时EIP里的内容是jmp esp,系统执行jmp esp,就正好就跳到我们的ShellCode的地方了.
如果ShellCode是开个端口,那我们就可以远程连上去;如果ShellCode是下载执行,那我们就可以让目标机在网页上下个文件并执行,只要你想到达的功能,都可以想办法实现。
利用栈溢出,将bof函数堆栈帧中的返回地址覆盖为jmp esp or call esp指令的地址,CPU执行到返回地址处时,它会执行jmp esp or call esp指令,执行完这个指令之后,CPU会返回到被中断指令的下一条指令处接着执行,我把Shellcode放到这个被中断指令处,CPU就会执行Shellcode,从而实现我们的攻击。
以上是关于软件安全实验——pre6(整数溢出堆溢出栈溢出漏洞预习)的主要内容,如果未能解决你的问题,请参考以下文章