COMMAND
default cron jobs (/etc/security & /etc/daily)
SYSTEM AFFECTED
4.4BSDlite2, OpenBSD 2.0, FreeBSD 2.1.5, BSD O/S 2.0, NetBSD 1.2
PROBLEM
The 4.4BSDlite2 version of /etc/security passes unchecked data to
a shell. These bugs make it possible for unpriviliged users to
obtain root access, EVEN IF THERE ARE NO SETUID PROGRAMS ON THE
SYSTEM.
The first problem with /etc/security is that it passes unchecked
data to a shell. If a user creates a file whose name contains
shell metacharacters and makes it executable and setuid,
/etc/security will gladly execute commands specified in the name
of the file as root. The problem is the big find line used to
search for setuid files, which in 4.4BSDlite2 reads:
(find / ! -fstype local -a -prune -o \
\( -perm -u+s -o -perm -g+s -o ! -type d -a ! -type f -a ! -type l -a \
! -type s \) | \
sort | sed -e 's/^/ls -ldgT /' | sh > $LIST) 2> $OUTPUT
The second problem with /etc/security is its poor use of temporary
files. In 4.4BSDLite2 /etc/security uses six temporary files
unsafely. They are all named /tmp/_secure?.$$, where ? is a
number in the range 1 through 6, and $$ is replaced with the
process id of the shell interpreting /etc/security at run time.
A malicious user needs merely to run an at job a minute before
/etc/security which creates symlinks named /tmp/_secure?.$$, and
wait for the cron job to overwrite the file of his choice. In
addition, the user has much control over the contents of some of
these temporary files, allowing users to obtain root access.
Similarly, the /etc/daily script search for core files to be
deleted can be induced to corrupt arbitrary files, and even
create valid .rhosts files. By creating files with names like:
+ + #.core
and leaving an appropriate symbolic link in /tmp, users can obtain
root priviliges.
SOLUTION
The version of /etc/security in OpenBSD 2.0 appears safe, as does
the version of /etc/daily in OpenBSD-current. On most operating
systems, mkdir is both atomic, and does not follow symbolic
links. Therefore it is possible to use mkdir in a shell script
to write portable and secure code.
# A viable /etc/security, which requires OpenBSD or GNU
# find and xargs.
# note that this version lacks features found in the 4.4Lite2
# /etc/security.
#------------------------- cut here -----------------------------
#!/bin/sh
#
PATH=/sbin:/bin:/usr/bin
LC_ALL=C; export LC_ALL
host=`hostname -s`
echo "Subject: $host security check output"
LOG=/var/log
umask 077
TDIR=/tmp/_secure.$$
if ! mkdir $TDIR ; then
echo $TDIR already exists
ls -alF $TDIR
exit 1
fi
TMP=$TDIR/secure
trap 'rm -rf $TDIR' 0 1 2 3 4 5 6 7 8 10 11 12 13 14 15
echo "checking setuid files and devices:"
find / -fstype local -and -type f -and \
\( -perm 4000 -or -perm 2000 \) -print0 | sort \
| xargs -0 ls -lgTd > $TMP
if [ ! -f $LOG/setuid.today ] ; then
echo "no $LOG/setuid.today"
cp $TMP $LOG/setuid.today
fi
if cmp $LOG/setuid.today $TMP >/dev/null; then :; else
echo "$host setuid diffs:"
diff -b $LOG/setuid.today $TMP
mv $LOG/setuid.today $LOG/setuid.yesterday
mv $TMP $LOG/setuid.today
fi
rm -f $TMP
#------------------------- cut here -----------------------------
# A viable /etc/daily based around the OpenBSD one:
#------------------------- cut here -----------------------------
#!/bin/sh -
PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local
host=`hostname -s`
echo "Subject: $host daily run output"
if [ -f /etc/daily.local ];then
echo ""
echo "Running daily.local:"
. /etc/daily.local
fi
UMASK=`umask`
umask 077
TDIR=/tmp/_daily.$$
if ! mkdir $TDIR ; then
echo $TDIR already exists
echo ls -ldgT $TDIR
exit 1
fi
umask $UMASK
TMP=$TDIR/daily
trap 'rm -rf $TDIR' 0 1 2 3 4 5 6 7 8 10 11 12 13 14 15
echo ""
echo "NOT Removing scratch and junk files."
find / \( ! -fstype local -o -fstype rdonly -o -fstype fdesc \
-o -fstype kernfs -o -fstype procfs \) -a -prune -o \
-name 'lost+found' -a -prune -o \
-name '*.core' -a -print > $TMP
if egrep -q '\.core$' $TMP; then
echo ""
echo "Possible core dumps:"
egrep '\.core$' $TMP
fi
msgs -c
if [ -f /etc/news.expire ]; then
/etc/news.expire
fi
if [ -f /var/account/acct ]; then
echo "" ;
echo "Purging accounting records:" ;
mv /var/account/acct.2 /var/account/acct.3 ;
mv /var/account/acct.1 /var/account/acct.2 ;
mv /var/account/acct.0 /var/account/acct.1 ;
cp /var/account/acct /var/account/acct.0 ;
sa -sq ;
fi
echo ""
if [ -d /var/yp/binding -a ! -d /var/yp/`domainname` ]; then
echo "Not running calendar, (yp client)."
else
echo "Running calendar."
calendar -a
fi
# Rotation of mail log now handled automatically by cron and 'newsyslog'
if [ -d /var/spool/uucp -a -f /etc/uuclean.daily ]; then
echo ""
echo "Cleaning up UUCP:"
echo /etc/uuclean.daily | su daemon
fi
echo ""
echo "Checking subsystem status:"
echo ""
echo "disks:"
df -k
echo ""
dump W
echo ""
mailq > $TMP
if ! grep -q "^Mail queue is empty$" $TMP; then
echo ""
echo "mail:"
cat $TMP
fi
if [ -d /var/spool/uucp ]; then
uustat -a > $TMP
if [ -s $TMP ]; then
echo ""
echo "uucp:"
cat $TMP
fi
fi
echo ""
echo "network:"
netstat -i
echo ""
t=/var/rwho/*
if [ "$t" != '/var/rwho/*' ]; then
ruptime
fi
echo ""
echo "NOT checking filesystems."
#echo "Checking filesystems:"
#fsck -n | grep -v '^\*\* Phase'
echo ""
if [ -f /etc/Distfile ]; then
echo "Running rdist:"
rdist -f /etc/Distfile
fi
sh /etc/security 2>&1 | mail -s "$host daily insecurity output" root
#------------------------- cut here -----------------------------