Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
1bit欠損メルセンヌ・ツイスタの予測 (魔女のお茶会 2021/冬)
Search
kobaryo
November 14, 2021
Programming
3
1.3k
1bit欠損メルセンヌ・ツイスタの予測 (魔女のお茶会 2021/冬)
kurenaif さん主催のCTF勉強会、魔女のお茶会 2021/冬にて発表させていただいた際のスライドです。
kobaryo
November 14, 2021
Tweet
Share
More Decks by kobaryo
See All by kobaryo
Gobra で見る形式検証 (mercari.go #26)
artoy
1
780
Other Decks in Programming
See All in Programming
DroidKnights 2025 - 다양한 스크롤 뷰에서의 영상 재생
gaeun5744
3
300
Rails産でないDBを Railsに引っ越すHACK - Omotesando.rb #110
lnit
1
160
Using AI Tools Around Software Development
inouehi
0
1.2k
関数型まつりレポート for JuliaTokai #22
antimon2
0
120
コード書くの好きな人向けAIコーディング活用tips #orestudy
77web
3
320
エンジニア向け採用ピッチ資料
inusan
0
120
事業戦略を理解してソフトウェアを設計する
masuda220
PRO
22
6.2k
CSC307 Lecture 17
javiergs
PRO
0
120
Bytecode Manipulation 으로 생산성 높이기
bigstark
2
360
Practical Tips and Tricks for Working with Compose Multiplatform Previews (mDevCamp 2025)
stewemetal
0
130
Haskell でアルゴリズムを抽象化する / 関数型言語で競技プログラミング
naoya
17
4.7k
単体テストの始め方/作り方
toms74209200
0
490
Featured
See All Featured
Learning to Love Humans: Emotional Interface Design
aarron
273
40k
Typedesign – Prime Four
hannesfritz
42
2.7k
RailsConf 2023
tenderlove
30
1.1k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
35
2.3k
KATA
mclloyd
29
14k
Site-Speed That Sticks
csswizardry
10
640
Embracing the Ebb and Flow
colly
86
4.7k
Building an army of robots
kneath
306
45k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
281
13k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
657
60k
We Have a Design System, Now What?
morganepeng
52
7.6k
Code Review Best Practice
trishagee
68
18k
Transcript
1bit欠損メルセンヌ・ツイスタの 予測 artoy
Who am I? • 名前: artoy (twitter: @artoy5884) • 専門:
プログラム検証 • CTF歴: 1週間
Outline • メルセンヌ・ツイスタとは • メルセンヌ・ツイスタの予測 • 1bit欠損メルセンヌ・ツイスタの予測
Outline • メルセンヌ・ツイスタとは • メルセンヌ・ツイスタの予測 • 1bit欠損メルセンヌ・ツイスタの予測
メルセンヌ・ツイスタとは 擬似乱数生成アルゴリズムの一種 • 周期が長い • 高次元均等分布 • 生成が速い • メモリ効率がいい
→ 様々な言語の標準ライブラリで用いられている
None
None
None
乱数生成方法 1. 内部状態の初期化 2. 内部状態の更新 3. 乱数の生成 * 3で624個乱数を生成したら2に戻る 𝑆!
𝑆" ・・・ 𝑆#$% シード値 𝑆#$& 𝑆#$' 𝑆!"#$ ・・・ 𝑟! 𝑟" 𝑟#$% 1 2 3 ・・・ *
内部状態の更新 • twist : 𝑆" , 𝑆"#$, , 𝑆"#&'( から
𝑆"#)*+ を求める操作 twist によって新しく624個の値を求めてそれらを新しい内部状態とする 𝑆! 𝑆" 𝑆$ ・・・ 𝑆%() ・・・ 𝑆#$% 𝑆%(* 内部状態
内部状態の更新 • twist : 𝑆" , 𝑆"#$, , 𝑆"#&'( から
𝑆"#)*+ を求める操作 twist によって新しく624個の値を求めてそれらを新しい内部状態とする 𝑆! 𝑆" 𝑆$ ・・・ 𝑆%() ・・・ 𝑆#$% 𝑆%(* twist 𝑆#$&
内部状態の更新 • twist : 𝑆" , 𝑆"#$, , 𝑆"#&'( から
𝑆"#)*+ を求める操作 twist によって新しく624個の値を求めてそれらを新しい内部状態とする 𝑆! 𝑆" 𝑆$ ・・・ 𝑆%() ・・・ 𝑆#$% 𝑆%(* twist 𝑆#$& 𝑆#$'
内部状態の更新 • twist : 𝑆" , 𝑆"#$, , 𝑆"#&'( から
𝑆"#)*+ を求める操作 twist によって新しく624個の値を求めてそれらを新しい内部状態とする 𝑆! 𝑆" 𝑆$ ・・・ 𝑆%() ・・・ 𝑆#$% 𝑆%(* 𝑆#$& 𝑆#$' ・・・ 𝑆!"#$ 新しい内部状態
twist • 𝑆"#$ の最下位ビットが0の時 𝑆"#)*+ = 𝑆"#&'( ⊕ 𝑆" &
0x80000000 | 𝑆"#$ & 0x7ffffffff ≫ 1 • 𝑆"#$ の最下位ビットが1の時 𝑆"#)*+ = 𝑆"#&'( ⊕ 𝑆" & 0x80000000 | 𝑆"#$ & 0x7ffffffff ≫ 1 ⊕ 0x9908b0df
twist • 𝑆"#$ の最下位ビットが0の時 𝑆"#)*+ = 𝑆"#&'( ⊕ 𝑆" &
0x80000000 | 𝑆"#$ & 0x7ffffffff ≫ 1 • 𝑆"#$ の最下位ビットが1の時 𝑆"#)*+ = 𝑆"#&'( ⊕ 𝑆" & 0x80000000 | 𝑆"#$ & 0x7ffffffff ≫ 1 ⊕ 0x9908b0df 𝑆& の最上位ビットのみを取っている
twist • 𝑆"#$ の最下位ビットが0の時 𝑆"#)*+ = 𝑆"#&'( ⊕ 𝑆" &
0x80000000 | 𝑆"#$ & 0x7ffffffff ≫ 1 • 𝑆"#$ の最下位ビットが1の時 𝑆"#)*+ = 𝑆"#&'( ⊕ 𝑆" & 0x80000000 | 𝑆"#$ & 0x7ffffffff ≫ 1 ⊕ 0x9908b0df 𝑆&'" の最上位ビット以外を取っている
twist • 𝑆"#$ の最下位ビットが0の時 𝑆"#)*+ = 𝑆"#&'( ⊕ 𝑆" &
0x80000000 | 𝑆"#$ & 0x7ffffffff ≫ 1 • 𝑆"#$ の最下位ビットが1の時 𝑆"#)*+ = 𝑆"#&'( ⊕ 𝑆" & 0x80000000 | 𝑆"#$ & 0x7ffffffff ≫ 1 ⊕ 0x9908b0df 異なるのはここだけ
twist 𝑆+ 𝑆+," = = 最上位ビット 最上位ビット 最下位ビット 30ビット 31ビット
twist 𝑆+ 𝑆+," 𝑆!"#$% = = ⊕ (⊕ 0x9908b0df) 𝑆!"&'(
= = 1 のとき = 0
乱数の生成 内部状態のそれぞれの値に temper を施して得られた値を乱数として出力する 𝑆#$& 𝑆#$' 𝑆!"#$ ・・・ 𝑟! 𝑟"
𝑟#$% temper ・・・
temper temper を疑似コードで表すと以下のようになっている temper 𝑥 : 𝑥 ← 𝑥 ⊕
𝑥 ≫ 11 𝑥 ← 𝑥 ⊕ 𝑥 ≪ 7 & 0x9d2c5680 𝑥 ← 𝑥 ⊕ 𝑥 ≪ 15 & 0xefc6000 𝑥 ← 𝑥 ⊕ 𝑥 ≫ 18 return 𝑥
Outline • メルセンヌ・ツイスタとは • メルセンヌ・ツイスタの予測 • 1bit欠損メルセンヌ・ツイスタの予測
メルセンヌ・ツイスタの予測 以下のどちらかが分かれば後続の乱数を全て予測することができる • シード値 • ある時点においての内部状態
メルセンヌ・ツイスタの予測 以下のどちらかが分かれば後続の乱数を全て予測することができる • シード値 • ある時点においての内部状態 → 本当は連続する624個の値であれば内部状態を跨いでもいい 例 :
𝑆"!, 𝑆"", … , 𝑆#%%
メルセンヌ・ツイスタの予測 以下のどちらかが分かれば後続の乱数を全て予測することができる • シード値 • ある時点においての内部状態 → 本当は連続する624個の値であれば内部状態を跨いでもいい 例 :
𝑆"!, 𝑆"", … , 𝑆#%% もし temper の逆操作(untemper)が可能なら、624個の連続する乱数を観測 すれば二つ目の条件を満たせるのでは?
untemper temper は 𝑥 と 𝑥 をシフトさせたものの XOR という操作で構成されている →
𝑥 のままの部分を利用してその操作の逆操作ができる 例 : 𝑥 ⊕ 𝑥 ≫ 11 𝑥 = 上位ビット 下位ビット
untemper temper は 𝑥 と 𝑥 をシフトさせたものの XOR という操作で構成されている →
𝑥 のままの部分を利用してその操作の逆操作ができる 例 : 𝑥 ⊕ 𝑥 ≫ 11 ⊕ ) 11ビット → 𝑥 のまま 𝑥 = 𝑥 ⊕ 𝑥 ≫ 11 =
untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≫ 11
目標 : 𝑦 から 𝑥 を求める 𝑥 = 11ビット (既知) 11ビット 10ビット
untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≫ 11
目標 : 𝑦 から 𝑥 を求める ⊕ ) 𝑦 = 𝑥 = 11ビット
untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≫ 11
目標 : 𝑦 から 𝑥 を求める ⊕ ) 𝑦 ⊕ (𝑦 ≫ 11) = 𝑥 = 11ビット = 𝑦 = 𝑦 ≫ 11
untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≫ 11
目標 : 𝑦 から 𝑥 を求める ⊕ ) 𝑦 ⊕ (𝑦 ≫ 11) = 𝑥 = 打ち消せる!
untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≫ 11
目標 : 𝑦 から 𝑥 を求める ⊕ ) 𝑦 ⊕ (𝑦 ≫ 11) = 𝑥 = 11ビット11ビット 緑の部分が分かった!
untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≫ 11
目標 : 𝑦 から 𝑥 を求める ⊕ ) 𝑦 ⊕ (𝑦 ≫ 11) = 𝑥 = 11ビット11ビット = 𝑧 緑の部分が分かった!
untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≫ 11
目標 : 𝑦, 𝑧 から 𝑥 を求める ⊕ ) 𝑦 ⊕ (𝑧 ≫ 11) = 𝑥 = 11ビット = 𝑦 = 𝑧 ≫ 11
untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≫ 11
目標 : 𝑦, 𝑧 から 𝑥 を求める ⊕ ) 𝑦 ⊕ (𝑧 ≫ 11) = 𝑥 = 11ビット 打ち消せる!
untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≫ 11
目標 : 𝑦, 𝑧 から 𝑥 を求める 𝑦 ⊕ (𝑧 ≫ 11) = 𝑥 = 𝑥 に戻せた!
untemper この操作を疑似コードで表すと以下のようになる untemper_right_shift (𝑦, shift): 𝑧 = 𝑦 known_bits =
shift while (𝑖 < 32) begin 𝑧 ← 𝑦 ⊕ 𝑧 ≫ shift known_bits ← known_bits + shift end return 𝑧
再掲 : temper temper を疑似コードで表すと以下のようになっている temper 𝑥 : 𝑥 ←
𝑥 ⊕ 𝑥 ≫ 11 𝑥 ← 𝑥 ⊕ 𝑥 ≪ 7 & 0x9d2c5680 𝑥 ← 𝑥 ⊕ 𝑥 ≪ 15 & 0xefc6000 ビットが and されている場合は? 𝑥 ← 𝑥 ⊕ 𝑥 ≫ 18 → ほぼ同様の方法で戻せる return 𝑥
untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≪ 7
& 0x9d2c5680 目標 : 𝑦 から 𝑥 を求める 𝑥 = 7ビット 7ビット 7ビット 7ビット 4ビット ⊕ ) 𝑦 = 7ビット & 0x9d2c5680
untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≪ 7
& 0x9d2c5680 目標 : 𝑦 から 𝑥 を求める ⊕ ) 𝑦 ⊕ 𝑦 ≪ 7 & 0x9d2c5680 = 𝑥 = & 0x9d2c5680 & 0x9d2c5680 & 0x9d2c5680 & 0x9d2c5680 = 𝑦 = 𝑦 ≪ 7 & 0x9d2c5680
untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≪ 7
& 0x9d2c5680 目標 : 𝑦 から 𝑥 を求める ⊕ ) 𝑦 ⊕ 𝑦 ≪ 7 & 0x9d2c5680 = 𝑥 = & 0x9d2c5680 & 0x9d2c5680 & 0x9d2c5680 & 0x9d2c5680 打ち消せる!
untemper 例 : 𝑦 = 𝑥 ⊕ 𝑥 ≪ 7
& 0x9d2c5680 目標 : 𝑦 から 𝑥 を求める ⊕ ) 𝑦 ⊕ 𝑦 ≪ 7 & 0x9d2c5680 = 𝑥 = & 0x9d2c5680 水色の部分が分かった! → 後はこれの繰り返し
untemper この操作を疑似コードで表すと以下のようになる untemper_left_shift (𝑦, shift, mask): 𝑧 = 𝑦 known_bits
= shift while (𝑖 < 32) begin 𝑧 ← 𝑦 ⊕ 𝑧 ≪ shift & mask known_bits ← known_bits + shift end return 𝑧
untemper untemper を疑似コードで表すと以下のようになる untemper 𝑥 : 𝑥 ← untemper_right_shift (𝑥,
18) 𝑥 ← untemper_left_shift (𝑥, 15, 0xefc60000) 𝑥 ← untemper_left_shift (𝑥, 7, 0x9d2c5680) 𝑥 ← untemper_right_shift (𝑥, 11) return 𝑥
メルセンヌ・ツイスタの予測 • 目標 : 内部状態の復元 • 条件 : 連続する624個の乱数が分かっている 𝑆#$&
𝑆#$' 𝑆!"#$ ・・・ 𝑟! 𝑟" 𝑟#$% untemper 内部状態 ・・・
どんな言語でも予測できるのか? • Python の random.getrandbits(32) はこの方法を使って予測することがで きる • 他の言語の標準ライブラリで実装されているメルセンヌ・ツイスタも同じ方法で 予測できるの?
どんな言語でも予測できるのか? • Python の random.getrandbits(32) はこの方法を使って予測することがで きる • 他の言語の標準ライブラリで実装されているメルセンヌ・ツイスタも同じ方法で 予測できるの?
→ No!!!
PHP : mt_rand() PHP の mt_rand()では 乱数を生成した後に1 ビット右シフトして出力し ている 範囲を指定した場合も…
https://github.com/php/php-src/blob/master/ext/standard/mt_rand.c
PHP : mt_rand() 右に1ビットシフトしてから 返している! https://github.com/php/php-src/blob/master/ext/standard/mt_rand.c
PHP : mt_rand() 観測した624個の乱数の落ちた最下位ビットが全て当たったときのみ先述の方法 で内部状態を復元できる → 最大 2624 回試さなくてはならない! シード値をブルートフォースした方が早い…
Outline • メルセンヌ・ツイスタとは • メルセンヌ・ツイスタの予測 • 1bit欠損メルセンヌ・ツイスタの予測
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
1bit欠損メルセンヌ・ツイスタの予測 これらの方法は、観測する乱数の個数は少なくてよいが、比較的早く生成された ものでなければならないという制限が付いている 例 : 𝑟$:::: から 𝑟*:::: までの乱数が分かってる →
たくさん分かってても生成されたのが遅すぎて予測できない
1bit欠損メルセンヌ・ツイスタの予測 • 乱数の生成された早さに依存しない予測方法はないのか? • 最初の方法は乱数の生成された早さに依存しないが、条件を緩めれば同じよう な方法で内部状態を復元できないか? • 624個の乱数の最下位ビットを全て当てるまで試すのではなく、1つずつ確定 させることはできないのか?
1bit欠損メルセンヌ・ツイスタの予測 • twist : 𝑆" , 𝑆"#$, , 𝑆"#&'( から
𝑆"#)*+ を求める操作 𝑟"#)*+ が正しい時、𝑟", 𝑟"#$, 𝑟"#&'( の落ちた最下位ビットのうち一つくらいは 正しいのではないか? 𝑆+ 𝑆+," 𝑆!"#$% 𝑆!"&'( twist 𝑟& ≪ 1 | 0, 𝑟& ≪ 1 | 1 untemper 𝑟&'" ≪ 1 | 0, 𝑟&'" ≪ 1 | 1 𝑟&'#$( 𝑟&'%)* ≪ 1 | 0, 𝑟&'%)* ≪ 1 | 1 temper, ≫ 1
どの最下位ビットを採用するのか? 再掲 : twist 𝑆+ 𝑆+," 𝑆!"#$% = = ⊕
(⊕ 0x9908b0df) 𝑆!"&'( = = 1 のとき = 0
どの最下位ビットを採用するのか? 再掲 : twist 𝑆+ 𝑆+," 𝑆!"#$% = = ⊕
(⊕ 0x9908b0df) 𝑆!"&'( = = 1 のとき = 0 1ビットしか残ってない
どの最下位ビットを採用するのか? 再掲 : twist 𝑆+ 𝑆+," 𝑆!"#$% = = ⊕
(⊕ 0x9908b0df) 𝑆!"&'( = = 1 のとき = 0 全ビット使われている
どの最下位ビットを採用するのか? 再掲 : twist 𝑆+ 𝑆+," 𝑆!"#$% = = ⊕
(⊕ 0x9908b0df) 𝑆!"&'( = = 1 のとき = 0 32ビット中実質31ビット使われている そのうち1ビットは大きな違いをもたらす
1bit欠損メルセンヌ・ツイスタの予測 • 目標 : 内部状態の復元 • 条件 : 連続する1248個の乱数が分かっている 𝑆+
𝑆+," 𝑆!"#$% 𝑆!"&'( twist 𝑟& ≪ 1 | 0, 𝑟& ≪ 1 | 1 untemper 𝑟&'" ≪ 1 | 0, 𝑟&'" ≪ 1 | 1 𝑟&'#$( 𝑟&'%)* ≪ 1 | 0, 𝑟&'%)* ≪ 1 | 1 temper, ≫ 1 合っているか確かめる
1bit欠損メルセンヌ・ツイスタの予測 • 目標 : 内部状態の復元 • 条件 : 連続する1248個の乱数が分かっている 𝑆+,"
𝑆+,$ 𝑆!"#$) 𝑆!"&'* twist 𝑟&'" ≪ 1 | 0, 𝑟&'" ≪ 1 | 1 untemper 𝑟&'$ ≪ 1 | 0, 𝑟&'$ ≪ 1 | 1 𝑟&'#$+ 𝑟&'%), ≪ 1 | 0, 𝑟&'%), ≪ 1 | 1 temper, ≫ 1 合っているか確かめる
1bit欠損メルセンヌ・ツイスタの予測 • 目標 : 内部状態の復元 • 条件 : 連続する1248個の乱数が分かっている この方法だと計算するのは
2&×624 通りでいい 𝑆+," 𝑆+,$ ・・・ 𝑆!"#$% ・・・ 𝑆!"&'# 𝑆!"#$) 𝑆!"&'( 内部状態が復元できた!!
本当に正しく予測できているのか? • 𝑆"#$ を復元した内部状態として採用すると必ず正しく予測できる • 正しく予測できない時 → 𝑟"#$ に付けるビットが異なるにもかかわらず 𝑟"#)*+
が同じになる場合がある時 𝑆+ 𝑆+," 𝑆!"#$% 𝑆!"&'( twist 𝑟& ≪ 1 | 0, 𝑟& ≪ 1 | 1 untemper 𝑟&'" ≪ 1 | 0, 𝑟&'" ≪ 1 | 1 𝑟&'#$( 𝑟&'%)* ≪ 1 | 0, 𝑟&'%)* ≪ 1 | 1 temper, ≫ 1
本当に正しく予測できているのか? • 全ての乱数に対してこの場合を探すのは大変すぎる • 二元体上では XOR もビットシフトも行列の演算で表せる → それらの合成関数である untemper
は線型写像なので、 untemper 𝑥 ⊕ 1 = untermper 𝑥 ⊕ untemper 1 これを使うと正しく予測できない場合を次のように変えることができる
本当に正しく予測できているのか? 正しく予測できないのは、以下を満たす 𝑏: , 𝑏&'( が存在する時 𝑏: , 𝑏&'( =
0, 1 untemper 𝑏! = = ⊕ (⊕ 0x9908b0df) = = 1 のとき = 0 untemper 1 0 untemper 𝑏%()
本当に正しく予測できているのか? • そのような 𝑏: , 𝑏&'( は存在しないので 𝑆"#$ を採用すれば正しく予測できる •
この方法を使えば1ビット欠損したメルセンヌ・ツイスタを乱数が生成された早さ にかかわらず、ブルートフォースも無しで予測することができる!!
まとめ • 普通のメルセンヌ・ツイスタは連続する624個の乱数を観測すれば予測できる • 1bit 欠損メルセンヌ・ツイスタは連続する1248個の乱数を観測すれば予測 できる • メルセンヌ・ツイスタに興味を持った方は kurenaif
さんの kurenaif Valentine Problems を解こう!
謝辞 本スライドで説明した、1248個の乱数から1bit欠損メルセンヌ・ツイスタを予測 する方法は、セキュリティキャンプ全国大会2021で kurenaif さんから出題してい ただいた問題によるものです。 また、untemper の線形性を利用した、1bit欠損メルセンヌ・ツイスタの予測方法 の正当性の説明は、同じく 魔女のお茶会
2021/冬 にて発表した しとおさん の 証明を元にスライドにさせていただきました。 kurenaif さん、しとおさん、本当にありがとうございました。
参考文献 • 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
参考文献 • 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/