# Nhập môn lập trình - IT001.P11.CTTN Tuần vừa rồi lớp CTF NMLT, thầy mình có tổ chức kiểm tra cuối kì bằng các bài tập CTF. Sau đây là WU của mình cho các câu giải được trong quá trình thi. [TOC] ## substitution ![image](https://hackmd.io/_uploads/SJwHvaJ8Jg.png) Chall cho mình một file python như sau ```python= KEY = { 'A': 'Q', 'B': 'W', 'C': 'E', 'D': 'R', 'E': 'T', 'F': 'Y', 'G': 'U', 'H': 'I', 'I': 'O', 'J': 'P', 'K': 'A', 'L': 'S', 'M': 'D', 'N': 'F', 'O': 'G', 'P': 'H', 'Q': 'J', 'R': 'K', 'S': 'L', 'T': 'Z', 'U': 'X', 'V': 'C', 'W': 'V', 'X': 'B', 'Y': 'N', 'Z': 'M', 'a': 'q', 'b': 'w', 'c': 'e', 'd': 'r', 'e': 't', 'f': 'y', 'g': 'u', 'h': 'i', 'i': 'o', 'j': 'p', 'k': 'a', 'l': 's', 'm': 'd', 'n': 'f', 'o': 'g', 'p': 'h', 'q': 'j', 'r': 'k', 's': 'l', 't': 'z', 'u': 'x', 'v': 'c', 'w': 'v', 'x': 'b', 'y': 'n', 'z': 'm', } def hehe(data, key): return ''.join(key.get(char, char) for char in data) def encrypt(plaintext): substituted = hehe(plaintext, KEY) return substituted if __name__ == "__main__": plaintext = "W1{???????????????}" encrypted = encrypt(plaintext) with open("encrypted.txt", "w") as f: f.write(encrypted) ``` Mã hóa bằng một hàm $\displaystyle f:KEY\rightarrow KEY$. Ta dựa vào các phép thế được đưa ra ở trên để giải mã lại flag trong file encrypted.txt ``` V1{lxwlzozxzogf} V->W, l->s,x->u,.... ``` Ngồi viết tay một hồi thì cuối cùng ta được flag là `W1{substitution}` :))) ## 4ES ![image](https://hackmd.io/_uploads/Hy8UjsgIJx.png) Chall cho ta một file python như sau : ```python= #!/usr/bin/env python3 from hashlib import sha256 from random import choices from Crypto.Cipher import AES from Crypto.Util.Padding import pad FLAG = b'W1{???????????????????????}' chars = b'AoThuatGiaDP' L = 3 w, x, y, z = ( bytes(choices(chars, k=L)), bytes(choices(chars, k=L)), bytes(choices(chars, k=L)), bytes(choices(chars, k=L)), ) k1 = sha256(w).digest() k2 = sha256(x).digest() k3 = sha256(y).digest() k4 = sha256(z).digest() pt = b'AES_AES_AES_AES!' ct = AES.new(k4, AES.MODE_ECB).encrypt( AES.new(k3, AES.MODE_ECB).encrypt( AES.new(k2, AES.MODE_ECB).encrypt( AES.new(k1, AES.MODE_ECB).encrypt( pt ) ) ) ) key = sha256(w + x + y + z).digest() enc_flag = AES.new(key, AES.MODE_ECB).encrypt(pad(FLAG, AES.block_size)) with open('output.txt', 'w') as f: f.write(f'pt = {pt.hex()}\nct = {ct.hex()}\nenc_flag = {enc_flag.hex()}') ``` Và một file output.txt ``` pt = 4145535f4145535f4145535f41455321 ct = a5d45cdb322abe38b9da6df19f997696 enc_flag = ecd0486b5c1c5a2b9af4e42abc8891445a97337cf1857e366bff6063bdbeaa7f ``` Dùng kĩ thuật MITM để Attack: Mục tiêu của ta là tìm lại 4 khóa $\displaystyle k_{1} ,k_{2} ,k_{3} ,k_{4}$. Để tìm lại 4 khóa này thì ta cần tìm lại 4 kí tự $\displaystyle w,x,y,z$ được tạo ngẫu nhiên bằng cách lấy 3 kí tự trong chars. Ta đã biết cả pt và ct thì việc cần làm là ta sẽ đi encrypt pt bằng 2 khóa bất kì rồi lưu lại trong bảng, tiếp đó ta sẽ decrypt ct bằng 2 khóa bất kì rồi sau đó sẽ tìm collision. Code em lấy ở một bài có ý tưởng giống : https://7rocky.github.io/en/ctf/other/crewctf/4es/ ```python= #!/usr/bin/env python3 from hashlib import sha256 from itertools import product from Cryptodome.Cipher import AES from Cryptodome.Util.Padding import unpad pt = bytes.fromhex("4145535f4145535f4145535f41455321") ct = bytes.fromhex("a5d45cdb322abe38b9da6df19f997696") enc_flag = bytes.fromhex("ecd0486b5c1c5a2b9af4e42abc8891445a97337cf1857e366bff6063bdbeaa7f") L = 3 chars = b'AoThuatGiaDP' char_keys = list(map(bytes, product(chars, repeat=L))) def encrypt_aes(key, plaintext): return AES.new(key, AES.MODE_ECB).encrypt(plaintext) def decrypt_aes(key, ciphertext): return AES.new(key, AES.MODE_ECB).decrypt(ciphertext) def mitm(): middle = {} for w in char_keys: k1 = sha256(w).digest() for x in char_keys: k2 = sha256(x).digest() enc = encrypt_aes(k2, encrypt_aes(k1, pt)) middle[enc] = (w, x) for z in char_keys: k4 = sha256(z).digest() for y in char_keys: k3 = sha256(y).digest() dec = decrypt_aes(k3, decrypt_aes(k4, ct)) if dec in middle: return middle[dec] + (y, z) wxyz = mitm() assert wxyz is not None, 'failed' w, x, y, z = wxyz k1 = sha256(w).digest() k2 = sha256(x).digest() k3 = sha256(y).digest() k4 = sha256(z).digest() assert ct == encrypt_aes(k4, encrypt_aes(k3, encrypt_aes(k2, encrypt_aes(k1, pt)))), 'Wrong keys found' key = sha256(b''.join(wxyz)).digest() FLAG = unpad(decrypt_aes(key, enc_flag), AES.block_size) print(FLAG.decode()) ``` ## hix ![image](https://hackmd.io/_uploads/BkQtsqZUke.png) Chall cho ta một file python ```python= import hashlib import random methods = ['md5', 'sha256', 'sha3_256', 'sha3_512', 'sha3_384', 'sha1', 'sha384', 'sha3_224', 'sha512', 'sha224'] def random_encrypt(x) : method = random.choice(methods) hash_obj = hashlib.new(method) hash_obj.update(x.encode()) return hash_obj.hexdigest() def main() : message = open("./../private/flag.txt", "r").read() enc = [] for char in message : x = (ord(char) + 20) % 130 x = hashlib.sha512(str(x).encode()).hexdigest() x = random_encrypt(x) enc.append(x) with open('encrypted_memory.txt', 'w') as f : f.write("ct = " + str(enc)) if __name__ == "__main__" : main() ``` và một file txt: ``` ct = ['f189636f8eef640b55d03387864fd17efd324453cc9276be5ff6bd4da88b13fca72438daaab00830a6d14330d37c0f7bee1e7c32d5dda0541a171f66a2343dc1', '1388cafa58065fa0c04372ce57f303cc4ec9fe62', 'f6266e2849bf8b8575701814cc3f3eb5369e887db54b34e85b1e4608b4fbf5e5', '31f33ac191e818db784cf8321d70f84763db2b2e599f90cf65868eec85a10f20ae0e23aa1cd48c2f13eec355b2975089490761a291ac2a1bcf33f5fbecead431', '981e4bce5dede3faa51a936f650e2c1d64169493860c67d68a1ffbbfa32f58598e7869f3f11aefc1620ee8d3ebe4e5f5', 'f06ffaaa6290bf47d26ba2c09c28dddd8f5bcad6ac464ec17fea48040acf1214d10bc109b7c47cffddb6bccd6b61b61a9e629a8f47ab26b80593f29c8c297489', 'a7d95b3bbde885b4eaa76afc6572e18e4483351005f637fe1f5a7bc0b000fe1f', '85245de371c327440a5f343f27d6df361225806e679950bab3a5a336', 'ea1923e909de3c3c3384ad9ae7696d73', '21df20aab35967470aada32375f535d4a735789bf0789fd421f85163c4d75c6e', 'b9491ae1a9de40d30a86c00139bd7d6f496f5bf4ce013bc2d5a43a97', '03f061f60f3527b15ff31d31dcce0761', '981e4bce5dede3faa51a936f650e2c1d64169493860c67d68a1ffbbfa32f58598e7869f3f11aefc1620ee8d3ebe4e5f5', 'f2a1a7e9dd5e6363050b0cdb0579ebfebdc5e348ab538bdcf47616139351cf2b9f92cb4d14446b3ad8bf182875b81e75', '24aaafc58a2b897aed5829b2e96d73b1de7cd680d76a1143cdc8baef', '6d80d11e5f1161ef86619dcdb186852b5218d6ac224b81b63555fe73741631c36ae0bcb5b3228fbed796c22dedeed587c9d65ddb825aee4fae92b6619e7ffd8f', '6f8b39550106044625102ee0cabf9fe1393f0013388633d5742fcc7e8df7708793a96885b9d18b795a2b0d9014704b9f', 'ddf3c543be9cac44f3af078583fe5fddb64104d93308c146c23f52ff25b2a6e23606c42dc0060a4dd9b11b446759cb5de1844471eb3d6d25c43c6fcc0d8d60c4', '95f2739053cf64555b0c0662b5e2d63822433f7fcac6960de6d57efda427461a58c6e2ffac6da6f4caa9407df10cc0be', 'a1bd4e0efc7ce8bd1d63433a0baa87e3a486fbfe2729d73d1dbf7d2822d201ee8726c6d94da1f09f1a53554e440ad6041ecab545b2085dc28c6f6849f0fcea23', 'a7d95b3bbde885b4eaa76afc6572e18e4483351005f637fe1f5a7bc0b000fe1f', '2b4561a521a82af6a26dfb76078ca97ba53a720f7ee67d923a6d3a13', 'b21ed1f3d501a8a842ef1b26ed3863cf10cf8231ee23a079f749cfa322702c8e', 'd798a32b52384219f8779dccf8b2173f4b73f075cbeb4507ee83c94e', 'b863fa3492fb87edcdef766f38a508ed', '9f876db4b58c1b7e499f35cdbd533a810060a0c8250bfc5421e0f42b2715b027', '4b14748ba0f3da581ddd7ec49dac41d34ea1ee6dae90818333b11501', '85153b2a5f8dea7f5488906cb65d61e9ac0666057636ff6b356dd4d8d0fc5d20', '6b91d6259827176bcb3f312a8faca297e56c7e627235b930cf8163b3e7a5328b', 'b21ed1f3d501a8a842ef1b26ed3863cf10cf8231ee23a079f749cfa322702c8e', '4c8740f90af1055f194a4c8e1b69522da228812465eb72b82b35c927bc48bf9d', 'b248b6b2f2c9365aa9a0e9b37a8057effd29bb2f34c79ec0b40124d08986832b5d227db95cb97b176541589985762d9a', '7260f9b5d1c58d0609523114ed324f396335d940f852dba558461b34c5a53630', 'a1bd4e0efc7ce8bd1d63433a0baa87e3a486fbfe2729d73d1dbf7d2822d201ee8726c6d94da1f09f1a53554e440ad6041ecab545b2085dc28c6f6849f0fcea23', '1077caf3ed754ed8fbd49c76134906e8', 'f3565219d115ec74a85056997cc25e98e3e4912a31c858c1e45b841047698e93', '83315b8fa07a35b12e3f47ebb365268b4a4a8ef2', '64c008d6460c2b98aba616b1d0d11a06b9df564b87d3aeedda83b36aacd3d0c160465109eb06c62e86e360cf026faa27a616dbbf2bec269be9ad128af96073bb', '60bbd94b3ac3ea7149fc6cd850d72d4f1750601275832815dd9a23d4c3757d84aca29d716da5dd72a0045f15ff969925', '94327e8c8321421e72f52cd726336e824630ec7dda31b07ce83f11b8234aea7a', 'a69ef62254280226cc4223a2341c727afcd7ce4e3ffd3f2f1c57d9d3cd30659b52b1c2b56f911a7157041b5f0ff8176f', '3c904622c8d8d79c6704d50ae0175b049b3a5708705ecdce932fe426b9f46f1bd6585b8288c1d38f6301c31af5feac02', 'a3939bf491ffd9824056e249d6e355d8423855f0'] ``` Đầu tiên ta để ý hàm sau: ```python= def random_encrypt(x) : method = random.choice(methods) hash_obj = hashlib.new(method) hash_obj.update(x.encode()) return hash_obj.hexdigest() ``` Khi ta gọi hàm này thì nó sẽ chọn ngẫu nhiên một hàm băm sau đó trả về giá trị $x$ đã được băm dưới dạng hexadecimal. Còn về flag sẽ được mã hóa từng kí tự như sau: ```python= for char in message : x = (ord(char) + 20) % 130 x = hashlib.sha512(str(x).encode()).hexdigest() x = random_encrypt(x) enc.append(x) ``` Nhìn thì có vẻ ngẫu nhiên nhưng ta có thể lợi dụng tính chất cố định của hàm băm để bruteforce lại kí tự ban đầu. ![image](https://hackmd.io/_uploads/BJkgnj8LJl.png) Cụ thể mỗi hàm băm H tạo đầu ra có độ dài cố định cho nên ta có thể dựa vào độ dài của các char sau khi băm để xác định ngược lại thuật toán băm và brute force: ```python= import hashlib hash_lengths = { 32: 'md5', 40: 'sha1', 56: ['sha224', 'sha3_224'], 64: ['sha256', 'sha3_256'], 96: ['sha384', 'sha3_384'], 128: ['sha512', 'sha3_512'] } def identify_hash_algorithm(hash_value): length = len(hash_value) return hash_lengths.get(length, "Unknown") def hash_attempt(method, data): try: hash_obj = hashlib.new(method) hash_obj.update(data.encode()) return hash_obj.hexdigest() except ValueError: return None def reverse_character(hash_value): algorithms = identify_hash_algorithm(hash_value) if algorithms == "Unknown": return None if not isinstance(algorithms, list): algorithms = [algorithms] for method in algorithms: for i in range(130): intermediate_hash = hashlib.sha512(str(i).encode()).hexdigest() if hash_attempt(method, intermediate_hash) == hash_value: original_char = chr((i - 20) % 130) return original_char return None encrypted_values = ['f189636f8eef640b55d03387864fd17efd324453cc9276be5ff6bd4da88b13fca72438daaab00830a6d14330d37c0f7bee1e7c32d5dda0541a171f66a2343dc1', '1388cafa58065fa0c04372ce57f303cc4ec9fe62', 'f6266e2849bf8b8575701814cc3f3eb5369e887db54b34e85b1e4608b4fbf5e5', '31f33ac191e818db784cf8321d70f84763db2b2e599f90cf65868eec85a10f20ae0e23aa1cd48c2f13eec355b2975089490761a291ac2a1bcf33f5fbecead431', '981e4bce5dede3faa51a936f650e2c1d64169493860c67d68a1ffbbfa32f58598e7869f3f11aefc1620ee8d3ebe4e5f5', 'f06ffaaa6290bf47d26ba2c09c28dddd8f5bcad6ac464ec17fea48040acf1214d10bc109b7c47cffddb6bccd6b61b61a9e629a8f47ab26b80593f29c8c297489', 'a7d95b3bbde885b4eaa76afc6572e18e4483351005f637fe1f5a7bc0b000fe1f', '85245de371c327440a5f343f27d6df361225806e679950bab3a5a336', 'ea1923e909de3c3c3384ad9ae7696d73', '21df20aab35967470aada32375f535d4a735789bf0789fd421f85163c4d75c6e', 'b9491ae1a9de40d30a86c00139bd7d6f496f5bf4ce013bc2d5a43a97', '03f061f60f3527b15ff31d31dcce0761', '981e4bce5dede3faa51a936f650e2c1d64169493860c67d68a1ffbbfa32f58598e7869f3f11aefc1620ee8d3ebe4e5f5', 'f2a1a7e9dd5e6363050b0cdb0579ebfebdc5e348ab538bdcf47616139351cf2b9f92cb4d14446b3ad8bf182875b81e75', '24aaafc58a2b897aed5829b2e96d73b1de7cd680d76a1143cdc8baef', '6d80d11e5f1161ef86619dcdb186852b5218d6ac224b81b63555fe73741631c36ae0bcb5b3228fbed796c22dedeed587c9d65ddb825aee4fae92b6619e7ffd8f', '6f8b39550106044625102ee0cabf9fe1393f0013388633d5742fcc7e8df7708793a96885b9d18b795a2b0d9014704b9f', 'ddf3c543be9cac44f3af078583fe5fddb64104d93308c146c23f52ff25b2a6e23606c42dc0060a4dd9b11b446759cb5de1844471eb3d6d25c43c6fcc0d8d60c4', '95f2739053cf64555b0c0662b5e2d63822433f7fcac6960de6d57efda427461a58c6e2ffac6da6f4caa9407df10cc0be', 'a1bd4e0efc7ce8bd1d63433a0baa87e3a486fbfe2729d73d1dbf7d2822d201ee8726c6d94da1f09f1a53554e440ad6041ecab545b2085dc28c6f6849f0fcea23', 'a7d95b3bbde885b4eaa76afc6572e18e4483351005f637fe1f5a7bc0b000fe1f', '2b4561a521a82af6a26dfb76078ca97ba53a720f7ee67d923a6d3a13', 'b21ed1f3d501a8a842ef1b26ed3863cf10cf8231ee23a079f749cfa322702c8e', 'd798a32b52384219f8779dccf8b2173f4b73f075cbeb4507ee83c94e', 'b863fa3492fb87edcdef766f38a508ed', '9f876db4b58c1b7e499f35cdbd533a810060a0c8250bfc5421e0f42b2715b027', '4b14748ba0f3da581ddd7ec49dac41d34ea1ee6dae90818333b11501', '85153b2a5f8dea7f5488906cb65d61e9ac0666057636ff6b356dd4d8d0fc5d20', '6b91d6259827176bcb3f312a8faca297e56c7e627235b930cf8163b3e7a5328b', 'b21ed1f3d501a8a842ef1b26ed3863cf10cf8231ee23a079f749cfa322702c8e', '4c8740f90af1055f194a4c8e1b69522da228812465eb72b82b35c927bc48bf9d', 'b248b6b2f2c9365aa9a0e9b37a8057effd29bb2f34c79ec0b40124d08986832b5d227db95cb97b176541589985762d9a', '7260f9b5d1c58d0609523114ed324f396335d940f852dba558461b34c5a53630', 'a1bd4e0efc7ce8bd1d63433a0baa87e3a486fbfe2729d73d1dbf7d2822d201ee8726c6d94da1f09f1a53554e440ad6041ecab545b2085dc28c6f6849f0fcea23', '1077caf3ed754ed8fbd49c76134906e8', 'f3565219d115ec74a85056997cc25e98e3e4912a31c858c1e45b841047698e93', '83315b8fa07a35b12e3f47ebb365268b4a4a8ef2', '64c008d6460c2b98aba616b1d0d11a06b9df564b87d3aeedda83b36aacd3d0c160465109eb06c62e86e360cf026faa27a616dbbf2bec269be9ad128af96073bb', '60bbd94b3ac3ea7149fc6cd850d72d4f1750601275832815dd9a23d4c3757d84aca29d716da5dd72a0045f15ff969925', '94327e8c8321421e72f52cd726336e824630ec7dda31b07ce83f11b8234aea7a', 'a69ef62254280226cc4223a2341c727afcd7ce4e3ffd3f2f1c57d9d3cd30659b52b1c2b56f911a7157041b5f0ff8176f', '3c904622c8d8d79c6704d50ae0175b049b3a5708705ecdce932fe426b9f46f1bd6585b8288c1d38f6301c31af5feac02', 'a3939bf491ffd9824056e249d6e355d8423855f0'] flag = '' for hash_val in encrypted_values: char = reverse_character(hash_val) if char: flag += char else: flag += '?' print("Flag:", flag) ``` Flag: ```W1{are_you_trying_to_predict_randomness@_@}``` ## gcdpoly Chall cho ta một file python: ```python= from Crypto.Util.number import getPrime, GCD, bytes_to_long, getRandomNBitInteger, long_to_bytes while True: p = getPrime(1024) q = getPrime(1024) e = 0x101 if GCD((p - 1) * (q - 1), e) == 1: break N = p * q flag = b'W1{??????????????????????????????}' assert len(flag) == 34 flag = bytes_to_long(flag) x = getRandomNBitInteger(flag.bit_length()) hint = x * flag more_hint = long_to_bytes(hint) + long_to_bytes(x) x_enc = pow(x, e, N) hint_enc = pow(hint, e, N) more_hint = pow(bytes_to_long(more_hint), e, N) print(f"{N = }") print(f"{e = }") print(f"{x_enc = }") print(f"{hint_enc = }") print(f"{more_hint = }") ``` Và một file encrypted.txt: ``` N = 17455402175437176532762011936605374319510514858668809934283527533793019636085567941340428251796332874658887034973125379656976787899948739462792044779394617697923593713865571947480575715808220681702040571713834962591162896883126625987542697405715758837886980695935599886296833761332668492586517301554181926290501066261415348762218737641025568994218666972555733452415715789836479580743518759606518560790216074668160062318077354838559692467462868744895402199050233183880016075435689438118159209700791215305127872319962224886262950782267716759134051276573034748789032249652527725069033671937172082733885413797270594046659 e = 257 x_enc = 9996223492945930307599847149030142323594733002172616721058190037475290836989969789254053717666020951921459176187704524599282156785134867888383301434607072014358468217885735213022405058511843363872636247404379439558338700149306005415828454372965267435748808512594750475657894932556732283868780381727090987803428282860149481730335955039459503864265070695125410078101017244023029264699216041534759532946007673595164569006924131708811385328137810260255820693022452407318872511604340046032703603689226966698963947140903268751317131092280784353673781583886340493103149941363175123086894375292235376263422464299937964242529 hint_enc = 14789408518282984435806404374838863944620574135335751718371470938725019307943299519996110191146342336694298360945290481279154411743070024294186906891227799217417210874041187513533095475177536407329059640170945862144612697751930832068191380343375683600799846751305650713478329200992580277004206060405550671431501911580994159286609366370084466999243157644437677404006488711305765810155959071167712372903081938159711179919854865680181098099964158600115467907298067836087274873276020158780106022394580520866275356696118835744838635778222876212312712636991385848611744174549177416655001397274793228678792857731056729401479 more_hint = 11029946941431959428525282661954512235928562456214126818506773711111719425190566952451999044331493580332064561323097236705681748867775061691143721439117904810714121197594096628261432006374462970213237363365127870527463436083313806638082369213886645623998873849163617775630517410918551847474235496220816340531322685920512036201318038335403616434596578979491681508214839604410062184542042494994435105891585296125095470899377721478810921246711310813490006032578790263495042565529710594969722185609497948277910670140174206535208998036166377688284289995920516604924956793172587315806815116854231045587846108456607501836645 ``` Từ đề bài mình đoán được phần nào ý của tác giả là lấy gcd của 2 đa thức với các hệ số thuộc trường hữu hạn $\displaystyle \mathbb{Z} /n\mathbb{Z}$ Trước hết ta cần xử lí giá trị của more_hint để sử dụng các tính chất số học. Ta để ý more_hint ban đầu được tính bởi: `more_hint = long_to_bytes(hint) + long_to_bytes(x)`. Em có thử ngồi nghịch một chút với python và phát hiện ra tính chất sau: ![image](https://hackmd.io/_uploads/HJSDE2U81e.png) Phép cộng 2 xâu bytes khác với phép cộng số học thông thường, cụ thể ta sẽ có công thức như dưới đây: $$\displaystyle more\_hint=hint\times 2^{34\times 8} +x=hint\times 2^{272} +x$$ Trong đó 34 chính là số bytes của $x$. Ta nhân 8 lên sẽ được số bit cần tính của $x$ vì ban đầu khi khai báo $x$ ta sử dụng các dòng lệnh sau : ```python= assert len(flag) == 34 flag = bytes_to_long(flag) x = getRandomNBitInteger(flag.bit_length()) ``` Như vậy dựa vào đây ta sẽ xây dựng 2 đa thức $f(x)$ và $g(x)$ có nghiệm trên $\displaystyle \mathbb{Z} /n\mathbb{Z}$ sẽ là $flag$ như sau: $\displaystyle f( x) =x\_enc\times x^{e} -hint\_enc$ và $\displaystyle g( x) =x\_enc\times \left( x\times 2^{272} +1\right)^{e} -more\_hint$ vì ta có $\displaystyle ( more\_hint)^{e} =\left( hint\times 2^{272} +x\right)^{e} =x^{e}\left( flag\times 2^{272} +1\right)^{e}$, nếu đưa về mod $\displaystyle n$ thì ta chuyển $\displaystyle x^{e}$ thành $\displaystyle x\_enc$ Code sage giải bài này: Ta sẽ dùng thuật Half GCD để tăng tốc độ tính gcd của 2 đa thức ```sage= import sys n = 17455402175437176532762011936605374319510514858668809934283527533793019636085567941340428251796332874658887034973125379656976787899948739462792044779394617697923593713865571947480575715808220681702040571713834962591162896883126625987542697405715758837886980695935599886296833761332668492586517301554181926290501066261415348762218737641025568994218666972555733452415715789836479580743518759606518560790216074668160062318077354838559692467462868744895402199050233183880016075435689438118159209700791215305127872319962224886262950782267716759134051276573034748789032249652527725069033671937172082733885413797270594046659 P.<x> = PolynomialRing(Zmod(n)) def HGCD(a, b): if 2 * b.degree() <= a.degree() or a.degree() == 1: return 1, 0, 0, 1 m = a.degree() // 2 a_top, a_bot = a.quo_rem(x^m) b_top, b_bot = b.quo_rem(x^m) R00, R01, R10, R11 = HGCD(a_top, b_top) c = R00 * a + R01 * b d = R10 * a + R11 * b q, e = c.quo_rem(d) d_top, d_bot = d.quo_rem(x^(m // 2)) e_top, e_bot = e.quo_rem(x^(m // 2)) S00, S01, S10, S11 = HGCD(d_top, e_top) RET00 = S01 * R00 + (S00 - q * S01) * R10 RET01 = S01 * R01 + (S00 - q * S01) * R11 RET10 = S11 * R00 + (S10 - q * S11) * R10 RET11 = S11 * R01 + (S10 - q * S11) * R11 return RET00, RET01, RET10, RET11 def GCD(a, b): print(a.degree(), b.degree()) q, r = a.quo_rem(b) if r == 0: return b R00, R01, R10, R11 = HGCD(a, b) c = R00 * a + R01 * b d = R10 * a + R11 * b if d == 0: return c.monic() q, r = c.quo_rem(d) if r == 0: return d return GCD(d, r) sys.setrecursionlimit(500000) e = 257 x_enc = 9996223492945930307599847149030142323594733002172616721058190037475290836989969789254053717666020951921459176187704524599282156785134867888383301434607072014358468217885735213022405058511843363872636247404379439558338700149306005415828454372965267435748808512594750475657894932556732283868780381727090987803428282860149481730335955039459503864265070695125410078101017244023029264699216041534759532946007673595164569006924131708811385328137810260255820693022452407318872511604340046032703603689226966698963947140903268751317131092280784353673781583886340493103149941363175123086894375292235376263422464299937964242529 hint_enc = 14789408518282984435806404374838863944620574135335751718371470938725019307943299519996110191146342336694298360945290481279154411743070024294186906891227799217417210874041187513533095475177536407329059640170945862144612697751930832068191380343375683600799846751305650713478329200992580277004206060405550671431501911580994159286609366370084466999243157644437677404006488711305765810155959071167712372903081938159711179919854865680181098099964158600115467907298067836087274873276020158780106022394580520866275356696118835744838635778222876212312712636991385848611744174549177416655001397274793228678792857731056729401479 more_hint = 11029946941431959428525282661954512235928562456214126818506773711111719425190566952451999044331493580332064561323097236705681748867775061691143721439117904810714121197594096628261432006374462970213237363365127870527463436083313806638082369213886645623998873849163617775630517410918551847474235496220816340531322685920512036201318038335403616434596578979491681508214839604410062184542042494994435105891585296125095470899377721478810921246711310813490006032578790263495042565529710594969722185609497948277910670140174206535208998036166377688284289995920516604924956793172587315806815116854231045587846108456607501836645 f=x_enc*x^e-hint_enc g=x_enc*(x*2^272+1)^e-more_hint print(GCD(f, g)) ``` Sau khi chạy ta được một đa thức dạng $ax+b$ như sau: ![image](https://hackmd.io/_uploads/S1D1dXzIkx.png) Vì các hệ số được lấy theo modulo $n$ nên để khôi phục lại flag ta phải tính nghiệm của đa thức là $x=-b/a$, nếu viết theo ngôn ngữ số học thông thường sẽ là $(n-b) \times a^{-1} \mod n$. Như vậy ta sẽ tìm được flag ```python= from Cryptodome.Util.number import * n=17455402175437176532762011936605374319510514858668809934283527533793019636085567941340428251796332874658887034973125379656976787899948739462792044779394617697923593713865571947480575715808220681702040571713834962591162896883126625987542697405715758837886980695935599886296833761332668492586517301554181926290501066261415348762218737641025568994218666972555733452415715789836479580743518759606518560790216074668160062318077354838559692467462868744895402199050233183880016075435689438118159209700791215305127872319962224886262950782267716759134051276573034748789032249652527725069033671937172082733885413797270594046659 a=9773221259870845390384818924439275392002320319354194062664469481906538648962533984193856566286726646549621893703410878200111498250592781252476398959962259814643582137359417983373155673348118323881888180481339981050792337421591386568963885268723828737013182590217759110103543677724987984290275930060782497628312542293026286318220951342121933270427041000128915820341861697214112686440657584246180841846516658949855303891393915680861967928721468562700586480922287501013135051379549802628887158314506259429804479176738033029076820721151360559878516632926877734775134865395004109545918323388019950517382238058253501658507 b=5357212906179756866715281890069471101230729984515492393746969161253055889321856843477372600935547086172508827384057895230497160502713414641618616945137818637240155063728755382396358659302086413212825145096220739713877265341187366255211704119424414138878404119457537474934520777352921220692584549517200469087399597743579923527176836836923022328356476427888546933649686694317099836309909270290836116559518044311984050115050973424402775001829379077243360179499178421927360804572434043005576157110890347548214244899458009827525995852187530858283237501761596156164759609769430221390571429877608824356245629296201289690916 d=inverse(a,n) c=(n-b)*d % n print(long_to_bytes(c)) ``` Ta được flag : ```W1{jus7_4dd_l1tt3_b1t_s3cr3t_hehe}``` Sau khi hết giải thì em có tìm hiểu được thêm một cách nữa sử dụng Groebner Basis. Tham khảo tại : - https://www.youtube.com/watch?v=jxv7XnKIPMQ&t=1017s - https://mystiz.hk/posts/2021/2021-02-15-dicectf-1/ Về cơ bản kĩ thuật này giúp biến đổi một hệ các đa thức đa biến về dạng đơn giản hơn rồi sau đó lấy gcd của chúng. Định nghĩa hình thức của cơ sở Groebner như sau: Về cơ sở lý thuyết em tham khảo tại [đây](https://file.notion.so/f/f/2118b33f-6c63-4414-95b2-c6cd3258445d/dd15eb31-6169-41ab-bca6-c27209a219a0/(Graduate_Studies_in_Mathematics)_Joseph_J._Rotman_-_Advanced_Modern_Algebra_Part_1-American_Mathematical_Society_(2015).pdf?table=block&id=f7362dbb-d043-4dab-a756-d0644b8e23b2&spaceId=2118b33f-6c63-4414-95b2-c6cd3258445d&expirationTimestamp=1736157600000&signature=BnHoofZD1uVroJvde4XPG7UdUnGeURsJaPMGlvFjxRg&downloadName=%28Graduate+Studies+in+Mathematics%29+Joseph+J.+Rotman+-+Advanced+Modern+Algebra%2C+Part+1-American+Mathematical+Society+%282015%29.pdf) (trang 639) ![image](https://hackmd.io/_uploads/HJ_2i6u8kx.png) Code sage cho bài này. ```python= n = 17455402175437176532762011936605374319510514858668809934283527533793019636085567941340428251796332874658887034973125379656976787899948739462792044779394617697923593713865571947480575715808220681702040571713834962591162896883126625987542697405715758837886980695935599886296833761332668492586517301554181926290501066261415348762218737641025568994218666972555733452415715789836479580743518759606518560790216074668160062318077354838559692467462868744895402199050233183880016075435689438118159209700791215305127872319962224886262950782267716759134051276573034748789032249652527725069033671937172082733885413797270594046659 e = 257 x_enc = 9996223492945930307599847149030142323594733002172616721058190037475290836989969789254053717666020951921459176187704524599282156785134867888383301434607072014358468217885735213022405058511843363872636247404379439558338700149306005415828454372965267435748808512594750475657894932556732283868780381727090987803428282860149481730335955039459503864265070695125410078101017244023029264699216041534759532946007673595164569006924131708811385328137810260255820693022452407318872511604340046032703603689226966698963947140903268751317131092280784353673781583886340493103149941363175123086894375292235376263422464299937964242529 hint_enc = 14789408518282984435806404374838863944620574135335751718371470938725019307943299519996110191146342336694298360945290481279154411743070024294186906891227799217417210874041187513533095475177536407329059640170945862144612697751930832068191380343375683600799846751305650713478329200992580277004206060405550671431501911580994159286609366370084466999243157644437677404006488711305765810155959071167712372903081938159711179919854865680181098099964158600115467907298067836087274873276020158780106022394580520866275356696118835744838635778222876212312712636991385848611744174549177416655001397274793228678792857731056729401479 flag_enc = (pow(x_enc,-1,n)*hint_enc)%n more_hint = 11029946941431959428525282661954512235928562456214126818506773711111719425190566952451999044331493580332064561323097236705681748867775061691143721439117904810714121197594096628261432006374462970213237363365127870527463436083313806638082369213886645623998873849163617775630517410918551847474235496220816340531322685920512036201318038335403616434596578979491681508214839604410062184542042494994435105891585296125095470899377721478810921246711310813490006032578790263495042565529710594969722185609497948277910670140174206535208998036166377688284289995920516604924956793172587315806815116854231045587846108456607501836645 P.<x,y> = PolynomialRing(Zmod(n)) f1 = x^e - x_enc f2 = y^e - flag_enc f3 = ((x*y)*2^(8*34) +x)^e - more_hint I = P.ideal([ f1,f2,f3 ]) for eq in I.groebner_basis(): print(eq) ``` ![image](https://hackmd.io/_uploads/SyI0jTdL1x.png) Cả 3 đa thức $f1,f2,f3$ đều có nghiệm chung là $x_{enc}$ và $flag_{enc}$. Sau khi có được $y+flag_{enc}$ thì em decrypt lại flag. ```python= from Cryptodome.Util.number import * print(long_to_bytes(-17455402175437176532762011936605374319510514858668809934283527533793019636085567941340428251796332874658887034973125379656976787899948739462792044779394617697923593713865571947480575715808220681702040571713834962591162896883126625987542697405715758837886980695935599886296833761332668492586517301554181926290501066261415348762218737641025568994218666972555733452415715789836479580743518759606518560790216074668160062318077354838559692467462868744895402199050233183880016075435689438118159209700791215305127872319962224886262950782267714174483004833663080642567977831782538069781591097917792917559279142465967909791046%n)) ``` ## DH Chall cho ta một file python: ```python= from Crypto.Util.number import isPrime, long_to_bytes, getPrime from Crypto.Cipher import AES from Crypto.Util.Padding import pad from random import randint from hashlib import sha256 FLAG = b"W1{fake-flag}" class DH: def __init__(self): self.gen_params() def gen_params(self): self.r = getPrime(512) while True: self.q = getPrime(42) self.p = (2 * self.q * self.r) + 1 if isPrime(self.p): break while True: self.h = getPrime(42) self.g = pow(self.h, 2 * self.r, self.p) if self.g != 1: break self.a = randint(2, self.p - 2) self.b = randint(2, self.p - 2) self.A, self.B = pow(self.g, self.a, self.p), pow(self.g, self.b, self.p) self.ss = pow(self.A, self.b, self.p) def encrypt(self, flag_part): key = sha256(long_to_bytes(self.ss)).digest()[:16] cipher = AES.new(key, AES.MODE_ECB) ct = cipher.encrypt(pad(flag_part, 16)).hex() return f"encrypted = {ct}" def get_params(self): return f"p = {self.p}\ng = {self.g}\nA = {self.A}\nB = {self.B}" def main(): dh = DH() print(dh.get_params()) print(dh.encrypt(FLAG)) if __name__ == "__main__": main() p = 85013941328859365232686230728938372320812319905627686919070637645614632817039920673725615375841158719310596592903101914818137738460649589340349796188816568005092757847 g = 20033344683527080232439150682925185454003164954955126339094967675384779782733210350757021743656898625398860187361281262413493941502725149445995471514781822892886669776 A = 76548721461171533747911417838852759206858825205673491250696441734297318615226024320798706656529038703728631231084155790148283919370554345818139818854112841655270107839 B = 2103083080159597422706551446020625757109756570951674830166998494220734179439318911618156966499109201221652320384817270671579741987575328177442670242481963924501204498 encrypted = "240e7b7678aaaa0dcbe06de7c5598a1ca0be7e2ae584bc7dfd2388cdb1d4fb6a37ceb94556757afc293999cbe5a5a2dbb4071ebf6cfd4332088555f9b2de1922" ``` Cụ thể đây là DFKE - trao đổi khóa Diffie-Hellman ![image](https://hackmd.io/_uploads/SyYnoAQ8Je.png) Như vậy ta có shared-secret được tính bằng cách lấy $\displaystyle ss=A^{b} \equiv B^{a}(\bmod p)$ và ta cần giải DLP để tính được $a,b$. Để ý $p=2qr+1$ có cấu trúc khá đặc biệt nên ta có thể thử dùng thuật Pohlig Hellman để giải quyết. Dùng tool để factor $p-1$: ![image](https://hackmd.io/_uploads/rJPBp0mUyl.png) Ta sẽ giải tìm $a$. ```python= p = 85013941328859365232686230728938372320812319905627686919070637645614632817039920673725615375841158719310596592903101914818137738460649589340349796188816568005092757847 g = 20033344683527080232439150682925185454003164954955126339094967675384779782733210350757021743656898625398860187361281262413493941502725149445995471514781822892886669776 a = 76548721461171533747911417838852759206858825205673491250696441734297318615226024320798706656529038703728631231084155790148283919370554345818139818854112841655270107839 order1 = p - 1 factors =[2,3532729477811,12032331071885089636894466252658319251627277386273560255196257546850692960100677515370690305182105378105239739195632026290421584949679657929829280135767593] from sage.all import * K = GF(p) res = [] for i in factors[:-1]: g_i = K(pow(g, order1 // i, p)) a_i = K(pow(a, order1 // i, p)) order = ZZ(i) x = discrete_log(a_i, g_i, ord=order) res.append(x) b = crt(res, factors[:-1]) print(b) B=2103083080159597422706551446020625757109756570951674830166998494220734179439318911618156966499109201221652320384817270671579741987575328177442670242481963924501204498 ss=pow(B,b,p) print(ss) ``` Ở trong thuật ta bỏ qua thừa số nguyên tố cuối cùng vì thừa số này khá lớn, nếu chạy sẽ mất rất nhiều thời gian. Để kiểm tra xem kết quả khi giải CRT với 2 thừa số nguyên tố có đúng hay không thì ta có thể ```print(b)``` ra và check lại phương trình $\displaystyle g^{a} \equiv A(\bmod p)$. Trong trường hợp này thì may mắn khi $\displaystyle g=h^{2r}$ cho nên $\displaystyle g^{q} =h^{2rq} =1$. Điều này chứng tỏ $g$ thuộc vào một nhóm con có cấp là $q$ cho nên ta chỉ cần giải bài DLP với $2$ và $q$ là đủ. ``` exp = 5657118519872 ss = 66277002472717231027343343852936854583223697325634048850185463896894064552042232620221279686774192339783152904946258245343020845163004549305451198199889253864704673751 ``` Sau khi có ss ta decrypt lại flag: ```python= from Cryptodome.Cipher import AES from Cryptodome.Util.Padding import unpad from hashlib import sha256 from binascii import unhexlify encrypted_hex = "240e7b7678aaaa0dcbe06de7c5598a1ca0be7e2ae584bc7dfd2388cdb1d4fb6a37ceb94556757afc293999cbe5a5a2dbb4071ebf6cfd4332088555f9b2de1922" encrypted_bytes = unhexlify(encrypted_hex) ss = 66277002472717231027343343852936854583223697325634048850185463896894064552042232620221279686774192339783152904946258245343020845163004549305451198199889253864704673751 key = sha256(ss.to_bytes((ss.bit_length() + 7) // 8, byteorder='big')).digest()[:16] cipher = AES.new(key, AES.MODE_ECB) decrypted_flag = unpad(cipher.decrypt(encrypted_bytes), 16) print(decrypted_flag.decode()) ``` Flag là : ```W1{so_you_know_about_the_Diffie-Hellman-key_exchange}``` ## Smooth Chall cho ta một file python: ```python= from Crypto.Util.number import * from Crypto.Cipher import AES import hashlib FLAG = b'W1{???????????????????????????????}' p = 29488186972994534670417457155366645471182192199548659526413766939622361834146838276946251349867903845037032661551684336383382344530884181161185642157078150688387732468939373900371006578051481214072847442197503071055434360628659838236532690198978142021175339546919178335298684316724700262657029112644393293166154034921906788597061578283700388736429770020977264432345664045738339856555098074820929368834732846308848389109185416718414806791898185843424928326488893410923833562473301321779917349894586830655477919639150483605307802684111768011681546150738863663345823398017514538698657196101072867671246153807653084849979 e = 0x10001 x = getRandomNBitInteger(1550) hint = pow(e, x, p) key = hashlib.sha512(long_to_bytes(x)).digest()[:16] cipher = AES.new(key, AES.MODE_ECB) print(hint) print(cipher.encrypt(FLAG)) # 15565989507023520599989453473844888345738293883767540044682200387116098778726727092639584680030282924680754836996824347666944190568219259972559913371376230295642182212369433729563762217727277805412925981228881460051783217872389120277083233191144941820776264906459152636385581608797345048727329767042394346810437890763454382771288788698625345149261169962034579205221557431869735207318556427897861682927352582426356093597283783347686188442927706115209934983043745580341869373867823516184740079142532925105289504694660421450970265051590438438057830106683050024773068164385422394299956145634093724572089312716467972923242 # b'\x15\xa5(l\xa3\xaar\xcd\xf0cN\ti\x12\xb8\x9fB\x82\xd3N\x86\x05\xa1\xb1\x82\xc4\xe1\x17vf\x0e,' ``` Ta cần giải bài DLP để tìm lại $x$ thỏa mãn $e^x=hint \bmod p$ Từ đề bài ta được gợi ý rằng $p-1$ là một số nguyên trơn (smooth), tức là một số nguyên mà có thể dễ dàng phân tích về dưới dạng các thừa số nguyên tố nhỏ hơn. Vậy ý tưởng của bài này tương tự như bài DH nhưng có vấn đề ở chỗ, nếu như ở bài DH ta có thể giải mà bỏ qua thừa số nguyên tố cuối cùng, thì ở bài này nếu như ta bỏ qua thừa số nguyên tố cuối cùng nghiệm sẽ không còn đúng nữa. Ở bài DH ta có thể xử lí mà bó qua thừa số nguyên tố cuối cùng bởi vì cấu tạo của số $g$ khá đặc biệt. Cụ thể $\displaystyle g=h^{2r}$ cho nên $\displaystyle g^{q} =h^{2rq} =1$ cho nên ta có thể xác định được số $x$ thực chất thỏa mãn $0<a<q$. Còn ở bài này thì không như vậy: ![image](https://hackmd.io/_uploads/rJWfO1VIyg.png) Trước tiên ta sẽ giải bài toán Loga rời rạc cho các nhóm con nhỏ trước. ```python= p = 29488186972994534670417457155366645471182192199548659526413766939622361834146838276946251349867903845037032661551684336383382344530884181161185642157078150688387732468939373900371006578051481214072847442197503071055434360628659838236532690198978142021175339546919178335298684316724700262657029112644393293166154034921906788597061578283700388736429770020977264432345664045738339856555098074820929368834732846308848389109185416718414806791898185843424928326488893410923833562473301321779917349894586830655477919639150483605307802684111768011681546150738863663345823398017514538698657196101072867671246153807653084849979 g = 0x10001 a = 15565989507023520599989453473844888345738293883767540044682200387116098778726727092639584680030282924680754836996824347666944190568219259972559913371376230295642182212369433729563762217727277805412925981228881460051783217872389120277083233191144941820776264906459152636385581608797345048727329767042394346810437890763454382771288788698625345149261169962034579205221557431869735207318556427897861682927352582426356093597283783347686188442927706115209934983043745580341869373867823516184740079142532925105289504694660421450970265051590438438057830106683050024773068164385422394299956145634093724572089312716467972923242 order1 = p - 1 factors=[2, 15739, 16193, 34146337, 34969303, 34996979, 35882111, 36235453, 36253411, 36425761, 36699737, 37482199, 38138383, 38335727, 38523971, 40124191, 41442881, 42039769, 43915397, 44131229, 44386523, 44601737, 44918989, 45234109, 47471797, 47549387, 47614939, 47685917, 48562117, 49424983, 49518451, 49592497, 51053143, 51603091, 51944203, 53274623, 53720993, 53812277, 53963431, 54074849, 54156757, 54161383, 54784601, 54825247, 55148501, 56594249, 58469773, 59463269, 60129947, 60522233, 60733993, 60796577, 61309427, 61975691, 62113789, 62556521, 62747701, 63628133, 64570157, 64974397, 65424053, 66279959, 7460499821685332157113516060069886564400427229472624761209683142140995510658080396491764993046100641019523524398412618076429841989075435122250235698035949] from sage.all import * K = GF(p) res = [] for i in factors[:-1]: g_i = K(pow(g, order1 // i, p)) a_i = K(pow(a, order1 // i, p)) order = ZZ(i) x = discrete_log(a_i, g_i, ord=order) res.append(x) b = crt(res, factors[:-1]) print(b) # x=1855512073301756587756398232044694453287540787670792602075522813991081601061737651961714765303931127584794086388195063345643498845590200784521163870740676836865034348236672261500637967777776528654389569453659092664362103325055772843213798878968673741544455797271025007597011355845701322512387115527794386532917304746518282690374593080519689235943954434619469423490386939887715663575148853233409899365866194541445855735427925029943387649934084213322940603441421762 ``` Nghiệm $x$ này vẫn chưa thỏa điều kiện ban đầu của bài. Để tìm ra chính xác $x$ ta thực hiện ý tưởng như sau: Ở bước cuối cùng của thuật Pohlig Hellman ta sử dụng CRT để tính nghiệm theo modulo các thừa số nguyên tố. Việc sử dụng CRT như vậy giúp ta sinh ra được một họ nghiệm (vô hạn nghiệm) gồm các số $x$ thỏa mãn hệ phương trình đồng dư. Cụ thể là định lí như dưới đây: ![image](https://hackmd.io/_uploads/Sky0ZzNUkl.png) Họ các nghiệm thỏa mãn sinh ra bởi phương trình đồng dư: ![image](https://hackmd.io/_uploads/BkN1zGEUkl.png) Như vậy, từ số $x$ tìm được ở trên, ta tiếp tục sinh ra thêm các nghiệm khác bằng cách cộng thêm vào $x$ modulo $m$ là tích của các thừa số nguyên tố, không tính thừa số nguyên tố cuối cùng. Bằng cách này ta có thể tìm được nghiệm chính xác của bài DLP mà không cần phải giải bài DLP cho thừa số nguyên tố lớn cuối cùng. Script sage cụ thể như sau: ```python= from sage.all import * p = 29488186972994534670417457155366645471182192199548659526413766939622361834146838276946251349867903845037032661551684336383382344530884181161185642157078150688387732468939373900371006578051481214072847442197503071055434360628659838236532690198978142021175339546919178335298684316724700262657029112644393293166154034921906788597061578283700388736429770020977264432345664045738339856555098074820929368834732846308848389109185416718414806791898185843424928326488893410923833562473301321779917349894586830655477919639150483605307802684111768011681546150738863663345823398017514538698657196101072867671246153807653084849979 e = 0x10001 hint = 15565989507023520599989453473844888345738293883767540044682200387116098778726727092639584680030282924680754836996824347666944190568219259972559913371376230295642182212369433729563762217727277805412925981228881460051783217872389120277083233191144941820776264906459152636385581608797345048727329767042394346810437890763454382771288788698625345149261169962034579205221557431869735207318556427897861682927352582426356093597283783347686188442927706115209934983043745580341869373867823516184740079142532925105289504694660421450970265051590438438057830106683050024773068164385422394299956145634093724572089312716467972923242 large_factor = 7460499821685332157113516060069886564400427229472624761209683142140995510658080396491764993046100641019523524398412618076429841989075435122250235698035949 FF = GF(p) g = FF(e) h = FF(hint) x = 1855512073301756587756398232044694453287540787670792602075522813991081601061737651961714765303931127584794086388195063345643498845590200784521163870740676836865034348236672261500637967777776528654389569453659092664362103325055772843213798878968673741544455797271025007597011355845701322512387115527794386532917304746518282690374593080519689235943954434619469423490386939887715663575148853233409899365866194541445855735427925029943387649934084213322940603441421762 delta = p // large_factor while True: x+=delta if g^x == h: print(x) break #x=39345789584158032512642763898941391995857388315308510231906758103602069400355492510675246179821522070796108546674823983368739227343617649698149723920009409085649862454006806441408416329515128156320837963767792617909364017501794254201478451853481782818746299820273100327214670628567515025254811562372652817330594388363763412836061592598885910564000993838864882287647394868600139277652449978683529171406785240992429298940751772312099536110532315909803441681024886529350 ``` Sau đó ta thay vào và giải ra flag: ```python= from Cryptodome.Util.number import * from Cryptodome.Cipher import AES import hashlib x=39345789584158032512642763898941391995857388315308510231906758103602069400355492510675246179821522070796108546674823983368739227343617649698149723920009409085649862454006806441408416329515128156320837963767792617909364017501794254201478451853481782818746299820273100327214670628567515025254811562372652817330594388363763412836061592598885910564000993838864882287647394868600139277652449978683529171406785240992429298940751772312099536110532315909803441681024886529350 ciphertext = b'\x15\xa5(l\xa3\xaar\xcd\xf0cN\ti\x12\xb8\x9fB\x82\xd3N\x86\x05\xa1\xb1\x82\xc4\xe1\x17vf\x0e,' key=hashlib.sha512(long_to_bytes(x)).digest()[:16] cipher=AES.new(key,AES.MODE_ECB) flag=cipher.decrypt(ciphertext) print(flag.decode()) ``` Flag là : ```W1{1t_1s_sm00th_bu7_n07_3n0ugh!}```