wp

Misc

只因因

首先通过重复的特征氨基酸序列对给出的基因片段进行排列

通过比对发现,3片段中的内容被1片段和5片段包含,6片段被2片段包含。

得出排序:4-2-5-1,把重复段去除,得到完整的基因段

TGTCTGCCATTCTTAAAAACAAAAATGTTGTTATTTTTATTTCAGATGCGATCTGTGAGCCGAGTCTTTA

其反向互补序列

TAAAGACTCGGCTCACAGATCGCATCTGAAATAAAAATAACAACATTTTTGTTTTTAAGA ATGGCAGACA

通过http://www.ncbi.nlm.nih.gov/BLAST/中的核苷酸查询(blastn),并且根据题目限定的种属,找到对应基因名称

cystic fibrosis transmembrane conductance regulator,简写为CFTR。

非预期 直接搜索一段序列就能找到国外教材的原题,或者直接在给出的网站搜索一段序列就能找到对应蛋白质。

这题是对象的一道遗传学作业题的简化版,师傅们太强啦

Crypto

coloratura

一道经典的MT19937题。

看到本题取随机数的函数为 colors = long_to_bytes(getrandbits(width * height * 24))[::-1],通过测试我们可以知道,getrandbits函数在收到超出32比特的参数,会把已生成的state放在低位,高位放置新生成的state。这里倒序则是把state0放在了最开始。

注意到draw.text((5, 5), flag, fill=(255, 255, 255)),flag填充的位置在(5,5),所以我们有4行未经过异或的原值,其比特数为 ,刚好有624组state,然后我们就可以依次生成后续的内容得到原图,异或之后得到flag图像。

还有的师傅是通过图像的中间部分获取624个state,然后向前恢复状态,也是一种思路。

from PIL import Image
import random
from Crypto.Util.number import *

def invert_right(m, l, val=”): length = 32 mx = 0xffffffff if val == ”: val = mx i, res = 0, 0 while i * l < length: mask = (mx << (length - l) & mx) >> i * l tmp = m & mask m = m ^ tmp >> l & val res += tmp i += 1 return res

def invert_left(m, l, val): length = 32 mx = 0xffffffff i, res = 0, 0 while i * l < length: mask = (mx >> (length - l) & mx) << i * l tmp = m & mask m ^= tmp << l & val res |= tmp i += 1 return res

def invert_temper(m): m = invert_right(m, 18) m = invert_left(m, 15, 4022730752) m = invert_left(m, 7, 2636928640) m = invert_right(m, 11) return m

def clone_mt(record): state = [invert_temper(i) for i in record] gen = random.Random() gen.setstate((3, tuple(state + [0]), None)) return gen

def makeSourceImg(): colors = long_to_bytes(g.getrandbits(width * height * 24))[::-1] img = Image.new(‘RGB’, (width, height)) x = 0 for i in range(height): for j in range(width): img.putpixel((j, i), (colors[x], colors[x + 1], colors[x + 2])) x += 3 return img

height, width = 208, 208

img = Image.open(‘attach.png’) a = [] for i in range(4): for j in range(208): p = img.getpixel((j, i)) for pi in p: a.append(pi) M = [] for i in range(len(a) // 4): px = (a[4 * i + 3] << 24) + (a[4 * i + 2] << 16) + (a[4 * i + 1] << 8) + a[4 * i + 0] M.append(px)

g = clone_mt(M)

img1 = makeSourceImg() img2 = Image.open(‘attach.png’) img3 = Image.new(“RGB”, (width, height)) for i in range(height): for j in range(width): p1, p2 = img1.getpixel((j, i)), img2.getpixel((j, i)) img3.putpixel((j, i), tuple([(p1[k] ^ p2[k]) for k in range(3)])) img3.save(‘flag.png’)

superecc

本题主要考察对于扭曲爱德华曲线方程到 Weierstrass 曲线的转换。

首先我们需要知道,本题使用的曲线方程为

这是一个一般性的扭曲爱德华曲线方程,我们对其变形,让其成为我们熟知的扭曲爱德华曲线形式

接下来,我们需要把扭曲爱德华曲线映射为蒙哥马利曲线,再用蒙哥马利曲线映射到 Weierstrass 曲线。映射方式如下:

映射后,我们分析曲线的阶和点的阶,发现符合Pohlig-Hellman的攻击方式。exp如下

from Crypto.Util.number import *
p = 101194790049284589034264952247851014979689350430642214419992564316981817280629
a = 73101304688827564515346974949973801514688319206271902046500036921488731301311
c = 78293161515104296317366169782119919020288033620228629011270781387408756505563
d = 37207943854782934242920295594440274620695938273948375125575487686242348905415
P.<z> = PolynomialRing(Zmod(p))

aa = a dd = (dc^4)%p J = (2(aa+dd)inverse(aa-dd,p))%p K = (4inverse(aa-dd,p))%p A = ((3-J^2)inverse(3K^2,p))%p B = ((2J^3-9J)inverse(27K^3,p))%p

for i in P(z^3+Az+B).roots(): alpha = int(i[0]) print(kronecker(3alpha^2+A,p)) for j in P(z^2-(3alpha^2+A)).roots(): s = int(j[0]) s = inverse_mod(s, p) if J==alpha3*s%p: Alpha = alpha S = s

def twist_to_weier(x,y): v = xinverse(c,p)%p w = yinverse(c,p)%p assert (aav^2+w^2)%p==(1+ddv^2w^2)%p s = (1+w)inverse_mod(1-w,p)%p t = sinverse(v,p)%p assert (Kt^2)%p==(s^3+Js^2+s)%p xW = (3s+J) * inverse_mod(3K, p) % p yW = t * inverse_mod(K, p) % p assert yW^2 % p == (xW^3+AxW+B) % p return (xW,yW)

def weier_to_twist(x,y): xM=S*(x-Alpha)%p yM=Sy%p assert (KyM^2)%p==(xM^3+JxM^2+xM)%p xe = xMinverse_mod(yM,p)%p ye = (xM-1)inverse_mod(xM+1,p)%p assert (aaxe^2+ye^2)%p==(1+ddxe^2ye^2)%p xq = xec%p yq = yec%p assert (axq^2+yq^2)%p==c^2(1+dxq^2yq^2) return (xq,yq)

E = EllipticCurve(GF(p), [A, B]) G = twist_to_weier(30539694658216287049186009602647603628954716157157860526895528661673536165645,64972626416868540980868991814580825204126662282378873382506584276702563849986) Q = twist_to_weier(98194560294138607903211673286210561363390596541458961277934545796708736630623,58504021112693314176230785309962217759011765879155504422231569879170659690008) P = E(G) Q = E(Q) factors, exponents = zip(factor(E.order())) primes = [factors[i] ^ exponents[i] for i in range(len(factors))][:-2] print(primes) dlogs = [] for fac in primes: t = int(int(P.order()) // int(fac)) dlog = discrete_log(tQ,t*P,operation=”+”) dlogs += [dlog] print(“factor: “+str(fac)+”, Discrete Log: “+str(dlog)) #calculates discrete logarithm for each prime order flag=crt(dlogs,primes) print(long_to_bytes(flag))

当然,在选手wp里,我也看到有师傅在转化为蒙哥马利曲线后,就直接定义曲线,这也是可以的。

dp_promax

本题是Secure_in_N的加强版(Secure_in_N在NSSCTF上有,感兴趣的师傅可以去看看),实际上是作者对论文方法的改进,让界扩大了。

本题特别感谢春哥提供的出题思路和相关实现,春哥在我出这题时给我提供了很多帮助、解答了我很多疑问,如果师傅们对本题内容有什么想法欢迎找我或者春哥讨论。本题DeeBaTo师傅参考了 Small CRT-Exponent RSA Revisited ,此文章的方法是对本题涉及方法的进一步扩展,扩大了本题相关论文的边界,感兴趣的师傅们也可以找DeeBaTo师傅讨论(dbt太强辣)。

这道题目的数据,我在赛前在facdb上查了是没有n的分解的,但是在比赛期间又能查到。更抽象的是分解上传的时间在比赛前,emmmm,算是我出题的一个失误吧,如果直接放远程随机n的形式,效果应该会好一点。revenge方法和本题预期解一样,就不另外写题解了。

2002年,Alexander May发表了文章 Cryptanalysis of Unbalanced RSA with Small CRT-Exponent,在其第四节提到了一种构造多项式和对应格的方法,其界为 。当时May构造的多项式为二元,这就会让格的行列式较大,对于更宽泛的界就难以归约出结果。

2006年,Daniel Bleichenbacher 和 Alexander May 发表了文章 New Attacks on RSA with Small Secret CRT-Exponents,在这篇文章里第四节,两人给当年的多项式,引入了一个对应 的新变量 ,使得 。通过引入这个新变量,降低了y的幂,从而让其行列式变小,当然新变量的幂也会相反增大行列式,因此需要平衡这两者达到规约出结果的目的。本题的实现也是依照第四节中描述的方法实现的。

作者给多项式乘了系数 ,并得到如下多项式:

我们知道 ,所以式子中需要对 的积化简。对于单项式 , 当 ,单项式变为 ,系数 变为 ;当 ,单项式变为 ,系数变为

def trans(f):
    my_tuples = f.exponents(as_ETuples=False)
    g = 0
    for my_tuple in my_tuples:
        exponent = list(my_tuple)
        mon = x ^ exponent[0] * y ^ exponent[1] * z ^ exponent[2]
        tmp = f.monomial_coefficient(mon)
    my_minus = min(exponent[1], exponent[2])
    exponent[1] -= my_minus
    exponent[2] -= my_minus
    tmp *= N^my_minus
    tmp *= x ^ exponent[0] * y ^ exponent[1] * z ^ exponent[2]
    
    g += tmp
return g

然后我们需要由系数向量 构造一个下三角矩阵。这里文章中并未告诉我们如何构造这样的矩阵,所以我们需要自己构造一个偏序。我们可以每次选一个多项式,跟已知的单项式集合做差,如果大小为1,就将这个差作为一个新的主元。

# construct partial order
while len(my_polynomials) > 0:
    for i in range(len(my_polynomials)):
        f = my_polynomials[i]
        current_monomial_set = set(x^tx * y^ty * z^tz for tx, ty, tz in f.exponents(as_ETuples=False))
        delta_set = current_monomial_set - known_set
        if len(delta_set) == 1:
            new_monomial = list(delta_set)[0]
            my_monomials.append(new_monomial)
            known_set |= current_monomial_set
            new_polynomials.append(f)            
            my_polynomials.pop(i)
            break
    else:
        raise Exception('GG')

于是,我们就可以构造出一个下三角矩阵,但由于我们之前化简的时候乘了 ,所以此时在对角线上可能有因数 ,这会让格的行列式大很多。我们需要对其对应的多项式乘上 的逆,以此消去这样的影响,让格的行列式尽可能的小。

# remove N^j
for i in range(nrows):
    Lii = L[i][i]
    N_Power = 1
    while (Lii % N == 0):
        N_Power *= N
        Lii //= N
    L[i][i] = Lii
    for j in range(ncols):
        if (j != i):
            L[i][j] = (L[i][j] * inverse_mod(N_Power, e^m))

此时,我们对格进行LLL规约,会得到两个短的系数向量 ,根据Howgrave Graham定理,他们在整数域上有根 。接下来就需要用式子 ,消去 ,在消元后的二元多项式里找到解即可。

以上就是本题的大致思路,下面对一些用到的参数进行说明,这部分的内容在论文的证明部分,具体就不多赘述。

变量上界

对于变量 文章并没有给出明确的不等式,只说了 充分大,所以 需要慢慢试(也许)。

完整exp如下

from copy import deepcopy
# https://www.iacr.org/archive/pkc2006/39580001/39580001.pdf
# Author: ZM__________J, To1in
N = 46460689902575048279161539093139053273250982188789759171230908555090160106327807756487900897740490796014969642060056990508471087779067462081114448719679327369541067729981885255300592872671988346475325995092962738965896736368697583900712284371907712064418651214952734758901159623911897535752629528660173915950061002261166886570439532126725549551049553283657572371862952889353720425849620358298698866457175871272471601283362171894621323579951733131854384743239593412466073072503584984921309839753877025782543099455341475959367025872013881148312157566181277676442222870964055594445667126205022956832633966895316347447629237589431514145252979687902346011013570026217
e = 13434798417717858802026218632686207646223656240227697459980291922185309256011429515560448846041054116798365376951158576013804627995117567639828607945684892331883636455939205318959165789619155365126516341843169010302438723082730550475978469762351865223932725867052322338651961040599801535197295746795446117201188049551816781609022917473182830824520936213586449114671331226615141179790307404380127774877066477999810164841181390305630968947277581725499758683351449811465832169178971151480364901867866015038054230812376656325631746825977860786943183283933736859324046135782895247486993035483349299820810262347942996232311978102312075736176752844163698997988956692449
alpha = log(e, N)
beta = 0.4090
delta = 50/2200
P.<x,y,z>=PolynomialRing(ZZ)

X = ceil(2 * N^(alpha + beta + delta - 1)) Y = ceil(2 * N^beta) Z = ceil(2 * N^(1 - beta))

def f(x,y): return x*(N-y)+N def trans(f): my_tuples = f.exponents(as_ETuples=False) g = 0 for my_tuple in my_tuples: exponent = list(my_tuple) mon = x ^ exponent[0] * y ^ exponent[1] * z ^ exponent[2] tmp = f.monomial_coefficient(mon)

    my_minus = min(exponent[1], exponent[2])
    exponent[1] -= my_minus
    exponent[2] -= my_minus
    tmp *= N^my_minus
    tmp *= x ^ exponent[0] * y ^ exponent[1] * z ^ exponent[2]
    
    g += tmp
return g

m = 5 # need to be adjusted according to different situations tau = ((1 - beta)^2 - delta) / (2 * beta * (1 - beta)) sigma = (1 - beta - delta) / (2 * (1 - beta))

print(sigma * m) print(tau * m)

s = ceil(sigma * m) t = ceil(tau * m) my_polynomials = [] for i in range(m+1): for j in range(m-i+1): g_ij = trans(e^(m-i) * x^j * z^s * f(x, y)^i) my_polynomials.append(g_ij)

for i in range(m+1): for j in range(1, t+1): h_ij = trans(e^(m-i) * y^j * z^s * f(x, y)^i) my_polynomials.append(h_ij)

known_set = set() new_polynomials = [] my_monomials = []

construct partial order

while len(my_polynomials) > 0: for i in range(len(my_polynomials)): f = my_polynomials[i] current_monomial_set = set(x^tx * y^ty * z^tz for tx, ty, tz in f.exponents(as_ETuples=False)) delta_set = current_monomial_set - known_set if len(delta_set) == 1: new_monomial = list(delta_set)[0] my_monomials.append(new_monomial) known_set |= current_monomial_set new_polynomials.append(f)
my_polynomials.pop(i) break else: raise Exception(‘GG’)

my_polynomials = deepcopy(new_polynomials)

nrows = len(my_polynomials) ncols = len(my_monomials) L = [[0 for j in range(ncols)] for i in range(nrows)]

for i in range(nrows): g_scale = my_polynomials[i](X * x, Y * y, Z * z) for j in range(ncols): L[i][j] = g_scale.monomial_coefficient(my_monomials[j])

remove N^j

for i in range(nrows): Lii = L[i][i] N_Power = 1 while (Lii % N == 0): N_Power *= N Lii //= N L[i][i] = Lii for j in range(ncols): if (j != i): L[i][j] = (L[i][j] * inverse_mod(N_Power, e^m))

L = Matrix(ZZ, L) nrows = L.nrows()

L = L.LLL()

Recover poly

reduced_polynomials = [] for i in range(nrows): g_l = 0 for j in range(ncols): g_l += L[i][j] // my_monomials[j](X, Y, Z) * my_monomials[j] reduced_polynomials.append(g_l)

eliminate z

my_ideal_list = [y * z - N] + reduced_polynomials

Variety

my_ideal_list = [Hi.change_ring(QQ) for Hi in my_ideal_list] for i in range(len(my_ideal_list),3,-1): print(i) V = Ideal(my_ideal_list[:i]).variety(ring=ZZ) print(V) #[{z: 12802692475349485610473781287027553390253771106432654573503896144916729600503566568750388778199186889792482907407718147190530044920232683163941552482789952714283570754056433670735303357508451647073211371989388879428065367142825533019883798121260838408498282273223302509241229258595176130544371781524298142815099491753819782913040582455136982147010841337850805642005577584947943848348285516563, y: 3628978044425516256252147348112819551863749940058657194357489608704171827031473111609089635738827298682760802716155197142949509565102167059366421892847010862457650295837231017990389942425249509044223464186611269388650172307612888367710149054996350799445205007925937223059, x: 405726385819346439624373295724174904587513703028810607887196207245590677314635812068207279608967556024765942887396159920943510998058855747055443081960935792673829634461468743022155648395228589917369402664408573172965003110508338065148371590644205978916495280169653332814813227881426466}]

from Crypto.Util.number import *
q = 3628978044425516256252147348112819551863749940058657194357489608704171827031473111609089635738827298682760802716155197142949509565102167059366421892847010862457650295837231017990389942425249509044223464186611269388650172307612888367710149054996350799445205007925937223059
N = 46460689902575048279161539093139053273250982188789759171230908555090160106327807756487900897740490796014969642060056990508471087779067462081114448719679327369541067729981885255300592872671988346475325995092962738965896736368697583900712284371907712064418651214952734758901159623911897535752629528660173915950061002261166886570439532126725549551049553283657572371862952889353720425849620358298698866457175871272471601283362171894621323579951733131854384743239593412466073072503584984921309839753877025782543099455341475959367025872013881148312157566181277676442222870964055594445667126205022956832633966895316347447629237589431514145252979687902346011013570026217
p = N//q
c = 28467178002707221164289324881980114394388495952610702835708089048786417144811911352052409078910656133947993308408503719003695295117416819193221069292199686731316826935595732683131084358071773123683334547655644422131844255145301597465782740484383872480344422108506521999023734887311848231938878644071391794681783746739256810803148574808119264335234153882563855891931676015967053672390419297049063566989773843180697022505093259968691066079705679011419363983756691565059184987670900243038196495478822756959846024573175127669523145115742132400975058579601219402887597108650628746511582873363307517512442800070071452415355053077719833249765357356701902679805027579294
e = 13434798417717858802026218632686207646223656240227697459980291922185309256011429515560448846041054116798365376951158576013804627995117567639828607945684892331883636455939205318959165789619155365126516341843169010302438723082730550475978469762351865223932725867052322338651961040599801535197295746795446117201188049551816781609022917473182830824520936213586449114671331226615141179790307404380127774877066477999810164841181390305630968947277581725499758683351449811465832169178971151480364901867866015038054230812376656325631746825977860786943183283933736859324046135782895247486993035483349299820810262347942996232311978102312075736176752844163698997988956692449
d = inverse(e,(p-1))
print(d)
print(long_to_bytes(int(pow(int(c),int(d),int(p)))))
 

写在后面

时间过的真快,上上届的我还是只会签到的参赛选手,上届的我只是一个组织者(背锅人),今年就以出题人的身份和大家见面了。这次题目质量不知道师傅们是否满意,如果对某道密码题有什么想交流的欢迎来找我。

不知道明年会以什么身份和大家见面呢👻。