Root Crontab Changes
[posted 1999/12/08]
Last summer, we had a couple of security incidents involving root crontab changes (among other things). Since then, we have been watching the crontabs rather closely. For a time, whenever a root crontab appeared to have changed, because PIKT reported a change in its timestamp or size, the PIKT alert would show the latest crontab in its entirety.
Good, except that on some systems, the timestamp was being updated daily, evidently because some automated process was rewriting the crontab regularly. And on those systems, every day we would have to inspect the crontab. In almost every case, there was no change, else the change was small and hard to discern.
If the timestamp changes, what if instead of dumping the entire crontab we diff it against the previous day's version? A revised CrontabChkWarning was the result:
CrontabChkWarning
init
status active
level warning
task "Check for crontab anomalies"
input proc "=ll =crontabs | =behead"
=lldata
keys $name
rule // initialize for current crontab
set #changed = #false()
rule // changed size?
if #defined(%size)
&& #size != %size
set #changed = #true()
output mail "$name has changed size to $text(#size),
was $text(%size)"
endif
rule // changed timestamp?
if #defined(%time)
&& $time ne %time
set #changed = #true()
output mail "$name has changed timestamp to
$mon $date $time, was %mon %date %time"
endif
rule // if changed, and we have a backup of the previous
// crontab, diff the current against the previous,
// else dump the current crontab
if #changed
if -e "=hstdir/crontabs/$name"
do #popen(DIFF, "=diff =hstdir/crontabs/$name
=crontabs/$name", "r")
while #read(DIFF) > 0
output mail $rdlin
endwhile
do #pclose(DIFF)
output mail =newline
else
do #fopen(CRONTAB, "=crontabs/$name", "r")
while #read(CRONTAB) > 0
output mail $rdlin
endwhile
do #fclose(CRONTAB)
output mail =newline
endif
endif
rule // save current crontab in the histories/crontabs directory
if ! -d "=hstdir/crontabs"
exec wait "=mkdir =hstdir/crontabs;
=chmod 750 =hstdir/crontabs"
endif
exec wait "cp -p =crontabs/$name =hstdir/crontabs"
rule // report if oddly-named
if $command("=egrep " . $squote() . "^?$name:" . $squote() .
" /etc/passwd") eq ""
&& $command("=ypmatch $name passwd 2>/dev/null") eq ""
output mail "no such acct: $inlin=newline"
endif
rule // report if non-root owner
if $owner ne "root"
#ifndef generic
# if db
&& $owner ne "ingres"
# endif
#endifdef
output mail "suspicious ownership: $inlin=newline"
do #fopen(CRONTAB, "=crontabs/$name", "r")
while #read(CRONTAB) > 0
output mail " $rdlin"
endwhile
do #fclose(CRONTAB)
output mail =newline
endif
We added three new rules: the first, where we initialize a new variable, #changed; the fourth, where we diff the current and previous crontab; and the fifth, where we save the current crontab in the histories/crontab directory. Note also the modifications to the second and third rules, where we set #changed to #true() if we detect crontab changes.
So, on a typical system, here are the contents of our /pikt/var/histories:
warsaw 508) find /pikt/var/histories -print /pikt/var/histories /pikt/var/histories/EMERGENCY.hst /pikt/var/histories/Debug.hst /pikt/var/histories/Urgent.hst /pikt/var/histories/Critical.hst /pikt/var/histories/Warning.hst /pikt/var/histories/Info.hst /pikt/var/histories/Notice.hst /pikt/var/histories/crontabs /pikt/var/histories/crontabs/adm /pikt/var/histories/crontabs/lp /pikt/var/histories/crontabs/root /pikt/var/histories/crontabs/sys /pikt/var/histories/crontabs/uucp
So, you see that your var/histories directory is a natural place to store earlier versions of files, not just .hst files, that you want to compare with their current versions.
Now we still get alerts when crontabs change their timestamp or size, but we
just see their diffs, if any.
For more examples, see Developer's Notes.