SUSCTF 2022

SUSCTF

large case

from Crypto.Util.number import *
from secret import e,message

def pad(s):
    if len(s)<3*L:
        s+=bytes(3*L-len(s))
    return s

L=128
p=127846753573603084140032502367311687577517286192893830888210505400863747960458410091624928485398237221748639465569360357083610343901195273740653100259873512668015324620239720302434418836556626441491996755736644886234427063508445212117628827393696641594389475794455769831224080974098671804484986257952189021223
q=145855456487495382044171198958191111759614682359121667762539436558951453420409098978730659224765186993202647878416602503196995715156477020462357271957894750950465766809623184979464111968346235929375202282811814079958258215558862385475337911665725569669510022344713444067774094112542265293776098223712339100693
r=165967627827619421909025667485886197280531070386062799707570138462960892786375448755168117226002965841166040777799690060003514218907279202146293715568618421507166624010447447835500614000601643150187327886055136468260391127675012777934049855029499330117864969171026445847229725440665179150874362143944727374907
n=p*q*r

assert isPrime(GCD(e,p-1)) and isPrime(GCD(e,q-1)) and isPrime(GCD(e,r-1)) and e==GCD(e,p-1)*GCD(e,q-1)*GCD(e,r-1)
assert len(message)>L and len(message)<2*L
assert b'SUSCTF' in message
m=bytes_to_long(pad(message))

c=pow(m,e,n)
print(c)
'''
2832775557487418816663494645849097066925967799754895979829784499040437385450603537732862576495758207240632734290947928291961063611897822688909447511260639429367768479378599532712621774918733304857247099714044615691877995534173849302353620399896455615474093581673774297730056975663792651743809514320379189748228186812362112753688073161375690508818356712739795492736743994105438575736577194329751372142329306630950863097761601196849158280502041616545429586870751042908365507050717385205371671658706357669408813112610215766159761927196639404951251535622349916877296956767883165696947955379829079278948514755758174884809479690995427980775293393456403529481055942899970158049070109142310832516606657100119207595631431023336544432679282722485978175459551109374822024850128128796213791820270973849303929674648894135672365776376696816104314090776423931007123128977218361110636927878232444348690591774581974226318856099862175526133892
'''

RSA加密中e与φ不互素的情形,我们考虑AMM算法进行有限域内的寻根操作

首先先确定e的几个因子

先确定e的取值,设gi分别是e与p,q,r的因数

\(M^{g_1g_2g_3}=C(modN)\)

\(C^{\frac{p-1}{g_1}}=1(modp)\)

按照这个思路确定g

发现g1=757,g2=66553 or 2 g3=5156273 or 2

若g2=2=>g3=2=>e=2*757

直接用f.roots()很容易验证无根

=>e=757*66553 *5156273

下面考虑高次剩余,由于考虑到高次剩余问题最后需要用到crt进行处理,所以次数不应该太大,这里我们考虑抹去e3,于是我们在c的基础上乘一个2(-Le)缩小M的值,把问题化到模pq上,这样我们就无需处理与r-1不互素的e3

import random
import time
import gmpy2
# About 3 seconds to run
def AMM(o, r, q):
    start = time.time()
    print('\n----------------------------------------------------------------------------------')
    print('Start to run Adleman-Manders-Miller Root Extraction Method')
    print('Try to find one {:#x}th root of {} modulo {}'.format(r, o, q))
    g = GF(q)
    o = g(o)
    p = g(random.randint(1, q))
    while p ^ ((q-1) // r) == 1:
        p = g(random.randint(1, q))
    print('[+] Find p:{}'.format(p))
    t = 0
    s = q - 1
    while s % r == 0:
        t += 1
        s = s // r
    print('[+] Find s:{}, t:{}'.format(s, t))
    k = 1
    while (k * s + 1) % r != 0:
        k += 1
    alp = (k * s + 1) // r
    print('[+] Find alp:{}'.format(alp))
    a = p ^ (r**(t-1) * s)
    b = o ^ (r*alp - 1)
    c = p ^ s
    h = 1
    for i in range(1, t):
        d = b ^ (r^(t-1-i))
        if d == 1:
            j = 0
        else:
            print('[+] Calculating DLP...')
            j = - discrete_log(d, a)
            print('[+] Finish DLP...')
        b = b * (c^r)^j
        h = h * c^j
        c = c^r
    result = o^alp * h
    end = time.time()
    print("Finished in {} seconds.".format(end - start))
    print('Find one solution: {}'.format(result))
    return result

def findAllPRoot(p, e):
    print("Start to find all the Primitive {:#x}th root of 1 modulo {}.".format(e, p))
    start = time.time()
    proot = set()
    while len(proot) < e:
        proot.add(pow(random.randint(2, p-1), (p-1)//e, p))
    end = time.time()
    print("Finished in {} seconds.".format(end - start))
    return proot

def findAllSolutions(mp, proot, cp, p):
    print("Start to find all the {:#x}th root of {} modulo {}.".format(e, cp, p))
    start = time.time()
    all_mp = set()
    for root in proot:
        mp2 = mp * root % p
        all_mp.add(mp2)
    end = time.time()
    print("Finished in {} seconds.".format(end - start))
    return all_mp

def check(m):
    h = int(m).to_bytes(300,'big')
    if b'SUSCTF' in h:
        print(h)
        return True
    else:
        return False
    
p=127846753573603084140032502367311687577517286192893830888210505400863747960458410091624928485398237221748639465569360357083610343901195273740653100259873512668015324620239720302434418836556626441491996755736644886234427063508445212117628827393696641594389475794455769831224080974098671804484986257952189021223
q=145855456487495382044171198958191111759614682359121667762539436558951453420409098978730659224765186993202647878416602503196995715156477020462357271957894750950465766809623184979464111968346235929375202282811814079958258215558862385475337911665725569669510022344713444067774094112542265293776098223712339100693
r=165967627827619421909025667485886197280531070386062799707570138462960892786375448755168117226002965841166040777799690060003514218907279202146293715568618421507166624010447447835500614000601643150187327886055136468260391127675012777934049855029499330117864969171026445847229725440665179150874362143944727374907
n=p*q*r
c=2832775557487418816663494645849097066925967799754895979829784499040437385450603537732862576495758207240632734290947928291961063611897822688909447511260639429367768479378599532712621774918733304857247099714044615691877995534173849302353620399896455615474093581673774297730056975663792651743809514320379189748228186812362112753688073161375690508818356712739795492736743994105438575736577194329751372142329306630950863097761601196849158280502041616545429586870751042908365507050717385205371671658706357669408813112610215766159761927196639404951251535622349916877296956767883165696947955379829079278948514755758174884809479690995427980775293393456403529481055942899970158049070109142310832516606657100119207595631431023336544432679282722485978175459551109374822024850128128796213791820270973849303929674648894135672365776376696816104314090776423931007123128977218361110636927878232444348690591774581974226318856099862175526133892
e1=757
e2=66553
e3=5156273
e=e1*e2*e3
c=c*gmpy2.invert(int(pow(2,1024*e,n)),n)
d1=gmpy2.invert(e2*e3,p-1)
cp=pow(c,d1,p)
d2=gmpy2.invert(e1*e3,q-1)
cq=pow(c,d2,q)
print(cp,cq)
mp = AMM(cp, e1, p)
mq = AMM(cq, e2, q)
p_proot = findAllPRoot(p, e1)
q_proot = findAllPRoot(q, e2)
mps = findAllSolutions(mp, p_proot, cp, p)
mqs = findAllSolutions(mq, q_proot, cq, q)
print (mps, mqs)

start = time.time()
print('Start CRT...')
for mpp in mps:
    for mqq in mqs:
        solution = CRT_list([int(mpp), int(mqq)], [p, q])
        if check(solution):
            print(solution)
    print(time.time() - start)

end = time.time()
print("Finished in {} seconds.".format(end - start))

InverseProblem

import numpy as np
from secret import flag

def gravity(n,d=0.25):
    A=np.zeros([n,n])
    for i in range(n):
        for j in range(n):
            A[i,j]=d/n*(d**2+((i-j)/n)**2)**(-1.5)
    return A

n=len(flag)
A=gravity(n)
x=np.array(list(flag))
b=A@x
np.savetxt('b.txt',b)

由于精度问题造成的误差,看了下学长的wp,说是python在计算float可能产生误差
但是每次误差不会超过2-53

image

实验了一下,就很神奇,原因是计算机采用二进制进行计算而十进制在向二进制转换的过程中如果二进制是无限的计算机在储存的过程中就会舍弃一部分造成计算上的精度问题

于是我们可以得到式子

C=Am+ε

在已知C与A的情况下求m,即LWE问题,这样就简单了

先对数据整数化处理一下,调用babai解cvp

import numpy as np
def babai(A, w):
    A = A.BKZ(block_siaze=24)
    G = A.gram_schmidt()[0]
    t = w
    for i in reversed(range(A.nrows())):
        c = ((t * G[i]) / (G[i] * G[i])).round()
        t -= A[i] * c
    return w - t

def gravity(n,d=0.25):
    A=np.zeros([n,n])
    for i in range(n):
        for j in range(n):
            A[i,j]=d/n*(d**2+((i-j)/n)**2)**(-1.5)
    return A
A=gravity(85)
for i in range(len(A)):
    for j in range(len(A)):
        A[i][j]=int(A[i][j]*10^18)
c=[3.657060500339054556e+02,3.832239212422502419e+02,4.006400878420690219e+02,4.178419900792603698e+02,4.347228858757071634e+02,4.511847676148433948e+02,4.671407458110251127e+02,4.825167947918074560e+02,4.972528090932780174e+02,5.113029695817293714e+02,5.246354631948004226e+02,5.372316391653928349e+02,5.490847173300799113e+02,5.601981891814167511e+02,5.705840667157972348e+02,5.802611340293560716e+02,5.892533389518160902e+02,5.975884263745526823e+02,6.052968650055386206e+02,6.124110630374365201e+02,6.189648165161182760e+02,6.249928981118596312e+02,6.305306816020913629e+02,6.356137112146116124e+02,6.402771610768811570e+02,6.445551788138646998e+02,6.484801562386791147e+02,6.520820067831411961e+02,6.553875449515115861e+02,6.584200543535777115e+02,6.611991006926840555e+02,6.637406026122149569e+02,6.660571276935107790e+02,6.681583443270599219e+02,6.700515409542964562e+02,6.717421256877905762e+02,6.732340393774057929e+02,6.745300469406174670e+02,6.756319058650934721e+02,6.765404381911505425e+02,6.772555468921494821e+02,6.777762178159265432e+02,6.781005372262200126e+02,6.782257385537030814e+02,6.781482768722584069e+02,6.778639205835502253e+02,6.773678481496929180e+02,6.766547414782241958e+02,6.757188729437999655e+02,6.745541865153842309e+02,6.731543734407333659e+02,6.715129401333222177e+02,6.696232624726493441e+02,6.674786187460366591e+02,6.650721936401939729e+02,6.623970470419692447e+02,6.594460419709976122e+02,6.562117241643640000e+02,6.526861416758931682e+02,6.488605883801759546e+02,6.447252539940367342e+02,6.402687687915280321e+02,6.354776457871529374e+02,6.303356463148024886e+02,6.248231231031890047e+02,6.189164222318630664e+02,6.125874450187269531e+02,6.058034773416537746e+02,5.985273843207024811e+02,5.907182434535461653e+02,5.823324532363482149e+02,5.733253130130134423e+02,5.636530294391988036e+02,5.532750701442269019e+02,5.421567579447032585e+02,5.302719786604150158e+02,5.176058598538473916e+02,5.041572643016256734e+02,4.899409301857818377e+02,4.749890824218749685e+02,4.593523418788242338e+02,4.430997787892412703e+02,4.263179996640761260e+02,4.091092256773656004e+02,3.915884105025300528e+02]
for i in range(len(c)):
    c[i]=int(c[i]*10^18)
A=Matrix(ZZ,A)
c=vector(ZZ,c)
L=babai(A,c)
m=A^(-1)*L
print(bytes(list(m)))

SpecialCurve3

from Crypto.Util.number import *
from secret import flag,getMyPrime
import hashlib
import random

class SpecialCurve:
    def __init__(self,p,a,b):
        self.p=p
        self.a=a
        self.b=b

    def __str__(self):
        return f'SpecialCurve({self.p},{self.a},{self.b})'

    def add(self,P1,P2):
        x1,y1=P1
        x2,y2=P2
        if x1==0:
            return P2
        elif x2==0:
            return P1
        elif x1==x2 and (y1+y2)%self.p==0:
            return (0,0)
        if P1==P2:
            t=(2*self.a*x1-self.b)*inverse(2*y1,self.p)%self.p
        else:
            t=(y2-y1)*inverse(x2-x1,self.p)%self.p
        x3=self.b*inverse(self.a-t**2,self.p)%self.p
        y3=x3*t%self.p
        return (x3,y3)

    def mul(self,P,k):
        assert k>=0
        Q=(0,0)
        while k>0:
            if k%2:
                k-=1
                Q=self.add(P,Q)
            else:
                k//=2
                P=self.add(P,P)
        return Q

def problem(size,k):
    p=getMyPrime(size)
    x=random.randint(1,p-1)
    y=random.randint(1,p-1)
    e=random.randint(1,p-1)
    a=k*random.randint(1,p-1)**2%p
    b=(a*x**2-y**2)*inverse(x,p)%p
    curve=SpecialCurve(p,a,b)
    G=(x,y)
    Q=curve.mul(G,e)
    print(f'curve={curve}')
    print(f'G={G}')
    print(f'Q={Q}')
    return e

e1=problem(128,1)
e2=problem(256,0)
e3=problem(512,-1)
enc=bytes_to_long(hashlib.sha512(b'%d-%d-%d'%(e1,e2,e3)).digest())^bytes_to_long(flag.encode())
print(f'enc={enc}')
'''
curve=SpecialCurve(233083587295210134948821000868826832947,73126617271517175643081276880688551524,88798574825442191055315385745016140538)
G=(183831340067417420551177442269962013567, 99817328357051895244693615825466756115)
Q=(166671516040968894138381957537903638362, 111895361471674668502480740000666908829)
curve=SpecialCurve(191068609532021291665270648892101370598912795286064024735411416824693692132923,0,58972296113624136043935650439499285317465012097982529049067402580914449774185)
G=(91006613905368145804676933482275735904909223655198185414549961004950981863863, 96989919722797171541882834089135074413922451043302800296198062675754293402989)
Q=(13504049588679281286169164714588439287464466303764421302084687307396426249546, 110661224324697604640962229701359894201176516005657224773855350780007949687952)
curve=SpecialCurve(52373730653143623993722188411805072409768054271090317191163373082830382186155222057388907031638565243831629283127812681929449631957644692314271061305360051,28655236915186704327844312279364325861102737672471191366040478446302230316126579253163690638394777612892597409996413924040027276002261574013341150279408716,42416029226399083779760024372262489355327595236815424404537477696856946194575702884812426801334149232783155054432357826688204061261064100317825443760789993)
G=(15928930551986151950313548861530582114536854007449249930339281771205424453985946290830967245733880747219865184207937142979512907006835750179101295088805979, 29726385672383966862722624018664799344530038744596171136235079529609085682764414035677068447708040589338778102975312549905710028842378574272316925268724240)
Q=(38121552296651560305666865284721153617113944344833289618523344614838728589487183141203437711082603199613749216407692351802119887009907921660398772094998382, 26933444836972639216676645467487306576059428042654421228626400416790420281717654664520663525738892984862698457685902674487454159311739553538883303065780163)
enc=4161358072766336252252471282975567407131586510079023869994510082082055094259455767245295677764252219353961906640516887754903722158044643700643524839069337
'''

第一次接触conic上的加密算法,感觉和ecc有点类似

题目中给出了三条曲线,分别对应着过原点的椭圆,抛物线,双曲线

对于椭圆我们考虑生成过程中t的变化

记第n个点对应的t为tn

=>tn+1=\(\frac{2ax_n-b}{2y_n}=\frac{2a-b/x_n}{2y_n/x_n}=\frac{a+t_n^2}{2t_n}\)(※)

考虑不动点构造递推=>\(\frac{t_{n+1}-\sqrt{a}}{t_{n+1}+\sqrt{a}}=(\frac{t_{n}-\sqrt{a}}{t_{n}+\sqrt{a}})^2\)

于是我们将A+A->\((\frac{t_{n}-\sqrt{a}}{t_{n}+\sqrt{a}})*(\frac{t_{n}-\sqrt{a}}{t_{n}+\sqrt{a}})\)

即我们将曲线上的点加同构到了GF(p)上的dlp问题,又注意到p-1较光滑,可解

对于抛物线我们同样考虑t的变化

由(※)=>\(t_{n+1}=\frac{t_n}{2}=>\frac{1}{t_{n+1}}=\frac{2}{t_n}\)

于是我们将A+A->\(\frac{1}{t_n}+\frac{1}{t_n}\)

即我们将曲线上的点加同构到了GF(p)上的加法运算

对于第三个未找到好的同构方式,学长说要注意题目所给数据的独特性,我们可以看的每个曲线所在的素数域都是由getMyPrime所得出,于是尝试了一下发现p+1是光滑的,这个时候可以猜到阶应该就是p+1了,看了下学长的wp似乎是找到了papper但是忘记在哪了,Google了一下我也没找到相应的证明方式只能先记着了

image

这下就没难度了,直接pohlig-hellman

import gmpy2
import hashlib
class SpecialCurve:
    def __init__(self,p,a,b):
        self.p=p
        self.a=a
        self.b=b

    def __str__(self):
        return f'SpecialCurve({self.p},{self.a},{self.b})'

    def add(self,P1,P2):
        x1,y1=P1
        x2,y2=P2
        if x1==0:
            return P2
        elif x2==0:
            return P1
        elif x1==x2 and (y1+y2)%self.p==0:
            return (0,0)
        if P1==P2:
            t=(2*self.a*x1-self.b)*gmpy2.invert(2*y1,self.p)%self.p
        else:
            t=(y2-y1)*gmpy2.invert(x2-x1,self.p)%self.p
        x3=self.b*gmpy2.invert(self.a-t**2,self.p)%self.p
        y3=x3*t%self.p
        return (x3,y3)

    def mul(self,P,k):
        assert k>=0
        Q=(0,0)
        while k>0:
            if k%2:
                k-=1
                Q=self.add(P,Q)
            else:
                k//=2
                P=self.add(P,P)
        return Q 
#------
curve1=(233083587295210134948821000868826832947,73126617271517175643081276880688551524,88798574825442191055315385745016140538)
G_1=(183831340067417420551177442269962013567, 99817328357051895244693615825466756115)
Q_1=(166671516040968894138381957537903638362, 111895361471674668502480740000666908829)
curve2=(191068609532021291665270648892101370598912795286064024735411416824693692132923,0,58972296113624136043935650439499285317465012097982529049067402580914449774185)
G_2=(91006613905368145804676933482275735904909223655198185414549961004950981863863, 96989919722797171541882834089135074413922451043302800296198062675754293402989)
Q_2=(13504049588679281286169164714588439287464466303764421302084687307396426249546, 110661224324697604640962229701359894201176516005657224773855350780007949687952)
curve3=(52373730653143623993722188411805072409768054271090317191163373082830382186155222057388907031638565243831629283127812681929449631957644692314271061305360051,28655236915186704327844312279364325861102737672471191366040478446302230316126579253163690638394777612892597409996413924040027276002261574013341150279408716,42416029226399083779760024372262489355327595236815424404537477696856946194575702884812426801334149232783155054432357826688204061261064100317825443760789993)
G_3=(15928930551986151950313548861530582114536854007449249930339281771205424453985946290830967245733880747219865184207937142979512907006835750179101295088805979, 29726385672383966862722624018664799344530038744596171136235079529609085682764414035677068447708040589338778102975312549905710028842378574272316925268724240)
Q_3=(38121552296651560305666865284721153617113944344833289618523344614838728589487183141203437711082603199613749216407692351802119887009907921660398772094998382, 26933444836972639216676645467487306576059428042654421228626400416790420281717654664520663525738892984862698457685902674487454159311739553538883303065780163)
enc=4161358072766336252252471282975567407131586510079023869994510082082055094259455767245295677764252219353961906640516887754903722158044643700643524839069337
#------
p1=curve1[0]
a1=curve1[1]
G=GF(p1)
PR.<x>=PolynomialRing(Zmod(p1))
f=x^2-a1
a_=int(f.roots()[0][0])
Gx=G_1[0]
Gy=G_1[1]
Qx=Q_1[0]
Qy=Q_1[1]
t1=int(Gy*gmpy2.invert(Gx,p1)%p1)
t2=int(Qy*gmpy2.invert(Qx,p1)%p1)
T1=(t1-a_)*gmpy2.invert(t1+a_,p1)%p1
T2=(t2-a_)*gmpy2.invert(t2+a_,p1)%p1
T1=G(T1)
T2=G(T2)
e1=T2.log(T1)
e1=int(e1)
print(e1)
#------
p2=curve2[0]
Gx=G_2[0]
Gy=G_2[1]
Qx=Q_2[0]
Qy=Q_2[1]
t1=Gy*gmpy2.invert(Gx,p2)%p2
t2=Qy*gmpy2.invert(Qx,p2)%p2
e2=t1*gmpy2.invert(t2,p2)%p2
e2=int(e2)
print(e2)
#------
p3=curve3[0]
curve3=SpecialCurve(52373730653143623993722188411805072409768054271090317191163373082830382186155222057388907031638565243831629283127812681929449631957644692314271061305360051,28655236915186704327844312279364325861102737672471191366040478446302230316126579253163690638394777612892597409996413924040027276002261574013341150279408716,42416029226399083779760024372262489355327595236815424404537477696856946194575702884812426801334149232783155054432357826688204061261064100317825443760789993)
factors, exponents = zip(*factor(p3+1))
primes = [factors[i] ^ exponents[i] for i in range(len(factors))]
K=[0]
for i in primes:
    t=(p3+1)//i
    GG=curve3.mul(G_3,t)
    QQ=curve3.mul(Q_3,t)
    TT=(0,0)
    for j in range(1,i):
        TT=curve3.add(TT,GG)
        if TT==QQ:
            K.append(j)
            break
print(K)
e3=crt(K,primes)
e3=int(e3)
print(e3)


import hashlib
from Crypto.Util.number import *
enc=4161358072766336252252471282975567407131586510079023869994510082082055094259455767245295677764252219353961906640516887754903722158044643700643524839069337
e1=184572164865068633286768057743716588370
e2=131789829046710687154053378348742202935151384644040019239219239301007568911745
e3=23331486889781766099145299968747599730779731613118514070077298627895623872695507249173953050022392729611030101946661150932813447054695843306184318795467216
m=long_to_bytes(bytes_to_long(hashlib.sha512(b'%d-%d-%d'%(e1,e2,e3)).digest())^enc)
print(m)

Ez_Pager_Tiper

problem.py

from Crypto.Util.number import *
from magic_box import *
from secret import mask1, mask2, seed1, seed2, seed3
n1, n2 = 64, 12

flag = 'SUSCTF{***}'

def encrypt(cipher, ipath, opath):
    ifile=open(ipath,'rb')
    ofile=open(opath,'wb')
    plaintext=ifile.read()
    for ch in plaintext:
        c=ch^cipher.getrandbit(8)
        ofile.write(long_to_bytes(c))
    ifile.close()
    ofile.close()

def problem1():
    r = getRandomInteger(6)
    magic = 1<<r
    lfsr1 = lfsr(seed1, mask1, n1)
    lfsr2 = lfsr(seed2, mask2, n2)
    cipher = generator(lfsr1, lfsr2, magic)
    encrypt(cipher, "MTk4NC0wNC0wMQ==_6d30.txt", "MTk4NC0wNC0wMQ==_6d30.enc")

def problem2():
    magic = getPrime(64)
    lfsr1=lfsr(seed1, mask1, n1)
    lfsr2=lfsr(seed3, mask2, n2)
    cipher = generator(lfsr1, lfsr2, magic)
    encrypt(cipher, "MTk4NC0xMi0yNQ==_76ff.txt", "MTk4NC0xMi0yNQ==_76ff.enc")
    # flag in it?
    print(f'hint={magic}')
    # hint = 15193544052573546419

problem1()
problem2()

magicbox.py

class lfsr():
    def __init__(self, seed, mask, length):
        self.length_mask = 2 ** length - 1
        self.mask = mask & self.length_mask
        self.state = seed & self.length_mask

    def next(self):
        next_state = (self.state << 1) & self.length_mask
        i = self.state & self.mask & self.length_mask
        output = 0
        while i != 0:
            output ^= (i & 1)
            i = i >> 1
        next_state ^= output
        self.state = next_state
        return output

    def getrandbit(self, nbit):
        output = 0
        for _ in range(nbit):
            output = (output << 1) ^ self.next()
        return output


class generator():
    def __init__(self, lfsr1, lfsr2, magic):
        self.lfsr1 = lfsr1
        self.lfsr2 = lfsr2
        self.magic = magic

    def infinit_power(self, magic):
        return int(magic)

    def malicious_magic(self, magic):
        now = (-magic & magic)
        magic ^= now
        return int(now), int(magic)

    def confusion(self, c1, c2):
        magic = self.magic
        output, cnt = magic, 0
        output ^= c1 ^ c2
        while magic:
            now, magic = self.malicious_magic(magic)
            cnt ^= now >> (now.bit_length() - 1)
            output ^= now
        output ^= cnt * c1
        return int(output)

    def getrandbit(self, nbit):
        output1 = self.lfsr1.getrandbit(nbit)
        output2 = self.lfsr2.getrandbit(nbit)
        return self.confusion(output1, output2)

一道非线性组合生成器,混淆的操作我是真没看懂,但是实验几次会发现如果magic是2的幂次相对于只使用lfsr2进行加密,而题目给出的magic2使得每次加密都是明文两个lfsr的结果异或

注意到给出的data数据前面几个字符似乎都是日期,并且你将加密文件的名字base64转码会发现是差不多的,于是我们可以相信密文正确解密后开始应该是类似的形式

这相当于你已经已知了16个字节的明文也就是128bits

而我们知道对于lfsr来说已知连续2n个bits即可解出掩码

于是对于problem1我们可以采取爆破224(这个地方我们只去解密文件的前四字节判断是否为date,这样的话时间开销还是能够接受的)或者直接逆向lfsr来获取mask,两种方式都是可行的

解决problem2时由于已知mask2,我们遍历seed3,同上操作通过已知16字节明文来恢复mask1和seed1,然后check一下Date和SUSCTF即可

from tqdm import tqdm
f1=open('MTk4NC0wNC0wMQ==_6d30.enc','rb')
c1=f1.read()

class lfsr():
    def __init__(self, seed, mask, length):
        self.length_mask = 2 ** length - 1
        self.mask = mask & self.length_mask
        self.state = seed & self.length_mask

    def next(self):
        next_state = (self.state << 1) & self.length_mask
        i = self.state & self.mask & self.length_mask
        output = 0
        while i != 0:
            output ^= (i & 1)
            i = i >> 1
        next_state ^= output
        self.state = next_state
        return output

    def getrandbit(self, nbit):
        output = 0
        for _ in range(nbit):
            output = (output << 1) ^ self.next()
        return output


def decrypt(c,gen):
    m=''
    for i in range(4):
        ch=c[i]
        m+=chr(ch^gen.getrandbit(8))
    return m

n=12
for se in tqdm(range(1,2**12)):
    for ma in (range(1,2**12)):
        lfsr2 = lfsr(se, ma, n)
        m=decrypt(c1,lfsr2)
        if 'Date' in m:
            print(se)
            print(ma)
            print(m)


#------

magic=15193544052573546419
def malicious_magic(magic):
    now = (-magic & magic)
    magic ^= now
    return int(now), int(magic)
cnt=0
output=magic
while magic:
    now, magic = malicious_magic(magic)
    cnt ^= now >> (now.bit_length() - 1)
    output ^= now
print(cnt)
print(output)

#------

c=b'\x18\xff\xa57\xa65"\x00\xfd/\x8d\x06\xe7z\xa4\xe6\t$\xec\x94$`\xaalB\xb6\x90`\x9e\'7\x9f\xcca\xaa1\x96\x19\t\xa2\xb8U\xde\xc5\xa0\xc7\xd23\xcd\xa0\xafRHP\x90\x8a\xa9M\x17@\xef8:]\xe1\xdc\x10\xad\xdfI\x04=\x01\x82\x1a\xec\x1e\x19\xdaV\x95\xc1K\x86\xfdZ\x90O7r\xeeZCewY8\xf1\x80\x81\x16NC\x94\xb0\xa0<\xd5\xc9\x1a\xeb.\xf6\xaa\xbb\xa6\x9a<t\xce\xdcQ$\xfdK\x89v\xee\xe0\x9dc\x9b6\xe6\xf0\xc9\xb6[l\xd3\xdc\xf8\n\xb7\xc6\xf9^\x0eIr\'>\x1dD!\x83\xfd\xc6Q\xf9\xce\xee%\xa7l\xb9\xfc\xcc\xf9;\x0b\x04\xce\x07\x97\xae\xf5C\xa5\x96\xfeU\xe8\xfb\x06\x96\xe3Dr\x8a\xc8\xb7\xa0\xe4s\xe3\xac\x9dT\t\x0eL*Vys\x03\xbb\xf2$\xa8pR\x8c\xa8h\xf6\x04<I4@}\xe5\x12\x8d\x14\xbe\xe4\xb1\x86V`\xcf\x9bE\x8e\xf0m\xbd\xedP\xe3\xafo7\xbd\xb1\xb9R\x9a2*\xaabz@\xb2\xf9\xf59c\xf9\x13\xf12\x8b\xc2z\xda\xf4\x87\x130\xc2\x93\xf3\xce\x84\xe8q.\x01\xeec\xb1\x10X\xcd\x00\x91%\xb7|yW\xf2\xc4\xb3\x997\xc1\xb45\xe5)K \xfc\x93\x04\xee-\xf9\xc3\x06Js\xe1\tZ\x86=k8:\x17\x9e}\xb1$\xce\xaa\xbc\x05\x97d\x83c~\xf57\x02\x08\xa2\tHz\xecq\x07Y\xe6}\xf8\xf0\xaa\x00\x1e\x12f\xf5\xab\xf62u\x12\xf5\xcfx\xc3)B\xd4\xd1\x9dxX\x0b#\x9f\xf1\xa0"\xb9M\xe9\xcf\x17\xc9\\\xbf\xf3y\xb1E\xc0\xa1\x88\x83\x95\xa6\xc9\xde=Md\xaf\xb4\x14\x815;\x18X\x9bQ2\x86\xa0`x{\x18\xde\xean\xcd\xc3*y\x82\x8c\xe4l\x88\x96\xb7\x0e\x03u\xe7\xe8."!\xfb\xb1Bg\xea\xa2`\x99\xab\xa4d\xedA\x0e\x86\xd5\xaf5D\'\xcd3\xbf\xcd5\xb4\xc3^\xfe\x14t\x90\xf9\x01\x98\xd5~s\xdc\xa7\xb1\x8d\xf6\xd9\x92\xb9\xe0\x996?\x8f\\\x1d\xb2\x12\x97\xf6\x07;\xfaTL\x92.\xaf\x16$\xfa\x94\xcb0\xbf\x8a\xad](\xc6}K\xc87\xfc\xc9\xc6\xdb\x08\x99\x9f(<\x1e\x97\xe1\x00^\x13]\x16\xe13\xee\xebS1h\xdd\xe11n\x1a\xe6\x12\x01\xb5\x0c|\xf5\x8a\xbd=5S\xe4\xf9$\xf1\xedp\xce\x1f\x96.\x97\xe4;\x02 \x17&\xd7\xd9*\x1f\xa4u\xe6\xad\xfd\xbaIR\x1f\x1a=F\xbf\xf3\x07\x19!=\xf7\xd8/\xe4i\xc6+\x9c5^t\x7f\xb2\x06\xe8\xf2\xac=\x16\x00r\xdd\x7f\xe34w\xde)\x86\x82\xb6gj\xf4\xfa\x18Pd\xd9\x82\xcd\xda\xee\x8bv\xd1NKu\\\x04\xe2\xbbt\x94\x82\x11\xc9\x1d\xdd\xb1\xb5\x8c\x86\xbfg\xa8L\x1eI\xb4\xddF\x8b\xa7\xd1\x16\x9c\x80\x94UXS\x13\x91\xf8\xe6=\x15\x16\x9a,\x0b8\xd2\xfeE\xa04"\xaa1}\xc7\x93\x9f\xb2%p\xb4\x01\x17r*\xd5\xc6\xfd\xfb\xb0<\x18j\x86\xab\xe0\x17\xf2R\xdfZ\xc3Ty\xe9\xd2j\x8b\x15\t\xabrHwi~1K\x89\xebVZ\xec}\x1av\xc5\x90\xb7\xa5$k\xd3"\x05\xfa\xc6\x1d\xfe\xb5\x9e;#ig\xdd\xa3\x9b.[\x96\xf1r\x01D\xe7\xdeqkj%\x9cvU\xec\x04\xb0\xab\x9e4\x13\xd7\x07\xa8\xcarK<-\xd1x\xb7|\xdf\xc5c\x13R\xd9\x1b\xd8\xa0=F\xbc9\xf2\x8c\xfc\xe8KO\xb6bfv\xaf\xb3\x9c\xa4\x1f(\xc4O\xa9\x0f]x\x832\x89\xd9\xc77\x1fa0\xe4\xac\xa7AA\xc4\x14\x9cm\xd5\xf0\x02\xb4\x9c(ej\xdb\x88U\xa7!+\x83]N\xee\x8c\x87*\x84W\x12$\xfc\x0eEC\xcb\x14\x88\x1eEr\x12N\x8fY\xd6\x18\x8b^\x98z\xbf\x84\xc1\xc8!\x8a\x1a\xaaQ\x89aP\xadL\xa4w\xda\xb9WN\x90\xe1\x9e\xcf\x1fx\xc7\x84\xf4\x0f\x9a8Q\xcc-\xbasuc0\x988\xf8"\xecmFeI?\x13m1\x11D\xe3l\x0b\x16\xf7\xa4\x05[\xd0\xeac\x16\xc6\n`\xcf\xbcf\xda\xc6\xf8n\xbc\xea*\x857n\xb2\x91\x12#\xe0\xf9\x12\xc3\x83\x8f\xfb\x1b\xb2\xd8\xf6\x1fmwC\xaa\x8b\xaeV\xabQ\xd3\xe2\xd6\x1e\xf1u\xc5\xfd\x8a\xf7rw\'\x12\x95\x9dl:o\r\r\x13\xf9\x02\xab\'.{\xc0b\x9f\xc1.\xde\xeb\xce\xf1Cd\xbf\xdfOAMi\xc3\x96bhC\xc7\xa6\x1ch\x06X\xed\xafR\xda\x80\xee \x1c=\x92\xf9&\x8b\xbf\x7f\xf1^k[\x94\xd26C\xfe\x1fY16\xc8Q\x9f7`\xc1\xf8D_\xef\xbb$L>R)\xef\xd7\xf5\r\x91\x17@\x8dZ\xa0\x81\x8a<F+\x8d\xd5\n5/\x10\x0ed\xb4\xc1\xc0\xb8a\xe5\x81\x85}\x0f2\x90\xa4\xaeg\xb7\xe8#\xe85h"?\x90\x99\x7f\xe46\xf7y\xe1o\x1e5\xe6o\xda?!\xeb\xc2\x98\xcf\x98\xb0\x15\x01]E\xa5V\x032\xaa\x16\x83K*\xe3$\xcb\xfc\xe6\xb4\x8f\xa2\xa7\x900\x04G\xd8\x95\x98$\x9fN~\xb1\xa7\x0c\x8bGd\xa6\xa7\xe0F\xdd\xa2\xfbU\x9bE\xdc\x025\x1e\xfe\xc5v#\xb1Ft\xc1\x9c\x909\xd9\xb8\xe1h\xc1G*\x02c\xe5\xc7\x91q\x86/\xab\xfaS\xb9Tk\x90\xcc\x07\xb1\xa3E\x11e\x95fw\x02\xbfF\xb7j\x82\r\x92hD\x92\'FD\xb9\x9b\x0c\xc4%\xb6>:\x1cZ \xc7\x1b\xa9\x15h[\xff?\x88\xcd\xbb\xe5\xf6\xb3\xb6B\xac\xda\xcd\xa7h\x81 \xcd\xeeu@\xef\x92!\xe2Vr+\x1d\xa6\x83Z\xdb\xb7\x1a\xd7#$)\xf1\x1f\x82\xdd\x80A\x94W\xf4\xa1\xe7J$\xda\x02\xd4\xb6\xe5\x84\xbegH\xb9\xc2\xe3\x82\xc5\xfd\xb1\xe7!\xeb\xe0\xd1J\x94\x04\x7f\x81\xee\xafn\xe3\x0e;\x17\xb6\x18\x9e;\xca\xb6`\x89\xd0\'\x87\xff\xcc \xf4yW#*j\x00ad\x9f.5/\xfc\tx1\xa6\xf7V.]\xfa\xec\xc9\x93\x87\x9a;\xb4\xe0\x0eC\x98<5\x14a\xb1c\xaa\x91\x08O\xbaIz*Y\xf0\t\x8e\x96\x92\xa8\x0b\xeb\xa7\xdep\xa2zl\xd6_\x05%\x96\xda\x9e"\x80\xeb\xf2bc\xfb\t\x1a\xe2h\xd1\x00tb|M\xf6\xd7\xc4.\xb5\xb1\xe3a\xa1\x96\x08\x9f\xa6:\xa8\xccouN^#fq\x03\xcan\x8cRJD6&\n\xda\xe0T\xf5\x18\xfcp\xcd\x8f`\xdcS\x10\x00;\xd2\xb2\x14\xbd\xe4xa\x88v\x03p\x83\xdeL\xb0\xa0\xe8D\x04\xdd~\xa0\xacR\xc6 \xe0\x83t\xbf\xbf\x1d#\xc4`\xbe\xc6\xd7\xf9Z\x08\x88Tm\x81\xd6f*\xdc\x13\x0e\xc7\xae-\xadSmQ\x02y\xa4.\xbe\x8b\xf4\xe7\x9c\xbd\xdc5\xc3\x98{Qw\xba\xeb\xf5\xc6jMy\xcaj\xd5\x01L8<c\xf0k\xd6p\xe7\x1cz\xffW72\xd3>Q\xe1\xab\xc3\x87tTW\xfdb\x88\x07Xu\xf8V\x92\x8e-\xffG\x80j\xd0/\x0e\xe1?W\x8f\xe7\xd2A\x80L\x19d\xce\xd0\xd4]\x00\x92\xde\xb9\xb3|\xda\x13mLQ_4\xbb1\xd3w\xa3f&\xd4=\xd6~/=\x83U\xb6W\xc7\x95\x95\x05\xcb\xe3\xde\xc1\x15\xc9\x9bB\xce\tH\\z\xc1\xf3c\xc1v\xca\xbd\x06,\xea\n\x03\x1b\x12!C\xefj\x97\x96K\x07O\xa9\x98\xca\t\x1e\xd9\xc5\x98=\x9cq\x80\xd0\xba?`\x14\x91\x8c\xa9\xab\x83\\\xa6\xb5>zq\xed\xc68\x0b\xba\xed\xda\xb2N\xfbI\xde\x8d\xb7\x1fu\xce\xf0[D\x9a\x8cW\xea\xd4\xebGz}\x81\xd4\xf4\'N\\\xceW\x96:E\x06\xed\xd3\x06\xf8\x18d\x9e\x0edW\xdcb\xe1\xea\x1c+I\xf8\xdc\xd4\xda\\r~\x96\xb9?yd\xd9\xb7}\xfb\x9ek\x85\xa84h\xf9\x8b\'(\xa9\xb4S\x87\xc7\x87\xa7\xf1\xdaIy\x95D\xfe\xe3\x90\xf0\x08\xf3\xfc\x00\x8e\x81\xc2k\xb8a3\xc1}@\x8d\\\xf9T\xfc\xb0J\xeb\x8f\x99\xc9~\x95_\x14\xdde\x1c\xaf\xbeG\xd8\xdc\x99))\xb5\x8f\xdd\xdc\x06O~(\x0c\xa1\x1d\xa5c4\xf9=~Z\xc2\xea5G\xed\x10\x85S\x00~\x0c*\x14$\xcb\x0b\x08$0\x89\x1e\xd6Qq\x81\x18_\xb0\xc9o\x81,_\x9c\xcfi\xb7\x86\xf1\xbd\xa8\xa2N\x84@~\xaf.\x9f\x89\xbd/\xc3n\xcd\xf1\xc0\x83\xbaQ\xe1,\x9c6"p]\xb5\xae\x835\xd1\xb6m\xe7M0&q\xb3\x91S\xd0o\xdbF\xbc<\x17{eP6\xc3c\x7fR\x9a\xacWd3?\xb6joQ\x1d\xb2_\xc1\x9d`\xb8\x08!\x97\xc3z3Y_\xb8@\x88\xecc\r\xc1\xb7y\x80'
class lfsr():
    def __init__(self, seed, mask, length):
        self.length_mask = 2 ** length - 1
        self.mask = mask & self.length_mask
        self.state = seed & self.length_mask

    def next(self):
        next_state = (self.state << 1) & self.length_mask
        i = self.state & self.mask & self.length_mask
        output = 0
        while i != 0:
            output ^^= (i & 1)
            i = i >> 1
        next_state ^^= output
        self.state = next_state
        return output

    def getrandbit(self, nbit):
        output = 0
        for _ in range(nbit):
            output = (output << 1) ^^ self.next()
        return output


def decrypt1(c,gen,pt):
    m=[]
    for i in range(16):
        ch=c[i]
        m.append(ch^^gen.getrandbit(8)^^ord(pt[i]))
    return m

def decrypt2(c,gen1,gen2):
    m=''
    for i in range(16):
        ch=c[i]
        m+=chr(ch^^gen1.getrandbit(8)^^gen2.getrandbit(8))
    return m

def decrypt3(c,gen1,gen2):
    m=''
    for i in range(len(c)):
        ch=c[i]
        m+=chr(ch^^gen1.getrandbit(8)^^gen2.getrandbit(8))
    return m

pt='Date: 1984-12-25'
mask2=2053
for i1 in range(2,2**12):
    print(i1)
    seed3=i1
    lfsr2=lfsr(seed3,mask2,12)
    state=decrypt1(c,lfsr2,pt)
    State=''
    for i in range(len(state)):
        State+=bin(state[i])[2:].rjust(8,'0')
    G=GF(2)
    A=Matrix.zero(64)
    for i in range(64):
        for j in range(64):
            A[i,j]=State[i+j]
    A=Matrix(G,A)
    if not A.det():
        continue
    list1=[]
    list2=[]
    for i in range(64,128):
        list1.append(State[i])
        list2.append(State[i-64])
    v1=vector(G,list1)
    v2=vector(G,list2)
    mask1=list(v1*A^(-1))
    temp=''
    for i in range(len(mask1)):
        temp+=str(mask1[i])
    temp=int(temp,2)
    maskM=Matrix.zero(64)
    for i in range(63):
        maskM[i,i+1]=1
    for i in range(64):
        maskM[63,i]=mask1[i]
    M = Matrix(G,maskM)
    M = M^64
    if not M.det():
        continue
    s=M^(-1)*v2
    s=list(s)
    tempp=''
    for i in range(len(s)):
        tempp+=str(s[i])
    lfsr1=lfsr(int(tempp,2),temp,64)
    lfsr2=lfsr(seed3,mask2,12)
    m=decrypt2(c,lfsr1,lfsr2)
    if pt in m:
        lfsr1=lfsr(int(tempp,2),temp,64)
        lfsr2=lfsr(seed3,mask2,12)
        mm=decrypt3(c,lfsr1,lfsr2)
        if 'SUSCTF' in mm:
            print(mm)
posted @ 2022-03-04 12:02  hash_hash  阅读(322)  评论(0编辑  收藏  举报