rekcehcglaf

Points 166

Solves 10

well… you know what it is

Author: Durrr


We get a single x86-64 binary, checker. Decompiling main gives a wall of statically linked startup noise (signal handlers, /dev/null redirections, pthread stack introspection), but one calls stand out:

v29 = (void *)sub_15A97B(sub_15E0B9);

Inside sub_15E0B9, the program builds a prompt string and calls sub_37F370 to print it:

sub_37F370("glaf: ", 13);

The function around the prompt input call sets up a runtime with the classic Wasmtime/Cranelift settings:

  sub_3DF681(&v560, aEnableVerifier, 15);
  sub_3DF681(v576, aFalse, 5);
  sub_44CD5C(v530, &rem[288], &v560, v576);
  sub_3E4E20(*(_QWORD *)v530, *(_QWORD *)&v530[8]);
  sub_3DF681(&v560, aOptLevel, 9);
  sub_3DF681(v576, aSpeed, 5);

enable_verifier = false with opt_level = speed is a Wasmtime engine configuration, so the checker is a WebAssembly module embedded somewhere in the binary. Scan for the \x00asm magic and dump every candidate:

from pathlib import Path

def read_uleb(data, i):
    result = 0
    shift = 0
    start = i
    while True:
        b = data[i]
        i += 1
        result |= (b & 0x7f) << shift
        if not (b & 0x80):
            return result, i
        shift += 7
        if shift > 64:
            raise ValueError(f"bad uleb at {start:x}")

def wasm_end(data, off):
    if data[off:off+4] != b"\x00asm":
        raise ValueError("not wasm magic")
    if data[off+4:off+8] != b"\x01\x00\x00\x00":
        raise ValueError("not wasm version 1")

    i = off + 8
    while i < len(data):
        sec_id = data[i]
        i += 1

        # valid section ids are 0..12-ish for core wasm;
        # custom section is 0.
        if sec_id > 12:
            return i - 1

        size, i = read_uleb(data, i)
        if i + size > len(data):
            return i

        i += size

        # If next byte doesn't look like a section id, assume module ended.
        if i >= len(data) or data[i] > 12:
            return i

    return i

data = Path("./checker").read_bytes()

for off in [i for i in range(len(data)) if data.startswith(b"\x00asm", i)]:
    try:
        end = wasm_end(data, off)
        out = f"dump_{off:x}.wasm"
        Path(out).write_bytes(data[off:end])
        print(f"{out}: offset={hex(off)} size={end-off}")
    except Exception as e:
        print(f"bad candidate at {hex(off)}: {e}")

The scan finds one real module at file offset 0x66404, saved as dump_66404.wasm. Disassemble it:

wasm2wat dump_66404.wasm -o dumped_from_hook.wat

The module exports a check function, function index 3, (func (;3;) (type 2) (param i32 i32) (result i32)): a pointer to the input and its length, returning success. Reconstructed, it is five gates in a row:

int check(char *input, int len) {
    if (cooldown_active())
        return 0;

    if (len != 88)
        return fail();

    if (!bytewise_check(input))
        return fail();

    if (!u32_chunk_check(input))
        return fail();

    if (!pairwise_packed_check(input))
        return fail();

    if (!vm_bytecode_check(input))
        return fail();

    reset_fail_counter();
    return 1;
}

A cooldown, a length check demanding exactly 88 bytes, then a per-byte table check, a u32 chunk check, a pairwise packed check, and finally a custom VM executing the bytecode stored at the start of the data segment.

When the AI reconstructed it this way, u braced for having to go all the way down into the VM. But people were solving it way too fast for that, and pressing on it made it click: the code is not like that at all. The very first bytewise check already constrains every flag byte on its own, so the others are redundant for the solve. The VM never needs to be understood.

The bytewise check compares each input byte against an 88-byte table under a per-index transform:

flag[i] = (table1[i] ^ ((i * 19 - 91) & 0xff)) - ((i * 17) ^ 35)
flag[i] &= 0xff

Inverting it recovers each flag byte as (table1[i] ^ ((i * 19 - 91) & 0xff)) - ((i * 17) ^ 35) & 0xff. The table is exactly 88 bytes, matching the length gate.

The module’s data segment 0 loads at linear memory address 1048576, and the bytewise table starts at 1049132.

  (data (;0;) (i32.const 1048576) "\015\02\17\03\00\02m\04\00\02\a6\05\00\02)\06\00\02[\07\00\02\17\08\00\02\01\03\00\02\99\09\00\0a\08\02\00\02\00\07\00\09\00\01\11\02!\03\00\04\00\02Z\05\00\02\13\06\00\02\03\07\00\02\1f\08\00\01@\024\03\00\04\00\02\81\05\00\02&\06\00\02\05\07\00\02%\08\00\01o\02G\03\00\04\00\02\a8\05\00\029\06\00\02\07\07\00\02+\08\00\01\9e\02Z\03\00\04\00\02\cf\05\00\02L\06\00\02\09\07\00\021\08\00\01\cd\02m\03\00\04\00\02\f6\05\00\02_\06\00\02\0b\07\00\027\08\00\01\fc\02\80\03\00\04\00\02\1d\05\00\02r\06\00\02\0d\07\00\02=\08\00\01+\02\93\03\00\04\00\02D\05\00\02\85\06\00\02\0f\07\00\02C\08\00\01Z\02\a6\03\00\04\00\02k\05\00\02\98\06\00\02\11\07\00\02I\08\00\01\89\02\b9\03\00\04\00\02\92\05\00\02\ab\06\00\02\13\07\00\02O\08\00\01\b8\02\cc\03\00\04\00\02\b9\05\00\02\be\06\00\02\15\07\00\02U\08\00\01\e7\02\df\03\00\04\00\02\e0\05\00\02\d1\06\00\02\17\07\00\02[\08\00\01\16\02\f2\03\00\04\00\02\07\05\00\02\e4\06\00\02\19\07\00\02a\08\00\01E\02\05\03\00\04\00\02.\05\00\02\f7\06\00\02\1b\07\00\02g\08\00\01t\02\18\03\00\04\00\02U\05\00\02\0a\06\00\02\1d\07\00\02m\08\00\01\a3\02+\03\00\04\00\02|\05\00\02\1d\06\00\02\1f\07\00\02s\08\00\01\d2\02>\03\00\04\00\02\a3\05\00\020\06\00\02!\07\00\02y\08\00\01\01\02Q\03\00\04\00\02\ca\05\00\02C\06\00\02#\07\00\02\7f\08\00\010\02d\03\00\04\00\02\f1\05\00\02V\06\00\02%\07\00\02\85\08\00\01_\02w\03\00\04\00\02\18\05\00\02i\06\00\02'\07\00\02\8b\08\00\01\89\02\bd\05\00\03\00\02\03\07\00\f4A\0c\0ac\ce\d12\c22\8c/\f3GZP\d4\bai\1c\d0]\abp\01\bb\fb_d\1fT\e7\ef\88\bf\d4\e3\7f\97Z\b1\02{\11\c9>\8c\ab+\ee`\a0\1b}\da|\e8\eb\83\80\b6\9c\8b\c8\c2N\fb]n=o\f5\8b\8c\e3\92o^\b9\e0\f7\e3s\ae`z\95\ff\ab\ed\83\92N\db\bf\f1OM\80\0d7\be\92\fd'\b6N-\b2\a9,\22j\d9?6\e3M\17=\e8\c1Q\08G\db~\c8\0b\0d\0e{~\1c\16EF<b\16@;`RO1\0fF\0f:\0fFQ|7Cs{3K\06\0a\0f\14PLaNjI\0fPYH=\11^K\0fTGI#\7fZK\22\13t\0c8\13J$D\13c\17\16\0c!/\13((+\1bSo\17A\0e@y\04\06.;\04\1d\de\be\ed\fe\bb\bf\ec\ff\9c\bc\ef\fc\95\bd\ee\fd\81\ba\e9\fa\a2\bb\e8\fb\b2\b8\eb\f8\da\b9\ea\f9\86\b6\e5\f6\8d\b7\e4\f7\b1\b4\e7\f4\a7\b5\e6\f5\86\b2\e1\f2\bc\b3\e0\f3\a8\b0\e3\f0\92\b1\e2\f1\97\ae\fd\ee\9e\af\fc\ef\ae\ac\ff\ec\b0\ad\fe\ed\b4\aa\f9\ea\9f\ab\f8\eb\a9\a8\fb\e8\a0\a9\fa\e9\a3\a6\f5\e6\a6\a7\f4\e7\9f\a4\f7\e4\c5\a5\f6\e5\b3\a2\f1\e2\b4\a3\f0\e3\bf\a0\f3\e0\bb\a1\f2\e1\88\9e\cd\de\f4\9f\cc\df\93\9c\cf\dc\8e\9d\ce\dd\a2\9a\c9\da\8b\9b\c8\db\8f\98\cb\d8\af\99\ca\d9\89\96\c5\d6\ae\97\c4\d7\bc\94\c7\d4\97\95\c6\d5\a7\92\c1\d2\ed\93\c0\d3\a7\90\c3\d0\fd\91\c2\d1\be\8e\dd\ce\87\8f\dc\cf\91\8c\df\cc\9f\8d\de\cd\81\8a\d9\ca\92\8b\d8\cb\9c\88\db\c8\b0\89\da\c9\89\86\d5\c6\80\87\d4\c7\80\84\d7\c4\8a\85\d6\c5\e2\82\d1\c2\b9\83\d0\c3\82\80\d3\c0\9f\81\d2\c1\f7\fe\ad\be\fe\ff\ac\bf\ea\fc\af\bc\e1\fd\ae\bd\d9\fa\a9\ba\ef\fb\a8\bb\cc\f8\ab\b8\98\f9\aa\b9\c5\f6\a5\b6\f7\f7\a4\b7\f7\f4\a7\b4\ef\f5\a6\b5\cf\f2\a1\b2\f6\f3\a0\b3\d2\f0\a3\b0\f0\f1\a2\b1\8a\ee\bd\ae\ce\ef\bc\af\e2\ec\bf\ac\d6\ed\be\ad\87\ea\b9\aa\e2\eb\b8\ab\ff\e8\bb\a8\e0\e9\ba\a9")
  (data (;1;) (i32.const 1049660) "\fe\ca\de\c0"))

The data segments can also be dumped with wasm-objdump -x on the extracted module.

Full solver, with the byte array being the data segment dump from the module.

bytecode = bytes([
    
    0x01,0x35, 0x02,0x17, 0x03,0x00, 0x02,0x6d, 0x04,0x00, 0x02,0xa6, 0x05,0x00, 0x02,0x29,
    0x06,0x00, 0x02,0x5b, 0x07,0x00, 0x02,0x17, 0x08,0x00, 0x02,0x01, 0x03,0x00, 0x02,0x99,
    0x09,0x00, 0x0a,0x08, 0x02,0x00, 0x02,0x00, 0x07,0x00, 0x09,0x00, 0x01,0x11, 0x02,0x21,
    0x03,0x00, 0x04,0x00, 0x02,0x5a, 0x05,0x00, 0x02,0x13, 0x06,0x00, 0x02,0x03, 0x07,0x00,
    0x02,0x1f, 0x08,0x00, 0x01,0x40, 0x02,0x34, 0x03,0x00, 0x04,0x00, 0x02,0x81, 0x05,0x00,
    0x02,0x26, 0x06,0x00, 0x02,0x05, 0x07,0x00, 0x02,0x25, 0x08,0x00, 0x01,0x6f, 0x02,0x47,
    0x03,0x00, 0x04,0x00, 0x02,0xa8, 0x05,0x00, 0x02,0x39, 0x06,0x00, 0x02,0x07, 0x07,0x00,
    0x02,0x2b, 0x08,0x00, 0x01,0x9e, 0x02,0x5a, 0x03,0x00, 0x04,0x00, 0x02,0xcf, 0x05,0x00,
    0x02,0x4c, 0x06,0x00, 0x02,0x09, 0x07,0x00, 0x02,0x31, 0x08,0x00, 0x01,0xcd, 0x02,0x6d,
    0x03,0x00, 0x04,0x00, 0x02,0xf6, 0x05,0x00, 0x02,0x5f, 0x06,0x00, 0x02,0x0b, 0x07,0x00,
    0x02,0x37, 0x08,0x00, 0x01,0xfc, 0x02,0x80, 0x03,0x00, 0x04,0x00, 0x02,0x1d, 0x05,0x00,
    0x02,0x72, 0x06,0x00, 0x02,0x0d, 0x07,0x00, 0x02,0x3d, 0x08,0x00, 0x01,0x2b, 0x02,0x93,
    0x03,0x00, 0x04,0x00, 0x02,0x44, 0x05,0x00, 0x02,0x85, 0x06,0x00, 0x02,0x0f, 0x07,0x00,
    0x02,0x43, 0x08,0x00, 0x01,0x5a, 0x02,0xa6, 0x03,0x00, 0x04,0x00, 0x02,0x6b, 0x05,0x00,
    0x02,0x98, 0x06,0x00, 0x02,0x11, 0x07,0x00, 0x02,0x49, 0x08,0x00, 0x01,0x89, 0x02,0xb9,
    0x03,0x00, 0x04,0x00, 0x02,0x92, 0x05,0x00, 0x02,0xab, 0x06,0x00, 0x02,0x13, 0x07,0x00,
    0x02,0x4f, 0x08,0x00, 0x01,0xb8, 0x02,0xcc, 0x03,0x00, 0x04,0x00, 0x02,0xb9, 0x05,0x00,
    0x02,0xbe, 0x06,0x00, 0x02,0x15, 0x07,0x00, 0x02,0x55, 0x08,0x00, 0x01,0xe7, 0x02,0xdf,
    0x03,0x00, 0x04,0x00, 0x02,0xe0, 0x05,0x00, 0x02,0xd1, 0x06,0x00, 0x02,0x17, 0x07,0x00,
    0x02,0x5b, 0x08,0x00, 0x01,0x16, 0x02,0xf2, 0x03,0x00, 0x04,0x00, 0x02,0x07, 0x05,0x00,
    0x02,0xe4, 0x06,0x00, 0x02,0x19, 0x07,0x00, 0x02,0x61, 0x08,0x00, 0x01,0x45, 0x02,0x05,
    0x03,0x00, 0x04,0x00, 0x02,0x2e, 0x05,0x00, 0x02,0xf7, 0x06,0x00, 0x02,0x1b, 0x07,0x00,
    0x02,0x67, 0x08,0x00, 0x01,0x74, 0x02,0x18, 0x03,0x00, 0x04,0x00, 0x02,0x55, 0x05,0x00,
    0x02,0x0a, 0x06,0x00, 0x02,0x1d, 0x07,0x00, 0x02,0x6d, 0x08,0x00, 0x01,0xa3, 0x02,0x2b,
    0x03,0x00, 0x04,0x00, 0x02,0x7c, 0x05,0x00, 0x02,0x1d, 0x06,0x00, 0x02,0x1f, 0x07,0x00,
    0x02,0x73, 0x08,0x00, 0x01,0xd2, 0x02,0x3e, 0x03,0x00, 0x04,0x00, 0x02,0xa3, 0x05,0x00,
    0x02,0x30, 0x06,0x00, 0x02,0x21, 0x07,0x00, 0x02,0x79, 0x08,0x00, 0x01,0x01, 0x02,0x51,
    0x03,0x00, 0x04,0x00, 0x02,0xca, 0x05,0x00, 0x02,0x43, 0x06,0x00, 0x02,0x23, 0x07,0x00,
    0x02,0x7f, 0x08,0x00, 0x01,0x30, 0x02,0x64, 0x03,0x00, 0x04,0x00, 0x02,0xf1, 0x05,0x00,
    0x02,0x56, 0x06,0x00, 0x02,0x25, 0x07,0x00, 0x02,0x85, 0x08,0x00, 0x01,0x5f, 0x02,0x77,
    0x03,0x00, 0x04,0x00, 0x02,0x18, 0x05,0x00, 0x02,0x69, 0x06,0x00, 0x02,0x27, 0x07,0x00,
    0x02,0x8b, 0x08,0x00, 0x01,0x89, 0x02,0xbd, 0x05,0x00, 0x03,0x00, 0x02,0x03, 0x07,0x00,
    0xf4,0x41, 0x0c,0x0a, 0x63,0xce, 0xd1,0x32, 0xc2,0x32, 0x8c,0x2f, 0xf3,0x47, 0x5a,0x50,
    0xd4,0xba, 0x69,0x1c, 0xd0,0x5d, 0xab,0x70, 0x01,0xbb, 0xfb,0x5f, 0x64,0x1f, 0x54,0xe7,
    0xef,0x88, 0xbf,0xd4, 0xe3,0x7f, 0x97,0x5a, 0xb1,0x02, 0x7b,0x11, 0xc9,0x3e, 0x8c,0xab,
    0x2b,0xee, 0x60,0xa0, 0x1b,0x7d, 0xda,0x7c, 0xe8,0xeb, 0x83,0x80, 0xb6,0x9c, 0x8b,0xc8,
    0xc2,0x4e, 0xfb,0x5d, 0x6e,0x3d, 0x6f,0xf5, 0x8b,0x8c, 0xe3,0x92, 0x6f,0x5e, 0xb9,0xe0,
    0xf7,0xe3, 0x73,0xae, 0x60,0x7a, 0x95,0xff, 0xab,0xed, 0x83,0x92, 0x4e,0xdb, 0xbf,0xf1,
    0x4f,0x4d, 0x80,0x0d, 0x37,0xbe, 0x92,0xfd, 0x27,0xb6, 0x4e,0x2d, 0xb2,0xa9, 0x2c,0x22,
    0x6a,0xd9, 0x3f,0x36, 0xe3,0x4d, 0x17,0x3d, 0xe8,0xc1, 0x51,0x08, 0x47,0xdb, 0x7e,0xc8,
    0x0b,0x0d, 0x0e,0x7b, 0x7e,0x1c, 0x16,0x45, 0x46,0x3c, 0x62,0x16, 0x40,0x3b, 0x60,0x52,
    0x4f,0x31, 0x0f,0x46, 0x0f,0x3a, 0x0f,0x46, 0x51,0x7c, 0x37,0x43, 0x73,0x7b, 0x33,0x4b,
    0x06,0x0a, 0x0f,0x14, 0x50,0x4c, 0x61,0x4e, 0x6a,0x49, 0x0f,0x50, 0x59,0x48, 0x3d,0x11,
    0x5e,0x4b, 0x0f,0x54, 0x47,0x49, 0x23,0x7f, 0x5a,0x4b, 0x22,0x13, 0x74,0x0c, 0x38,0x13,
    0x4a,0x24, 0x44,0x13, 0x63,0x17, 0x16,0x0c, 0x21,0x2f, 0x13,0x28, 0x28,0x2b, 0x1b,0x53,
    0x6f,0x17, 0x41,0x0e, 0x40,0x79, 0x04,0x06, 0x2e,0x3b, 0x04,0x1d, 0xde,0xbe, 0xed,0xfe,
    0xbb,0xbf, 0xec,0xff, 0x9c,0xbc, 0xef,0xfc, 0x95,0xbd, 0xee,0xfd, 0x81,0xba, 0xe9,0xfa,
    0xa2,0xbb, 0xe8,0xfb, 0xb2,0xb8, 0xeb,0xf8, 0xda,0xb9, 0xea,0xf9, 0x86,0xb6, 0xe5,0xf6,
    0x8d,0xb7, 0xe4,0xf7, 0xb1,0xb4, 0xe7,0xf4, 0xa7,0xb5, 0xe6,0xf5, 0x86,0xb2, 0xe1,0xf2,
    0xbc,0xb3, 0xe0,0xf3, 0xa8,0xb0, 0xe3,0xf0, 0x92,0xb1, 0xe2,0xf1, 0x97,0xae, 0xfd,0xee,
    0x9e,0xaf, 0xfc,0xef, 0xae,0xac, 0xff,0xec, 0xb0,0xad, 0xfe,0xed, 0xb4,0xaa, 0xf9,0xea,
    0x9f,0xab, 0xf8,0xeb, 0xa9,0xa8, 0xfb,0xe8, 0xa0,0xa9, 0xfa,0xe9, 0xa3,0xa6, 0xf5,0xe6,
    0xa6,0xa7, 0xf4,0xe7, 0x9f,0xa4, 0xf7,0xe4, 0xc5,0xa5, 0xf6,0xe5, 0xb3,0xa2, 0xf1,0xe2,
    0xb4,0xa3, 0xf0,0xe3, 0xbf,0xa0, 0xf3,0xe0, 0xbb,0xa1, 0xf2,0xe1, 0x88,0x9e, 0xcd,0xde,
    0xf4,0x9f, 0xcc,0xdf, 0x93,0x9c, 0xcf,0xdc, 0x8e,0x9d, 0xce,0xdd, 0xa2,0x9a, 0xc9,0xda,
    0x8b,0x9b, 0xc8,0xdb, 0x8f,0x98, 0xcb,0xd8, 0xaf,0x99, 0xca,0xd9, 0x89,0x96, 0xc5,0xd6,
    0xae,0x97, 0xc4,0xd7, 0xbc,0x94, 0xc7,0xd4, 0x97,0x95, 0xc6,0xd5, 0xa7,0x92, 0xc1,0xd2,
    0xed,0x93, 0xc0,0xd3, 0xa7,0x90, 0xc3,0xd0, 0xfd,0x91, 0xc2,0xd1, 0xbe,0x8e, 0xdd,0xce,
    0x87,0x8f, 0xdc,0xcf, 0x91,0x8c, 0xdf,0xcc, 0x9f,0x8d, 0xde,0xcd, 0x81,0x8a, 0xd9,0xca,
    0x92,0x8b, 0xd8,0xcb, 0x9c,0x88, 0xdb,0xc8, 0xb0,0x89, 0xda,0xc9, 0x89,0x86, 0xd5,0xc6,
    0x80,0x87, 0xd4,0xc7, 0x80,0x84, 0xd7,0xc4, 0x8a,0x85, 0xd6,0xc5, 0xe2,0x82, 0xd1,0xc2,
    0xb9,0x83, 0xd0,0xc3, 0x82,0x80, 0xd3,0xc0, 0x9f,0x81, 0xd2,0xc1, 0xf7,0xfe, 0xad,0xbe,
    0xfe,0xff, 0xac,0xbf, 0xea,0xfc, 0xaf,0xbc, 0xe1,0xfd, 0xae,0xbd, 0xd9,0xfa, 0xa9,0xba,
    0xef,0xfb, 0xa8,0xbb, 0xcc,0xf8, 0xab,0xb8, 0x98,0xf9, 0xaa,0xb9, 0xc5,0xf6, 0xa5,0xb6,
    0xf7,0xf7, 0xa4,0xb7, 0xf7,0xf4, 0xa7,0xb4, 0xef,0xf5, 0xa6,0xb5, 0xcf,0xf2, 0xa1,0xb2,
    0xf6,0xf3, 0xa0,0xb3, 0xd2,0xf0, 0xa3,0xb0, 0xf0,0xf1, 0xa2,0xb1, 0x8a,0xee, 0xbd,0xae,
    0xce,0xef, 0xbc,0xaf, 0xe2,0xec, 0xbf,0xac, 0xd6,0xed, 0xbe,0xad, 0x87,0xea, 0xb9,0xaa,
    0xe2,0xeb, 0xb8,0xab, 0xff,0xe8, 0xbb,0xa8, 0xe0,0xe9, 0xba,0xa9,
])

for i in range(88):
    table = bytecode[(1049132 - 1048576) + i]
    print(chr(((table ^ ((i * 19 - 91) & 0xff)) - ((i * 17) ^ 35)) & 0xff), end='')

https://chatgpt.com/share/6a76a928-4c60-83ec-bb5e-9a2633a2fd7e

rekcehcglaf Flag: ITFest26{s0rry_f0r_fl4gcH3ck3r_4g41n_1_pr0m1s3_th1s_w3r3_th3_l4st_fl4gcH3ck3r_1n_1tf3st}