Mini L-CTF 2023

算得上国内比较高质量的比赛,五个题做了四个。丢一个官方wp以示敬意。

not_RSA

应该是非预期解。用羊城杯的思路打的,就是类似于维纳的二元copper求p+q,最后分解n解密。

c = (99256707703226697226473841185259891785249728547098403329816239886722383460401685922071518907503131597586071155779535217957728713395973126772467862964939878117327514388525570332680833383088079421676354296281469250418264543833996288111463346112204924207384792233847819302304599120532752360882845527395569869907, 22655358796075424487044406387957775030913109276145369023351200306937368259451273503046617611110582153415768404632774105652118572620829335937285604752846386248015325031053581797994703852239663030464437053164169557845378554791579176562234005623449839772205446210182378591192462742536627314113540667791362602148)
n = 103255210447201501371417366314698617128571899104178153644502440939179707560694633551313596814867085426522157527557873368089757491021794381392940645658031944937376477744644393844781470315770893253591718873148298034783254899285894192568113349056391974188706470251298810392910953025658474958447750644663120809161
e = 9583844349143763216577056030562049770207021495053166931622586942299664240152962005166123845294740910682002626836306629541171186155613228080333603389256263599077945629292275075204305859845966709343064493385561097725880568307482154382068336740221552489762224156862649726139521041709368241009505934006275050727466118506845275244191749935821428533956123008950814817446865191293160484499980805375127998664220058827288306710393417375308743911041896280674963812278295706552776413678414777273337864851457733395645762523718466931204393235542589232058708808567310860905450262329471814920834990514208969596816366119172152233564
r = 8989358155637718504643502172367267711566059539795670198816016094340804453065250030031846883560365745256555868280844477116616537047437144736243403626554094

import itertools

def small_roots(f, bounds, m=1, d=None):
        if not d:
                d = f.degree()

        R = f.base_ring()
        N = R.cardinality()

        f /= f.coefficients().pop(0)
        f = f.change_ring(ZZ)

        G = Sequence([], f.parent())
        for i in range(m+1):
                base = N^(m-i) * f^i
                for shifts in itertools.product(range(d), repeat=f.nvariables()):
                        g = base * prod(map(power, f.variables(), shifts))
                        G.append(g)

        B, monomials = G.coefficient_matrix()
        monomials = vector(monomials)

        factors = [monomial(*bounds) for monomial in monomials]
        for i, factor in enumerate(factors):
                B.rescale_col(i, factor)

        B = B.dense_matrix().LLL()

        B = B.change_ring(QQ)
        for i, factor in enumerate(factors):
                B.rescale_col(i, 1/factor)

        H = Sequence([], f.parent().change_ring(QQ))
        for h in filter(None, B*monomials):
                H.append(h)
                I = H.ideal()
                if I.dimension() == -1:
                        H.pop()
                elif I.dimension() == 0:
                        roots = []
                        for root in I.variety(ring=ZZ):
                                root = tuple(R(root[var]) for var in f.variables())
                                roots.append(root)
                        return roots
        return []
a=n+1
b=n^2-n+1
delta=int(1024*0.300)
PR.<x,y>=PolynomialRing(Zmod(e))
f=x*(y^2 + a*y + b) + 1 
bounds=(2^delta,2^512)
print(small_roots(f,bounds,m=3,d=5))

预期解参考Improving small private exponent attack on the Murru-Saettone cryptosystem中的第三种攻击,照着实现即可:

from not_RSA import *
from Crypto.Util.number import *
from tqdm import trange
from sage.all import *
c = (99256707703226697226473841185259891785249728547098403329816239886722383460401685922071518907503131597586071155779535217957728713395973126772467862964939878117327514388525570332680833383088079421676354296281469250418264543833996288111463346112204924207384792233847819302304599120532752360882845527395569869907, 22655358796075424487044406387957775030913109276145369023351200306937368259451273503046617611110582153415768404632774105652118572620829335937285604752846386248015325031053581797994703852239663030464437053164169557845378554791579176562234005623449839772205446210182378591192462742536627314113540667791362602148)
n = 103255210447201501371417366314698617128571899104178153644502440939179707560694633551313596814867085426522157527557873368089757491021794381392940645658031944937376477744644393844781470315770893253591718873148298034783254899285894192568113349056391974188706470251298810392910953025658474958447750644663120809161
e = 9583844349143763216577056030562049770207021495053166931622586942299664240152962005166123845294740910682002626836306629541171186155613228080333603389256263599077945629292275075204305859845966709343064493385561097725880568307482154382068336740221552489762224156862649726139521041709368241009505934006275050727466118506845275244191749935821428533956123008950814817446865191293160484499980805375127998664220058827288306710393417375308743911041896280674963812278295706552776413678414777273337864851457733395645762523718466931204393235542589232058708808567310860905450262329471814920834990514208969596816366119172152233564
p0 = 8989358155637718504643502172367267711566059539795670198816016094340804453065250030031846883560365745256555868280844477116616537047437144736243403626554094

M = n^2 - ((n+1)//2)^2 - n + 1 + (p0 + n//p0 + (n+1)//2)^2

def factorize_extend3(e, N, M):
    f = continued_fraction(e/(M+1))
    try:
        Len = min(len(f),10000)
    except:
        Len = 10000
    for i in trange(Len+1):
        a = f.numerator(i)
        b = f.denominator(i)
        if gcd(a,e*b-1) == a and a != 1:
            S = var('S')
            phi_N = (e*b-1)//a
            eqn = S^2 + (N+1)*S + N^2 - N + 1 - phi_N
            sols = solve(eqn == 0, S, solution_dict=True)
            for sol in sols:
                tmp = sol[S]
                if tmp.is_integer():
                    x = var('x')
                    eqn1 = x^2 - tmp*x + N
                    sols1 = solve(eqn1 == 0, x, solution_dict=True)
                    p = int(sols1[0][x])
                    if gcd(p,N) == p:
                        return (b,p,N//p)
    return None
d,p,q = factorize_extend3(e,n,M)
assert p*q == n
m = special_power(c, d, n)
m = (long_to_bytes(m[0]),long_to_bytes(m[1]))
print(m[0][:m[0].find(b'#')]+m[1][:m[1].find(b'#')])

guess

参考安全客,本题的p很小并且基本都是光滑的,在计算离散对数的时候更容易,为了方便处理把矩阵对角线位置搞成2。我的exp:

from sage.all import *

a = [422188638284971, 567993961287239, 64518145072391, 189406615756847, 25277183320159, 549010515148831, 40204590272807, 31963903326139, 605589542022937, 514839026795959, 246070932456049, 354300963783727, 515736857403959, 367026463640831, 606628994042041, 184511561449009, 99410905319087, 319660469477341, 144744843674861, 138522022786331, 632604974486581, 13754176085077, 358427769652783, 455153120482121, 534375989625367, 164268248977471, 196695647903219, 205438585292309, 240664611322259, 312378783633431, 357098978954831, 246981515967931, 180283457194531, 594616974804509, 445126166849279, 569049561912311, 234800802761419, 90582473539111, 356854594542917, 394542328564817, 629380891108243, 163207392944803, 345755478167621, 622356968504879, 441212905950913, 31614614878411, 59705584705747, 618633632515127, 527622454232417, 436699878005059, 301140147531211, 357955084856293]

r = 80564133870089
p = 634339207044821
print(len(a))

F = GF(p)
g = F.primitive_element() #获取原根g
es = []
for i in a:
    e = F(i).log(g) #以原根为底数开log,因为循环群中所有的数都能以原根表示
    es.append(e)
    
r = F(r).log(g)
mod = p - 1

A = Matrix(ZZ, 54, 54)
for i in range(52):
    A[i,i] = 2
    A[i,53] = -es[i] * 2 ^ 2000
    
A[52,52] = 2
A[52,53] = r
for i in range(52):
    A[52,i] = 1
A[53,53] = mod
k = A.BKZ(block_size=30)[0]
print(len(list(k)))
print(k)
# key = ''
# for i in k:
#     if i:
#         key += '1'
#     else:
#         key += '0'
# print(len(k))
# print(int(key,2))

Giveaway

关键代码:

def bake(list1,list2):
    return reduce((lambda x,y: x ^ y) , list(map(lambda x, y: x and y, list1, list2)) )

这里是把chocolate_jam和flag的512位比特按位与然后把得到的结果的每一位异或起来得到cookie,and操作可以看作模2下的乘法,^操作可以看作模2下的加法,因此可以把已知条件转换为模2下的矩阵方程。通过server的交互我们可以得到大约507组方程,未知数为512个,因此我们的矩阵为507x512的形状,这样的矩阵是非满秩的,即\(\small Ax=B\)是多解的,只能解出一个特解,因此我们需要找到矩阵的右核,遍历非满秩矩阵的通解,找到flag。exp如下:

# 获取server数据的代码
from pwn import *
import itertools
from hashlib import sha256

table = string.ascii_letters + string.digits
io = remote('127.0.0.1',23967)
#io.interactive()
def proof():
    print(io.recvline())
    s1 = io.recvline()
    p1 = s1.find(b'X+')
    p2 = s1.find(b')')
    p3 = s1.find(b'== ')
    s = s1[p1+2:p2]
    h = s1[p3+3:-1]
    h = h.decode()
    print(h)
    print(s)
    print(io.recvuntil(b'Plz Tell Me XXXX :'))
    for i in itertools.product(table, repeat=4):
        d = ''.join(i).encode()
        dd = d + s
        #print(dd)
        if sha256(dd).hexdigest() == h:
            print(d)
            io.sendline(d)
            #io.interactive()
            break
proof()

a = []
t = io.recvuntil(b'coins')
time = int(t[-10:-6])
print(time)
io.recvuntil(b'[-] ')
for i in range(time-1):
    print(i)
    io.sendline(b'1')
    tmp = io.recvline()
    start = tmp.find(b'(')
    tail = tmp.find(b')')
    tul = tmp[start:tail+1]
    tul = eval(tul)
    a.append(tul)
    io.recvuntil(b'[-] ')

io.sendline(b'1')
tmp = io.recvline()
start = tmp.find(b'(')
tail = tmp.find(b')')
tul = tmp[start:tail+1]
tul = eval(tul)
a.append(tul)
print(a)

# attack 代码
from Crypto.Util.number import *

def check(m):
    s = ''
    for i in m:
        s = s + str(i)
        msg = int(s,2)
        msg = long_to_bytes(msg)
        if b'mini' in msg:
            print(msg)

def dec2blist(decimal, length):
    """
    Converts an integer to a binary list of a specified length, filling zeros on the left if necessary.
    """
    bitlist = []
    while decimal > 0:
        bit = decimal % 2
        bitlist.append(bit)
        decimal //= 2
    bitlist.reverse()
    if len(bitlist) < length:
        bitlist = [0] * (length - len(bitlist)) + bitlist
    return bitlist

a = []
print('Known_length:',len(a))
r = []
x = []
for i in a:
    r.append(i[0])
    x.append(dec2blist(i[1],512))

A = matrix(Zmod(2),x)
B = vector(r)
m = A.solve_right(B)
# 特解
print(m)
ker=A.right_kernel()
for i in ker:
    mm = m+i
    check(mm)

curvesignin

首先通过题目曲线的加法公式来判断曲线形式,具体需要通过求某一点的斜率来判断。当曲线为标准形式\(\small y^2\equiv x^3+ax+b\)时,求斜率\(\small \lambda\)(两点P=Q)的公式为\(\small \lambda=\frac{3x^2+a}{2y}\)...求曲线某一点的斜率公式为\(\small -\alpha_x/ \alpha_y\)。本题给的是\(\small \frac{3cx^2+a}{2by}\),不难判断曲线为\(\small by^2 \equiv cx^3+ax+d\)。下一步就是要求未知参数,包括模数q。利用曲线上已知的n个点构造方程,grobner basis求解,发现结果不理想。于是考虑归一化y前的系数。把整个曲线设置为\(\small y^2 \equiv ax^3+bx+c\)再求解得到需要的参数a b c q。然后这个形式的曲线仍然不方便计算dlp,考虑做一下代换(映射),现有曲线为\(\small y^2 \equiv ax^3+bx+c\),目标曲线为\(\small y_0^2 \equiv x_0^3+b_0x_0+c_0\),代换关系为\(\small ax^3=x_0^3\),设\(\small k=\sqrt[3](a)\;mod\;q\)\(\small x_0=kx\)\(\small b_0x_0=bkx\),因此\(\small b_0=bk^{-1}\),这样代换之后用sage的log求解即可。注意点G不是原根,求出dlp以后还需要爆破加上阶的倍数转字节。exp:

from Crypto.Util.number import *

G = (543964449142803087188784, 288605946000947449279542)
print(long_to_bytes(G[0]))
Ps = [(615716520116903164238350, 815735349922061558569393), (256042693223413187255214, 400772499742719022770893), (620452972415969514798065, 660749148335795540667246), (118301218326000413235697, 338882909672963432813505), (793604064853039619406880, 93386361837996865025986)]

R.<a,b,c>=ZZ[]
F = [a * G[0] ^ 3 + b * G[0] + c - G[1] ^ 2]
for _ in range(5):
    f = a * Ps[_][0] ^ 3 + b * Ps[_][0] + c -Ps[_][1] ^ 2
    F.append(f)

L = Ideal(F).groebner_basis()
print(L)

q=878502880971205563250537
a = -662963051503062411245929 % q
b = -405447704422414394053669 % q
c = -189827742241355283851865 % q


P.<k> = PolynomialRing(GF(q))  
f = k^3 - a 
f.roots()

k = 27410504822203866013322
gx = G[0] * k % q
gy = G[1]
tx = Ps[-1][0] * k % q
ty = Ps[-1][1]

A = b * inverse_mod(k,q) 
B = c
E=EllipticCurve(Zmod(q),[A,B])
G=E(gx,gy)
print(G.order())
T=E(tx,ty)
dlp = discrete_log(T,G,operation='+')
for i in range(10):
    print(long_to_bytes(439251440486032596897638 + i * dlp))
posted @ 2023-05-09 23:43  ZimaB1ue  阅读(182)  评论(0编辑  收藏  举报