2026年8月12日
2026年8月12日
cron.d・cron.hourlyのセキュリティ確認方法:権限・中身・ログを点検する手順
/etc/cron.d や /etc/cron.hourly に置かれたファイルは、多くが root 権限で自動実行されます。攻撃者がここに1行書き足せば、毎時・毎分 root でコマンドを実行させられるため、侵入後に居座る手口(永続化)としてよく使われます。この記事では、cron 関連のファイルを点検する手順を、Debian/Ubuntu(cron パッケージ)と RHEL 系(AlmaLinux、Rocky Linux など。cronie パッケージ)の両方について説明します。
cron.d と cron.hourly の役割
| 場所 | 中身 | 実行のされ方 |
|---|---|---|
| /etc/crontab | システム全体の crontab | cron デーモンが読む。実行ユーザーを各行に書く |
| /etc/cron.d/ | パッケージや管理者が置く crontab 形式のファイル | cron デーモンが読む。書式は /etc/crontab と同じ(ユーザー名の列がある) |
| /etc/cron.hourly/ | 実行ファイル(シェルスクリプトなど) | run-parts が1時間ごとにまとめて実行する |
| /etc/cron.daily/ など | 同上(日次・週次・月次) | run-parts(または anacron)が実行する |
| /var/spool/cron/ | 各ユーザーが crontab -e で作った crontab |
Debian/Ubuntu は /var/spool/cron/crontabs/、RHEL 系は /var/spool/cron/ |
cron.hourly を誰が起動しているかはディストリビューションで異なります。RHEL 系では /etc/cron.d/0hourly が run-parts で /etc/cron.hourly を実行し、Debian/Ubuntu では /etc/crontab の行が run-parts で実行します。点検するときは「どの設定がどこを呼んでいるか」を先に確認しておくと、見落としが減ります。
# cron.hourly を呼び出している設定を探す
grep -rn "cron.hourly" /etc/crontab /etc/cron.d/ 2>/dev/null
ステップ1:所有者と権限を確認する
まずディレクトリとファイルの所有者・権限を見ます。
ls -ld /etc/crontab /etc/cron.d /etc/cron.hourly /etc/cron.daily /etc/cron.weekly /etc/cron.monthly
ls -l /etc/cron.d/ /etc/cron.hourly/
確認するポイントは次のとおりです。
- 所有者とグループがすべて root であること
- グループやその他のユーザーに書き込み権限(
w)が付いていないこと - cron.d のファイルに実行権限は不要(cron デーモンが読むだけ)。cron.hourly のスクリプトには実行権限が必要
Debian の cron のマニュアル(man cron)では、/etc/crontab と /etc/cron.d のファイルは root の所有で、グループやその他から書き込めない状態でなければならないと定められています。さらに Debian/Ubuntu では、cron.d のファイル名は英字・数字・下線・ハイフンだけで構成する必要があり、. を含むファイル(myjob.conf など)は無視されます。
条件に合わないものを一覧にするには find が便利です。
# root 以外が所有しているもの
sudo find /etc/crontab /etc/cron.* ! -user root -ls
# グループまたはその他に書き込み権限があるもの
sudo find /etc/crontab /etc/cron.* -perm /022 -ls
何も表示されなければ問題ありません。CIS Benchmark などの強化基準では、さらに一般ユーザーから中身を読めないよう、/etc/crontab を 600、cron.d や cron.hourly などのディレクトリを 700 にすることが推奨されています。変える場合は次のようにします。
sudo chown root:root /etc/crontab
sudo chmod 600 /etc/crontab
sudo chown root:root /etc/cron.d /etc/cron.hourly /etc/cron.daily /etc/cron.weekly /etc/cron.monthly
sudo chmod 700 /etc/cron.d /etc/cron.hourly /etc/cron.daily /etc/cron.weekly /etc/cron.monthly
監視ツールなど、一般ユーザーでこれらを読む仕組みがある場合は動かなくなることがあるので、変更前に確認してください。
ステップ2:中身を読んで見慣れないジョブを探す
権限が正しくても、中身が不正なら意味がありません。すべてのファイルの中身に目を通します。
# cron.d の全ファイルを、ファイル名つきで表示(コメント行と空行は除く)
sudo grep -Hv -e '^\s*#' -e '^\s*$' /etc/crontab /etc/cron.d/*
# cron.hourly などのスクリプトを順に表示
for f in /etc/cron.hourly/* /etc/cron.daily/*; do echo "=== $f"; sudo cat "$f"; done
# ユーザーごとの crontab も確認する
sudo ls -l /var/spool/cron/ /var/spool/cron/crontabs/ 2>/dev/null
次のような記述は、詳しく調べる対象です。
curlやwgetで外部からスクリプトを取得し、そのままshやbashに渡しているbase64 -dなどで中身を隠したコマンド- /tmp、/dev/shm、/var/tmp など誰でも書き込める場所のファイルを実行している
- 名前が正規のファイルに似ているが、導入した覚えのないパッケージ名やファイル名
- 実行ユーザーが root で、出力を
>/dev/null 2>&1で捨てている見慣れないジョブ
ステップ3:パッケージ由来のファイルが改変されていないか確かめる
/etc/cron.d や /etc/cron.daily のファイルの多くは、パッケージに含まれています。どのパッケージのファイルかを調べ、配布時から変わっていないか検証できます。
Debian/Ubuntu の場合:
# どのパッケージのファイルか
dpkg -S /etc/cron.d/* /etc/cron.hourly/* 2>&1
# パッケージのファイルが変更されていないか検証(変更があった行だけ表示される)
sudo dpkg --verify cron
RHEL 系の場合:
# どのパッケージのファイルか
rpm -qf /etc/cron.d/* /etc/cron.hourly/*
# パッケージのファイルが変更されていないか検証
sudo rpm -V cronie
「どのパッケージにも属していない」と表示されたファイルは、管理者が自分で置いたか、外部から置かれたかのどちらかです。自分たちで置いた覚えがあるかを一つずつ確認します。設定ファイルは管理者が編集することもあるため、rpm -V や dpkg --verify で変更と出ても、それだけで不正とは限りません。変更箇所を中身で確かめてください。
ステップ4:run-parts の対象になるファイルを確かめる
cron.hourly に置いたスクリプトが実行されない、あるいは「実行されていないと思っていたファイルが実行されていた」という食い違いは、run-parts の規則が原因のことがあります。
Debian/Ubuntu の run-parts は、ファイル名に英数字・下線・ハイフン以外の文字を含むファイルを実行しません。たとえば backup.sh のように . を含むファイルは対象外です。実際に実行されるファイルは次のコマンドで確認できます。
# 実行はせず、対象になるファイル名だけを表示する(Debian/Ubuntu)
run-parts --test /etc/cron.hourly
RHEL 系では、run-parts が実行権限のあるファイルを順に実行します。どちらの場合も、実行権限を外したファイルは実行されません。
ステップ5:SELinux のコンテキストを確認する(RHEL 系)
SELinux が有効な RHEL 系では、cron.d のファイルに正しいセキュリティコンテキストが付いていないと、crond が読み込めないことがあります。別の場所で作ったファイルを mv で移した場合によく起きます。
getenforce
ls -Z /etc/cron.d/ /etc/cron.hourly/
# コンテキストをポリシーの既定値に戻す
sudo restorecon -Rv /etc/cron.d /etc/cron.hourly
restorecon は既定のコンテキストに戻すだけなので、ファイルの中身や権限は変わりません。
ステップ6:実行ログを確認する
cron の実行記録はディストリビューションによって場所が異なります。
# RHEL 系
sudo tail -n 50 /var/log/cron
sudo journalctl -u crond --since "1 hour ago"
# Debian/Ubuntu(rsyslog が入っていれば /var/log/syslog にも出る)
sudo journalctl -u cron --since "1 hour ago"
見慣れないコマンドが定期的に実行されていないか、所有者や権限の問題で読み込みを拒否したという警告が出ていないかを確認します。
ステップ7:変更を検知する仕組みを作る
定期的に目で確認するのは手間がかかるので、変更があったら気付ける仕組みを用意します。本格的にはファイル改ざん検知ツールの AIDE を使い、/etc 全体の変化を記録する方法があります。小規模なサーバーなら、ハッシュ値の一覧を保存して比べるだけでも効果があります。
#!/bin/bash
# /usr/local/sbin/cron-baseline-check(root だけが読み書きできる場所に置く)
set -eu
BASE=/var/lib/cron-baseline.sha256
CUR=$(mktemp)
find /etc/crontab /etc/cron.d /etc/cron.hourly /etc/cron.daily /etc/cron.weekly /etc/cron.monthly /var/spool/cron \
-type f -exec sha256sum {} + 2>/dev/null | sort -k2 > "$CUR"
if [ ! -f "$BASE" ]; then
cp "$CUR" "$BASE" # 初回は基準として保存する
chmod 600 "$BASE"
echo "baseline created"
elif ! diff -u "$BASE" "$CUR"; then
echo "cron files changed" >&2
rm -f "$CUR"
exit 1
fi
rm -f "$CUR"
sudo install -o root -g root -m 700 cron-baseline-check /usr/local/sbin/cron-baseline-check
sudo /usr/local/sbin/cron-baseline-check # 1回目で基準を作る
2回目以降に差分が出たら、その変更を自分たちが行ったかを確認し、正当な変更なら基準ファイルを削除して作り直します。この検知スクリプト自体が書き換えられると意味がないため、置き場所と基準ファイルは root だけが書き込める状態にしておきます。また、サーバー上だけで完結する検知は、root を取られた後には信用できない点に注意してください。
cron を使えるユーザーを制限する
一般ユーザーの crontab は /etc/cron.allow と /etc/cron.deny で制限できます。cron.allow があれば、そこに書かれたユーザーだけが crontab コマンドを使えます。
# root だけに crontab の利用を許可する例
echo root | sudo tee /etc/cron.allow
sudo chmod 600 /etc/cron.allow
Web サーバーの実行ユーザー(www-data、apache、nginx など)が crontab を持っている場合は、正当な用途があるかを確認してください。WordPress の WP-Cron をサーバーの cron で動かしている場合などは必要ですが、覚えのないものは調査対象です。
まとめ
- cron.d と cron.hourly は root で定期実行されるため、侵入後の永続化に使われやすい
- 所有者が root で、グループ・その他に書き込み権限がないことを find で確認する
- 中身を読み、外部からの取得・難読化・/tmp の実行などを探す
dpkg -S/rpm -qfとdpkg --verify/rpm -Vで、パッケージ外のファイルや改変を見つける- RHEL 系は
ls -Zとrestoreconでコンテキストを確認し、ログは /var/log/cron か journalctl で見る - ハッシュ値の比較や AIDE で変更に気付ける仕組みを作る