package KUANG; main::pdebug "Loading Kuang rule now ...", 6.1; # writers() # Get the writers of the named file or directory - return as (UID, GID, OTHER) # triplet. Owner can always write, since he can chmod the file if he # wants. # # Calls: main::stat() # Called From: KUANG::eval_access(). # Expects: A filename as the only argument. # Returns: A string containing the owner, group and other permissions # associated with the filename given on invocation. # Side effects: none. # Errors: If the file doesn't exist (as tested with the "-e" test) the # string returned is empty. # # (fixme) are there any problems in this sort of builtin rule? should # we make this knowledge more explicit? # sub writers { my($target) = @_; my(@srtn,$gid,$other,$uid); @srtn = main::stat($target); if ( $#srtn != -1 ) { main::pdebug "stat(\"$target\"), mode: $srtn[2], uid/gid: $srtn[4]/$srtn[5]", '6.3'; $uid = $srtn[4]; $gid = $srtn[5]; if (($srtn[2] & 020) != 020) { $gid = ""; } if ( ( $srtn[2] & 02 ) == 02 ) { $other = 1; } else { $other = 0; } } else { ($uid,$gid,$other) = ("","",""); } return($uid, $gid, $other); } # eval_access() # Given a filename, add appropriate plans reflecting the access to the file # itself and the directory which contains it. # # Called from KUANG::control_file_attacks(). # Calls KUANG:writers() # Expects: a filename and a reference to a "new_plans" associative array. # Returns: none. # Side effects: Creates new entries in the "new_plans" assoc. array. # Error checking: none. # sub eval_access { my($ob,$new_plans_r) = @_; # See if we can overwrite the file my($owner, $group, $other) = &writers($ob); # owner can w file always with chmod. $$new_plans_r{"w $ob u $owner"} = "$ob is writeable by uid $owner" if ($owner ne ""); $$new_plans_r{"w $ob g $group"} = "$ob is writeable by gid $group" if ($group ne ""); $$new_plans_r{"w $ob u .*"} = "$ob is world writeable" if ($other); # We can replace the file if we can write to # the directory containing the file. my($dirname); ( $dirname = $ob ) =~ s/(.+)\/[^\/]+/$1/; ($owner, $group, $other) = &writers($dirname); $$new_plans_r{"r $ob u $owner"} = "directory of $ob is writeable by uid $owner" if ($owner ne ""); $$new_plans_r{"r $ob g $group"} = "directory of $ob is writeable by gid $group" if ($group ne ""); $$new_plans_r{"r $ob u .*"} = "directory of $ob is world writeable" if ($other); } # end of eval_access() # control_file_attacks() # This routine establishes some rules indicating that access to a given # set of files will allow access to the root account. It then goes # through the list of files to create rules about who does have access # to these files. # # Expects: A reference to a "new_plans" associative array. # Calls: KUANG::eval_access() # Called by: KUANG::run_KUANG() # Returns: nothing. # Side-effects: Creates new entries in "new_plans" assoc. array. # Errors: none. sub control_file_attacks { my($new_plans_r) = pop(@_); # # Controlling files for root and users. If any of these files can # be replaced or written by non-root user, then the system is # vulnerable. my(@sys_files) = ("/etc/passwd","/usr/lib/aliases", "/etc/aliases", "/etc/rc", "/etc/rc.boot", "/etc/rc.single", "/etc/rc.config", "/etc/rc.local", "/usr/lib/crontab", "/usr/spool/cron/crontabs","/etc/hosts.eqiv"); foreach (@sys_files) { # first we create the rules pertaining to the control-files ... $$new_plans_r{"u 0 r $_"} = "root access via replaceable $_"; $$new_plans_r{"u 0 w $_"} = "root access via. writeable $_"; # then we create rules describing the access to these files. eval_access($_,$new_plans_r); } ## Check uid specific "home" directory and "control files". If any ## are writeable by other than root or the uidthemselves, then that ## uid can be accessed. my(@pwent); while ( @pwent = main::getpwent ) { my($home_s) = $pwent[7]; my($uid_s) = $pwent[2]; if ( $home_s ne "" && main::stat($home_s) ) { eval_access($home_s,$new_plans_r); $home_s = "" if ($home_s eq "/"); foreach (".rhosts",".login",".logout",".cshrc",".profile") { my($target) = "$home_s/$_"; if ( main::stat($target) ) { $$new_plans_r{"u $uid_s r $target"} = "Access user if I can replace $_ control file"; $$new_plans_r{"u $uid_s w $target"} = "Access user if I can write $_ control file"; eval_access("$target", $new_plans_r); } } } } } # end of control_file_attacks() # gid_attacks () # # Called by: anonymous return for this file. sub gid_attacks { my($new_plans_r) = pop(@_); # # Plans for attacking GIDs... # N.B. At this stage all this routine does is load the plans. It # should evaluate the state of the files first, to see if the plan # is worth loading. # # If we can write or replace /etc/group we can become any group # $$new_plans_r{"g .* r /etc/group"} = "/etc/group can be replaced"; $$new_plans_r{"g .* w /etc/group"} = "/etc/group can be overwritten"; eval_access("/etc/group", $new_plans_r); } # end of gid_attacks() # main for KUANG # the "well defined" name of the KuangPlus routine which will, in turn, invoke # the subroutines created above. # # Expects: nothing # Calls: KUANG::control_file_attacks() # Called by: KuangPlus::kuangplus() # Returns: a new associative array of plans. # Side effects: none. # Errors: none. my(@uname) = main::uname(); if ( $uname[0] =~ /(Linux|FreeBSD)/ ) { main::pdebug "Invoking KUANG rule set\n", 6.3; KUANG::control_file_attacks(\%main::new_plans); KUANG::gid_attacks(\%main::new_plans); } else { main::pdebug "Not appropriate platform for KUANG rules\n", 6.3; }