# WannaGame Cyber Knight 2024 ## Warmup - chall.py ```python from Crypto.Util.number import * from Crypto.Util.Padding import pad from flag import flag from gmpy2 import next_prime message = b"The challenge's flag is: " + flag assert len(message) % 3 == 0 part1 = message[ : len(message)//3] part2 = message[len(message)//3 : 2*len(message)//3] part3 = message[2*len(message)//3 : ] sep = "#"*50 ### part 1: Warm-up!!! p1 = getPrime(512) q1 = getPrime(512) e1 = 11 n1 = p1 * q1 c1 = pow(bytes_to_long(part1), e1, n1) print(f'{n1 = }') print(f'{c1 = }') print(sep) ### part 2: One more warm-up p2 = getPrime(1024) q2 = getPrime(1024) n2 = p2*q2 e2 = 17 print(f'{n2 = }') print(f'Real: {pow(bytes_to_long(part2), e2, n2)}') print(f'Fake: {pow(bytes_to_long(pad(part2, 16)), e2, n2)}') print(sep) ### part 3: The last "warm-up" p3,q3 = getPrime(1024), getPrime(1024) n3 = p3 * q3 power1, power2 = getPrime(128), getPrime(128) hint1 = pow((5*p3 + q3), power1, n3) hint2 = pow((3*p3 - 6*q3), power2, n3) e3 = next_prime(hint1 + hint2) c3 = pow(bytes_to_long(part3), e3, n3) print(f'{power1 = }') print(f'{power2 = }') print(f'{n3 = }') print(f'{hint1 = }') print(f'e3 = {e3}') print(f'c3 = {c3}') ``` ### Solution - Ta thấy **FLAG** được chia làm 3 phần có độ dài bằng nhau và được encrypt theo những cách riêng. - Trong lúc diễn ra giải mình đã decrypt part 2 đầu tiên - Ta có thể thấy được phần **Real** là **FLAG** sau khi encrypt còn **FAKE** là `pad(FLAG)` sau khi decrypt - Từ đây ta có thể bruteforce hết 16 trường hợp padding có thể và tìm **GCD** giữa 2 phương trình sẽ ra được (x - **FLAG**) - Part 2: ```python from sage.all import * from Crypto.Util.number import * from Crypto.Util.Padding import pad n2 = 19474122562778153703776906234103999694212644666605609184003798714902765132186442270466413951685774096672600567147343733732980015161843616032962680857426708013412213676449861264155297288378479051436442079937058938840721841860491557712477170366628773688474634289507561262017382779613838956398073685517438883550013563737919252844748012781298212795883037816078874734356565506037923530508679278288097627681354570158653097456586858900957218344882318145320067184688094419746980554117499065442646088691226289819440434740945671726394148366572739206464970977471727197412322136575227898097076974504287523260885786539573649783161 Real = 15636713266108700464255355282572617369877416762456096086219951374063737359559203674454786265210431923339051086905021926447634666321503985769492720281115251569517394409997291661224097948320454420612478527160246859233690225189444979403485318497052362506638619146425116246060495195223943704647051304805754627681254961634405541974848733951519522486396630609303328097791062133825888690738129275001884247653912942268589773666008633702027740289575546500039837328482189167641136880443555991865875325701485342918164649959219812272157959283006261464655737656031536878365520892164396734804695179528627342810772907739309174183304 Fake = 1901702486995025261538235880685531918771879286327896110111123890041702504494161154076737842357693655100992364579118174208323657012644698015659577716664514828814910889887457132697217080176306194565595589316187612945830703382031867854831608485926311704058671506309214522654866677161067649723883561755848469509892417562279714400249673848323509379109641015242793126408247724149712397397815568653344550396176143675147065423111733067480933601053636423334747702190300411842735906299712817499994397771633456847764374609445235276286713165285446045446123174200222508610983202190618557770618976371164659678708230871711252574428 def mygcd(f,g): while g != 0: r = g g = f % g f = r return f/f.lc() P = PolynomialRing(Zmod(n2), 'x') x = P.gen() p1 = x ** 17 - Real test = b'' for i in range(16): padding = pad(test, 16)[i:] p2 = (2 ** ((16 - i) * 8) * x + bytes_to_long(padding)) ** 17 - Fake t = mygcd(p1, p2) if t.degree() == 1: print(long_to_bytes(int(-t[0]) % n2)) break test += b'\x01' ``` ``` _lA_15_h1hi__75f26f70b3fa1343153e ``` - Tiếp đến là part 1, sau khi decrypt xong part 2 ta biết được độ dài của từng thành phần là 33 ký tự và ta cũng biết được 25 ký tự đầu + thêm "W1{" là 28 ký tự. Giờ ta chỉ cần small_roots để tìm nốt phần còn lại. - Part 1: ```python from sage.all import * from Crypto.Util.number import * from Crypto.Util.Padding import pad n1 = 78100017093042237362585803611409807219210686771960485296694671535293298684240391999007937128416933959949645805107079926652343615109335481443408319617478348436412156203990196717356668119025919695447421356649708220217528303406969275048476878616591756855326600516077838718677582495646251901652030452534739980093 c1 = 21635094418936882229318713100837056867730099226953717083953338957077431886511252266065867668680214410547969294123420920750052299754539343424046939642019856856637610693840992502539517724666655388883948397275320947350529293996784254524553566619189584795444284261967162826834735297201762488824862476117397669601 message = b"The challenge's flag is: W1{\x00\x00\x00\x00\x00" message = bytes_to_long(message) P = PolynomialRing(Zmod(n1), 'x') x = P.gen() f = (message + x) ** 11 - c1 FLAG = f.small_roots(X=2**50, beta=0.5, epsilon=0.015) print(b"The challenge's flag is: W1{" + long_to_bytes(int(FLAG[0]))) ``` ``` The challenge's flag is: W1{A_p0r ``` - Đến với part cuối cùng, bạn có thể tham khảo Modular Binomials - Giờ bạn chỉ cần bruteforce hết tất cả các hint2 phù hợp cho đến khi nào xuất hiện $p$ là được - Part 3: ```python import math from Crypto.Util.number import * from Crypto.Util.Padding import pad from gmpy2 import next_prime power1 = 216515682056074917890546762360422775759 power2 = 181261651362416112447857910756974715253 n3 = 21359428772367567762465177652844235016399133661109374171468438854776561246101192389880378563002849491963133317822943728663659947135787662718455826488579139110050277672340965370240377096647337916266432987314270685198434379314123632571454622139263629066529934934145277818436942937601606973322506307539554768214679444922661752071123001107723044536675764939605450418550872933311130411355136155266638811137191949645399770280529254355150837851996371018266855150538183941848828564579160123399867316204489663532672172085306182442910667707542525506401104797512848908939121437800995819119455775357620426817733038470495147276793 hint1 = 21165771737478725950851391268640839742282738803460982495702522148737396455865260539601340347151173375617657055655041043517629027492240234235183580296831387302596600695810822254431016035932293471628759197250919622480691606652634091857970182748360357623125515085919940174799049558949629746381693045991973156293505919951290743073241929003963830080590083449251389368841868741731209817737242746289090221167945649864511277017186337822956848075038306043349755593388069666507069280026419826266727104285440789673438901905195589837692674191838634684368474851772930333812341283933491515890683850912453399208298386277684434445251 e3 = 37168304783168980393920173579379710993074140739837792931728618759911393587739974232300850198277091657299514225039110963445571485237776790392542202542871178828780578794970964528554601212794008256754381717435669983473010005148311575428821001052237868999060224515275873668669213982474419104490433570572105845650251833635244326980880559203371594232931397547585750248068741794286062945536039069446641110263273308725041343806223241749757368437839639116744539107461153105289738651458945715419945820612645097201055521095139028646379860663948813756288318543675883368961217287301785966051639519026541020372279840744062219039191 c3 = 1096068813757106861072993580055082418953963625562486119593706360815623910450146273178847639647973866883042811190136050041007753762866893600939527521717867763640548952449681014994092425911219237532793217675033536632826439864589482883751579581380877418411747043359264793736856167378058319757084322522347500331245555941134923517945671002893760566068668447306048296218747434127148067031352038287679665515172443331784546991573906825482519792803285599056922851777942853546318660474033734361350200902964188996923538488714615644961271863386265655804388494579456404108898159557142645993950324859997370434010675900211652602515 hint2 = 16002533045690254443068782310738871250791401936376810436026096611173997131874713692699509851125918281681857169384069919927942457745536556157358622246039791526183978099160142274123585176861714785125622520184750360992318398495677483570850818303877511375934709429355933493870164423524789358108740524580132689356745913683953583907638630199407764152341314098334360879226873052554853127798796323157550889095327658860530066789036903926800520362801333073394783514073083438782669371432525889153218716327204307527616619189943438808687186472110179071919843691902953035148876003368294450160955668114087621163981454466377784586475 while True: p = math.gcd((pow(3, (-power1 * power2), n3) * pow(hint2, power1, n3) - pow(5, (-power2 * power1), n3) * pow(hint1, power2, n3)) * (n3 - 6) % n3, n3) if p != 1: break hint2 += 1 p3 = p p3 = 149980597760000270288020712564730421423970532153531109538283372659249090766781034501997790624701674901461522882220113357725378245572684453212159175961586586191733952945436353476578468558720453741235833653421187406171537615120165747876478240633282821626182454380149314563971827086993047558004886558019962996419 q3 = n3 // p3 assert p3 * q3 == n3 phi = (p3 - 1) * (q3 - 1) d = pow(e3, -1, phi) m = pow(c3, d, n3) print(long_to_bytes(m)) ``` ``` a91411ae238_happy_hacking!!!!!!!} ``` - Kết hợp cả 3 part lại ta có được ``` W1{A_p0r_lA_15_h1hi__75f26f70b3fa1343153ea91411ae238_happy_hacking!!!!!!!} ``` ## 435 - server.py ```python from Crypto.Cipher import AES from Crypto.Util import Counter from Crypto.Util.number import long_to_bytes as ltb, bytes_to_long as btl from Crypto.Util.Padding import pad, unpad import os GREEN = '\033[92m' RED = '\033[91m' YELLOW = '\033[33m' NORM = '\033[0m' BANNER = \ f""" {YELLOW} ___ __ ________ ________ ________ ________ ___ __ _____ ________ |\ \ |\ \|\ __ \|\ ___ \|\ ___ \|\ __ \|\ \ |\ \ / __ \|\ ___ \ \ \ \ \ \ \ \ \|\ \ \ \\\ \ \ \ \\\ \ \ \ \|\ \ \ \ \ \ \|\/_|\ \ \ \\\ \ \ \ \ \ __\ \ \ \ __ \ \ \\\ \ \ \ \\\ \ \ \ __ \ \ \ __\ \ \|/ \ \ \ \ \\\ \ \ \ \ \|\__\_\ \ \ \ \ \ \ \\\ \ \ \ \\\ \ \ \ \ \ \ \ \|\__\_\ \ \ \ \ \ \\\ \ \ \ \____________\ \__\ \__\ \__\\\ \__\ \__\\\ \__\ \__\ \__\ \____________\ \ \__\ \__\\\ \__\\ \|____________|\|__|\|__|\|__| \|__|\|__| \|__|\|__|\|__|\|____________| \|__|\|__| \|__| {NORM} Once upon a time, deep in the forest, there lies a great treasure but protected with a digital padlock. Rumor said that whoever could open the treasure would be rewarded with great riches. You, as a treasure hunter, decided to go on a trip to uncover this elusive prize. After facing countless obstacles and challenges, you finally reached the location of the treasure. Next to the treasure lies a truncated poem. You, as a master in cryptography also, will not give up easily right? Goodluck! ;) """ menu = \ f"""\ {NORM} 1. ECB_encrypt 2. CBC_decrypt 3. Crack Password {"~"*100} """ class Riddle: def __init__(self, key, nonce): self.key = key self.nonce = nonce def GCM_encrypt(self, plaintext): """ Return encryption of AES-GCM with provided plaintext """ cipher_ = AES.new(self.key, AES.MODE_GCM, self.nonce) return cipher_.encrypt_and_digest(plaintext) def ECB_encrypt(self, plaintext): """ Return encryption of AES-ECB with provided plaintext """ cipher_ = AES.new(self.key, AES.MODE_ECB) return cipher_.encrypt(plaintext) def CBC_decrypt(self, iv, ciphertext): """ Return decryption of AES-CBC with provided (ciphertext, iv) """ cipher_ = AES.new(self.key, AES.MODE_CBC, iv) return cipher_.decrypt(ciphertext) print(BANNER) cipher = Riddle(key = os.urandom(16), nonce = os.urandom(12)) secret = os.urandom(256) poem = \ pad(b"""\ The security code is simple, an intricate dance of numbers. A shield against intruders, a fortress for slumbers. Digits align in harmony, a secret melody they sing, Guarding the treasures of a realm, where secrets take their wing : \ """, 16) + secret print(secret.hex()) ciphertext, tag = cipher.GCM_encrypt(poem) print(f"ct = '{ciphertext.hex()}'") print(f"tag = '{tag.hex()}'") while True: print(menu) option = int(input("Your choice > ")) try: match option: case 1: m = bytes.fromhex(input("Please give me your message (in hex) > ")) c = cipher.ECB_encrypt(m) print(c.hex()) case 2: iv = bytes.fromhex(input("Please give me your iv (in hex) > ")) c = bytes.fromhex(input("Please give me your ciphertext (in hex) > ")) m = cipher.CBC_decrypt(iv, c) print(m.hex()) case 3: key = bytes.fromhex(input("Give me your secret (in hex) > ")) if key == secret: FLAG = open('flag.txt', 'r').read() print(f"{GREEN}Wow you did surprise me!! Here is your reward {FLAG}") exit() else: print("Ahh, try better next time :(") exit() case _: print("No idea what you choose :(") except Exception as e: print(f"{RED}An exception occurred: {str(e)}") ``` ### Solution - Bài này cho ta **ciphertext** và **tag** đã được encrypt qua AES GCM cũng với các chức năng **ECB_encrypt** và **CBC_decrypt** - Ta biết phần encrypt của GCM hoặc động giống như Counter nên ta có thể đưa phần xor(ciphertext, plaintext) qua **CBC_decrypt** với IV = 0 để tìm lại được Counter - Giờ ta chỉ cần cộng dần Counter lên rồi cho đi qua **ECB_encrypt** rồi lấy kết quả đó xor với ciphertext sẽ được xâu gốc ```python from Crypto.Cipher import AES from Crypto.Util import Counter from Crypto.Util.number import * from Crypto.Util.Padding import pad, unpad import os from pwn import * # io = process(['python3', 'server.py']) io = remote("45.122.249.68", 20024) context.log_level = 'debug' secret = os.urandom(256) poem = \ pad(b"""\ The security code is simple, an intricate dance of numbers. A shield against intruders, a fortress for slumbers. Digits align in harmony, a secret melody they sing, Guarding the treasures of a realm, where secrets take their wing : \ """, 16) + secret io.recvuntil(b'ct = ') ct = io.recvline().strip().decode()[1:-1] ct = bytes.fromhex(ct) test = xor(ct, poem) io.sendafter(b'> ', b'2\n') io.sendafter(b'> ', b"00" * 16 + b"\n") io.sendafter(b'> ', test.hex().encode() + b'\n') res = io.recvline().strip().decode() res = res[:32] counter = bytes_to_long(bytes.fromhex(res)) payload = b"" while len(payload) != len(ct): payload += long_to_bytes(counter) counter += 1 io.sendafter(b'> ', b'1\n') io.sendafter(b'> ', payload.hex().encode() + b'\n') res = io.recvline().strip().decode() res = bytes.fromhex(res) payload = xor(res, ct)[240:] io.sendafter(b'> ', b'3\n') io.sendafter(b'> ', payload.hex().encode() + b'\n') io.interactive() ``` ``` Wow you did surprise me!! Here is your reward W1{7hE_S3cRe7_L13s_4m0N9_M0De5_0f_0p3r4ti0n_cddde2b1d3eec22ffcd8c871faca7639} ``` ## AESSS - server.py ```python import os import random from Crypto.Cipher import AES from Crypto.Util import Counter from Crypto.Hash import SHA256 from secret import FLAG flag1 = FLAG[:len(FLAG)//3].encode() flag2 = FLAG[len(FLAG)//3:2*len(FLAG)//3].encode() flag3 = FLAG[2*len(FLAG)//3:].encode() assert len(flag1) == len(flag2) == len(flag3) == 48 def pad(data, block_size): return data + (block_size - len(data) % block_size) * bytes([block_size - len(data) % block_size]) def challenge_part1(flag): key = os.urandom(16) iv = os.urandom(16) options = ['ecb', 'cbc', 'cfb', 'ofb', 'ctr'] suboptions = ['data', 'flag'] for _ in range(3): # you only have 3 tries, stingy huh? [option, suboption, *more] = input('> ').split(' ') if option not in options: print('invalid option!') continue if suboption not in suboptions: print('invalid suboption!') continue options.remove(option) if suboption == 'data': message = bytes.fromhex(more[0]) else: message = flag if option == 'ecb': cipher = AES.new(key, AES.MODE_ECB) elif option == 'cbc': cipher = AES.new(key, AES.MODE_CBC, iv) elif option == 'cfb': cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128) elif option == 'ofb': cipher = AES.new(key, AES.MODE_OFB, iv) elif option == 'ctr': cipher = AES.new(key, AES.MODE_CTR, counter=Counter.new(16, prefix=iv[:14])) ciphertext = cipher.encrypt(message) print(ciphertext.hex()) else: print('No more options!') def challenge_part2(flag): key = os.urandom(16) while True: try: options = input('> ').split(' ') if (options[0] == "encrypt_flag"): iv = os.urandom(16) cipher = AES.new(key, AES.MODE_GCM, iv) message = flag ciphertext= cipher.encrypt(message) print("Encrypted flag and tag, iv:", ciphertext.hex(), iv.hex()) elif (options[0] == "encrypt_data"): iv = bytes.fromhex(options[1]) cipher = AES.new(key, AES.MODE_GCM, iv) message = bytes.fromhex(input('input data:')) ciphertext= cipher.encrypt(message) print("Encrypted message and tag, iv:", ciphertext.hex(), iv.hex()) else: print("Invalid option!") except Exception as e: print("An error occurred: ", e) break def challenge_part3(flag): try: salt = os.urandom(2) # prevent rainbow table attack and ... brute force attack? password_file_path = os.path.join(os.path.dirname(__file__), "rockyou.txt") # rockyou.txt is a list of common passwords print(f"Looking for rockyou.txt at: {password_file_path}") if not os.path.isfile(password_file_path): print(f"Error: {password_file_path} not found") return with open(password_file_path, "rb") as file: passwords = file.readlines()[:100] password = random.choice(passwords).strip() key = SHA256.new(password + salt.hex().encode()).digest()[:16] cipher = AES.new(key, AES.MODE_ECB) ciphertext = cipher.encrypt(pad(password, 16)) print("Encrypted password:", ciphertext.hex()) input_password = input('input password:') if input_password.encode() == password: print("Correct password!") print("Good job! Here is your flag:", flag) else: print("Incorrect password!") except FileNotFoundError: print("rockyou.txt not found") return except Exception as e: print(f"An error occurred: {e}") return def main(): chall_option = input('challenge option:') if chall_option == '1': challenge_part1(flag1) elif chall_option == '2': challenge_part2(flag2) elif chall_option == '3': challenge_part3(flag3) else: print('Invalid option!') if __name__ == '__main__': main() ``` ### Solution - Part 1: ```python import os import random from Crypto.Cipher import AES from Crypto.Util import Counter from Crypto.Util.Padding import pad from Crypto.Hash import SHA256 from pwn import * io = remote("45.122.249.68", 20026) context.log_level = "DEBUG" io.sendlineafter(b"option:", b'1') io.sendline(b"cfb data " + b"00" * 48) enc1 = io.recvline().strip()[2:].decode() print(enc1) io.sendline(b"ofb flag") enc2 = io.recvline().strip()[2:].decode() print(enc2) enc1 = bytes.fromhex(enc1) enc2 = bytes.fromhex(enc2) print(xor(enc1, enc2)) ``` ``` W1{AES_1s_w1d3ly_us3d_1n_r3al_w0rld_applic4t10ns ``` - Part 2: ```python import os import random from Crypto.Cipher import AES from Crypto.Util import Counter from Crypto.Util.Padding import pad from Crypto.Hash import SHA256 from pwn import * io = remote("45.122.249.68", 20026) context.log_level = "DEBUG" io.sendlineafter(b"option:", b'2') io.sendlineafter(b"> ", b"encrypt_flag") io.recvuntil(b"iv: ") enc1, iv = io.recvline().strip().decode().split(" ") enc1 = bytes.fromhex(enc1) io.sendlineafter(b"> ", b"encrypt_data" + b" " + iv.encode()) io.sendlineafter(b"input data:", b"00" * 48) io.recvuntil(b"iv: ") enc2, iv = io.recvline().strip().decode().split(" ") enc2 = bytes.fromhex(enc2) print(xor(enc1, enc2)) ``` ``` _4nd_1s_c0ns1d3r3d_t0_b3_0n3_0f_th3_m0st_s3cur3_ ``` - Part 3: ```python import os import random from Crypto.Cipher import AES from Crypto.Util import Counter from Crypto.Util.Padding import pad from Crypto.Hash import SHA256 from pwn import * io = remote("45.122.249.68", 20026) context.log_level = "DEBUG" io.sendlineafter(b"option:", b'3') io.recvuntil(b"password: ") enc = io.recvline().strip().decode() print(enc) with open("rockyou.txt", "rb") as file: passwords = file.readlines()[:100] for password in passwords: password = password.strip() print(password) for bf1 in range(256): for bf2 in range(256): salt = bytes([bf1]) + bytes([bf2]) key = SHA256.new(password + salt.hex().encode()).digest()[:16] cipher = AES.new(key, AES.MODE_ECB) ciphertext = cipher.encrypt(pad(password, 16)) if ciphertext.hex() in enc or enc in ciphertext.hex(): print("True") io.sendlineafter(b"input password:", password) io.interactive() ``` ``` alg0r1thms_1n_pr4ct1c3_66e14a195cd4b2e9d5059ca1a ``` - Ghép cả 3 lại: ``` W1{AES_1s_w1d3ly_us3d_1n_r3al_w0rld_applic4t10ns_4nd_1s_c0ns1d3r3d_t0_b3_0n3_0f_th3_m0st_s3cur3_alg0r1thms_1n_pr4ct1c3_66e14a195cd4b2e9d5059ca1a ``` ## Master of gacha ### Solution ```python from pwn import * from sage.all import * import random import json from Crypto.Util.number import * from hashlib import sha256 class Twister: N = 624 M = 397 A = 0x9908b0df def __init__(self): self.state = [[(1 << (32 * i + (31 - j))) for j in range(32)] for i in range(624)] self.index = 0 @staticmethod def _xor(a, b): return [x ^ y for x, y in zip(a, b)] @staticmethod def _and(a, x): return [v if (x >> (31 - i)) & 1 else 0 for i, v in enumerate(a)] @staticmethod def _shiftr(a, x): return [0] * x + a[:-x] @staticmethod def _shiftl(a, x): return a[x:] + [0] * x def get32bits(self): if self.index >= self.N: for kk in range(self.N): y = self.state[kk][:1] + self.state[(kk + 1) % self.N][1:] z = [y[-1] if (self.A >> (31 - i)) & 1 else 0 for i in range(32)] self.state[kk] = self._xor(self.state[(kk + self.M) % self.N], self._shiftr(y, 1)) self.state[kk] = self._xor(self.state[kk], z) self.index = 0 y = self.state[self.index] y = self._xor(y, self._shiftr(y, 11)) y = self._xor(y, self._and(self._shiftl(y, 7), 0x9d2c5680)) y = self._xor(y, self._and(self._shiftl(y, 15), 0xefc60000)) y = self._xor(y, self._shiftr(y, 18)) self.index += 1 return y def getrandbits(self, bit): return self.get32bits()[:bit] class Solver: def __init__(self): self.equations = [] self.outputs = [] def insert(self, equation, output): for eq, o in zip(self.equations, self.outputs): lsb = eq & -eq if equation & lsb: equation ^= eq output ^= o if equation == 0: return lsb = equation & -equation for i in range(len(self.equations)): if self.equations[i] & lsb: self.equations[i] ^= equation self.outputs[i] ^= output self.equations.append(equation) self.outputs.append(output) def solve(self): num = 0 for i, eq in enumerate(self.equations): if self.outputs[i]: # Assume every free variable is 0 num |= eq & -eq state = [(num >> (32 * i)) & 0xFFFFFFFF for i in range(624)] return state def get_random_bytes(num): x = os.urandom(num) y = random.randbytes(num) # print(x.hex(), y.hex()) return x + y def inv(n, q): return egcd(n, q)[0] % q def egcd(a, b): s0, s1, t0, t1 = 1, 0, 0, 1 while b > 0: q, r = divmod(a, b) a, b = b, r s0, s1, t0, t1 = s1, s0 - q * s1, t1, t0 - q * t1 pass return s0, t0, a def sqrt(n, q): assert n < q for i in range(1, q): if i * i % q == n: return (i, q - i) pass raise Exception("not found") ################### ECC Implement ################### Coord = collections.namedtuple("Coord", ["x", "y"]) class EC(object): """System of Elliptic Curve""" def __init__(self, a, b, q, n): """elliptic curve as: (y**2 = x**3 + a * x + b) mod q - a, b: params of curve formula - q: prime number """ assert 0 < a and a < q and 0 < b and b < q and q > 2 assert (4 * (a ** 3) + 27 * (b ** 2)) % q != 0 self.a = a self.b = b self.q = q self.zero = Coord(0, 0) self.order = n pass def is_valid(self, p): if p == self.zero: return True l = (p.y ** 2) % self.q r = ((p.x ** 3) + self.a * p.x + self.b) % self.q return l == r def at(self, x): assert x < self.q ysq = (x ** 3 + self.a * x + self.b) % self.q y, my = sqrt(ysq, self.q) return Coord(x, y), Coord(x, my) def neg(self, p): return Coord(p.x, -p.y % self.q) def add(self, p1, p2): if p1 == self.zero: return p2 if p2 == self.zero: return p1 if p1.x == p2.x and (p1.y != p2.y or p1.y == 0): return self.zero if p1.x == p2.x: l = (3 * p1.x * p1.x + self.a) * inv(2 * p1.y, self.q) % self.q pass else: l = (p2.y - p1.y) * inv(p2.x - p1.x, self.q) % self.q pass x = (l * l - p1.x - p2.x) % self.q y = (l * (p1.x - x) - p1.y) % self.q return Coord(x, y) def mul(self, p, n): r = self.zero m2 = p while 0 < n: if n & 1 == 1: r = self.add(r, m2) pass n, m2 = n >> 1, self.add(m2, m2) pass return r # def order(self, g): # assert self.is_valid(g) and g != self.zero # for i in range(1, self.q + 1): # if self.mul(g, i) == self.zero: # return i # pass # raise Exception("Invalid order") # pass class DSA(object): def __init__(self, ec : EC, g : Coord): self.ec = ec self.g = g self.n = ec.order pass def gen(self, priv): assert 0 < priv and priv < self.n return self.ec.mul(self.g, priv) def sign(self, hashval, priv, r): assert 0 < r and r < self.n m = self.ec.mul(self.g, r) return (m.x, inv(r, self.n) * (hashval + m.x * priv) % self.n) def validate(self, hashval, sig, pub): assert self.ec.is_valid(pub) assert self.ec.mul(pub, self.n) == self.ec.zero w = inv(sig[1], self.n) u1, u2 = hashval * w % self.n, sig[0] * w % self.n p = self.ec.add(self.ec.mul(self.g, u1), self.ec.mul(pub, u2)) return p.x % self.n == sig[0] pass ######### redo this after done code P = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff A = 0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc B = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b curve_order = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551 G = Coord(0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296, \ 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5) ec_object = EC(A, B, P, curve_order) dsa_object = DSA(ec_object, G) Ln = 256 num = 1500 bit = 24 twister = Twister() output = [] # io = process(['python3', 'server.py']) io = remote("45.122.249.68", 20025) # Register io.sendlineafter(b">> ", b"2") io.sendlineafter(b"username: ", b"admin") io.recvuntil(b"Account information:\n") user = [] cookies = [] user.append(io.recvline().strip()) io.recvuntil(b"Cookie: ") cookies.append(io.recvline().strip()) # login io.sendlineafter(b">> ", b"1") io.sendlineafter(b"information: ", user[0]) io.sendlineafter(b"cookie: ", cookies[0]) # Gacha outputs = [] for i in range(num): print(i) io.sendlineafter(b">> ", b"4") io.sendlineafter(b"number: ", b"1") io.recvuntil(b"Lucky number is: ") res = io.recvline().strip() res = int(res) res = res & 0xffffff res = res.to_bytes(3, "little") outputs.append(int.from_bytes(res, "big")) # io.interactive() # outputs = [int.from_bytes(random.randbytes(3)[::-1], "big") for _ in range(num)] equations = [twister.getrandbits(bit) for _ in range(num)] solver = Solver() for i in range(num): print(i) io.sendlineafter(b">>", b"3") for j in range(bit): solver.insert(equations[i][j], (outputs[i] >> (bit - 1 - j)) & 1) state = solver.solve() recovered_state = (3, tuple(state + [0]), None) random.setstate(recovered_state) for i in range(num): assert outputs[i] == random.getrandbits(bit) n = 50 context.log_level = 'debug' for i in range(n): # register io.sendlineafter(b">> ", b"2") io.sendlineafter(b"username: ", b"admin") user_id = get_random_bytes(8) io.recvuntil(b"Account information:\n") user.append(io.recvline().strip()) io.recvuntil(b"Cookie: ") cookies.append((io.recvline().strip(), random.randbytes((curve_order).bit_length() // 16).hex())) B = 2 ** 128 M = Matrix(QQ, n + 3, n + 3) M[0, 0] = B M[1, 1] = B M[2, 2] = B / curve_order for i in range(n): hashval = bytes_to_long(sha256(user[i + 1]).digest()[:Ln]) r, s = cookies[i + 1][0].decode().split(".") k = int(cookies[i + 1][1], 16) r = int(r, 16) s = int(s, 16) M[0, i + 3] = inverse(s, curve_order) * inverse(2 ** 128, curve_order) * hashval % curve_order M[1, i + 3] = inverse(2 ** 128, curve_order) * (-k % curve_order) % curve_order M[2, i + 3] = inverse(s, curve_order) * inverse(2 ** 128, curve_order) * r % curve_order M[i + 3, i + 3] = curve_order def register_account(username : str, isAdmin : bool, privkey: int): tmp = {"username" : username, "isAdmin" : isAdmin} user_id = get_random_bytes(8) tmp["userID"] = user_id.hex() while True: random_k = int.from_bytes(get_random_bytes((curve_order).bit_length() // 16), "big") if random_k < curve_order: break tmp_hash = bytes_to_long(sha256(json.dumps(tmp).encode()).digest()[:Ln]) r, s = dsa_object.sign(tmp_hash, privkey, random_k) cookie = long_to_bytes(r).hex() + "." + long_to_bytes(s).hex() return tmp, cookie M = M.LLL() for i in M: if i[0] == B and i[1] == B: print(i[-1] * 2 ** 128 % curve_order + int(cookies[-1][1], 16)) k = i[-1] * 2 ** 128 % curve_order + int(cookies[-1][1], 16) hashval = bytes_to_long(sha256(user[-1]).digest()[:Ln]) r, s = cookies[-1][0].decode().split(".") r = int(r, 16) s = int(s, 16) d = (k * s - hashval) * inverse(r, curve_order) % curve_order print(d) payload1, payload2 = register_account("admin", True, d) print(payload1) io.sendlineafter(b">> ", b"1") io.sendlineafter(b"information: ", json.dumps(payload1).encode()) io.sendlineafter(b"cookie: ", payload2) io.sendlineafter(b">> ", b"3") io.interactive() ``` ``` W1{it_1s_n3v3r_saf3_t0_us3_n0n_secure_cryptogr4phic_r4nd0mizat!0n} ```