概要: 過去 2 年間、私たちは Phobos ファミリー ランサムウェアのプルーフ オブ シンクリプターをいじっていました。 機能しますが、ここで説明する理由により、使用するのは実用的ではありません. その結果、現在、正確な世界の患者をこのレベルに戻すためにそれを利用することができませんでした. 私たちは今、私たちの調査結果と手段を投稿することを決心しました. 脆弱性と、デクリプターの計算の複雑さとパフォーマンスをどのように改善して、ほぼ賢明な実装を実現したかを説明できます。 結果として得られた思考の証明は、
CERT-Polska/phobos-cuda-decryptor-poc.
いつ、何を、なぜフォボスは火星の2つの純粋な衛星の中で最も内側にあり、より優れており、反対はダイモスです。 . それにもかかわらず、これは最近のランサムウェア ファミリであり、2019 年初頭に最初に注目されました。特に魅力的なものではありません。Dharma と多くの類似点があり、間違いなく類似の作成者によって作成されるようになりました。 Phobos (ランサムウェア) について明確に魅力的なものは何もありません。重要な機能強化や魅力的な側面はありません。即時長のフォボス。 その後、フォボスの鍵となる時刻表が機能することは独特になり、おそらく破損しているとさえ言えます。 これは、デクリプターを作成することを期待して、追加の比較を行うために私たちについて紹介しました. それでも先回りはやめましょう
リバースエンジニアリングやりましょうすべてのランサムウェア (アンチデバッグ ファセット、シャドウ コピーの削除、基本的なディスク トラバーサル操作など) に類似している愚かな小さなプリントをスキップします。 私たちにとって最も魅力的な操作は、おそらく基本的な時刻表です:
風変わりな断片は、1 つの驚異的なエントロピー供給の使用の別の方法として、マルウェア作成者がいくつかの腐ったものを利用することに決めたということです。
QueryPerformanceCounter() への 2 回の呼び出し- GetLocalTime() + SystemTimeToFileTime()
GetTickCount() )GetCurrentProcessId()GetCurrentThreadId() 遅かれ早かれ、SHA-256 ラウンドの変数にもかかわらず決定論的なシーケンスが適用されます.
平均して、キーをテストするには、256 回の SHA-256 実行と 1 回の AES 復号化が必要です.
私たちの頭の中のアラーム。 私たち全員が 1 2 次元の精度 (たとえば、ファイルのタイムスタンプやログの使用) で感染の時刻を知っていると仮定すると、各要素をブルートパワーする操作のシーケンスは次のようになります:
手術 いいえ。 オペレーション QueryPerformanceCounter 1 107 QueryPerformanceCounter 2 107
GetTickCount103 SystemTimeToFileTime
105 GetCurrentProcessId230
GetCurrentThreadId230
または、各要素がおそらくさらに独立してブルート強制される可能性があることはもはや明らかではありません (107 現代のコンピューターではもはやそれほど強力ではありません)。 それにもかかわらず、私たちは全体のキーをより高く取得しますか?
仮定、仮定 (本当のことを言うと、テストしたいという欲求を達成するキーはいくつありますか?)
暫定位置 (141 ビットのエントロピー) それにもかかわらず、単純な乗算により、素朴なキーのシーケンスブルートパワー攻撃は:
>>> 10
7 * 10 7 * 10 3 * 10
5 * 2 30
* 2 30* 256 295147905179352825856000000000000000000000 >>> ログ(10
7
* 10
7 * 10
3 *
10 )5 *
2 30 * 2 30 * 256
), 2) 141.08241808752197 # は 141 ビットの数値です
ここにあるのは... 明らかに山のような、不可解な巨大な数字です。
しかし、私たちはもはや情報なしで提示するものではありません。
私もいいですか?本当にあなたのPIDを占有してください。 (81 ビットのエントロピー) いくつかの仮定から始めましょう。
私たちはすでにそれを作成しました: ガジェット/ファイル ログに起因するもので、私たち全員が 1 次元精度で時刻を知っています。 そのような情報源の 1 つは、おそらくハウス ウィンドウズ イベント ログである可能性があります:
デフォルトでは、すべての固有のアクティビティをトリガーするイベントはありません。とはいえ、十分なフォレンジック予測があれば、ランサムウェア アクティビティの PID と TID が高くなるスキルは繰り返し発生します。ハウス ウィンドウの PID は連続しているため、それらを重要な量だけ制限します。 したがって、ほとんどの場合、力ずくのパンチに専念することはもうありません
230 キーレジデンス.ランサムウェア アクティビティの PID と TID (基本的なスレッドの) は既にわかっています。 心配しないでください。ここに、この記事全体で最高のハンドウェーブがあります。

>>> ログ(
10 7
*
10 7 * 10
3 *
105 * 256 , 2) 81.08241808752197
81 ビットのエントロピーは総当りを考えるにはあまりにも強力な光の能力ですが、それにもかかわらず、私たちはどこかを得ています.
Δt=t1 - t0 (67 ビットのエントロピー) 適度に構成できるもう 1 つの仮定は、2 つの連続した QueryPerformanceCounter 呼び出しが返されますアナログの結果。 つまり、2d QueryPerformanceCounter は、繰り返し、最初のものよりも小さくなります。 両方のカウンターの合計ブルート パワーを達成するに値するものはありません。 たとえば、次のコードの使用:
ために qpc1 の 変化
( 10 7): ために qpc2 の 変化
(
10 7):
比較(qpc1 , )qpc2)

私たちは以下を達成することができます:
ために qpc1 )の 変化
(10
7): ために qpc_delta の
変化
( 103):
比較( qpc1
,
qpc1 + qpc_delta) 103 は、経験的に十分であると決心しました。 優雅な 1 ミリ秒ですが、ほとんどの場合、これで十分である必要があります。 でも試してみましょう:
>>> ログ(
10 7 * 10 3 * 10 3 *
10 5 * 256 , 2 ) 67.79470570797253 とにかく、正確な時間が必要なのは誰ですか? (61 ビットのエントロピー) 267 sha256 の呼び出しは軽い 負荷 ですが、扱いやすくなっています。 例として、これは偶然にもほぼ正確に最新の BTC ハッシュレートです。 これは、BTC ネットワーク全体が、無意味に電気エネルギーを燃やすのではなく、フォボスの犠牲者を解読するために一度転用された場合、2d
1
. 最終解説の時間: SystemTimeToFileTime は、おそらく精度を占有するだけかもしれません10 マイクロ秒に相当します。 それでも
GetLocalTime はもはや:
これは、私たちが完全に総当たりに値することを意味します 103 105
の別の選択肢:
>>> ログ(10
7 * 10 3* 10
3 * 103 *
256
,
2)
61.150849518197795
計算時間 (51 ビットのエントロピー) 最適化するべき余分な明らかなものはありません。 多分、どこかでより優れたアルゴリズムを表現できるのではないでしょうか?
と思った 鍵 を に展開GetTickCount() ^ QueryPerformanceCounter().Low. 単純な総当たりアルゴリズムは、両方のパーツのすべてのスキル値を比較しますが、ほとんどの場合、かなり高い値を達成できます。 例として、 4 ^ 0==5 ^ 1==6 ^ 2=...==4。 を完全に気にかけているので、類似のキーであるために停止するタイマー値を無視できます。
def 範囲(
fst,
snd ): s0
,
s1 = fst e0 , e1
= snd 外=シチュエーション () ために 私
の 変化(
s0, s1 ) + 1): ために
j
の 変化(e0
, e1 +1 ): 外.追加(
私 ^ j ) 戻る 外
残念ながら、これはかつてほとんど CPU を集中的に使用することはありませんでした (覚えておいてください、私たちはスキルと同じくらい強力なパフォーマンスを絞り出すために生きています)。 重複に時間を費やさないようにする、より優れた再帰アルゴリズムがあることがわかりました。 プランバックは、それはかろうじて微妙であり、もはやあまり望ましいものではありません:

uint64_t
fillr ( uint64_t バツ) {
uint64_t r =
バツ ; 一方 (
バツ) { r =
バツ
- 1 ; バツ &=
r
; }
戻る r ;}
uint64_t シグマ(
uint64_t a- , uint64_t b ) { )戻る あ |
b
|
fillr(
あ & b); }
空所 merge_xors ( uint64_t s0
, uint64_t
e0, uint64_t s1, )uint64_t e1 , int64_t
少し, uint64_t
プレフィックス, std :: ベクター<uint32_t> *外
) { もしも
( 少し < 0) { 外->push_back (prefix ) ; 戻る;
} uint64_t 隠す= 1ULL <<少し; uint64_
フォボスは火星の2つの純粋な衛星の中で最も内側にあり、より優れており、反対はダイモスです。 . それにもかかわらず、これは最近のランサムウェア ファミリであり、2019 年初頭に最初に注目されました。特に魅力的なものではありません。Dharma と多くの類似点があり、間違いなく類似の作成者によって作成されるようになりました。 Phobos (ランサムウェア) について明確に魅力的なものは何もありません。重要な機能強化や魅力的な側面はありません。即時長のフォボス。 その後、フォボスの鍵となる時刻表が機能することは独特になり、おそらく破損しているとさえ言えます。 これは、デクリプターを作成することを期待して、追加の比較を行うために私たちについて紹介しました. それでも先回りはやめましょう
リバースエンジニアリングやりましょうすべてのランサムウェア (アンチデバッグ ファセット、シャドウ コピーの削除、基本的なディスク トラバーサル操作など) に類似している愚かな小さなプリントをスキップします。 私たちにとって最も魅力的な操作は、おそらく基本的な時刻表です:
風変わりな断片は、1 つの驚異的なエントロピー供給の使用の別の方法として、マルウェア作成者がいくつかの腐ったものを利用することに決めたということです。
QueryPerformanceCounter() への 2 回の呼び出し- GetLocalTime() + SystemTimeToFileTime()
GetTickCount() )GetCurrentProcessId()GetCurrentThreadId() 遅かれ早かれ、SHA-256 ラウンドの変数にもかかわらず決定論的なシーケンスが適用されます.
平均して、キーをテストするには、256 回の SHA-256 実行と 1 回の AES 復号化が必要です.
私たちの頭の中のアラーム。 私たち全員が 1 2 次元の精度 (たとえば、ファイルのタイムスタンプやログの使用) で感染の時刻を知っていると仮定すると、各要素をブルートパワーする操作のシーケンスは次のようになります:
手術 いいえ。 オペレーション QueryPerformanceCounter 1 107 QueryPerformanceCounter 2 107
GetTickCount103 SystemTimeToFileTime
105 GetCurrentProcessId230
GetCurrentThreadId230
または、各要素がおそらくさらに独立してブルート強制される可能性があることはもはや明らかではありません (107 現代のコンピューターではもはやそれほど強力ではありません)。 それにもかかわらず、私たちは全体のキーをより高く取得しますか?
仮定、仮定 (本当のことを言うと、テストしたいという欲求を達成するキーはいくつありますか?)
暫定位置 (141 ビットのエントロピー) それにもかかわらず、単純な乗算により、素朴なキーのシーケンスブルートパワー攻撃は:
>>> 10
7 * 10 7 * 10 3 * 10
5 * 2 30
* 2 30* 256 295147905179352825856000000000000000000000 >>> ログ(10
7
* 10
7 * 10
3 *
10 )5 *
2 30 * 2 30 * 256
), 2) 141.08241808752197 # は 141 ビットの数値です
ここにあるのは... 明らかに山のような、不可解な巨大な数字です。
しかし、私たちはもはや情報なしで提示するものではありません。
私もいいですか?本当にあなたのPIDを占有してください。 (81 ビットのエントロピー) いくつかの仮定から始めましょう。
私たちはすでにそれを作成しました: ガジェット/ファイル ログに起因するもので、私たち全員が 1 次元精度で時刻を知っています。 そのような情報源の 1 つは、おそらくハウス ウィンドウズ イベント ログである可能性があります:
デフォルトでは、すべての固有のアクティビティをトリガーするイベントはありません。とはいえ、十分なフォレンジック予測があれば、ランサムウェア アクティビティの PID と TID が高くなるスキルは繰り返し発生します。ハウス ウィンドウの PID は連続しているため、それらを重要な量だけ制限します。 したがって、ほとんどの場合、力ずくのパンチに専念することはもうありません
230 キーレジデンス.ランサムウェア アクティビティの PID と TID (基本的なスレッドの) は既にわかっています。 心配しないでください。ここに、この記事全体で最高のハンドウェーブがあります。

>>> ログ(
10 7
*
10 7 * 10
3 *
105 * 256 , 2) 81.08241808752197
81 ビットのエントロピーは総当りを考えるにはあまりにも強力な光の能力ですが、それにもかかわらず、私たちはどこかを得ています.
Δt=t1 - t0 (67 ビットのエントロピー) 適度に構成できるもう 1 つの仮定は、2 つの連続した QueryPerformanceCounter 呼び出しが返されますアナログの結果。 つまり、2d QueryPerformanceCounter は、繰り返し、最初のものよりも小さくなります。 両方のカウンターの合計ブルート パワーを達成するに値するものはありません。 たとえば、次のコードの使用:
ために qpc1 の 変化
( 10 7): ために qpc2 の 変化
(
10 7):
比較(qpc1 , )qpc2)

私たちは以下を達成することができます:
ために qpc1 )の 変化
(10
7): ために qpc_delta の
変化
( 103):
比較( qpc1
,
qpc1 + qpc_delta) 103 は、経験的に十分であると決心しました。 優雅な 1 ミリ秒ですが、ほとんどの場合、これで十分である必要があります。 でも試してみましょう:
>>> ログ(
10 7 * 10 3 * 10 3 *
10 5 * 256 , 2 ) 67.79470570797253 とにかく、正確な時間が必要なのは誰ですか? (61 ビットのエントロピー) 267 sha256 の呼び出しは軽い 負荷 ですが、扱いやすくなっています。 例として、これは偶然にもほぼ正確に最新の BTC ハッシュレートです。 これは、BTC ネットワーク全体が、無意味に電気エネルギーを燃やすのではなく、フォボスの犠牲者を解読するために一度転用された場合、2d
1
. 最終解説の時間: SystemTimeToFileTime は、おそらく精度を占有するだけかもしれません10 マイクロ秒に相当します。 それでも
GetLocalTime はもはや:
これは、私たちが完全に総当たりに値することを意味します 103 105
の別の選択肢:
>>> ログ(10
7 * 10 3* 10
3 * 103 *
256
,
2)
61.150849518197795
計算時間 (51 ビットのエントロピー) 最適化するべき余分な明らかなものはありません。 多分、どこかでより優れたアルゴリズムを表現できるのではないでしょうか?
と思った 鍵 を に展開GetTickCount() ^ QueryPerformanceCounter().Low. 単純な総当たりアルゴリズムは、両方のパーツのすべてのスキル値を比較しますが、ほとんどの場合、かなり高い値を達成できます。 例として、 4 ^ 0==5 ^ 1==6 ^ 2=...==4。 を完全に気にかけているので、類似のキーであるために停止するタイマー値を無視できます。
def 範囲(
fst,
snd ): s0
,
s1 = fst e0 , e1
= snd 外=シチュエーション () ために 私
の 変化(
s0, s1 ) + 1): ために
j
の 変化(e0
, e1 +1 ): 外.追加(
私 ^ j ) 戻る 外
残念ながら、これはかつてほとんど CPU を集中的に使用することはありませんでした (覚えておいてください、私たちはスキルと同じくらい強力なパフォーマンスを絞り出すために生きています)。 重複に時間を費やさないようにする、より優れた再帰アルゴリズムがあることがわかりました。 プランバックは、それはかろうじて微妙であり、もはやあまり望ましいものではありません:

uint64_t
fillr ( uint64_t バツ) {
uint64_t r =
バツ ; 一方 (
バツ) { r =
バツ
- 1 ; バツ &=
r
; }
戻る r ;}
uint64_t シグマ(
uint64_t a- , uint64_t b ) { )戻る あ |
b
|
fillr(
あ & b); }
空所 merge_xors ( uint64_t s0
, uint64_t
e0, uint64_t s1, )uint64_t e1 , int64_t
少し, uint64_t
プレフィックス, std :: ベクター<uint32_t> *外
) { もしも
( 少し < 0) { 外->push_back (prefix ) ; 戻る;
} uint64_t 隠す= 1ULL <<少し; uint64_
やりましょうすべてのランサムウェア (アンチデバッグ ファセット、シャドウ コピーの削除、基本的なディスク トラバーサル操作など) に類似している愚かな小さなプリントをスキップします。 私たちにとって最も魅力的な操作は、おそらく基本的な時刻表です:
- GetLocalTime() + SystemTimeToFileTime()
10
7
*10
7
* 10 3
*
105
* 256 , 2
)
81.08241808752197
81 ビットのエントロピーは総当りを考えるにはあまりにも強力な光の能力ですが、それにもかかわらず、私たちはどこかを得ています.
Δt=t1 - t0 (67 ビットのエントロピー)
適度に構成できるもう 1 つの仮定は、2 つの連続した QueryPerformanceCounter 呼び出しが返されますアナログの結果。 つまり、2d QueryPerformanceCounter は、繰り返し、最初のものよりも小さくなります。 両方のカウンターの合計ブルート パワーを達成するに値するものはありません。 たとえば、次のコードの使用:
ために
qpc1
の
変化
( 10 7): ためにqpc2 の 変化
(
10 7):比較(qpc1
, )qpc2)
私たちは以下を達成することができます:
ために qpc1 )の 変化
(
10
7): ために qpc_delta の
変化
(
103
):
比較
( qpc1
,
qpc1 + qpc_delta
)
103 は、経験的に十分であると決心しました。 優雅な 1 ミリ秒ですが、ほとんどの場合、これで十分である必要があります。 でも試してみましょう:
>>>
ログ( 10
7 * 10 3 * 10 3
*
105 * 256
- , 2
) 67.79470570797253
とにかく、正確な時間が必要なのは誰ですか? (61 ビットのエントロピー) 267 sha256 の呼び出しは軽い 負荷 ですが、扱いやすくなっています。 例として、これは偶然にもほぼ正確に最新の BTC ハッシュレートです。 これは、BTC ネットワーク全体が、無意味に電気エネルギーを燃やすのではなく、フォボスの犠牲者を解読するために一度転用された場合、2d
1
.
最終解説の時間:
SystemTimeToFileTime は、おそらく精度を占有するだけかもしれません10 マイクロ秒に相当します。 それでも
GetLocalTime はもはや:
これは、私たちが完全に総当たりに値することを意味します 103 105
- の別の選択肢:
>>> ログ(10
7 * 10 3 * 10
3
*
10
3
*
256
- ,
)
61.150849518197795
def 範囲(計算時間 (51 ビットのエントロピー) 最適化するべき余分な明らかなものはありません。 多分、どこかでより優れたアルゴリズムを表現できるのではないでしょうか?
と思った 鍵 を に展開GetTickCount() ^ QueryPerformanceCounter().Low. 単純な総当たりアルゴリズムは、両方のパーツのすべてのスキル値を比較しますが、ほとんどの場合、かなり高い値を達成できます。 例として、 4 ^ 0==5 ^ 1==6 ^ 2=...==4。 を完全に気にかけているので、類似のキーであるために停止するタイマー値を無視できます。
fst,
snd
- ): s0
,
s1=
- fst e0
- , uint64_t b ) { )戻る あ |
b
|
fillr( あ & b
); }
空所 merge_xors ( uint64_t s0
, uint64_t
e0, uint64_t s1, )uint64_t e1
,
int64_t
少し, uint64_t
プレフィックス, std :: ベクター<uint32_t
>
*外
) {
もしも
( 少し <0) { 外->push_back
(prefix
) ; 戻る ;
} uint64_t隠す= 1ULL
<<少し; uint64_
, e1
=
snd
外
=シチュエーション () ために
私
の 変化(
s0, s1 ) +- 1):
ために
j変化(
e0
, e1 +
1
):
外.追加(
私 ^ j ) 戻る 外
残念ながら、これはかつてほとんど CPU を集中的に使用することはありませんでした (覚えておいてください、私たちはスキルと同じくらい強力なパフォーマンスを絞り出すために生きています)。 重複に時間を費やさないようにする、より優れた再帰アルゴリズムがあることがわかりました。 プランバックは、それはかろうじて微妙であり、もはやあまり望ましいものではありません:
uint64_t
fillr
(
uint64_t バツ) {
uint64_tr
バツ ; 一方 (=
バツ) {
r =
バツ
- 1 ; バツ
- &=
r
;
}
戻る r ;
}
uint64_t シグマ(
uint64_t
a
- , uint64_t b ) { )戻る あ |
風変わりな断片は、1 つの驚異的なエントロピー供給の使用の別の方法として、マルウェア作成者がいくつかの腐ったものを利用することに決めたということです。
手術 | いいえ。 オペレーション | ||||||
---|---|---|---|---|---|---|---|
QueryPerformanceCounter 1 | 107 | QueryPerformanceCounter 2 |
|