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

Câu hỏi

specctf.tar.gz
autocooker.tar.gz

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

Suy nghĩ

Creating the SpecCTF Writeup

For the SpecCTF writeup in Vietnamese:

  • Focus on being detailed but concise—moderate length, not too long.
  • Include necessary flags and commands, but avoid unnecessary long explanations.
  • Honestly note that we couldn’t verify the binary due to timeout but verified the hash. Mention the side-channel noise and slow timing, suggesting a solution: “bỏ qua timing và đảo hash.”
  • No citations or web references needed. Add byte arrays, functions, and step-by-step instructions, ensuring it's not overwhelming.
  • Explain the Auto Cooker transformation to check flag length constraints.

Thought for 3m 11s

Trả lời

Flags tìm được

Auto Cooker

text
GPNCTF{I_FE31_1iK3_YOU_arE_RE4dY_f0R_ouR_HardEsT_DiShES_N0W}

SpecCTF

text
GPNCTF{th15_MEA1_IS_5PecU1aT1v3lY_del1C1OUs!!!!}

Write-up: Auto Cooker

1. Kiểm tra binary

bash
file autocooker nm -C autocooker

Binary không strip, có các symbol khá rõ:

text
check_recipe_length salt fry trim mix taste FOOD RECIPE DELICIOUS TARGET_LENGTH GRAIN_OF_SALT

2. Logic chính

Trong main, chương trình:

  1. Đọc input bằng fgets(RECIPE, 0x40, stdin).
  2. Check độ dài.
  3. Copy RECIPE sang FOOD.
  4. Chạy các bước:
text
salt() fry() trim() mix() taste()

Sau đó taste() so sánh FOOD với mảng DELICIOUS.

3. Check độ dài

TARGET_LENGTH = 0x3d = 61.

Hàm check_recipe_length() kiểm tra:

c
RECIPE[61] == 0 RECIPE[60] != 0

fgets giữ lại newline, flag thật dài 60 ký tự, sau đó byte thứ 60 là \n, byte thứ 61 là \0.

4. Các phép biến đổi

salt()

XOR từng byte với GRAIN_OF_SALT = 0xaa.

c
FOOD[i] ^= 0xaa;

fry()

Đảo hai nibble của từng byte.

c
FOOD[i] = (FOOD[i] << 4) | (FOOD[i] >> 4);

Ví dụ:

text
0xAB -> 0xBA

trim()

Từ index 61 đến 63, chỉ giữ lại low nibble:

c
FOOD[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:

c
FOOD[i] = old_FOOD[63 - i];

taste()

So sánh 64 byte cuối với DELICIOUS.

5. Đảo ngược

Vì phép biến đổi chính là:

text
recipe byte -> XOR 0xaa -> swap nibble -> reverse -> compare with DELICIOUS

Ta đảo ngược như sau:

text
recipe[i] = swap_nibbles(DELICIOUS[63 - i]) ^ 0xaa

Lấy các byte từ 0 đến 59, bỏ byte newline ở cuối.

6. Script solve Auto Cooker

python
from 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:

text
GPNCTF{I_FE31_1iK3_YOU_arE_RE4dY_f0R_ouR_HardEsT_DiShES_N0W}

Write-up: SpecCTF

1. Ý tưởng challenge

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.

2. Main logic

Trong main:

asm
strlen(argv[1]) len % 8 == 0

Nếu độ dài không chia hết cho 8 thì in:

text
NOPE

Sau đó chương trình xử lý từng block 8 byte:

c
r15 = 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:

c
if (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à:

c
hashy(input_qword) == ENC[i]

3. Hàm hash

Hàm hashy:

c
uint64_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.
  • Nhân với hằng số lẻ đảo được modulo 2^64.
  • XOR với constant đảo bằng XOR lại constant.

4. Mảng ENC

Dump .data thấy ENC bắt đầu ở 0x70c0:

text
0xee7590ece97175e5 0x1a01fede7030037a 0xaf373fe536545eed 0xf925da06efd9fb45 0x02bc2f1d4df279cf 0x6e4032298a6842ff

Có 6 block, mỗi block 8 byte, nên flag dài:

text
6 * 8 = 48 bytes

5. Đảo hash

Với:

c
y = x ^ (x >> 33)

Do x >> 66 = 0, ta có:

c
x = y ^ (y >> 33)

Nhân modulo đảo bằng:

python
pow(0xf451af975d152cad, -1, 1 << 64)

6. Script solve SpecCTF

python
from 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:

text
GPNCTF{th15_MEA1_IS_5PecU1aT1v3lY_del1C1OUs!!!!}

Script tổng hợp cho cả hai challenge

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ả:

text
Auto Cooker: GPNCTF{I_FE31_1iK3_YOU_arE_RE4dY_f0R_ouR_HardEsT_DiShES_N0W} SpecCTF: GPNCTF{th15_MEA1_IS_5PecU1aT1v3lY_del1C1OUs!!!!}

Chia sẻ Q&A này