
Chronomancer
- Mình tiến hành mở IDA để xem code của bài này.

- Xem qua hàm
mainthì không có gì đặc biệt nên mình mở thử strings xem có gì đặc biệt không.

- Khá bất ngờ vì nó có một phần của flag ở đây.
- Mình mở nó để xem nơi nó được gọi.

- Sau khi phân tích và dọn lại code thì được như hình bên trên.
- Ở đây hàm này sẽ ghép các string lại với nhau, cuối flag sẽ là một số (v15) được trả về từ hàm
sub_140001510. - Từ đây ta sẽ có cách để giải được flag của bài này là tìm ra
v15là gì.- Mình có viết lại 1 script python mô phỏng lại hàm
sub_140001510để lấy giá trị của v15 và ghép vào mảnh flag đã có trước đó.
v2 = -16657for i in range(100):v2 = 26125 * v2 - 3233flag = "PTITCTF{t1m3_is_n0t_l1n3ar_" + str(v2 & 0xFFFF) + "}"print(flag) - Mình có viết lại 1 script python mô phỏng lại hàm
- Sau khi giải xong thì ta sẽ có được flag cho bài này.
Flag
PTITCTF{t1m3_is_n0t_l1n3ar_15347}R(everse)TIT
- Mình tiến hành mở IDA để dịch code của bài này.

- Trước tiên ở trong hàm main ta sẽ thấy được một đoạn để check flag ở đây.
- Trước mắt ta có thể biết được độ dài của flag là 48.
- Ta tiến hành debug để có thể xem được flag đã được check như nào.

- Ở đây ta thấy được nó đang check với 1 đoạn mã hóa nào đó.
- Tại sao lại có nó ở đây, ta cần phải check lại hàm
__security_check_xem liệu nó đang làm gì với input của ta. - Sau khi phân tích qua hàm
__security_check_thì ta biết được nó đang sử dụng SHA-1 để mã hóa input ta nhập vào và rồi sẽ so sánh với các đoạn mã hóa có sẵn của bài. - Trước mắt thì ta sẽ thấy chương trình đang lấy ra 7 ký tự đầu tiên trong flag sẽ đó qua 1 hàm hash SHA-1 và rồi so sánh với chuỗi
edadb1236fb6dfc71c58c701fc92607a584add9f.

- Vì ở phần ngay dưới mình thấy chương trình check index 7 của flag với
{nên mình nghĩ 7 ký tự đầu tiên sẽ làPTITCTF. - Mình tiến hành tạo 1 hàm SHA-1 bằng python và check xem hash của PTITCTF có đúng là
edadb1236fb6dfc71c58c701fc92607a584add9fkhông.
import hashlibdef sha1(text): return hashlib.sha1(text.encode()).hexdigest()
text = "PTITCTF"print(sha1(text) == "edadb1236fb6dfc71c58c701fc92607a584add9f")
# True- Vậy thì ta đã biết được phần nào chương trình đang làm gì rồi, ta sẽ tiếp tục phân tích các phần ở bên dưới.

- Ta thấy chương trình đang lấy từ từ từng 3 ký tự một trong flag ra để so sánh với các string SHA-1 có sẵn của chương trình.
- Ta sẽ giải từ từ từng 3 ký tự một của bài này, vì ở đây chỉ đang lấy 3 ký tự và hash nên ta có thể bruteforce nó một cách khá nhanh.
- Mình sẽ viết 1 đoạn script python để bruteforce từng 3 ký tự một cho flag của bài này (ở script này mình đã tổng hợp lại hết các mã hash và giải ra flag luôn).
import hashlibimport stringfrom itertools import product
def sha1(text): return hashlib.sha1(text.encode()).hexdigest()
hash_list = [ "0a4d82caaf74346c40ca0ba69f89369724ca9035", "ea9259b4a6f43783cdf6aa523e9db3412963b31c", "156cc4c77e3bf1dcb387b189b56309dde6ef6220", "b5efa8eadf6f33f30d19468ebdf52d155526d2de", "987e2a2582fa4ddd068259c1ae224615e5062157", "6512cf6bb1044e3dc68030a8d6d669257dafa9bc", "44e5a6f66307595eae078a70dd059662aadfc43b", "7925a6c0d9b673bbdef61b8f4c45fe3e14ba9e03", "a7a3472af6383407428a85ed8062165fb2ca772f", "6fa550f5c70df6f3b67e3483ba841a2aa2a0a89e", "2ae7b876a4073754b71cc8501a3fc2b255c154ce", "542ec1a1f6d635ffa543cc877622854254d8e25f", "c1d5e47df628b6ca0f1f832e6dd29897e2b0b9fe"]
flag = "PTITCTF{"char = string.printablefor i in hash_list: for chars in product(char, repeat=3): hashed = sha1("".join(chars)) if hashed == i: flag += "".join(chars) break
flag = flag.ljust(47, "?") + "}"print(flag)- Vì là bruteforce nên sẽ mất 1 chút thời gian để chạy code nhưng cũng khá nhanh và ta đã có được flag cho bài này.
Flag
PTITCTF{7h47_15_7h3_W4Y_W3_50lv3_7h15_ch4ll3n93}AntiChatGPT
- Ở bài này mình sẽ dùng Ghidra để xong code của chương trình.


- Trong hàm
FUN_00401020khá là rối, sau khi phân tích nhanh qua thì mình nhận thấy hàmFUN_00402240chính là hàm main của chương trình này.

- Hàm
FUN_00402240cũng có khá nhiều các hàm rác để làm rối.

- Sau khi tìm một lúc thì mình đã thấy được hàm
FUN_00402ff0chính là hàm xử lý logic chính của bài này.

- Trước hết, hàm
FUN_00402ff0đang chuẩn bị các dữ liệu ban đầu cho mảnglocal_f8. - Hàm
FUN_00403470thực chất là đang xor từng ký tự với0x5A. - Mình tiến hành viết một đoạn script python để xem liệu sau khi xử lý thì
local_f8sẽ là gì.local_f8 = [0x296b320e,0x05296b05,0x690c051b,0x09052328,0x69283969,0x6911052e,0x6a1c0523,0x0e190528]raw = b''.join(x.to_bytes(4, 'little') for x in local_f8)raw += (0x7b1c).to_bytes(2, 'little')decoded = bytes([b ^ 0x5A for b in raw])print(decoded.decode('utf-8', errors='replace'))# Th1s_1s_A_V3ry_S3cr3t_K3y_F0r_CTF!

-
Tiếp đến, hàm
FUN_00402ff0chuẩn bị mảnglocal_88. -
Mảng
local_88được đi qua hàmFUN_00403440xor với0x5Avà được in ra. -
Đây là đoạn code python tường minh hơn của đoạn xử lý
local_88này.local_88 = [31, 52, 46, 63, 40, 122, 46, 50,63, 122, 51, 52, 57, 59, 52, 46,59, 46, 51, 53, 52, 122, 114, 60,54, 59, 61, 115, 96, 122]print(''.join(chr(i ^ 0x5A) for i in local_88))# Enter the incantation (flag): -
Tiếp đến hàm sẽ xử lý dữ liệu nhập vào.

- Ở đoạn này chương trình kiểm tra đọc dữ liệu thất bại không.
- Nếu đúng thì in ra một đoạn text.
local_118 = [0x36333b1c, 0x2e7a3e3f, 0x3b3f287a35,0x337a3e, 0x2e343b3934, 0x507b3435332e3b]for val in local_118:for b in val.to_bytes(len(hex(val)[2:]) // 2, 'little'):print(chr(b ^ 0x5A), end="")# Failed to read incantation!

- Nếu đã đọc được dữ liệu nhập vào thì sẽ xử lý ở phần này.
- Đổi ký tự
\nở cuối thành\0, đảm bảo độ dài là bội của 8 và không vượt quá 0x40.

- Và đây là đoạn xử lý cuối cùng.
- Đưa input qua hàm
FUN_00401e20để xử lý, sau đó so sánh với mảngDAT_00412330có sẵn. - Nếu đúng thì in ra
INCREDIBLE!hoặc không thì in raTHE CURSE HOLDS STRONG. - Mình tiến hành viết một đoạn script mô phỏng lại các hàm và mảng của chương trình để từ đó sẽ giải ngược ra lại flag.
DAT_004120f0 = [ 0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76, 0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0, 0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15, 0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75, 0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84, 0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF, 0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8, 0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2, 0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73, 0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB, 0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79, 0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08, 0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A, 0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E, 0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF, 0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16]DAT_00412330 = [ 0x40, 0x32, 0xC4, 0xDA, 0x67, 0xA9, 0x1C, 0x97, 0x69, 0xA1, 0xD8, 0xBE, 0x1F, 0xEE, 0xE9, 0xA1, 0xF5, 0x28, 0x54, 0x09, 0x55, 0x5D, 0xC5, 0x7D, 0xCD, 0x26, 0x6B, 0x36, 0x22, 0x15, 0x0C, 0xE2, 0x5E, 0x5E, 0xBE, 0xA5, 0xFF, 0x4A, 0x24, 0x34, 0x05, 0xF5, 0x7D, 0xDD, 0xBA, 0x9F, 0x62, 0xEB]
def FUN_00401d10(key): p = [int.from_bytes(key[i * 4:(i + 1) * 4], "little") for i in range(4)] out = [0] * 8
u5 = p[3] u4 = ((u5 << 11) | (u5 >> 21)) ^ p[0] u1 = p[1] u2 = p[2] ^ ((u1 + u4) & 0xFFFFFFFF) u5 = ((u2 ^ 99) + u5) & 0xFFFFFFFF out[0] = u4 & 0xFFFFFFFF
u3 = (((u5 * 0x800) & 0xFFFFFFFF) | (u5 >> 21)) ^ u4 u1 = (u1 + u4 + u3) & 0xFFFFFFFF u2 = (u2 ^ u1) & 0xFFFFFFFF u5 = ((u2 ^ 0x1F) + u5) & 0xFFFFFFFF out[1] = u1
u3 = (((u5 * 0x800) & 0xFFFFFFFF) | (u5 >> 21)) ^ u3 u1 = (u1 + u3) & 0xFFFFFFFF u2 = (u2 ^ u1) & 0xFFFFFFFF u4 = (u2 ^ 0x68) & 0xFFFFFFFF u5 = (u5 + u4) & 0xFFFFFFFF out[2] = u4
u3 = (((u5 * 0x800) & 0xFFFFFFFF) | (u5 >> 21)) ^ u3 u1 = (u1 + u3) & 0xFFFFFFFF u2 = (u2 ^ u1) & 0xFFFFFFFF u5 = ((u2 ^ 0x13) + u5) & 0xFFFFFFFF out[3] = u5
u3 = (((u5 * 0x800) & 0xFFFFFFFF) | (u5 >> 21)) ^ u3 u1 = (u1 + u3) & 0xFFFFFFFF u2 = (u2 ^ u1) & 0xFFFFFFFF u5 = ((u2 ^ 0xE1) + u5) & 0xFFFFFFFF out[4] = u3 & 0xFFFFFFFF
u3 = (((u5 * 0x800) & 0xFFFFFFFF) | (u5 >> 21)) ^ u3 u1 = (u1 + u3) & 0xFFFFFFFF u2 = (u2 ^ u1) & 0xFFFFFFFF u5 = ((u2 ^ 0x8A) + u5) & 0xFFFFFFFF out[5] = u1
u3 = (((u5 * 0x800) & 0xFFFFFFFF) | (u5 >> 21)) ^ u3 u1 = (u1 + u3) & 0xFFFFFFFF u2 = (u2 ^ u1) & 0xFFFFFFFF u4 = (u2 ^ 0xE5) & 0xFFFFFFFF u5 = (u5 + u4) & 0xFFFFFFFF out[6] = u4
out[7] = ((((u5 * 0x800) & 0xFFFFFFFF) | (u5 >> 21)) ^ u3) out[7] = ((out[7] + u1) ^ u2 ^ 0x20) + u5 out[7] &= 0xFFFFFFFF
return out
def rol32(x, r): x &= 0xFFFFFFFF return ((x << r) | (x >> (32 - r))) & 0xFFFFFFFF
def FUN_00401cb0(x, k): t = x ^ k b = [0] * 4 for i in range(4): b[i] = DAT_004120f0[(t >> (i * 8)) & 0xFF] u = b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24) return (rol32(u, 13) + 0x9E3779B9) & 0xFFFFFFFF
def FUN_00401e20(block8, subkeys): L = int.from_bytes(block8[:4], 'little') R = int.from_bytes(block8[4:], 'little') R ^= FUN_00401cb0(L, subkeys[7]) for i in range(6, -1, -1): L, R = R, L ^ FUN_00401cb0(R, subkeys[i]) return (L.to_bytes(4, 'little') + R.to_bytes(4, 'little')).decode('utf-8')
key = b"Th1s_1s_A_V3ry_S3cr3t_K3y_F0r_CTF!"subkeys = FUN_00401d10(key)flag = ""for i in range(0, len(DAT_00412330), 8): flag += FUN_00401e20(DAT_00412330[i:i + 8], subkeys)print(flag)- Và sau khi giải xong ta đã có được flag cho bài này.
Flag
PTITCTF{k1ng_0f_Pt1t_NigM4o_z3ro_d4Y_zxo}Baby VM
- Mình tiến hành mở file bằng IDA để xem chương trình hoạt động như nào.

- Nghiên cứu code một hồi thì mình thấy chương trình này đang chuẩn bị môi trường cho python, vậy thì đây đích thị là file được build từ python qua pyinstaller rồi.
- Mình sử dụnng
pyinstxtractor-ngđể có thể chuyển từ file elf này sang file pyc.

- Vậy là mình đã thành công có được file pyc của bài này.
- Tiếp theo mình sẽ dùng
pylingualđể dịch từ file pyc này sang code python.

- Thế là ta đã có được code python của bài này.
- Cùng phân tích đoạn code này.
- Dưới đây là đoạn code mình đã clean lại.
import hashlibreg1 = 0reg2 = 0ip = 0stack = []
# Key validationkey = input("Do you know what this chall's about?\n> ").encode()hash_key = hashlib.sha256(key).hexdigest()if hash_key != 'da985b099592755e04c6be1188e42c8a2c1d7a7e1783076a8127b79933cee55f': print('Wrong!') exit()
# Decrypt datafor i in range(len(data)): data[i] ^= key[i % len(key)]
# Flag validationflag = input('Good! Now give me the flag\n> ')if len(flag) != 53: print('Wrong!') exit()
# Push flag characters to stackfor char in flag: stack.append(ord(char))
# Process instructionscheck = Truewhile ip < len(data): match data[ip]: case 161: val = data[ip + 1] stack.append(val) ip += 2 case 162: stack.append(reg1) ip += 1 case 163: stack.append(reg2) ip += 1 case 177: reg1 = stack.pop() ip += 1 case 178: reg2 = stack.pop() ip += 1 case 81: reg1 += stack.pop() ip += 1 case 82: reg2 += stack.pop() ip += 1 case 97: reg1 -= stack.pop() ip += 1 case 98: reg2 -= stack.pop() ip += 1 case 113: reg1 ^= stack.pop() ip += 1 case 114: reg2 ^= stack.pop() ip += 1 case 129: reg1 *= stack.pop() ip += 1 case 130: reg2 *= stack.pop() ip += 1 case 144: if reg1 != reg2: check = False ip += 1 case 105: ip += 1 case _: ip += 1
# Output resultif check: print('Correct!')else: print('Wrong!')- Đầu tiên thì chương trình dùng
sha256để hash input nhập vào và so sánh nó với đoạnda985b099592755e04c6be1188e42c8a2c1d7a7e1783076a8127b79933cee55f. - Mình sử dụng Sha256 Decrypt trên mạng để xem liệu đoạn hash này có sẵn hay không.

- Và ở đây mình đã có key đầu tiên của bài này.
- Vấn đề ở đây là pylingual không hề có đoạn
datanào ở trong code cả. - Tại sao lại vậy, mình nghi ngờ có gì đó còn ở trong phần bytecode python, mình quay lại pylingual để check kỹ lại.

- Đúng như mình nghi ngờ thì ở đây có một list các số nhưng pylingual không hề dịch sang code python, mình lấy nó vào thêm vào code python của mình.
- Dưới đây sẽ là đoạn code python để giải flag của mình.
- Trước hết ta cần hiểu cách hoạt động của VM như nào.
- Với các case
[81, 82, 97, 98, 113, 114, 129, 130]sẽ là các phép tính bình thường và ip sẽ +1. - Với các case
[162, 163, 177, 178, 105]sẽ là các push và pop, ip cũng sẽ +1. - Với case
161thì cũng là push nhưng nó không push thanh ghi và sẽ push giá trị đi theo ngay sau, cùng với đó ip +2. - Và cuối cùng là case
144, nó sẽ kết thúc 1 lần tính toán và return giá trị của reg1 và reg2, nếu 2 thanh ghi khác nhau thì flag sẽ là sai.
- Với các case
- Tức là ở đây, mỗi lần chạy vào case
144sẽ check xem ký tự ở đấy có thỏa mãn với đề bài hay không. - Theo mạch logic đó, mình tiến hành viết script python mô phỏng lại các case của VM và cùng với đó là bruteforce các ký tự của flag.
key = b"virtualmachine"data = [ 31, 0, 195, 29, 212, 142, 29, 204, 83, 10, 57, 200, 75, 12, 215, 196, 192, 29, 23, 192, 80, 63, 8, 243, 1, 0, 223, 12, 215, 108, 35, 29, 212, 99, 5, 236, 8, 194, 103, 0, 207, 111, 196, 0, 240, 29, 212, 115, 14, 253, 8, 210, 201, 107, 7, 228, 215, 52, 3, 29, 212, 77, 205, 210, 8, 209, 58, 200, 86, 7, 230, 0, 27, 197, 212, 99, 237, 4, 192, 120, 1, 56, 207, 237, 31, 200, 232, 198, 7, 192, 181, 63, 8, 243, 1, 216, 207, 120, 39, 0, 211, 118, 244, 192, 98, 204, 243, 209, 10, 200, 122, 55, 31, 249, 27, 197, 212, 110, 13, 204, 48, 18, 1, 200, 74, 196, 113, 219, 32, 29, 212, 94, 5, 31, 8, 243, 1, 216, 207, 33, 31, 24, 27, 213, 119, 224, 205, 113, 8, 194, 91, 0, 220, 12, 36, 200, 101, 29, 23, 8, 252, 4, 208, 194, 109, 56, 7, 196, 116, 232, 27, 213, 119, 192, 232, 223, 8, 1, 1, 200, 26, 23, 230, 0, 27, 29, 196, 192, 203, 28, 192, 14, 9, 0, 207, 97, 215, 109, 27, 198, 247, 192, 237, 63, 241, 10, 1, 0, 223, 12, 215, 100, 27, 21, 212, 67, 5, 60, 8, 194, 44, 0, 207, 214, 196, 0, 16, 213, 152, 19, 5, 253, 8, 10, 217, 0, 207, 17, 31, 24, 211, 113, 244, 192, 97, 204, 111, 10, 218, 11, 7, 196, 114, 59, 226, 29, 28, 208, 205, 26, 48, 10, 201, 111, 15, 12, 215, 98, 211, 112, 199, 8, 238, 204, 205, 10, 58, 0, 254, 12, 31, 0, 195, 29, 212, 27, 5, 60, 8, 194, 18, 0, 31, 196, 50, 200, 65, 29, 199, 8, 62, 4, 192, 209, 26, 0, 254, 212, 31, 200, 108, 29, 36, 8, 205, 111, 224, 10, 201, 158, 7, 196, 158, 0, 192, 29, 7, 8, 205, 156, 8, 17, 248, 0, 7, 212, 31, 200, 112, 245, 28, 192, 189, 12, 192, 61, 1, 200, 48, 12, 196, 0, 0, 213, 108, 19, 5, 253, 8, 10, 1, 216, 207, 19, 39, 0, 211, 76, 28, 0, 205, 63, 192, 3, 218, 0, 60, 12, 215, 123, 27, 6, 28, 241, 221, 204, 114, 10, 25, 200, 108, 12, 247, 200, 112, 29, 212, 110, 222, 4, 227, 10, 201, 167, 7, 55, 230, 0, 195, 213, 119, 8, 237, 204, 48, 2, 1, 200, 97, 196, 102, 0, 192, 29, 247, 192, 161, 31, 8, 243, 1, 216, 207, 12, 39, 200, 10, 29, 20, 192, 97, 204, 49, 209, 1, 59, 7, 196, 98, 11, 226, 29, 28, 208, 205, 110, 8, 226, 201, 118, 7, 20, 215, 95, 27, 213, 31, 211, 30, 204, 35, 49, 1, 249, 223, 196, 116, 232, 27, 213, 82, 8, 29, 204, 42, 10, 201, 194, 220, 12, 20, 200, 211, 6, 229, 8, 5, 220, 192, 67, 1, 56, 207, 62, 23, 200, 5, 213, 10, 8, 222, 4, 19, 10, 201, 106, 7, 231, 230, 0, 195, 213, 96, 8, 29, 204, 99, 10, 233, 0, 207, 224, 215, 219, 27, 198, 28, 3, 5, 204, 242, 17, 248, 0, 7, 212, 31, 200, 127, 21, 212, 253, 5, 28, 8, 194, 109, 200, 73, 12, 196, 235, 27, 213, 125, 8, 14, 253, 8, 210, 201, 30, 63, 196, 137, 0, 3, 29, 212, 105, 5, 204, 105, 209, 1, 235, 207, 63, 31, 27, 27, 228, 196, 8, 205, 93, 48, 10, 201, 237, 15, 12, 215, 93, 211, 72, 199, 8, 14, 4, 192, 118, 58, 249, 7, 212, 31, 200, 232, 29, 36, 192, 216, 12, 8, 194, 109, 0, 207, 75, 31, 219, 27, 246, 212, 157, 5, 31, 8, 243, 1, 0, 7, 212, 31, 200, 37, 37, 212, 26, 5, 28, 8, 194, 67, 200, 240, 215, 4, 0, 211, 125, 7, 8, 252, 4, 208, 194, 106, 0, 239, 196, 112, 0, 19, 29, 212, 107, 5, 204, 99, 10, 218, 0, 236, 12, 215, 199, 32, 29, 229, 208, 5, 204, 108, 10, 57, 200, 108, 228, 31, 200, 118, 213, 124, 8, 222, 239, 192, 61, 1, 27, 7, 245, 31, 0, 27, 197, 28, 192, 42, 4, 0, 194, 106, 0, 239, 12, 215, 100, 27, 213, 213, 211, 5, 63, 8, 194, 231, 0, 12, 12, 230, 216, 211, 118, 244, 8, 205, 107, 8, 50, 1, 200, 54, 196, 247, 0, 192, 29, 39, 192, 13, 4, 3, 10, 248, 0, 7, 212, 31, 200, 107, 21, 28, 192, 110, 4, 224, 10, 201, 210, 7, 196, 149, 219, 0, 29, 212, 99, 5, 31, 241, 10, 217, 0, 207, 206, 7, 200, 113, 37, 212, 104, 5, 204, 98, 10, 218, 235, 7, 196, 243, 0, 32, 228, 28, 208, 205, 17, 8, 50, 1, 200, 204, 4, 215, 101, 27, 213, 102, 211, 5, 239, 8, 194, 208, 0, 12, 245, 31, 0, 195, 29, 212, 79, 5, 12, 192, 97, 233, 200, 97, 12, 215, 102, 27, 198, 247, 192, 247, 4, 3, 243, 1, 216, 7, 196, 116, 232, 211, 27, 28, 16, 5, 204, 92, 194, 211, 0, 220, 12, 4, 0, 211, 51, 7, 241, 5, 4, 8, 210, 1, 200, 33, 12, 7, 200, 112, 29, 244, 192, 100, 204, 107, 10, 218, 235, 7, 196, 78, 11, 226, 29, 28, 208, 5, 204, 99, 10, 233, 0, 207, 96, 39, 0, 211, 39, 212, 14, 222, 15, 8, 194, 253, 0, 60, 245, 31, 216, 211, 118, 28, 224, 5, 204, 43, 50, 1, 200, 187, 12, 215, 138, 27, 198, 28, 19, 5, 204, 101, 10, 234, 249, 7, 12, 31, 216, 211, 78, 28, 48, 5, 204, 162, 18, 201, 76, 207, 37, 196, 0, 32, 29, 212, 99, 14, 253, 8, 210, 1, 200, 96, 12, 39, 200, 112, 29, 244, 8, 205, 75, 8, 194, 88, 219, 7, 23, 31, 200, 121, 29, 247, 241, 5, 4, 208, 10, 201, 51, 31, 196, 114, 0, 243, 213, 23, 192, 24, 223, 19, 10, 201, 97, 236, 245, 31, 216, 211, 190, 4, 192, 40, 12, 8, 194, 53, 200, 33, 215, 36, 200, 36, 22, 229, 8, 221, 204, 99, 226, 1, 200, 105, 12, 23, 200, 226, 213, 191, 211, 5, 31, 8, 194, 33, 59, 254, 12, 31, 216, 211, 100, 20, 8, 205, 111, 8, 226, 1, 200, 92, 12, 215, 131, 192, 22, 28, 192, 114, 4, 51, 10, 248, 0, 223, 196, 73, 0, 19, 213, 62, 8, 61, 4, 192, 86, 201, 48, 220, 12, 20, 0, 211, 90, 28, 51, 252, 4, 208, 194, 98, 0, 63, 196, 116, 232, 211, 56, 212, 209, 5, 223, 8, 49, 1, 200, 46, 12, 20, 249, 195, 29, 212, 113, 5, 60, 192, 20, 1, 24, 7, 196, 122, 200, 120, 198, 247, 8, 205, 57, 3, 10, 248, 0, 223, 196, 48, 24, 211, 29, 28, 48, 205, 240, 192, 248, 218, 27, 207, 16, 36, 0, 226, 29, 28, 208, 5, 204, 73, 10, 9, 200, 108, 228, 215, 113, 211, 66, 28, 211, 5, 63, 8, 194, 100, 0, 28, 245, 31, 0, 195, 29, 212, 68, 61, 4, 192, 97, 233, 200, 99, 196, 113, 0, 192, 246, 212, 246, 62, 4, 241, 10, 1, 216, 207, 20, 31, 56, 211, 50, 4, 8, 205, 206, 8, 194, 230, 219, 7, 23, 215, 106, 240, 29, 229]
for i in range(len(data)): data[i] ^= key[i % len(key)]
def run_segment(seg_ops, ch): stack = [ch] reg1 = reg2 = 0 ip = 0 while ip < len(seg_ops): op = seg_ops[ip] if op == 161: stack.append(seg_ops[ip+1]); ip += 2 elif op == 162: stack.append(reg1); ip += 1 elif op == 163: stack.append(reg2); ip += 1 elif op == 177: reg1 = stack.pop(); ip += 1 elif op == 178: reg2 = stack.pop(); ip += 1 elif op == 81: reg1 += stack.pop(); ip += 1 elif op == 82: reg2 += stack.pop(); ip += 1 elif op == 97: reg1 -= stack.pop(); ip += 1 elif op == 98: reg2 -= stack.pop(); ip += 1 elif op == 113: reg1 ^= stack.pop(); ip += 1 elif op == 114: reg2 ^= stack.pop(); ip += 1 elif op == 129: reg1 *= stack.pop(); ip += 1 elif op == 130: reg2 *= stack.pop(); ip += 1 elif op == 105: ip += 1 elif op == 144: return reg1, reg2 else: ip += 1 return reg1, reg2
segments = []pc = 0start = 0while pc < len(data): op = data[pc] if op == 161: pc += 2 else: pc += 1 if op == 144: segments.append(data[start:pc]) start = pc
flag = ""for seg in segments: for ch in range(32, 127): r1, r2 = run_segment(seg, ch) if r1 == r2: flag = chr(ch) + flag break
print(flag)- Sau khi chạy thì ta đã có được flag cho bài này.
Flag
PTITCTF{UPvkfGVCWQR6F9U0dp4qn4JSs+XGebuYEgumYvXT/hw=}R(ush)TIT
- Mình sử dụng IDA để đọc code của chương trình này.

- Đọc qua 2 hàm có trong main thì mình xác định được hàm xử lý logic chính là
sub_140001000. - Tiến hành phân tích nó.

- Ở phần đầu này thì chương trình đang xử lý dữ liệu vào.
- Dữ liệu được nhập vào dạng
file_name flag. - Vì thế ta sẽ thấy
sub_140005A90(v17) != 2(chính là độ dài của mảng agrv) được so sánh với 2. - Nếu không đúng sẽ in ra gợi ý và kết thúc chương trình luôn.

- Tiếp đến ngay phía dưới, ta thấy
v12chính là argv[1] (tức là phần flag ta nhập vào) được so sánh với 42, ta biết được flag sẽ có độ dài 42 ký tự.

- Ngay phía dưới ta sẽ thấy flag được lấy từng ký tự ra và gán vào v25.
v25 = sub_140001E90(v24)
- Sau đó sẽ đi qua 1 hàm xử lý và gán lại vào mảng v23.
v39 = sub_140001640(&v26, 1LL);sub_140005AA0(v23, v39, &off_14001F878);
- Sau khi phân tích thì ta sẽ biết được hàm
sub_140001640là dạng hàm tính CRC32.


- Sau đó các dữ liệu trả về từ hàm CRC32 sẽ được so sánh lần lượt với mảng
v27, nếu sai thì sẽ thoát chương trình. - Mình có viết một đoạn script python để bruteforce các ký tự đi qua hàm CRC32 và rồi so sánh với dữ liệu có sẵn trong
v27, nếu đúng ta sẽ biết đó là ký tự có trong flag.
import stringimport zlib
check = [ 0xB969BE79, 0xBE047A60, 0xDD0216B9, 0xBE047A60, 0x3DD7FFA7, 0xBE047A60, 0x4DBD0B28, 0x15D54739, 0x4AD0CF31, 0x83DCEFB7, 0x7808A3D2, 0x8D076785, 0xF4DBDF21, 0x9E6BFFD3, 0x29D6A3E8, 0x5767DF55, 0xF26D6A3E, 0x84B12BAE, 0x6ABF4A82, 0xFBDB2615, 0x29D6A3E8, 0x3DD7FFA7, 0xF3B61B38, 0x6C09FF9D, 0x01D41B76, 0xF4DBDF21, 0x29D6A3E8, 0x83DCEFB7, 0x7808A3D2, 0x29D6A3E8, 0xBE047A60, 0x916B06E7, 0x6DD28E9B, 0x29D6A3E8, 0x5767DF55, 0x6DD28E9B, 0x6B643B84, 0x6DD28E9B, 0x6C09FF9D, 0x84B12BAE, 0x6DD28E9B, 0xFCB6E20C]
def crc32(data): return zlib.crc32(data) & 0xFFFFFFFF
char = string.printableflag = ""for i in range(42): for j in char: if crc32(j.encode("utf-8")) == check[i]: flag += jprint(flag)- Và ta đã có được flag cho bài này.
Flag
PTITCTF{B1n90!_Ru57y_C4rg0_1n_Th3_R3v3r53}