[Securinets CTF Quals 2023] PolyLCG DigestiveV2

PolyLCG

第1个题是个LCG问题,通过一堆参数生成两个序列,如果flag位为1则输出x序列为0则输出 y序列

from random import randint


xcoeff=[2220881165502059873403292638352563283672047788097000474246472547036149880673794935190953317495413822516051735501183996484673938426874803787946897055911986,3780868071235160215367199770952656972709510983146503211692869296836254519620768737356081836837102329626660962468333562050121427935761471039362287802941597,4902413424318450578332022710023815992030030940432088134156375736636296016273860394626141407089100644225364129305706233708267009976783719598126300552264686]
ycoeff=[10133630993576627916260025550504106878405253409844193620608338129978685236278362029266353690006955194818074387390350472504283291952199370441443295790407675,3364000239596805500788439152587586988694473612770420810400457954622820421525205173981972752548906690775960238564395459369815397933405749174182967563999094, 5184466564604150683447715719961919989718796968566745874607480183961791804239357212974694797397047787503590843234526492414458478882622032364603797888695699]
p=10369539704979520345376943788090457296701518777268113122376443474930073612795297691185597789473973789467303121639140064504782927997022419913721978857764263


class LCG:
    def __init__(self,p,xcoeffs,ycoeffs):
        self.p=p
        self.xcoeffs=xcoeffs
        self.ycoeffs=ycoeffs
        self.xstate =randint(1,p-1)
        self.ystate =randint(1,p-1)
        for i in range(randint(1,1337)):
            self.next()
    def next(self):
        self.xstate=pow(self.xcoeffs[0]+self.xcoeffs[1]*self.xstate+self.xcoeffs[2]*self.xstate**2,1,self.p)
        self.ystate=pow(self.ycoeffs[0]+self.ycoeffs[1]*self.ystate+self.ycoeffs[2]*self.ystate**2,1,self.p)
    
    def encrypt(self,msg):
        bin_msg=list(map(int, list(f"{msg:0512b}")))
        encrypted=[]
        for i in bin_msg:
            self.next()
            if i==1:
                encrypted.append(self.xstate)
            else:
                encrypted.append(self.ystate)
            
        return encrypted
            


flag=b"Securinets{???????????????????????????????????????}"
flag=int.from_bytes(flag,"big")

lcgCipher=LCG(p,xcoeff,ycoeff)
encrypted_flag=lcgCipher.encrypt(flag)
print("encrypted_flag=",encrypted_flag)

当第1个给定后这人LCG就能算出来,只需要其中一个序列即可。

from output import encrypted_flag

xcoeff=[...]
ycoeffs=[...]
p=...

v = '0'
ystate = encrypted_flag[0]
for i in range(1, len( encrypted_flag )):
    ystate=( ycoeffs[0]+ycoeffs[1]*ystate+ycoeffs[2]*ystate**2 ) %p
    if encrypted_flag[i] == ystate:
        v += '0'
    else:
        v += '1'

print(v)
m = ''.join([chr(int(v[i:i+8],2)) for i in range(0, 512, 8)])
print(m)
#Securinets{1e9Endre_5ymBO1s_goEs_brrrrrrrrrrrrrrrrrrrrrrrrrr}

DigestiveV2

这题犯了个低级错误一直没作出来,后来发现写反了。

用椭圆曲线的签名,但使用了一个有明显问题的pub_hash函数。

先把名字用json打包后转整型再模O,当输入40个字符发生碰撞后即可生成相同的pub_hash值。pub_hash相同签名也就相同了。

from fastecdsa.curve import P192
from fastecdsa.point import Point
from secrets import randbelow,flag,banner,menu
from Crypto.Util.number import bytes_to_long,inverse
from string import ascii_uppercase
import json
#P192 Order
O=6277101735386680763835789423176059013767194773182842284081
d=randbelow(O)
G=Point(0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012, 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811,curve=P192)
P=d*G

def pub_hash(m):
     return (bytes_to_long(m.encode())%O)>>60 
def ecdsa_sign(m):
        h = pub_hash(m)
        k = randbelow(O)
        r = (k * G).x % O
        s = (inverse(k, O) * (h + r * d)) % O
        return json.dumps({"r":r,"s":s})
def verify(msg,r,s):
    h=pub_hash(msg)
    if r > 0 and r < O and s > 0 and s < O:
        u1=(h*inverse(s,O))%O
        u2=(r*inverse(s,O))%O
        V=u1*G+u2*P
        if r==V.x:
            return True
    return False
print(banner)
print(menu)
normal_user=["JAKC","YASSINE"]
for i in range(2):
    try:
            
        choice=json.loads(input())
        if "option" not in choice or "name" not in choice:
            print("Give me a option and a message next time")
            continue
        if choice["option"]=="sign":
            name=choice["name"]
            if any(i not in ascii_uppercase for i in name) or len(name)!=40:
                print("give me a strong and long name next time")
            normal_user.append(name)
            payload=json.dumps({"username":name,"admin":"false"})
            print(ecdsa_sign(payload))
        if choice["option"]=="verify_admin":
            if "r" not in choice or 's' not in choice :
                print("Remember to return with the admin signature next time")
                continue
            if choice["name"] in normal_user:
                print("Dont Try to cheat your way through!")
                continue
            payload=json.dumps({"username":choice["name"],"admin":"truee"})
            if verify(payload,choice['r'],choice['s']):
                print("Welcome back admin",flag)
            else:
                print("You seemed to send something wrong try again")
                continue
    except:
        pass

40个字符去碰撞,第个变化的int值作为参数求一个与中间值的差。

当输入N*40时会产生一个hash值h0,与目的值h1的差作为变更目标,就变成了一个背包问题。用格求解。

行求差值

O=6277101735386680763835789423176059013767194773182842284081
def pub_hash(m):
     return (bytes_to_long(m.encode())%O)>>60 

v1 = json.dumps({"username":"NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN","admin":"truee"})   
#'{"username": "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", "admin": "truee"}' 
h1 = bytes_to_long(v1.encode())%O

v2 = json.dumps({"username":"YASSINE","admin":"truee"})
#v2 = '{"username": "YASSINE", "admin": "truee"}'     #normal_user=["JAKC","YASSINE"]
h2 = bytes_to_long(v2.encode())%O

hx = (((h2-h1))%O)

print('h1:',h1>>60)
print('h2:',h2>>60)
print(hx)

再求背包问题。这里直接用>>60可能会有误差,所以少移几位,并从结果中找到一个最接近的值,恰好这个相等。

O = 6277101735386680763835789423176059013767194773182842284081
x = 3302636172704920594268150250909543749562995783341298138374
#x>>60 = 2187014057453355045706725449238469327308
nbit = 40
M = matrix(ZZ, nbit+1,nbit+1)
for i in range(nbit+1):
    M[i,i] = 1
    M[i,nbit] = (2**(160+i*8)%O)>>56

M[-1,-1] = x>>56

res = M.LLL() 
for i in range(nbit+1):
    if all(abs(v)<13 for v in res[i]) :
        s = 0
        for j in range(nbit):
            s += res[i][j]*M[j,-1]
        print(res[i])
        print(s)

#----check---
k = (2, -8, 4, -1, -3, -6, 1, -6, 5, 0, -8, -10, 3, -6, 0, 6, 3, 4, 1, -5, 2, 5, -6, 5, -1, -1, -4, -4, 3, 2, -4, 2, -6, 0, -1, 1, -1, 3, 7, 2, 0)

生成 name并验证

name = ''
for i in range(nbit):
    name += chr(ord('N')+k[i])

print(name)

v0 = json.dumps({"username":"NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN","admin":"truee"})   
h0 = bytes_to_long(v0.encode())%O

v1 = json.dumps({"username":name[::-1],"admin":"false"})    
h1 = bytes_to_long(v1.encode())%O

v2 = json.dumps({"username":"YASSINE","admin":"truee"})
h2 = bytes_to_long(v2.encode())%O
print(v1,v2,'\n',h0>>60,'\n',h1>>60,'\n',h2>>60)
'''
{"username": "PUQMOMNHPJPQJJMMSHSPIORQTNHQDFNSHOHKMRFP", "admin": "false"} {"username": "YASSINE", "admin": "truee"}
 3669927246502793964990422003272902381467
 1089989626468487772900189775859714399277
 1089989626468487772900189775859714399277
'''

你可能感兴趣的:(python,开发语言)