攻撃を知り、防ぐ

SSH とリモートアクセス

総当たりのあとのログイン、置かれた鍵、ssh -R のトンネルとトンネルの道具、弱い sshd と sudo の設定を、ネットワーク名前空間の接続元から起こして、通知と記録の読み方と、入り口の閉じ方を学びます。

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

#攻撃者は、SSH をどう使うか

SSH は、サーバーを管理する入り口です。攻撃者にとっても、同じ入り口になります。入ったあとの動きは、だいたい次の4つです。

段階攻撃者がすることこの講座で見るもの
入る推測したパスワードでログインするSSH 総当たりの検知と遮断 の「総当たりのあとにログインが成功」の通知
居座るauthorized_keys に自分の鍵を足す重要システムファイルの改ざん監視 の通知と、書き換えたセッション
奥へ通すSSH のトンネルを張る、トンネルの道具を動かすSSH のトンネルの検知 の通知、roamswitch ports -a、プロセス実行の記録と攻撃手口の検知 の EXR-010
入り口を広げるroot のログインや、パスワードなしの sudo を残すSSH と sudo の設定の診断

総当たりを遮断する設定と、接続元を許可リストに限る設定は、SSH の保護の設定 で扱いました。この講座は、その先を扱います。遮断のしきい値に届かずに入られたあと、何が見えて、何が見えないかです。

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

この講座では、sshd と sudo の設定を、わざと弱くします。実機では試さないでください。この講座の VM は tart exec で操作するので、SSH の設定を誤っても作業を続けられます。

#練習の場を用意する

外からの攻撃者に見立てるため、VM の中にネットワーク名前空間を作ります。手順は SSH の保護の設定 と同じです。10.99.0.2 と 10.99.0.3 が外の接続元、10.99.0.1 が VM の側です。

テスト用のユーザーと、外の接続元を作る

ユーザーを作り、ネットワークの設定を足す操作です(変更を伴う操作です)。パスワードは練習用のダミーです。

sudo useradd -m -s /bin/bash labuser
echo 'labuser:Practice-Pass-123' | sudo chpasswd
sudo install -d -m 700 -o labuser -g labuser /home/labuser/.ssh
sudo apt install -y sshpass
sudo ip netns add outside
sudo ip link add veth-host type veth peer name veth-out
sudo ip link set veth-out netns outside
sudo ip addr add 10.99.0.1/24 dev veth-host
sudo ip link set veth-host up
sudo ip netns exec outside ip addr add 10.99.0.2/24 dev veth-out
sudo ip netns exec outside ip addr add 10.99.0.3/24 dev veth-out
sudo ip netns exec outside ip link set veth-out up
sudo roamswitch server restart
sudo roamswitch bans | sed -n 5,6p
出力の抜粋
  ✓ roamswitch-server サービスを正常に再起動しました。
  状態      : 検知のみ(遮断しない)
  理由      : fail2ban が SSH を防御しているため、遮断はそちらに任せています(ssh_guard_yield_to_fail2ban=true)。

この VM では fail2ban が sshd を見ているので、RoamSwitch のガードは「検知のみ」です。この講座では、fail2ban は止めません。これから見る通知は、検知のみの状態でも出ます。

元に戻す手順は、最後の演習の終わりにまとめてあります。

#推測されたパスワードで入られる

遮断は、失敗が ssh_guard_max_failures(既定 6 回)に届いたときです。攻撃者が、それより少ない回数で当てたら、遮断は起きません。ガードは、その代わりに「失敗のあとの成功」を知らせます。

コードでは、窓の時間(ssh_guard_window_secs、既定 600 秒)のうちに、しきい値の半分(既定なら 3 回)以上失敗した接続元が、そのあとログインに成功すると知らせます。

3 回間違えたあとに入り、鍵を置く

10.99.0.2 から、間違ったパスワードで 3 回試します。4 回目に正しいパスワードで入り、攻撃者の鍵を authorized_keys に足します。鍵は、この場で作る練習用のものです。

F="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o PubkeyAuthentication=no -o NumberOfPasswordPrompts=1 -o LogLevel=ERROR -o ConnectTimeout=5"
ssh-keygen -q -t ed25519 -N "" -C lab-dummy-key -f /tmp/lab_key
for p in wrong-1 wrong-2 wrong-3; do sudo ip netns exec outside sshpass -p $p ssh $F -b 10.99.0.2 labuser@10.99.0.1 true; done
sudo ip netns exec outside sshpass -p Practice-Pass-123 ssh $F -b 10.99.0.2 labuser@10.99.0.1 "echo logged in from \$SSH_CLIENT; sleep 2; echo '$(cat /tmp/lab_key.pub)' >> ~/.ssh/authorized_keys; sleep 3; true"
実行結果
labuser@10.99.0.1: Permission denied (publickey,password).
labuser@10.99.0.1: Permission denied (publickey,password).
labuser@10.99.0.1: Permission denied (publickey,password).
logged in from 10.99.0.2 58019 22

通知の履歴を見ます。新しいものが上です。

sudo roamswitch notifications | head -34
出力の抜粋
  2026-11-12T02:32:38.874191964+00:00
  クリティカルパス改ざん検知 (FIM) (1 件の改ざん)
    ホスト: ubuntu (IP: 192.168.64.5)
    ハッシュが一致しない重要システムファイル:
    /home/labuser/.ssh/authorized_keys: 新しいファイル
      プロセス: labuser / /usr/bin/bash / コマンド: bash -c echo logged in from $SSH_CLIENT; sleep 2; echo 'ssh-ed25519 SHA256:KQPtaxW8VtuW6Dpd188A+Xk78Q3T8jGiz/l39e6vdBw lab-dummy-key' >> ~/.ssh/authorized_keys;… (pid 1711)
      SSH ログイン: 10.99.0.2:58019 から labuser が 2026-11-12 02:32:36 にログインしたセッションです(認証: password)
    推奨対応: `sudo roamswitch fim verify` で改ざん箇所を特定し復旧してください。
    時刻: 2026-11-12 02:32:38

  2026-11-12T02:32:36.558087448+00:00
  【重大】SSH 総当たりのあとにログインが成功: 10.99.0.2
    ホスト: ubuntu (IP: 192.168.64.5)
    接続元: 10.99.0.2
    ログインしたアカウント: labuser
    認証方法: password
    その前の失敗: 3 回
    パスワードが推測された可能性があります。心当たりがなければ、すぐに確認してください。
    ・who や last で今のセッションを確かめ、不審なら loginctl terminate-session で切ってください
    ・そのアカウントのパスワードを変え、鍵認証だけにしてください(PasswordAuthentication no)
    ・authorized_keys、cron、systemd のユーザーサービスに、見覚えのない変更がないか確かめてください
    セッションは切っていません。
    時刻: 2026-11-12 02:32:36

2つの通知が出ました。下が先です。

  • 総当たりのあとにログインが成功: 3 回の失敗のあとの成功を、重大として知らせました。時刻は UTC で、日本時間では 11:32:36 です
  • FIM の通知: 書き込みの直後に、authorized_keys が足されたことを知らせました。書いたコマンドと、そのプロセスが属する SSH のログイン(接続元・ポート・時刻・認証方法)が付いています
  • セッションは切っていません: 通知のあとも、攻撃者のセッションは続きます。切るかどうかは、人が決めます
  • 遮断しない接続元: 開いている SSH のセッションの接続元は、SSH ガードが遮断しません。自分の作業を締め出さないためです。その接続元が攻撃者のものでも同じなので、セッションを切るのは人です

コマンドの中の公開鍵は、SHA256: で始まる指紋にまとめて出ます。長いコマンドは、途中で … に切れます。

「総当たりのあとにログインが成功」が届いたら

まず、そのログインが自分かどうかを確かめます。

  • 自分だった(パスワードを打ち間違えただけ): 鍵でのログインに切り替えます。この講座の最後の演習で、パスワードのログインを止めます
  • 心当たりがない: パスワードが知られています。セッションを切り、パスワードを変えます。同じ時刻の前後に、FIM の通知が出ていないかも見ます。上の例のように、入った直後に鍵を置かれていることがあります

どちらか分からないときは、心当たりがないほうとして扱います。

#置かれた鍵を見つける

パスワードを変えても、置かれた鍵は残ります。攻撃者は、次から鍵で入れます。鍵のログインには失敗が要らないので、「総当たりのあとにログインが成功」の通知も出ません。

置かれた鍵で入り、通知と記録を突き合わせる

攻撃者が、別の接続元 10.99.0.3 から、置いた鍵で入ります。入ったあと、もう一度 authorized_keys を書き換えます。

K="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -o ConnectTimeout=5 -o BatchMode=yes -i /tmp/lab_key"
sudo ip netns exec outside ssh $K -b 10.99.0.3 labuser@10.99.0.1 'echo key login from $SSH_CLIENT; sleep 2; echo "# lab note" >> ~/.ssh/authorized_keys; sleep 3; true'
sudo roamswitch notifications | head -14
出力の抜粋
key login from 10.99.0.3 52049 22

  2026-11-12T02:32:56.342263139+00:00
  クリティカルパス改ざん検知 (FIM) (1 件の改ざん)
    ホスト: ubuntu (IP: 192.168.64.5)
    ハッシュが一致しない重要システムファイル:
    /home/labuser/.ssh/authorized_keys: 新しいファイル
      プロセス: labuser / /usr/bin/bash / コマンド: bash -c echo key login from $SSH_CLIENT; sleep 2; echo "# lab note" >> ~/.ssh/authorized_keys; sleep 3; true (pid 1915)
      SSH ログイン: 10.99.0.3:52049 から labuser が 2026-11-12 02:32:54 にログインしたセッションです(認証: publickey, 鍵 SHA256:KQPtaxW8VtuW6Dpd188A+Xk78Q3T8jGiz/l39e6vdBw)
    推奨対応: `sudo roamswitch fim verify` で改ざん箇所を特定し復旧してください。
    時刻: 2026-11-12 02:32:56

今度は、認証が publickey で、鍵の指紋が付いています。「新しいファイル」のままなのは、基準(ベースライン)にこのファイルが無いためです。基準と比べるので、2回目の書き換えも「新しいファイル」と出ます。

指紋が、どの鍵のものかを確かめます。sshd のログにも、同じ指紋が残っています。

ssh-keygen -lf /tmp/lab_key.pub
sudo journalctl -u ssh --since "-3min" --no-pager | grep Accepted
実行結果
256 SHA256:KQPtaxW8VtuW6Dpd188A+Xk78Q3T8jGiz/l39e6vdBw lab-dummy-key (ED25519)
Nov 12 02:32:36 ubuntu sshd[1571]: Accepted password for labuser from 10.99.0.2 port 58019 ssh2
Nov 12 02:32:54 ubuntu sshd[1852]: Accepted publickey for labuser from 10.99.0.3 port 52049 ssh2: ED25519 SHA256:KQPtaxW8VtuW6Dpd188A+Xk78Q3T8jGiz/l39e6vdBw

実際の調査では、/tmp/lab_key.pub の代わりに ssh-keygen -lf ~labuser/.ssh/authorized_keys で、ファイルにある鍵の指紋を一覧にします。ログの指紋と合う行が、使われた鍵です。

何を見張っているかは、detection-coverage で確かめられます。

sudo roamswitch detection-coverage --server | grep -A2 T1098.004
実行結果
  🟡 T1098.004 Account Manipulation: SSH Authorized Keys  [一部]  稼働中
      担当: criticalPathFim
      見えない部分: 見ているのは、ログインできるアカウントと root の .ssh にある authorized_keys / authorized_keys2 だけです。sshd の AuthorizedKeysFile で別の場所にした鍵や、AuthorizedKeysCommand で渡す鍵は見ていません。

鍵を取り除きます(変更を伴う操作です)。fim verify で変わったファイルを確かめ、消したあとにもう一度確かめます。

sudo roamswitch fim verify
sudo rm /home/labuser/.ssh/authorized_keys
sudo roamswitch fim verify
実行結果
  🔍 最重要システムファイルの整合性を検証中... (93 件)
  🚨 ファイルの変更・改ざんが検知されました!
    [内容の変更] /etc/passwd
    [内容の変更] /etc/gshadow
    [新しいファイル] /home/labuser/.ssh/authorized_keys
    [内容の変更] /etc/shadow
    [内容の変更] /etc/group
  このうち 4 件は、2026-11-12 02:32 のパッケージの更新で説明できなかったため、基準に取り込んでいない変更です。
  🔍 SUID / SGID ファイルの一覧を確認中...
  ✓ SUID / SGID ファイルに、追加や変更はありません。 (20 件を確認)

  正規のOS更新等による変更の場合は 'sudo roamswitch fim update' でベースラインを更新してください。
  🔍 最重要システムファイルの整合性を検証中... (93 件)
  🚨 ファイルの変更・改ざんが検知されました!
    [内容の変更] /etc/shadow
    [内容の変更] /etc/passwd
    [内容の変更] /etc/group
    [内容の変更] /etc/gshadow
  このうち 4 件は、2026-11-12 02:32 のパッケージの更新で説明できなかったため、基準に取り込んでいない変更です。
  🔍 SUID / SGID ファイルの一覧を確認中...
  ✓ SUID / SGID ファイルに、追加や変更はありません。 (20 件を確認)

  正規のOS更新等による変更の場合は 'sudo roamswitch fim update' でベースラインを更新してください。

鍵を消すと、authorized_keys の行は消えました。この練習では、ファイルごと消しました。実機で正当な鍵と同じファイルにあるときは、攻撃者の行だけを消します。

残った 4 件は、最初の演習で labuser を作ったときの変更です。直後に入れた sshpass のパッケージの更新でも、説明できない変更として、基準に取り込まれませんでした。パッケージの更新が、更新と無関係な変更を基準に入れないためです。自分の変更だと確かめてから、基準を更新します。

sudo grep -c labuser /etc/passwd /etc/group /etc/shadow /etc/gshadow
sudo roamswitch fim update | tail -3
sudo roamswitch fim verify
実行結果
/etc/passwd:1
/etc/group:1
/etc/shadow:1
/etc/gshadow:1
  ✓ ベースラインDBを正常に更新・署名保存しました。 (92 件記録)
  ✓ SUID / SGID ファイルの基準を更新しました。 (20 件記録)

  🔍 最重要システムファイルの整合性を検証中... (92 件)
  ✓ 改ざんは検知されませんでした。すべて正常です。 (92 ファイルを監視中)
  🔍 SUID / SGID ファイルの一覧を確認中...
  ✓ SUID / SGID ファイルに、追加や変更はありません。 (20 件を確認)
調べる前に `fim update` を実行する

sudo roamswitch fim update は、いまの状態をそのまま基準にします。攻撃者の鍵が残ったまま実行すると、その鍵が「正しい状態」になり、fim verify に出なくなります。先に変わった箇所を調べ、自分の変更だと分かったものだけを残してから実行します。

#トンネルと、トンネルの道具

入った攻撃者は、そのサーバーを踏み台にして、奥のネットワークへ通り道を作ります。SSH だけでもできます。ssh -R は、サーバーの側に待ち受けのポートを開き、そこへの接続を攻撃者の側へ運びます。

ssh -R のトンネルを張り、見つけて切る

10.99.0.3 から、トンネルだけのセッションを張ります。-N はコマンドを実行しない指定です。サーバーの 127.0.0.1:9000 への接続が、攻撃者の側の 10.99.0.3:8080 へ運ばれます。

sudo ip netns exec outside setsid sshpass -p Practice-Pass-123 ssh $F -b 10.99.0.3 -N -R 127.0.0.1:9000:10.99.0.3:8080 labuser@10.99.0.1 </dev/null >/dev/null 2>&1 &
sleep 5
sudo roamswitch ports -a | grep -A1 "Port 9000"
実行結果
  • Port 9000  sshd            PID: 2176   127.0.0.1 (ローカル限定)[注意]
    ↳ SSH バージョンバナー公開: SSHを最新版に更新し、必要に応じてバナーの内容を最小化してください。

9000 番で、sshd が待ち受けています。-a を付けないと、127.0.0.1 だけの待ち受けは一覧に出ません。2行目の注意は、ポートの持ち主が sshd なので付いたもので、トンネルの判定ではありません。

通知の履歴も見ます。

sleep 5
sudo roamswitch notifications | sed -n 5,20p
実行結果
  2026-11-12T02:33:17.162653456+00:00
  SSH のセッションが待ち受けを開きました: 127.0.0.1:9000(labuser)
    ホスト: ubuntu (IP: 192.168.64.5)
    SSH でログインしたセッション(sshd PID 2176、アカウント labuser)が、このマシンの 127.0.0.1:9000 で待ち受けています。このマシンの中からだけ接続できる待ち受けです。`ssh -R` のトンネルでは、この待ち受けへの接続が、セッションの相手の側へ運ばれます。
    セッションの接続元: 10.99.0.3:52603
    自分で張ったトンネルなら、許可に加えます: sudo roamswitch server allowlist add ssh-remote-listen labuser:9000 --days 30 <理由>
    心当たりがなければ、セッションを切ります: sudo kill 2176
    時刻: 2026-11-12 02:33:17

  2026-11-12T02:33:16.316313708+00:00
  🔎 不審な実行パターンを検知
    ssh のトンネル(-R / -D)が開かれました (EXR-016)
    プロセス: /usr/bin/ssh (PID: 2104) / 重要度: 中
    詳細: ssh がリバーストンネル(-R: 相手の側に待ち受けを開き、このマシンへ運ぶ)を開きました(127.0.0.1:9000:10.99.0.3:8080)。ユーザー: root。コマンド: ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o PubkeyAuthentication=no -o NumberOfPasswordPrompts=1 -o LogLevel=ERROR -o ConnectTimeout=5 -b 10.99.0.3 -N -R 127.0.0.1:9000:10.99.0.3:8080 labuser@10.99.0.1
    次の確認コマンド: sudo roamswitch events tree 2104
    (通知のみ・遮断はしていません)

上の通知が、サーバーの側で見るものです。SSH のセッション(sshd の子)が待ち受けを開いたので、ポート・アドレス・アカウント・接続元を付けて知らせました。

下の EXR-016 は、トンネルを張った側の ssh についての通知です。この練習では、攻撃者の ssh も同じ VM(名前空間)で動いているので、出ました。攻撃者のマシンから張られたときは、上の通知だけです。

どのセッションのトンネルかを調べます。

who | wc -l
loginctl list-sessions
P=$(sudo ss -ltnpH 'sport = :9000' | sed -n 's/.*pid=\([0-9]*\).*/\1/p')
ps -o pid,user,args -p $P
loginctl show-session $(cat /proc/$P/sessionid) -p Name -p RemoteHost -p Service
実行結果
0
SESSION  UID USER    SEAT TTY STATE  IDLE SINCE
      5 1002 labuser -    -   active no   -

1 sessions listed.
    PID USER     COMMAND
   2176 labuser  sshd: labuser
Name=labuser
RemoteHost=10.99.0.3
Service=sshd

who は 0 行でした。端末(TTY)を持たないセッションは、who に出ません。loginctl には出ていて、接続元が 10.99.0.3 だと分かります。PID は、通知の「sshd PID」と同じです。

セッションを切ります(変更を伴う操作です)。

sudo loginctl terminate-session $(cat /proc/$P/sessionid)
sleep 2
sudo ss -ltnH 'sport = :9000' | wc -l
実行結果
0

セッションを切ると、トンネルの待ち受けも消えました。

自分で使う -R のトンネルなら、通知の行のとおり sudo roamswitch server allowlist add ssh-remote-listen labuser:9000 --days 30 <理由> で許可します。照合するのは、セッションのアカウントとポートです。許可を足して同じトンネルを張り直すと、この通知は出ませんでした。許可は sudo roamswitch server allowlist remove ssh-remote-listen labuser:9000 で外します。

SSH の代わりに、トンネル専用の道具を持ち込むこともあります。プロセス実行の記録と攻撃手口の検知 は、chisel や ligolo-ng のような道具を、名前で数えます。スクリプトや AI エージェントの実行環境(python、node など)が、120 秒のうちに 3 種類を起動すると、EXR-010 として知らせます。

スクリプトから、トンネルの道具を3つ起動する

名前だけを借りた、何もしないダミーを作ります。中身は echo だけです(変更を伴う操作です)。

sudo mkdir -p /opt/labtools
for t in chisel ligolo-ng suo5; do printf '#!/bin/sh\necho "dummy %s"\n' $t | sudo tee /opt/labtools/$t >/dev/null; sudo chmod 755 /opt/labtools/$t; done
python3 -c '
import subprocess
for t in ["chisel", "ligolo-ng", "suo5"]:
    subprocess.run(["/opt/labtools/" + t])
'
sleep 8
sudo roamswitch notifications | grep -m1 -B2 -A7 EXR-010
実行結果
dummy chisel
dummy ligolo-ng
dummy suo5
  2026-11-12T02:34:11.477616267+00:00
  🔎 不審な実行パターンを検知
    スクリプトやエージェントが複数のペネトレーションテスト用ツールを立て続けに起動しました (EXR-010)
    プロセス: /usr/bin/python3.12 (PID: 2835) / 重要度: 中
    詳細: python3.12 (pid 2835) が、120 秒以内に、攻撃用ツール 3 種類を起動しました: chisel, ligolo-ng, suo5。スクリプトや AI エージェントの実行環境が、ペンテストのツールを動かしている形です
    次の確認コマンド: sudo roamswitch events tree 2835
    (通知のみ・遮断はしていません)
    プロセス: admin / /usr/bin/dash / コマンド: /bin/sh /opt/labtools/suo5 (pid 2838)
    親プロセス: python3.12 ← dash ← dash ← tart-guest-agent ← systemd

重要度は「中」で、遮断はしません。最後の 2 行は、3 つ目の道具を動かしたプロセスと、その親の python3.12 です。同じ3つを、SSH でログインした人のシェルから順に動かすと、どうなるかも見ます。

sudo ip netns exec outside sshpass -p Practice-Pass-123 ssh $F -b 10.99.0.2 labuser@10.99.0.1 '/opt/labtools/chisel; /opt/labtools/ligolo-ng; /opt/labtools/suo5'
sleep 8
sudo roamswitch notifications | grep -c EXR-010
実行結果
dummy chisel
dummy ligolo-ng
dummy suo5
1

EXR-010 は 1 件のままで、増えませんでした。人が端末から道具を動かすのは、管理や検証でよくあることなので、このルールの対象から外れます。

#入り口を閉じる

ここまでの攻撃は、パスワードのログインと、SSH のトンネルを使いました。どちらも sshd の設定で止められます。SSH と sudo の設定の診断 は、その設定の弱いところを診断します。

設定をわざと弱くして、診断を見る

root の直接ログインを許し、labuser にパスワードなしの sudo を与えます(変更を伴う操作です)。戻し方は、この演習の最後にあります。

printf 'PermitRootLogin yes\n' | sudo tee /etc/ssh/sshd_config.d/00-lab.conf
echo 'labuser ALL=(ALL) NOPASSWD: ALL' | sudo tee /etc/sudoers.d/90-lab
sudo chmod 440 /etc/sudoers.d/90-lab
sudo roamswitch status --server | grep -A2 -E "SSH rootログイン|sudo権限"
出力の抜粋
  [要対策] SSH rootログイン・認証設定 - ⚠️ Root直接ログインまたは平文パスワード許可中
         公開鍵認証を強制し、直接のrootログインを禁止します。
         ↳ /etc/ssh/sshd_config で PermitRootLogin no と PasswordAuthentication no を設定してください。
--
  [要対策] sudo権限・NOPASSWD監査 - ⚠️ パスワードなし全権限Sudo設定を検出
         sudoers 設定で不要な NOPASSWD や過剰な特権昇格がないか検証します。
         ↳ NOPASSWD 設定を最小限のコマンドのみに制限してください。

sshd を再起動する前から、診断に出ました。SSH の診断は、sshd が設定ファイルを評価した結果(sshd -T)を読むためです。設定が効くのは、sshd を再起動してからです。

sshd_config.d のファイルは、名前の順に読まれ、同じ項目は最初の値が使われます。00- で始めたのは、ほかのファイルより先に読ませるためです。

パスワードなしの sudo は、自分でも探せます。

sudo grep -rn NOPASSWD /etc/sudoers /etc/sudoers.d/
実行結果
/etc/sudoers.d/90-cloud-init-users:4:admin ALL=(ALL) NOPASSWD:ALL
/etc/sudoers.d/90-cloud-init-users:7:ubuntu ALL=(ALL) NOPASSWD:ALL
/etc/sudoers.d/90-lab:1:labuser ALL=(ALL) NOPASSWD: ALL

足した 90-lab のほかに、cloud-init が最初のユーザー(admin と ubuntu)に付けた行があります。

/etc/sudoers.d と /etc/ssh/sshd_config.d に置いたファイルは、その場で 重要システムファイルの改ざん監視 の通知になります。書いたプロセスも付きます。

sudo roamswitch notifications | sed -n 5,13p
実行結果
  2026-11-12T02:45:44.019799955+00:00
  クリティカルパス改ざん検知 (FIM) (1 件の改ざん)
    ホスト: ubuntu (IP: 192.168.64.5)
    ハッシュが一致しない重要システムファイル:
    /etc/sudoers.d/90-lab: 新しいファイル
      プロセス: root / /usr/bin/tee / コマンド: tee /etc/sudoers.d/90-lab (pid 22893) ※プロセスは既に終了していたため、実行の記録(2026-11-12 02:45:43)から補いました。
      SSH のログインは見つかりませんでした(systemd サービス tart-guest-agent.service のプロセスです)
    推奨対応: `sudo roamswitch fim verify` で改ざん箇所を特定し復旧してください。
    時刻: 2026-11-12 02:45:43

この VM は tart exec で操作しているので、SSH のログインではなく、tart-guest-agent.service から書いたと出ています。00-lab.conf にも、同じ通知が出ました。

次に、入り口を閉じます。パスワードのログインを止める前に、自分の鍵を登録します。そのうえで、root のログイン、パスワードのログイン、TCP の転送(トンネル)を止めます。

sudo rm /etc/sudoers.d/90-lab
ssh-keygen -q -t ed25519 -N "" -C admin-key -f /tmp/admin_key
sudo install -m 600 -o labuser -g labuser /tmp/admin_key.pub /home/labuser/.ssh/authorized_keys
printf 'PermitRootLogin no\nPasswordAuthentication no\nKbdInteractiveAuthentication no\nAllowTcpForwarding no\n' | sudo tee /etc/ssh/sshd_config.d/00-lab.conf
sudo sshd -t && sudo systemctl restart ssh
sudo roamswitch status --server | grep "SSH rootログイン"
出力の抜粋
  [合格] SSH rootログイン・認証設定 - 稼働中 (公開鍵認証のみ・Root禁止)

sshd -t は、設定に誤りがないかを確かめます。誤りがあると、再起動しません。

攻撃者と同じ3つの手を、もう一度試します。パスワード、鍵、鍵とトンネルです。

A="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -o ConnectTimeout=5 -o BatchMode=yes -i /tmp/admin_key"
sudo ip netns exec outside sshpass -p Practice-Pass-123 ssh $F -b 10.99.0.2 labuser@10.99.0.1 true
sudo ip netns exec outside ssh $A -b 10.99.0.2 labuser@10.99.0.1 'echo key ok from $SSH_CLIENT'
sudo ip netns exec outside ssh $A -o ExitOnForwardFailure=yes -b 10.99.0.2 -N -R 127.0.0.1:9000:10.99.0.2:8080 labuser@10.99.0.1
実行結果
labuser@10.99.0.1: Permission denied (publickey).
key ok from 10.99.0.2 35039 22
Error: remote port forwarding failed for listen port 9000

正しいパスワードでも入れず、登録した鍵では入れました。トンネルは、鍵で入れても断られました。

元に戻し、練習の場も片付けます。

sudo rm /etc/ssh/sshd_config.d/00-lab.conf
sudo systemctl restart ssh
sudo rm -rf /opt/labtools /tmp/lab_key /tmp/lab_key.pub /tmp/admin_key /tmp/admin_key.pub
sudo ip netns del outside
sudo loginctl terminate-user labuser
sudo userdel -r labuser
sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|allowtcpforwarding) '
出力の抜粋
permitrootlogin without-password
passwordauthentication yes
allowtcpforwarding yes

この VM の元の値に戻りました。

鍵だけにしても、置かれた鍵は残る

PasswordAuthentication no は、パスワードのログインを止めるだけです。鍵のログインは止めません。攻撃者がすでに鍵を置いていたら、設定を固めたあとも入れます。設定を固める前に、authorized_keys に見覚えのない鍵がないかを確かめます。

#見えない部分

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

使い捨ての Ubuntu 24.04 の VM で確かめた範囲と、コードで確かめた範囲です。

  • 「総当たりのあとにログインが成功」は、成功の前に失敗があるときだけです。コードでは、しきい値の半分(既定 3 回)以上の失敗が、窓の時間のうちに要ります。最初から正しいパスワードや、盗んだ鍵で入られたときは、この通知は出ません
  • 通知は、セッションを切りません。切るのは、人が loginctl terminate-session で行います
  • 鍵の監視は、authorized_keys と authorized_keys2 だけです。AuthorizedKeysFile で別の場所にした鍵と、AuthorizedKeysCommand で渡す鍵は見ていません(detection-coverage の表示)
  • ssh -R の待ち受けの通知は、知らせるだけです。トンネルもセッションも切りません。切るのは、人が loginctl terminate-session で行います
  • トンネルの道具は、名前で数えます。名前を変えた道具は数えません。コードの注記では、ngrok、frp、ssh は、毎日の作業で使われるため、名前の一覧から外しています
  • SSH の診断が見るのは、PermitRootLogin と PasswordAuthentication です。AllowTcpForwarding は、診断の対象ではありません(コードで確認)
確認問題

「SSH のセッションが待ち受けを開きました」が届きました。who には、そのセッションが出ません。どう確かめますか

解説を見る

ssh -N -R のように端末を持たないセッションは、who に出ません。通知の sshd の PID(ss -ltnp の待ち受けの PID と同じ)から sessionid を読むと、loginctl でアカウントと接続元が分かります。心当たりがなければ、loginctl terminate-session で切ります。

確認問題

「総当たりのあとにログインが成功」が届き、パスワードを変えました。まだ確かめることは何ですか

解説を見る

入った直後に鍵を置かれていると、パスワードを変えても鍵で入れます。鍵のログインには失敗が要らないので、同じ通知も出ません。fim update を先に実行すると、置かれた鍵が基準に入ってしまいます。

#次に学ぶこと

  • 総当たりの遮断と、SSH の接続元を許可リストに限る設定は、SSH の保護の設定 で扱いました。鍵を置かれても、許可リストの外からの新しい接続は入れません
  • 重要なファイルの改ざんの監視の全体は、「居座りと改ざん」(準備中)で扱います
  • 設定ファイルの全項目は、Linux Server の運用マニュアル にあります
扱う機能

対応する攻撃手法 (ATT&CK): T1572 T1098.004 T1548.003 T1059.004 T1027 T1105 T1553.001

確認した内容

2026-11-12に確認しました。対象は Linux 1.17.39 です。公開版 1.17.39 の入った使い捨ての Ubuntu 24.04 の VM 2 台で、fail2ban が動いたまま、ネットワーク名前空間の接続元から全演習を日本語と英語(LANG=en_US.UTF-8)で上から実行し、ssh -R の待ち受けの通知と許可リスト、コマンド行の伏せ字も確かめました。

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