サードパーティソフトウェアからのクレデンシャル収集

Credential gathering techniques discussed here can open the door to cybercriminals as depicted in this conceptual image.

This post is also available in: English (英語)

概要

ソフトウェア界隈ではユーザビリティかセキュリティかの論争がたえません。多くのサードパーティプログラムは、ユーザーの認証情報(クレデンシャル)を保存することで生活を便利にし、時間を節約してくれます。ただしこの便利さはセキュリティの犠牲のうえに成り立っており、クレデンシャルの保存はパスワード窃取のリスクとなります。こうして集められたクレデンシャルは、その後サイバー攻撃に利用されるおそれがあるのです。

本稿はクレデンシャル窃取の危険性を解説するため、一般的なサードパーティソフトウェアにおけるクレデンシャルの収集シナリオをいくつか検証していきます。実際の攻撃シナリオにもとづいて、それらのサードパーティソフトウェアでパスワードがどのように保存されているのか、それらパスワードはどのように取得されているのか、パスワード取得の振る舞いをどのように監視するのかについて見ていきます。

Cortex XDRをご利用のお客様は、Windows、Linux、MacOSエージェントのCortex 3.4でリリースされたCredential Gathering Protectionモジュール(クレデンシャル収集防止モジュール)により、本稿で解説した攻撃からの保護を受けています。

関連するUnit 42のトピック Credential harvesting

目次

クレデンシャル窃取の危険性: 攻撃者のアクセス拡大方法
クレデンシャル収集の実際
WinSCPの場合
Gitの場合
RDCManの場合
OpenVPNの場合
Chromiumベースのブラウザの場合
Firefoxブラウザの場合
Emotet?
結論
IoC
追加リソース

クレデンシャル窃取の危険性: 攻撃者のアクセス拡大方法

クレデンシャル窃取は明らかな悪です。ただしクレデンシャル窃取がもたらす影響の大きさを強調することは重要です。

多くの人は、複数のプログラムに同じパスワードを使いまわす傾向があります。しかもほとんどパスワードを変更しません。変更する場合でも、見抜かれやすいパターンでやってしまいます。

ですから、あるソースからパスワードをひとつ入手できれば、攻撃者はそのパスワードをべつのリソースに使いまわしてみることができます。使いまわしてみる対象には、しっかり保護されたソースも含まれます。つまり、安全性の高いプログラムAがあるとして、それより安全性の低いプログラムBで、同じパスワードやパターンが使われていれば、結果的にプログラムAの安全性を低下させてしまうことになるのです。

このほか、あるユーザーがオペレーティングシステム用のパスワードをほかの安全でない場所で使っていることがわかった場合、その攻撃者にはまったく新たな可能性の地平が開けることになります。

たとえばあるユーザーXがWindowsアカウントのドメインとLinuxのFTPファイルサーバーに同じパスワードを使っているとしましょう。このシナリオでユーザーXは一般的なプログラムWinSCPを使い、ファイルサーバー内のファイルを管理しています。WinSCPはパスワードの保存を推奨していませんが、ユーザーXは毎週このファイルサーバーにアクセスするので、パスワードを保存することで時間を節約したがっています。

WinSCPのダイアログ。赤枠で Save Password (not recommended) オプションをハイライト表示している
図1. WinSCPはパスワード保存を推奨していない

本稿後半でデモを行いますが、ユーザーパスワードはWinSCPの保存場所から簡単に取得できます。XのPCに侵入できれば、Xのドメインアカウントのパスワードも入手できます。なぜならパスワードが安全な状態で保存されていないからです。しかもこのパスワードはファイルサーバーへの接続にも有効です。このファイルサーバーにはなにか機微情報を含むファイルがあって、いまや攻撃者はそれらにアクセスできる状態かもしれません。そこからさらにBloodHoundようなツールを使えば、組織内をどこまで侵害できそうかを推定することもできます。

クレデンシャル収集の実際

WinSCPの場合

WinSCPはメジャーなMicrosoft Windows用SFTP・FTPクライアントです。ローカルWindowsコンピュータとリモートサーバーとの間で、FTP・FTPS・SCP・SFTPなどでファイルをコピーするのに使います。

検証したバージョン:

5.19.6 (Build 12002 2022-02-22)

クレデンシャルの保存場所:

WinSCPは暗号化したユーザーパスワードをレジストリキーHKCU\software\martin prikryl\winscp 2\sessions\<session_name>以下のPasswordという値に保存します。

クレデンシャルの復元方法:

WinSCPはユーザーパスワードのビットに対して対称的な数学的演算を行います。パスワードの各バイトを受け取り、0xFF(11111111)との補数を計算した後、バイト0xA3 (10100011)とのXORを取ります。

この暗号化処理は、補数を求めた後1回XORを行うという内容になっています。そしてそのパスワードがレジストリ値Passwordに保存されます。この一連の演算は対称的なので、同じ演算をもう一回逆順に行えば元の値を得られます。

たとえばよく使われるパスワードの「Aa123456」を例に取ってみましょう。WinSCPはこのパスワードを「1D3D6D6E6F68696A」として保存します。

図2がこのパスワードの復号手順です。

WinSCPのパスワード復号手順。この表のように「0xA3」とのXORを取ってからその補数を求める。
図2. WinSCPに保存されているパスワードの復号

このパスワードはHostName、UserNameと一緒に保存されています。Passwordのレジストリ値からこれを取得するにはパスワード先頭のインデックスを見つけねばなりませんが、この計算はとても簡単です。WinSCPのバージョンにより、レジストリ値の1バイト目か3バイト目が、ユーザー名、ホスト名、パスワードを連結した長さになります。この長さを表すバイトの後ろに続くバイトが開始インデックスで、この値は2倍されます。長さと開始インデックスは同じ方法で暗号化されています。

UserNameとHostNameも別のレジストリ値に保存されているので、その長さと値もわかっています。Passwordレジストリの値をインデックス「開始インデックス + UserNameの長さ + HosNameの長さ」から「長さ」まで復号すればパスワードを得られます。

このスクリーンショットは、レジストリの値をインデックス「開始インデックス + UserNameの長さ + HosNameの長さ」から「長さ」まで復号すればパスワードを得られることを示しています。その結果がパスワードです。
図3. ユーザー名、ホスト名、パスワードが連結されている場所

利用実態

私たちはこれまでに図4のようなスクリプトが複数のお客様環境で実行されている様子を確認しました。

Unit 42が複数の顧客環境で観測した不審なPowerShellスクリプト。デコードされたスクリプトはWinSCPのパスワードの復号を試みるもの。
図4. コマンドがエンコードされている不審なPowerShell
  • -enc は「EncodedCommand」の略で、base-64でエンコードされた文字列がコマンドとして使用されることを意味しています。

デコードされたスクリプトからはWinSCPのパスワード復号の試みが確認されました。

クレデンシャル収集に使用されたPowerShellスクリプトをデコードしたもの。
図5. WinSCPのパスワードを抽出・復号するPowerShellスクリプト
この図の赤い矩形は、Cortex XDRのCredential Gathering Protectionモジュールが疑わしいレジストリの操作を特定した様子を表しています。
図6. Cortex XDRがWinSCPに保存されたパスワードの読み取りを防止したところ

Gitの場合

検証したバージョン:

2.35.1.windows.2

クレデンシャルの保存場所:

GitではパスワードとPAT(Personal Access Token)の両方を使えます。

ユーザーがGitのクレデンシャルを保存して時間を節約したければ、次のコマンドで行えます。

git config credential.helper 'store'

このコマンドを使うと、Gitはユーザーのクレデンシャルを平文で無期限にディスク上に保存します。

パスワードを含む可能性のあるファイル:

  • <userprofile>\.git-credentials
  • <userprofile>\.config\git\credentials

Gitでは、従来のパスワードの代わりに、PATをクレデンシャルとして使えます。このトークンはよりモジュール化されていて、それぞれに異なる権限と有効期限を持つアクセストークンをいくつでも作成できます。

ユーザーのアクションをさらに細かくモジュール化してそれぞれのアクションを特定のPATに関連付けた形で制御することは可能ですが、ユーザーのPATを持つ人は当該ユーザーがアクセスできるリポジトリをすべて閲覧できます。

これらのトークンは上に記載したのと同じファイルにも平文で保存されます。

クレデンシャルの復元方法:

これらのファイルを読める人は、ユーザー名、パスワード、トークン、関連Gitリポジトリを平文で見られます。

RDCManの場合

検証したバージョン:

2.83

リモートデスクトップ接続マネージャーのRDCManは、複数のリモートデスクトップ接続を管理します。自動チェックインシステムやデータセンターなど、定期的にマシンにアクセスする必要があるサーバーラボの管理に便利です。

クレデンシャルの保存場所:

ユーザーがRDCManを使うセッションのパスワードを保存する場合、デフォルトの設定ファイルは%localappdata%\Microsoft\Remote Desktop Connection Manager\RDCMan.settingsです。

このファイルは各RDP接続に関するメタデータ一般を含むXMLファイルです。

私たちは、このデータのなかにあるCredentialsProfilesというXMLタグに着目しました。

このタグの下にはCredentialsProfilesという別のタグがあって、その中にcredentialsProfileというXMLタグがあり、ここにPasswordというタグがあります。

RDCMan.settingsのcredentialsProfileというXMLタグにあるPasswordタグの内容を赤い矩形で強調表示しているところ。
図7 RDCMan.settingsのXMLタグを確認したところ

クレデンシャルの復元方法:

パスワードを取得するには、RDCManプログラムを利用するユーザーのコンテキストでコマンドを実行する必要があります。これはパスワードが DATA Protection API (DPAPI) を使って保存されているためです。DPAPIはCryptProtectData関数とCryptUnprotectData関数を使うことで、あらゆる種類のデータをそれぞれの関数で対称暗号化/復号できます。

つまり、パスワードを取得するにはCryptUnprotectData関数を呼び出さねばなりません。

通常であれば、データを復号できるのは、そのデータを暗号化したユーザーと同じログインクレデンシャルを持つユーザーだけです。

RDCManからのクレデンシャル収集は、ほかのソフトウェアで見てきた例とちがい、攻撃者側に「対象ユーザーのコンテキストでソフトウェアを実行する」という余計なステップが必要ですが、その結果はじゅうぶんその価値に見合うものです。これがうまくいけば、この特定のユーザーが接続するすべてのマシンのすべてのユーザー名とそのパスワードを取得できるからです。

攻撃者が対象ユーザーのコンテキストでコマンドを実行さえできれば、あとクレデンシャル収集のためにやるべきことは以下だけです。

  1. RDCMan.settingsファイルを開いてpasswordというXMLタグがあることを確認する
  2. タグ内の文字列をbase64でデコードする
  3. デコードしたパスワード文字列を引数に指定してCryptUnprotectData関数を呼び出す
  4. 戻り値をUTF-8(ないしそのほかの適切なフォーマット)でデコードする
  5. 不要なNull文字を削除する

上の例の場合、ファイルに保存されているパスワードは次のとおりです。

AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAA8/nnW5aFNUi0AKiTG4y9UQAAAAACAAAAAAAQZgAAAAEAACAAAADIjLLw0X4z9RDdWgPpqabLU7hTcJ1HVlFklpzX3eA14QAAAAAOgAAAAAIAACAAAAB01OvDCNCjaEhrq8J8hRm/SKycef7nR52ZkqcPLJqMsCAAAACg2htaeRsutDziS3FISeEAg3DsBpGxBGpPeWlUSVnXOkAAAAB5Tei9g5KWcVIhOKQ2cXxr5ONUOHMEEH5h3Lmp12mPlWaaZ6y8dGIVz8WnNKr4e73dhqNU8NyzI7RZBamS6DG6

そして復号されたパスワードはAa123456となります。

赤い矩形はパスワード復号結果を示す。RDCManを標的とするクレデンシャル収集が完了した状態。
図8 RDCManからパスワードを復元

OpenVPNの場合

検証したバージョン:

2.5.029

OpenVPNはVPN(仮想プライベートネットワーク)ソフトウェアで、ルーティング構成ないしブリッジ構成や、リモートアクセス設備内で、ポイント・ツー・ポイントまたはサイト・ツー・サイト接続を安全に行う技術を実装しています。

クレデンシャルの保存場所:

OpenVPN はユーザーのパスワードをレジストリキーHKCU\software\openvpn-gui\configs\<session_name>auth-dataという値に保存します。

クレデンシャルの復元方法:

OpenVPNもDPAPIのしくみを使っています。エントロピー用のパラメータ(NULLに設定可能)がオプションで使えます。

暗号化の段階でオプションのエントロピー用DATA_BLOB構造体を使った場合は、同じDATA_BLOB構造体を復号段階でも使う必要があります。

OpenVPNの場合、このエントロピーはentropyというレジストリ値に保存されます。また、entropyレジストリの値はHKCU\software\openvpn-gui\configs\<session_name>にも格納されています。

したがって、CryptUnprotectData関数をauth-dataからのパスワードとentropy(entropyレジストリ値から)を引数にして呼び出すと、セッションパスワードを得られます。

entropyというレジストリ値には00が1バイト余分に含まれているので、それは削除する必要があります。

図上: OpenVPNのパスワード復元用PowerShellスクリプト。下はauth-dataとentropyのレジストリ値がreg.exeによって表示されています。
図9 図上: OpenVPNのパスワード復元用PowerShellスクリプト。図下: auth-dataとentropyのレジストリ値をreg.exe(PoC)経由で表示しているところ。

Chromiumベースのブラウザの場合

検証したバージョン:

  • Google Chrome: 103.0.5060.53 (Official Build) (64-bit)
  • Microsoft Edge: 103.0.1264.37 (Official Build) (64-bit)
  • Opera: 88.0.4412.53

Chromiumプロジェクトは、Google Chromeブラウザを支えるオープンソースプロジェクト、Chromiumを含んでいます。

一般的傾向として、多くの人はインターネットを閲覧するさい、パスワードを保存しています。

このスクリーンショットは、Google Chromeの設定で保存されたパスワードを確認する際に表示される画面を再編集したものです。
図10 Google Chrome Version 102.0.5005.115 (Official Build) (64-bit)で保存されたパスワード

クレデンシャルの保存場所:

Microsoft Edge、Opera、Google ChromeなどのChromiumベースのブラウザを使用する場合、パスワードは通常 login dataと呼ばれるSQLiteデータベースファイル内に暗号化した状態で配置されます。

各プロファイルは、パスワードデータベース(login dataファイル)を持っています。

パスワードの暗号化に使用される鍵は、親フォルダ内のlocal stateというJSONファイルに配置されています。

例:

login data の場所:

  • Google Chrome: %localappdata%\google\chrome\user data\<PROFILE>\login data
  • Microsoft Edge: %localappdata%\microsoft\edge\user data\<PROFILE>\login data
  • Opera: %appdata%\opera software\opera stable\<PROFILE>\login data

local stateの場所:

  • Google Chrome: %localappdata%\google\chrome\user data\local state
  • Microsoft Edge: %localappdata%\microsoft\edge\user data\local state
  • Opera: %appdata%\opera software\opera stable\local state

クレデンシャルの復元方法:

login dataデータベース内にある各パスワードはAES-GCMモード(Advanced Encryption Standard with GCM)で暗号化されています。AES-GCMは共通鍵暗号方式なので暗号化にも復号にも同じ鍵が使えます。AESのアルゴリズムは、128ビットブロックごとに異なる鍵を使用し、その鍵は前のブロックの計算にもとづいて作られます。最初のブロックでは、初期化ベクトル(IV)を使用するオプションがあります。

Chromiumベースのブラウザが保存しているパスワードを復号するには以下が必要です。

  1. 暗号化されたパスワード
  2. 初期化ベクトル
  3. AES鍵

それぞれどのように取り出すかを見てみましょう。

A. 暗号化されたパスワード

login dataデータベースからエクスポート可能です。暗号化されたパスワードはpassword_value列の15番目の位置の文字から最後の文字から16文字を引いた位置までを取り出します: [15:-16]

B. 初期化ベクトル

同じくpassword_valueフィールド列の3番目の位置の文字から15番目の位置の文字までに格納されています: [3:15]

C. AES鍵

local state JSONファイルに書き込まれています。書き込まれている場所はos_cryptキーと encrypted_keyキーの下で、これをbase64でデコードします。

ChromiumベースのブラウザはDPAPIのしくみを使ってAES鍵を保存しているので、この鍵を取得するにはbase64からデコードして対象ユーザーのコンテキストでCryptUnprotectData関数を使います。

Google Chromeの例:

Google Chromeのlocal state JSONファイルに保存されたパスワードの例。os_crypt、encrypted_key、password_managerが確認できる。
図11 Google Chromeのlocal state JSONファイルに保存されたパスワード

これは先頭にDPAPIという5文字のプレフィックスを付けた状態で保存されています。

デコードされたパスワードを表す図。先頭に赤くハイライトされているのが5文字のプレフィックス。DPAPI
図12. Google Chromeに保存されているパスワードをデコードしたもの

当該ユーザーのコンテキストでの実行が可能である場合に、攻撃者によるユーザークレデンシャル収集の完了までに必要なステップは次のとおりです。

  1. login dataファイル、local stateファイルの両方をコピーする
  2. local state JSONファイルからAES-GCM鍵を得る
  3. (base64で)デコードし、(CryptUnprotectData関数で)復号し、鍵からパディングを除去する
  4. login dataデータベースの各パスワードを復号されたAES-GCM鍵で復号する
Chromeに保存されたパスワードを復元する概念実証 (PoC)、python_Chrome_pass.pyを実行した結果のスクリーンショット。機微情報は編集済み。
図13. Chromeに保存されたパスワードを復元するPoC

PythonによるChromeパスワード抽出方法についてはこちらの記事をご覧ください。

利用実態

私たちは、regsvr32.exeを使って、以下のコマンドラインでexcel.exeからDLLを動作させる例を確認しています。

C:\windows\system32\regsvr32.exe
C:\users\<username>\appdata\local\uolegxnwf\kgnkudbadmpogg.dll

(kgnkudbadmpogg.dllのSHA256: 6599FEE8C7ADF30A00889A7070600F472F8CEAD8EA4DD1A85E724ED15F2AED0F)

一連のイベント後、最終ペイロードはMicrosoft Edgeのクレデンシャルファイルにアクセスしようとしていました。

  • login dataファイル(SQLiteデータベースファイル):
    C:\users\<username>\appdata\local\microsoft\edge\user data\default\login data
  • local stateファイル(暗号鍵を含む)
    C:\users\<username>\appdata\local\microsoft\edge\user data\local state
赤い矩形はCredential Gathering Protectionモジュールと観測された暗号鍵を含むファイルパスをハイライトしています。
図14 Cortex XDRがMicrosoft Edgeブラウザに保存されたパスワードを読み取ろうとする試みを検出したところ

Firefoxブラウザの場合

検証したバージョン:

Firefox Version 101.0.1 (64-bit)

これまでパスワードを保存するという振る舞いパターンに関して述べてきたことは、Mozilla Firefoxなどのブラウザを使う場合にも当てはまります。

Mozilla Firefoxに保存されたパスワードのスクリーンショット(鍵情報は編集済み)。
図15. Firefox Version 101.0.1 (64bit)に保存されているパスワード

クレデンシャルの保存場所:

Mozilla Firefoxも、Chromiumベースのブラウザ同様、プロファイルごとにパスワードファイルを持てます。

このファイルをlogins.jsonといい、これは%appdata%\mozilla\firefox\profiles\<PROFILE>\logins.jsonに配置されています。

ユーザー名もパスワードも暗号化された状態で保存されています。

このスクリーンショットには、encryptedUsername、encryptedPasswordなどのログインデータが表示されています。
図16. Firefoxのlogins.jsonファイルに保存されているパスワード

クレデンシャルの復元方法:

logins.jsonファイル内の各ユーザー名とパスワードはPKCS #11という暗号化規格で暗号化されています。FirefoxではNSSライブラリ(nss3.dll)を開発してこの規格をブラウザに採用しています。

NSSはそのバージョンによって、key3.dbkey4.dbというファイルに秘密鍵を保存します。

対象ユーザーのパスワードを得るには、攻撃者はこれらのファイルのいずれか1つとlogins.jsonファイルにアクセスする必要があります。

つまり、攻撃者が対象ユーザーと同一のマシン上での実行権限を得られるなら、次のステップでパスワードを窃取可能です。

  1. 攻撃者はlogins.jsonファイルをコピーする
  2. NSSライブラリ(nss3.dll)をロードする
  3. コピーしたlogins.jsonからencryptedUsernameencryptedPasswordを(base64で)デコードする
  4. 各入力をSecItemに格納する。このオブジェクトは後でNSS全体でバイナリデータのブロックをやり取りするのに使われる
  5. 出力用のSecItemオブジェクトを作成する
  6. nss3.dllのPK11復号関数を使ってencryptedUsernameencryptedPasswordの入力オブジェクトをそれぞれ復号し、先程作った出力用SecItemオブジェクトにデータを格納する

Chromiumベースのブラウザとはちがい、対象ユーザーのパスワードを取得するさい、攻撃者が当該ユーザーのコンテキストで実行している必要はありません。対象ユーザーのファイルシステムのプロファイルにアクセスする権限を持っているなら、どのユーザーでも利用可能です。

赤い矩形は、ユーザーSがユーザーDのクレデンシャルを収集している様子をハイライト表示している。
図17 ユーザーSが、Firefoxのプロファイル2に保存されたユーザーDのパスワードを収集したところ(PoC)

利用実態

私たちは、以下のようなスクリプトが実行されていた様子を観測しました。

Mozilla Firefox からのクレデンシャルを収集しようとする不審なPowerShellスクリプト。難読化されている。
図18. 難読化された不審なPowerShellスクリプト

難読化を解除した状態:

難読化を解除したPowerShellスクリプト。図には一連のリンクが表示されている。
図19 難読化を解除した後のPowerShellスクリプト

スクリプトの内容:

  1. %localappdata%\ujXgADというフォルダを作成する
  2. $Linksの各リンクに対し、Invoke-WebRequestの作成を試み、DLLを1つダウンロードして先に作成したフォルダにrRXqwGvGNR.wTjという名前で保存する
  3. エクスプロイトが成功したらbreakする

次に、エンドポイントにDLLが1つ作成され、以下のコマンドラインを指定してregsvr32.exeが使われていました。

C:\WINDOWS\system32\regsvr32.exe
C:\Users\<USERNAME>\AppData\Local\Temp\..\ujXgAD\rRXqwGvGNR.wTj

このパスは回避技術が使われている点が要注目です。\..\を使ってLocalフォルダに戻ることで攻撃者は直接のアクセスを避けています。

regsvr32.exeの使用後:

A. DLLはランダムな名前のフォルダに、ランダムな名前で、拡張子をDLLにした自分自身をコピーする
C:\Users\<USERNAME>\AppData\Local\<random_folder_name>\<random_dll_name>.dll

B. DLLが探索コマンドを複数実行する

    1. systeminfo: マシンの情報を一覧表示
    2. ipconfig /all: マシンのすべてのネットワークインターフェイスをリストアップ
    3. nltest.exe /dclist: ドメイン内の全ドメインコントローラを一覧表示

C. DLLがcertutil.exeをベースにした2つのファイルをランダムな名前で作成して実行する

  1. ファイルの1つには新たなランダム名が付けられるがMicrosoftの署名がついたままになっている
  2. もう1つはcertutilを改ざんしたもので、名前は元のままだが機能は異なり署名もついていない

D. 上記ステップCは2回行われる

署名のないファイルのSHA256:
A88C344F3F80F8A3EA2E9BA0687FEBCEE2A730FD9AC037D54C4FD21C0AB91039

CertutilのSHA256: このファイルじたいは良性であることに注意
D252235AA420B91C38BFEEC4F1C3F3434BC853D04635453648B26B2947352889

この署名のないcertutil.exeは、ChromiumベースのブラウザとFirefoxベースのブラウザの両方のパスワードファイルにアクセスしようとします。

図19のリンクを確認すると2つのリンクだけが機能していました。

クレデンシャル収集攻撃中にダウンロードされたDLLのlw1JF63zARLUV8UwpwGnWpgg.dllとRwuuPYoVei7FkJB.dll
図20 ダウンロード可能だったDLL
  • 最初にダウンロードされたDLL:
    hxxps://www[.]yell[.]ge/nav_logo/AEnTP/
    ダウンロードされたファイルの名前: RwuuPYoVei7FkJB.dll
    (SHA256: A1D513E4A5C83895E5769C994C4D319959EF5AE3F679CE6C0C5211B5BECA7695)
  • 2番目にダウンロードされたDLL:
    hxxps://yakosurf[.]com/wp-includes/S/
    ダウンロードしたファイル名:lw1JF63zARLUV8UwpwGnWpgg.dll
    (SHA256:1B8638333751EFCB6B5332C801C11DF0DE3D7077C6ACEA1D663C0302519D7172)

どちらのファイルも実は同じDLLで、SHA256ハッシュを変えるささいな変更があるのみです。

このサンプルを調べたところ、最初のDLLはEmotetマルウェアファミリに属するものであることが確認されました。

赤い矩形はCredential Gathering Protectionモジュールが鍵ファイルのパスを識別した様子を表しています。
図21. Cortex XDRがFirefoxブラウザに保存されたパスワードを読み取ろうとする試みを防止したところ

Cortex XDRはこの攻撃を発生と同時に防止するので攻撃の次の段階が実行されていません。このマルウェアは、最初にFirefox、次にMicrosoft Edge、最後にGoogle Chromeの順でパスワードを読み取ろうとします。

デモのためにレポートモードにしたCortex XDRでの例を示します。この例からは、Credential Gathering Protectionモジュールが、Chromiumベースのブラウザに保存されたパスワードを読み取ろうとする試みについても検知していることがわかります。

赤い矩形はCortex XDRのCredential Gathering Protectionモジュールが鍵ファイルのパスを識別した様子を表しています。
図22. Cortex XDRがMicrosoft Edgeブラウザに保存されたパスワードを読み取ろうとする試みを検出したところ
赤い矩形はCredential Gathering Protectionモジュールが鍵ファイルのパスを識別した様子を表しています。
図23. Cortex XDRがGoogle Chromeブラウザに保存されたパスワードを読み取ろうとする試みを検出したところ

Emotet?

Emotetが関与した事例を2つ確認したので、このマルウェアファミリとそのサードパーティクレデンシャルの収集方法についてもう少し詳しく調べてみました。マルウェアはコードをすべて自前で実装する必要がない場合もあります。NirsoftのWebBrowserPassViewツールのような既存ツールをラップして、Webブラウザが保存しているパスワードを明らかにするだけでよいのです。

WebBrowserPassView.exeでユーザー名やパスワード、またそのそれぞれを保存しているファイルパスを表示したところ。取得された各パスワードがWebブラウザに表示されているところ(機微情報は編集済み)。
図24. WebBrowserPassView.exeでユーザー名やパスワード、またそのそれぞれを保存しているファイルパスを表示したところ。

Chromiumベースブラウザのlogin dataファイルとFirefoxブラウザのlogins.jsonファイルが確認できます。

赤い矩形はCredential Gathering Protectionモジュールが鍵ファイルのパスを識別した様子を表しています。
図25 Cortex XDRがWebBrowserPassView.exeによるWebブラウザの保存パスワード読み取り試行を防止したところ

結論

サードパーティのソフトウェアのなかには、クレデンシャルの保存方法が、思ったより安全ではないものもあることがわかりました。これらのプログラムのほとんどは、ユーザーのクレデンシャルをファイルやレジストリ値としてローカルディスクに保存しています。この事実が攻撃者の探すセキュリティのアキレス腱となり、組織を攻撃するためのアクセス権を与えてしまうことになります。

Cortex XDRをご利用中のパロアルトネットワークスのお客様は、新たなCredential Gathering Protectionモジュールにより、上記で解説したシナリオにくわえ、本稿で言及しなかったほかのクレデンシャル収集技術に対する保護も受けています。またLocal Analysis、BTP(Behavioral Threat Protection)、BIOC、Analytics BIOCsルールなど、追加の保護レイヤーも利用できます。

WildFireをお使いのパロアルトネットワークスのお客様は、こうしたクレデンシャルの収集を試行するツールからの保護を受けています。

NirsoftのツールはWildFireではグレーウェアとしてマークされ、XDRエージェントによってブロックされます。

IoC

以下のレジストリ値への不正アクセス
  • HKCU\software\martin prikryl\winscp 2\sessions\<session_name>\Password
  • HKCU\software\openvpn-gui\configs\<session_name>\auth-data
  • HKCU\software\openvpn-gui\configs\<session_name>\entropy
以下のファイルへの不正アクセス
  • <userprofile>\.git-credentials
  • <userprofile>\.config\git\credentials
  • %localappdata%\Microsoft\Remote Desktop Connection Manager\RDCMan.settings
  • %localappdata%\google\chrome\user data\<PROFILE>\login data
  • %localappdata%\microsoft\edge\user data\<PROFILE>\login data
  • %appdata%\opera software\opera stable\<PROFILE>\login data
  • %localappdata%\google\chrome\user data\local state
  • %localappdata%\microsoft\edge\user data\local state
  • %appdata%\opera software\opera stable\local state
  • %appdata%\mozilla\firefox\profiles\<PROFILE>\logins.json
  • %appdata%\mozilla\firefox\profiles\<PROFILE>\key<3/4>.json
悪意のあるハッシュ
6599FEE8C7ADF30A00889A7070600F472F8CEAD8EA4DD1A85E724ED15F2AED0F

A88C344F3F80F8A3EA2E9BA0687FEBCEE2A730FD9AC037D54C4FD21C0AB91039

A1D513E4A5C83895E5769C994C4D319959EF5AE3F679CE6C0C5211B5BECA7695

1B8638333751EFCB6B5332C801C11DF0DE3D7077C6ACEA1D663C0302519D7172

追加リソース