攻撃を知り、防ぐ

入り込んだあとに、攻撃者が残すもの

root の鍵の追加、cron の設置、SUID、ログの消去を無害な形で起こし、FIM と関連の機能がどう知らせるかを確かめ、カーネルの設定で足がかりを減らします。

  • レベル: 基礎
  • 約45分
  • 2026-11-12

#入り込んだあとに、攻撃者が残すもの

サーバーに入り込んだ攻撃者は、次も入れるように居座りの仕掛けを残します。root を取ったら、それを保つ手も打ちます。最後に、ログを消して痕跡を隠します。よく使われる手口と、Server Edition で見張る機能は次のとおりです。

手口例見張る機能
鍵を足して、パスワードなしで戻る/root/.ssh/authorized_keys に自分の公開鍵を書く重要システムファイルの改ざん監視
権限の設定を書き換える/etc/sudoers.d/ にファイルを足す同上
定期実行や自動起動に登録する/etc/cron.d/、/etc/systemd/system/ にファイルを置く同上
root で動くプログラムを残す自分のプログラムに SUID を付けるSUID・SGID の変更の監視
痕跡を消す/var/log/auth.log を空にするログ消去の監視

どれも、root を取られたあとの動きです。だから、この講座の機能は止めずに知らせます。止める相手がもう root を持っているためです。最後の章では、root を取らせにくくする設定(カーネルの堅牢化 と 権限昇格の通り道になるカーネルモジュールの点検)を見ます。

練習は、使い捨ての環境で

この講座の演習は、重要なファイルやログを、わざと書き換えます。使い捨ての VM で試してください。演習の VM の時計は UTC です。日本時間は 9 時間足して読みます。

#重要なファイルの書き換えを見つける

重要システムファイルの改ざん監視 は、sudoers、PAM、sshd の設定、cron、systemd のユニット、各アカウントの authorized_keys などのハッシュを記録します。書き込みと、見張るフォルダーへの新しいファイルは、その場で見ます(fanotify)。さらに、定期の検査で全体を比べ直します。

SSH でログインして root の鍵を足し、元に戻す

重要なファイルを書き換える操作です(変更を伴う操作です)。最後に元に戻します。

この演習では、同じ VM の中から 127.0.0.1 に SSH でログインして試しました。admin は、自分のアカウント名に置き換えます。足す鍵は、形だけの無効なダミーです。

ssh-keygen -q -t ed25519 -N "" -f ~/.ssh/lab_key
cat ~/.ssh/lab_key.pub >> ~/.ssh/authorized_keys
ssh -o StrictHostKeyChecking=accept-new -i ~/.ssh/lab_key admin@127.0.0.1 \
  'echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEXAMPLE lab@example" | sudo tee -a /root/.ssh/authorized_keys >/dev/null'
sleep 5
sudo roamswitch notifications | grep -A8 FIM | head -9
出力の抜粋
  クリティカルパス改ざん検知 (FIM) (1 件の改ざん)
    ホスト: ubuntu (IP: 192.168.64.8)
    ハッシュが一致しない重要システムファイル:
    /root/.ssh/authorized_keys: 内容の変更
      プロセス: root(ログインしたアカウント: admin) / /usr/bin/tee / コマンド: tee -a /root/.ssh/authorized_keys (pid 26094)
      SSH ログイン: 127.0.0.1:37356 から admin が 2026-11-12 01:33:38 にログインしたセッションです(認証: publickey, 鍵 SHA256:rkBIL270U06lSAVL6PeFRbgM29moyiChfjIWkAVaSBA)
    推奨対応: `sudo roamswitch fim verify` で改ざん箇所を特定し復旧してください。
    時刻: 2026-11-12 01:33:39

書き込んだ直後に、通知が出ました。種類は「内容の変更」です。見るところは 3 つです。

  • プロセス: 書き込んだのは root の tee です。ログインしたアカウント(admin)も出ます
  • SSH ログイン: どこから(127.0.0.1:37356)、いつ、どの鍵でログインしたセッションかが出ます。外から入られたときは、ここに相手のアドレスが出ます
  • 2 行目のコマンド: 自分の authorized_keys に鍵を足した操作も、別の通知になりました(抜粋では省いています)。各アカウントの鍵も見ているためです

ログインの情報は、fim_intrusion_context_enabled(既定 true)が付けます。/proc、実行の記録、logind から読むだけで、どこにも送りません(設定ファイルの注記)。

通知が来たら、何が変わったかを fim verify で確かめます。元に戻したら、もう一度確かめます。

sudo roamswitch fim verify
sudo sed -i '/lab@example/d' /root/.ssh/authorized_keys
sudo roamswitch fim verify
sudo roamswitch fim update
出力の抜粋
  🔍 最重要システムファイルの整合性を検証中... (90 件)
  🚨 ファイルの変更・改ざんが検知されました!
    [内容の変更] /root/.ssh/authorized_keys
    [内容の変更] /home/admin/.ssh/authorized_keys
  🔍 SUID / SGID ファイルの一覧を確認中...
  ✓ SUID / SGID ファイルに、追加や変更はありません。 (20 件を確認)

  正規のOS更新等による変更の場合は 'sudo roamswitch fim update' でベースラインを更新してください。
  🔍 最重要システムファイルの整合性を検証中... (90 件)
  🚨 ファイルの変更・改ざんが検知されました!
    [内容の変更] /home/admin/.ssh/authorized_keys
  🔍 SUID / SGID ファイルの一覧を確認中...
  ✓ SUID / SGID ファイルに、追加や変更はありません。 (20 件を確認)

  正規のOS更新等による変更の場合は 'sudo roamswitch fim update' でベースラインを更新してください。
  📝 現在のシステムファイルからベースラインDBを再作成中...
  ✓ ベースラインDBを正常に更新・署名保存しました。 (90 件記録)
  ✓ SUID / SGID ファイルの基準を更新しました。 (20 件記録)

root の鍵の行を消すと、/root/.ssh/authorized_keys は一覧から消えました。基準と同じ中身に戻ったためです。残った /home/admin/.ssh/authorized_keys は、自分で足した鍵なので、fim update で基準に入れました。

確かめる前に `fim update` を実行する

fim update は、その時点のすべての状態を正しいものとして基準にします。攻撃者が足した鍵が残っていれば、それも正しいものになり、以後は知らせません。fim verify の一覧を1行ずつ確かめて、残すものだけになってから実行します。

新しく置かれたファイル

cron やユニットのファイルを新しく置く手口も、その場で見つかります。/etc/cron.d・/etc/sudoers.d・/etc/systemd/system などの見張るフォルダーに、ファイルができた時点で知らせます。

定期実行のファイルを、2 つの置き方で置く

ファイルを置く操作です(変更を伴う操作です)。前の演習の鍵で SSH でログインし、1 つは tee で書き、もう 1 つは /tmp で作ってから mv で移します。中身は、何もしない /usr/bin/true を動かすだけです。

ssh -i ~/.ssh/lab_key admin@127.0.0.1 \
  'echo "*/5 * * * * root /usr/bin/true" | sudo tee /etc/cron.d/lab-persist >/dev/null'
ssh -i ~/.ssh/lab_key admin@127.0.0.1 \
  'echo "*/5 * * * * root /usr/bin/true" > /tmp/lab-mv && sudo mv /tmp/lab-mv /etc/cron.d/lab-mv'
sleep 5
sudo roamswitch notifications | grep -A6 FIM | head -14
出力の抜粋
  クリティカルパス改ざん検知 (FIM) (1 件の改ざん)
    ホスト: ubuntu (IP: 192.168.64.8)
    ハッシュが一致しない重要システムファイル:
    /etc/cron.d/lab-mv: 新しいファイル
      ログインの経路は調べられませんでした(別の場所から移動(mv)で置かれたため、書いたプロセスは特定できません)
    推奨対応: `sudo roamswitch fim verify` で改ざん箇所を特定し復旧してください。
    時刻: 2026-11-12 01:33:56
--
  クリティカルパス改ざん検知 (FIM) (1 件の改ざん)
    ホスト: ubuntu (IP: 192.168.64.8)
    ハッシュが一致しない重要システムファイル:
    /etc/cron.d/lab-persist: 新しいファイル
      プロセス: root(ログインしたアカウント: admin) / /usr/bin/tee / コマンド: tee /etc/cron.d/lab-persist (pid 26455)
      SSH ログイン: 127.0.0.1:37324 から admin が 2026-11-12 01:33:55 にログインしたセッションです(認証: publickey, 鍵 SHA256:rkBIL270U06lSAVL6PeFRbgM29moyiChfjIWkAVaSBA)

2 つとも「新しいファイル」として、置いた 1 秒後に出ました。tee で書いたほうは、書いたプロセスと SSH のログインまで分かります。mv で移したほうは、書いたプロセスが分かりません。だれが置いたかは、ファイルの時刻と、last や journalctl のログインの記録を突き合わせて調べます。

同じ VM で、lab-persist を消して、同じ中身で置き直しました。置き直したときも、もう一度「新しいファイル」として通知が出ました。消して置き直す手口も、見落としません。

元に戻します。ファイルを消して、確かめます。

sudo rm /etc/cron.d/lab-persist /etc/cron.d/lab-mv
sudo roamswitch fim verify
実行結果
  🔍 最重要システムファイルの整合性を検証中... (90 件)
  ✓ 改ざんは検知されませんでした。すべて正常です。 (90 ファイルを監視中)
  🔍 SUID / SGID ファイルの一覧を確認中...
  ✓ SUID / SGID ファイルに、追加や変更はありません。 (20 件を確認)

#SUID の付いたファイルを見つける

SUID が付いたプログラムは、実行した人ではなく、持ち主の権限で動きます。持ち主が root なら、だれが実行しても root です。root を取った攻撃者は、自分用の入り口として、これを残します。SUID・SGID の変更の監視 は、SUID と SGID が付いたファイルの一覧を基準にして、追加と変更を知らせます。

SUID を付けたプログラムを置く

root で動くファイルを作る操作です(変更を伴う操作です)。最後に消します。中身は、自分の ID を表示するだけの id の写しです。

sudo cp /usr/bin/id /usr/local/bin/lab-id
sudo chmod u+s /usr/local/bin/lab-id
/usr/local/bin/lab-id
sleep 5
sudo roamswitch notifications | grep -A10 SUID | head -11
sudo roamswitch fim verify
出力の抜粋
uid=1000(admin) gid=109(admin) euid=0(root) groups=109(admin)
  SUID/SGIDファイルの追加・変更を検知 (1 件)
    ホスト: ubuntu (IP: 192.168.64.8)
    SUID または SGID が付いた実行ファイルに、次の変化がありました:
    /usr/local/bin/lab-id に SUID/SGID が付きました(新規、または後から付与。モード 4755、所有者 UID 0、どのパッケージにも属していません)
    SUID が付いた実行ファイルは、実行した人ではなく持ち主(多くは root)の権限で動きます。攻撃者が root を保つために使う手口です。
    確認: `ls -l` で持ち主と更新時刻を、`last` で直近のログインを見てください。
    正規の変更だと分かっているとき: `sudo roamswitch fim update` で現在の状態を基準にします。
    時刻: 2026-11-12 01:34:19
    プロセス: root / /usr/bin/chmod / コマンド: chmod u+s /usr/local/bin/lab-id (pid 26651)
    親プロセス: sudo ← dash ← tart-guest-agent ← systemd / サービス: tart-guest-agent.service
  🔍 最重要システムファイルの整合性を検証中... (90 件)
  ✓ 改ざんは検知されませんでした。すべて正常です。 (90 ファイルを監視中)
  🔍 SUID / SGID ファイルの一覧を確認中...
  🚨 SUID / SGID ファイルの追加・変更が見つかりました!
    [SUID/SGID の追加] /usr/local/bin/lab-id (4755)

1 行目を見てください。一般の admin が実行したのに、euid=0(root) です。これが、SUID が居座りに使われる理由です。

通知には、chmod を実行したプロセスと、その親が出ています。モードの変更は、その場で見ているためです(fanotify)。全体の一覧も、suid_watch_interval_secs(既定 600 秒)ごとに作り直して比べます。「どのパッケージにも属していません」は大事な手がかりです。パッケージの更新で説明できる変更は、知らせずに基準に入ります(設定ファイルの注記)。

元に戻します。

sudo rm /usr/local/bin/lab-id
sudo roamswitch fim verify | tail -2
実行結果
  ✓ SUID / SGID ファイルに、追加や変更はありません。 (20 件を確認)

正しい変更だと分かっている SUID のファイルは、fim update で基準に入れます。入れるまで、fim verify は失敗として出し続けます。

#ログの消去を見つける

ログは、ふだんから書き足されて変わります。だから、ハッシュを比べる FIM では、消されたことが分かりません。ログ消去の監視 は、auth.log、syslog、audit.log、wtmp などを 30 秒ごとに見て、小さくなった、消えた、別のファイルに入れ替わったことを知らせます。ログのローテーションで説明できる変化は除きます。

認証のログを空にする

ログを消す操作です(変更を伴う操作です)。先に写しを取って、最後に戻します。

sudo cp -p /var/log/auth.log /root/auth.log.bak
sudo truncate -s 0 /var/log/auth.log
sleep 35
sudo roamswitch notifications | grep -A8 'ログの消去' | head -9
実行結果
  ログの消去の疑いを検知 (1 件)
    ホスト: ubuntu (IP: 192.168.64.8)
    次のログが、ログローテーションでは説明できない形で変わりました:
    /var/log/auth.log が小さくなりました(63186 バイト → 99 バイト)
    攻撃者が痕跡を消した可能性があります。
    確認: `last` と `sudo journalctl -u ssh` で直近のログインを、`sudo roamswitch fim verify` で重要ファイルと SUID ファイルの変化を見てください。
    時刻: 2026-11-12 01:34:50
    変更したプロセスは特定できません(ファイルの状態を定期的に比べて見つけたため)

空にしてから 30 秒以内に、通知が出ました。99 バイトは、空にしたあとに書き足された分です。最後の行のとおり、だれが消したかは分かりません。ファイルの大きさを定期的に比べて見つけるためです。

写しを戻します。

sudo cp /root/auth.log.bak /var/log/auth.log
sudo rm /root/auth.log.bak

同じ VM で、ほかの消し方と、正しい作業も試しました。

  • sudo sed -i '/127.0.0.1/d' /var/log/auth.log で行だけを消すと、「別のファイル(inode が違う)に差し替えられ、小さくなりました」と知らせました。sed -i は、新しいファイルを作って置き換えるためです
  • 同じファイルの変化が、前の通知から 5 分以内に続いたときは、すぐには知らせません。5 分たってから、「前の通知のあと、同じファイルでさらに 1 回起きました」のように、回数を入れた 1 件で知らせました。保留のあいだに sudo roamswitch server restart を挟んでも、この 1 件は出ました
  • sudo logrotate -f /etc/logrotate.conf でローテーションすると、auth.log は新しい小さなファイル(40 秒後に 99 バイト)になりましたが、通知は出ませんでした。古い中身は auth.log.1 に残っていて、ローテーションで説明できるためです
ログが消されたら

消されたあとでも、手がかりは残ります。通知の「確認」にあるとおり、last(wtmp から読むログインの記録)と journalctl を見ます。続けて sudo roamswitch fim verify で、鍵や SUID が足されていないかを確かめます。ログを消す前に、居座りの仕掛けが置かれていることが多いからです。

#足がかりを減らす

ここまでの機能は、root を取られたあとに知らせるものでした。カーネルの堅牢化 は、その前の段階で、権限を上げる足がかりを減らします。Server Edition のデーモンは、起動するたびに、カーネルの設定(sysctl)を書き込みます。

カーネルの設定と、その効き目を見る

一般のアカウント(admin)で実行します。何も変更しません。

sysctl kernel.yama.ptrace_scope kernel.unprivileged_userns_clone kernel.unprivileged_bpf_disabled
strace -o /dev/null true 2>&1; echo "strace: $?"
unshare -U true 2>&1; echo "unshare: $?"
sudo roamswitch server config get harden_userns_enabled
実行結果
kernel.yama.ptrace_scope = 2
kernel.unprivileged_userns_clone = 0
kernel.unprivileged_bpf_disabled = 1
strace: test_ptrace_get_syscall_info: PTRACE_TRACEME: Operation not permitted
strace: ptrace(PTRACE_TRACEME, ...): Operation not permitted
strace: 1
unshare: unshare failed: Operation not permitted
unshare: 1
true

3 つの設定は、次の意味です。

設定値効き目
kernel.yama.ptrace_scope2ほかのプロセスの中身を読む ptrace を、管理者だけにする。一般のアカウントは、自分の子のプロセスも追えない
kernel.unprivileged_userns_clone0一般のアカウントが、ユーザー名前空間を作れない
kernel.unprivileged_bpf_disabled1一般のアカウントが、eBPF のプログラムを読み込めない

比べるため、デーモンを一度も動かしていない、起動したばかりの VM でも見ました。そのときの値は、ptrace_scope が 1、unprivileged_userns_clone が 1、unprivileged_bpf_disabled が 2 でした(Ubuntu 24.04 の既定)。strace -o /dev/null true と unshare -U true は、どちらも終了コード 0 で通りました。RoamSwitch は /etc/sysctl.d/ にファイルを置かず、起動のたびに書き込みます。

`harden_userns_enabled` を切るかどうか

ユーザー名前空間を禁じると、root を使わないコンテナ(Podman rootless など)や、サンドボックスで動くアプリが動かなくなります(設定ファイルの注記)。それらを使うなら、sudo roamswitch server config set harden_userns_enabled false にします。

ただし、切っても、すぐには元に戻りません。false にすると、「非特権ユーザー名前空間の制限は、OS を再起動したときに外れます」と出ます。

同じ VM で server restart しても、unprivileged_userns_clone は 0 のままで、unshare -U true は失敗しました。OS を再起動したあとに 1 になり、unshare が通りました。true に戻して server restart すると、すぐ 0 に戻りました。

設定ファイルの注記のとおり、根本の対策は、修正済みのカーネルに更新することです。

権限昇格の通り道になるカーネルモジュールの点検 は、2026 年のローカル権限昇格で使われたカーネルモジュールを、読み込める状態かどうか調べます。

危険なモジュールを調べて、読み込みを止める

調べるだけなら、何も変更しません。--apply と --remove は、/etc/modprobe.d/ にファイルを書く、変更を伴う操作です。

roamswitch kernel-modules
出力の抜粋
  kernel 7.0.0-42-generic
  非特権のユーザー名前空間: 使えない(esp4・esp6・act_pedit への経路は閉じています)
  
  algif_aead  読み込み禁止済み  [Copy Fail / CVE-2026-31431]
  esp4        未使用・要求されれば読み込まれます  [Dirty Frag / Fragnesia / CVE-2026-43284]
  esp6        未使用・要求されれば読み込まれます  [Dirty Frag / Fragnesia / CVE-2026-43284]
  rxrpc       未使用・要求されれば読み込まれます  [Dirty Frag / DirtyDecrypt / CVE-2026-43500]
  act_pedit   未使用・要求されれば読み込まれます  [act_pedit page-cache write / CVE-2026-46331]

2 行目は、前の演習のユーザー名前空間の設定とつながっています。名前空間が使えないので、esp4、esp6、act_pedit への経路は閉じている、という判定です。algif_aead は、この VM では Ubuntu 自身の /etc/modprobe.d/disable-algif_aead.conf が、すでに止めていました。

使っていない esp4 の読み込みを止めて、本当に読み込めないかを確かめます。

sudo roamswitch kernel-modules --apply esp4
cat /etc/modprobe.d/roamswitch-kernel-module-surface.conf
sudo modprobe esp4 2>&1; echo "modprobe: $?"
出力の抜粋
書き込みました: /etc/modprobe.d/roamswitch-kernel-module-surface.conf (esp4)
  esp4        読み込み禁止済み  [Dirty Frag / Fragnesia / CVE-2026-43284]
# Written by RoamSwitch (roamswitch kernel-modules --apply). Stops these kernel modules from
# being loaded on demand. Undo: roamswitch kernel-modules --remove (or delete this file).
install esp4 /bin/false
modprobe: ERROR: ../libkmod/libkmod-module.c:1084 command_do() Error running install command '/bin/false' for module esp4: retcode 1
modprobe: ERROR: could not insert 'esp4': Invalid argument
modprobe: 1

元に戻します。設定ファイルが消えて、esp4 は読み込める状態に戻ります。

sudo roamswitch kernel-modules --remove
roamswitch kernel-modules | grep '^  esp4'
出力の抜粋
設定ファイルを削除しました。
  esp4        未使用・要求されれば読み込まれます  [Dirty Frag / Fragnesia / CVE-2026-43284]

IPsec の VPN(strongSwan など)で使っている esp4・esp6 は、自動で対象から外れます(コマンドの出力の注記)。このコマンドは、カーネルが修正済みかどうかは見ません。

#見えない部分

確かめたこと・確かめていないこと

使い捨ての Ubuntu 24.04 の VM で確かめた範囲では、次のようになります。

  • アカウントごとの crontab と ~/.bashrc は、Server の FIM の対象外です。admin で crontab - と ~/.bashrc への追記をしました。fim verify は「すべて正常」で、FIM の通知も増えませんでした。見ているのは /etc/cron.d/ などのシステム全体の場所です
  • mv で置かれたファイルは、書いたプロセスが分かりません。見張るフォルダーに置かれたことは、その場で分かります。だれが書いたかは、ファイルの時刻とログインの記録から調べます
  • ログの消去も、書いたプロセスは分かりません。30 秒ごとの比較のため、その間に消して同じ大きさまで埋め戻されると、wtmp・btmp 以外のログでは分かりません(コードの注記で確認)
  • 知らせるだけで、止めません。この講座の通知のあと、プロセスやネットワークの隔離は起きませんでした。root を取られたあとの対応は、人が行います
  • カーネルの設定は、すべての権限昇格を防ぎません。この VM でも、sudo strace -o /dev/null true は通りました。管理者の権限を持つプロセスには効きません。根本の対策は、カーネルの更新です
確認問題

sudo roamswitch fim verify に [新しいファイル] /etc/cron.d/backup-sync が出ました。心当たりがありません。最初にすることはどれですか

解説を見る

fim update は、その時点の状態をすべて正しいものにします。攻撃者のファイルも正しいものになり、以後は知らせません。通知に書いたプロセスが出ていればそれを、mv で置かれて出ていなければ、中身と時刻から、だれがいつ置いたかをたどります。消すのは、ほかの仕掛け(鍵、SUID)がないかを確かめてからです。

確認問題

rootless のコンテナを使うため、harden_userns_enabled を false にして sudo roamswitch server restart を実行しました。unshare -U true はどうなりますか

解説を見る

デーモンは、起動するときに禁止の値を書き込みます。false にすると書き込まなくなりますが、すでに書いた値は戻しません。同じ VM では、OS を再起動したあとに通るようになりました。

#次に学ぶこと

扱う機能

対応する攻撃手法 (ATT&CK): T1098.004 T1548.003 T1548.001 T1685.006 T1055

確認した内容

2026-11-12に確認しました。対象は Linux 1.17.39 です。使い捨ての Ubuntu 24.04 の VM(公開版 1.17.39)で、同じ VM への SSH ログインで鍵・cron・SUID・ログの消去を起こし、消して置き直す形、5 分以内の繰り返しとその保留中のデーモンの再起動、sed -i と logrotate、カーネルの設定とモジュール、harden_userns_enabled を切ったあとの OS の再起動を、日本語と英語で実行して確かめました。

コマンドの出力は、製品の版や環境によって変わります。違いが出たら、まず製品の版を確かめてください。