WannaGame NMLT

Đây là Write-up giành cho cuộc thi CTFs để lấy điểm Nhập môn lập trình của mình vào năm nhất.

Pwn

1. Hello pwner

image

Flag: W1{welcome_to_assembly}

Crypto

2. Substitution

Tải file, giải nén và mở chall lên:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
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)

Phân tích:

1
2
def hehe(data, key):
    return ''.join(key.get(char, char) for char in data)

Hàm hehe lấy data và chuyển đổi từng ký tự trong dữ liệu đó sang ký tự tương ứng bằng danh sách của KEY.

1
2
3
def encrypt(plaintext):
    substituted = hehe(plaintext, KEY)
    return substituted

Hàm encrypt có mục đich là sử dụng hàm hehe để mã hóa dữ liệu.  

Giải:

Để lấy được plaintext thì ta cần giải mã encrypted, nghĩa là phải đảo ngược lại quá trình chuyển đổi ký tự trong danh sách của KEY

1
KEY_rev = {i : v for v, i in KEY.items()}

Rồi sử dụng hàm hehe chuyển đổi tiếp 1 lần nữa để được data ban đầu.

1
2
def hehe(data, key):
    return ''.join(key.get(char, char) for char in data)

Script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
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',
}
KEY_rev = {i : v for v, i in KEY.items()}
def hehe(data, key):
  return "".join(key.get(char, char) for char in data)

def decrypt(plaintext):
  substituted = hehe(plaintext, KEY_rev)
  return substituted

if __name__ == "__main__":
  encrypted = "V1{lxwlzozxzogf}"
  decrypted = decrypt(encrypted)
  print(decrypted)
Flag: W1{substitution}

3. Hix

Tải file, giải nén và mở chall lên:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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()

Phân tích:

1
methods = ['md5', 'sha256', 'sha3_256', 'sha3_512', 'sha3_384', 'sha1', 'sha384', 'sha3_224', 'sha512', 'sha224']

Đây là các thuật toán được sử dụng cho hàm băm.

1
2
3
4
5
def random_encrypt(x) :
    method = random.choice(methods)
    hash_obj = hashlib.new(method)
    hash_obj.update(x.encode())
    return hash_obj.hexdigest()

Hàm random_encrypt dùng để mã hóa một chuỗi x bằng cách sử dụng một thuật toán băm bất kì có sẵn trong methods.

1
2
3
4
5
for char in message :
        x = (ord(char) + 20) % 130
        x = hashlib.sha512(str(x).encode()).hexdigest()
        x = random_encrypt(x)
        enc.append(x)

Lấy từng kí tự trong message, sau đó lấy giá trị của ký tự đó trong ASCII rồi cộng 20 và lấy modulo 130. Tiếp theo, chuyển nó về một chuỗi rồi băm bằng SHA-512. Đến cuối thì sử dụng hàm random_encrypt.  

Giải:

Ý tưởng: Vì x nằm trong đoạn từ 0 đến 129 (là một số nhỏ) nên ta có thể brute-force từng giá trị x khi thử với mỗi phương pháp băm để tìm ra các x tương ứng với từng giá trị của ct một cách dễ dàng.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def reverse_encrypt(enc_hash):
    for method in methods:
        try:
            for x in range(130):
                test_input = hashlib.sha512(str(x).encode()).hexdigest()
                hash_obj = hashlib.new(method)
                hash_obj.update(test_input.encode())
                if hash_obj.hexdigest() == enc_hash:
                    return x
        except:
            continue

Sau đó, với từng giá trị trong ct, ta dùng hàm reverse_encrypt và truy ngược lại giá trị x ban đầu bằng các phép toán và đổi sang dạng ASCII để giải mã nó bằng hàm sau:

1
2
3
4
5
6
7
def decrypt():
    decrypted_message = ""
    for ct_hash in ct:
        x = reverse_encrypt(ct_hash)
        original_ascii = (x - 20) % 130
        decrypted_message += chr(original_ascii)
    return decrypted_message

Script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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']
import hashlib

methods = ['md5', 'sha256', 'sha3_256', 'sha3_512', 'sha3_384', 'sha1', 'sha384', 'sha3_224', 'sha512', 'sha224']

def reverse_encrypt(enc_hash):
    for method in methods:
        try:
            for x in range(130):
                test_input = hashlib.sha512(str(x).encode()).hexdigest()
                hash_obj = hashlib.new(method)
                hash_obj.update(test_input.encode())
                if hash_obj.hexdigest() == enc_hash:
                    return x
        except:
            continue

def decrypt():
    decrypted_message = ""
    for ct_hash in ct:
        x = reverse_encrypt(ct_hash)
        original_ascii = (x - 20) % 130
        decrypted_message += chr(original_ascii)
    return decrypted_message

if __name__ == "__main__":
    flag = decrypt()
    print(flag)
Flag: W1{are_you_trying_to_predict_randomness@_@}

4. DH

Tải file, giải nén và mở chall lên:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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"

Phân tích:

Trong class DH, có:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
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)

Hàm gen_params tạo một bộ khóa Diffie-Hellman.

1
2
def get_params(self):
        return f"p = {self.p}\ng = {self.g}\nA = {self.A}\nB = {self.B}"

Khi lấy hàm get_params thì sẽ trả về các giá trị p, g, A, B.

1
2
3
4
5
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}"

Ngoài ra, còn có hàm encrypt lấy giá trị ss từ hàm gen_params để tạo key cho thuật toán mã hóa AES chế độ ECB, cuối cùng dùng nó để mã hóa FLAG.

Giải:

Để tìm ra FLAG thì ta cần lấy được key để giải mã cipher

1
2
3
key = sha256(long_to_bytes(ss)).digest()[:16]
cipher = AES.new(key, AES.MODE_ECB)
flag = cipher.decrypt(encrypted)

Do đó, ta phải tìm ra giá trị ss để tạo được key. Ta có các phương trình sau:

$A≡g^a$ (mod p); $B≡g^b$ $⇒ss≡A^b≡(g^a )^b≡g^{ab}≡(g^b )^a≡B^a$ (mod p)

Ta đã có p, g, A, vậy ta chỉ cần có thêm b là có thể tìm ra ss. Tới đây, tôi sử dụng thuật toán Pohlig-Hellman để tìm b qua công thức $B≡g^b$ (mod p) bằng công cụ Alpetron.

image

Set k = 0, lấy được b = exp và tính được ss. Sau đây là script cho thử thách:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
from Crypto.Util.number import long_to_bytes
from Crypto.Cipher import AES
from hashlib import sha256

p = 85013941328859365232686230728938372320812319905627686919070637645614632817039920673725615375841158719310596592903101914818137738460649589340349796188816568005092757847
g = 20033344683527080232439150682925185454003164954955126339094967675384779782733210350757021743656898625398860187361281262413493941502725149445995471514781822892886669776
A = 76548721461171533747911417838852759206858825205673491250696441734297318615226024320798706656529038703728631231084155790148283919370554345818139818854112841655270107839
B = 2103083080159597422706551446020625757109756570951674830166998494220734179439318911618156966499109201221652320384817270671579741987575328177442670242481963924501204498
encrypted = "240e7b7678aaaa0dcbe06de7c5598a1ca0be7e2ae584bc7dfd2388cdb1d4fb6a37ceb94556757afc293999cbe5a5a2dbb4071ebf6cfd4332088555f9b2de1922"
encrypted = bytes.fromhex(encrypted)
b = 2891401095038
ss = pow(A, b, p)

key = sha256(long_to_bytes(ss)).digest()[:16]
cipher = AES.new(key, AES.MODE_ECB)
flag = cipher.decrypt(encrypted)
print(flag.decode())
Flag: W1{so_you_know_about_the_Diffie-Hellman-key_exchange}

Reverse

5. GiacMoTrua2

Tải file về và phiên dịch mã giả bằng IDA, ta có:

image

Chương trình yêu cầu ta nhập một chuỗi bí mật và đem chuỗi đó so sánh với flag sau khi qua hàm Lookthis được mã hóa thành: “W1{live_speels_a5_NoOn_4v4ry_emit!}”

image image

Hàm swap dùng để đổi hai kí tự cho nhau. Hàm Lookthis có 3 vòng lặp riêng biệt trên từng khoảng với nhiệm vụ đổi ký tự tương ứng. Như vậy, ta chỉ cần viết script đảo ngược lại và đổi ký tự thì sẽ cho ra được flag ban đầu:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
def reverse_lookthis(flag):

   for k in range(32, 28, -1):
       if k < (61 - k):
           flag[k], flag[61 - k] = flag[61 - k], flag[k]

   for j in range(13, 7, -1):
       if j < (21 - j):
           flag[j], flag[21 - j] = flag[21 - j], flag[j]

   for i in range(6, 2, -1):
       if i < (9 - i):
           flag[i], flag[9 - i] = flag[9 - i], flag[i]

   return flag


fake_flag = [ord(c) for c in "W1{live_speels_a5_NoOn_4v4ry_emit!}"]
real_flag = reverse_lookthis(fake_flag)

flag = "".join(chr(c) for c in real_flag)
print(flag)
Flag: W1{evil_sleeps_a5_NoOn_4v4ry_time!}

6. Easy Flag Checker

Tải file và phiên dịch mã giả bằng IDA, có:

image

Qua hàm main, chương trình yêu cầu ta nhập một chuỗi 20 ký tự, sau đó thực hiện vòng lặp kiểm tra với mỗi ký tự thứ 4*i+32 XOR với 0x38. Sau đó lấy ký tự đó so sánh ngay với dword_4020 thứ i. Chúng ta sẽ truy cập thử vào dword_4020:

image

Ở đây, các giá trị trong unk_4020 là các byte đã được mã hóa. Do đó, ta sẽ lấy các giá trị khác 0 để thực hiện phép XOR và nối lại tạo thành flag.

Sau đây là script cho thử thách:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
def reverse_lookthis(flag):

   for k in range(32, 28, -1):
       if k < (61 - k):
           flag[k], flag[61 - k] = flag[61 - k], flag[k]

   for j in range(13, 7, -1):
       if j < (21 - j):
           flag[j], flag[21 - j] = flag[21 - j], flag[j]

   for i in range(6, 2, -1):
       if i < (9 - i):
           flag[i], flag[9 - i] = flag[9 - i], flag[i]

   return flag


fake_flag = [ord(c) for c in "W1{live_speels_a5_NoOn_4v4ry_emit!}"]
real_flag = reverse_lookthis(fake_flag)

flag = "".join(chr(c) for c in real_flag)
print(flag)
Flag: W1{v3ry_34sy_r1gh7?}

7. GiacMoTrua1

Ta thấy tệp thử thách có định dạng .pyc. Sau khi thử tìm kiếm thì tôi biết được đây là một tệp đầu ra được biên dịch được tạo từ mã nguồn được viết bằng ngôn ngữ lập trình Python. Do đó tôi sử dụng PyLingual – một công cụ online để dịch ngược và khôi phục mã nguồn Python:

image

Tải file python này về, ta nhận được thử thách như sau:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
dic = [0] * 85
dic[0] = 33
dic[1] = 35
dic[2] = 36
dic[3] = 37
dic[4] = 38
dic[5] = 40
dic[6] = 41
dic[7] = 42
dic[8] = 43
dic[9] = 44
dic[10] = 45
dic[11] = 46
dic[12] = 47
dic[13] = 48
dic[14] = 49
dic[15] = 50
dic[16] = 51
dic[17] = 52
dic[18] = 53
dic[19] = 54
dic[20] = 55
dic[21] = 56
dic[22] = 57
dic[23] = 58
dic[24] = 59
dic[25] = 60
dic[26] = 61
dic[27] = 62
dic[28] = 63
dic[29] = 64
dic[30] = 65
dic[31] = 66
dic[32] = 67
dic[33] = 68
dic[34] = 69
dic[35] = 70
dic[36] = 71
dic[37] = 72
dic[38] = 73
dic[39] = 74
dic[40] = 75
dic[41] = 76
dic[42] = 77
dic[43] = 78
dic[44] = 79
dic[45] = 80
dic[46] = 81
dic[47] = 82
dic[48] = 83
dic[49] = 84
dic[50] = 85
dic[51] = 86
dic[52] = 87
dic[53] = 88
dic[54] = 89
dic[55] = 90
dic[56] = 91
dic[57] = 97
dic[58] = 98
dic[59] = 99
dic[60] = 100
dic[61] = 101
dic[62] = 102
dic[63] = 103
dic[64] = 104
dic[65] = 105
dic[66] = 106
dic[67] = 107
dic[68] = 108
dic[69] = 109
dic[70] = 110
dic[71] = 111
dic[72] = 112
dic[73] = 113
dic[74] = 114
dic[75] = 115
dic[76] = 116
dic[77] = 117
dic[78] = 118
dic[79] = 119
dic[80] = 120
dic[81] = 121
dic[82] = 122
dic[83] = 123
dic[84] = 125
flag = input("Let me help you check your flag: ")
length = len(flag)
ans = [0] * length * 2
for i in range(length):
   ans[i] = dic[ord(flag[i]) ^ 112]
for i in range(length, length * 2):
   ans[i] = ans[i - length]
fin = ""
for i in range((23 * length + 16) % length, (23 * length + 16) % length + length):
   fin += chr(ans[i])
if fin == "R8Abq,R&;j%R6;kiiR%hR@k6iy0Ji.[k!8R,kHR*i??":
   print("Rightttt!")
   print("Heyy you are really lovely, i promise!")
else:
   print("Think more....")

Phân tích:

Flag được nhập vào và qua một số bước biến đổi trở thành một đoạn mã hóa cho trước là: “R8Abq,R&;j%R6;kiiR%hR@k6iy0Ji.[k!8R,kHR*i??” Đầu tiên, chúng ta lấy từng giá trị của flag dạng thập phân ở hệ Unicode để XOR với 112 rồi lấy đó làm chỉ số để tra bảng dic và gán vào dãy ans, tiếp theo lấy ans thêm vào sau để tạo thành 1 dãy có độ dài gấp đôi flag.

1
2
3
4
for i in range(length):
   ans[i] = dic[ord(flag[i]) ^ 112]
for i in range(length, length * 2):
   ans[i] = ans[i - length]

Sau đó lập thành dãy fin mà ta đã biết trước bằng cách lấy i trong khoảng từ (23*length+16) % length tới (23*length+16) % length + length và lấy đó làm chỉ số với ans để chuyển nó về dạng Unicode và thêm vào fin.

1
2
3
fin = ""
for i in range((23 * length + 16) % length, (23 * length + 16) % length + length):
    fin += chr(ans[i])

Giải:

Nhận thấy, fin có độ dài bằng length và lớn hơn 16 nên: $23×length+16≡16$ (mod length)

Do đó, ans sẽ được tính từ chỉ số 16 của fin tăng dần và quay ngược lên 15. Sau đó, ta phải đảo ngược bảng giá trị dic cùng với việc thực hiện phép XOR với 112 để truy ngược lại giá trị ban đầu của flag.

Đây là script cho thử thách:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
dic = [0] * 85
dic[0] = 33
dic[1] = 35
dic[2] = 36
dic[3] = 37
dic[4] = 38
dic[5] = 40
dic[6] = 41
dic[7] = 42
dic[8] = 43
dic[9] = 44
dic[10] = 45
dic[11] = 46
dic[12] = 47
dic[13] = 48
dic[14] = 49
dic[15] = 50
dic[16] = 51
dic[17] = 52
dic[18] = 53
dic[19] = 54
dic[20] = 55
dic[21] = 56
dic[22] = 57
dic[23] = 58
dic[24] = 59
dic[25] = 60
dic[26] = 61
dic[27] = 62
dic[28] = 63
dic[29] = 64
dic[30] = 65
dic[31] = 66
dic[32] = 67
dic[33] = 68
dic[34] = 69
dic[35] = 70
dic[36] = 71
dic[37] = 72
dic[38] = 73
dic[39] = 74
dic[40] = 75
dic[41] = 76
dic[42] = 77
dic[43] = 78
dic[44] = 79
dic[45] = 80
dic[46] = 81
dic[47] = 82
dic[48] = 83
dic[49] = 84
dic[50] = 85
dic[51] = 86
dic[52] = 87
dic[53] = 88
dic[54] = 89
dic[55] = 90
dic[56] = 91
dic[57] = 97
dic[58] = 98
dic[59] = 99
dic[60] = 100
dic[61] = 101
dic[62] = 102
dic[63] = 103
dic[64] = 104
dic[65] = 105
dic[66] = 106
dic[67] = 107
dic[68] = 108
dic[69] = 109
dic[70] = 110
dic[71] = 111
dic[72] = 112
dic[73] = 113
dic[74] = 114
dic[75] = 115
dic[76] = 116
dic[77] = 117
dic[78] = 118
dic[79] = 119
dic[80] = 120
dic[81] = 121
dic[82] = 122
dic[83] = 123
dic[84] = 125
fin = "R8Abq,R&;j%R6;kiiR%hR@k6iy0Ji.[k!8R,kHR*i??"

length = len(fin)

ans = [ord(fin[i - 16]) for i in range(length)]

reverse_dic = {v: i for i, v in enumerate(dic)}
flag = "".join(chr(reverse_dic[ans[i]] ^ 112) for i in range(length))

print(flag)
Flag: W1{H3pe_y3U_w1ll_enJ9y_th2s_ch311_s0_m3c1!}
comments powered by Disqus