CRYPTO
百万赏金
任务描述.txt:
干员,本次的行动任务非常艰巨,我们截获了博士的实验数据,但是这个文件被加密了,请你认真搜索,搞定就撤。 已知实验数据加密规则严格遵循以下顺序执行: 第一轮:对原始 Flag 执行凯撒密码加密(偏移量未知,偏移范围 1
10,仅对字母字符移位) 第二轮:将凯撒加密后的字符串执行栅栏密码加密(密钥未知,密钥范围 24,加密方式为按W型读取,且标准型带有两个特殊字符) 密文:DFGNBSZNGNMKFF
按任务描述倒推就行了呗。key = 4的 W 型栅栏求解:
轨道编号: 0 1 2 3 2 1 0 1 2 3 2 1 0 1 字符序列: D N G F N B F S M F K Z G N
rail 0: D F G rail 1: N B S Z N rail 2: G N M K rail 3: F F
得到第一轮结束第二轮前的密文:DNGFNBFSMFKZGN。然后凯撒爆破一下在shift = 5时找到flag。
flag{YIBAIWANHAFUBI}
博士的实验数据
题目:
题干 密文:QJBXQJFXZAKL已知为仿射密码加密,26 个大写英文字母映射规则不变:A=0,B=1,C=2,…,Z=25,加密核心公式:y ≡ (a·x + b) mod 26(x = 明文字母值,y = 密文字母值)。无直接密钥,仅给出 2 组明密文对应提示: 明文T 对应 密文X 明文F 对应 密文J 附加要求:仿射密码中a 必须与 26 互质(必要条件,需验证),请先推导密钥a、b,再解出完整明文。
关键提示 解同余方程组:将两组明密文值代入加密公式,得到两个模 26 等式,两式相减消去 b,先求 a,再回代求 b; 互质性验证:gcd (a,26)=1(最大公约数为 1),否则仿射密码无唯一解; 模逆元求解:扩展欧几里得算法,核心要求a·a⁻¹ ≡ 1 mod 26; 解密公式:x ≡ a⁻¹·(y - b) mod 26,若y-b为负数,先加 26 再计算,最终结果仍取模 26; 字母值速查:F=5,T=19,J=9,X=23(对应提示明密文)。
仿射加密,通过已知明密文对,可以求出a = 1, b = 4,a = 1其实就是凯撒,直接解就行。
exp.py:
c = "QJBXQJFXZAKL"flag = "flag{"for i in c: flag += chr(((ord(i) - ord("A") - 4) % 26) + ord("A"))flag += "}"print(flag)# flag{MFXTMFBTVWGH}冰原上的OTP谜题
冰原上的OTP谜题:
小冰狐想用一次性密码本(OTP)加密 “winter_polarctf”,但它在生成密钥时犯了个有趣的错误:它把明文每个字符的 ASCII 值拆成二进制后,将第 i 位(从 0 开始,LSB 为第 0 位)与密钥对应位做了异或,却误将所有位的异或结果按 “第 7 位→第 0 位” 的顺序拼接成了密文。 已知: 明文:winter_polarctf(ASCII 编码,共 16 字节) 密钥生成规则:由 “ice” 的 ASCII 值(0x69, 0x63, 0x65)重复拼接至 16 字节 密文二进制串(按错误顺序拼接,共 16×8=128 位): 11010110 10010110 11101001 10101100 01100101 01001011 10111001 11011011 01110110 01011001 11001101 10110101 11001011 10001101 01011101 11101011 请还原正确的加密过程,计算出按正确顺序(第 0 位→第 7 位)拼接的密文二进制对应的十六进制,即为 flag(格式:十六进制小写,无空格)。
文字游戏么,只需要将密文逆转一下正确拼接并转成十六进制就行。
exp.py:
cipher_bits = """11010110 10010110 11101001 10101100 01100101 01001011 10111001 1101101101110110 01011001 11001101 10110101 11001011 10001101 01011101 11101011"""groups = cipher_bits.split()ans = ''.join(f'{int(b[::-1], 2):02x}' for b in groups)print(f'flag{{{ans}}}')# flag{6b699735a6d29ddb6e9ab3add3b1bad7}RC4的密钥流泄露
题目.txt:
某设备采用RC4流密码加密传输数据,安全测试人员获取到一组“部分明文-密文对”及目标密文(目标密文对应明文为flag)。已知RC4加密为明文与密钥流逐字节异或,且两组数据使用相同密钥加密,可通过已知信息推导密钥流,进而破解目标密文。 已知信息:
- 加密算法:RC4流密码(密钥长度为8字节,可打印ASCII字符;加密/解密均为 明文字节 ⊕ 密钥流字节 = 密文字节,⊕表示异或运算);
- 核心特性:同一密钥生成的密钥流固定,且密钥流与明文长度一致;异或运算可逆(a⊕b=c → c⊕b=a、c⊕a=b),手动计算即可推导;
- 已知信息:
- 已知明文片段(P):TestData_ForRC4_Decrypt(共22字节,对应密文前22字节);
- 对应密文片段(C):54 65 73 74 44 61 74 61 5F 46 6F 72 52 43 34 5F 44 65 63 72 79 70(十六进制,空格分隔字节,共22字节);
- 目标密文(C_flag):66 6C 61 67 7B 70 6F 6C 61 72 5F 6B 69 6E 67 6B 69 6E 67 7D(十六进制,共20字节,对应明文为flag);
- 公钥指数 e = 17
- 模数 n = 20099
- 目标密文 c = 15523(修正为flag加密后的真实密文,替代原错误c=12345)
- 补充说明:RC4密钥流生成与明文无关,仅由密钥决定;本题无需推导完整密钥,仅需通过已知明文-密文对推导对应长度密钥流,即可解目标密文。 异或运算规则:0⊕0=0、0⊕1=1、1⊕0=1、1⊕1=0,十六进制转十进制后计算,结果再转回十六进制/ASCII。
。这不是没加密吗,最后 RC4 异或的是0,给的对应密文还少了一个字节。。。
exp.py:
P = "TestData_ForRC4_Decrypt"c = ""for i in P: c += hex(ord(i))[2:]C = "54 65 73 74 44 61 74 61 5F 46 6F 72 52 43 34 5F 44 65 63 72 79 70".replace(" ", "")assert C == c[:-2].upper()C_flag = "66 6C 61 67 7B 70 6F 6C 61 72 5F 6B 69 6E 67 6B 69 6E 67 7D"print(bytes.fromhex(C_flag).decode())# flag{polar_kingking}伪ASR
题目:
from sympy import isprimefrom sympy.ntheory import legendre_symbolimport randomfrom Crypto.Util.number import bytes_to_long
k=79 #<-- i couldn't stress more
def get_p(): global k while True: r=random.randint(2**69,2**70) p=2**k*r+1 if isprime(p): return p else: continue
def get_q(): while True: r=random.randint(2**147,2**148) q=4*r+3 if isprime(q): return q else: continue
def get_y(): global n,p,q while True: y=random.randint(0,n-1) if legendre_symbol(y,p)==1: continue elif legendre_symbol(y,q)==1: continue else: return y
flag=b'flag{redacted:)}'flag_pieces=[flag[0:10],flag[11:21],flag[22:32],flag[33:43],flag[44:]]#assert int(bytes_to_long((flag_pieces[i] for i in range(5)))).bit_length()==k
p=get_p()q=get_q()n=p*qprint(f'{n=}')
y=get_y()print(f'{y=}')
def encode(m):
global y,n,k x = random.randint(1, n - 1) c=(pow(y,m,n)*pow(x,pow(2,k),n))%n return c
cs=[]for i in range(len(flag_pieces)): ci=encode(bytes_to_long(flag_pieces[i])) cs.append(ci)
print(f'{cs=}')
'''# n = ...# y = ...# cs = [...]'''加密核心代码如下:
p = 2^k * r + 1q = 4*r + 3n = p*qc = y^m * x^(2^k) mod n其中我们已知:k = 79,且y满足:
同时p也是一个特殊的素数满足:
说明模p下的乘法群 含有一个阶为2^79的子群。
加密形式是一个典型的高次剩余类加密:
观察n:
也就是说:
并且位数也不大,用factordb分解就行(CopperSmith 是不是应该也行虽然我没打出来哦莫)。
接着我们取r = (p - 1) // (2 ** 79),在模p下:
而前面已经说过:
因此:
随机项x就被消除了。
然后我们设:
而由于y在p,q下均为非二次剩余,则:
- 模
p时,y的指数为奇数,可推出y^r的阶恰好为2^79。
因此整个表达式都落在该子群中,有:
离散对数在该子群中计算,指数按模2^79计算的:
也就是说对c^r%p的离散对数的结果就是m。
exp.py:
# SageMath 10.7from Crypto.Util.number import long_to_bytesfrom sage.all import *
n = ...y = ...cs = [ ...]k = 79p = 685956097824618861007209469433124282167197697q = 729686531647380216431209698235054102301315851r = (p - 1) // (2 ** k)Fp = GF(p)g = Fp(pow(y, r, p))flag = b""for i, c in enumerate(cs): c1 = Fp(pow(c, r, p)) m = discrete_log(c1, g) flag += long_to_bytes(m)print(flag.decode())# flag{go0_j06!let1sm0v31n_t0_th3renges~>_<}ECC的攻击模块
题目:
from Crypto.Util.number import bytes_to_longfrom hashlib import sha256from os import urandom# from secret import p, a, b, flagp=getPrime(512)flag=b'flag{??????????????}'a=randint(1,p-1)b=randint(1,p-1)ECC = EllipticCurve(GF(p), [a, b])R, E, C = [ECC.random_point() for _ in range(3)]M=Matrix(ZZ,len(flag),3)pad = lambda m: urandom(8) + m + b'\x00' * (ZZ(p).nbits() // 8 - len(m) - 8 - 1)out = list()for i in range(len(flag)): m = pad(chr(flag[i]).encode()) nonce = urandom(16) sh = sha256(nonce + m).digest() Q = b2l(m)*R + b2l(nonce)*E + b2l(sh)*C out.append(Q)原题么。。传送门:【巅峰极客 2023】Rosita
题目先随机生成p,a,b作为曲线ECC的参数,并在曲线上随机取了R,E,C三点,但是参数和坐标均未知。接着自定义了pad方式:
pad = lambda m: urandom(8) + m + b'\x00' * (ZZ(p).nbits() // 8 - len(m) - 8 - 1)对flag的每一个字节进行如上方式的填充,并在每一次加密过程中都生成一个临时字节流nonce,并计算nonce + pad(flag[i])的sha256哈希值,最后给出对应的点Qi坐标:
要求我们通过所有Qi坐标还原出m。
首先肯定得先还原出曲线ECC的参数p,a,b,曲线方程为:
我们可知Qi也均为曲线上的点且我们有很多组,故可以Qi写作:
这里可以仿照无参数的LCG的思路,通过消元的方法最后能够通过gcd打出p,然后再代回原模方程中求解a和b。这种方法上面传送门中鸡块师傅的推导过程已经很清晰了,尝试一下梭哈的办法求 Gröbner 基来还原a,b,p参数。
recover_Parameters.sage:
from sage.all import *from itertools import combinationsfrom math import gcd
ns = {}with open("坐标.txt", "r", encoding="utf-8") as f: exec(f.read(), {}, ns)coords = [tuple(map(Integer, P)) for P in ns["x"]]kps = []for idx in combinations(range(len(coords)), 3): cc = None (x1, y1), (x2, y2), (x3, y3) = coords[idx[0]], coords[idx[1]], coords[idx[2]] R = PolynomialRing(ZZ, ['a', 'b', 'p', 'k1', 'k2', 'k3'], order='lex') a, b, p, k1, k2, k3 = R.gens() f1 = y1 ** 2 - x1 ** 3 - a * x1 - b - k1 * p f2 = y2 ** 2 - x2 ** 3 - a * x2 - b - k2 * p f3 = y3 ** 2 - x3 ** 3 - a * x3 - b - k3 * p G = Ideal([f1, f2, f3]).groebner_basis() for g in reversed(G): cc = abs(Integer(g.constant_coefficient())) if cc != 0: break if cc is not None: kps.append(cc) for i in range(len(kps)): for j in range(i + 1, len(kps)): g = Integer(gcd(int(kps[i]), int(kps[j]))) if g.bit_length() <= 512: print("p =", g) p = g u1 = (pts[0][1]^2 - pts[0][0]^3) % p u2 = (pts[1][1]^2 - pts[1][0]^3) % p a = ((u2 - u1) * inverse_mod(pts[1][0] - pts[0][0], p)) % p b = (u1 - a*pts[0][0]) % p print("a =", a) print("b =", b) raise SystemExit"""p = 11093300438765357787693823122068501933326829181518693650897090781749379503427651954028543076247583697669597230934286751428880673539155279232304301123931419a = 490963434153515882934487973185142842357175523008183292296815140698999054658777820556076794490414610737654365807063916602037816955706321036900113929329671b = 7668542654793784988436499086739239442915170287346121645884096222948338279165302213440060079141960679678526016348025029558335977042712382611197995002316466"""恢复参数了之后拿到原曲线,但是我们测试后发现这是一个异常椭圆曲线:E.order() = p:
p = 11093300438765357787693823122068501933326829181518693650897090781749379503427651954028543076247583697669597230934286751428880673539155279232304301123931419a = 490963434153515882934487973185142842357175523008183292296815140698999054658777820556076794490414610737654365807063916602037816955706321036900113929329671b = 7668542654793784988436499086739239442915170287346121645884096222948338279165302213440060079141960679678526016348025029558335977042712382611197995002316466E = EllipticCurve(GF(p), [a, b])print(E.order())# 11093300438765357787693823122068501933326829181518693650897090781749379503427651954028543076247583697669597230934286751428880673539155279232304301123931419这时候我们就可以通过 Smart Attack 攻击将椭圆曲线上的离散对数问题转成模p上的普通属于问题:
对于任选生成元G,对每个输出点Qi求:
类比Qi,我们可以令:
则原椭圆曲线上的式子可变为:
接下来注意一下mi的pad结构:
下面我们令pad(flag[i]) = mi * 2 ** 432,我们现在可以根据上面的等式找到矩阵的等式:
我们记上述等式为:
我们的目标并非直接求出X,而是想办法恢复所有的mi。
注意到矩阵M的列数只有 3,因此它的左核空间维度很大。如果取一个向量
该向量满足
那么左乘原式可得
由此可得
也就是说,任何与M的三列都正交的向量,也一定与D在模p意义下正交。反过来说,我们虽然并不知道M,但是可以先从已知的D出发,去构造所有满足
的整数向量。这样的向量空间中会包含真正与M正交的那些关系。接下来再通过核空间把M反推出来。
接下来我们要构造与D正交的格,我们要找的是整数向量v,使得
等价存在整数t使得
于是可以构造如下格基:
这里K是一个足够大的权重。我们对格向量:
进行格基规约,得到最后的一个坐标就是
当这个格向量很短是,最后一维也会倾向于比较小;而由于这一维被乘上了很大的K,最优情况下就会逼迫
成立,即
所以,对这个格做 LLL 之后,得到的短向量前 73 维就会给出一批与D模p正交的整数关系。接着取这些短向量的前 70 行(经验做法)为矩阵R,也就是选取一批较短的正交关系,其中每行都满足
并且我们希望其中包含足够多的来自M左核空间的关系。而前面提到过:真正与M的三列正交的向量,一定也会满足与D模p正交。因此,R的行空间中应当包含M的左核中的许多向量。如果某一向量x是M的一列,则对于所有这些左核向量v都满足
那么M的三列向量都应落在R的右核中,于是我们直接求R的右核获得所有满足
的向量空间。然后其中很明显m列的向量是其中最短的一列,我们对右核基再做一次 LLL 就会得到这一列,再取最低有效字节即可获得flag。
exp.py:
# SageMath 10.7from Crypto.Util.number import *from random import randint
def SmartAttack(P, Q, p): E = P.curve() Eqp = EllipticCurve(Qp(p, 2), [ZZ(t) + randint(0, p)*p for t in E.a_invariants()])
P_Qps = Eqp.lift_x(ZZ(P.xy()[0]), all=True) for P_Qp in P_Qps: if GF(p)(P_Qp.xy()[1]) == P.xy()[1]: break
Q_Qps = Eqp.lift_x(ZZ(Q.xy()[0]), all=True) for Q_Qp in Q_Qps: if GF(p)(Q_Qp.xy()[1]) == Q.xy()[1]: break
p_times_P = p * P_Qp p_times_Q = p * Q_Qp
x_P, y_P = p_times_P.xy() x_Q, y_Q = p_times_Q.xy()
phi_P = -(x_P / y_P) phi_Q = -(x_Q / y_Q)
k = phi_Q / phi_P return ZZ(k)
ns = {}with open("坐标.txt", "r", encoding="utf-8") as f: exec(f.read(), {}, ns)coords = [tuple(map(Integer, P)) for P in ns["x"]]p = 11093300438765357787693823122068501933326829181518693650897090781749379503427651954028543076247583697669597230934286751428880673539155279232304301123931419a = 490963434153515882934487973185142842357175523008183292296815140698999054658777820556076794490414610737654365807063916602037816955706321036900113929329671b = 7668542654793784988436499086739239442915170287346121645884096222948338279165302213440060079141960679678526016348025029558335977042712382611197995002316466E = EllipticCurve(Zmod(p), [a, b])G = E.gens()[0]d = []for Pxy in coords: Q = E(Pxy) d.append(int(SmartAttack(G, Q, p)))n = len(coords)L = Matrix(ZZ, n + 1, n + 1)K = 2 ** 100for i in range(n): L[i, i] = 1 L[i, -1] = d[i] * KL[-1, -1] = p * Kres = L.LLL()R = Matrix(ZZ, 70, n)for i in range(70): R[i] = res[i][:-1]basis = R.right_kernel().basis()basis = Matrix(ZZ, basis)v = basis.LLL()[0]flag = b""for x in v: flag += long_to_bytes(int(x & 0xff))print(flag.decode())# Congratulations! Your flag is: flag{893d041e-c0a2-3145-5320-cdee7d3c87fb}Some information may be outdated









