堆栈粉碎代码不能在Linux内核2.6.38.7上运行...请帮忙 [英] Stack smashing code not working on Linux kernel 2.6.38.7... Please help

查看:142
本文介绍了堆栈粉碎代码不能在Linux内核2.6.38.7上运行...请帮忙的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在阅读The Shellcoders Handbook,并一直在提及这个链接来练习堆栈溢出。但似乎Linux内核开发人员已经使内核非常安全。这是我的问题。



1)此代码

  void函数(int a,int b,int c){
char buffer1 [8];
char buffer2 [10];
int * ret;

ret = buffer1 + 6;
* ret + = 8;
}

void main(){
int x;

x = 0;
函数(1,2,3);
x = 1;
printf(%d \\\
,x);

$ / code>

给出输出



< pre $ $ cc smash.c
smash.c:在函数'function'中:
smash.c:7:8:warning:从不兼容指针类型赋值
$ ./a.out
1

但替换 * ret + = 8 * ret = 8 给出以下输出

  ***检测到堆栈粉碎***:./a.out终止
======= Backtrace:=========
/lib/i386-linux-gnu/libc.so.6(__fortify_fail+0x50)[0xa86df0]
/lib/i386-linux-gnu/libc.so.6(+0xe5d9a)[0xa86d9a]
./a.out[0x8048448]
./a.out[0x8048477]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xe7)[0x9b7e37]
./a.out[0x8048381]
=======内存映射:========
003df000-003e0000 r-xp 00000000 00:00 0 [vdso ]
009a1000-00afb000 r-xp 00000000 08:01 3277633 /lib/i386-linux-gnu/libc-2.13.so
00afb000-00afc000 --- p 0015a000 08:01 3277633 / lib / i386 -linux-GNU /的libc-2.1 3.so
00afc000-00afe000 r - p 0015a000 08:01 3277633 /lib/i386-linux-gnu/libc-2.13.so
...
...

如果我用<$替换 ret = buffer1 + 6 c $ c> ret = buffer1 + 7 ,结果与上面相同。
如果我用 ret = buffer1 + 8 (或任何更大的值)替换 ret = buffer1 + 6, ,对于上述情况(即,我是否将值 * ret 的值增加8或将其更改为8),都会被捣毁。



请告诉我这是怎么发生的。有用的链接也将不胜感激。
最重要的是,如何禁用Linux内核的这个安全功能,以便我可以使用这本书?



平台:i386
内核:2.6.38

解决方案

要禁用堆栈粉碎检测,请使用 - fno-stack-protector 编译时。您可能还希望在使用Shellcoders手册时使用-ggdb和-mpreferred-stack-boundary = 4来启用GDB符号并标准化堆栈。

编辑:
当我编译你提供的代码时( gcc -fno-stack-protector -ggdb -mpreferred-stack-boundary = 4 -o sc in.c ) ,编译器重新安排了函数中局部变量的顺序。我使用GDB发现了这个:

  willi @ ubuntu:〜/ testing $ gdb sc 
(gdb)set disassembly -flavor intel
(gdb)反汇编函数
函数函数的汇编代码转储:
0x080483c4< + 0> ;: push ebp
0x080483c5< + 1> ;: mov ebp ,esp
0x080483c7 + 3:sub esp,0x20
0x080483ca +6:lea eax,[ebp-0xc]
0x080483cd< + 9> ;: add eax, 0x6
0x080483d0 <+ 12>:mov DWORD PTR [ebp-0x4],eax
0x080483d3 <+ 15>:mov eax,DWORD PTR [ebp-0x4]
0x080483d6< + 18> ;: mov eax,DWORD PTR [eax]
0x080483d8< + 20> ;: lea edx,[eax + 0x8]
0x080483db< + 23>:mov eax,DWORD PTR [ebp- 0x4]
0x080483de< + 26> ;: mov DWORD PTR [eax],edx
0x080483e0< + 28>:leave
0x080483e1< + 29> ;: ret
End汇编转储。

0x080483ca告诉我: ebp - 0xC 是buffer1和0x080483d0告诉我 ebp - 0x4 是ret。因此,这些变量不存在于堆栈中,因为它们存在于我们的C代码中。鉴于 ret 是我们最高的局部变量,我们可以直接使用它。不过,让我们来处理你的代码吧。

要修改返回指针,我们需要改变保存在ebp下的地址,所以 ebp + 0x4 。因此,为了从我们的变量buffer1得到返回指针,我们必须添加0xC(以获得 ebp ),然后0x4(返回指针是0x4在 EBP )。现在我们可以进行修改了。



我从你的C代码中取消了你想跳过 x = 1 并直接返回到 printf 。我反汇编了 main 以找到对返回指针的适当修改:

  (gdb)反汇编main 
函数main的汇编代码转储:
0x080483e2< + 0> ;: push ebp
0x080483e3< + 1> ;: mov ebp,esp
0x080483e5 < + 3> ;:和esp,0xfffffff0
0x080483e8< + 6> ;: sub esp,0x20
0x080483eb< + 9> ;: mov DWORD PTR [esp + 0x1c],0x0
0x080483f3< + 17> ;: mov DWORD PTR [esp + 0x8],0x3
0x080483fb< + 25> ;: mov DWORD PTR [esp + 0x4],0x2
0x08048403< + 33> ;: mov DWORD PTR [esp],0x1
0x0804840a< + 40> ;: call 0x80483c4< function>
0x0804840f <+ 45>:mov DWORD PTR [esp + 0x1c],0x1
0x08048417< + 53>:mov eax,DWORD PTR [esp + 0x1c]
0x0804841b< 57>:mov DWORD PTR [esp + 0x4],eax
0x0804841f< + 61> ;: mov DWORD PTR [esp],0x80484f0
0x08048426< + 68>:call 0x80482f4< printf @ plt> ;
0x0804842b< + 73>:离开
0x0804842c< + 74>:ret
汇编器转储结束。

如果不修改返回指针,调用函数在0x0804840a返回0x0804840f。但是我们想跳过这个并返回到下一条指令。下一条指令从0x08048417开始,这是0x8字节。所以我们对返回指针的修改必须将它的值增加0x8。

考虑到这些因素,我使用下面的代码来打印0而不是1 :

  void function(int a,int b,int c){
char buffer1 [8];
char buffer2 [10];
int * ret;

ret = buffer1 + 0x10;
* ret + = 8;
}

void main(){
int x;

x = 0;
函数(1,2,3);
x = 1;
printf(%d \\\
,x);
}


I have been reading "The Shellcoders Handbook" and been referring to this link for practice of stack overflow. But it seems the Linux kernel developers have made the kernel very secure. Here are my problems.

1) This code

void function(int a, int b, int c) {
   char buffer1[8];
   char buffer2[10];
   int* ret;

   ret = buffer1 + 6;
   *ret+=8;
}

void main() {
  int x;

  x = 0;
  function(1,2,3);
  x = 1;
  printf("%d\n",x);
}

gives the output

$ cc smash.c
smash.c: In function ‘function’:
smash.c:7:8: warning: assignment from incompatible pointer type
$ ./a.out
1

but replacing the line *ret+=8 with *ret=8 gives the following output

*** stack smashing detected ***: ./a.out terminated
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(__fortify_fail+0x50)[0xa86df0]
/lib/i386-linux-gnu/libc.so.6(+0xe5d9a)[0xa86d9a]
./a.out[0x8048448]
./a.out[0x8048477]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xe7)[0x9b7e37]
./a.out[0x8048381]
======= Memory map: ========
003df000-003e0000 r-xp 00000000 00:00 0          [vdso]
009a1000-00afb000 r-xp 00000000 08:01 3277633    /lib/i386-linux-gnu/libc-2.13.so
00afb000-00afc000 ---p 0015a000 08:01 3277633    /lib/i386-linux-gnu/libc-2.13.so
00afc000-00afe000 r--p 0015a000 08:01 3277633    /lib/i386-linux-gnu/libc-2.13.so
...
...

If I replace ret = buffer1 + 6 with ret = buffer1 + 7, the result is same as above. If I replace ret = buffer1 + 6 with ret=buffer1+8 (or any larger value), there is smashed stack for BOTH the cases described above (i.e. whether I increment the value *ret by 8 or change it to 8).

Please tell me how this happens. Helpful links will also be appreciated. And above all, how can I disable this security feature of the Linux kernel so that I can work with this book?

Platform: i386 Kernel: 2.6.38

解决方案

To disable the stack smashing detection, use -fno-stack-protector when compiling. You may also want to use -ggdb and -mpreferred-stack-boundary=4 when working through "The Shellcoders Handbook" to enable GDB symbols and standardize the stack.

edit: When I compiled the code you provided (gcc -fno-stack-protector -ggdb -mpreferred-stack-boundary=4 -o sc in.c), the compiler rearranged the order of the local variables in function. I found this by using GDB:

willi@ubuntu:~/testing$ gdb sc
(gdb) set disassembly-flavor intel
(gdb) disassemble function
Dump of assembler code for function function:
   0x080483c4 <+0>: push   ebp
   0x080483c5 <+1>: mov    ebp,esp
   0x080483c7 <+3>: sub    esp,0x20
   0x080483ca <+6>: lea    eax,[ebp-0xc]
   0x080483cd <+9>: add    eax,0x6
   0x080483d0 <+12>:    mov    DWORD PTR [ebp-0x4],eax
   0x080483d3 <+15>:    mov    eax,DWORD PTR [ebp-0x4]
   0x080483d6 <+18>:    mov    eax,DWORD PTR [eax]
   0x080483d8 <+20>:    lea    edx,[eax+0x8]
   0x080483db <+23>:    mov    eax,DWORD PTR [ebp-0x4]
   0x080483de <+26>:    mov    DWORD PTR [eax],edx
   0x080483e0 <+28>:    leave  
   0x080483e1 <+29>:    ret    
End of assembler dump.

0x080483ca tells me that ebp - 0xC is buffer1, and 0x080483d0 tells me ebp - 0x4 is ret. So, the variables do not exist on the stack as they exist in our C code. Given that ret is our top-most local variable, we could work with it directly. Let's work with your code, though.

To modify the return pointer, we need to change the address stored just below the saved ebp, so ebp + 0x4. So, to get to the return pointer from from our variable buffer1, we have to add 0xC (to get to ebp), and then 0x4 (return pointer is 0x4 under ebp). Now we can make our modifications.

I take from your C code that you'd like to skip the assignment of x = 1 and return directly to the printf. I disassembled main to find the appropriate modification to the return pointer:

(gdb) disassemble main
Dump of assembler code for function main:
   0x080483e2 <+0>: push   ebp
   0x080483e3 <+1>: mov    ebp,esp
   0x080483e5 <+3>: and    esp,0xfffffff0
   0x080483e8 <+6>: sub    esp,0x20
   0x080483eb <+9>: mov    DWORD PTR [esp+0x1c],0x0
   0x080483f3 <+17>:    mov    DWORD PTR [esp+0x8],0x3
   0x080483fb <+25>:    mov    DWORD PTR [esp+0x4],0x2
   0x08048403 <+33>:    mov    DWORD PTR [esp],0x1
   0x0804840a <+40>:    call   0x80483c4 <function>
   0x0804840f <+45>:    mov    DWORD PTR [esp+0x1c],0x1
   0x08048417 <+53>:    mov    eax,DWORD PTR [esp+0x1c]
   0x0804841b <+57>:    mov    DWORD PTR [esp+0x4],eax
   0x0804841f <+61>:    mov    DWORD PTR [esp],0x80484f0
   0x08048426 <+68>:    call   0x80482f4 <printf@plt>
   0x0804842b <+73>:    leave  
   0x0804842c <+74>:    ret    
End of assembler dump.

Without modification to the return pointer, the call to function at 0x0804840a returns to 0x0804840f. But we want to skip this and return to the next instruction. The next instruction begins at 0x08048417, which is 0x8 bytes further along. So our modification to the return pointer must increase its value by 0x8.

Taking these things into consideration, I used the following code to print "0" rather than "1":

void function(int a, int b, int c) {
   char buffer1[8];
   char buffer2[10];
   int* ret;

   ret = buffer1 + 0x10;
   *ret+=8;
}

void main() {
  int x;

  x = 0;
  function(1,2,3);
  x = 1;
  printf("%d\n",x);
}

这篇关于堆栈粉碎代码不能在Linux内核2.6.38.7上运行...请帮忙的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆