/*
 * 
 * ftp_mic_overflows
 *
 * This plugin was written by Renaud Deraison and is released
 * under the GPL
 *
 */
#include <includes.h>

#define NAME "ftp misc. overflows"
#define DESC "\
Some FTP server do not check the length of arguments of several commands \n\
and can thus exploit potential buffer overflows. This plugin attempts to \n\
find which commands are subject to possible buffer overflows\n\n\n\
Risk factor : high"
#define COPYRIGHT "no copyright"
#define SUMM "attempts to find some buffer overflows on a remote ftp server"

static
const char * commands[] = { 
"MKD", "PORT", "STOR", "MSAM", "RNTO", 
"NLST", "APPE", "MRSQ", "SITE", "XMKD", 
"ACCT", "TYPE", "MLFL", "MRCP", "DELE",
"RMD", "STOU", "SMNT", "MAIL", "ALLO",
"CWD", "STAT", "XRMD", "SIZE", "MODE",
"MSND", "MDTM", "RETR", "MSOM", "RNFR",
"LIST", "HELP"};

#define NUM_COMMANDS 32


int init_ftp_session(struct arglist *);
int ftp_login(struct arglist *, int);
int send_bogus_command(int, char*, char*);


PlugExport int plugin_init(struct arglist *desc);
PlugExport int plugin_init(struct arglist *desc)
{
	plug_set_name(desc, NAME);
	plug_set_description(desc, DESC);
	plug_set_summary(desc, SUMM);
	plug_set_copyright(desc, COPYRIGHT);
	plug_set_category(desc, ACT_ATTACK);
	plug_set_family(desc, "FTP");
	return(0);
}
	


PlugExport int plugin_run(struct arglist * args);	
PlugExport int plugin_run(struct arglist * args)
{
	int soc;
        char * crap;
        char * command_set;
	int * vul;
        int one_flaw = 0;
        int i;
        
        vul = emalloc(NUM_COMMANDS*sizeof(int));
        signal(SIGPIPE, SIG_IGN);
        for(i=0;i<NUM_COMMANDS;i++)vul[i]=0;
        
	if(host_get_port_state(args, 21)<=0)return(0);
	soc = init_ftp_session(args);
        if(soc < 0)return(0);
        
        crap = emalloc(4096);
        memset(crap, 'A', 4095);
        
        /* first, we try to overflow the ftp server without
           logging in
         */
        
        if(send_bogus_command(soc, "HELP", crap))
        {
          vul[NUM_COMMANDS-1]=1;
          one_flaw++;
         }
        shutdown(soc, 2);
        close(soc);
        
        /* now, we log in and try all our commands */
        soc = init_ftp_session(args);
        if((soc >= 0) && 
           (plug_get_key(args, "ftp/anonymous_access"))&& 
           (ftp_login(args, soc)))
        {
         for(i=0;i<NUM_COMMANDS-1;i++)
         {
          if(send_bogus_command(soc, commands[i], crap))
           {
            vul[i]=1;
            one_flaw++;
            shutdown(soc, 2);
            soc = init_ftp_session(args);
            if(!ftp_login(args, soc))i=NUM_COMMANDS;
           }
          }
         }
         
        /* finished. We just have to report which commands could create
         * a buffer overflow...
         */
         if(one_flaw)
         {
         efree(&crap);
         crap = emalloc(2048);
         command_set = emalloc((one_flaw+3)*4);
         for(i=0;i<NUM_COMMANDS;i++)
         {
          if(vul[i]){ strcat(command_set, commands[i]);
          	       strcat(command_set, " ");
                     }
          
         }
         sprintf(crap,
"The following commands appear to crash the remote FTP server :\n\
%s\n\
These might be buffer overflows which can allow a remote user to \n\
gain root easily\n\n\
Solution: contact your vendor for a patch", command_set);
         
         post_hole(args, 21, crap);
         efree(&command_set);
         }
         efree(&crap);
         
	return(0);
}


int init_ftp_session(args)
 struct arglist * args;
{
 int soc = open_sock_tcp(args, 21);
 char * buf = emalloc(1024);
 
 if(soc >= 0)recv_line(soc, buf, 1024);
 if(strncmp(buf, "220", 3))return(-1);
 while(buf[3]=='-')recv_line(soc, buf, 1024);
 if(strstr(buf, "Microsoft"))soc = -1;
 efree(&buf);
 return(soc);
}


int ftp_login(args, soc)
 struct arglist * args;
 int soc;
{
 char * buf = emalloc(1024);
 int a = 0;
 int ok;
 sprintf(buf, "USER ftp\n");
 send(soc, buf, strlen(buf), 0);
 bzero(buf, 1024);
 recv_line(soc, buf, 1024);
 sprintf(buf, "PASS joe@\n");
 send(soc, buf, strlen(buf), 0);
 bzero(buf, 1024);
 recv_line(soc, buf, 1024);
 
 if(!strncmp(buf, "230", 3))ok = 1;
 else ok = 0;
 
 a = 1;
 while(a)
 { 
  bzero(buf, 1024);
  ioctl(soc, FIONREAD, &a);
  if(a)recv_line(soc, buf, 1024);
  }
  
 efree(&buf);
 return(ok);
}


int send_bogus_command(soc, command, crap)
 int soc;
 char * command;
 char * crap;
{
 char * buf = emalloc(strlen(crap)+strlen(command)+10);
 int ret = 0;
 sprintf(buf, "%s %s\n", command, crap);
 send(soc, buf, strlen(buf), 0);
 bzero(buf, strlen(buf));
 
 recv_line(soc, buf, strlen(crap)+strlen(command)+10);
 if(!strlen(buf))ret = 1;
 else ret = 0;
 efree(&buf);
 return(ret);
}
