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.