第五空间复现

ecc

Ecc套娃,第一部分直接ECDLP,第二部分Pohlig-Hellman算法,题目直接给了不是很懂,第三部分Smart’s Attack。

from Crypto.Util.number import long_to_bytes
p1 = 146808027458411567
A1 = 46056180
B1 = 2316783294673
E1 = EllipticCurve(GF(p1),[A1,B1])
P1 = E1(119851377153561800 , 50725039619018388)
Q1 = E1(22306318711744209 , 111808951703508717)
n1=discrete_log(Q1,P1,operation='+')
print(long_to_bytes(n1))

p2 = 1256438680873352167711863680253958927079458741172412327087203 A2 = 377999945830334462584412960368612 B2 = 604811648267717218711247799143415167229480 E2 = EllipticCurve(GF(p2),[A2,B2]) P2=E2(550637390822762334900354060650869238926454800955557622817950 , 700751312208881169841494663466728684704743091638451132521079) Q2=E2(1152079922659509908913443110457333432642379532625238229329830 , 819973744403969324837069647827669815566569448190043645544592) factors, exponents = zip(factor(E2.order())) primes = [factors[i] ^ exponents[i] for i in range(len(factors))][:-1] print (primes) dlogs = [] for fac in primes: t = int(int(P2.order()) // int(fac)) dlog = discrete_log(tQ2,t*P2,operation=”+”) dlogs += [dlog] print(“factor: “+str(fac)+”, Discrete Log: “+str(dlog)) #calculates discrete logarithm for each prime order n2=crt(dlogs,primes) print (long_to_bytes(n2))

p3 = 0xd3ceec4c84af8fa5f3e9af91e00cabacaaaecec3da619400e29a25abececfdc9bd678e2708a58acb1bd15370acc39c596807dab6229dca11fd3a217510258d1b A3 = 0x95fc77eb3119991a0022168c83eee7178e6c3eeaf75e0fdf1853b8ef4cb97a9058c271ee193b8b27938a07052f918c35eccb027b0b168b4e2566b247b91dc07 B3 = 0x926b0e42376d112ca971569a8d3b3eda12172dfb4929aea13da7f10fb81f3b96bf1e28b4a396a1fcf38d80b463582e45d06a548e0dc0d567fc668bd119c346b2 E3 = EllipticCurve(GF(p3),[A3,B3]) P3=E3(10121571443191913072732572831490534620810835306892634555532657696255506898960536955568544782337611042739846570602400973952350443413585203452769205144937861 ,8425218582467077730409837945083571362745388328043930511865174847436798990397124804357982565055918658197831123970115905304092351218676660067914209199149610) Q3=E3(964864009142237137341389653756165935542611153576641370639729304570649749004810980672415306977194223081235401355646820597987366171212332294914445469010927 ,5162185780511783278449342529269970453734248460302908455520831950343371147566682530583160574217543701164101226640565768860451999819324219344705421407572537) factor(E3.order()) def SmartsAttack(P, Q, p): E = P.curve() Eqp = EllipticCurve(Qp(p, 2), [ZZ(t) + randint(0, p) * p for t in E.a_invariants()]) P_Qps = Eqp.lift_x(ZZ(P.xy()[0]), all=True) for P_Qp in P_Qps: if GF(p)(P_Qp.xy()[1]) == P.xy()[1]: break Q_Qps = Eqp.lift_x(ZZ(Q.xy()[0]), all=True) for Q_Qp in Q_Qps: if GF(p)(Q_Qp.xy()[1]) == Q.xy()[1]: break p_times_P = p * P_Qp p_times_Q = p * Q_Qp x_P, y_P = p_times_P.xy() x_Q, y_Q = p_times_Q.xy() phi_P = -(x_P // y_P) phi_Q = -(x_Q // y_Q) k = phi_Q // phi_P return ZZ(k) n3=SmartsAttack(P3,Q3,p3) print(long_to_bytes(n3)) #https://crypto.stackexchange.com/questions/70454/why-smarts-attack-doesnt-work-on-this-ecdlp

print(b’flag{‘+long_to_bytes(n1)+long_to_bytes(n2)+long_to_bytes(n3)+b’}’) #b’flag{025ab3d9-2521-4a81-9957-8c3381622434}’

doublesage

看着是个LWE问题,当时比赛的时候直接摆烂随便传,每次都传[1-23]和[1-143],然后试了十几次就过了哈哈。赛后学习一下别的师傅的做法。

方法一:

​意义下,求​使得​尽可能小,很容易想到的是让这个式子等于0,于是就只需要解方程​,但是我们知道矩阵A不一定存在逆矩阵,所以需要引入概念伪逆矩阵(pseudo-inverse),或者称其为摩尔彭若斯广义逆(Moore–Penrosepseudoinverse)。

  • m*n 矩阵 A 的 Moore–Penrose 伪逆 是满足以下条件的唯一确定的 n*m矩阵:

    ​​​​​

    如果 A 是列满秩,则

    如果 A 是行满秩,则

  • 如果没有特别指明,矩阵的伪逆就是指摩尔-彭若斯广义逆。广义逆有时也被当作摩尔-彭若斯广义逆的同义词用。摩尔-彭若斯广义逆常应用于求非一致线性方程组最小范数最小二乘解(最小二乘法),并使得解的形式变得简单。矩阵的摩尔-彭若斯广义逆在实数域和复数域上都是唯一的,并且可以通过奇异值分解求得。

可以看到矩阵伪逆正是解决最小范数问题的一种方法。所以我们只需要求出矩阵A的伪逆,然后和b相乘后得到x。

from pwn import *
def solve(p, rows):
    r.recvuntil(b'Matrix A')
    r.recvline()
def get_vector():
    r.recvuntil(b'[')
    s = r.recvuntil(b']')[:-1]    
    return [int(x) for x in s.split()]

A = []

for i in range(rows):
    s = get_vector(p)
    A.append(s)

r.recvline()
b = r.recvline()
b = get_vector()

Zp = Zmod(p)

A = Matrix(Zp, A)
b = vector(Zp, b)

x = A.transpose().pseudoinverse() * b

x = str(x).replace('(', '[').replace(')', ']')
r.sendline(x.encode())
r.recvuntil(b'The norm of vector x*A-C is')
res = r.recvline()
if (b'True' in res):
    print('Pass chall {}'.format(p))
    return True
else:
    print('Fail chall {}'.format(p))
    return False

while True: r = remote(‘122.112.210.186’,51436) if solve(23, 5) and solve(143, 15): r.interactive() else: r.close() #https://wp.n03tack.top/posts/56002/#doublesage

这波学到了,还有矩阵伪逆这玩意

方法二:

LWE问题,找LWE的脚本直接梭,脚本来自NaN。(LWE脚本先存了)

from pwn import *
from sage.modules.free_module_integer import IntegerLattice
def Babai_closest_vector(L, w):
    '''
    Yet another method to solve apprCVP, using a given good basis.
    INPUT:
    * "L" -- a matrix representing the LLL-reduced basis (v1, ..., vn) of a
    lattice.
    * "w" -- a target vector to approach to.
    OUTPUT:
    * "v" -- a approximate closest vector.
    Quoted from "An Introduction to Mathematical Cryptography":
    In both theory and practice, Babai's closest plane algorithm
    seems to yield better results than Babai's closest vertex algorithm.
    '''
    G, _ = L.gram_schmidt()
    t = w
    i = L.nrows() - 1
    while i >= 0:
        w -= round( (w*G[i]) / G[i].norm()^2 ) * L[i]
        i -= 1
    return t - w
r = remote('122.112.210.186', 51436)
context(log_level='debug')
r.recvuntil('modulus')
r.recvline()
r.recvline()
data = r.recvline().decode()
M = []
for i in range(5):
    t_vec = []
    vec = r.recvline().decode()[1:-2]
    vec = vec.split(' ')
    for x in vec:
        if x.isdecimal():
            t_vec.append(int(x))
    assert len(t_vec) == 23
    M.append(t_vec)
assert len(M) == 5
M = Matrix(ZZ, M)
m, n = 5, 23
r.recvline()
r.recvline()
vec = r.recvline().decode()[1:-2]
vec = vec.split(' ')
b_vec = []
for x in vec:
    if x.isdecimal():
        b_vec.append(int(x))
assert len(b_vec) == 23
A_value = M # exactly 'A' in General situation
A_value = A_value.transpose()
b_value = b_vec # exactly 'a' in General situation
m = 23
n = 5
p = 29
# construct the lattice L after applying LLL
Lattice = Matrix(ZZ, m + n, m)
for x in range(m):
    for y in range(n):
        Lattice[m + y, x] = A_value[x, y]
    Lattice[x, x] = p
lattice = IntegerLattice(Lattice, lll_reduce=True)
print('LLL done')
# prepare for Babai's algorithm
target = vector(ZZ, b_value)
closest_v = Babai_closest_vector(lattice.reduced_basis, target)
print('Find closest vector:{} '.format(closest_v))
# solve the equation: A*s = b
A_LWE = matrix(Zmod(p), A_value)
s = A_LWE.solve_right(closest_v)
s = list(s)
r.sendlineafter('modulus 29 :', str(s))
r.recvuntil('modulus')
r.recvline()
r.recvline()
data = r.recvline().decode()
M = []
for i in range(15):
    t_vec = []
    vec = r.recvline().decode()[1:-2]
    vec = vec.split(' ')
    for x in vec:
        if x.isdecimal():
            t_vec.append(int(x))
    assert len(t_vec) == 143
    M.append(t_vec)
    assert len(M) == 15
    M = Matrix(ZZ, M)
r.recvline()
r.recvline()
b_vec = []
for i in range(7):
    if i != 6:
        vec = r.recvline().decode()[1:-1]
    else:
        vec = r.recvline().decode()[1:-2]
    vec = vec.split(' ')
    for x in vec:
        if x.isdecimal():
            b_vec.append(int(x))
assert len(b_vec) == 143
print(b_value)

A_value = M # exactly ‘A’ in General situation A_value = A_value.transpose() b_value = b_vec # exactly ‘a’ in General situation m = 143 n = 15 p = 227

construct the lattice L after applying LLL

Lattice = Matrix(ZZ, m + n, m) for x in range(m): for y in range(n): Lattice[m + y, x] = A_value[x, y] Lattice[x, x] = p lattice = IntegerLattice(Lattice, lll_reduce=True) print(‘LLL done’)

prepare for Babai’s algorithm

target = vector(ZZ, b_value) closest_v = Babai_closest_vector(lattice.reduced_basis, target) print(‘Find closest vector:{} ‘.format(closest_v))

solve the equation: A*s = b

A_LWE = matrix(Zmod(p), A_value) s = A_LWE.solve_right(closest_v) s = list(s) print(s) r.sendlineafter(‘modulus 227 :’, str(s)) r.interactive()

signin

这题已知p^q的低400位,和p*q,实际上还是一种解方程,只不过需要一位一位枚举p和q,然后在已知p的低位的情况,使用coppersmith。(最近这种异或爆破好像挺多的,学一手)

from Crypto.Util.number import *
c = 20447834080716676861260898086596427975708825833039405478043730491684492090126834957765281802337614379260353025780350269473938100277831839834052494528642858771208829522444199753332668288237520529204598641192166056115125100497251758639697188043033677583625259284290926751392026007345743270672794001549260660437
e = 65537
n = 51829690315852722180065756920738886752496164137616266689326816383511598394192418757462476837101165832835728876608898794315487561791469438010457161419486159257838354726118084197036612143071280955688965597991177195321580364900266798655104065461134993248598785717447158334659990278280156559953140328939918569751
x = 1458992978872343557099505109358348200022788430032606776639391204880828365157718281673562914452477666861716725617956844822
x = bin(x)[2:]
P = []

def find(guessp, i): p = int(guessp, 2) q = int(x[-i:], 2) ^^ p

if (q * p) % 2 ** i == n % 2 ** i:
    if i == 399:
        P.append(p)
    else:
        find('1'+guessp, i+1)
        find('0'+guessp, i+1)

find(‘1’,1) for i in range(len(P)): pb=len(bin(_p)[2:]) _p = P[i] kbits = 512 - pb R.<x> = Zmod(n)[] f = P[i] + x * 2^pb f = f.monic() roots = f.small_roots(X=2^kbits,beta=0.4) if roots: p = int(_p + roots[0]* 2^pb) print(p) break q = n // p assert p * q == n phi = (q - 1) * (p - 1) d = inverse_mod(e,phi) print(long_to_bytes(pow(c,d,n)))

secrets

哭了,格子构造出来不能出,用了NaN的svp方法也没用,求一个密码爷爷教教怎么做。

当时写出了这个式子

然后就可以构造一个格子

这里R用来调格子的det,让它满足LLL规约的不等式要求。下面的脚本是NaN的脚本,但是并不能出。

from Crypto.Util.number import *
import itertools
from sage.modules.free_module_integer import IntegerLattice
p =9990023055834814541617846807430043006619434399269736419836238596241876512761228099716117843097392140738452382394167885743945642291607707968961478893802663
a =[5966930779052686477031701769573660079506373243735898860732951813437052962193127602437592171689011479433096174113531891952361650308071688129029761338139979, 5970641925133316779789534939731049546081094069820251739654111253332315359322044145559737555982279747038885936478125413531493247569529490923909073652886513, 5647457807686675609383359511965317690655998242830631042112382847759941921904234938861931780977072229311090688925189099391590810644016635012424277294034433]
e = [[0, 1, 2], [2, 2, 2], [0, 2, 0]]
c =5011570647869537688965619278497453589689533788035020706968424121134157038323546474418039082230117040297759615283099576609259094217120003896586061782747210
cc =bytes.fromhex('4f79c22cea53737e05f284dd1ff6b3eba6d8aa9d1bb31cc47835d1057052bab5')
M = Matrix(ZZ, [
[1, 0, 0, 0, a[0]],
[0, 1, 0, 0, a[1]],
[0, 0, 1, 0, a[2]],
[0, 0, 0, 1, p],
[0, 0, 0, 0, c]
])
# print(M.LLL())
from sage.modules.free_module_integer import IntegerLattice
#https://github.com/rkm0959/Inequality_Solving_with_CVP/blob/main/solver.sage
def Babai_CVP(mat, target):
    M = IntegerLattice(mat, lll_reduce=True).reduced_basis
    G = M.gram_schmidt()[0]
    diff = target
    for i in reversed(range(G.nrows())):
        diff -= M[i] * ((diff * G[i]) / (G[i] * G[i])).round()
    return target - diff
def solve(mat, lb, ub, weight = None):
    num_var = mat.nrows()
    num_ineq = mat.ncols()
    max_element = 0
    for i in range(num_var):
        for j in range(num_ineq):
            max_element = max(max_element, abs(mat[i, j]))
if weight == None:
    weight = num_ineq * max_element
# sanity checker
if len(lb) != num_ineq:
    print(&quot;Fail: len(lb) != num_ineq&quot;)
    return
if len(ub) != num_ineq:
    print(&quot;Fail: len(ub) != num_ineq&quot;)
    return
for i in range(num_ineq):
    if lb[i] &gt; ub[i]:
        print(&quot;Fail: lb[i] &gt; ub[i] at index&quot;, i)
        return
# heuristic for number of solutions
DET = 0
if num_var == num_ineq:
    DET = abs(mat.det())
    num_sol = 1
    for i in range(num_ineq):
        num_sol *= (ub[i] - lb[i])
    if DET == 0:
        print(&quot;Zero Determinant&quot;)
    else:
        num_sol //= DET
    # + 1 added in for the sake of not making it zero...
        print(&quot;Expected Number of Solutions : &quot;, num_sol + 1)
# scaling process begins
max_diff = max([ub[i] - lb[i] for i in range(num_ineq)])
applied_weights = []
for i in range(num_ineq):
    ineq_weight = weight if lb[i] == ub[i] else max_diff // (ub[i] - lb[i])
applied_weights.append(ineq_weight)
for j in range(num_var):
    mat[j, i] *= ineq_weight
lb[i] *= ineq_weight
ub[i] *= ineq_weight
# Solve CVP
target = vector([(lb[i] + ub[i]) // 2 for i in range(num_ineq)])
result = Babai_CVP(mat, target)

for i in range(num_ineq):
    if (lb[i] &lt;= result[i] &lt;= ub[i]) == False:
        print(&quot;Fail : inequality does not hold after solving&quot;)
        break
# recover x
fin = None

if DET != 0:
    mat = mat.transpose()
    fin = mat.solve_right(result)
    
## recover your result
return result, applied_weights, fin

lb = [2^93, 2^190, 2^60, 2^185, 0] ub = [2^97, 2^195, 2^70, 2^195, 1] print(solve(M, lb, ub))

个人信息保护

五层套娃密码,有点搞其实。

第一层的n可以直接查库分解

from Crypto.Util.number import *

with open(‘out’) as f: s = f.read().splitlines()

c = eval(s[0]) n = eval(s[1]) p = 22186905890293167337018474103 q = 64390888389278700958517837593 e = 65537 d = inverse(e, (p-1)*(q-1)) m = pow(c, d, n) print(long_to_bytes(m)) #XIAOming

第二层可以把n除p,然后查库分解q,q的因数很多算phi要注意一下。当然注意到m是小于p的,也可以直接模p。

c = eval(s[2])
n = eval(s[3])
p = 11616788973244169211540879051135531683500013311175857700532973853592727185033846064980717918194540453710515251945345524986932165003196804187526561468278997
e = 65537
q = n // p
dp = inverse(e, (p-1))
m = pow(c, dp, p)
print(long_to_bytes(m))
#17810111101

第三层是矩阵乘法,直接sage解方程就可以了

with open('out') as f:
    s = f.read()
s = s.splitlines()

q = eval(s[4]) A = eval(s[5]) b = eval(s[6])

A = Matrix(Zmod(q), A) b = vector(Zmod(q), b)

x = A.solve_right(b) x = ”.join([chr(i) for i in x]) print(x) #XIAOming@cmail.com

第四层就很搞了,乍一看是一个没有任何问题的AES,但是细想,前面用了这么多random,怕不是MT19937。数一下:第一层的a、b是两个96bits数,就是6个state;第二层q用了randrange,取的两个数的范围相差​​​​​,需要爆破最后一位,得到一个state;第三层的key每个都是32bits,一共有34*18=612个state。这时一共有619个state,还差5个state。注意到在第二层里的message,结尾填充了160bits随机数,正好5个state。

这样就凑齐了624位数,不过需要合理爆破一下第一层的a、b。

from sympy import prevprime
from Crypto.Util.number import *
from randcrack import RandCrack
from Crypto.Cipher import AES
from string import printable

def sendstate(x, tmp): for i in range(tmp): rc.submit(x % (1 << 32)) x >>= 32

p1 = 22186905890293167337018474103 q1 = 64390888389278700958517837593 prep1 = prevprime(p1) preq1 = prevprime(q1) with open(‘out’) as f: s = f.read().splitlines() n = eval(s[3]) p = 11616788973244169211540879051135531683500013311175857700532973853592727185033846064980717918194540453710515251945345524986932165003196804187526561468278997 e = 65537 q = n // p key = eval(s[5]) key = [x for y in key for x in y] pads = b’\xf1\x0f\xb5\xb5\xae\xf0\x05\x92BWR\xd0>\x91\x0cv\xbc ]\x81’ pads = bytes_to_long(pads)

c4 = eval(s[7]) c4 = long_to_bytes(c4) for a in range(prep1, p1): for b in range(preq1, q1): for c in range(2): rc = RandCrack() fq = (q << 1) + c sendstate(a, 3) sendstate(b, 3) sendstate(pads, 5) sendstate(fq, 1) for i in key: rc.submit(i) aeskey = long_to_bytes(rc.predict_getrandbits(128)) cipher = AES.new(aeskey, AES.MODE_ECB) try: m = cipher.decrypt(c4).decode() if all(char in printable for char in m): print(m, a, b) except: continue #No.555_hack_road 22186905890293167337018474052 64390888389278700958517837515

然后就可以用第四层求出来的随机数的state,预测第五层的ElGamal中的所有参数,然后直接梭就完事了。

from Crypto.Util.number import *
from randcrack import RandCrack

def sendstate(x, tmp): for i in range(tmp): rc.submit(x % (1 << 32)) x >>= 32

with open(‘out’) as f: s = f.read().splitlines() n = eval(s[3]) p = 11616788973244169211540879051135531683500013311175857700532973853592727185033846064980717918194540453710515251945345524986932165003196804187526561468278997 e = 65537 q = n // p key = eval(s[5]) key = [x for y in key for x in y] pads = b’\xf1\x0f\xb5\xb5\xae\xf0\x05\x92BWR\xd0>\x91\x0cv\xbc ]\x81’ pads = bytes_to_long(pads) a=22186905890293167337018474052 b=64390888389278700958517837515 rc=RandCrack() sendstate(a, 3) sendstate(b, 3) sendstate(pads, 5) sendstate(q << 1, 1) for i in key: rc.submit(i) aeskey = long_to_bytes(rc.predict_getrandbits(128)) q, g, h = [eval(x) for x in s[8].split()] c1, c2 = [eval(x) for x in s[9].split()] gg = rc.predict_randrange(q-1) x = rc.predict_randrange(q-1) y = rc.predict_randrange(q-1) s = pow(g, x*y, q) m5 = c2 * inverse(s, q) % q print(long_to_bytes(m5)) #MakeDSAGreatAgain_University

from hashlib import *
 
name = b'XIAOming'
phone = b'17810111101'
mail = b'XIAOming@cmail.com'
address = b'No.555_hack_road'
school = b'MakeDSAGreatAgain_University'
flag = 'flag{' + sha256(name).hexdigest()[:8] + '-' + sha256(phone).hexdigest()[:4] + '-' + sha256(mail).hexdigest()[:4] + '-' + sha256(address).hexdigest()[:4] + '-' + sha256(school).hexdigest()[:12] + '}'
print(flag)
#flag{9cb2979a-d651-b79e-a86b-ee01181ded94}