2026 年 5 月の同じ週に、こんな組み合わせを見た。

  • The Hacker News の xlabs_v1 ボットネット記事 → ELK で確認したら、まったく同じサブネットから 11,000 件超の攻撃を観測済みだった
  • Copy.FailDirty Flag という Linux kernel のローカル特権昇格脆弱性が立て続けに公開
  • 後者にはまだ CVE 番号すら付いていない

リモートからの侵入観測と、侵入後の権限昇格対策は、本来なら別々の話に見える。けれど自分のように VPS にハニーポットを置いている側からすると、入口(攻撃の流入)と出口(侵入されたら何が起きるか)は地続きに見える。

今回は 「公式パッチを待つ間に kernel LPE を modprobe blacklist で塞ぐ」 というシンプルな手順のメモを書く。ついでに、その動機になった xlabs_v1 の話も軽くだけ触れる。


1. 入口の話:xlabs_v1 を ELK で照合してみた

5/7、The Hacker News が xlabs_v1 という Mirai 派生ボットネットの記事を出した。ADB(Android Debug Bridge、5555/tcp)経由で IoT デバイスを乗っ取り、DDoS-for-hire(Minecraft 等のゲームサーバを標的にした 21 種のフラッド変種を備える)として運用されるやつだ。

C2 は 176.65.139[.]44(AS51396 Pfcloud, ルクセンブルク)。

「あ、これ前にも見たことあるサブネットじゃん」となった。

ローカル ELK で同サブネット 176.65.139[.]0/24 を引いてみたら、こんな感じだった。

全期間ヒット数 : 11,028+ hits
active な IP   : 15+
標的ポート Top : 22/tcp (3,143) > 8080 > 80 > 5555 > 23
主犯級         : 176.65.139[.]95   (5,350 hits)
                 176.65.139[.]111  (2,480 hits)
honeypot 内訳  : suricata 5,892 / cowrie 4,799 / adbhoney 119 ...

主犯級 2 IP(176.65.139[.]95 / .111)に絞って Cowrie ヒットを 20 日分引いてみるとこう。

主犯級2 IPからのCowrieヒット20日分。4/27にスパイクが集中している

Mirai 系の 22 / 23 / 5555 三点セットが綺麗に出てる。記事に出ていないペイロード名(zyre.arm7 / zyre.x86 等)も並行で観測した。

サブネット全体(過去 2 週間)のダッシュボード抜粋。

Top 10 Attackers と Top 10 Targeted Ports。サブネット全体で active な IP が並び、22/tcp が突出している

ただ、今回の主題はここではない。ここで言いたいのは、公開サーバを運用していると、「もし侵入されたら何が起きるか」を考える時間が増える、ということ。今回 LPE の話を書く動機の半分はここにある。


2. 出口の話:kernel LPE が二発来た

同じ週、Linux kernel のローカル特権昇格脆弱性が立て続けに二発公開された。

通称CVE影響対象発火条件
Copy.FailCVE-2026-31431 (CVSS 7.8)algif_aead (AF_ALG)非特権ローカルユーザ
Dirty Flag未アサインxfrm-ESP (esp4/esp6) / RxRPCxfrm-ESP は CAP_NET_ADMIN + userns / RxRPC は特権不要

どちらも、ローカルにシェルが取れた攻撃者が root に化けるためのものだ。リモート脆弱性とは違うレイヤーの話。

普通のサーバなら「ローカルにシェル取られた時点で詰みじゃね」と言える。けれどハニーポット運用は逆で、(疑似的な)シェルを取らせるのが仕事だ。Cowrie や Dionaea が攻撃者にコンテナ内シェルを与え、その挙動を観察する。コンテナ内シェルから kernel LPE 経由でホスト権限を奪われる、というのは現実的な脅威になる。

コンテナ運用ではあるがエスケープされると即死につながるし、即時対応の範囲ととらえた。

だから LPE 対策は、ハニーポットを自宅で置いてる側からすると他人事では済まない。


3. 塞ぐ前に確認すること

公式パッチが出る前に preventive にやるなら、確認は 2 方向ある。自環境のロード状況と、そのモジュールが業務で使われていないかだ。後者を飛ばすとサービスが死ぬので、塞ぐ前にひと呼吸入れる。

筆者の場合、という前置きで読んでいただき確認は各自行っていただきたい。

3-1. ディストリ・カーネルとモジュールロード状況

# (1) ディストリ・カーネル
uname -r
cat /etc/os-release

# (2) 該当モジュールが既にロードされてないか
lsmod | grep -E "esp4|esp6|rxrpc|af_alg|algif"

# (3) 過去にロードされた形跡(/sys/module 配下)
ls /sys/module/ | grep -E "esp4|esp6|rxrpc|af_alg|algif"

ロードされている → 依存関係を確認してから rmmod を検討する。 ロードされていない → preventive blacklist で塞いで終わり。

筆者の環境は Ubuntu 24.04.4 LTS / Linux 6.8.0-106-generic で、esp4/esp6/rxrpc は未ロード。af_alg は別途対策済みだった。

3-2. 該当モジュールを業務で使っていないか

blacklist は「使っていないモジュールを塞ぐから安全」という前提に立っている。逆に言えば、業務で使っているモジュールを塞いだ瞬間にサービスが死ぬ

雑な目安で恐縮ではあるが、事前確認が必要。

IPsec / VPN → esp4 / esp6 が必須

  • StrongSwan / Libreswan で IPsec トンネルを張っている場合、esp4 / esp6 のブロックは厳禁
  • WireGuard は kernel-side で esp を使わないので影響なし
  • SSL VPN(Cisco AnyConnect、OpenVPN 等)も esp 系不要
ip xfrm state | head                                                       # 出力があれば IPsec 稼働中
systemctl is-active strongswan strongswan-starter strongswan-swanctl 2>/dev/null
systemctl is-active ipsec 2>/dev/null                                      # Libreswan (RHEL系) 用

AFS(Andrew File System)クライアント → rxrpc が必須

  • OpenAFS / kafs を使う環境では rxrpc モジュールが必要
  • 大学・研究機関のホームディレクトリ共有で稀に残っている
  • 一般的なエンタープライズではほぼ使われていない
mount | grep -E "afs|kafs"
lsmod | grep -E "rxrpc|kafs"

AF_ALG(algif_aead 等)→ 念のため確認

  • LUKS フルディスク暗号化は通常 dm-crypt を直接使うので AF_ALG 不要
  • ただし一部の古い暗号化ライブラリや、kernel keyring 経由の暗号化アクセラレーションが AF_ALG socket を使うケースがある
  • wpa_supplicantcryptsetup の設定次第で有効化されている可能性
# AF_ALG socket を使ってるプロセスがいるか
sudo ss -xa | grep -i alg

# 既存プロセスの memory map から AF_ALG 利用を見つける
sudo grep -l "if_alg\|AF_ALG" /proc/*/maps 2>/dev/null

筆者の VPS(T-Pot ハニーポット)は IPsec も AFS も AF_ALG 利用ライブラリも使っていなかったので、全部 blacklist で塞いで問題なかった。が、エンタープライズの本番サーバや SOC のテナントノードでは、上のどれかが普通に動いている可能性がある。塞ぐ前にひと呼吸入れる、という話。


4. blacklist に何を追加するか

/etc/modprobe.d/ 配下に .conf を作る。中身は blacklist + install トラップの二段構え

なぜ二段構えなのか。図にするとこう。

[攻撃者・あるいは syscall がモジュールロードを誘発]
          ↓
     modprobe esp4
          ↓
  ┌────────────────────────────┐
  │ 1段目: blacklist           │ ← 名前一致でロード拒否
  └────────────────────────────┘
          ↓ alias 経由でバイパスされる場合あり
  ┌────────────────────────────┐
  │ 2段目: install /bin/true   │ ← 代わりに /bin/true を実行
  └────────────────────────────┘
          ↓
       本体はロードされず終了

blacklist 単体では、alias 経由(例: socket(AF_ALG, ...) での auto-load)で抜けられるケースがある。install <module> /bin/true を併記すると、modprobe はモジュール本体ではなく /bin/true を実行するので、確実にブロックできる。

Copy.Fail (CVE-2026-31431) 用

sudo tee /etc/modprobe.d/blacklist-algif_aead.conf <<'EOF'
blacklist algif_aead
blacklist algif_skcipher
blacklist algif_hash
blacklist algif_rng
blacklist af_alg
install af_alg /bin/false
install algif_aead /bin/false
install algif_skcipher /bin/false
install algif_hash /bin/false
install algif_rng /bin/false
EOF

ここで /bin/false を使っているのは、ロード試行時に exit 1 で帰したいから(後述の検証で違いが出る)。

Dirty Flag 用

sudo tee /etc/modprobe.d/dirtyfrag.conf <<'EOF'
blacklist esp4
blacklist esp6
blacklist rxrpc
install esp4 /bin/true
install esp6 /bin/true
install rxrpc /bin/true
EOF

こちらは /bin/true を使った。検証時の挙動が変わるだけで、防御効果は同じだ。

反映

sudo update-initramfs -u

これで再起動後も効くようになる。


5. 簡易検証

設定が効いているかは、強制ロードしてみて確かめる。

sudo modprobe esp4 ; echo "exit: $?"
sudo modprobe rxrpc ; echo "exit: $?"

# 実際にロードされていないかを確認
lsmod | grep -E "esp4|esp6|rxrpc"

注意:exit code に騙されない

install <module> /bin/true を使った場合、modprobe/bin/true を実行して exit 0 を返す。つまりコマンドラインからは「ロード成功」に見えてしまう。

正しい確認指標は lsmod の方だ。

modprobe esp4         → exit 0 (install /bin/true の挙動)
lsmod | grep esp4     → 何も返らない (実際にはロードされていない)
                                ↑
                          これが真の成功の証

/bin/false を使った場合は exit 1 で帰るので分かりやすい。が、どちらでも防御効果は同じ。気分の問題に近い。

auto-load 経路もテストする

カーネルは特定の syscall(例: socket(AF_ALG, ...)socket(AF_INET, SOCK_RAW, IPPROTO_ESP))で、関連モジュールを自動ロードしようとする。これがブロックされているかも見ておく。

# AF_ALG 経路(Copy.Fail 側)
sudo python3 -c "
import socket
try:
    s = socket.socket(38, 5, 0)   # AF_ALG=38
    print('NG: socket created (auto-load 経路が生きている)')
except OSError as e:
    print(f'OK: blocked - {e}')
"

# IPPROTO_ESP 経路(Dirty Flag 側)
sudo python3 -c "
import socket
try:
    s = socket.socket(socket.AF_INET, socket.SOCK_RAW, 50)  # IPPROTO_ESP=50
    print('socket created')
    s.close()
except Exception as e:
    print(f'Error: {e}')
"

# socket 試行後のロード状況再確認
lsmod | grep -E "esp4|esp6|rxrpc|af_alg|algif" || echo "OK: still blocked"

OK: still blocked が出れば、auto-load 経路も死んでいることが確認できる。

なお IPPROTO_ESP の SOCK_RAW socket は root で実行すると作成自体は通る(SOCK_RAW は本来 CAP_NET_RAW 必要、非特権で同経路を試したいときは unshare -Ur 経由で)。ただし lsmodesp4 が出てこなければ、脆弱コードパス(ESP モジュール本体)には到達していないので問題ない。


6. ロールバック

公式パッチが当たった後、blacklist を解除したい場合:

sudo rm /etc/modprobe.d/dirtyfrag.conf
sudo update-initramfs -u
sudo modprobe esp4 esp6 rxrpc

reboot は不要。即時で戻る。


おわりに

雑記なので感想で締める。

正直、こんな対策を毎週手動でやらなくていい世界のほうが良い。というか倫理って世界共通だと思ってるけど違うんだろうか。

ただ現実には、CVE 番号すら付いていない(Dirty Flag は2026/05/08もそう)状態で PoC が公開され、攻撃者の手元にだけツールが先に届くタイミングがある。そのギャップを埋める手段として、/etc/modprobe.d/ は地味に強い。アプリケーション層を一切触らず、必要最小限の攻撃面を削るだけで済む。というか緩和策以外の方法が無い。

自宅にハニーポットを置いている側からすると、xlabs_v1 のような感染試行を ELK で照合する機会があり、攻撃の入口と出口は地続きに見える。「侵入されたとして、その先で何が起きるか」を仮定せずには、防御の境界線を引けない。今回の LPE 対策は、その仮定の話だ。

まぁしっかりアウトバウンドは閉めてますけどね。

そして私はまだGWなう。


参考: Linux Kernel LPE 脆弱性情報 (Dirty Flag) - SIOS Security
参考: Mirai-Based xlabs_v1 Botnet Exploits ADB to Hijack IoT Devices for DDoS Attacks - The Hacker News
参考: CVE-2026-31431 - NVD
T-Pot: Deutsche Telekom Security GmbH / https://github.com/telekom-security/tpotce / GPL v3.0