アクセスを得るのは仕事の半分で、残りの半分はそれを失わないことです。だからこそ自動化された悪意あるソフトウェアのほぼすべてが、まず自分の再起動を手当てします。再起動の後、ファイルが消された後、パスワードが変えられた後にも動くように。そしてここから、あの聞き慣れた話が来ます——「掃除したのに二日後にまた戻ってきた」。

その手当てが行われる場所はそれほど多くなく、そのすべてを数分で確認できます。以下が順を追った一巡です。

1. SSH の鍵

戻ってくる最も単純な道:authorized_keys の一行は、パスワードの変更もシステムの更新も再起動も生き延びます。

sudo find /home /root -name authorized_keys -exec ls -l {} \; -exec cat {} \;

各行は誰かの恒久的なアクセス権です。誰のものか言えないなら、他人のものとして扱ってください。行末のコメントにも注意を。あれは任意のテキストであり、あなたの名前と一致していても何も証明しません。

2. すべてのユーザーの cron ジョブ

自分の crontab を確認するだけではありません。

for u in $(cut -f1 -d: /etc/passwd); do echo "== $u"; sudo crontab -u "$u" -l 2>/dev/null; done
sudo ls -la /etc/cron.d/ /etc/cron.hourly/ /etc/cron.daily/
sudo cat /etc/crontab

探すべき兆候:@reboot の行、長くて読み解けないコマンド、シェルへ流し込まれる curlwget の呼び出し、そして /tmp/dev/shm/var/tmp から走るあらゆるもの。それらのディレクトリから標準のものが動くことはありません。

3. systemd のタイマーとサービス

cron の現代的な代替であり、そして目に見えて確認される回数が少ないものです。

systemctl list-timers --all
systemctl list-units --type=service --state=running
sudo ls -la /etc/systemd/system/ /run/systemd/system/

さらに別途、root 権限なしで動き、共通の一覧には現れないユーザーのサービスがあります。

systemctl --user list-units --type=service
sudo ls -la /home/*/.config/systemd/user/

もう一つの細部:あるユーザーに対して有効にされた lingering(loginctl enable-linger)は、そのサービスが有効なセッション無しでも動くことを許します。loginctl list-users で確認してください。

4. シェルの起動ファイル

シェルの起動ファイルの末尾に足されたコードは、ログインのたびに走ります。

sudo tail -5 /root/.bashrc /root/.profile /home/*/.bashrc /home/*/.profile
sudo ls -la /etc/profile.d/

ちょうどファイルの末尾を見てください——ざっと目を通したときに気づかれないよう、そこに足されるのです。

5. ld.so.preload

指定されたライブラリを、走っているすべてのプロセスへ読み込ませるファイルです。そして通常の状況でそれが作られることはありません。

ls -l /etc/ld.so.preload

その存在は実質的に深刻な侵害の紛れもない印であり、そうしたライブラリはたいていファイルもプロセスもネットワーク接続も一緒に隠します。そしてその後、そのマシンであなたが見るものはもう信頼に値しません。

6. パッケージ管理のフック

思い出されることの少ない手口:apt はパッケージに対する各操作の前後でコマンドを実行できます。

sudo ls -la /etc/apt/apt.conf.d/
sudo grep -r 'DPkg::Pre-Invoke\|DPkg::Post-Invoke\|APT::Update' /etc/apt/apt.conf.d/

そうしたフックは更新のインストールのたびに走ります——つまり定期的に、しかも root として。

7. Ubuntu の今日のメッセージ

ディレクトリ /etc/update-motd.d/ には、SSH ログインのたびに走って歓迎の文面を作る実行可能なスクリプトが置かれています。そしてこの場所が都合よいのは、まさにシステムの一部に見えるからです。

sudo ls -la /etc/update-motd.d/

8. 遅延実行の at ジョブ

sudo atq
sudo ls -la /var/spool/cron/atjobs/ 2>/dev/null

古く、あまり使われない仕組みであり、だからこそ最も確認されないものです。

何か見つけたらどうするか

最初の反応は、見つけたものをすぐ消すことです。そしてそれは誤りです。それと一緒に、それがどうやってそこへ来たのかという情報も消え、その問いへの答えが無ければすべては繰り返されます。

  1. そのファイルやジョブの控えと、その変更時刻を保存する。
  2. その時刻を手がかりに、Web サーバーのログと auth.log で同じ分に何が起きていたかを見る。侵入口はたいていそこにあります。
  3. 一覧の八か所すべてを確認する。何かが見つかった一か所だけではありません。裏口が一つだけ残されることはほとんどありません。
  4. そのうえで掃除し、穴そのものを塞ぐ。

普通がどんな姿かを知っていること

この確認の本当の難しさはコマンドではなく、一覧が以前どうだったかを覚えていなければ、ジョブ一覧の見慣れない一行がまったく怪しく見えないということにあります。そして誰かが設定したサーバーや、一年前に設定されたサーバーでは、自分のものと他人のものを見分けるのはほぼ不可能です。

ここから実務的な結論が出ます。サーバーがまともなうちに「今の状態」のスナップショットを今日取っておくことには意味がある、ということです。健全なマシンでの cron ジョブ、タイマー、鍵、サービスの一覧こそ、あとですべてを突き合わせる基準になります。そしてそれが日付とともに一か所にまとめられ守られていれば、裏口探しを何時間もの作業から二つの一覧の比較へ変えてくれます。それがどう見えるかは下のデモページで。