”*“ 表示未做出的题,“~” 表示赛后复现出的题,“!” 表示赛中做出的题
1,check-little (!)
题目
from Crypto.Util.number import *from Crypto.Util.Padding import padfrom Crypto.Cipher import AESimport os
flag, key = open('secret').read().split('\n')
e = 3
while 1:    p = getPrime(1024)    q = getPrime(1024)    phi = (p - 1) * (q - 1)    if phi % e != 0:        breakN = p * qc = pow(key, e, N)
iv = os.urandom(16)ciphertext = AES.new(key = long_to_bytes(key)[:16], iv = iv, mode = AES.MODE_CBC).encrypt(pad(flag.encode(),16)).hex()
f = open('output.txt', 'w')f.write(f'N = {N}\n')f.write(f'c = {c}\n')f.write(f'iv = {iv}\n')f.write(f'ciphertext = {ciphertext}\n')分析
(emmm,为啥会有人的key是p啊????)
所以直接gcd(c, n)就有key了,然后AES解密即可
EXP
from Crypto.Util.number import *from Crypto.Util.Padding import padfrom Crypto.Cipher import AESfrom sage.all import *
n = 18795243691459931102679430418438577487182868999316355192329142792373332586982081116157618183340526639820832594356060100434223256500692328397325525717520080923556460823312550686675855168462443732972471029248411895298194999914208659844399140111591879226279321744653193556611846787451047972910648795242491084639500678558330667893360111323258122486680221135246164012614985963764584815966847653119900209852482555918436454431153882157632072409074334094233788430465032930223125694295658614266389920401471772802803071627375280742728932143483927710162457745102593163282789292008750587642545379046283071314559771249725541879213c = 10533300439600777643268954021939765793377776034841545127500272060105769355397400380934565940944293911825384343828681859639313880125620499839918040578655561456321389174383085564588456624238888480505180939435564595727140532113029361282409382333574306251485795629774577583957179093609859781367901165327940565735323086825447814974110726030148323680609961403138324646232852291416574755593047121480956947869087939071823527722768175903469966103381291413103667682997447846635505884329254225027757330301667560501132286709888787328511645949099996122044170859558132933579900575094757359623257652088436229324185557055090878651740iv = b'\x91\x16\x04\xb9\xf0RJ\xdd\xf7}\x8cW\xe7n\x81\x8d'ciphertext = "bf87027bc63e69d3096365703a6d47b559e0364b1605092b6473ecde6babeff2"
key = gcd(c, n)aes = AES.new(key = long_to_bytes(key)[:16], iv = iv, mode = AES.MODE_CBC)aes.decrypt(bytes.fromhex(ciphertext))2,ezran (!)
题目
from Crypto.Util.number import *from random import *
f = open('flag.txt', 'r')flag = f.read().encode()
gift=b''for i in range(3108):    r1 = getrandbits(8)    r2 = getrandbits(16)    x=(pow(r1, 2*i, 257) & 0xff) ^ r2    c=long_to_bytes(x, 2)    gift+=c
m = list(flag)for i in range(2025):    shuffle(m)
c = "".join(list(map(chr,m)))
f = open('output.txt', 'w')f.write(f"gift = {bytes_to_long(gift)}\n")f.write(f"c = {c}\n")分析
题目类似鸡块师傅的这题:CatCTF2024-Random game↗
所以对于本题而言就是:
1,当是64的倍数时,
(pow(r1, 2*i, 257) & 0xff) ^ r2相当于0/1 ^ r2,因此这时候 的前15bit是已知的;2,当i是128的倍数时,
(pow(r1, 2*i, 257) & 0xff) ^ r2相当于1 ^ r2,因此这时候 是已知的;3,其余情况下,因为
(pow(r1, 2*i, 257) & 0xff)最大也只是一个8bit的数,所以(pow(r1, 2*i, 257) & 0xff) ^ r2相当于0~255 ^ r2,因此这时候 的前8bit是能确定的。
因此我们可以根据这个造出对应的MT矩阵,但是并不是一个满秩矩阵(需要才行,但我们的是19932),因此——假如我们是直接解矩阵方程的话,还需要对求出的结果的右核空间进行爆破。
但我没用这个方法,我这里是使用了gf2bv↗库来做这题;同样,因为前面的原因,我们这里就需要使用solve_all()函数来求解出所有可能解(假如结果唯一,可以用solve_one())。
EXP
import randomfrom gf2bv import LinearSystemfrom gf2bv.crypto.mt import MT19937from tqdm import *from pwn import *from Crypto.Util.number import *
def mt19937(bs, out):    lin = LinearSystem([32] * 624)    mt = lin.gens()
    rng = MT19937(mt)    zeros = []    for o in range(len(out)):        _ = rng.getrandbits(8)        if o%128 == 0:            zeros.append((rng.getrandbits(16)) ^ (out[o]^1))        elif o%64 == 0:            zeros.append((rng.getrandbits(16)>>1) ^ (out[o]>>1))        else:            zeros.append((rng.getrandbits(16)>>8) ^ (out[o]>>8))    zeros += [mt[0] ^ (0x80000000)]    # zeros += [mt[0] ^ (0x00000000)]    sol = lin.solve_all(zeros)
    return sol
if(1):    gift = ""    sys.set_int_max_str_digits(14990)    gift = long_to_bytes(int(gift))    cs = [bytes_to_long(gift[i:i+2]) for i in range(0, len(gift), 2)]    RNGs = mt19937(int(10), cs)    for RNG in tqdm(RNGs):        flag = 1        rng = MT19937(RNG).to_python_random()        for i in range(3108):            r1 = rng.getrandbits(8)            r2 = rng.getrandbits(16)            x = (pow(r1, 2*i, 257) & 0xff) ^ r2            if x != cs[i]:                flag = 0                break        if flag:            c = ")9Lsu_4s_eb__otEli_nhe_tes5gii5sT@omamkn__ari{efm0__rmu_nt(0Eu3_En_og5rfoh}nkeoToy_bthguuEh7___u"            x = [i for i in range(len(c))]            for i in range(2025):                rng.shuffle(x)            flag = ""            for i in range(len(c)):                flag += c[x.index(i)]            print(flag)# flag{7hE_numbEr_0f_biT5_i5_Enou9h_@L5o_ThE_r4nk_must_3n0ugh}3,sk (~)
题目
from random import randintfrom Crypto.Util.number import getPrime, inverse, long_to_bytes, bytes_to_longfrom math import gcdimport signalfrom secret import flag
def gen_coprime_num(pbits):    lbits = 2 * pbits + 8    lb = 2**lbits    ub = 2**(lbits + 1)    while True:        r = randint(lb, ub)        s = randint(lb, ub)        if gcd(r, s) == 1:            return r, s
def mult_mod(A, B, p):    result = [0] * (len(A) + len(B) - 1)    for i in range(len(A)):        for j in range(len(B)):            result[i + j] = (result[i + j] + A[i] * B[j]) % p
    return result
def gen_key(p):    f = [randint(1, 2**128) for i in ":)"]    h = [randint(1, 2**128) for i in ":("]
    R1, S1 = gen_coprime_num(p.bit_length())    R2, S2 = gen_coprime_num(p.bit_length())
    B = [[randint(1, p - 1) for i in ":("] for j in ":)"]
    P = []    for b in B:        P.append(mult_mod(f, b, p))
    Q = []    for b in B:        Q.append(mult_mod(h, b, p))
    for i in range(len(P)):        for j in range(len(P[i])):            P[i][j] = P[i][j] * R1 % S1            Q[i][j] = Q[i][j] * R2 % S2
    sk = [(R1, S1), (R2, S2), f, h, p]    pk = [P, Q, p]
    return sk, pk
def encrypt(pk, pt):    P, Q, p = pk    pt = bytes_to_long(pt)
    PP = 0    QQ = 0
    for i in range(len(P)):        u = randint(1, p)        for j in range(len(P[0])):            PP = PP + P[i][j] * (u * pt**j % p)            QQ = QQ + Q[i][j] * (u * pt**j % p)
    return PP, QQ
def decrypt(sk, ct):    RS1, RS2, f, h, p = sk    R1, S1 = RS1    R2, S2 = RS2
    P, Q = ct    invR1 = inverse(R1, S1)    invR2 = inverse(R2, S2)    P = (P * invR1 % S1) % p    Q = (Q * invR2 % S2) % p
    f0q = f[0] * Q % p    f1q = f[1] * Q % p    h0p = h[0] * P % p    h1p = h[1] * P % p
    a = f1q + p - h1p % p    b = f0q + p - h0p % p
    pt = -b * inverse(a, p) % p    pt = long_to_bytes(pt)
    return pt
signal.alarm(30)p = getPrime(256)sk, pk = gen_key(p)ticket = long_to_bytes(randint(1, p))enc_ticket = encrypt(pk, ticket)print(pk)print(enc_ticket)
for i in range(2):    op = int(input("op>").strip())    if op == 1:        msg = input("pt:").strip().encode()        ct = encrypt(pk, msg)        print(f"ct: {ct}")    elif op == 2:        user_input = input("ct:").strip().split(" ")        if len(user_input) == 2:            ct = [int(user_input[0]), int(user_input[1])]        else:            print("invalid ct.")            break
        user_input = input("your f:").strip().split(" ")        if len(user_input) == 2:            user_f = [int(user_input[0]), int(user_input[1])]        else:            print("invalid f.")            break
        user_input = input("your h:").strip().split(" ")        if len(user_input) == 2:            user_h = [int(user_input[0]), int(user_input[1])]        else:            print("invalid h.")            break
        user_input = input("your R1 S1:").strip().split(" ")        if len(user_input) == 2:            user_r1s1 = [int(user_input[0]), int(user_input[1])]        else:            print("invalid R1 S1.")            break
        user_input = input("your R2 S2:").strip().split(" ")        if len(user_input) == 2:            user_r2s2 = [int(user_input[0]), int(user_input[1])]        else:            print("invalid R2 S2.")            break
        pt = decrypt((user_r1s1, user_r2s2, user_f, user_h, p), ct)        if pt == ticket:            print(flag)        else:            print(pt.hex())    else:        print("invalid op.")        break
print("bye!")分析
当时赛中可能是看傻了。。。现在赛后跟别的师傅聊了后,复现出来了。
题目要求我们提交解密参数及密文,使得decrypt(enc)=token。
而题目的关键是加密函数:
def encrypt(pk, pt):    P, Q, p = pk    pt = bytes_to_long(pt)
    PP = 0    QQ = 0
    for i in range(len(P)):        u = randint(1, p)        for j in range(len(P[0])):            PP = PP + P[i][j] * (u * pt**j % p)            QQ = QQ + Q[i][j] * (u * pt**j % p)
    return PP, QQ转换成式子如下:
用 来替代的话,则为:
因为我们知道 、 的值以及和,而且的大小也不大,所以我们完全可以造格(类似背包格的造法)或者cuso来解出 ,继而求出pt(也就是我们的token)。这里我采用的是cuso。
又因为我们的解密是需要我们输入解密参数的,所以我们可以自己生成一个公私钥对,将我们算出的token用我们的公钥加密,然后返回我们的新密文及对应的私钥即可。
EXP
from cuso import find_small_rootsfrom sage.all import *from Crypto.Util.number import *from math import gcdfrom random import randintfrom pwn import *
def gen_coprime_num(pbits):    lbits = 2 * pbits + 8    lb = 2**lbits    ub = 2**(lbits + 1)    while True:        r = randint(lb, ub)        s = randint(lb, ub)        if gcd(r, s) == 1:            return r, s
def mult_mod(A, B, p):    result = [0] * (len(A) + len(B) - 1)    for i in range(len(A)):        for j in range(len(B)):            result[i + j] = (result[i + j] + A[i] * B[j]) % p
    return result
def gen_key(p):    f = [randint(1, 2**128) for i in ":)"]    h = [randint(1, 2**128) for i in ":("]
    R1, S1 = gen_coprime_num(p.bit_length())    R2, S2 = gen_coprime_num(p.bit_length())
    B = [[randint(1, p - 1) for i in ":("] for j in ":)"]
    P = []    for b in B:        P.append(mult_mod(f, b, p))
    Q = []    for b in B:        Q.append(mult_mod(h, b, p))
    for i in range(len(P)):        for j in range(len(P[i])):            P[i][j] = P[i][j] * R1 % S1            Q[i][j] = Q[i][j] * R2 % S2
    sk = [(R1, S1), (R2, S2), f, h, p]    pk = [P, Q, p]
    return sk, pk
def encrypt(pk, pt):    P, Q, p = pk    pt = bytes_to_long(pt)
    PP = 0    QQ = 0
    for i in range(len(P)):        u = randint(1, p)        for j in range(len(P[0])):            PP = PP + P[i][j] * (u * pt**j % p)            QQ = QQ + Q[i][j] * (u * pt**j % p)
    return PP, QQ
sh = process(["python", "task.py"])pk = eval(sh.recvline().strip().decode())enc_ticket = eval(sh.recvline().strip().decode())P,Q,p = pkPP,QQ = enc_ticket
# cusoupt0, upt1, upt2, upt3, upt4, upt5 = var('upt0, upt1, upt2, upt3, upt4, upt5')f1 = P[0][0]*upt0 + P[0][1]*upt1 + P[0][2]*upt2 + P[1][0]*upt3 + P[1][1]*upt4 + P[1][2]*upt5 - PPf2 = Q[0][0]*upt0 + Q[0][1]*upt1 + Q[0][2]*upt2 + Q[1][0]*upt3 + Q[1][1]*upt4 + Q[1][2]*upt5 - QQ
relations = [f1,f2]bounds = {upt0:(0, 2**256), upt1:(0,p), upt2:(0,p), upt3:(0,2**256), upt4:(0,p), upt5:(0,p)}roots = find_small_roots(    relations,    bounds,)
for root in roots:    u0 = root[upt0]    u1 = root[upt3]    m1 = root[upt1] * inverse(u0,p) % p    m2 = root[upt4] * inverse(u1,p) % p    if m1 == m2:        sk1, pk1 = gen_key(p)        RS1, RS2, f, h, pp = sk1        R1,S1 = RS1        R2,S2 = RS2        ct1 = encrypt(pk1,long_to_bytes(m1))        sh.sendlineafter(b"op>",b'2')        sh.sendlineafter(b"ct:",f"{ct1[0]} {ct1[1]}".encode())        sh.sendlineafter(b"your f:",f"{f[0]} {f[1]}".encode())        sh.sendlineafter(b"your h:",f"{h[0]} {h[1]}".encode())        sh.sendlineafter(b"your R1 S1:",f"{R1} {S1}".encode())        sh.sendlineafter(b"your R2 S2:",f"{R2} {S2}".encode())        print(sh.recvline().strip().decode())4,Blurred (~)
题目
from sage.all import *from sage.misc import prandomimport randomfrom Crypto.Cipher import AESfrom Crypto.Hash import SHA256from Crypto.Util.Padding import pad
q = 1342261n = 1031PR = PolynomialRing(Zmod(q), "x")x = PR.gens()[0]Q = PR.quotient(x**n + 1)flag = b"flag{*****************************}"
def sample(rand):    return (rand.getrandbits(1) - rand.getrandbits(1)) * (rand.getrandbits(1) * rand.getrandbits(1))
def GenNTRU():    f = [sample(prandom) for _ in range(n)]    g1 = [sample(prandom)for _ in range(n)]    g2 = [sample(prandom) for _ in range(n)]    g1x = Q(g1)    g2x = Q(g2)
    while True:        fx = Q(f)        try:            h1 = g1x / fx            h2 = g2x / fx            return (h1.lift(), h2.lift(), fx)        except:            prandom.shuffle(f)            continue
for _ in range(20259):    c = int(input("c :"))    if c == 1:        coin = random.getrandbits(1)        if coin == 0:            pk1, pk2, fx = GenNTRU()        else:            pk1, pk2, fx = Q.random_element().lift(), Q.random_element().lift(), Q([sample(prandom) for _ in range(n)])
        print("Hints:", pk1.list(), pk2.list())
    elif c == 2:        SHA = SHA256.new()        SHA.update(str(random.getrandbits(256)).encode())        KEY = SHA.digest()        cipher = AES.new(KEY, AES.MODE_ECB)        flag = pad(flag, AES.block_size)        print("Flag:", cipher.encrypt(flag))    else:        break分析
虽然一眼就能看出——需要我们收集足够的bit来打MT19937,但是赛中没想出如何区分NTRU,所以就没出。之后跟别的师傅聊了下,然后复现出来了。
区分的关键是那个环:
因为的不可约多项式是,里边有这个因子,且是这个多项式的根,所以可以有这样一个映射,将环映射到整数域上:
因此我们可以将 和 映射成 和 ,造这样的格:
然后LLL取第一行即可。
而经过测试会发现:如果是NTRU,LLL出来的结果会很小;因此取一个100的界限即可。
不过后面打MT19937的时候,使用gf2bv会存在 mt[0]的高位取0与1的问题,所以为稳妥起见,我两种都尝试了。
EXP
from sage.all import *from pwn import *from tqdm import trangefrom gf2bv import LinearSystemfrom gf2bv.crypto.mt import MT19937from Crypto.Util.number import *from Crypto.Cipher import AESfrom Crypto.Hash import SHA256
q = 1342261n = 1031PR = PolynomialRing(Zmod(q), "x")x = PR.gens()[0]Q = PR.quotient(x**n + 1)
sh = process(["python", "task.py"])
bits = []for i in trange(19968):    sh.sendlineafter(b"c :",b'1')    res = sh.recvline().strip().decode().split(':')[-1]    start1 = res.find('[')    end1 = res.find(']')    h1 = Q(eval(res[start1:end1+1]))    start2 = res.find('[',end1)    end2 = res.find(']',end1+1)    h2 = Q(eval(res[start2:end2+1]))    h1_value = h1.lift()(-1)    h2_value = h2.lift()(-1)    L = Matrix(ZZ,[        [1,h1_value,h2_value],        [0,-q,0],        [0,0,-q]    ])    line = L.LLL()[0]    if int(line.norm()) < 100:        bits.append(0)    else:        bits.append(1)
sh.sendlineafter(b"c :",b'2')sh.recvuntil(b"Flag: ")enc_flag = eval(sh.recvline().strip())print(f"enc_flag = {enc_flag}")print(f"bits = {bits}")
def mt19937(out, ch):    hig = [int(0x00000000), int(0x80000000)]    lin = LinearSystem([32] * 624)    mt = lin.gens()
    rng = MT19937(mt)    zeros = []    for o in out:        zeros.append(rng.getrandbits(1) ^ int(o))    zeros.append(mt[0] ^ hig[ch])
    for sol in lin.solve_all(zeros):        rng = MT19937(sol)        pyrand = rng.to_python_random()        RNG = pyrand        STATE = RNG.getstate()[1][:-1]        STATE = STATE + (len(STATE),)        RNG.setstate((3,STATE,None))
        for i in trange(19968):            RNG.getrandbits(1)
        SHA = SHA256.new()        SHA.update(str(RNG.getrandbits(256)).encode())        KEY = SHA.digest()        cipher = AES.new(KEY, AES.MODE_ECB)        flag = cipher.decrypt(enc_flag)        if b"flag" in flag:            print(f"high = {ch}")            print(f"{flag = }")
RNG = mt19937(bits, 0)RNG = mt19937(bits, 1)