ByteCTF 2022

Choose_U_Flag

分析:
一.非预期解
1.首先再熟悉一下ntru加密系统,这里直接搬图了:

image
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()
posted @ 2022-09-30 21:50  ZimaB1ue  阅读(121)  评论(0编辑  收藏  举报