(本文之前已经投稿给安全客了)
之前在学习家用路由器这本书以及看网上大佬写相关文章 的时候,总感觉有些关键细节一笔带过,有时候给我造成了很大的困扰,鉴于这个原因,我想到把自己的一些思考以及实际操作经验写出来给后来者,希望他们不要再走我走过的弯路。
首先看源代码:
将vuln_system.c 拷贝至对应目录下:
执行如下命令:
而后会出现错误:
程序引发了一段故障,使用如下命令重新执行:
这里直接运行,发生崩溃就退出了;
加-g是等待调试的:
执行完这条指令之后,使用IDA进行附加调试:
这里选择大端是因为这个文件是mips大端格式的;
附加之后,在IDA里面按F9键(书里面写的是F5,,这是错的!)可以看到程序在试图执行0x41414141的时候崩溃了,如下图所示:
这是因为0x41414141将原来的返回地址给覆盖了,程序在返回的时候返回的是0x41414141这个无效地址而不是原来的地址,故会崩溃.
通过阅读vuln_system.c的源码可以知道,main函数里面,在读取完passwd这个文件之后,将passwd文件里面的所有数据存入堆栈的局部变量buf里面,而buf的大小仅为256字节,而passwd文件有600字节大小的数据写入buf,导致了缓冲区溢出;
通过静态分析发现,如果要使缓冲区溢出,并控制到堆栈中的返回地址saved_ra,需要覆盖的数据大小应该达到0x1A0-0x04即0x19c字节;作者这里运用这个公式的依据是什么呢?让我们回顾一下X86架构下的情形:
偏移不就是找buf和ra之间的偏移么,ra是存储于栈里面的(有点类似于x86里面的ret指令),buf指向栈里面,只要计算出buf的初始位置和ra之间的偏移,就可以计算出有多少个字节就可以溢出到ra了!
上图是主函数里面的一开始的部分,为了进一步分析出偏移,笔者将相关汇编指令誊写并注释如下:
结合源代码看,可以发现主函数里面调用的第一个函数为memset函数,貌似源代码里面没有这个函数,怎么回事,继续看,发现在调用这个函数之前是调用了3个参数的,分别用到a0,a1,和a2这几个寄存器,(在MIPS架构里面,是用a0-a3这几个寄存器来传参的),而函数memset的原型为void* memset(void* s,int ch,unsigned n)
,其主要功能为:在内存空间里以s为起始的地方,将开始的n个字节设为指定值;可以发现传给a2的值为v1,而传给$v1的为0x100(0x100实际上就是十进制256),分析到这里,大家应该会清晰了吧,这里在做的事情其实是内存初始化,通过这个函数将内存里面的256个字节初始化为0,而这里的内存初始地址是通过指令addiu $v0,$fp,0x1D0+var_1A0
来确定的,显然,0x1D0+var_1A0
就是我们要找的buf的起始偏移,到这里,我们才能确定:需要覆盖的数据大小应该为0x1D0+var_1A0-0x1D0-var_4
即0x19c字节;
输入指令之后,程序就会处于等待调试的状态; 而后利用IDA附加该进程(此过程在前面已经叙述过);
由于这里使用附加调试的效果不是太好,我这里使用的运行时调试的方法,读者可以参考这本书即可;
在主函数结尾的地方下断点,按F9运行程序,会在0x004006CC这个地址断下:
双击0x004006D0这行;来到返回地址在栈空间0x40800104(也可以利用SP+0x1D0+VAR_4得到)处,如下图 所示:
查看HEX VIEW-1窗口,发现返回地址已经被覆盖为0x42424242,如下图所示,此时缓冲区已经被输入的数据所覆盖,并且越界后覆盖了堆栈上的其他数据;
继续按F8键执行指令jr $ra,程序就会跳往0x42424242出执行,如下图所示:
小结
在这一节里面,主要学习的知识点是如何计算偏移达到覆盖返回地址的目的,这里总结出一个公式:
偏移=函数返回地址-缓冲区首地址;
(注:在堆栈中,一般函数返回地址处于高地址,缓冲区地址处于低地址),今天就暂时写到这里,后面有时间我会带来更多的分享.
最后夸一夸看雪的markdown,用户体验特别好!!!
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2018-8-31 18:16
被蓝色淡风编辑
,原因: 代码不齐