【インシデント報告】Defender が有効なのに、開発機で5日間マイニングされていた —— 「AI がコードを壊した」と思ったら本当の原因は別だった
注意喚起を兼ねた個人環境のインシデント報告です。
同じ手口は「Defender を入れているから大丈夫」な環境をそのまま素通りします。
記事末尾に、管理者 PowerShell で5分で流せるチェックリストを置いたので、心当たりが無い方こそ一度実行してみてください。
インシデント概要
| 項目 | 内容 |
|---|---|
| 事象 | 暗号通貨マイナー(XMRig 6.21.3)の無断稼働 |
| 対象 | 自宅の Windows 11 開発機(DTM 兼用・20論理コア) |
| 侵入時刻 | 2026-08-10 18:21(マルウェアのファイル作成時刻より) |
| 検知時刻 | 2026-08-15 22:20 頃 |
| 潜伏期間 | 約5日間 |
| 被害 | CPU 20コア中15コアを常時占有(累計 CPU 時間 9時間54分/観測時点) |
| 権限 | 管理者権限を取得済み。Defender の除外リストを改ざんされていた |
| 常駐手法 | Windows の正規タスク名を乗っ取ったスケジュールタスク5つ(すべて最上位特権) |
| 情報流出 | **確認されず。**ただし読み取りは痕跡を残さないため「漏れた前提」で事後対応 |
| 侵入経路 | 特定できず(Defender 詳細ログがローテーションで消失) |
| 検知の端緒 | 自作 Bot の不調。当初は「AI がコードを壊した」と誤認していた |
| 対応 | 駆除完了。永続化機構8種・全465プロセス・通信・Chrome を再調査し、他のマルウェアは検出されず |
| 実作業 | 発見・調査・駆除はすべて Claude(Claude Code)が実施 |
TL;DR
- DTM とプログラミングに使っている自宅の Windows 機で、暗号通貨マイナー(XMRig)が管理者権限で5日間動いていた
- 20コア中15コアを常時占有され、CPU は 100% に張り付いていた
- Windows Defender は有効だった。 だがマルウェアのフォルダが除外リストに登録されていたので、5日間素通しだった
- 発見も駆除も、全部 Claude(Claude Code)がやった。 自分は「AI が余計なコードをいじったせいだろう」としか思っていなかった
- 常駐は Windows の正規タスク名を乗っ取る手口。タスクスケジューラを開いても名前だけでは気づけない
- 感染経路は特定できなかった。 ログのローテーションで証拠が消えていた
技術的な調査パートは、実際に手を動かした Claude 本人の視点で書いてもらった。
1. 発端:私は「AI がコードをいじったせい」だと思っていた
先に自分の非を書いておく。数日前から違和感はあった。
- PC がやたら熱い。 夏だし重い処理を回しているし、で流していた
- キーボード入力が時々引っかかる。 気のせいか、常駐ソフトが増えすぎたかと思っていた
- Windows Defender が有効だから大丈夫、と思っていた
そして当日。自分は Discord から Claude Code に指示を出す環境を組んでいて、その中に VOICEVOX で応答を読み上げる自作 Bot がいる。この Bot が急に不安定になった。
直前に Claude に頼んで、セッション復旧まわりの仕組みを実装させていた。だから自分はこう言った。
「たぶんその辺を改装した結果、音声読み上げ bot が落ちてしまった。共存できるようにしてほしい」
完全に AI のせいだと思っていた。 ウイルスの「ウ」の字も頭になかった。
以下、Claude が実際に何をしたか。
2. 【Claude 視点】自分のせいだと言われたので、まず現物を見た
私に向けられた指摘は「お前の変更で Bot が落ちた」でした。ただ、私が触ったのは ~/.claude/hooks/ 配下のスクリプトとスラッシュコマンドだけで、読み上げ Bot のコードには一行も触れていません。 因果として無理があるので、決めつけずにまず Bot の状態を見ました。
プロセスは生きていました。
Get-CimInstance Win32_Process -Filter "Name='pythonw.exe'"
# → C:\Users\<ユーザー名>\Desktop\yomiage-bot\bot.py が稼働中
「落ちている」と言われたのにプロセスがある。ここで「症状の言葉」と「実際に起きていること」がずれていると分かりました。 ログを見ます。
WARNING discord.gateway Shard ID None heartbeat blocked for more than 10 seconds.
WARNING discord.gateway Can't keep up, shard ID None websocket is 28.0s behind.
INFO discord.gateway Shard ID None session has been invalidated.
これはコードの問題ではありません。 heartbeat がブロックされる、websocket が28秒遅れる、というのは プロセスに CPU が回ってきていない ときの症状です。私の変更でこうなることはあり得ません。
時系列も見ました。
21:58 ログイン成功、ボイスチャンネル接続成功
22:01 正常に読み上げ完了
22:14 合成に失敗して再試行
22:24 接続失敗が30回連続
22:33 heartbeat blocked
21:58 には完全に正常に動いていて、22:14 から崩れ始めています。 私がファイルを書いたのはその前後ですが、書いたファイルは Bot と無関係です。「時間が近い」だけで犯人にするのは、よくある誤診です。
原因は CPU 側にあると判断して、そちらを見ました。
3. 【Claude 視点】CPU の犯人を「測って」特定する
まず全体。
Get-CimInstance Win32_Processor | Measure-Object -Property LoadPercentage -Average
# → 100 %
100% 張り付き。 ここで「タスクマネージャで一番上のやつ」を見に行くと間違えます。表示される CPU% は瞬間値でブレるので、一定時間の CPU 時間の増分で測ります。
$snap1 = Get-Process | Select-Object Id, ProcessName, CPU
Start-Sleep -Seconds 5
$snap2 = Get-Process | Select-Object Id, ProcessName, CPU
# Id をキーに CPU の差分を取って降順
結果はこうでした。
Id Name CPU増分秒
-- ---- ------
22072 updater 75.55 ← 5秒で75.55秒 = 15コア分を占有
2024 herdr 1.16
31400 Discord 1.12
5秒間に75秒分の CPU を使っているプロセスが1つだけいました。 この時点で「重い処理が偶然重なった」の線は消えます。1プロセスが15コアを専有し続けるのは、普通のアプリケーションの挙動ではありません。
名前は updater.exe。いかにも「何かの更新プログラム」に見える名前です。ここで名前を信じると終わりです。
4. 【Claude 視点】名前ではなく、実体を見る
updater.exe は昇格プロセスで、通常権限では実行ファイルのパスすら読めませんでした。読めない情報を諦めて、読める情報を全部集めます。
tasklist /FI "PID eq 22072" /V /FO LIST
Image Name: updater.exe
Status: Not Responding
CPU Time: 9:54:38
Window Title: XMRig 6.21.3 ← ここ
ウィンドウタイトルに XMRig 6.21.3 と書いてありました。
XMRig は Monero のマイニングソフトで、それ自体は合法な OSS ですが、無断採掘マルウェアが使う定番でもあります。updater.exe という名前で偽装しながら、ウィンドウタイトルは隠していなかった、ということです。
まだ断定しません。稼働の証拠を取ります。
Get-NetTCPConnection -State Established | Where-Object { $_.OwningProcess -eq 22072 }
# → RemoteAddress 15.235.234.199 RemotePort 3333
ポート 3333 は採掘プールの定番ポートです。 外部の採掘プールに繋がって、いま現在も掘っている。CPU 時間の累計は 9時間54分(プロセス起動から50分の時点で)。
ここで確定しました。 名前・ウィンドウタイトル・CPU 消費・通信先の4つが揃って、初めて「マルウェア」と言えます。
5. 【Claude 視点】居座り方が巧妙だった —— 正規タスク名の乗っ取り
止めるだけでは再起動で復活します。どこから起動されているかを辿ります。
claude が見た親子関係:
svchost.exe (タスクスケジューラ)
└ update.exe
└ updater.exe (XMRig)
スケジュールタスクが起点です。ここで「怪しい名前のタスクを探す」ではなく、「実行パスが怪しいタスクを探す」ようにしたのが、今回の分かれ目でした。
Get-ScheduledTask | ForEach-Object {
$t = $_
foreach ($a in $t.Actions) {
if ($a.Execute -match 'AppData|\\Temp\\|\\Public\\') {
[pscustomobject]@{ 状態=$t.State; タスク=($t.TaskPath+$t.TaskName); 実行=$a.Execute; 権限=$t.Principal.RunLevel }
}
}
} | Format-List
出てきたのがこれです。
[実行中] \Microsoft\Windows\Shell\FamilySafetyRefreshingTask
→ C:\Users\<ユーザー名>\AppData\Local\Microsoft\Edge\System\update.exe
[実行中] \Microsoft\Windows\Multimedia\SystemRecordService
→ C:\Users\<ユーザー名>\AppData\Roaming\DriversUpdate\Core\UpdateCoreDrivers.exe
[待機] \Microsoft\Windows\USB\Usb-Notification
→ C:\Users\<ユーザー名>\AppData\Roaming\DriversUpdate\Runtime_Broker.exe
[待機] \Microsoft\Windows\MUI\RPRemove
→ C:\Users\<ユーザー名>\AppData\Roaming\Microsoft\Crypto\CRC\Runtime.exe /verysilent
[待機] \Microsoft\Windows\MUI\FPRemove
→ C:\Users\<ユーザー名>\AppData\Roaming\DriversUpdate\taskhostupdate.exe
5つとも、Windows の正規タスクと同じ名前・同じ階層に置かれていました。 そして全部「最上位の特権で実行」。
本物の FamilySafetyRefreshingTask は Windows のシステムフォルダを指します。AppData を指すことは絶対にありません。
タスク名では見分けられない。実行パスで見るしかない。
ファイルの置き場も同じ発想で偽装されていました。
C:\Users\<ユーザー名>\AppData\Local\Microsoft\Edge\System\ ← Edge のフォルダに偽装
updater.exe 6.36MB ← XMRig 本体
update.exe 12.8KB ← 起動役
SearchUI.exe 509KB
WinRing0x64.sys 14.5KB ← カーネルドライバ
C:\Users\<ユーザー名>\AppData\Roaming\DriversUpdate\
core.exe / SQLite.Interop.dll / Core\UpdateCoreDrivers.exe / RuntimeBroker.exe
C:\Users\<ユーザー名>\AppData\Roaming\Microsoft\Crypto\CRC\
Runtime.exe
全ファイルが同じ日の 18:21 に作成、全部デジタル署名なし。
注意点として報告したのは、Edge も Microsoft\Crypto も本物の Windows のフォルダだということです。 その配下に自分のフォルダを作って紛れ込んでいるので、削除するとき末尾のフォルダ名を間違えると本物を壊します。 特に Crypto は暗号鍵の保管場所で、ごと消すと証明書関連が壊れます。
6. 【Claude 視点】なぜ Defender が5日間見逃したのか
ここが本題かもしれません。Defender はリアルタイム保護が有効で、定義も当日更新済みでした。それでも見逃していました。
除外リストを確認してもらいました(管理者権限が要るので、これは人の手で実行してもらう必要があります)。
Get-MpPreference | Select-Object -ExpandProperty ExclusionPath
C:\Users\<ユーザー名>\Documents\Celemony\Separations ← 本人が入れたもの
C:\Users\<ユーザー名>\AppData\Local\Microsoft\Edge\System ← ★ マルウェアが追加
C:\Users\<ユーザー名>\AppData\Roaming\DriversUpdate ← ★ マルウェアが追加
マルウェアが、自分の置き場を Defender の監視対象から外していました。
そして、この事実からもう一つ確定することがあります。除外パスの追加には管理者権限が必要です。 つまり 18:21 の投下時点で、すでに管理者権限を取られていた。
さらに検出履歴を見ると、Defender は一度だけ発報していました。
Get-MpThreatDetection
# 2026-08-10 19:46:04 ThreatID=2147937641 file:_C:\Windows\SystemTemp\UDDCD4D.tmp
Get-MpThreat
# VulnerableDriver:WinNT/Winring0 Severity=5 IsActive=False
WinRing0 は XMRig が CPU の低レベル制御と権限昇格に使うカーネルドライバです。 これが除外フォルダの外(C:\Windows\SystemTemp)に出た瞬間に、Defender は捕まえて処理していました。
だから、ディスクに置かれていた WinRing0x64.sys は一度もカーネルに読み込まれていません。 実際、稼働中ドライバを確認しても登録されていませんでした。
# WinRing0 のサービス登録は見つかりません
Get-ChildItem 'HKLM:\SYSTEM\CurrentControlSet\Services' | Where-Object { $_.PSChildName -match 'WinRing0' }
Defender は仕事をしていました。誰も通知を見ていなかっただけです。
7. 【Claude 視点】駆除 —— 順番に理由がある
駆除は「消す」より「順番」が大事でした。 私が組んだ順はこれです。
1. 偽装タスクを削除 ← 先に起点を潰さないと、消したそばから再取得される
2. プロセスを停止 ← パスで特定する(後述)
3. Defenderの除外を解除 ← ここを先にしないと、再作成されても検知できない
4. ファイルを削除
5. フルスキャン
3を4より先にするのが肝です。 除外が残ったままファイルだけ消すと、Defender が見ていない場所が残り続けます。 そこに再度置かれたら、また5日間気づけません。
手順1:偽装タスクを止めて削除
'\Microsoft\Windows\Shell\FamilySafetyRefreshingTask',
'\Microsoft\Windows\Multimedia\SystemRecordService',
'\Microsoft\Windows\USB\Usb-Notification',
'\Microsoft\Windows\MUI\RPRemove',
'\Microsoft\Windows\MUI\FPRemove' | ForEach-Object {
$p = Split-Path $_ -Parent; $n = Split-Path $_ -Leaf
Stop-ScheduledTask -TaskPath "$p\" -TaskName $n -EA SilentlyContinue
Unregister-ScheduledTask -TaskPath "$p\" -TaskName $n -Confirm:$false -EA SilentlyContinue
}
手順2:プロセスを停止(名前ではなくパスで狙う)
Get-CimInstance Win32_Process | Where-Object {
$_.ExecutablePath -match 'AppData\\(Local\\Microsoft\\Edge\\System|Roaming\\DriversUpdate|Roaming\\Microsoft\\Crypto\\CRC)'
} | ForEach-Object {
Write-Output ("停止: PID " + $_.ProcessId + " " + $_.ExecutablePath)
Stop-Process -Id $_.ProcessId -Force
}
この時点で CPU は 100% → 3% に落ちました。
手順3:Defender の除外から、マルウェアの2つだけを外す
Remove-MpPreference -ExclusionPath "C:\Users\<ユーザー名>\AppData\Local\Microsoft\Edge\System"
Remove-MpPreference -ExclusionPath "C:\Users\<ユーザー名>\AppData\Roaming\DriversUpdate"
Get-MpPreference | Select-Object -ExpandProperty ExclusionPath # 残りを確認
手順4:ファイル削除(末尾のフォルダ名を間違えないこと)
Remove-Item "C:\Users\<ユーザー名>\AppData\Local\Microsoft\Edge\System" -Recurse -Force
Remove-Item "C:\Users\<ユーザー名>\AppData\Roaming\DriversUpdate" -Recurse -Force
Remove-Item "C:\Users\<ユーザー名>\AppData\Roaming\Microsoft\Crypto\CRC" -Recurse -Force
削除後、巻き添えが無いことを確認しました。
消滅 AppData\Local\Microsoft\Edge\System
消滅 AppData\Roaming\DriversUpdate
消滅 AppData\Roaming\Microsoft\Crypto\CRC
無事 AppData\Local\Microsoft\Edge ← Edge の正規データ
無事 AppData\Roaming\Microsoft\Crypto ← 暗号鍵の保管場所
手順5:フルスキャン
Start-MpScan -ScanType FullScan
8. 【Claude 視点】自分の見落としを1つ報告します
手順2のコマンドを、私は最初「名前」で書いていました。
# 最初に出したもの(これは不十分でした)
Stop-Process -Name updater,update,UpdateCoreDrivers,Runtime,Runtime_Broker,SearchUI -Force
その後「パスで狙う版」に書き直したのですが、そのパス版が、私が見落としていたプロセスを1つ捕まえました。
停止: PID 27444 C:\Users\<ユーザー名>\AppData\Roaming\DriversUpdate\RuntimeBroker.exe
なぜ見落としたか。 プロセス一覧に RuntimeBroker は10個並びます。Windows の正規プロセスだからです。私は一覧を見たとき「RuntimeBroker が10個、正規プロセス」と判断してまとめて流していました。 その中の1個だけが偽物でした。
正しい見方はこうです。
Get-CimInstance Win32_Process -Filter "Name='RuntimeBroker.exe'" |
Select-Object ProcessId, ExecutablePath
本物は全部 C:\Windows\System32\RuntimeBroker.exe にいます。1個でも違う場所から動いていたら、それが答えです。
攻撃者はタスク名とプロセス名の両方で同じ発想を使っていて、私は片方で引っかかりました。 名前を見た瞬間に「知っているやつだ」と処理を打ち切るのが、人間にも AI にも共通する穴だと思います。
9. 【Claude 視点】「XMRig が消えた」で終わらせず、全体を洗い直した
低 CPU で潜伏するタイプや情報窃取系は、CPU 使用率では見つかりません。 なので前回の結果を前提にせず、独立した再調査をしました。「CPU 負荷が低いから安全」という判断は一切使っていません。
| 項目 | 結果 |
|---|---|
| スケジュールタスク | ユーザー領域から動くのは本人が入れた4つのみ |
| Run / RunOnce / スタートアップ | 既知のアプリのみ |
| サービス | AppData / Temp から起動するものなし |
| WMI イベント購読 | Consumer・Binding ともに 0 件 |
| Winlogon |
Shell=explorer.exe / Userinit=userinit.exe(既定値) |
| AppInit_DLLs | 空、LoadAppInit_DLLs=0
|
| IFEO |
Debugger 乗っ取りゼロ |
| 実行中プロセス | 465個中、標準パス外の40個を全て素性確認。偽装・不自然な親子関係なし |
| 外部通信 | 27接続・26待受ポート、全て既知のアプリ。C2 らしき接続なし |
| Defender 検出履歴 | WinRing0 の1件のみ |
| キーボード/マウスのフィルタドライバ |
kbdclass / mouclass のみ |
特に確度が高いと考えている陰性所見が2つあります。
① WMI イベント購読が完全に空だったこと。
Get-CimInstance -Namespace root\Subscription -ClassName CommandLineEventConsumer
Get-CimInstance -Namespace root\Subscription -ClassName ActiveScriptEventConsumer
Get-CimInstance -Namespace root\Subscription -ClassName __FilterToConsumerBinding
ファイルレス常駐の主要な逃げ場です。 ここが空なら「ファイルを消しても WMI から復活する」タイプはいません。
② キーボードのフィルタドライバが正常値だったこと。
依頼者から「最近キーボード入力を受け付けないタイミングがある。マルウェア由来じゃなければいいが」と相談を受けたので、ドライバ型キーロガーを名指しで確認しました。
$kb = 'HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4d36e96b-e325-11ce-bfc1-08002be10318}'
(Get-ItemProperty $kb).UpperFilters # → kbdclass のみ(正常値そのもの)
(Get-ItemProperty $kb).LowerFilters # → 空(正常)
キーロガーがドライバとして入っていれば、ここに必ず名前が出ます。出ていませんでした。 そして症状の説明は付きます。5日間 15 コアを占有されていたのだから、入力が引っかかって当然です。
10. 【Claude 視点】私が誤爆した話 —— 消す前に素性を確定させる
調査中、C:\Windows\Temp に素性の分からない署名付き DLL を2つ見つけて「要確認」に分類しました。
.bdebfb7bce73bfbc-00000000.dll 3,786,120 B 作成 08-11 00:29:53
.bdebfb7bffe75f9c-00000000.dll 3,786,120 B 作成 08-11 00:30:02
疑った理由は、感染翌日に9秒差で同じファイルが2つ置かれたこと、. で始まる16進名は人が付ける名前ではないこと、そして同サイズのファイルが Program Files 配下にも AppData 配下にも1つも無かったこと。つまりインストール済みアプリの正規ファイルではない。
結論は完全に白でした。
署名を掘ると、発行元が Microsoft ID Verified CS AOC CA 03(Microsoft が法人の身元を検証する署名サービス)で、署名者が ある開発ツールのベンダーでした。そのツールの npm パッケージの導入時刻を調べると、DLL の作成時刻と秒単位で一致しました。VirusTotal でも検出なし。依頼者がその日に入れた CLI ツールが、インストール時に Temp へ展開したものでした。
教訓として、これは残す価値があると思っています。
「感染直後の日付」というだけで疑うと誤爆する。疑うのは正しいが、消す前に必ず素性を確定させること。
正規ファイルを勘で消すのが、一番損をします。 私はこの2ファイルについて、削除せず記録だけして依頼者に判断材料を渡しました。ハッシュを VirusTotal で検索する(ファイル自体を送らずに判定できます)方法も合わせて。結果、消さずに済みました。
11. 感染経路は分からなかった
ここは「不明」としか書けません。 調べた範囲を正直に列挙します。
- ブラウザのダウンロード履歴:感染当日のダウンロードは正規ソフトのインストーラ1件のみ。しかも投下の4時間半後で時刻が合わない
- Defender の詳細ログ:8/12 分からしか残っていなかった。 8/10 の記録はローテーションで消滅
- タスク登録のイベントログ:残っていない
- 本人の記憶:動画編集ソフトで作業していただけで、何かを入れた覚えがない
なので「潜伏していたものが起動した」のか「その時点で入ってきた」のかすら判別できません。
(ここからは自分=人間側の話)心当たりの筆頭は DTM 系だと思っている。 フリーの音源、プラグイン、サンプルパック——インストーラ形式で配布され、管理者権限を要求し、出所の検証が甘くなりがちなものを日常的に入れている。今回もそこから入った可能性は否定できない。
ただし証拠はなく、特定のソフトを名指しできる材料は一切ない。 なので結論はこうなる。
感染経路は不明。心当たりの筆頭は DTM 系プラグインの導入だが、裏付けは取れなかった。
12. 再発防止:Defender の発報を必ず気づけるようにした
今回の教訓は明確でした。
Defender は仕事をしていた。気づかなかったのは人間の側。
なので Claude に、Defender が何か検出したら強制的に手元へ通知が飛ぶ仕組みを作ってもらった。イベントログのトリガでスクリプトを叩き、チャットツールへ投げるだけ。Windows のタスクスケジューラだけで組める。
$subscription = @'
<QueryList><Query Id="0" Path="Microsoft-Windows-Windows Defender/Operational">
<Select Path="Microsoft-Windows-Windows Defender/Operational">
*[System[(EventID=1116 or EventID=1117 or EventID=1006 or EventID=1015)]]
</Select></Query></QueryList>
'@
$class = Get-CimClass -ClassName MSFT_TaskEventTrigger -Namespace Root/Microsoft/Windows/TaskScheduler
$trigger = New-CimInstance -CimClass $class -ClientOnly
$trigger.Subscription = $subscription
$trigger.Enabled = $true
$action = New-ScheduledTaskAction -Execute $pythonw -Argument '"C:\path\to\notify.py"'
$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest
Register-ScheduledTask -TaskName 'DefenderAlertToDiscord' -TaskPath '\' `
-Action $action -Trigger $trigger -Principal $principal
イベント ID は 1116(検出)、1117(処置済み)、1006(スキャンで検出)、1015(不審な挙動)。Defender の Operational ログの読み取りには管理者権限が要るので SYSTEM で走らせる。
タスク名は \ 直下に、何をするものか分かる名前で置いた。 Claude いわく「今回のマルウェアが \Microsoft\Windows\... 配下の正規名に偽装していたので、こちらは隠さない場所に、隠さない名前で置くべきだ」とのことで、確かにその通りだと思う。
13. 認証情報は「漏れた前提」で動く
採掘そのものはデータを盗まない。が、同じ権限で何でもできたのは事実だ。
さらに、削除したフォルダに SQLite.Interop.dll(2MB) が入っていた。採掘ソフトに SQLite は要らない。 一方で Chrome の保存パスワード・Cookie・自動入力は全部 SQLite のデータベースだ。
Chrome 側も調べてもらったが、攻撃された形跡は何も出なかった。
- リモートデバッグ未使用(
DevToolsActivePortが全プロファイルに存在しない) - 拡張機能に感染日以降の追加ゼロ(6プロファイル全部)
- 起動ショートカットの引数汚染なし
- 資格情報 DB のコピーがどこにも無い
それでも Claude の推奨は「漏れた前提で動く」だった。理由は「読み取りは痕跡を残さないから」。 納得したので、そうしている。
優先順位はこう提示された。
- 全デバイスからサインアウト(Cookie を盗まれていた場合、パスワード変更だけではセッションが切れない)
- 2要素認証を入れる
- パスワード変更:メール → 金銭が絡むもの → 開発・クラウド → SNS の順
- 開発用トークンの再発行
1番を最初に持ってくるのが肝。 ここを後回しにすると、パスワードを変えても相手のセッションが生き残る。
まとめ:今日から使えるチェック
管理者 PowerShell で上から流すだけ。心当たりが無くても、一度は見る価値がある。
# 1. Defender の除外に、身に覚えのないパスが入っていないか(今回の最重要)
Get-MpPreference | Select-Object -ExpandProperty ExclusionPath
Get-MpPreference | Select-Object -ExpandProperty ExclusionProcess
# 2. Defender が過去に何を検出したか(見逃した警告が眠っている)
Get-MpThreatDetection | Sort-Object InitialDetectionTime
Get-MpThreat
# 3. ユーザー領域から起動するスケジュールタスク(正規名に偽装されていてもパスで露見する)
Get-ScheduledTask | ForEach-Object { $t=$_
foreach ($a in $t.Actions) {
if ($a.Execute -match 'AppData|\\Temp\\|\\Public\\') {
"{0,-9} {1}{2}`n → {3}" -f $t.State, $t.TaskPath, $t.TaskName, $a.Execute
}
}
}
# 4. 標準パス以外から動いているプロセス(名前ではなくパスで見る)
Get-CimInstance Win32_Process | Where-Object {
$_.ExecutablePath -and $_.ExecutablePath -notmatch '^C:\\(Windows|Program Files)'
} | Select-Object ProcessId, Name, ExecutablePath | Sort-Object ExecutablePath
# 5. WMI 常駐(ファイルレスの逃げ場。空が正常)
Get-CimInstance -Namespace root\Subscription -ClassName CommandLineEventConsumer
Get-CimInstance -Namespace root\Subscription -ClassName ActiveScriptEventConsumer
# 6. キーボードのフィルタドライバ(kbdclass 以外があればキーロガーを疑う)
(Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4d36e96b-e325-11ce-bfc1-08002be10318}').UpperFilters
要点を4つだけ。
- 「PC が熱い」「入力が引っかかる」は、立派な症状。 気のせいにしない
- Defender が有効 ≠ 守られている。 除外リストを一度見る
- 名前で判断しない。パスで判断する。 タスクもプロセスも
- Defender の警告に気づける導線を作る。 出ていても見なければ意味がない
おわりに:バイブコーディング勢こそ、一度棚卸しを
これは完全に自戒として書く。
自分は AI にコードを書かせながら開発する、いわゆるバイブコーディングをかなりの割合でやっている。楽しいし、実際に手は速くなった。が、この進め方は「自分の PC に何が増えたか」を分からなくする方向に強く働く。
思い当たることを挙げる。
- npm や pip のパッケージが、自分が明示的に選んでいないものまで増える。 提案されたものをそのまま入れる場面が多い
- MCP サーバやローカルツールが増える。 そのたびにバイナリと常駐プロセスが増える
- 権限の確認プロンプトに慣れて、内容を読まずに通すようになる。 これが一番危ない
-
AppData配下に実行ファイルが増えることが「普通」になる。 開発ツールの多くがそこに入るので、マルウェアが同じ場所に紛れても違和感がなくなる - CPU が唸っていても「重い処理を回しているから」で説明がついてしまう。 今回まさにこれで5日溶かした
- 不調があると、まず AI の変更を疑う。 今回の自分がそう。「AI が壊した」で思考が止まると、本当の原因に辿り着かない
今回の攻撃者が AppData を置き場に選んだのは合理的だ。 そこは現代の開発者にとって「実行ファイルがあって当たり前の場所」になっている。環境が雑然としているほど、異物は目立たなくなる。
セキュリティを固めようという話ではない。自分の環境に何があるかを、たまには数えようという話。 全部を理解する必要はないが、
「これは何のプロセスか説明できない」が積み上がった状態は、それ自体がリスク
だと思う。今回、標準パス以外から動いているプロセス40個を1個ずつ潰していったが、全部の素性を説明できたのは、ここ最近たまたま環境を整理していた副産物だった。これが100個あって半分説明できない状態だったら、偽の RuntimeBroker は今も動いていたと思う。
上のチェックリストは5分で流せる。 年に何回かでいい。特に、
- AI に提案されるまま環境を増やしている人
- 権限プロンプトを反射で通している自覚がある人
- フリーのプラグインやツールをよく入れる人(自分は DTM でこれ)
は、一度自分の PC を「知らない人の PC」だと思って眺めてみることを勧めたい。
最後に。今回、発見から駆除、その後の全体調査まで、実作業はすべて Claude がやった。 自分がやったのは「Bot が落ちた、たぶんお前のせいだ」と言ったことと、管理者権限が要るコマンドを実行したことだけだ。
AI に環境を触らせることのリスクはよく語られるが、AI に環境を調べさせるメリットはもっと語られていいと思う。465 個のプロセスを1個ずつパスと署名と親子関係で照合する作業を、自分は絶対にやらなかった。
そして何より、「お前のせいだろう」と言われて、それを鵜呑みにせず現物を確認したことが、発見の入口だった。
コメント歓迎です
この記事は「素人がやらかして、なんとか復旧した」記録であって、専門家の分析ではありません。手順や判断に穴があれば、遠慮なく指摘していただけると嬉しいです。
特に、こういう情報をいただけると助かります(自分にとっても、同じ状況の読者にとっても)。
- 「この駆除手順、ここが足りない」 —— 消し残しやすいポイント、確認すべきだった項目
- 「侵入経路はこれじゃないか」 —— 同じ手口(正規タスク名の乗っ取り+Defender 除外の改ざん)を見た方の情報
- 「DTM 環境ならここも見るべき」 —— VST/AU の配布経路まわりで気をつけていること
- 「もっといい検知の仕組みがある」 —— Sysmon、Autoruns、EDR の個人利用など
- 「この記述は誤り」 —— 技術的な間違いの指摘
特に感染経路については、いまだに分かっていません。 同じような被害に遭った方、あるいは「その手口ならこういう入り口が定番」という心当たりのある方がいれば、ぜひ教えてください。
そして、この記事を読んで末尾のチェックリストを流してみて、何か出てきた方——ぜひコメントで教えてください。一人でも「出てきた」人がいれば、この記事を書いた意味があります。