Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 26 Apr 2001 19:15:02 -0700
From:      Cy Schubert - ITSD Open Systems Group <Cy.Schubert@uumail.gov.bc.ca>
To:        freebsd-security@freebsd.org
Subject:   Security advisory: krb5 ftpd buffer overflows (fwd)
Message-ID:  <200104270215.f3R2FHP61668@cwsys.cwsent.com>

next in thread | raw e-mail | index | archive | help
Looks like we need to patch our krb5 port.


Regards,                         Phone:  (250)387-8437
Cy Schubert                        Fax:  (250)387-5766
Team Leader, Sun/Alpha Team   Internet:  Cy.Schubert@osg.gov.bc.ca
Open Systems Group, ITSD, ISTA
Province of BC            


------- Forwarded Message

[headers removed]
Message-ID: <ldvsniw5vq3.fsf@saint-elmos-fire.mit.edu>
Date: Wed, 25 Apr 2001 20:51:48 -0400
Reply-To: Tom Yu <tlyu@MIT.EDU>
Sender: Bugtraq List <BUGTRAQ@SECURITYFOCUS.COM>
From: Tom Yu <tlyu@MIT.EDU>
Subject: Security advisory: krb5 ftpd buffer overflows
X-To: kerberos@MIT.EDU
X-cc: krbdev@MIT.EDU
To: BUGTRAQ@SECURITYFOCUS.COM

- -----BEGIN PGP SIGNED MESSAGE-----

		      KRB5 FTPD BUFFER OVERFLOWS

2001-04-25

SUMMARY:

Buffer overflows exist in the FTP daemon included with MIT krb5.

IMPACT:

* If anonymous FTP is enabled, a remote user may gain unauthorized
  root access.

* A user with access to a local account may gain unauthorized root
  access.

* A remote user who can successfully authenticate to the FTP daemon
  may obtain unauthorized root access, regardless of whether anonymous
  FTP is enabled or whether access is granted to a local account.
  This vulnerability is believed to be somewhat difficult to exploit.

VULNERABLE DISTRIBUTIONS:

* MIT Kerberos 5, all releases.

FIXES:

The recommended approach is to apply the included patches and to
rebuild your ftpd.  The included patches are against krb5-1.2.2.

If you cannot patch your ftpd currently, workarounds include disabling
anonymous FTP access, if you have it enabled; this will limit the most
likely exploitation to users with local account access or who can
successfully authenticate to the daemon.

This announcement and code patches related to it may be found on the
MIT Kerberos security advisory page at:

	http://web.mit.edu/kerberos/www/advisories/index.html

The main MIT Kerberos web page is at:

	http://web.mit.edu/kerberos/www/index.html

ACKNOWLEDGEMENTS:

Thanks to Matt Crawford for providing some insight into the specific
ways in which krb5 ftpd is vulnerable.

DETAILS:

The remote vulnerability exploitable via anonymous FTP or local
account access results from a buffer overflow in code that calls
ftpglob(), a function responsible for expanding glob characters in
pathnames.  Recent versions of ftpd (krb5-1.2 or later) should not
contain buffer overflows in the ftpglob() function itself.

Remote users able to authenticate to the FTP daemon may be able to
exploit a lack of bounds-checking in calling radix_encode().  Login
access is not required; the ability to force arbitrary data to be
base64-encoded by radix_encode() is sufficient.

This vulnerability is believed to be somewhat difficult to exploit
(but by no means impossible) due to the need for an attacker to inject
data that will base64-encode to the desired machine code and target
address.

PATCHES AGAINST krb5-1.2.2:

These patches are against the krb5-1.2.2 release.  They may also apply
against earlier releases, though.  The patches may also be found at:

	http://web.mit.edu/kerberos/www/advisories/ftpbuf_122_patch.txt

Index: ftpcmd.y
===================================================================
RCS file: /cvs/krbdev/krb5/src/appl/gssftp/ftpd/ftpcmd.y,v
retrieving revision 1.14.4.2
diff -c -r1.14.4.2 ftpcmd.y
*** ftpcmd.y	2001/01/17 23:25:16	1.14.4.2
- - --- ftpcmd.y	2001/04/25 20:16:45
***************
*** 805,815 ****
  		 * This is a valid reply in some cases but not in others.
  		 */
  		if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) {
! 			*(char **)&($$) = *ftpglob((char *) $1);
! 			if (globerr != NULL) {
  				reply(550, globerr);
  				$$ = NULL;
! 			}
  			free((char *) $1);
  		} else
  			$$ = $1;
- - --- 805,819 ----
  		 * This is a valid reply in some cases but not in others.
  		 */
  		if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) {
! 			char **vv;
!
! 			vv = ftpglob((char *) $1);
! 			if (vv == NULL || globerr != NULL) {
  				reply(550, globerr);
  				$$ = NULL;
! 			} else
! 				$$ = *vv;
!
  			free((char *) $1);
  		} else
  			$$ = $1;
Index: ftpd.c
===================================================================
RCS file: /cvs/krbdev/krb5/src/appl/gssftp/ftpd/ftpd.c,v
retrieving revision 1.43.2.1
diff -c -r1.43.2.1 ftpd.c
*** ftpd.c	2000/05/23 21:39:07	1.43.2.1
- - --- ftpd.c	2001/04/25 20:16:48
***************
*** 761,767 ****
- - --- 761,777 ----
  		int result;
  #ifdef GSSAPI
  		if (auth_type && strcmp(auth_type, "GSSAPI") == 0) {
+ 			int len;
+
  			authorized = ftpd_gss_userok(&client_name, name) == 0;
+ 			len = sizeof("GSSAPI user  is not authorized as "
+ 				     "; Password required.")
+ 				+ strlen(client_name.value)
+ 				+ strlen(name);
+ 			if (len >= sizeof(buf)) {
+ 				syslog(LOG_ERR, "user: username too long");
+ 				name = "[username too long]";
+ 			}
  			sprintf(buf, "GSSAPI user %s is%s authorized as %s",
  				client_name.value, authorized ? "" : " not",
  				name);
***************
*** 772,778 ****
- - --- 782,800 ----
  #endif /* GSSAPI */
  #ifdef KRB5_KRB4_COMPAT
  		if (auth_type && strcmp(auth_type, "KERBEROS_V4") == 0) {
+ 			int len;
+
  			authorized = kuserok(&kdata,name) == 0;
+ 			len = sizeof("Kerberos user .@ is not authorized as "
+ 				     "; Password required.")
+ 				+ strlen(kdata.pname)
+ 				+ strlen(kdata.pinst)
+ 				+ strlen(kdata.prealm)
+ 				+ strlen(name);
+ 			if (len >= sizeof(buf)) {
+ 				syslog(LOG_ERR, "user: username too long");
+ 				name = "[username too long]";
+ 			}
  			sprintf(buf, "Kerberos user %s%s%s@%s is%s authorized as %s",
  				kdata.pname, *kdata.pinst ? "." : "",
  				kdata.pinst, kdata.prealm,
***************
*** 1179,1184 ****
- - --- 1201,1211 ----
  	} else {
  		char line[FTP_BUFSIZ];

+ 		if (strlen(cmd) + strlen(name) + 1 >= sizeof(line)) {
+ 			syslog(LOG_ERR, "retrieve: filename too long");
+ 			reply(501, "filename too long");
+ 			return;
+ 		}
  		(void) sprintf(line, cmd, name), name = line;
  		fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
  		st.st_size = -1;
***************
*** 1417,1422 ****
- - --- 1444,1453 ----
  	return (file);
  }

+ /*
+  * XXX callers need to limit total length of output string to
+  * FTP_BUFSIZ
+  */
  #ifdef STDARG
  secure_error(char *fmt, ...)
  #else
***************
*** 1616,1628 ****
  {
  	char line[FTP_BUFSIZ];
  	FILE *fin;
! 	int c;
  	char str[FTP_BUFSIZ], *p;

  	(void) sprintf(line, "/bin/ls -lgA %s", filename);
  	fin = ftpd_popen(line, "r");
  	lreply(211, "status of %s:", filename);
  	p = str;
  	while ((c = getc(fin)) != EOF) {
  		if (c == '\n') {
  			if (ferror(stdout)){
- - --- 1647,1665 ----
  {
  	char line[FTP_BUFSIZ];
  	FILE *fin;
! 	int c, n;
  	char str[FTP_BUFSIZ], *p;

+ 	if (strlen(filename) + sizeof("/bin/ls -lgA ")
+ 	    >= sizeof(line)) {
+ 		reply(501, "filename too long");
+ 		return;
+ 	}
  	(void) sprintf(line, "/bin/ls -lgA %s", filename);
  	fin = ftpd_popen(line, "r");
  	lreply(211, "status of %s:", filename);
  	p = str;
+ 	n = 0;
  	while ((c = getc(fin)) != EOF) {
  		if (c == '\n') {
  			if (ferror(stdout)){
***************
*** 1639,1645 ****
  			*p = '\0';
  			reply(0, "%s", str);
  			p = str;
! 		} else	*p++ = c;
  	}
  	if (p != str) {
  		*p = '\0';
- - --- 1676,1691 ----
  			*p = '\0';
  			reply(0, "%s", str);
  			p = str;
! 			n = 0;
! 		} else {
! 			*p++ = c;
! 			n++;
! 			if (n >= sizeof(str)) {
! 				reply(551, "output line too long");
! 				(void) ftpd_pclose(fin);
! 				return;
! 			}
! 		}
  	}
  	if (p != str) {
  		*p = '\0';
***************
*** 1723,1728 ****
- - --- 1769,1778 ----

  char cont_char = ' ';

+ /*
+  * XXX callers need to limit total length of output string to
+  * FTP_BUFSIZ bytes for now.
+  */
  #ifdef STDARG
  reply(int n, char *fmt, ...)
  #else
***************
*** 1744,1765 ****
  #endif

  	if (auth_type) {
! 		char in[FTP_BUFSIZ], out[FTP_BUFSIZ];
  		int length, kerror;
  		if (n) sprintf(in, "%d%c", n, cont_char);
  		else in[0] = '\0';
  		strncat(in, buf, sizeof (in) - strlen(in) - 1);
  #ifdef KRB5_KRB4_COMPAT
  		if (strcmp(auth_type, "KERBEROS_V4") == 0) {
! 			if ((length = clevel == PROT_P ?
! 			     krb_mk_priv((unsigned char *)in,
! 					 (unsigned char *)out,
! 					 strlen(in), schedule, &kdata.session,
! 					 &ctrl_addr, &his_addr)
! 			     : krb_mk_safe((unsigned char *)in,
! 					   (unsigned char *)out,
! 					   strlen(in), &kdata.session,
! 					   &ctrl_addr, &his_addr)) == -1) {
  				syslog(LOG_ERR,
  				       "krb_mk_%s failed for KERBEROS_V4",
  				       clevel == PROT_P ? "priv" : "safe");
- - --- 1794,1825 ----
  #endif

  	if (auth_type) {
! 		/*
! 		 * Deal with expansion in mk_{safe,priv},
! 		 * radix_encode, gss_seal, plus slop.
! 		 */
! 		char in[FTP_BUFSIZ*3/2], out[FTP_BUFSIZ*3/2];
  		int length, kerror;
  		if (n) sprintf(in, "%d%c", n, cont_char);
  		else in[0] = '\0';
  		strncat(in, buf, sizeof (in) - strlen(in) - 1);
  #ifdef KRB5_KRB4_COMPAT
  		if (strcmp(auth_type, "KERBEROS_V4") == 0) {
! 			if (clevel == PROT_P)
! 				length = krb_mk_priv((unsigned char *)in,
! 						     (unsigned char *)out,
! 						     strlen(in),
! 						     schedule, &kdata.session,
! 						     &ctrl_addr,
! 						     &his_addr);
! 			else
! 				length = krb_mk_safe((unsigned char *)in,
! 						     (unsigned char *)out,
! 						     strlen(in),
! 						     &kdata.session,
! 						     &ctrl_addr,
! 						     &his_addr);
! 			if (length == -1) {
  				syslog(LOG_ERR,
  				       "krb_mk_%s failed for KERBEROS_V4",
  				       clevel == PROT_P ? "priv" : "safe");
***************
*** 1803,1815 ****
  		}
  #endif /* GSSAPI */
  		/* Other auth types go here ... */
! 		if (kerror = radix_encode(out, in, &length, 0)) {
  			syslog(LOG_ERR, "Couldn't encode reply (%s)",
  					radix_error(kerror));
  			fputs(in,stdout);
  		} else
! 		printf("%s%c%s", clevel == PROT_P ? "632" : "631",
! 				 n ? cont_char : '-', in);
  	} else {
  		if (n) printf("%d%c", n, cont_char);
  		fputs(buf, stdout);
- - --- 1863,1878 ----
  		}
  #endif /* GSSAPI */
  		/* Other auth types go here ... */
! 		if (length >= sizeof(in) / 4 * 3) {
! 			syslog(LOG_ERR, "input to radix_encode too long");
! 			fputs(in, stdout);
! 		} else if (kerror = radix_encode(out, in, &length, 0)) {
  			syslog(LOG_ERR, "Couldn't encode reply (%s)",
  					radix_error(kerror));
  			fputs(in,stdout);
  		} else
! 			printf("%s%c%s", clevel == PROT_P ? "632" : "631",
! 			       n ? cont_char : '-', in);
  	} else {
  		if (n) printf("%d%c", n, cont_char);
  		fputs(buf, stdout);
***************
*** 1822,1827 ****
- - --- 1885,1894 ----
  	}
  }

+ /*
+  * XXX callers need to limit total length of output string to
+  * FTP_BUFSIZ
+  */
  #ifdef STDARG
  lreply(int n, char *fmt, ...)
  #else
***************
*** 1866,1872 ****

  	if (cp = strchr(cbuf,'\n'))
  		*cp = '\0';
! 	reply(500, "'%s': command not understood.", cbuf);
  }

  delete_file(name)
- - --- 1933,1940 ----

  	if (cp = strchr(cbuf,'\n'))
  		*cp = '\0';
! 	reply(500, "'%.*s': command not understood.",
! 	      FTP_BUFSIZ - sizeof("'': command not understood."), cbuf);
  }

  delete_file(name)
***************
*** 2143,2149 ****
  	int code;
  	char *string;
  {
! 	reply(code, "%s: %s.", string, strerror(errno));
  }

  auth(type)
- - --- 2211,2233 ----
  	int code;
  	char *string;
  {
! 	char *err_string;
! 	size_t extra_len;
!
! 	err_string = strerror(errno);
! 	if (err_string == NULL)
! 		err_string = "(unknown error)";
! 	extra_len = strlen(err_string) + sizeof("(truncated): .");
!
! 	/*
! 	 * XXX knows about FTP_BUFSIZ in reply()
! 	 */
! 	if (strlen(string) + extra_len > FTP_BUFSIZ) {
! 		reply(code, "(truncated)%.*s: %s.",
! 		      FTP_BUFSIZ - extra_len, string, err_string);
! 	} else {
! 		reply(code, "%s: %s.", string, err_string);
! 	}
  }

  auth(type)
***************
*** 2226,2231 ****
- - --- 2310,2319 ----
  			secure_error("ADAT: krb_mk_safe failed");
  			return(0);
  		}
+ 		if (length >= (FTP_BUFSIZ - sizeof("ADAT=")) / 4 * 3) {
+ 			secure_error("ADAT: reply too long");
+ 			return(0);
+ 		}
  		if (kerror = radix_encode(out_buf, buf, &length, 0)) {
  			secure_error("Couldn't encode ADAT reply (%s)",
  				     radix_error(kerror));
***************
*** 2360,2365 ****
- - --- 2448,2463 ----
  		}

  		if (out_tok.length) {
+ 			if (out_tok.length >= ((FTP_BUFSIZ - sizeof("ADAT="))
+ 					       / 4 * 3)) {
+ 				secure_error("ADAT: reply too long");
+ 				syslog(LOG_ERR, "ADAT: reply too long");
+ 				(void) gss_release_cred(&stat_min, &server_creds);
+ 				if (ret_flags & GSS_C_DELEG_FLAG)
+ 					(void) gss_release_cred(&stat_min,
+ 								&deleg_creds);
+ 				return(0);
+ 			}
  			if (kerror = radix_encode(out_tok.value, gbuf, &out_tok.length, 
0)) {
  				secure_error("Couldn't encode ADAT reply (%s)",
  					     radix_error(kerror));
***************
*** 2458,2463 ****
- - --- 2556,2564 ----
   *	n>=0 on success
   *	-1 on error
   *	-2 on security error
+  *
+  * XXX callers need to limit total length of output string to
+  * FTP_BUFSIZ
   */
  #ifdef STDARG
  secure_fprintf(FILE *stream, char *fmt, ...)
***************
*** 2575,2580 ****
- - --- 2676,2690 ----
  			    dir->d_name[2] == '\0')
  				continue;

+ 			if (strlen(dirname) + strlen(dir->d_name)
+ 			    + 1 /* slash */
+ 			    + 2	/* CRLF */
+ 			    + 1 > sizeof(nbuf)) {
+ 				syslog(LOG_ERR,
+ 				       "send_file_list: pathname too long");
+ 				ret = -2; /* XXX */
+ 				goto data_err;
+ 			}
  			sprintf(nbuf, "%s/%s", dirname, dir->d_name);

  			/*

- -----BEGIN PGP SIGNATURE-----
Version: PGP 6.5.8

iQCVAwUBOudtAKbDgE/zdoE9AQHhJgP/RFEDX/KL3YoavQSP9jJYO+GTg2MBfWRd
B4wakx2PYbt4LSGSNu/VyZKFGQhVqe0F38C7oGBrCyRzZfC5MPSBmo/B6pxaeM9P
oUo3Bny+JgybyOZ9wp7pGW2cRHH/zKbakrsaGFWgeAucceZeDana+TEZqGlQLIst
wfRPsXU7WA8=
=+0c0
- -----END PGP SIGNATURE-----

------- End of Forwarded Message




To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-security" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200104270215.f3R2FHP61668>