|
Vulnerability default cron jobs (/etc/security & /etc/daily) Affected 4.4BSDlite2, OpenBSD 2.0, FreeBSD 2.1.5, BSD O/S 2.0, NetBSD 1.2 Description 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 -----------------------------