Crypto
渐增套娃
题目:
一天,小明在古玩市场得到一个神秘的套娃,听说这个套娃藏着古代最有钱富豪的宝箱密码,套娃的底部,你发现了三段标注 “核心加密” 的神秘字符串,根据小明细心研究,这些是逐步叠加嵌套得到的密码,已知flag在初步被加密为24292125,请帮小明得到宝箱的密码,或许他能和你46分(注:每一个结果中间加一个空格号,再套上flag{})
第一段:22151311012182213
第二段:36122921261633143212
第一段:3619373118351933
QWERTY 密码,解得三段:stepwise,nwlahycrxw,nomziboc,后面两个凯撒爆破一下。
flag{stepwise encryption strength}
Ez_AES
题目:
k = 54686973497341555331323435363638
IV = 496E697469616C697A6174696F6E2056
c = ae4ff1ccc900eb0ba99857c297cf0b73
#得到的答案进行MD5的32位小写加密以获得flag
exp.py:
from hashlib import md5from Crypto.Cipher import AESfrom Crypto.Util.Padding import unpad
k = bytes.fromhex("54686973497341555331323435363638")IV = bytes.fromhex("496E697469616C697A6174696F6E2056")c = bytes.fromhex("ae4ff1ccc900eb0ba99857c297cf0b73")cipher = AES.new(k, AES.MODE_CBC, IV)pt = unpad(cipher.decrypt(c), 16)print("flag{" + md5(pt).hexdigest() + "}")# flag{3f234da2fe6e8c189048e522b18fefed}trod
题目:
#!/usr/bin/python3.10def Sn(i): s = '' while i != 0: digit = i & 0xff i >>= 8 s += chr(digit) return s
def In(s): val = 0 for i in range(len(s)): digit = ord(s[len(s) - i - 1]) val <<= 8 val |= digit return val
def egcd(a, b): if a == 0: return b, 0, 1 else: g, y, x = egcd(b % a, a) return g, x - (b // a) * y, y
def add(a, b, p): if b == -1: return a if a == -1: return b x1, y1 = a x2, y2 = b x3 = ((x1 * x2 - x1 * y2 - x2 * y1 + 2 * y1 * y2) * mod_inv(x1 + x2 - y1 - y2 - 1, p)) % p y3 = ((y1 * y2) * mod_inv(x1 + x2 - y1 - y2 - 1, p)) % p return x3, y3
def double_add(a, p): return add(a, a, p)
def mod_inv(a, p): a %= p g, x, y = egcd(a, p) if g != 1: raise Exception('No inverse exists for %d mod %d' % (a, p)) else: return x % p
def mod_mul(m, g, p): r = -1 while m != 0: if m & 1: r = add(r, g, p) m >>= 1 g = double_add(g, p) return r
def encrypt(message, key): return message ^ key
p = ...
g = (..., ...)
A = (..., ...)
B = (..., ...)
if __name__ == "__main__": from secret import aliceSecret, bobSecret, flag
assert A == mod_mul(aliceSecret, g, p) assert B == mod_mul(bobSecret, g, p)
aliceMS = mod_mul(aliceSecret, B, p) bobMS = mod_mul(bobSecret, A, p) assert aliceMS == bobMS masterSecret = aliceMS[0] * aliceMS[1]
length = len(flag) encrypted_message = encrypt(In(flag), masterSecret)
print("length = %d, encrypted_message = %d" % (length, encrypted_message))
# length = 62, encrypted_message = ...p不是素数,同时构造群使用的add可以通过数学变换转换为模p下的普通乘法。
然后为了方便解出具体的x和y值,还可以定义一个新的映射:
这样在结合V和W两个映射就能轻松的解得x和y的值了。
所以我们可以简单的将两个公钥转换为模p下的 DLP 问题求解出私钥,然后解得共享密钥在映射域的值,然后回推回题目需要的共享密钥坐标即可。
exp.py:
# SageMath 10.7from Crypto.Util.number import long_to_bytes
def map_v(point): x, y = R(point[0]), R(point[1]) return (x - y - 1) / (x - y)
def map_w(point): x, y = R(point[0]), R(point[1]) return y / (x - y)
p = ...g = (..., ...)A = (..., ...)B = (..., ...)encrypted_message = ...R = Integers(p)vg = map_v(g)va = map_v(A)vb = map_v(B)wb = map_w(B)alice_secret = discrete_log(va, vg)vs = vb ** alice_secretws = wb ** alice_secretUs = 1 / (1 - vs)ys = ws * Usxs = Us + ysmaster_secret = int(xs) * int(ys)decrypted_int = encrypted_message ^^ master_secretprint(long_to_bytes(decrypted_int).decode()[::-1])# flag{Who_has_the_computer_organization_principle_in_the_exam?}xiaoji的RSA
题目:
import libnumfrom flag import *
m1 = libnum.s2n(flag[:21])m2 = libnum.s2n(flag[21:])p = libnum.generate_prime(1024)q = libnum.generate_prime(1024)e = 0x10001n = p * qh1 = pow(2025 * p + 2024 * q, 7717, n)h2 = pow(2024 * p + 2025 * q, 8859, n)c1 = pow(m1, e, n)c2 = pow(m2, e, n)
print("h1=", h1)print("h2=", h2)print("n=", n)print("c=", (c1 + c2))print("leak=", (c1 * 2 - c2))#output# h1 = ...# h2 = ...# n = ...# c = ...# leak = ...先恢复c1和c2:
然后就是费马定理。
固有p = gcd(pow(2025, e1 * e2, n) * pow(h1, e2, n) - pow(2024, e1 * e2, n) * pow(h2, e1, n), n)。
exp.py:
from gmpy2 import gcd, invertfrom Crypto.Util.number import long_to_bytes
h1 = ...h2 = ...n = ...c = ...leak = ...e, e1, e2 = 0x10001, 7717, 8859c1 = (c + leak) // 3c2 = (2 * c - leak) // 3p = gcd(pow(2025, e1 * e2, n) * pow(h1, e2, n) - pow(2024, e1 * e2, n) * pow(h2, e1, n), n)q = n // passert p * q == nphi = (p - 1) * (q - 1)d = invert(e, phi)m1 = pow(c1, d, n)m2 = pow(c2, d, n)flag = long_to_bytes(m1) + long_to_bytes(m2)print(flag.decode())# flag{fc08e137-5be9-4677-a1f5-4e640223df0e}创造lcg
题目:
from Crypto.Util.number import *from secret import FLAGp = getPrime(128)step = len(FLAG) // 3lcg = [bytes_to_long(FLAG[:step]), bytes_to_long(FLAG[step:2*step]), bytes_to_long(FLAG[2*step:])]a = getPrime(80)b = getPrime(80)c = getPrime(80)a = 678292774844628690689951b = 799218428050845578943269c = 871991670671866736323531p = 226554022535584634512578046463759712133
for _ in range(10): new_state = (a*lcg[0] + b*lcg[1] + c*lcg[2]) % p lcg = lcg[1:] + [new_state] #print(lcg)print(lcg)print(a, b, c, p)难评,给个推导,但是其实给的输出就是 flag。
exp.py:
from Crypto.Util.number import long_to_bytes, inverse
lcg = [531812496965714475754459274425954913, 573493247306997567791036597408132959, 531874692922906583591521900672740733]flag = long_to_bytes(lcg[0]) + long_to_bytes(lcg[1]) + long_to_bytes(lcg[2])print(flag.decode())# flag{try_to_transform_it_into_formulaic_form}a = 678292774844628690689951b = 799218428050845578943269c = 871991670671866736323531p = 226554022535584634512578046463759712133a_inv = inverse(a, p)for _ in range(10): new_state = (lcg[2] - lcg[1] * c - lcg[0] * b) * a_inv % p lcg = [new_state] + lcg[:-1]print(long_to_bytes(lcg[0]), long_to_bytes(lcg[1]), long_to_bytes(lcg[2]))# b'\x1a\xcapQ|\x12\x8fRD\xcd\xbcT;\x05~\x8c' b')X^;\xdd\x08\x1bv3"\xfe\x91<\xaf\x01U' b'\x896@\xcey\x87\xc7\xbc\xa0ox\xce\t\xc2?`'高位攻击
题目:
from Crypto.Util.number import *from secret import flag# 生成1024位的大素数p和qp = getPrime(1024)q = getPrime(1024)
# 计算RSA模数nn = p * q
d=getPrime(521)e = inverse(d,(p-1)*(q-1))flag_int = bytes_to_long(flag())c = pow(flag_int, e, n)# 生成提示信息1:p的高位部分(右移224位)hint1 = p >> (1024-224)
# 生成提示信息2:q的高位部分(右移224位)hint2 = q >> (1024-224)print(f"n = {n}")print(f"e = {e}")print(f"c = {c}")print(f"hint1 = {hint1}")print(f"hint2 = {hint2}")'''n = ...e = ...c = ...hint1 = ... # p高位hint2 = ... # q高位'''0.25 < d < 0.292,上 Boneh 板子打就行,然后两个高位不用也行,或者可以用来约束确定p和q。
exp.py:
# SageMath 10.7from __future__ import print_functionimport time
############################################# Config##########################################
"""Setting debug to true will display more informationsabout the lattice, the bounds, the vectors..."""debug = True
"""Setting strict to true will stop the algorithm (andreturn (-1, -1)) if we don't have a correctupperbound on the determinant. Note that thisdoesn't necesseraly mean that no solutionswill be found since the theoretical upperbound isusualy far away from actual results. That is whyyou should probably use `strict = False`"""strict = False
"""This is experimental, but has provided remarkable resultsso far. It tries to reduce the lattice as much as it canwhile keeping its efficiency. I see no reason not to usethis option, but if things don't work, you should trydisabling it"""helpful_only = Truedimension_min = 7 # stop removing if lattice reaches that dimension
############################################# Functions##########################################
# display stats on helpful vectorsdef helpful_vectors(BB, modulus): nothelpful = 0 for ii in range(BB.dimensions()[0]): if BB[ii,ii] >= modulus: nothelpful += 1
print(nothelpful, "/", BB.dimensions()[0], " vectors are not helpful")
# display matrix picture with 0 and Xdef matrix_overview(BB, bound): for ii in range(BB.dimensions()[0]): a = ('%02d ' % ii) for jj in range(BB.dimensions()[1]): a += '0' if BB[ii,jj] == 0 else 'X' if BB.dimensions()[0] < 60: a += ' ' if BB[ii, ii] >= bound: a += '~' print(a)
# tries to remove unhelpful vectors# we start at current = n-1 (last vector)def remove_unhelpful(BB, monomials, bound, current): # end of our recursive function if current == -1 or BB.dimensions()[0] <= dimension_min: return BB
# we start by checking from the end for ii in range(current, -1, -1): # if it is unhelpful: if BB[ii, ii] >= bound: affected_vectors = 0 affected_vector_index = 0 # let's check if it affects other vectors for jj in range(ii + 1, BB.dimensions()[0]): # if another vector is affected: # we increase the count if BB[jj, ii] != 0: affected_vectors += 1 affected_vector_index = jj
# level:0 # if no other vectors end up affected # we remove it if affected_vectors == 0: print("* removing unhelpful vector", ii) BB = BB.delete_columns([ii]) BB = BB.delete_rows([ii]) monomials.pop(ii) BB = remove_unhelpful(BB, monomials, bound, ii-1) return BB
# level:1 # if just one was affected we check # if it is affecting someone else elif affected_vectors == 1: affected_deeper = True for kk in range(affected_vector_index + 1, BB.dimensions()[0]): # if it is affecting even one vector # we give up on this one if BB[kk, affected_vector_index] != 0: affected_deeper = False # remove both it if no other vector was affected and # this helpful vector is not helpful enough # compared to our unhelpful one if affected_deeper and abs(bound - BB[affected_vector_index, affected_vector_index]) < abs(bound - BB[ii, ii]): print("* removing unhelpful vectors", ii, "and", affected_vector_index) BB = BB.delete_columns([affected_vector_index, ii]) BB = BB.delete_rows([affected_vector_index, ii]) monomials.pop(affected_vector_index) monomials.pop(ii) BB = remove_unhelpful(BB, monomials, bound, ii-1) return BB # nothing happened return BB
"""Returns:* 0,0 if it fails* -1,-1 if `strict=true`, and determinant doesn't bound* x0,y0 the solutions of `pol`"""def boneh_durfee(pol, modulus, mm, tt, XX, YY): """ Boneh and Durfee revisited by Herrmann and May
finds a solution if: * d < N^delta * |x| < e^delta * |y| < e^0.5 whenever delta < 1 - sqrt(2)/2 ~ 0.292 """
# substitution (Herrman and May) PR.<u, x, y> = PolynomialRing(ZZ) Q = PR.quotient(x*y + 1 - u) # u = xy + 1 polZ = Q(pol).lift()
UU = XX*YY + 1
# x-shifts gg = [] for kk in range(mm + 1): for ii in range(mm - kk + 1): xshift = x^ii * modulus^(mm - kk) * polZ(u, x, y)^kk gg.append(xshift) gg.sort()
# x-shifts list of monomials monomials = [] for polynomial in gg: for monomial in polynomial.monomials(): if monomial not in monomials: monomials.append(monomial) monomials.sort()
# y-shifts (selected by Herrman and May) for jj in range(1, tt + 1): for kk in range(floor(mm/tt) * jj, mm + 1): yshift = y^jj * polZ(u, x, y)^kk * modulus^(mm - kk) yshift = Q(yshift).lift() gg.append(yshift) # substitution
# y-shifts list of monomials for jj in range(1, tt + 1): for kk in range(floor(mm/tt) * jj, mm + 1): monomials.append(u^kk * y^jj)
# construct lattice B nn = len(monomials) BB = Matrix(ZZ, nn) for ii in range(nn): BB[ii, 0] = gg[ii](0, 0, 0) for jj in range(1, ii + 1): if monomials[jj] in gg[ii].monomials(): BB[ii, jj] = gg[ii].monomial_coefficient(monomials[jj]) * monomials[jj](UU,XX,YY)
# Prototype to reduce the lattice if helpful_only: # automatically remove BB = remove_unhelpful(BB, monomials, modulus^mm, nn-1) # reset dimension nn = BB.dimensions()[0] if nn == 0: print("failure") return 0,0
# check if vectors are helpful if debug: helpful_vectors(BB, modulus^mm)
# check if determinant is correctly bounded det = BB.det() bound = modulus^(mm*nn) if det >= bound: print("We do not have det < bound. Solutions might not be found.") print("Try with highers m and t.") if debug: diff = (log(det) - log(bound)) / log(2) print("size det(L) - size e^(m*n) = ", floor(diff)) if strict: return -1, -1 else: print("det(L) < e^(m*n) (good! If a solution exists < N^delta, it will be found)")
# display the lattice basis if debug: matrix_overview(BB, modulus^mm)
# LLL if debug: print("optimizing basis of the lattice via LLL, this can take a long time")
BB = BB.LLL()
if debug: print("LLL is done!")
# transform vector i & j -> polynomials 1 & 2 if debug: print("looking for independent vectors in the lattice") found_polynomials = False
for pol1_idx in range(nn - 1): for pol2_idx in range(pol1_idx + 1, nn): # for i and j, create the two polynomials PR.<w,z> = PolynomialRing(ZZ) pol1 = pol2 = 0 for jj in range(nn): pol1 += monomials[jj](w*z+1,w,z) * BB[pol1_idx, jj] / monomials[jj](UU,XX,YY) pol2 += monomials[jj](w*z+1,w,z) * BB[pol2_idx, jj] / monomials[jj](UU,XX,YY)
# resultant PR.<q> = PolynomialRing(ZZ) rr = pol1.resultant(pol2)
# are these good polynomials? if rr.is_zero() or rr.monomials() == [1]: continue else: print("found them, using vectors", pol1_idx, "and", pol2_idx) found_polynomials = True break if found_polynomials: break
if not found_polynomials: print("no independant vectors could be found. This should very rarely happen...") return 0, 0
rr = rr(q, q)
# solutions soly = rr.roots()
if len(soly) == 0: print("Your prediction (delta) is too small") return 0, 0
soly = soly[0][0] ss = pol1(q, soly) solx = ss.roots()[0][0]
# return solx, solydef example(): ############################################ # Problem to solve (edit the following values) ##########################################
# the modulus N = ...
# the public exponent e = ...
# ciphertext c = ...
############################################ # The hypothesis on the private exponent ########################################## # d is ~521-bit in the generation code # N is ~2048-bit, so delta around 521/2048 ~= 0.254 # Keep < 0.292 delta = 0.26
############################################ # Lattice parameters ########################################## # Try slightly larger m than the toy example m = 5 t = int((1-2*delta) * m) # optimization from Herrmann and May
# bounds X = 2*floor(N^delta) Y = floor(N^(1/2))
############################################ # Don't touch anything below ##########################################
# Problem put in equation P.<x,y> = PolynomialRing(ZZ) A = int((N+1)/2) pol = 1 + x * (A + y)
# Checking bounds if debug: print("=== checking values ===") print("* delta:", delta) print("* delta < 0.292", delta < 0.292) print("* size of e:", int(log(e)/log(2))) print("* size of N:", int(log(N)/log(2))) print("* m:", m, ", t:", t)
# boneh_durfee if debug: print("=== running algorithm ===") start_time = time.time()
solx, soly = boneh_durfee(pol, e, m, t, X, Y)
# found a solution? if solx > 0: print("=== solution found ===")
d = int(pol(solx, soly) / e) print("private key found:", d)
# decrypt m_int = pow(int(c), int(d), int(N)) try: from Crypto.Util.number import long_to_bytes print(long_to_bytes(m_int).decode()) except Exception: # fallback if pycryptodome not available in your Sage env print("m_int =", m_int)
else: print("=== no solution was found ===")
if debug: print(("=== %s seconds ===" % (time.time() - start_time)))
example()# flag{please-put-this-one-in-sagamath}Some information may be outdated









