ナビゲーションをスキップする
BlackBerry ThreatVector ブログ

SunSeed マルウェア詳細分析:ウクライナ難民支援の取り組みをフィッシングの標的に

本ブログ記事は、2022年3月24日に米国で公開されたBlackBerryのブログ記事の抄訳版です。原文はこちらからご覧頂けます。

 

新たに発見されたマルウェアがウクライナ難民を支援するヨーロッパ政府職員を襲う

あらゆる人々の関心がウクライナに向けられています。、この重大な問題が懸念されていた通り、攻撃者によりフィッシング詐欺の対象として使用されていることが明らかになりました。3月初旬のニュース記事によると、ウクライナからの難民の支援を担当するヨーロッパ政府の職員が、ベラルーシの利益のために活動していることが以前に特定されている、Ghostwriter と呼ばれる(TA445 または UNC1151 としても知られている)脅威グループによる標的となった可能性が高いことがわかっています。

悪意のある Excel® ドキュメントを含む UKR[.]net のアドレスから発信されたメールが、あるヨーロッパ政府機関に送信されたことが、調査担当者により確認されています。UKR.net はウクライナで人気のある ISP およびメールプロバイダであり、主に個人用のメールアカウントの作成に使用されています。このメールの件名は、「IN ACCORDANCE WITH THE DECISION OF THE EMERGENCY MEETING OF THE SECURITY COUNCIL OF UKRAINE DATED 24.02.2022.」(2022.02.24 付けのウクライナ安全保障会議の緊急会合での決定に基づき)でした。

また調査担当者は、送信者のメールアカウントがウクライナ軍の一員のものであり、ウクライナの兵士や市民を標的とした 事前のフィッシング攻撃で漏洩した可能性があると推測しています。

 

技術的分析:「Lua の世界」へ

感染経路

被害者が悪意のある Excel ドキュメントを開くと、図 1 に示すように、「コンテンツの有効化」を求める偽のスプラッシュスクリーンが表示されます。

図 1 - 悪意のあるコンテンツを有効にするようにユーザーを促す偽のスプラッシュスクリーン

この偽のスプラッシュスクリーンは画像で構成されていますが、被害者が画像を操作して見せ掛けであることを判断できないように、この Excel シートは保護されています。被害者がコンテンツを有効にすると、以下のマクロが実行されます。

図 2 - SunSeed をインストールする悪意のあるマクロ

 

インストール

このマクロは Windows® インストーラオブジェクトを作成し、UILevel を 2 に設定します。MSDN ドキュメントからの以下のスニペットが示すように、これは「サイレントインストール」のための設定です。

msiUILevelNone

2

サイレントインストール


最後に、マクロは InstallProduct メソッドを呼び出し、URL を渡します。これにより、Windows は指定された URL から MSI インストーラをフェッチし、インストールします。フェッチされたインストーラを調べたところ、以下の文字列が見つかりました。

図 3 - インストーラが Windows XML ツールセットを使用して作成されたことを示す文字列

この文字列は、Windows Installer XML(WiX)ツールセットを使用して作成されたことを示しています。WiX は、ユーザーが Windows 用のインストーラを作成することを支援するために Microsoft 社により開発されたオープンソースのツールセットです。WiX インストールは、ツールセットによりコンパイルされるインストールを記述する XML を含む WXS ファイルに基づいています。WiX ツールセットを使用することにより、このプロセスを反転させ、インストーラを記述する XML を生成することができます。これは、WiX と一緒に提供されている Dark ツールを使用して行えます。

“dark.exe {MSI ファイルの名前} -x {抽出先のパス}”

このコマンドにより、インストーラ内にパッケージ化されているファイルも抽出されます。これらのファイルについては、この後詳しく解説します。

生成された WXS XML を見ると、図 4 に示すように、目的は偽の「Software Protection Service」の登録であることがわかります。

図 4 - 悪意のあるインストーラを記述する WXS XML の抜粋

図 5 に示すように、このコードは Windows のスタートアップフォルダを使用してブートします。

図 5 - ブートメカニズムを示す WXS XML スニペット

インストーラには以下のファイルが含まれます。

ファイル名

目的

Software Protection Service.lnk

ブート時に起動するように、Windows のスタートアップフォルダに入れられるショートカット

http.lua

HTTP/1.1 クライアントサポート(LuaSocket ライブラリの一部)

ltn12.lua

LuaSocket ライブラリの一部

lua5.1.dll

Lua ランタイム

luacom.dll

Windows の COM オブジェクトとやり取りするための Lua アドオン

mime.dll

mime.lua

MIME サポート(LuaSocket ライブラリの一部)

print.lua

悪意のある Lua スクリプト(SunSeed)

socket.dll

socket.lua

LuaSocket ライブラリコア

sppsvc.exe

スタンドアロン Lua インタープリタ – LuaBinaries 5.1.5 Windows x86 リリースから直接

tp.lua

統合 SMTP/FTP サブシステム(LuaSocket ライブラリの一部)

url.lua

URI 解析サポート(LuaSocket ライブラリの一部)

 

これらのファイルの大半は、軽量のオープンソースプログラミング言語である Lua の必要最小限のインストールを形成しています。これは、悪意のあるコアスクリプトである「print.lua」を実行するために必要です。print.lua ファイルは、このマルウェアの特に興味深い点の 1 つです。

 

Print.lua

print.lua スクリプトの冒頭には、いくつかの config 解析コードが含まれます。

図 6 - config 文字列の解析に使用される関数

この後、config の宣言が続きます。

図 7 - 上記の関数を使用したグローバル config 変数の宣言

アナリストによる解析を困難にするために、以下の関数もスクリプトの冒頭で名前が変更されます。

図 8 - 難読化目的での Lua 関数の名前の変更

config 解析スクリプトを簡素化すると、以下のスクリプトが得られます。

図 9 - 簡素化された config 解析関数

圧縮アルゴリズムに詳しい読者なら、これが LZ 解凍の実装であることに気づくでしょう。この解凍関数は、1 つの文字を読み取り、これを base 36 から変換することにより、config からトークンを取得します。最初の値は、実際のトークンに対して何文字取得すべきかを示します。実際のトークンも base 36 からデコードされます。

簡単な例を示します。

図 10 - config データからの LZ トークンの取得

このプロセスが繰り返され、config が解凍されます。

図 11 - 解凍されたマルウェアの config

残念ながらこれではまだ意味がわからないため、目的を明確にするにはさらに作業が必要です。

Lua スクリプトでは、この後さらに多くの関数が宣言されていますが、スクリプトの最後に最終的な呼び出しがあります。

図 12 - Lua スクリプトの最後で行われる呼び出し

「E」がこのコードの主要な関数です。スクリプトの前の段階で宣言され、図 8 に示した「C」は、Lua 環境変数 _ENV を返す関数です。ここからは、「F」への呼び出しを見ていきます。F は元々組み込みの構成を解凍する関数でしたが、図 13 に示すように、後から再宣言されています。

図 13 - F の再宣言されたバージョン

さらに調べていくと、この関数は前に解凍した config を解析することがわかりました。config からデータを取り出す、ここの関数「o」と「d」は、それぞれ 4 バイトと 1 バイトの値を取得し、各バイトを 0x73 と XOR します。詳細は省きますが、config 全体を XOR デコードすると、以下が得られます。

図 14 - デコードされた config

これで Lua コードの目的が見えてきます。

 

さらに深く Lua の世界へ

関数 F に話を戻すと、3 つの異なる「for」ループがあり、各ループにより config のセグメントがデコードされます。最初のループではループカウンタは 0 であるため、何も実行されませんが、2 つ目のループでは変数のテーブルが解析されます。2 つ目のループについて詳しく見ていく前に、まず解析された config データが挿入される変数「a」の宣言を確認する必要があります。

図 15 - config 変数「a」の宣言

ここで「f」は、最初はすべて 0 として宣言されている 47 個の項目を含むテーブルです。次に、2 つ目の「for」ループを含む、関数 F の抜粋を見てみましょう。

図 16 - 変数テーブルをデコードする関数 F の抜粋

この 2 つ目の「for」ループ内では、各反復は、どの「if-else」コードブロックに入るべきかを決定するために使用されるローカル変数「e」を宣言します。関数 d は config から 1 バイトを取得し、整数として解析します。この値は、変数のデータ型とその解析方法に対応しています。3 つのデータ型は以下のとおりです。

0x01 = ? (Unused)
0x02 = String
0x03 = Integer

ただしこのスクリプトでは、データ型 0x02 と 0x03 のみを使用しています。最も一般的な変数の型は文字列型(0x02)であり、関数「s」を呼び出します。これにより、文字列の長さを示す 4 バイトの整数が読み取られ、この長さ値を使用して実際の文字列が読み取られます。ループに入る前に、関数 o が呼び出されます。この関数は、ループに必要な反復回数を決定するために使用される 4 バイトの整数を読み取ります。図 17 では、このプロセスを解説しています。

図 17 - config 解析プロセスの解説

上の図に示すように、最初の 5 バイトは最初のループカウンタ(0)と 1 バイトの整数(同じく 0)として取得され、a[4] に格納されます。次に、2 つ目のループカウンタが取得されます(0x18 = 24)。これは、config の変数セクションに 24 個の値が含まれることを示しています。

続いて、ループによりこれらの値の解析が開始されます。最初の変数は文字列型(0x02)であるため、まず長さがデコードされ(0x06 = 6)、次に文字列自体が読み取られます(「serial」)。次以降の変数についても同じ手順を実行すると、「string」、「gsub」というように文字列が得られます。

実際、config 全体で見つかった整数型(0x03)の変数は 1 つだけです。デコードの結果、この整数の値は 3 になりました。変数テーブルに格納されている最後の値は、文字列「collectgarbage」です。図 17 では、黒いカーソルが変数テーブルの終わりを示しています。

SunSeed が面白くなってくるのは、3 つ目のループ、つまり config の最後のセクションです。変数テーブルの後にある、最後のループカウンタは 0x2f = 47 です。最初に 47 個の 0 を含むテーブルが宣言されていたのはこのためです。これは、config のこの最後のセクションからの 47 個のデコードされた値を格納するためのものです。config のこのセクションは、2 つの 4 バイト値からデコードされる 47 個の「フレーム」から構成されます。

 

マシンへのステップイン

驚いたことに、SunSeed の作成者は、図 12 に示した前述の最後の関数 E で、疑似仮想マシン(VM)を作成しているようです。詳しく調べたところ、高度に難読化された print.lua は、「Prometheus」と呼ばれるオープンソースの Lua 難読化ツールを使用して作成されているようです。(以前にブログで紹介した、同じ名前のトラフィックディレクションシステム(TDS) と混同しないでください。)

Prometheus 難読化ツールには、Lua スクリプトをバイトコードに変換し、処理するための VM を作成する「VMify」ステップと、すべての変数をスクリプトの冒頭のテーブルに挿入する「ConstantArray」ステップの両方があります。どちらも聞き覚えのある内容ではないですか。いずれにしても、この仮想マシンは変数テーブルと一時的な「レジスタ」のセットを使用して、前述の 47 個のフレームを取得し、SunSeed のコア機能を実行します。

この VM は、異なるケースに対する switch 文と同じ機能を実行する「if-else」文の複雑なセットを含む、大きなループとして構築されています。この場合、各ケースは 1 つの命令と考えることができます。この VM を詳しく調べると、フレームデータがどのように使用されているかを理解することができます。最初の 10 個のフレームは以下のとおりです。

 

インデックス

フレーム

1

2

3

5

1

22

0

2

4118

2

0

0

0

3

3

0

1

4

8192

4

0

2

5

10240

5

0

1

2

2

6

0

1

1

6

7

0

2

7

14336

8

0

1

2

2

9

0

1

1

8

10

0

1

1

9


ループの始まりで最初のフレームが取得され、最初の項目(22)が、移動先の「if」文ブロックを識別するために使用されます。この VM「命令」を図 18 に示します。

図 18 – 「アクション」22 に対する if-else コードブロック

コンテキスト:

n = config の 2 つ目のセクションからデコードされた変数テーブル
i = Lua 環境変数 _ENV
o = 一時的なレジスタストレージ
l = 現在のフレーム

いくつかのローカル変数の宣言の後、以下の行があります。

o[l[2]] = i[n[l[3]]]

ここでは、フレームインデックス 3(l[3] = 2)が変数テーブルへのルックアップ(n[2] = ”string”)として使用されており、これが _ENV 変数に対するインデックス(i)として使用されています。この値は、フレームインデックス 2(l[2] = 0)を使用して、レジスタテーブル(o)に格納されています。これを簡素化すると以下のようになります。

o[0] = _ENV[”string”]

このコードは、Lua のコア文字列操作関数への参照を含む、Lua 環境からの文字列関数テーブルを読み込みます。続く 2 行は以下のとおりです。

e = e + 1
l = c[e]

これらのステップは単に次のフレームに進むためのものです。この手順で、最初の 10 個のフレームが以下のように簡素化されます。

o[0] = _ENV[“string”]
o[0] = o[0][“gsub”]
o[1] = _ENV[“require”]
o[2] = “luacom”
s = o[1](“luacom”)
o[1] = s[1]
o[1] = o[1][“CreateObject”]
o[2] = “Scripting.FileSystemObject”
s = o[1](“Scripting.FileSystemObject”)
o[1] = s[1]
o[1] = o[1][“Drives”]
o[2] = o[1]
o[1] = o[1][“Item”]

多少のリファクタリングにより、これは以下のようになります。

gsub_func = _ENV[“string”][“gsub”]
require(‘luacom’)
drives_item = luacom.CreateObject(“Scripting.FileSystemObject”)[“Drives”][“Item”]

変数テーブルとフレーム内の情報を使用して、VM は Lua コードを動的に構築して実行します。これを難読化ツールに組み込むことは至難の業であったはずです。

この動的な構築プロセスでは、完全に難読化するか隠すことができない、スクリプトを読んだ調査担当者が簡単に識別できるような Lua 関数への直接呼び出しは避けています。たとえば、前述の図 8 に示したように、コードを難読化するために、一部の Lua 関数の名前が変更されています。しかし、簡単な検索置換操作により、関数呼び出しをコードに復元することができます。図 9 の config 解析コードはこのようにして簡素化したものです。

フレームを進んでいくと、最終的な Lua プログラムは(多少の力仕事により)以下の Lua コードまで簡素化することができます。

図 19 - 簡素化された Lua スクリプト、機能的には SunSeed と同じ

SunSeed はコマンドアンドコントロール(C2)(84[.]32.188[.]96)から送信される、実行すべき追加の Lua スクリプトの有無をチェックしながら、ループ内で待機しています。残念ながら、私たちの調査中には C2 からの追加のスクリプトは見られませんでした。

注目すべき重要な点が、トロイの木馬化されたインストーラにより、コアスクリプトには不要な余分なモジュール「tp.lua」がインストールされることです。これは、このモジュールが今後の Lua スクリプトに必要であることを示しています。tp.lua は、SMTP と FTP をサポートする Lua ライブラリです。つまり、C2 から送信される今後のスクリプトはメールやファイル操作に関連している可能性が高いということです。

 

まとめ

機能的な観点から見ると、SunSeed はかなり基本的なマルウェアですが、このマルウェアが難読化されている方法は決して単純ではありません。一般的に、コンパイルされたバイナリよりも、スクリプトの目的を隠すことははるかに困難です。マシンコードと異なり、スクリプトは読むことを意図したものであるためです。しかし、ここで確認した難読化は強力でした。

主として大人気のゲーム Roblox で使用されていることから、ここ数年で Lua の人気は高まっています。このような注目を集めるシナリオでの採用は、利用可能なオープンソースの Lua ツールや情報の増加と相まって、マルウェアの世界でも Lua の利用が増えつつあることを示している可能性があります。

数百万人もの人々がウクライナから避難している中、攻撃者は、このような人々の安全確保を支援する組織に打撃を与えるための新たな方法を模索しています。この状況は今後も展開していくため、防御担当者が SunSeed などの悪質な脅威により適切に対処できるように、BlackBerry は新たな情報を入手し次第、共有していきます。

 

侵入の痕跡 (IOC):

84[.]32.188[.[96 - SunSeed C2 -  

84[.]32.188[.]141 - Hosting Trojanised MSI

31d765deae26fb5cb506635754c700c57f9bd0fc643a622dc0911c42bf93d18f – Trojanised MSI

1561ece482c78a2d587b66c8eaf211e806ff438e506fcef8f14ae367db82d9b3 - Malicious Excel Document

7bf33b494c70bd0a0a865b5fbcee0c58fa9274b8741b03695b45998bcd459328 – Core print.lua script
 

 

 

・お問い合わせ:https://www.blackberry.com/ja/jp/forms/enterprise/contact-us

・イベント/セミナー情報:https://www.blackberry.com/ja/jp/events/jp-events-tradeshows

・サイバーセキュリティチームによるコンサルティング: https://www.blackberry.com/ja/jp/services/blackberry-cybersecurity-consulting/overview

・BlackBerry Japan:https://www.blackberry.com/ja/jp

The BlackBerry Research and Intelligence Team

About The BlackBerry Research and Intelligence Team

BlackBerry の Research and Intelligence Team は、新たに生じている脅威と持続的な脅威を検証し、セキュリティ担当者とその所属企業のために、インテリジェンス解析を提供しています。