/*
 * ftp writeable dirs
 *
 * This plugin explores a FTP server and searches for 
 * world writeable directories
 *
 * This plugin was written by Renaud Deraison and is distributed
 * under the GPL
 */
#include <includes.h>

#define NAME "ftp writeable directories"
#define DESCRIPTION "\
It is usually a bad idea to have world writeable\n\
directories in a public FTP server, since it may\n\
allow anyone to use the FTP server as a 'warez'\n\
server (this means that the FTP server will be\n\
used to exchange non-free software between\n\
software pirates). It may also allow anyone to\n\
make a denial of service by filling up the FTP\n\
server filesystem\n\n\
Risk factor : medium"

#define COPYRIGHT "this plugin is distributed under the GPL"
#define SUMMARY "checks if the remote FTP server has any world writeable dirs"


struct ftp_dirs {
 char * name;  /* full name  */
 int writeable;
 struct ftp_dirs * next;
};




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

void ftp_explore_dir(int soc, struct ftp_dirs * dirs);

PlugExport int plugin_run(struct arglist * env);
PlugExport int plugin_run(struct arglist * env)
{
 int soc;
 struct ftp_dirs * dirs = emalloc(sizeof(struct ftp_dirs));
 char * list = emalloc(1);
 char * report;
 if(!plug_get_key(env, "ftp/anonymous_access"))return(0);
 if(host_get_port_state(env, 21)<=0)return(0);
 if((soc = open_sock_tcp(env, 21))<0)return(0);
 if(ftp_log_in(soc, "anonymous", "joe@"))return(0);
 dirs->name = emalloc(2);
 dirs->name[0]='/';
 dirs->writeable=0;
 ftp_explore_dir(soc, dirs);

 while(dirs)
 { 
 if(dirs->writeable)
 { 
  char* t;
  t = emalloc(strlen(list)+strlen(dirs->name)+2);
  sprintf(t, "%s\n%s", list, dirs->name);
  efree(&list);
  list = t;
 }
 dirs = dirs->next;
 }
 
 
 if(strlen(list)>1)
 {
  report = emalloc(255+strlen(list));
  sprintf(report, "\
The following directories are world-writeable. You should\n\
correct this problem quickly\n%s\n", list);
  post_hole(env, 21, report);
  efree(&report);
 }
 efree(&list);
}
 
/*
 * This recursive function fills in the ftp_dirs structure
 * with the name of the directories of the remote ftp
 * server... it's a convenient way to find the listing
 * of the remote ftp server, but it's not the purpose
 * of this plugin :)
 */
void ftp_explore_dir(int soc, struct ftp_dirs * dirs)
{
 struct sockaddr_in addr;
 char * buf;
 char * command;
 int f = 0;
 int data_soc;
 struct ftp_dirs * start = dirs;
 char * name;
 if(!dirs)return;
 name  = dirs->name;
 /* 
  * we do all our stuff in passive mode (more convenient according to me)
  */
 if(ftp_get_pasv_address(soc, &addr))return;
 data_soc = socket(AF_INET, SOCK_STREAM, 0);
 if(connect(data_soc, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)))
 	return;
 command = emalloc(strlen(dirs->name)+20);
 sprintf(command, "LIST %s\n", dirs->name);
 send(soc, command, strlen(command), 0);
 buf = emalloc(255);
 recv_line(soc, buf, 255);
 recv_line(soc, buf, 255);
 
 while(!f)
 {
  recv_line(data_soc, buf, 255);
  if(!strlen(buf))f = 1;
  else
  { 
    char * data[9];
    char * mode;
    int i;
    int is_dir = 0;
    int is_writeable = 0;
    char * file_name = NULL;
    
    for(i=0;i<9;i++)data[i]=emalloc(strlen(buf)+1);
    sscanf(buf, "%s %s %s %s %s %s %s %s %s", data[0],
    	         data[1], data[2], data[3], data[4], 
                 data[5], data[6], data[7], data[8]);
    mode = emalloc(strlen(data[0])+1);
    strncpy(mode, data[0], strlen(data[0]));
    if(strcmp(data[8], ".")&&strcmp(data[8], "..")&&strcmp(data[8], "/"))
    {
    file_name = emalloc(strlen(name)+strlen(data[8])+2);
    if(data[8][strlen(data[8])]=='\n')data[8][strlen(data[8])]=0;
    sprintf(file_name, "%s/%s", strcmp(name, "/")?name:"", data[8]);
    }
    for(i=0;i<9;i++)efree(&data[i]);
    if(file_name)
    {
     if(strcmp(file_name, "/"))
     {
      if(mode[0]=='d')is_dir=1;
      else is_dir = 0;
      if(mode[8]=='w')is_writeable=1;
      else is_writeable = 0;
      
    
      if(is_dir)
      {
      while(dirs->next)dirs = dirs->next;
      dirs->next = emalloc(sizeof(struct ftp_dirs));
      dirs = dirs->next;
      dirs->name = emalloc(strlen(file_name)+1);
      strncpy(dirs->name, file_name, strlen(file_name));
      if(is_writeable)dirs->writeable = 1;
      else dirs->writeable = 0;
      }
      }
     efree(&mode);
     efree(&file_name);
    }
  }
 }
 shutdown(data_soc, 2);
 close(data_soc);
 efree(&buf);
 ftp_explore_dir(soc, start->next);
}
    
    
    
    
    
    
    
    
 
 
 



