COMMAND
catopen() (libc)
SYSTEMS AFFECTED
FreeBSD 5.0-CURRENT, 4.x and 3.x prior to 2000-09-27
PROBLEM
Following is based on FreeBSD-SA-00:53 Security Advisory.
catopen() and setlocale() are functions which are used to display
text in a localized format, e.g. for international users.
There are two problems addressed in this advisory:
1) The catopen() function did not correctly bounds-check an
internal buffer which could be indirectly overflowed by the
setting of an environment variable. A privileged application
which uses catopen() could be made to execute arbitrary code
by an unprivileged local user.
2) The catopen() and setlocale() functions could be made to use an
arbitrary file as the source for localized data and message
catalogs, instead of one of the system files. An attacker
could create a file which is a valid locale file or message
catalog but which contains special formatting characters which
may allow certain badly written privileged applications to be
exploited and execute arbitrary code as the privileged user.
This second vulnerability is slightly different from the problem
originally discovered by Ivan Arce of Core-SDI which affects
multiple UNIX operating systems, which involved a different
environment variable and which FreeBSD is not susceptible to.
However Vulnerability 2 was discovered in FreeBSD after the
publication the Core-SDI advisory, and has the same effect on
vulnerable applications.
Note that the FreeBSD base system is not believed to be vulnerable
to either of these problems, nor are any vulnerable third party
programs (including FreeBSD ports) currently known. Therefore
the impact on the majority of FreeBSD systems is expected to be
nonexistent.
SOLUTION
Vulnerability 1 described above is the more serious of the two,
since it does not require the application to contain a coding
flaw in order to exploit it. A scanning utility is provided to
detect privileged binaries which use the catopen() function (both
statically and dynamically linked binaries), which should be
either rebuilt, or have their privileges limited to minimize
potential risk. It is not feasible to detect binaries which are
vulnerable to the second vulnerability, however the provided
utility will also report statically linked binaries which use the
setlocale() functions and which *may* potentially be vulnerable.
Most of the binaries reported will not in fact be vulnerable, but
should be recompiled anyway for maximum assurance of security.
Note that some FreeBSD system binaries may be reported as
possibly vulnerable by this script, however this is not the case.
Statically linked binaries which are identified as vulnerable or
potentially vulnerable should be recompiled from source code after
patching and recompiling libc, if possible, in order to correct
the vulnerability. Dynamically linked binaries will be corrected
by simply patching and recompiling libc as described below.
As an interim measure, consider removing any identified setuid or
setgid binary, removing set[ug]id privileges from the file, or
limiting the file access permissions, as appropriate.
Of course, it is possible that some of the identified files may be
required for the correct operation of your local system, in which
case there is no clear workaround except for limiting the set of
users who may run the binaries, by an appropriate use of user
groups and removing the "o+x" file permission bit.
1) Download the 'scan_locale.sh' and 'test_locale.sh' scripts from
ftp://ftp.freebsd.org/pub/FreeBSD/CERT/tools/SA-00:53/scan_locale.sh
ftp://ftp.freebsd.org/pub/FreeBSD/CERT/tools/SA-00:53/test_locale.sh
2) Verify the md5 checksums and compare to the value below:
# /sbin/md5 scan_locale.sh
MD5 (scan_locale.sh) = efea80f74b05e7ddbc0261ef5211e453
# /sbin/md5 test_locale.sh
MD5 (test_locale.sh) = 2a485bf8171cc984dbc58b4d545668b4
3) Run the scan_locale.sh script against your system:
# sh scan_locale.sh ./test_locale.sh /
This will scan your entire system for setuid or setgid binaries
which make use of the exploitable function catopen(), or the
potentially exploitable function setlocale(). Each returned
binary should be examined (eg. with 'ls -l' and/or other tools)
to determine what security risk it poses to your local
environment, eg. whether it can be run by arbitrary local users
who may be able to exploit it to gain privileges.
Note that this script reports setlocale() usage (i.e.
vulnerability 2) only in statically linked binaries, not
dynamically linked binaries, because of the high rate of false
positives. It is likely that the majority of such setlocale()
binaries identified are not insecure and their identification
by this script should not be taken as evidence that they are
vulnerable, but they should be recompiled anyway for maximum
assurance of security.
4) Remove the binaries, or reduce their file permissions, as
appropriate.
Upgrade your vulnerable FreeBSD system to 4.1-STABLE or 3.5-STABLE
after the correction date, or patch your present system source
code and rebuild. Then run the scan_locale.sh script as
instructed above and identify any statically-linked binaries
as reported by the script. These should either be removed,
recompiled, or have privileges restricted to secure them against
this vulnerability (since statically-linked binaries will not be
affected by simply recompiling the shared libc library).
To patch your present system: save the patch below into a file,
and execute the following commands as root:
cd /usr/src/lib/libc
patch < /path/to/patch/file
make all
make install
Patches for FreeBSD systems before the correction date:
Index: msgcat.c
===================================================================
RCS file: /usr2/ncvs//src/lib/libc/nls/msgcat.c,v
retrieving revision 1.21
retrieving revision 1.27
diff -u -r1.21 -r1.27
--- nls/msgcat.c 2000/01/27 23:06:33 1.21
+++ nls/msgcat.c 2000/09/01 11:56:31 1.27
@@ -91,8 +91,9 @@
__const char *catpath = NULL;
char *nlspath;
char *lang;
- long len;
char *base, *cptr, *pathP;
+ int spcleft;
+ long len;
struct stat sbuf;
if (!name || !*name) {
@@ -106,10 +107,10 @@
} else {
if (type == NL_CAT_LOCALE)
lang = setlocale(LC_MESSAGES, NULL);
- else {
- if ((lang = (char *) getenv("LANG")) == NULL)
- lang = "C";
- }
+ else
+ lang = getenv("LANG");
+ if (lang == NULL || strchr(lang, '/') != NULL)
+ lang = "C";
if ((nlspath = (char *) getenv("NLSPATH")) == NULL
#ifndef __NETBSD_SYSCALLS
|| issetugid()
@@ -129,13 +130,22 @@
*cptr = '\0';
for (pathP = path; *nlspath; ++nlspath) {
if (*nlspath == '%') {
+ spcleft = sizeof(path) - (pathP - path);
if (*(nlspath + 1) == 'L') {
++nlspath;
- strcpy(pathP, lang);
+ if (strlcpy(pathP, lang, spcleft) >= spcleft) {
+ free(base);
+ errno = ENAMETOOLONG;
+ return(NLERR);
+ }
pathP += strlen(lang);
} else if (*(nlspath + 1) == 'N') {
++nlspath;
- strcpy(pathP, name);
+ if (strlcpy(pathP, name, spcleft) >= spcleft) {
+ free(base);
+ errno = ENAMETOOLONG;
+ return(NLERR);
+ }
pathP += strlen(name);
} else *(pathP++) = *nlspath;
} else *(pathP++) = *nlspath;
@@ -186,7 +196,7 @@
MCSetT *set;
long lo, hi, cur, dir;
- if (!cat || setId <= 0) return(NULL);
+ if (cat == NULL || setId <= 0) return(NULL);
lo = 0;
if (setId - 1 < cat->numSets) {
@@ -212,8 +222,8 @@
if (hi - lo == 1) cur += dir;
else cur += ((hi - lo) / 2) * dir;
}
- if (set->invalid)
- (void) loadSet(cat, set);
+ if (set->invalid && loadSet(cat, set) <= 0)
+ return(NULL);
return(set);
}
@@ -225,7 +235,7 @@
MCMsgT *msg;
long lo, hi, cur, dir;
- if (!set || set->invalid || msgId <= 0) return(NULL);
+ if (set == NULL || set->invalid || msgId <= 0) return(NULL);
lo = 0;
if (msgId - 1 < set->numMsgs) {
@@ -318,7 +328,7 @@
off_t nextSet;
cat = (MCCatT *) malloc(sizeof(MCCatT));
- if (!cat) return(NLERR);
+ if (cat == NULL) return(NLERR);
cat->loadType = MCLoadBySet;
if ((cat->fd = _open(catpath, O_RDONLY)) < 0) {
@@ -351,7 +361,7 @@
cat->numSets = header.numSets;
cat->sets = (MCSetT *) malloc(sizeof(MCSetT) * header.numSets);
- if (!cat->sets) NOSPACE();
+ if (cat->sets == NULL) NOSPACE();
nextSet = header.firstSet;
for (i = 0; i < cat->numSets; ++i) {
Index: setlocale.c
===================================================================
RCS file: /home/ncvs/src/lib/libc/locale/setlocale.c,v
retrieving revision 1.27
retrieving revision 1.28
diff -u -r1.27 -r1.28
--- locale/setlocale.c 2000/09/04 03:43:24 1.27
+++ locale/setlocale.c 2000/09/08 07:29:48 1.28
@@ -129,7 +129,7 @@
if (!env || !*env)
env = getenv("LANG");
- if (!env || !*env)
+ if (!env || !*env || strchr(env, '/'))
env = "C";
(void) strncpy(new_categories[category], env, ENCODING_LEN);