2024.07.09 某内部赛

8abyRSA

题面

from Crypto.Util.number import getPrime, bytes_to_long
from LCGRandom import lcg

flag = b"DASCTF{xxxx}"
m = bytes_to_long(flag)
q = getPrime(512)
p = getPrime(512)
Q = pow(q, 2, p)  # Q = q^2 mod p


n = p*q
e = getPrime(512)
c = pow(m, e, n)
print('c = ' + str(c))

mul = p
n = e
c = Q


lcg1 = lcg(2023, mul, n, c)
print(lcg1.generate(6))

# Data
# c = 14968774972802568447907734980942381885170753466134942777553237930032293557412276601708303806296932877792965437305452274767013479132983066980557420348521340748653518789837988349171582558831336243036976332252071289409948879158346086243451785505819420501498240344839999096449458990633841326838003742773196414924
# [1829291767649461103161355195365566822501625317566021355553611375584303624765389047237072963403651896280059309840080549881138083110457632256619677785411889, 8614784647131959905489143385414473366220060118109917221659356152996027731619256154868253763867569947876938642677951734943013867186530789820165520197525548, 7845112879564311528346861967994968539141359532994486000551038947867807602664345244223622318955917969589790931596150225393028516278795565957075910272882168, 464696663556289552651401659156226806419638205935729756668755007052945250273596319671267391086858010496056118762904740103810918505838370732992982394379753, 6368769952056267497431070536699202267447921981615807369090285698513090854334005214066202915932322373348204466447234330868540529876844912401907159350901200, 4401214958628797991805307100256403706658940216552816808894942142670769563264366171079551655390635613048161714797548328158857275108940238519111063046341755]

c = 14968774972802568447907734980942381885170753466134942777553237930032293557412276601708303806296932877792965437305452274767013479132983066980557420348521340748653518789837988349171582558831336243036976332252071289409948879158346086243451785505819420501498240344839999096449458990633841326838003742773196414924
lcg_gen_6 = [1829291767649461103161355195365566822501625317566021355553611375584303624765389047237072963403651896280059309840080549881138083110457632256619677785411889, 8614784647131959905489143385414473366220060118109917221659356152996027731619256154868253763867569947876938642677951734943013867186530789820165520197525548, 7845112879564311528346861967994968539141359532994486000551038947867807602664345244223622318955917969589790931596150225393028516278795565957075910272882168,
             464696663556289552651401659156226806419638205935729756668755007052945250273596319671267391086858010496056118762904740103810918505838370732992982394379753, 6368769952056267497431070536699202267447921981615807369090285698513090854334005214066202915932322373348204466447234330868540529876844912401907159350901200, 4401214958628797991805307100256403706658940216552816808894942142670769563264366171079551655390635613048161714797548328158857275108940238519111063046341755]

seed = 2023

Exploit

SageMath's Code

from sage.all import *
import random
from Crypto.Util.number import long_to_bytes

lcg_gen_6 = [1829291767649461103161355195365566822501625317566021355553611375584303624765389047237072963403651896280059309840080549881138083110457632256619677785411889, 8614784647131959905489143385414473366220060118109917221659356152996027731619256154868253763867569947876938642677951734943013867186530789820165520197525548, 7845112879564311528346861967994968539141359532994486000551038947867807602664345244223622318955917969589790931596150225393028516278795565957075910272882168,
             464696663556289552651401659156226806419638205935729756668755007052945250273596319671267391086858010496056118762904740103810918505838370732992982394379753, 6368769952056267497431070536699202267447921981615807369090285698513090854334005214066202915932322373348204466447234330868540529876844912401907159350901200, 4401214958628797991805307100256403706658940216552816808894942142670769563264366171079551655390635613048161714797548328158857275108940238519111063046341755]

"""
seed = 2023

x = [seed]
x = x + lcg_gen_6
"""
x = lcg_gen_6

x1 = x[0]
x2 = x[1]
x3 = x[2]

t = []

for i in range(1, len(x)):
    t.append(x[i] - x[i-1])

# 恢复 Modulus
m = 0
for i in range(1, len(t)-1):
    m = GCD(t[i+1]*t[i-1] - t[i]*t[i], m)
print(f'[+] modulus: {m}')
n = m

# 恢复 Multiplier, Increment (a, b)
R = Zmod(n)
a = R(x[2] - x[1]) / (x[1] - x[0])
b = R(x[1] - a * x[0])
print(f'[+] a = {a}\n[+] b = {b}')

# 也许参数位置不对
p = ZZ(a)
e = ZZ(b)
Q = ZZ(n)

# Q \equiv q^2 \pmod{p}
# 求二次剩余, 返回空, 大概率位置错了
# []

# check location
print(f'[*] Parameters location check:\t{is_prime(p)} {is_prime(e)} {is_prime(Q)}')
# 1 0 1
# p, q, e 均为 512-bits 素数, 而上面第二个返回值为 False, 则说明位置错了
Q, e = e, Q

# 判二次剩余
if not legendre_symbol(Q, p) == 1:
    print('[-] No solution')
else:
    print('[+] Found solution')

# Solution quadratic residue
# Modulus is prime, So
# Cipolla Algorithm

class CipollaAlgorithm:
    def __init__(self, p):
        self.p = p  # 模数p

    def mul(self, a, b, w):
        # 复数乘法
        x = (a[0] * b[0] + a[1] * b[1] * w) % self.p
        y = (a[0] * b[1] + a[1] * b[0]) % self.p
        return (x, y)

    def qpow_r(self, a, b):
        # 实数快速幂
        res = 1
        while b:
            if b & 1:
                res = res * a % self.p
            a = a * a % self.p
            b >>= 1
        return res

    def qpow_i(self, a, b, w):
        # 复数快速幂
        res = (1, 0)
        while b:
            if b & 1:
                res = self.mul(res, a, w)
            a = self.mul(a, a, w)
            b >>= 1
        return res[0]

    def cipolla(self, n):
        n %= self.p
        if self.qpow_r(n, (self.p - 1) // 2) == self.p - 1:
            return -1  # 没有解

        while True:
            a = random.randint(0, self.p-1)
            w = (a*a - n) % self.p
            if self.qpow_r(w, (self.p - 1) // 2) == self.p - 1:
                break
        
        x = (a, 1)
        return self.qpow_i(x, (self.p + 1) // 2, w)

# Q = q^2 mod p
p = p 
n = Q
cipolla = CipollaAlgorithm(p)
ans1 = cipolla.cipolla(n)
ans2 = (-ans1) % p

ans = []

if ans1 == -1:
    print("[-] No solution")
else:
    if ans1 > ans2:
        ans1, ans2 = ans2, ans1
    if ans1 == ans2:
        ans.append(ans1)
    else:
        ans.append(ans1)
        ans.append(ans2)

# check prime
for i in ans:
    if not is_prime(i):
        # print(f'[-] {i} is not prime')
        pass
    else:
        q = i
        print(f'[+] {i} is prime')
        
print('[*] DONE Quadratic Residue')
        
phi = (p - 1) * (q - 1)
d = inverse_mod(e, phi)

c = 14968774972802568447907734980942381885170753466134942777553237930032293557412276601708303806296932877792965437305452274767013479132983066980557420348521340748653518789837988349171582558831336243036976332252071289409948879158346086243451785505819420501498240344839999096449458990633841326838003742773196414924

m = pow(c, d, p * q)
print(f'[*] 全因子解密: \n{long_to_bytes(m)}')

# uuid 长 42 bytes
# DASCTF{} 长 8 bytes
# 50 * 8 = 400 bits < 512 bits
# 单因子解密
phi = p - 1
d = inverse_mod(e, phi)
m = power_mod(c, d, p)
print(f'[*] 单因子解密: \n{long_to_bytes(m)}')

"""
inva = inverse(a, n)
x1 = x[0]
for i in range(300):
    x1 = (x1 - b) * inva % n
    try:
        flag = long_to_bytes(x1)
        if b'flag{' in flag:
            print(flag)
    except Exception as err:
        print(err)
        continue
"""