网鼎杯-Qualifier-2022/玄武组/Crypto/crypto557(new_stream) Writeup
网鼎杯-Qualifier-2022/玄武组/Crypto/crypto557(new_stream) Writeup
1. challenge
附件下载:https://pan.baidu.com/share/init?surl=Rar0fxHzpDiL4bK8AP2Zbw 提取码:GAME
new_stream.py
from Crypto.Util.number import *
import random
from secret import flag
f = open("output.txt","w")
for i in range(18):
p = getPrime(256)
x = random.randint(2, p-1)
tmp = bytes_to_long(flag)
enc = []
for j in range(size(tmp)):
r = random.randint(2, p-1)
if tmp%2:
enc += [pow(x, r, p)]
else:
enc += [r]
tmp //= 2
f.write("p = " + str(p) + "\n")
f.write("enc = " + str(enc) + "\n")
output.txt 太长了,本文就不贴出来了。
2. 分析
阅读代码,进行了18轮加密。
每一轮都重新生成随机数$x_i$,对flag的每一bit $m_{j}$,生成随机数$r_{ij}$进行加密:
$$
c_{ij}=\left{
\begin{aligned}
& x_i^{r_{ij}} \mod p, & m[j]%2==1 \
& r_{ij}, & m[j]%2 == 0
\end{aligned}
\right.
$$
我首先尝试了一下能否对每一轮的 $x_i^{r_{ij}} \mod p$,根据多组(m, c)还原出x,尝试无果。
大概是还原不出来$x_i$了,于是考虑能否根据数论的一些性质,将$x_i^{r_{ij}} \mod p$和$r_{ij}$区分开来。
因为之前做过一道二次剩余相关的题目VolgaCTF2020Qualifier Crypto Guess writeup,所以我尝试了二次剩余:
在GF(p)上,有(p-1)/2个二次剩余。
所以$x_i$ 有一半的可能满足:
$$
x_i^{\frac{p-1}{2}} \equiv 1 \mod p \
\Rightarrow (x_i^{\frac{p-1}{2}})^{r_{ij}} \equiv 1 \mod p \
\Rightarrow c_{ij}^{r_{ij}} \equiv 1 \mod p
$$
即,如果某一轮的$x_i$是p的二次剩余,则该轮使用模幂加密的密文都是p的二次剩余。而,对任意随机数$r_{ij}$是不满足该特点的。
根据这一特点,因为我们有18组数据,可以分析出flag每一位二进制bit的奇偶性的统计学规律。
我们可以生成一些数据验证一下。
import random
p = random_prime(2L**256,2L**255)
x1 = random.randint(2, p-1)
print(f"x is quadratic residue?: {pow(x1,(p-1)//2,p)==1}")
r = []
for i in range(18):
r.append(random.randint(2, p-1))
c = []
for i in range(18):
c.append(pow(x1, r[i], p))
print("---------")
for i in range(len(c)):
print(f"ci is quadratic residue?: {pow(c[i], (p-1)//2, p)==1}")
print("---------")
for i in range(len(c)-1):
print(f"ri is quadratic residue?: {pow(r[i], (p-1)//2, p)==1}")
x is quadratic residue?: True
---------
ci is quadratic residue?: True
ci is quadratic residue?: True
ci is quadratic residue?: True
ci is quadratic residue?: True
ci is quadratic residue?: True
ci is quadratic residue?: True
ci is quadratic residue?: True
ci is quadratic residue?: True
ci is quadratic residue?: True
ci is quadratic residue?: True
ci is quadratic residue?: True
ci is quadratic residue?: True
ci is quadratic residue?: True
ci is quadratic residue?: True
ci is quadratic residue?: True
ci is quadratic residue?: True
ci is quadratic residue?: True
ci is quadratic residue?: True
---------
ri is quadratic residue?: False
ri is quadratic residue?: False
ri is quadratic residue?: True
ri is quadratic residue?: True
ri is quadratic residue?: False
ri is quadratic residue?: True
ri is quadratic residue?: False
ri is quadratic residue?: False
ri is quadratic residue?: False
ri is quadratic residue?: True
ri is quadratic residue?: False
ri is quadratic residue?: False
ri is quadratic residue?: False
ri is quadratic residue?: True
ri is quadratic residue?: False
ri is quadratic residue?: True
ri is quadratic residue?: True
首先我想要确定哪些轮次的x是p的二次剩余,因为x为非二次剩余的轮次,明文的奇偶性没有统计学规律。
统计每一轮密文是二次剩余的概率
- 对于x是p的二次剩余的轮次,概率应约等于$p(bin(m[i])==1)*1/2+p(bin(m[i])==0)\approx 3/4 $
- 对于x不是p的二次剩余的轮次,概率应约等于$p(bin(m[i])==1)*1/2+p(bin(m[i])==0)*1/2 \approx 1/2$
因而可以得知x是p的二次剩余的轮次。
对于这些轮次,对x是p的二次剩余的轮次,统计每一位密文在这些轮次上是二次剩余的概率,明文的奇偶性有明显的统计学规律:
- 明文的二进制bit为奇数,密文必然是p的二次剩余
- 明文的二进制bit为偶数,密文有1/2的概率是二次剩余
3. exp
import json
from Crypto.Util.number import *
def load():
cipher = []
p = []
lines = open("output.txt", "rb").readlines()
for i in range(0, len(lines), 2):
p.append(Integer(lines[i].strip(b"p = ").strip(b"\n")))
cipher.append(json.loads(lines[i+1].strip(b"enc = ").strip(b"\n")))
return cipher, p
def quadratic_residue_round(cipher, p):
count = []
for i in range(18):
c = 0
for j in range(len(cipher[0])):
if pow(cipher[i][j], (p[i]-1)//2, p[i])==1:
c += 1
count.append(c)
print(f"quadratic_residue count of : {count}")
# the round that x is quadratic residue
q_round = []
for i in range(18):
if count[i] > len(cipher[0])*2//3:
q_round.append(i)
print(f"these rounds has quadratic residue x: {q_round}")
return q_round
def guess_flag(cipher, p, q_round):
binary = []
for i in range(len(cipher[0])):
odd = True
for j in q_round:
if pow(cipher[j][i], (p[j]-1)//2, p[j])!=1:
odd = False
break
if odd:
binary.insert(0, '1')
else:
binary.insert(0, '0')
return binary
cipher, p = load()
q_round = quadratic_residue_round(cipher, p)
binary = guess_flag(cipher, p, q_round)
flag=long_to_bytes(int("".join(binary), 2))
print(flag)
quadratic_residue count of : [247, 244, 236, 252, 180, 254, 256, 168, 247, 247, 174, 243, 242, 253, 167, 159, 256, 242]
these rounds has quadratic residue x: [0, 1, 2, 3, 5, 6, 8, 9, 11, 12, 13, 16, 17]
b'flag{c8b10fb7-95f8-47a2-bfb3-b9489ad39711}'
本文同步发表于
- 博客: https://ssst0n3.github.io/
- 公众号: 石头的安全料理屋