Black Hat MEA Final 2024
一部記号の乱用・誤用がありますがわざとなので無視してください。
Trouble in pairs[crypto 16 solves]
1 | #!/usr/bin/env python3 |
問題の要約
1 | elif choice == 'l': |
公開鍵として、$p,q,g$が与えられ、その後何回でも使える2つの選択肢「Leak, Eval」与えられえます。
- Leakは適当に選ばれた平文に対するFiatSchnorr暗号の暗号文を返却します。ただ、
ここで選ばれた平文
は具体的に何なのかはわかりません。 - Evalは入力された復号を行い、エラーが起きなければ復号した結果=平文をeval()に入れて実行してくれます。
ここで、FiatSchnorr暗号はなんなのか見ていきます。コードとしては以下になりますが、数式に起こして簡単化していきます。
1 | def Encrypt(self, m: bytes) -> bytes: |
$$ A = g^r,B=g^s, C=Pk_0^r*m, D=Pk_1^s*m, E=g^u, F=g^v$$
$$ G=pk_0^u*PK_1^{-v}, t=Hash(E,F,G), H=u+t*r, I=v+t*s $$
という10本の式ができました。登場人物としては、$g$は初めに共有された値、$r,s,u,v$はその場で決まるランダムな値、$m$は平文、暗号文は$A,B,C,D,E,F,G,H,I$です。ただ、$Pk_i$は公開鍵ですが今回は具体的な値はわかりません
。
この時、$Pk_i$がわかってないので、$C,D$の値を私たちが構成できません…omg悲しいですね…
では、これの何が数学的な脆弱性or実装の脆弱性なんでしょうか?それを考える前に、一つこれがAES, RSAなどとは違う点を先に考えてみます。
これは簡単で、同じ平文を暗号化するたびに暗号文の値が変化します。
これが大きな違いです。$r,s,u,v$はその場で決まるランダムな値なために起こることですね。
ということは、こちらで制御できる可能性ってあるにはあるんですよね。。。例えば、$$A = g^r$$を例にとって考えます。$A^x$の値が欲しいなと思った時は右辺の値をどうすればよいですか…?
$$A^x = (g^r)^x=g^{xr}$$でいいわけです。ということは、$A$に対して$x$を操作することで、$r$に大きな影響が出そうですよね?
とはいってもです。$xr$の値が分かるというわけではないです。
$$g^{xr}$$がわかるということです。
じゃあどうすればよいか?わからないなら消せばいいじゃないか
です。掛け算で全てを無に帰す最強の数がありますよね?そう、$0$です。なので、$r=0$とすると$$A^x=g^{xr}=g^{0*r}=1$$より間接的に$r$の影響を無視することができます!!
では、この話を全ての乱数に適応すると??
$$A = 1\B=1\C=m\D=m\E=1\F=1\G=1*1\t=Hash(E,F,G)\H=0\I=0$$
になりますね??、オーマイが、$C=m$よりさっきは作ることが難しかったですが、今回は簡単に作れそうですね!なので、平文$m$に対して、暗号文は$(1,1,m,m,1,1,0,0)$というばかげた話になります。
やった、これで第一段階突破!
Evalの突破
次は、Eval関数をうまく使ってFlagを何とかしようぜってことです。
1 | elif choice == 'e': |
eval(userDecrypt.decode())
とあるので、例えば私たちが送る暗号文をFlag[0]
とすると、eval(Flag[0])
ではFlagに格納している先頭文字列が返却されます。なので、response
に入る文字列はB
となりますが…
んーーー、これresponseに入った結果を再度暗号化してるじゃないか...
え、どうしよ案件です。
ここで、最初の問題のコードに立ち戻ります、私達が情報を得ることができるものって何がありますか??ということです。
1 | print('|\n| LEAK = {}'.format(leak)) # leakの結果 |
この3つがprintのの中に変数を格納して結果を返してくれるやつです。
あれ、無理やりエラーを吐かせれば3つ目のもので結果得られるんじゃね??omg
でも無理やりとはいってもどの場所ではかっせかなぁです。
吐かせる場所はここです。
1 | response = eval(userDecrypt.decode()) |
仮に、平文が”abc”とするとeval(abc)した結果は、name 'abc' is not defined
より、エラーからevalの内容がわかるということです。
ただ数値の場合は、 cannot convert 'int' object to bytes
という怒られ方をしてます。これはエラーの吐く場所が違いますが結果は使えないので無視します。
まとめると
- ある平文を$(1,1,m,m,1,1,0,0)$で暗号文を偽装
- 平文は
flag[:3]
といった形をとれば、eval(flag[:3])
はBHF
と変えてくれます(Flagに格納されている文字列の先頭3bytes)が、私たちが見れるものは暗号化が施されている… - 2の暗号文を見破るためにerror based oracleを構成し、flagの文字列を得る
- Good Game yay!!
宿題
この最後のエラー部分がなくなった場合はどう解くのだろうか??
予想solve数[13-14]程度(2減)
1 | #!/usr/bin/env python3 |