株式会社Flatt SecurityとTokyo HackerOne Clubが共催した脆弱性勉強会「Security․Tokyo #1」。満員の会場ではテーマの異なる6つのLTが発表され、盛況のうちに終わりました。
今回は、LT1「Deep in 国際化ドメイン名」とLT2「Credential GuardをバイパスするPass-the-Challengeについて」の発表内容をお届けします。
<注意事項>
本記事は技術的知見の共有を目的としています。掲載された発表内容の悪用や曲解、その他社会通念に反する行為を固く禁じます。
▼LT3 ,4はこちら▼
- Deep in 国際化ドメイン名
- Credential GuardをバイパスするPass-the-Challengeについて
- Security․Tokyoでは運営メンバーを募集しています!(応募〆切:3/17(金)18:00)
Deep in 国際化ドメイン名
スライド
プロフィール
影白(@KageShiron)
大学院でドメイン名のセキュリティについて研究。現在はWeb診断員として日々脆弱性と戦っています。
Firefoxに存在した国際化ドメイン名に関する脆弱性
今回紹介する脆弱性は、Firefoxに存在した、国際化ドメイン名の不適切なURLパース(CVE-2021-43533)というものです。2年ぐらい前に、Firefox 94のバージョンで修正されました。
まずは、国際化ドメイン名というものについて説明させていただきます。
馴染みのある方もいるかもしれませんが、国際化ドメイン名というのはドメインをUnicodeに拡張した規格です。日本語のほか、ドイツ語や絵文字なども使用できます。最近だと、検索流入を見越してか「○○攻略.com」のような形で、ソシャゲの攻略サイトで使われることがあるようです。
Unicodeはとにかく複雑
Unicodeには、絵文字、サロゲートペア、結合文字、異体字セレクタなど色々な機能があり、かなり複雑になっています。
また、異なる言語や文化ごとの事情も考慮する必要があります。例えば、以下のようなものが挙げられます。
- 組文字
- 絵文字
- 筆記体
- 言語による表記揺れ
- (例)トルコ語と英語での表記揺れ
- トルコ語:(大文字)I (小文字)ı
- 英語 :(大文字)I (小文字)i
- (例)ドイツ語ではßの大文字にSSを用いることがある
- (例)トルコ語と英語での表記揺れ
もう1つ重要な規格があります。旧来のASCII文字との互換性を維持するためにUラベルとAラベルというアルゴリズムが規定されています。DNS、HTTPヘッダ等では原則Aラベルしか使用できません。
表記の自由度が高い国際化ドメイン名
国際化ドメイン名の表記の自由度はかなり高く設計されています。 利便性を確保するための正規化処理として、以下のように変換が行われます。
- ひらがな・カタカナと濁点の組み合わせ
- 「か゛」(U+304B U+3099)→「が」(U+304C)
- 飾り文字
- 「ⓔ」(U+24D4)→「e」(U+0065)
- 組文字
- 「㌍」(U+330D)→「カロリー」(U+30AB U+30EA U+30EA U+30FC)
- 読点をドットに変換
- 「。」(U+3002)→「.」(U+002E)
国際化ドメイン名に起因する諸問題
ここからは、国際化ドメイン名に起因する諸問題を見ていきたいと思います。
ホモグラフ攻撃
ホモグラフとは、「似た字形の文字」のことを指します。数字の「0(ゼロ)」とアルファベットの「O(オー)」などがその例です。 よりわかりにくい例で言うと、アルファベットの「a(エー)」とキリル文字の「а(アー)」があります。フォントによっては見た目が完全に同じになるため、これを悪用して偽ドメインが作られた場合、URLを見ただけでは全く区別が付かなくなってしまうという問題があります。Webブラウザ側でも色々な対策を試みているのですが、あまり実効性があるとは言えません。
数年前、偽の「apple.com」のドメインを実際に登録し、証明書まで発行する実証実験が報告されています。
スパムフィルタのバイパス
スパムフィルタのブラックリストにドメインが登録されている場合であっても、ドメイン内の一部のドットを句点に変えて送信すれば、フィルタを通り抜けてしまう可能性が考えられます。
URLの正規化による解釈の揺れ
また、URLの正規化によって、別ドメインと解釈される可能性もあります。似た字形の文字が正規化で別の字に置き換えられることにより、別ドメインとなってしまうケースもあります。例えば、トルコ語の「İ(U+0130)」を英語の「i(U+0069)」に置き換えてしまうと状況によっては別のドメインと解釈されるおそれがあります。
以下のケースのように、XXSやSQLインジェクションに繋がる可能性のあるメタ文字が出現する例もあります。
- 組文字内のスラッシュが出現
- ℀ (U+2100) → a/c
- 全角記号が半角に
- ” → "
- ’ → '
- 1. → 1.
これはBlackHat USA2019で報告された、HostSplitという脆弱性です。ドメインの正規化をリクエストに含んだ場合、機能Aは「.office.com」のサブドメインとして認証してしまい、機能Bは認証情報を正規化後のドメインに送信してしまう、というものでした。
信用できないURLパーサ
さて、そろそろ本題に入ります。
まず、「URLパーサは本当に信用できるのか」。セキュリティをやっている方なら、独自のURLパーサや正規表現で判断すると、バグや解釈誤りが発生しやすいというのはご存知かと思います。例えば、「http[:]//example․com@example․net」というURLでは@の前の「example.com」は認証情報ですが「http[:]//example․com」までをみて「これはexample․comというドメイン名なんだ」と判断してしまったり、区切りであるバックスラッシュの解釈を誤ってしまったりといった可能性があります。
対策として、言語公式のURLパーサや、よくメンテナンスされているURLパーサを使おうという話もあるにはあるのですが、本当に大丈夫なのか疑問に思っています。
実際に調査してみると、Webサイトの国際化ドメイン名の解釈はあまり統一されていないことがわかります。オプションの指定によっては、HostSplitや予期せぬバグに繋がってしまう可能性もあることがわかりました。かなり仕様が複雑すぎるのと、何が正しいかわからないくらいバグりまくっているので、バグの検証が難しいという問題もあります。
ブラウザによって異なるURLの解釈
ここでは、2つURLを用意してみました。1つ目は男性の絵文字に、赤い髪を合成した絵文字を入れ、2つ目はxn--に続けて日本語を入れてみました。これを調査した地点の古いGoogle Chrome、Firefox、Safariは、これらをどう解釈するのでしょうか?
まず、1つ目のURLを見ていきます。Google Chromeが、中央にあるゼロ幅接合子を削除して処理を行った一方、Firefoxではゼロ幅接合子を削除せずに処理していました。Safariでは、エラーになってしまいました。
ということで、ブラウザによって、URLの解釈が異なってしまうという結果になりました。
もう1つのURLでは、UTF-16の上位バイトが無視されて処理されてしまいました。実際にソースを読んだ感じだと、途中でC言語のcharにキャストしていたのが悪かったのかなと思っています。こちらの脆弱性(CVE-2021-43533)は、フィッシング等に悪用されるおそれがあるとして現在は修正されています。
URLの解釈を誤るとどのような問題が起こるのか
では、URLの解釈を誤ると、実際にどのような問題が起こるのでしょうか。
実は、具体的な被害の想定はかなり難しいです。「xn--あああ.example.com」が入力できる場所には「evil.example.com」も入力できますし、「予期しないホスト名と一致する」ことは脆弱性に該当しますが、逆に「一致しない」ことで起こる脆弱性は多くないです。フィッシングや検出回避などに悪用されることは想定されますが、複数のアプリや機能間の解釈の違いを悪用されると発見は困難だと思います。見つかりそうな箇所のアイデアがあれば、ぜひ教えてください。
まとめ
国際化ドメインは非常に複雑で、かなりバグだらけです。「Uniform Resource Identifierだから同一性を担保できる」という過度な期待はやめましょう。
バグまみれではありますが、国際化ドメインのバグは脆弱性には直結しにくいものが大半です。これからもFQDNをチェックしつつ、Host Splitには注意を払っていきましょう。
Credential GuardをバイパスするPass-the-Challengeについて
スライド
プロフィール
なお(@n_etupirka)
とあるセキュリティベンダのペネトレーションテスターです。 ペンテストの傍ら、趣味でAndroidアプリの脆弱性を報告したりしてます。
Credential Guardの概要
今回は、Pass-the-Challengeという攻撃手法についてご説明します。Pass-the-ChallengeはCredential Guardをバイパスする攻撃手法のため、まずはCredential Guardについて少し解説させていただきます。
Credential Guardとは、NTLMハッシュなどの資格情報の保護を目的としたWindowsの機能です。資格情報を仮想化ベースセキュリティという技術によってLSASSから分離することで保護しています。
Credential Guardの構成は上図のようになっています。HypervisorによってOSとは異なるメモリ空間を持っているVM上に、LSAIsoというプロセスが起動しています。LSAIsoは資格情報の暗号化や復号などといった直接的な操作を行いますが、他のプログラムとは一切通信ができず、LSASSからのみ呼び出すことが可能です。これに対して、LSASSはLSAIsoを介して資格情報を使用し、他のプログラムと通信することで認証を行っています。
ここで、実際にCredential Guardが無効の場合と有効な場合のそれぞれで、LSASSのメモリをダンプしてみました。
まず、Credential Guardが無効の場合は、LSASSのメモリ上にNTLMハッシュが保存されているので、NTLMハッシュを入手でき、悪用することができてしまいます。それに対して、Credential Guardが有効な場合は、LSASSのメモリ上には暗号化されたNTLMハッシュしか保存されていないため、NTLMハッシュを入手することができず、悪用ができない仕組みになっています。
Pass-the-Challenge
Pass-the-Challengeの概要
Pass-the-Challengeとは、2022年12月27日にOliver Lyak(@ly4k_)氏によって公開された攻撃手法です。Credential Guardによって保護された資格情報からNTLMハッシュを回復することができます。
従来のCredential Guardのバイパス手法は、端末に侵入して細工を施した後にログオンしたユーザを攻撃対象とするため、標的となるユーザのログオンを待つ必要がありました。Pass-the-Challengeでは、端末に侵入した時点でログオンしていたユーザも攻撃対象となるので、侵入時点で悪用ができてしまうという特徴があります。
Pass-the-Challengeは、LSAIsoのNtlmlumCalculateNtResponseというメソッドを悪用して、NetNTLMv1応答値を取得します。NtlmlumCalculateNtResponseは、NetNTLMv1応答値を計算するためのメソッドです。Windowsの現在のバージョンでは、基本的にNetNTLMv1認証自体が無効化されているのですが、この認証方法を有効化することはもちろん可能です。互換性のためにNtlmlumCalculateNtResponseも存在しています。
具体的な流れは、上図に示した通りです。
まず、LSASSのメモリから攻撃に必要な情報3つ(Context Handle、Proxy Info、Encrypted blob)をダンプします。次に、LSAIsoを呼び出すことができるのはLSASSだけなので、LSAIsoを呼び出すためのSecurityPackage.dllという攻撃用のライブラリをLSASSにロードします。そして、SecurityPackage.dllを介してLSAIsoのNtlmlumCalculateNtResponseを呼び出して、 LSASSからダンプした必要情報とChallengeの値を与えることで、NetNTLMv1応答値を取得するという流れです。
Pass-the-Challengeの対策
Pass-the-Challengeは、現在のCredential Guardの仕様に基づく攻撃手法なので、CVEが付番されるような脆弱性ではなく、根本的な対策は難しいものと思われます。そのため、多層防御などによりネットワーク全体のセキュリティレベルを向上させることが重要です。
実際にやってみた
続いて、実際にやってみた結果を紹介します。
今回、検証するために用意した環境は以下の通りです。
- Active Directory環境
- ドメインコントローラ:1台
- Credential Guardを有効化したクライアント端末:1台
- ドメインアカウント:john.smith
- パスワード:zmh4A5HVXG3sCiwuESKh
- NTLMハッシュ:664169295f48fcb3d5e43cd77c49e566
Credential Guardの保護対象となるのはドメインアカウントなどのドメインに紐づく資格情報のみであるため、Active Directory環境を用意しました。その環境に、適当なパスワードを設定した適当なアカウントを用意しています。今回のゴールは、赤文字で示したNTLMハッシュの入手です。
まずはじめに、PypykatzというPython版Mimikatzを使って、必要な情報をダンプします。Pass-the-Challengeを発見したOliver Lyak氏が、Pass-the-Challenge用に改変したPypykatzを公開しているので、今回はそちらを利用しました。攻撃に必要なContext Handle、Proxy Info、Encrypted blobを取得できました。
次に、SecurityPackage.dllをインジェクトします。SecurityPackage.dllをインジェクトするためのPassTheChallenge.exeもOliver Lyak氏によって公開されているので、簡単に利用することが可能です。
最後に、PassTheChallenge.exeからライブラリを呼び出し、先ほど入手した3つの情報(Context Handle、Proxy Info、Encrypted blob)をLSAIsoに渡すことで、NetNTLMv1応答値を取得することができました。画像の赤枠で囲っているところが、入手したNetNTLMv1応答値です。これだけの手順で簡単に取得することができてしまいました。
NetNTLMv1応答値のままでは悪用が難しいため、NTLMハッシュに復元する必要があります。NetNTLMv1応答値の生成には、危殆化した暗号化方式であるDES暗号が使われているので、これを利用して復元していきます。
なお、Pass-the-Challengeの元記事では、crack.shというDES暗号のクラッキングに特化した研究目的のWebサービスを利用しています。このサービスを使うと数秒でNTLMハッシュに回復することが可能ですが、Webサービスに気軽に認証情報をアップロードするわけにもいかないので、今回は自力でのNTLMハッシュ回復を試みました。
NetNTLMv1応答値の生成方法は上図のようになっています。
ここで注目していただきたいのは、赤枠の部分です。DES暗号の3つ目のブロックは末尾5BytesがNULLバイトになっているため、実質的な鍵空間が2Bytesしかありません。そのため、簡単に特定することが可能です。残りの2つのブロックに関しては、DES暗号のクラッキングによって秘密鍵を特定していきます。
先程お話した通り、末尾2Bytesは簡単に特定ができます。今回は、Hashcatが公開しているユーティリティツールを利用しました。
続いて、DES暗号のクラッキングを行います。GeForce RTX 3090単体を使って、14日程度でクラックが完了しました。短期間でクラックが可能なことがわかります。
これで秘密鍵2つと末尾2Bytesが揃いました。
NTLMハッシュから秘密鍵を生成するアルゴリズムを参考にして、その逆の処理を行うプログラムを作成し、無事NTLMハッシュの回復に成功しました。
まとめ
Pass-the-Challengeを用いることで、侵害後の端末においてCredential Guardが有効であったとしても、比較的容易にNTLMハッシュを回復できてしまいました。
ですが、Credential Guardは攻撃の障壁として十分有効な機能のため、有効化することがもちろん推奨されています。しかし、こういった単一の機能や製品に頼るのではなく、多層防御によりネットワーク全体のセキュリティレベルを向上することも重要です。
以下の記事でより詳細に解説していますので、興味ある方はぜひお読みください。
参考文献
- “Pass-the-Challenge: Defeating Windows Defender Credential Guard”. (accessed January 25, 2023)
- “Protect derived domain credentials with Windows Defender Credential Guard”. (accessed January 25, 2023)
- “Net-LTLMv1認証の危険性”. (accessed January 25, 2023)
- “NTLMv1 to NTLM Reversing”. (accessed January 25, 2023)
▼LT3 ,4はこちら▼
Security․Tokyoでは運営メンバーを募集しています!(応募〆切:3/17(金)18:00)
オフライン開催の脆弱性勉強会「Security․Tokyo」では、セキュリティに関心のある/熱量のある運営メンバーを募集しています!
興味を持ってくださった方はぜひ下のリンクから応募をお願いします。
(当日、会場で応募いただいた方は、今回の追加募集が終わり次第ご連絡しますので、少々お待ちください)
Security․Tokyoを企画したFlatt Security代表取締役CEOの井手が、今回の勉強会企画開催に込めた思いについて書きました。こちらもぜひ併せて ご覧ください!