Slide 1

Slide 1 text

1bit欠損メルセンヌ・ツイスタの 予測 artoy

Slide 2

Slide 2 text

Who am I? • 名前: artoy (twitter: @artoy5884) • 専門: プログラム検証 • CTF歴: 1週間

Slide 3

Slide 3 text

Outline • メルセンヌ・ツイスタとは • メルセンヌ・ツイスタの予測 • 1bit欠損メルセンヌ・ツイスタの予測

Slide 4

Slide 4 text

Outline • メルセンヌ・ツイスタとは • メルセンヌ・ツイスタの予測 • 1bit欠損メルセンヌ・ツイスタの予測

Slide 5

Slide 5 text

メルセンヌ・ツイスタとは 擬似乱数生成アルゴリズムの一種 • 周期が長い • 高次元均等分布 • 生成が速い • メモリ効率がいい → 様々な言語の標準ライブラリで用いられている

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

乱数生成方法 1. 内部状態の初期化 2. 内部状態の更新 3. 乱数の生成 * 3で624個乱数を生成したら2に戻る 𝑆! 𝑆" ・・・ 𝑆#$% シード値 𝑆#$& 𝑆#$' 𝑆!"#$ ・・・ 𝑟! 𝑟" 𝑟#$% 1 2 3 ・・・ *

Slide 10

Slide 10 text

内部状態の更新 • twist : 𝑆" , 𝑆"#$, , 𝑆"#&'( から 𝑆"#)*+ を求める操作 twist によって新しく624個の値を求めてそれらを新しい内部状態とする 𝑆! 𝑆" 𝑆$ ・・・ 𝑆%() ・・・ 𝑆#$% 𝑆%(* 内部状態

Slide 11

Slide 11 text

内部状態の更新 • twist : 𝑆" , 𝑆"#$, , 𝑆"#&'( から 𝑆"#)*+ を求める操作 twist によって新しく624個の値を求めてそれらを新しい内部状態とする 𝑆! 𝑆" 𝑆$ ・・・ 𝑆%() ・・・ 𝑆#$% 𝑆%(* twist 𝑆#$&

Slide 12

Slide 12 text

内部状態の更新 • twist : 𝑆" , 𝑆"#$, , 𝑆"#&'( から 𝑆"#)*+ を求める操作 twist によって新しく624個の値を求めてそれらを新しい内部状態とする 𝑆! 𝑆" 𝑆$ ・・・ 𝑆%() ・・・ 𝑆#$% 𝑆%(* twist 𝑆#$& 𝑆#$'

Slide 13

Slide 13 text

内部状態の更新 • twist : 𝑆" , 𝑆"#$, , 𝑆"#&'( から 𝑆"#)*+ を求める操作 twist によって新しく624個の値を求めてそれらを新しい内部状態とする 𝑆! 𝑆" 𝑆$ ・・・ 𝑆%() ・・・ 𝑆#$% 𝑆%(* 𝑆#$& 𝑆#$' ・・・ 𝑆!"#$ 新しい内部状態

Slide 14

Slide 14 text

twist • 𝑆"#$ の最下位ビットが0の時 𝑆"#)*+ = 𝑆"#&'( ⊕ 𝑆" & 0x80000000 | 𝑆"#$ & 0x7ffffffff ≫ 1 • 𝑆"#$ の最下位ビットが1の時 𝑆"#)*+ = 𝑆"#&'( ⊕ 𝑆" & 0x80000000 | 𝑆"#$ & 0x7ffffffff ≫ 1 ⊕ 0x9908b0df

Slide 15

Slide 15 text

twist • 𝑆"#$ の最下位ビットが0の時 𝑆"#)*+ = 𝑆"#&'( ⊕ 𝑆" & 0x80000000 | 𝑆"#$ & 0x7ffffffff ≫ 1 • 𝑆"#$ の最下位ビットが1の時 𝑆"#)*+ = 𝑆"#&'( ⊕ 𝑆" & 0x80000000 | 𝑆"#$ & 0x7ffffffff ≫ 1 ⊕ 0x9908b0df 𝑆& の最上位ビットのみを取っている

Slide 16

Slide 16 text

twist • 𝑆"#$ の最下位ビットが0の時 𝑆"#)*+ = 𝑆"#&'( ⊕ 𝑆" & 0x80000000 | 𝑆"#$ & 0x7ffffffff ≫ 1 • 𝑆"#$ の最下位ビットが1の時 𝑆"#)*+ = 𝑆"#&'( ⊕ 𝑆" & 0x80000000 | 𝑆"#$ & 0x7ffffffff ≫ 1 ⊕ 0x9908b0df 𝑆&'" の最上位ビット以外を取っている

Slide 17

Slide 17 text

twist • 𝑆"#$ の最下位ビットが0の時 𝑆"#)*+ = 𝑆"#&'( ⊕ 𝑆" & 0x80000000 | 𝑆"#$ & 0x7ffffffff ≫ 1 • 𝑆"#$ の最下位ビットが1の時 𝑆"#)*+ = 𝑆"#&'( ⊕ 𝑆" & 0x80000000 | 𝑆"#$ & 0x7ffffffff ≫ 1 ⊕ 0x9908b0df 異なるのはここだけ

Slide 18

Slide 18 text

twist 𝑆+ 𝑆+," = = 最上位ビット 最上位ビット 最下位ビット 30ビット 31ビット

Slide 19

Slide 19 text

twist 𝑆+ 𝑆+," 𝑆!"#$% = = ⊕ (⊕ 0x9908b0df) 𝑆!"&'( = = 1 のとき = 0

Slide 20

Slide 20 text

乱数の生成 内部状態のそれぞれの値に temper を施して得られた値を乱数として出力する 𝑆#$& 𝑆#$' 𝑆!"#$ ・・・ 𝑟! 𝑟" 𝑟#$% temper ・・・

Slide 21

Slide 21 text

temper temper を疑似コードで表すと以下のようになっている temper 𝑥 : 𝑥 ← 𝑥 ⊕ 𝑥 ≫ 11 𝑥 ← 𝑥 ⊕ 𝑥 ≪ 7 & 0x9d2c5680 𝑥 ← 𝑥 ⊕ 𝑥 ≪ 15 & 0xefc6000 𝑥 ← 𝑥 ⊕ 𝑥 ≫ 18 return 𝑥

Slide 22

Slide 22 text

Outline • メルセンヌ・ツイスタとは • メルセンヌ・ツイスタの予測 • 1bit欠損メルセンヌ・ツイスタの予測

Slide 23

Slide 23 text

メルセンヌ・ツイスタの予測 以下のどちらかが分かれば後続の乱数を全て予測することができる • シード値 • ある時点においての内部状態

Slide 24

Slide 24 text

メルセンヌ・ツイスタの予測 以下のどちらかが分かれば後続の乱数を全て予測することができる • シード値 • ある時点においての内部状態 → 本当は連続する624個の値であれば内部状態を跨いでもいい 例 : 𝑆"!, 𝑆"", … , 𝑆#%%

Slide 25

Slide 25 text

メルセンヌ・ツイスタの予測 以下のどちらかが分かれば後続の乱数を全て予測することができる • シード値 • ある時点においての内部状態 → 本当は連続する624個の値であれば内部状態を跨いでもいい 例 : 𝑆"!, 𝑆"", … , 𝑆#%% もし temper の逆操作(untemper)が可能なら、624個の連続する乱数を観測 すれば二つ目の条件を満たせるのでは?

Slide 26

Slide 26 text

untemper temper は 𝑥 と 𝑥 をシフトさせたものの XOR という操作で構成されている → 𝑥 のままの部分を利用してその操作の逆操作ができる 例 : 𝑥 ⊕ 𝑥 ≫ 11 𝑥 = 上位ビット 下位ビット

Slide 27

Slide 27 text

untemper temper は 𝑥 と 𝑥 をシフトさせたものの XOR という操作で構成されている → 𝑥 のままの部分を利用してその操作の逆操作ができる 例 : 𝑥 ⊕ 𝑥 ≫ 11 ⊕ ) 11ビット → 𝑥 のまま 𝑥 = 𝑥 ⊕ 𝑥 ≫ 11 =

Slide 28

Slide 28 text

untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≫ 11 目標 : 𝑦 から 𝑥 を求める 𝑥 = 11ビット (既知) 11ビット 10ビット

Slide 29

Slide 29 text

untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≫ 11 目標 : 𝑦 から 𝑥 を求める ⊕ ) 𝑦 = 𝑥 = 11ビット

Slide 30

Slide 30 text

untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≫ 11 目標 : 𝑦 から 𝑥 を求める ⊕ ) 𝑦 ⊕ (𝑦 ≫ 11) = 𝑥 = 11ビット = 𝑦 = 𝑦 ≫ 11

Slide 31

Slide 31 text

untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≫ 11 目標 : 𝑦 から 𝑥 を求める ⊕ ) 𝑦 ⊕ (𝑦 ≫ 11) = 𝑥 = 打ち消せる!

Slide 32

Slide 32 text

untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≫ 11 目標 : 𝑦 から 𝑥 を求める ⊕ ) 𝑦 ⊕ (𝑦 ≫ 11) = 𝑥 = 11ビット11ビット 緑の部分が分かった!

Slide 33

Slide 33 text

untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≫ 11 目標 : 𝑦 から 𝑥 を求める ⊕ ) 𝑦 ⊕ (𝑦 ≫ 11) = 𝑥 = 11ビット11ビット = 𝑧 緑の部分が分かった!

Slide 34

Slide 34 text

untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≫ 11 目標 : 𝑦, 𝑧 から 𝑥 を求める ⊕ ) 𝑦 ⊕ (𝑧 ≫ 11) = 𝑥 = 11ビット = 𝑦 = 𝑧 ≫ 11

Slide 35

Slide 35 text

untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≫ 11 目標 : 𝑦, 𝑧 から 𝑥 を求める ⊕ ) 𝑦 ⊕ (𝑧 ≫ 11) = 𝑥 = 11ビット 打ち消せる!

Slide 36

Slide 36 text

untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≫ 11 目標 : 𝑦, 𝑧 から 𝑥 を求める 𝑦 ⊕ (𝑧 ≫ 11) = 𝑥 = 𝑥 に戻せた!

Slide 37

Slide 37 text

untemper この操作を疑似コードで表すと以下のようになる untemper_right_shift (𝑦, shift): 𝑧 = 𝑦 known_bits = shift while (𝑖 < 32) begin 𝑧 ← 𝑦 ⊕ 𝑧 ≫ shift known_bits ← known_bits + shift end return 𝑧

Slide 38

Slide 38 text

再掲 : temper temper を疑似コードで表すと以下のようになっている temper 𝑥 : 𝑥 ← 𝑥 ⊕ 𝑥 ≫ 11 𝑥 ← 𝑥 ⊕ 𝑥 ≪ 7 & 0x9d2c5680 𝑥 ← 𝑥 ⊕ 𝑥 ≪ 15 & 0xefc6000 ビットが and されている場合は? 𝑥 ← 𝑥 ⊕ 𝑥 ≫ 18 → ほぼ同様の方法で戻せる return 𝑥

Slide 39

Slide 39 text

untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≪ 7 & 0x9d2c5680 目標 : 𝑦 から 𝑥 を求める 𝑥 = 7ビット 7ビット 7ビット 7ビット 4ビット ⊕ ) 𝑦 = 7ビット & 0x9d2c5680

Slide 40

Slide 40 text

untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≪ 7 & 0x9d2c5680 目標 : 𝑦 から 𝑥 を求める ⊕ ) 𝑦 ⊕ 𝑦 ≪ 7 & 0x9d2c5680 = 𝑥 = & 0x9d2c5680 & 0x9d2c5680 & 0x9d2c5680 & 0x9d2c5680 = 𝑦 = 𝑦 ≪ 7 & 0x9d2c5680

Slide 41

Slide 41 text

untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≪ 7 & 0x9d2c5680 目標 : 𝑦 から 𝑥 を求める ⊕ ) 𝑦 ⊕ 𝑦 ≪ 7 & 0x9d2c5680 = 𝑥 = & 0x9d2c5680 & 0x9d2c5680 & 0x9d2c5680 & 0x9d2c5680 打ち消せる!

Slide 42

Slide 42 text

untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≪ 7 & 0x9d2c5680 目標 : 𝑦 から 𝑥 を求める ⊕ ) 𝑦 ⊕ 𝑦 ≪ 7 & 0x9d2c5680 = 𝑥 = & 0x9d2c5680 水色の部分が分かった! → 後はこれの繰り返し

Slide 43

Slide 43 text

untemper この操作を疑似コードで表すと以下のようになる untemper_left_shift (𝑦, shift, mask): 𝑧 = 𝑦 known_bits = shift while (𝑖 < 32) begin 𝑧 ← 𝑦 ⊕ 𝑧 ≪ shift & mask known_bits ← known_bits + shift end return 𝑧

Slide 44

Slide 44 text

untemper untemper を疑似コードで表すと以下のようになる untemper 𝑥 : 𝑥 ← untemper_right_shift (𝑥, 18) 𝑥 ← untemper_left_shift (𝑥, 15, 0xefc60000) 𝑥 ← untemper_left_shift (𝑥, 7, 0x9d2c5680) 𝑥 ← untemper_right_shift (𝑥, 11) return 𝑥

Slide 45

Slide 45 text

メルセンヌ・ツイスタの予測 • 目標 : 内部状態の復元 • 条件 : 連続する624個の乱数が分かっている 𝑆#$& 𝑆#$' 𝑆!"#$ ・・・ 𝑟! 𝑟" 𝑟#$% untemper 内部状態 ・・・

Slide 46

Slide 46 text

どんな言語でも予測できるのか? • Python の random.getrandbits(32) はこの方法を使って予測することがで きる • 他の言語の標準ライブラリで実装されているメルセンヌ・ツイスタも同じ方法で 予測できるの?

Slide 47

Slide 47 text

どんな言語でも予測できるのか? • Python の random.getrandbits(32) はこの方法を使って予測することがで きる • 他の言語の標準ライブラリで実装されているメルセンヌ・ツイスタも同じ方法で 予測できるの? → No!!!

Slide 48

Slide 48 text

PHP : mt_rand() PHP の mt_rand()では 乱数を生成した後に1 ビット右シフトして出力し ている 範囲を指定した場合も… https://github.com/php/php-src/blob/master/ext/standard/mt_rand.c

Slide 49

Slide 49 text

PHP : mt_rand() 右に1ビットシフトしてから 返している! https://github.com/php/php-src/blob/master/ext/standard/mt_rand.c

Slide 50

Slide 50 text

PHP : mt_rand() 観測した624個の乱数の落ちた最下位ビットが全て当たったときのみ先述の方法 で内部状態を復元できる → 最大 2624 回試さなくてはならない! シード値をブルートフォースした方が早い…

Slide 51

Slide 51 text

Outline • メルセンヌ・ツイスタとは • メルセンヌ・ツイスタの予測 • 1bit欠損メルセンヌ・ツイスタの予測

Slide 52

Slide 52 text

1bit欠損メルセンヌ・ツイスタの予測 mt_rand() が生成する乱数を予測する方法はある • AMBIONICS SECURITY “BREAKING PHP'S MT_RAND() WITH 2 VALUES AND NO BRUTEFORCE” https://www.ambionics.io/blog/php-mt-rand-prediction • kurenaif “kurenaif Valentine Problems/three_values_twister” https://github.com/kurenaif/kurenaif_valentine_problems/tree/ main/three_values_twister

Slide 53

Slide 53 text

1bit欠損メルセンヌ・ツイスタの予測 これらの方法は、観測する乱数の個数は少なくてよいが、比較的早く生成された ものでなければならないという制限が付いている 例 : 𝑟$:::: から 𝑟*:::: までの乱数が分かってる → たくさん分かってても生成されたのが遅すぎて予測できない

Slide 54

Slide 54 text

1bit欠損メルセンヌ・ツイスタの予測 • 乱数の生成された早さに依存しない予測方法はないのか? • 最初の方法は乱数の生成された早さに依存しないが、条件を緩めれば同じよう な方法で内部状態を復元できないか? • 624個の乱数の最下位ビットを全て当てるまで試すのではなく、1つずつ確定 させることはできないのか?

Slide 55

Slide 55 text

1bit欠損メルセンヌ・ツイスタの予測 • twist : 𝑆" , 𝑆"#$, , 𝑆"#&'( から 𝑆"#)*+ を求める操作 𝑟"#)*+ が正しい時、𝑟", 𝑟"#$, 𝑟"#&'( の落ちた最下位ビットのうち一つくらいは 正しいのではないか? 𝑆+ 𝑆+," 𝑆!"#$% 𝑆!"&'( twist 𝑟& ≪ 1 | 0, 𝑟& ≪ 1 | 1 untemper 𝑟&'" ≪ 1 | 0, 𝑟&'" ≪ 1 | 1 𝑟&'#$( 𝑟&'%)* ≪ 1 | 0, 𝑟&'%)* ≪ 1 | 1 temper, ≫ 1

Slide 56

Slide 56 text

どの最下位ビットを採用するのか? 再掲 : twist 𝑆+ 𝑆+," 𝑆!"#$% = = ⊕ (⊕ 0x9908b0df) 𝑆!"&'( = = 1 のとき = 0

Slide 57

Slide 57 text

どの最下位ビットを採用するのか? 再掲 : twist 𝑆+ 𝑆+," 𝑆!"#$% = = ⊕ (⊕ 0x9908b0df) 𝑆!"&'( = = 1 のとき = 0 1ビットしか残ってない

Slide 58

Slide 58 text

どの最下位ビットを採用するのか? 再掲 : twist 𝑆+ 𝑆+," 𝑆!"#$% = = ⊕ (⊕ 0x9908b0df) 𝑆!"&'( = = 1 のとき = 0 全ビット使われている

Slide 59

Slide 59 text

どの最下位ビットを採用するのか? 再掲 : twist 𝑆+ 𝑆+," 𝑆!"#$% = = ⊕ (⊕ 0x9908b0df) 𝑆!"&'( = = 1 のとき = 0 32ビット中実質31ビット使われている そのうち1ビットは大きな違いをもたらす

Slide 60

Slide 60 text

1bit欠損メルセンヌ・ツイスタの予測 • 目標 : 内部状態の復元 • 条件 : 連続する1248個の乱数が分かっている 𝑆+ 𝑆+," 𝑆!"#$% 𝑆!"&'( twist 𝑟& ≪ 1 | 0, 𝑟& ≪ 1 | 1 untemper 𝑟&'" ≪ 1 | 0, 𝑟&'" ≪ 1 | 1 𝑟&'#$( 𝑟&'%)* ≪ 1 | 0, 𝑟&'%)* ≪ 1 | 1 temper, ≫ 1 合っているか確かめる

Slide 61

Slide 61 text

1bit欠損メルセンヌ・ツイスタの予測 • 目標 : 内部状態の復元 • 条件 : 連続する1248個の乱数が分かっている 𝑆+," 𝑆+,$ 𝑆!"#$) 𝑆!"&'* twist 𝑟&'" ≪ 1 | 0, 𝑟&'" ≪ 1 | 1 untemper 𝑟&'$ ≪ 1 | 0, 𝑟&'$ ≪ 1 | 1 𝑟&'#$+ 𝑟&'%), ≪ 1 | 0, 𝑟&'%), ≪ 1 | 1 temper, ≫ 1 合っているか確かめる

Slide 62

Slide 62 text

1bit欠損メルセンヌ・ツイスタの予測 • 目標 : 内部状態の復元 • 条件 : 連続する1248個の乱数が分かっている この方法だと計算するのは 2&×624 通りでいい 𝑆+," 𝑆+,$ ・・・ 𝑆!"#$% ・・・ 𝑆!"&'# 𝑆!"#$) 𝑆!"&'( 内部状態が復元できた!!

Slide 63

Slide 63 text

本当に正しく予測できているのか? • 𝑆"#$ を復元した内部状態として採用すると必ず正しく予測できる • 正しく予測できない時 → 𝑟"#$ に付けるビットが異なるにもかかわらず 𝑟"#)*+ が同じになる場合がある時 𝑆+ 𝑆+," 𝑆!"#$% 𝑆!"&'( twist 𝑟& ≪ 1 | 0, 𝑟& ≪ 1 | 1 untemper 𝑟&'" ≪ 1 | 0, 𝑟&'" ≪ 1 | 1 𝑟&'#$( 𝑟&'%)* ≪ 1 | 0, 𝑟&'%)* ≪ 1 | 1 temper, ≫ 1

Slide 64

Slide 64 text

本当に正しく予測できているのか? • 全ての乱数に対してこの場合を探すのは大変すぎる • 二元体上では XOR もビットシフトも行列の演算で表せる → それらの合成関数である untemper は線型写像なので、 untemper 𝑥 ⊕ 1 = untermper 𝑥 ⊕ untemper 1 これを使うと正しく予測できない場合を次のように変えることができる

Slide 65

Slide 65 text

本当に正しく予測できているのか? 正しく予測できないのは、以下を満たす 𝑏: , 𝑏&'( が存在する時 𝑏: , 𝑏&'( = 0, 1 untemper 𝑏! = = ⊕ (⊕ 0x9908b0df) = = 1 のとき = 0 untemper 1 0 untemper 𝑏%()

Slide 66

Slide 66 text

本当に正しく予測できているのか? • そのような 𝑏: , 𝑏&'( は存在しないので 𝑆"#$ を採用すれば正しく予測できる • この方法を使えば1ビット欠損したメルセンヌ・ツイスタを乱数が生成された早さ にかかわらず、ブルートフォースも無しで予測することができる!!

Slide 67

Slide 67 text

まとめ • 普通のメルセンヌ・ツイスタは連続する624個の乱数を観測すれば予測できる • 1bit 欠損メルセンヌ・ツイスタは連続する1248個の乱数を観測すれば予測 できる • メルセンヌ・ツイスタに興味を持った方は kurenaif さんの kurenaif Valentine Problems を解こう!

Slide 68

Slide 68 text

謝辞 本スライドで説明した、1248個の乱数から1bit欠損メルセンヌ・ツイスタを予測 する方法は、セキュリティキャンプ全国大会2021で kurenaif さんから出題してい ただいた問題によるものです。 また、untemper の線形性を利用した、1bit欠損メルセンヌ・ツイスタの予測方法 の正当性の説明は、同じく 魔女のお茶会 2021/冬 にて発表した しとおさん の 証明を元にスライドにさせていただきました。 kurenaif さん、しとおさん、本当にありがとうございました。

Slide 69

Slide 69 text

参考文献 • Makoto Matsumoto, Takuji Nishimura “Mersenne Twister: A 623- dimensionally equidistributed uniform pseudorandom number generator” http://www.math.sci.hiroshima-u.ac.jp/m-mat/MT/ARTICLES/mt.pdf • 松本 眞 「あなたの使っている乱数、大丈夫?」 http://www.math.sci.hiroshima-u.ac.jp/m-mat/TEACH/ichimura-sho- koen.pdf • inaz2 ももいろテクノロジー 「Mersenne Twisterの出力を推測してみる」 https://inaz2.hatenablog.com/entry/2016/03/07/194147

Slide 70

Slide 70 text

参考文献 • AMBIONICS SECURITY “BREAKING PHP'S MT_RAND() WITH 2 VALUES AND NO BRUTEFORCE” https://www.ambionics.io/blog/php-mt-rand-prediction • kurenaif “kurenaif Valentine Problems/three_values_twister” https://github.com/kurenaif/kurenaif_valentine_problems/tree/main/ three_values_twister • sekai 6715.jp 「メルセンヌ・ツイスタを倒す」 https://6715.jp/posts/6/