生日攻击及CTF例子

生日攻击及CTF例子

I n s t r u c t i o n Instruction Instruction

以生日悖论为基础的生日攻击,实际上是碰撞攻击的一种,其主要思想就是在一个随机集合中找到两个相同点(给定一个点的大小找是否有另一个点)的概率一般来说很大(有一个近似计算公式),以此概率来判断是否遍历爆破整个集合找出两个相同点(概率很大的情况下,很可能不止两个点相同)

近似计算公式
P = 1 − e − n ( n − 1 ) 2 N P= 1- e^{-\frac{n(n-1)}{2N}} P=1e2Nn(n1)
其中 P P P是指存在两个相同点的概率,对应的, e − n ( n − 1 ) 2 N e^{-\frac{n(n-1)}{2N}} e2Nn(n1)即是整个集合中不存在两个相同点的概率; n n n是指该随机集合目前实际的总个数, N N N是指在该随机情况的条件下最多有 N N N种不同的可能

生日攻击一般都用作哈希碰撞,构造彩虹表等等,这里我们用浅显的生日悖论的原理来在给定一个点的情况下,来判断是否可以找到一个随机集合中另一个相同点

E x a m p l e Example Example

P r o b l e m Problem Problem

来源于:第五空间CTF 2022 vgcd

from random import getrandbits, seed
from Crypto.Util.number import getPrime, inverse, bytes_to_long, isPrime
from os import urandom
from secret import flag

def sample(rho, eta, gamma, p):
    seed(urandom(8))
    return p * getrandbits(gamma - eta) + getrandbits(rho)

def gen_vector(rho, eta, gamma, m, p, K):
    v = vector([sample(rho, eta, gamma, p) for i in range(m)])
    return K*v 
    
rho = 6
m = 3
eta = 288
gamma = 512
p = getPrime(gamma)
q = getPrime(gamma)
n = p * q
e = 65537
phi = (p - 1) * (q - 1)
c = pow(bytes_to_long(flag), e, n)
print(n)
print(c)

pp = p >> (gamma - eta)
assert isPrime(pp)
K = Matrix.random(Integers(pp), m, m).lift()
t1 = [gen_vector(rho, eta, gamma, m, pp, K) for i in range(1024)]
print(t1)
for i in range(6):
    t2 = [gen_vector(rho, eta, gamma, m, pp, K) for i in range(512)]
    print(t2)

A n a l y s i s Analysis Analysis

题目关键是找到pp,而围绕pp题目生成了一大堆由随机数作为基底的数(但是这里不能用随机数内置的算法逆向求解,因为每次生成几个随机数都会重新设置seed

我们把打印出来的t1, t2的生成过程(这两个列表的生成过程是一样的)用数学符号表达出来
K ⋅ V = ( k 0 k 1 k 2 k 3 k 4 k 5 k 6 k 7 k 8 ) ( v 0 v 1 v 2 ) = ( k 0 v 0 + k 1 v 1 + k 2 v 2 k 3 v 0 + k 4 v 1 + k 5 v 2 k 6 v 0 + k 7 v 1 + k 8 v 2 ) \begin{array}{ll}\pmb{K}\cdot \pmb{V} &= \begin{pmatrix} k_0 \qquad k_1 \qquad k_2 \\ k_3 \qquad k_4 \qquad k_5\\ k_6 \qquad k_7 \qquad k_8 \end{pmatrix} \begin{pmatrix} v_0 \quad v_1 \quad v_2 \end{pmatrix} \\ &= \begin{pmatrix} k_0v_0 + k_1v_1 +k_2v_2\qquad k_3v_0+k_4v_1+k_5v_2 \qquad k_6v_0+k_7v_1+k_8v_2 \end{pmatrix} \end{array} KKVV= k0k1k2k3k4k5k6k7k8 (v0v1v2)=(k0v0+k1v1+k2v2k3v0+k4v1+k5v2k6v0+k7v1+k8v2)

这里的 K , V \pmb{K,V} K,VK,V分别是矩阵和向量,这里进行的并不是矩阵乘法,当前类型的矩阵和向量左乘后得到的就是如上表达式

其中 K \pmb{K} KK在有所有的t1, t2中的生成过程是一样的,而 V \pmb{V} VV是在随机生成的;

我们仔细查看 V \pmb{V} VV的生成过程(由于随机数生成大小的参数是给定了的,这里直接用数值表示随机数大小)

V = ( v 0 v 1 v 2 ) = ( p p ⋅ r 224 + r 6 p p ⋅ r 224 ′ + r 6 ′ p p ⋅ r 224 ′ ′ + r 6 ′ ′ ) \pmb{V} = \begin{pmatrix} v_0 & v_1 & v_2 \end{pmatrix} =\begin{pmatrix} pp \cdot r_{224} + r_6 \qquad pp \cdot r'_{224} + r'_6 \qquad pp \cdot r''_{224} + r''_6 \end{pmatrix} VV=(v0v1v2)=(ppr224+r6ppr224+r6ppr224′′+r6′′)

其中 r i r_i ri表示 i i i bits大小的随机数,将 V \pmb{V} VV的表达式代入 K ⋅ V \pmb{K}\cdot \pmb{V} KKVV

K ⋅ V = ( k 0 ( p p ⋅ r 224 + r 6 ) + k 1 ( p p ⋅ r 224 ′ + r 6 ′ ) + k 2 ( p p ⋅ r 224 ′ ′ + r 6 ′ ′ ) ⋯ ⋯ ) \pmb{K}\cdot \pmb{V}= \begin{pmatrix} &k_0(pp \cdot r_{224} + r_6) + k_1(pp \cdot r'_{224} + r'_6) +k_2( pp \cdot r''_{224} + r''_6) \qquad \cdots \qquad \cdots& \end{pmatrix} KKVV=(k0(ppr224+r6)+k1(ppr224+r6)+k2(ppr224′′+r6′′))
单独看第一项
k 0 ( p p ⋅ r 224 + r 6 ) + k 1 ( p p ⋅ r 224 ′ + r 6 ′ ) + k 2 ( p p ⋅ r 224 ′ ′ + r 6 ′ ′ ) = ( k 0 ⋅ r 224 + k 1 ⋅ r 224 ′ + k 2 ⋅ r 224 ′ ′ ) ⋅ p p + ( k 0 r 6 + k 1 r 6 ′ + k 2 r 6 ′ ′ ) k_0(pp \cdot r_{224} + r_6) + k_1(pp \cdot r'_{224} + r'_6) +k_2( pp \cdot r''_{224} + r''_6) \qquad \\ = (k_0\cdot r_{224} + k_1 \cdot r'_{224} + k_2 \cdot r''_{224})\cdot pp + (k_0r_6+k_1r'_6+k_2r''_6) k0(ppr224+r6)+k1(ppr224+r6)+k2(ppr224′′+r6′′)=(k0r224+k1r224+k2r224′′)pp+(k0r6+k1r6+k2r6′′)
此时我们就可以应用生日攻击的思想了,单独看上式的 ( k 0 r 6 + k 1 r 6 ′ + k 2 r 6 ′ ′ ) (k_0r_6+k_1r'_6+k_2r''_6) (k0r6+k1r6+k2r6′′) ,如果我们能在t1, t2的所有输出中找到两个相同的 r 6 , r 6 ′ , r 6 ′ ′ r_6, r'_6, r''_6 r6,r6,r6′′,那么我们就可以将两者相减,得到一个这样的表达式 x ⋅ p p x\cdot pp xpp ,同时,使用相同的 r 6 , r 6 ′ , r 6 ′ ′ r_6, r'_6, r''_6 r6,r6,r6′′ 总共有三组( K ⋅ V \pmb{K}\cdot \pmb{V} KKVV ),每组之间相减都可以得到表达式 x ′ ⋅ p p x'\cdot pp xpp,将三组之间的相减结果取最大公因数即可得到 p p pp pp (很大概率 p p pp pp 应该是 x ⋅ p p x\cdot pp xpp 中最大的素因子)

实际寻找相同的 r 6 , r 6 ′ , r 6 ′ ′ r_6, r'_6, r''_6 r6,r6,r6′′过程中,当然是先确定一个t1, t2的结果,再遍历剩余的t1, t2中的每一个结果

t1, t2总共有4096组结果,每组结果中有三项数值,我们来计算一下生日攻击的成功概率

r 6 , r 6 ′ , r 6 ′ ′ r_6, r'_6, r''_6 r6,r6,r6′′的所有可能应该有 2 18 2^{18} 218种,而当前的结果有4096种;相当于 N = 2 18 , n = 4096 N = 2^{18},n = 4096 N=218n=4096 ,代入生日攻击概率的近似计算公式
P = 1 − e − 4096 ( 4096 − 1 ) 2 ⋅ 2 18 = 1 − 1.2763491830534272 e − 14 \begin{array}{ll} P &= 1 - e^{-\frac{4096(4096-1)}{2\cdot 2^{18}}}\\ &=1 - 1.2763491830534272e^{-14} \end{array} P=1e22184096(40961)=11.2763491830534272e14

sage: float(e^(-(4096*(4096-1)) / (2^19)))
1.2763491830534272e-14

所以几乎是百分百会有碰撞情况发生

得到 p p pp pp 之后,将其按照高位攻击的思路恢复原始的 p p p 即可,具体使用small_roots 函数的一种方法可以看 (10条消息) 2022 川渝网安比赛_初赛Crypto复现_M3ng@L的博客-CSDN博客 里的一道例题

S o l u t i o n Solution Solution

# type:ignore
from Crypto.Util.number import *
from tqdm import trange
import gmpy2

with open("output3.txt") as f:
    input = [line.strip() for line in f.readlines()]
n = int(input[0])
c = int(input[1])
cipher = []
for i in input[2:]:
    for j in eval(i):
        cipher.append(j)
# print(len(cipher))

# for i in trange(len(cipher)):
#     for j in range(i, len(cipher)):
#         temp = [cipher[i][k] - cipher[j][k] for k in range(3)]
#         gcd = [gmpy2.gcd(temp[0], temp[1]), gmpy2.gcd(temp[0], temp[2]), gmpy2.gcd(temp[1], temp[2])]
#         for k in gcd:
#             if k.bit_length() == 288:
#                 print(k)
# 356748247030693416847133214972226346960895831956739561317797640603898384278276333472137
pp = 356748247030693416847133214972226346960895831956739561317797640603898384278276333472137
eta = 288
gamma = 512
e = 0x10001
R.<x> = PolynomialRing(Zmod(n))
f = (pp << (gamma - eta)) + x
p = int((pp << (gamma - eta)) + f.small_roots(beta=0.49, epsilon=0.02)[0])
assert n % p == 0
q = n // p
phi = (p-1) * (q-1)
d = gmpy2.invert(e, phi)
m = pow(c, d, n)
print(long_to_bytes(m).decode())

R e f e r e n c e Reference Reference

(10条消息) 【2022 第五空间】5_vgcd WriteUP_Mr_AgNO3的博客-CSDN博客

你可能感兴趣的:(密码学知识总结,Crypto,生日攻击)