Logo
Overview
[SECCON CTF 14] Ez Flag Checker writeup

[SECCON CTF 14] Ez Flag Checker writeup

December 14, 2025
2 min read

✨ 바이너리 공짜 다운로드 ✨

TL;DR

The checker validates the format SECCON{??????????????????} (exactly 26 bytes) And the encrypts the 18 bytes inside the braces with a simple XOR stream derived from the 16 byte constant expand 32 byte k.

Analysis

Main logic

cmp [rbp-0x140], 0x1a ; length == 26
call strncmp ; "SECCON{" prefix check, n=7
...
sub edx, eax ; last_char - '}' == 0
call sigma_encrypt ; encrypt 0x12 bytes at buf+7
call memcmp ; compare with flag_enc, len=0x12
```c
Print Enter flag:
1. fgets() a line into a stack buffer
2. Strip the newline using strcspn(buf, "\n") and null-terminate
3. Compute len = strlen(buf)
4. Reject unless len == 0x1a (26)
5. Reject unless strncmp(buf, "SECCON{", 7) == 0
6. Reject unless buf[len-1] == '}'
7. Encrypt the 18-byte substring buf+7 (the bytes inside braces) into a temporary buffer using sigma_encrypt(buf+7, out, 0x12)
8. Compare out against a global byte array flag_enc with memcmp(out, flag_enc, 0x12)
In C-like pseudocode
```c
int main(void) {
char buf[0x100];
printf("Enter flag: ");
if (!fgets(buf, sizeof(buf), stdin)) return 1;
buf[strcspn(buf, "\n")] = 0;
size_t len = strlen(buf);
if (len != 0x1a) { puts("wrong :("); return 1; }
if (strncmp(buf, "SECCON{", 7) != 0) { puts("wrong :("); return 1; }
if (buf[len - 1] != '}') { puts("wrong :("); return 1; }
uint8_t out[0x12];
sigma_encrypt((uint8_t*)buf + 7, out, 0x12);
if (memcmp(out, flag_enc, 0x12) == 0) {
puts("correct flag!");
return 0;
}
puts("wrong :(");
return 1;
}

sigma_encrypt

Phase A: Build a 16-byte key array

The function loads 4 DWORDs from a global sigma_words and writes them byte-by-byte into a local 16-byte array (little-endian).

Looking at .data (e.g., readelf -x .data chall) shows sigma_words is literally the ASCII string: expand 32-byte k

So the 16 key bytes are -> key = b”expand 32-byte k”

Phase B: XOR each input byte with a position dependent mask

for (size_t i = 0; i < len; i++) {
uint8_t k = key[i & 0xF]; // key repeats every 16 bytes
uint8_t m = (uint8_t)(k + i); // add the index
out[i] = in[i] ^ m; // XOR
}

Also see these exact operations in the assemble

0i<180 \le i < 18

ci=pi((kimod16+i)mod256)c_i=p_i \oplus ((k_i mod 16+i) mod 256)

That means the entire challenge reduces to Extract flag_enc(18 bytes) from .rodata Use key expand 32 byte k Decrypt with formula above

flag_enc in .rodata is the

03 15 13 03 11 5b 1f 43 63 61 59 ef bc 10 1f 43 54 a8

Exploit

flag validate