home *** CD-ROM | disk | FTP | other *** search
- From: df@death.cert.sei.cmu.edu (Dan Farmer)
- Newsgroups: alt.sources
- Subject: (anonymous) ftp checker, cops, etc.
- Message-ID: <DF.90Nov20174708@death.cert.sei.cmu.edu>
- Date: 20 Nov 90 22:47:08 GMT
-
-
- Thought I'd toss this out to the sharks. This is a shar file with a shell
- script and an C program that attempts to check for proper anonymous ftp
- setup. Comments are encouraged; after a bit, I'll put out another patch to
- COPS with this and other stuff in it (to forstall questions, cops is a
- security checking thingee that is available via anon-ftp from cert.sei.cmu.edu,
- and uunet.uu.net, or other fine archive sites near you.)
-
- -- dan
- df@cert.sei.cmu.edu
-
- (More or less what the man page will say:)
-
- This shell script checks to see if you've set up (mainly anonymous)
- ftp correctly. The "-a" option checks your anon-ftp setup; without that,
- this script doesn't do a whole lot -- just check to see if your ftpusers
- file doesn't have any root accounts in it.
-
- There is no "right" way to set up ftp, but there are lots of wrong
- ways :-) I suggest everything be owned by either root or ftp, and nothing
- be world writable, with the exception of a ~ftp/incoming directory or
- something like that. You can change the owners via the $primary and
- $secondary variables (default root & ftp, respectively), and the publically
- writable directory is $incoming (default ~ftp/incoming). Do not make
- ~ftp/pub world writable, if you are storing data or programs for people
- to use; you're inviting intruders to write all over the files and programs,
- and leave all kinds of nasties...
-
- Here are the assumptions I made for anon-ftp:
-
- o User "ftp" should have a non-valid password ("*", whatever) and a invalid
- shell, but a valid home directory -- this is where all the anonymous
- stuff gets stashed. This checks for the passwd and valid home dir only.
- I would suggest a .rhosts file of 0 size, owned by root, but that's
- personal preference. This will complain if a .rhosts file exists, and
- is either non-0 or non-root owned.
-
- o All root equivalent accounts (uid=0) with valid passwords should be in
- /etc/ftpusers
-
- o The home dir for ftp is in /etc/passwd, should be a valid directory, and
- should not be "/" (if the dir is invalid, ftpd should choke.)
-
- o The ~ftp/etc/{passwd|group} files should be different than their
- counterparts in /etc (don't want password files available via anon-ftp.)
- In addition, it seems as though the entries in ~ftp/etc/{passwd|group}
- files don't do a whole lot -- you might see something like:
- With the entries:
- drwxr-xr-x 8 cert ftp 512 Nov 7 16:56 pub/
- Without:
- drwxr-xr-x 8 8001 105 512 Nov 7 16:56 pub/
- Some versions of ftpd allow you to leave the files off entirely; that
- is the preferred method, IMHO; else, you might try putting a null file
- there. Experiment...
-
- o ~ftp, ~ftp/bin, ~/ftp/etc should all be non-world-writeable, and owned
- by either root or ftp. The ls command should be mode 111, the password
- and group files 444.
-
-
-
- Finally, the shar:
-
- ================Cut here with pointy sharp things=============================
- #!/bin/sh
- # This is a shell archive (shar 3.10)
- # made 11/20/1990 20:09 UTC by df@death.cert.sei.cmu.edu
- # Source directory /tmp/junk
- #
- # existing files WILL be overwritten
- #
- # This shar contains:
- # length mode name
- # ------ ---------- ------------------------------------------
- # 5930 -rwx------ ftp.chk
- # 4196 -rw------- is_able.c
- #
- touch 2>&1 | fgrep '[-amc]' > /tmp/s3_touch$$
- if [ -s /tmp/s3_touch$$ ]
- then
- TOUCH=can
- else
- TOUCH=cannot
- fi
- rm -f /tmp/s3_touch$$
- # ============= ftp.chk ==============
- sed 's/^X//' << 'SHAR_EOF' > ftp.chk &&
- X:
- X#!/bin/sh
- X#
- X# Usage: ftp.chk [-a]
- X#
- X# This shell script checks to see if you've set up (mainly anonymous)
- X# ftp correctly. The "-a" option checks your anon-ftp setup; without that,
- X# this script doesn't do a whole lot -- just check to see if your ftpusers
- X# file doesn't have any root accounts in it.
- X#
- X# See the man page for a more detailed description, but here's what this
- X# checks for:
- X#
- X# - User ftp exists in the password file.
- X# - root (or all root equivalents) are in ftpusers file.
- X# - Home directory for ftp should exist, and not be /
- X# - The ~ftp/etc/{passwd|group} should not be the same as the real ones.
- X# - Various critical files/directories should exist, and have correct
- X# permissions and owners:
- X#
- X# File/Dir Perms Owner Other
- X# ========= ====== ====== ======
- X# ~ftp 555 ftp
- X# or
- X# ~ftp non-w.w. root
- X#
- X# ~ftp/bin non-w.w. root/ftp
- X# ~ftp/bin/ls 111 root/ftp
- X# ~ftp/etc non-w.w. root/ftp non-w.w. root 0 size, is optional
- X# ~ftp/* non-w.w. other dirs/files in ~ftp
- X#
- X
- X# Where is everyone?
- XECHO=/bin/echo
- XTEST=/bin/test
- XAWK=/bin/awk
- XGREP=/bin/grep
- XLS=/bin/ls
- XCMP=/bin/cmp
- X
- X# If an argument is present, it should be an "a"
- Xif $TEST $# -gt 1 ; then
- X $ECHO Usage: $0 [-a]
- X exit 1
- X fi
- Xif $TEST $# -eq 1 ; then
- X if $TEST $1 = "-a" ; then
- X anonymous=yes
- X else
- X $ECHO Usage: $0 [-a]
- X exit 1
- X fi
- X fi
- X
- X#
- X# some might have this as ftpd; is the account in /etc/passwd
- Xftpuid=ftp
- X
- X# system files
- Xftpusers=/etc/ftpusers
- Xpasswd=/etc/passwd
- Xgroup=/etc/group
- X
- X# ftp's files:
- Xftproot=`$AWK -F: '/^'"$ftpuid"':/{print $6}' $passwd`
- X
- X# if the user ftp doesn't exist, no-anon stuff....
- Xif $TEST -z $ftproot -a "$anonymous" = "yes" ; then
- X $ECHO Warning! Need user $ftp for anonymous ftp to work!
- X exit
- X fi
- X
- Xftprhosts=$ftproot/.rhosts
- Xftpbin=$ftproot"/bin"
- Xftpls=$ftpbin"/ls"
- Xftpetc=$ftproot"/etc"
- Xftppasswd=$ftpetc"/passwd"
- Xftpgroup=$ftpetc"/group"
- X
- X# the pub/incoming stuff; by default, pub is *not* world writable, incoming
- X# is; if you want pub to be world writable, just change incoming to "pub"
- Xincoming=incoming
- Xftpincoming=$ftproot"/"$incoming
- Xftppub=$ftproot"/pub"
- X
- Xcrit_files="$ftpgroup $ftppasswd $ftpls"
- X
- X# primary and secondary owners...
- Xprimary=root
- Xsecondary=ftp
- X
- Xif $TEST -s $ftpusers
- X then
- X # check to see if root (or root equivalents) is in ftpusers file
- X all_roots=`$AWK -F: '{if ($3==0 && length($2)==13) printf("%s ", $1)}' $passwd`
- X if $TEST -n "$all_roots" ; then
- X for i in $all_roots
- X do
- X if $TEST ! "`$GREP $i $ftpusers`"
- X then
- X $ECHO Warning! $i should be in $ftpusers!
- X fi
- X done
- X fi
- X fi
- X
- X# do the anonymous ftp checking stuff now
- Xif $TEST -n "$anonymous" ; then
- X #
- X # ftp's home dir checking
- X if $TEST ! -d "$ftproot" -o -z "$ftproot"; then
- X $ECHO Warning! Home directory for ftp doesn\'t exist!
- X fi
- X if $TEST "$ftproot" = "/" ; then
- X $ECHO Warning! $ftproot ftp\'s home directory should not be \"/\"!
- X fi
- X #
- X # Don't want the passwd and group files to be the real ones!
- X if $TEST "`$CMP $passwd $ftppasswd 2> /dev/null`" ; then
- X :
- X else $ECHO Warning! $ftppasswd and $passwd are the same!
- X fi
- X if $TEST "`$CMP $group $ftpgroup 2> /dev/null`" ; then
- X :
- X else $ECHO Warning! $ftpgroup and $group are the same!
- X fi
- X
- X # want to check all the critical files and directories for correct
- X # ownership.
- X #
- X # This is what a "/bin/ls -l" of a file should look like:
- X # ---x--x--x 1 root 81920 Dec 31 1999 /bin/ls
- X # So in awk, $3 is the owner, $1 is the permission.
- X #
- X crit_files=$crit_files" "$ftpbin" "$ftpetc
- X for i in $crit_files
- X do
- X if $TEST ! -f $i -a ! -d $i; then
- X $ECHO Warning! File $i is missing!
- X fi
- X
- X owner=`$LS -ld $i | $AWK '{print $3}'`
- X if $TEST "$owner" = "$primary" -o "$owner" = "$secondary" ; then
- X :
- X else
- X $ECHO Warning! $i should be owned by $primary or $secondary!
- X fi
- X done
- X
- X # ftproot is special; if owned by root; should be !world writable;
- X # if owned by ftp, should be mode 555
- X owner=`$LS -ld $ftproot | $AWK '{print $3}'`
- X perms=`$LS -ld $ftproot | $AWK '{print $1}'`
- X if $TEST "$owner" = "$primary" -o "$owner" = "$secondary" ; then
- X :
- X else
- X $ECHO Warning! $i should be owned by $primary or $secondary!
- X fi
- X
- X if $TEST "$owner" = "$primary" ; then
- X ./is_able $ftproot w w
- X elif $TEST "$owner" != "$secondary" ; then
- X $ECHO Warning! $ftproot should be owned by $primary or $secondary!
- X elif $TEST "$perms" != "dr-xr-xr-x" ; then
- X $ECHO Warning! $ftproot should be mode 555!
- X fi
- X
- X #
- X # check the .rhosts file:
- X if $TEST -f $ftprhosts ; then
- X if $TEST -s $ftprhosts ; then
- X $ECHO Warning! $ftprhosts should be be empty!
- X fi
- X owner=`$LS -ld $ftprhosts | $AWK '{print $3}'`
- X if $TEST "$owner" = "$primary" -o "$owner" = "$secondary" ; then
- X :
- X else
- X $ECHO Warning! $ftprhosts should be owned by $primary or $secondary!
- X fi
- X fi
- X
- X #
- X # finally, some permissions of miscellaneous files:
- X perms=`$LS -ld $ftpls | $AWK '{print $1}'`
- X if $TEST "$perms" != "---x--x--x" ; then
- X $ECHO Warning! Incorrect permissions on \"ls\" in $ftpbin!
- X fi
- X
- X perms=`$LS -ld $ftppasswd | $AWK '{print $1}'`
- X if $TEST "$perms" != "-r--r--r--" ; then
- X $ECHO Warning! Incorrect permissions on \"passwd\" in $ftpetc!
- X fi
- X
- X perms=`$LS -ld $ftpgroup | $AWK '{print $1}'`
- X if $TEST "$perms" != "-r--r--r--" ; then
- X $ECHO Warning! Incorrect permissions on \"group\" in $ftpetc!
- X fi
- X
- X # Finally, the ~ftp/{pub|incoming|whatever} stuff; don't want the
- X # the number of blocks, or the "." and ".." entries:
- X all_dirs=`$LS -al $ftproot | $AWK '{if (NF >= 8) if ($NF!="." && $NF!="..") print $NF}'`
- X for i in $all_dirs
- X do
- X if $TEST -n "`is_able $ftproot/$i w w`" -a $i != "$ftpincoming" ; then
- X $ECHO Warning! Anon-ftp directory $i is World Writable!
- X fi
- X done
- X fi
- X
- X# end of script
- SHAR_EOF
- chmod 0700 ftp.chk || echo "restore of ftp.chk fails"
- if [ $TOUCH = can ]
- then
- touch -am 1120145290 ftp.chk
- fi
- # ============= is_able.c ==============
- sed 's/^X//' << 'SHAR_EOF' > is_able.c &&
- X/*
- X Usage: is_able filename {W|w|G|g} {R|r|W|w|B|b}
- X world/group read/write/both
- X
- X This checks determines whether a file is (group or world)
- X writable or readable, and prints out a short message to
- X that effect.
- X
- X Permissions bits: vvv--- Permission bits
- X 1 = execute 00000
- X 2 = writable ^
- X 4 = readable + Setuid bits
- X
- X Setuid bits:
- X 1 = sticky
- X 2 = set group id
- X 4 = set user od
- X
- X Pete Shipley (shipley@mica.berkeley.edu) gutted my original code,
- X made in cleaner and smarter, and combined everything into one compact
- X file. What a deal, huh? Then I came along and beat up his code and
- X made it look ugly again (I changed the is_writeable option to return
- X true if _any_ parent directories are writable, not just the target. So
- X you can blame me if you want. Better yet, just send me a patch if I
- X blew it.)
- X
- X*/
- X
- X#include <sys/types.h>
- X#include <sys/stat.h>
- X#include <ctype.h>
- X#include <stdio.h>
- X
- X#define G_READ_TEST 00044 /* group (or world) readable */
- X#define W_READ_TEST 00004 /* world readable */
- X#define G_READ_STRING "Warning! %s is group readable!\n"
- X#define W_READ_STRING "Warning! %s is _World_ readable!\n"
- X
- X#define G_WRITE_TEST 00022 /* group (or world) writable */
- X#define W_WRITE_TEST 00002 /* world writable */
- X#define G_WRITE_STRING "Warning! %s is group writable!\n"
- X#define W_WRITE_STRING "Warning! %s is _World_ writable!\n"
- X
- Xmain(argc,argv)
- Xint argc;
- Xchar **argv;
- X{
- Xchar file[256], wg, rwb, gstring[35],wstring[35];
- Xregister int group, read, write, both, verbose, xmode;
- Xstatic struct stat statb;
- X
- Xgroup=read=write=both=verbose=xmode=0;
- X
- X/* check out arguments */
- Xif (argc != 4) {
- X fprintf(stderr, "Usage: %s file {W|w|G|g} {R|r|W|w|B|b}\n",argv[0]);
- X exit(1);
- X }
- X
- X/* parse arguments */
- Xstrcpy(file, argv[1]);
- X
- X/* get stats on file in question -- if doesn't exist, exit */
- Xif (stat(file,&statb) < 0) {
- X perror(file);
- X exit(2);
- X }
- X
- Xif (isupper(argv[2][0]))
- X wg = tolower(argv[2][0]); /* world or group */
- Xelse wg = argv[2][0]; /* world or group */
- X
- Xif (isupper(argv[3][0]))
- X rwb = tolower(argv[3][0]); /* read/write/both */
- Xelse rwb = argv[3][0]; /* read/write/both */
- X
- X/* set the report string and some flags */
- Xif (wg == 'g') group = 1;
- X
- Xif (rwb == 'r') {
- X if (group) strcpy(gstring, G_READ_STRING);
- X else strcpy(wstring, W_READ_STRING);
- X read = 1;
- X }
- Xelse if (rwb == 'w') {
- X if (group) strcpy(gstring, G_WRITE_STRING);
- X else strcpy(wstring, W_WRITE_STRING);
- X write = 1;
- X }
- Xelse if (rwb == 'b') {
- X /* do the write first, then read check */
- X if (group) strcpy(gstring, G_WRITE_STRING);
- X else strcpy(wstring, W_WRITE_STRING);
- X both = read = write = 1;
- X }
- X
- X/*
- X * the write stuff, so to speak...
- X * What I'm doing in this mess is to parse the file in question, check out
- X * whole path; 'cause if anything is world writable, you can compromise.
- X * For instance, if /usr is world writable, then /usr/spool/mail is
- X * compromisable, no matter what its permissions are.
- X *
- X*/
- Xif (write) {
- X /* 256 levels of dirs, max len each 256 chars */
- X char foo_dirs[256][256];
- X char *foo_file;
- X int i = 0, j;
- X
- X foo_file = file;
- X strcpy(foo_dirs[i++], foo_file);
- X
- X j=strlen(foo_file) - 1;
- X do {
- X if (foo_file[j] == '/')
- X strncpy(foo_dirs[i++], foo_file, j);
- X } while (--j > 0);
- X
- X for (j = 0; j < i; j++) {
- X if (stat(foo_dirs[j],&statb) < 0)
- X continue;
- X xmode=statb.st_mode;
- X if (!group) {
- X if (xmode & W_WRITE_TEST) {
- X printf( wstring, file);
- X if (both) goto bboth;
- X exit(!xmode);
- X }
- X }
- X else if (xmode & G_WRITE_TEST) {
- X printf(gstring, file);
- X if (both) goto bboth;
- X exit(!xmode);
- X }
- X }
- X
- Xif (!both) exit(!xmode);
- X}
- X
- Xbboth: if (rwb == 'b') {
- X /* do the read now */
- X if (group) strcpy(gstring, G_READ_STRING);
- X else strcpy(wstring, W_READ_STRING);
- X }
- X
- X/*
- X * For the rest -- only flag if the file/dir in question passes test; you
- X * don't care about the rest of it's tree
- X *
- X*/
- X
- X/* test premissions on file in question */
- Xif (group)
- X xmode = statb.st_mode & G_READ_TEST;
- Xelse
- X xmode = statb.st_mode & W_READ_TEST;
- X
- X/* report finding */
- Xif (xmode) printf( (group ? gstring : wstring), file);
- X
- Xexit(!xmode);
- X}
- SHAR_EOF
- chmod 0600 is_able.c || echo "restore of is_able.c fails"
- if [ $TOUCH = can ]
- then
- touch -am 1120144490 is_able.c
- fi
- exit 0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- --
- -- dan
-
- df@cert.sei.cmu.edu
-