ByteCTF 2022
Choose_U_Flag
分析:
一.非预期解
1.首先再熟悉一下ntru加密系统,这里直接搬图了:
2.task.py的逻辑分析
设置了signal.alarm(30),限制第一次输入不能太慢;随机选取了12个字符作为key,用ntru加密,然后允许用户输入密文解密,有106次机会。非预期的产生主要是由于接口对于输入的过滤不严格,仅仅过滤了key的密文(从某种角度上讲还真像真实场景里出现的漏洞)。我们已知加密过程为\(e\equiv r\cdot h+m(mod q)\),解密为\(a \equiv p \cdot r \cdot g + m \cdot f(mod q)\),\(a \cdot F_p \equiv m \cdot f \cdot F_p \equiv m(mod p)\);那么只需要把密文e改为-e,解密过程为\(a \cdot F_p \equiv (-m)\cdot f \cdot(F_p) \equiv m(mod p)\),那么我们在绕过过滤条件的同时得到了-m,那么再把-m变号转为字节发送给Server即可。
exp:
l= [-18, -22, 21, 27, 5, 3, 20, -2, -6, -18, -29, -24, -23, -9, -18, 1, 2, -10, -8, 23, 25, -12, -7, 5, -4, 20, -19, 26, -23, 10, 31, 1, 3, -1, -4, 15, -4, 10, 15, -21, 27, 1, -25, -9, -12, 7, 0, -8, 14, -31, 14, 23, -11, -1, -18, -14, 7, -17, -3, 1, -31, 1, 1, 13, -6, -6, -29, -23, -27, 12, 18, -19, -14, 27, -4, 23, 18, 32, -17, 8, -22, -24, 16, 25, 17, 21, -1, -17, 8, -29, -7, -18, 9, -12, -14, -22, 5, -19, -12, -24, -3, -4, 0, -8, 4, -2, 16]
print([(-1)*i for i in l])
l1 = [-1, -1, -1, -1, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, -1, 0, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, -1, 0, -1, 0, -1, -1, 0, -1, 0, -1, -1, 0, -1, 0, -1, -1, 0, -1, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0, -1, -1, 0, 0, 0, -1, -1, -1, -1, -1, -1]
l2 = [(-1)*i for i in l1]
print(l2)
m = [0 , 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1]
print(len(m))
m_str = [str(i) for i in m]
print(m_str)
for i in range(0,96,8):
tmp = int(''.join(m_str[i:i+8]),2)
print(tmp)
print(chr(tmp))
预期解
选择密文攻击(CCA),详细分析过程见官方wp,这里给出我的exp:
import gmpy2
import numpy as np
from sympy import GF, ZZ, Poly, invert
from sympy.abc import x # 符号类,即未知数
from pwn import *
N = 107
p = 3
q = 64
R_poly = Poly(x ** N - 1, x).set_domain(ZZ)
def is_2_power(n):
return n != 0 and (n & (n - 1) == 0)
# 求环下的模逆
def invert_poly(f_poly, R_poly, p):
if gmpy2.is_prime(p):
inv_poly = invert(f_poly, R_poly, domain=GF(p))
elif is_2_power(p):
inv_poly = invert(f_poly, R_poly, domain=GF(2))
e = int(gmpy2.log2(p))
for i in range(1, e):
inv_poly = ((2 * inv_poly - f_poly * inv_poly ** 2) %
R_poly).trunc(p)
else:
raise Exception("Cannot invert polynomial in Z_{}".format(p))
return inv_poly
def decrypt(f_poly,cipher_poly,Fp):
a_poly = ((f_poly * cipher_poly) % R_poly).trunc(q)
b_poly = a_poly.trunc(p)
return ((Fp * b_poly) % R_poly).trunc(p)
#context(log_level='debug')
CHALLENGE_ID = 'a8367b702df28efe1e66986d6561b22d'
io = remote(CHALLENGE_ID + '.2022.capturetheflag.fun', 1337, ssl=True)
recv_h = io.recvline()
h_poly = eval(recv_h[11:-1])
key_poly = eval(io.recvline()[15:-1])
key_poly = Poly(key_poly,[x])
print(key_poly)
print(io.recvuntil(b'>'))
h = Poly(h_poly, [x])
c = 9
ch = c*h+c
#print(ch)
k1 = ch.all_coeffs()
#print(k1)
io.sendline(str(k1))
dec = eval(io.recvline()[20:-1])
dec_Poly = Poly(dec,[x])
print(dec_Poly)
for i in range(107):
tmp = Poly(x**i, [x])*(-q)
#print(tmp)
Fp = dec_Poly*invert_poly(tmp,R_poly,p)
#print(Fp)
f = invert_poly(Fp,R_poly,p)
#print(f)
result = decrypt(f,key_poly,Fp)
l = result.all_coeffs()
print(l)
l1 = [str(abs(i)) for i in l]
tmps = ''.join(l1)
print(tmps)
# for i in range(0, 96, 8):
# tmp = int(''.join(tmps[i:i + 8]), 2)
# print(chr(tmp),end='')
# print()
Compare
分析:
一个同态加密系统,构造绕过方式如下:
\(A=g^ar_1^n(mod~n^2)\)
\(B=g^br_2^n(mod~n^2)\)
\(=>AB^{-1}g^{2^{512}}=g^{2^{512}+a-b}r^n(mod~n^2)\)
\(=>dec(AB^{-1}g^{2^{512}})=2^{512}+a-b\)
故\(2^{512}>MSG<=>b>a\)
exp:
from pwn import *
from Crypto.Util.number import *
context(log_level='debug')
CHALLENGE_ID = 'd40cf531845ef6ab8442dcf68f51fc5b'
p = remote(CHALLENGE_ID + '.2022.capturetheflag.fun', 1337, ssl=True)
p.sendline(b"2**512<MSG")
for i in range(100):
print(f'第{i}轮')
p.recvuntil(b'n = ')
n = int(p.recvline()[:-1])
p.recvuntil(b'g = ')
g = int(p.recvline()[:-1])
p.recvuntil(b'a = ')
a = int(p.recvline()[:-1])
p.recvuntil(b'b = ')
b = int(p.recvline()[:-1])
sign = a * inverse(b, n ** 2) * pow(g, 2 ** 512, n ** 2) % n ** 2
p.sendline(str(sign))
p.interactive()