Challenge: Auto Cooker We present for our desert a...
Tạo vào: 5 tháng 6, 2026
Trả lời bằng GPT-5.5 Thinking bởi Chat01
Tạo vào: 5 tháng 6, 2026
Trả lời bằng GPT-5.5 Thinking bởi Chat01
Challenge: Auto Cooker
We present for our desert a slightly deranged ghost, even though they may be unstable, they are still delicious. Share a meal and a CPU with this ghost and we're sure you'll be satisfied
Challenge: SpecCTF
I always feel like cooking is such a chore... You have to chop up all your ingredients, cook them for hours and then make the plating look half-decent.
But not with this new machine I got! You just have to put in your recipe (weirdly, the interface calls it a flag...) and it will get cooked for you. It's so easy, even someone with no experience in cooking reverse engineering can do it.
flag format: GPNCTF{....}
tìm flag rồi viết write up chi tiết
Auto Cooker
textGPNCTF{I_FE31_1iK3_YOU_arE_RE4dY_f0R_ouR_HardEsT_DiShES_N0W}
SpecCTF
textGPNCTF{th15_MEA1_IS_5PecU1aT1v3lY_del1C1OUs!!!!}
bashfile autocooker nm -C autocooker
Binary không strip, có các symbol khá rõ:
textcheck_recipe_length salt fry trim mix taste FOOD RECIPE DELICIOUS TARGET_LENGTH GRAIN_OF_SALT
Trong main, chương trình:
fgets(RECIPE, 0x40, stdin).RECIPE sang FOOD.textsalt() fry() trim() mix() taste()
Sau đó taste() so sánh FOOD với mảng DELICIOUS.
TARGET_LENGTH = 0x3d = 61.
Hàm check_recipe_length() kiểm tra:
cRECIPE[61] == 0 RECIPE[60] != 0
Vì fgets giữ lại newline, flag thật dài 60 ký tự, sau đó byte thứ 60 là \n, byte thứ 61 là \0.
salt()XOR từng byte với GRAIN_OF_SALT = 0xaa.
cFOOD[i] ^= 0xaa;
fry()Đảo hai nibble của từng byte.
cFOOD[i] = (FOOD[i] << 4) | (FOOD[i] >> 4);
Ví dụ:
text0xAB -> 0xBA
trim()Từ index 61 đến 63, chỉ giữ lại low nibble:
cFOOD[i] &= 0x0f;
Phần này xử lý \0 và padding, không ảnh hưởng flag chính.
mix()Reverse toàn bộ 64 byte:
cFOOD[i] = old_FOOD[63 - i];
taste()So sánh 64 byte cuối với DELICIOUS.
Vì phép biến đổi chính là:
textrecipe byte -> XOR 0xaa -> swap nibble -> reverse -> compare with DELICIOUS
Ta đảo ngược như sau:
textrecipe[i] = swap_nibbles(DELICIOUS[63 - i]) ^ 0xaa
Lấy các byte từ 0 đến 59, bỏ byte newline ở cuối.
pythonfrom pathlib import Path import struct def swap_nibbles(x): return ((x << 4) & 0xff) | (x >> 4) p = Path("autocooker").read_bytes() DATA_VA = 0x404040 DATA_OFF = 0x3040 TARGET_LENGTH = struct.unpack_from("<I", p, DATA_OFF + (0x404060 - DATA_VA))[0] GRAIN_OF_SALT = p[DATA_OFF + (0x404064 - DATA_VA)] delicious = p[DATA_OFF + (0x404080 - DATA_VA):][:64] recipe = bytearray() for i in range(TARGET_LENGTH): recipe.append(swap_nibbles(delicious[63 - i]) ^ GRAIN_OF_SALT) flag = bytes(recipe[:-1]).decode() print(flag)
Output:
textGPNCTF{I_FE31_1iK3_YOU_arE_RE4dY_f0R_ouR_HardEsT_DiShES_N0W}
Challenge cố tình dùng cơ chế kiểu Spectre/cache timing để làm check flag trở nên “khó debug”. Tuy nhiên phần quan trọng vẫn nằm trong binary.
Binary chứa mảng ENC gồm các giá trị 64-bit. Input được chia thành từng block 8 byte, rồi mỗi block được hash và so với một phần tử trong ENC.
Trong main:
asmstrlen(argv[1]) len % 8 == 0
Nếu độ dài không chia hết cho 8 thì in:
textNOPE
Sau đó chương trình xử lý từng block 8 byte:
cr15 = ENC[i]; r14 = *(uint64_t *)&argv[1][i * 8]; specte_byte(0x1337, 0x1337);
Trong hàm bị giấu sau side-channel, có đoạn quan trọng:
cif (hashy(r14) == r15) access arr2[0x2800]; else access arr2[0xa200];
Timing/cache chỉ dùng để đo xem nhánh nào chạy. Về mặt logic, điều kiện đúng là:
chashy(input_qword) == ENC[i]
Hàm hashy:
cuint64_t hashy(uint64_t x) { x ^= x >> 33; x *= 0xf451af975d152cad; x ^= x >> 33; x ^= 0xc2ceaade1a351c23; x ^= x >> 33; return x; }
Các phép này đều đảo được:
x ^= x >> 33 đảo được vì 33 * 2 > 64.2^64.Dump .data thấy ENC bắt đầu ở 0x70c0:
text0xee7590ece97175e5 0x1a01fede7030037a 0xaf373fe536545eed 0xf925da06efd9fb45 0x02bc2f1d4df279cf 0x6e4032298a6842ff
Có 6 block, mỗi block 8 byte, nên flag dài:
text6 * 8 = 48 bytes
Với:
cy = x ^ (x >> 33)
Do x >> 66 = 0, ta có:
cx = y ^ (y >> 33)
Nhân modulo đảo bằng:
pythonpow(0xf451af975d152cad, -1, 1 << 64)
pythonfrom pathlib import Path import struct MASK = (1 << 64) - 1 def unxor_right(y, s=33): return (y ^ (y >> s)) & MASK def hashy(x): x ^= x >> 33 x = (x * 0xf451af975d152cad) & MASK x ^= x >> 33 x ^= 0xc2ceaade1a351c23 x ^= x >> 33 return x & MASK def unhashy(y): inv_mul = pow(0xf451af975d152cad, -1, 1 << 64) x = unxor_right(y) x ^= 0xc2ceaade1a351c23 x = unxor_right(x) x = (x * inv_mul) & MASK x = unxor_right(x) return x p = Path("specCTF").read_bytes() DATA_VA = 0x70a0 DATA_OFF = 0x60a0 ENC_VA = 0x70c0 enc = [ struct.unpack_from("<Q", p, DATA_OFF + (ENC_VA - DATA_VA) + i * 8)[0] for i in range(6) ] chunks = [] for y in enc: x = unhashy(y) assert hashy(x) == y chunks.append(struct.pack("<Q", x)) flag = b"".join(chunks).decode() print(flag)
Output:
textGPNCTF{th15_MEA1_IS_5PecU1aT1v3lY_del1C1OUs!!!!}
python#!/usr/bin/env python3 from pathlib import Path import struct MASK = (1 << 64) - 1 # ---------- Auto Cooker ---------- def swap_nibbles(x): return ((x << 4) & 0xff) | (x >> 4) auto = Path("autocooker").read_bytes() DATA_VA_AUTO = 0x404040 DATA_OFF_AUTO = 0x3040 TARGET_LENGTH = struct.unpack_from( "<I", auto, DATA_OFF_AUTO + (0x404060 - DATA_VA_AUTO) )[0] GRAIN_OF_SALT = auto[ DATA_OFF_AUTO + (0x404064 - DATA_VA_AUTO) ] delicious = auto[ DATA_OFF_AUTO + (0x404080 - DATA_VA_AUTO): ][:64] recipe = bytearray() for i in range(TARGET_LENGTH): recipe.append(swap_nibbles(delicious[63 - i]) ^ GRAIN_OF_SALT) auto_flag = bytes(recipe[:-1]).decode() print("Auto Cooker:", auto_flag) # ---------- SpecCTF ---------- def unxor_right(y, s=33): return (y ^ (y >> s)) & MASK def hashy(x): x ^= x >> 33 x = (x * 0xf451af975d152cad) & MASK x ^= x >> 33 x ^= 0xc2ceaade1a351c23 x ^= x >> 33 return x & MASK def unhashy(y): inv_mul = pow(0xf451af975d152cad, -1, 1 << 64) x = unxor_right(y) x ^= 0xc2ceaade1a351c23 x = unxor_right(x) x = (x * inv_mul) & MASK x = unxor_right(x) return x spec = Path("specCTF").read_bytes() DATA_VA_SPEC = 0x70a0 DATA_OFF_SPEC = 0x60a0 ENC_VA = 0x70c0 enc = [ struct.unpack_from( "<Q", spec, DATA_OFF_SPEC + (ENC_VA - DATA_VA_SPEC) + 8 * i )[0] for i in range(6) ] chunks = [] for y in enc: x = unhashy(y) assert hashy(x) == y chunks.append(struct.pack("<Q", x)) spec_flag = b"".join(chunks).decode() print("SpecCTF:", spec_flag)
Kết quả:
textAuto Cooker: GPNCTF{I_FE31_1iK3_YOU_arE_RE4dY_f0R_ouR_HardEsT_DiShES_N0W} SpecCTF: GPNCTF{th15_MEA1_IS_5PecU1aT1v3lY_del1C1OUs!!!!}