Press Release | Media on CrowdStrike

ウクライナを標的にしたと報じられたPartyTicketランサムウェア、復号可能と判明

クラウドベースのエンドポイント、クラウドワークロード、アイデンティティ、データ保護の業界リーダーであるCrowdStrike Holdings, Inc.(NASDAQ: CRWD)の日本法人であるクラウドストライク 株式会社(本社:東京都港区、以下クラウドストライク)は本日、ウクライナの標的に対してデプロイされたランサムウェア「PartyTicket」に関する詳細情報をブログで紹介します。

2022年2月23日、ウクライナの組織を標的とした破壊的な攻撃が行われました。業界のレポートによると、被害に遭ったいくつかの企業や組織において、PartyTicket(またはHermeticRansom)というGo言語で書かれたランサムウェアが検知されました1。他に、CrowdStrike のインテリジェンスチームがDriveSlayer(HermeticWiper)として追跡する同系の高度なワイパー型攻撃も確認されています。

PartyTicketランサムウェアを分析したところ、ファイルの暗号化は表面的なもののようです。暗号化キーの初期化を正しく行っていないため、拡張子が.encryptedJBの暗号化ファイルであれば復元できることがわかりました。

技術分析
PartyTicketランサムウェアの検体には、4dc13bb83a16d4ff9865a51b3e4d24112327c526c1392e14d56f20d6f4eaf382というSHA256ハッシュがあります。cdir.exe、cname.exe、connh.exe、intpub.exeというファイル名と関連があることが確認されています。

Go言語の1.10.1バージョンで書かれたランサムウェアの検体には、voteFor403、C:/projects/403forBiden/wHiteHousE、primaryElectionProcessといった米国の政治システムを示す記号が多数含まれています。

このランサムウェアはすべてのドライブレターで実行を繰り返し、各ドライブ内とサブフォルダー内のファイルを再帰的に列挙します。ただし、WindowsおよびProgram Filesの文字列を含むファイルパスや、C:\Documents and Settings(Windows XPより新しいWindowsバージョンでは、C:\Users)のフォルダパスは除きます。以下の拡張子の付いたファイルを暗号化の対象として選びます。

acl、avi、bat、bmp、cab、cfg、chm、cmd、com、contact、crt、css、dat、dip、dll、doc、docx、dot、encryptedjb、epub、exe、gif、htm、html、ico、in、iso、jpeg、jpg、mp3、msi、odt、one、ova、pdf、pgsql、png、ppt、pptx、pub、rar、rtf、sfx、sql、txt、url、vdi、vsd、wma、wmv、wtv、xls、xlsx、xml、xps、zip

前述のパスに該当しない、これらの拡張子の各ファイルパスについて、ランサムウェアが自身のインスタンスを実行元と同じディレクトリにコピーしてコマンドラインで実行し、ファイルパスを引数として渡します。親のランサムウェアプロセスは、クローンにランダムなUUIDで名前を付けます。これはパブリックライブラリ2が生成したもので、現在のタイムスタンプおよび感染したホストのネットワークアダプターのMACアドレスが使用されます。

マルウェアの開発者はGoのWaitGroup処理で並列処理機能を実装しようとしたようですが、おそらくコーディングエラーのため、ランサムウェアは大量のスレッド(列挙したファイルパスごとに1つ)を作成し、選択したファイルの数と同じ回数分、自身のバイナリをカレントディレクトリにコピーします。すべての暗号化スレッドの終了後、コマンドラインから元のバイナリが消去されます。

検体がファイルパスを引数として受け取ると、Galois/Counter Mode(GCM)のAESを使用してファイルを暗号化します。AESキーはGoのrandパッケージのIntn関数で生成されます。文字列1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ内のオフセットを選択することで32バイトのキーを生成します。もう1つ別のコーディングエラーと思われますが、キーの生成後にIntn関数のシードが更新されています。つまり、バイナリとクローンが実行されるたび、同じAESキーが生成されています。ホスト上で暗号化されたすべてのファイルには同じ暗号化キーが使用されており、対応するPartyTicketの検体のキーで復号化が可能です。このエラーを活用して暗号化ファイルを復元するためのスクリプトをGitHubのCrowdStrikeのレポジトリに掲載しています。

AES暗号化キーはファイルごとにRSA-OAEPで暗号化されており、以下のパラメータのパブリックRSAキーを使用しています。

Modulus (N): 0xcbb94cb189a638b51e7cfe161cd92edb7145ecbd93989e78c94f8c15c61829286fd834d80c931daed4ac4aba14835fd3a6721602bcaa7193245fc6cf8e1d3261460ff3f1cbae3d44690beb989adee69ac486a932ee44dbdf0a44e772ab9822a16753cd08bdbb169f866f722114ee69c0cf1588fdaf8f7efd1c3ed243786078593f9b0cd867bab2b170c1843660d16e2181ae679137e2650551a41631398e027206e22a55858c741079ceafd50d5bd69546d4d52f5a33b0a576e1750d3f83afa1ce4403d768cbd670b443f61794b44705a8b1132c0c0ce77dbd04053ba20aec9baf23944270f10d16ad0727ed490c91c7f469278827c20a3e560f7c84015f7e1b
Exponent (E): 0x10001

暗号化の前に、ランサムウェアは
.[vote2024forjb@protonmail[.]com].encryptedJB というフォーマットでファイル名を変更しています(バイナリに含まれているその他の政治的な内容からすると、「JB」はおそらく米国大統領、Joseph Bidenのイニシャルでしょう)。その後、ランサムウェアはコンテンツを暗号化されたデータで上書きします。PartyTicketはファイルの最初の9437184バイト(9.44 MB)だけを暗号化するでしょう。引数として渡されたファイルがこの容量を超えている場合は、超過分は暗号化されません。ファイルのコンテンツを暗号化した後、PartyTicketはRSAで暗号化されたAESキーをファイルの最後に追加します。

このランサムウェアはファイルの暗号化が開始する前に、read_me.htmlという名前のHTMLの脅迫メモをユーザーのデスクトップディレクトリに残します(図1)。メモの文章は、文法ミスが意図的なものでない限り、英語が堪能な人物によって書かれたものでも、チェックされたものでもないようです。

 

図1:脅迫メモ

 

評価
CrowdStrike のインテリジェンスチームは、現時点ではPartyTicket 攻撃が名の知れた攻撃者によって行われたとは考えていません。

このランサムウェアには実装エラーが含まれており、暗号化が遅く、復号化することができます。このエラーから、マルウェアの作成者はGoで書くのに慣れていない、または、おそらく開発時間が限られていたためにマルウェアを十分にテストしていないということが示唆されます。特にPartyTicketは、低レベルのNTFSの構文解析アルゴリズムを使用したDriveSlayerのレベルにさえ達していません。相対的に完成度が低く、政治的なメッセージを含むこと、デプロイのタイミング、ウクライナの組織を標的としたものであることから、通常のランサムウェアのような身代金要求としてではなく、DriveSlayer攻撃に便乗して使用されたと思われます。

YARAシグネチャ
以下のYARAルールを使用してPartyTicketを検知することができます。


rule CrowdStrike_PartyTicket_01 : ransomware golang 
{
    meta:
        copyright = "(c) 2022 CrowdStrike Inc."
        description = "Detects Golang-based crypter"
        version = "202202250130"
        last_modified = "2022-02-25"
    strings:
        $ = ".encryptedJB" ascii
        $start = { ff 20 47 6f 20 62 75 69 6c 64 20 49 44 3a 20 22 }
        $end = { 0a 20 ff }
    condition:
        uint16(0) == 0x5A4D and uint32(uint32(0x3C)) == 0x00004550 and
        for 1 of ($end) : ( @start < @ and @start + 1024 > @) and
        all of them
}

rule CrowdStrike_PartyTicket_02 : PartyTicket golang 
{
    meta:
        copyright = "(c) 2022 CrowdStrike Inc."
        description = "Detects Golang-based PartyTicket ransomware"
        version = "202202250130"
        last_modified = "2022-02-25"
      strings:
        $s1 = "voteFor403"
        $s2 = "highWay60"
        $s3 = "randomiseDuration"
        $s4 = "subscribeNewPartyMember"
        $s5 = "primaryElectionProces"
        $s6 = "baggageGatherings"
        $s7 = "getBoo"
        $s8 = "selfElect"
        $s9 = "wHiteHousE"
        $s10 = "encryptedJB"
        $goid = { ff 20 47 6f 20 62 75 69 6c 64 20 49 44 3a 20 22 71 62 30 48 37 41 64 57 41 59 44 7a 66 4d 41 31 4a 38 30 42 2f 6e 4a 39 46 46 38 66 75 70 4a 6c 34 71 6e 45 34 57 76 41 35 2f 50 57 6b 77 45 4a 66 4b 55 72 52 62 59 4e 35 39 5f 4a 62 61 2f 32 6f 30 56 49 79 76 71 49 4e 46 62 4c 73 44 73 46 79 4c 32 22 0a 20 ff }
        $pdb = "C://projects//403forBiden//wHiteHousE"
    condition:
        (uint32(0) == 0x464c457f or (uint16(0) == 0x5a4d and uint16(uint32(0x3c)) == 0x4550)) and 4 of ($s*) or $pdb or $goid

PartyTicketで暗号化されたファイルを復号化するためのスクリプト
AESキー生成における前述の実装エラーにより、PartyTicketによる暗号化に使用されたAESキーを復元することができます。以下のGoのスクリプトによって、PartyTicketの検体
4dc13bb83a16d4ff9865a51b3e4d24112327c526c1392e14d56f20d6f4eaf382を使って暗号化されたファイルを復号化できます。このスクリプトは“-p”フラグによって複合化するファイルを引数として受け取り、復号化した出力を同じディレクトリ内の“decrypted.bin”に保存します。このスクリプトはコンパイルして実行することも、Goの実行パッケージで実行することもできます。Goのgo1.16.6のバージョンを使用してテストされました。


package main
 
import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/hex"
	"fmt"
	"os"
	"flag"
)
 
func main() {
 
	encrypted_filepath := flag.String("p", "encrypted.bin", "Path to encrypted file")
	flag.Parse()
 
	fmt.Printf("Decrypting file : %s\n", *encrypted_filepath)
	key_bytes := []byte("6FBBD7P95OE8UT5QRTTEBIWAR88S74DO")
	key := hex.EncodeToString(key_bytes)
	fmt.Printf("Decryption key : %s\n", key_bytes)
 
	dat, err := os.ReadFile(*encrypted_filepath)
	if err != nil {
		fmt.Println("Unable to open file, please supply path of encrypted file with flag -p, default file path is ./encrypted.bin")
		os.Exit(3)
	}
 
	decrypted_filepath := "decrypted.bin"
	filecontents := dat
	encrypted_contents := filecontents[:len(filecontents) - 288]
	enc_size := len(encrypted_contents)
	bsize := 1048604
	cycles := enc_size / bsize
 
	if cycles == 0{ 
 
		encrypted := hex.EncodeToString(encrypted_contents)
		decrypted := decrypt(encrypted, key)
		write_output(decrypted_filepath, decrypted)
		} else {
			for i:=0; i= 9 {
					start := 9 * bsize
					end := enc_size
					data := string(encrypted_contents[start:end])
					write_output(decrypted_filepath, data)
					break
				}
				block_start := i * bsize
				block_end := (i+1) * bsize
				if block_end > enc_size{
					block_end := enc_size
					encrypted:=hex.EncodeToString(encrypted_contents[block_start:block_end])
					decrypted := decrypt(encrypted, key)
					write_output(decrypted_filepath, decrypted)
 
				}
 
				encrypted:=hex.EncodeToString(encrypted_contents[block_start:block_end])
				decrypted := decrypt(encrypted, key)
				write_output(decrypted_filepath, decrypted)
			}
		}
 
		fmt.Printf("Decrypted file written to : %s\n", decrypted_filepath)
 
	}
 
func write_output(filepath string, data string) {
		f, err := os.OpenFile(filepath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
		if err != nil {
			panic(err)
		}
		byte_data := []byte(data)
		f.Write(byte_data)
		f.Close()
}
 
func decrypt(encryptedString string, keyString string) (decryptedString string) {
 
	key, _ := hex.DecodeString(keyString)
	enc, _ := hex.DecodeString(encryptedString)
 
	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err.Error())
	}
	aesGCM, err := cipher.NewGCM(block)
	if err != nil {
		panic(err.Error())
	}
	nonceSize := aesGCM.NonceSize()
	nonce, ciphertext := enc[:nonceSize], enc[nonceSize:]
	plaintext, err := aesGCM.Open(nil, nonce, ciphertext, nil)
	if err != nil {
		panic(err.Error())
	}
 
	return fmt.Sprintf("%s", plaintext)
}

 


1. https[:]//symantec-enterprise-blogs.security[.]com/blogs/threat-intelligence/ukraine-wiper-malware-russia
2. https[:]//pkg.go.dev/github[.]com/satori/go.uuid#NewV1

追加のリソース

 

※この資料は、米国時間2022年3月1日に発表されたCrowdStrikeのインテリジェンスチームによるブログの抄訳です。

CrowdStrikeについて
CrowdStrike Holdings Inc.(Nasdaq:CRWD)は、サイバーセキュリティのグローバルリーダーであり、エンドポイント、クラウドワークロード、アイデンティティ、データを含む企業におけるリスクを考える上で重要な領域を保護する世界最先端のクラウドネイティブのプラットフォームにより、現代のセキュリティを再定義しています。

CrowdStrike Falcon®プラットフォームは、CrowdStrike Security CloudとワールドクラスのAIを搭載し、リアルタイムの攻撃指標、脅威インテリジェンス、進化する攻撃者の戦術、企業全体からの充実したテレメトリーを活用して、超高精度の検知、自動化された保護と修復、精鋭による脅威ハンティング、優先付けられた脆弱性の可観測性を提供します。

Falconプラットフォームは、軽量なシングルエージェント・アーキテクチャを備え、クラウド上に構築されており、迅速かつスケーラブルな展開、優れた保護とパフォーマンス、複雑さの低減、短期間での価値提供を実現します。

CrowdStrike: We Stop Breaches

詳細はこちら: https://www.crowdstrike.jp/

ソーシャルメディア: Blog | Twitter

無料トライアル: https://go.crowdstrike.com/try-falcon-prevent-jp.html

© 2022 CrowdStrike, Inc. All rights reserved. CrowdStrike、Falconのロゴ、CrowdStrike Falcon、CrowdStrike Threat Graphは、CrowdStrike, Inc.が所有するマークであり、米国および各国の特許商標局に登録されています。CrowdStrikeは、その他の商標とサービスマークを所有し、第三者の製品やサービスを識別する目的で各社のブランド名を使用する場合があります。