The service runs a custom kernel with an “extended” eBPF verifier bug. A BPF program can build an arbitrary 64-bit offset using a non-constant shift, then add it to a map value pointer without a proper bounds check. This gives out-of-bounds read/write relative to the map value and lets us locate and overwrite the current task’s cred struct to become root and read /flag.
The program lets a user provide a list of (shift, enable) slots. The BPF program computes:
offset = sum_i ( (1ULL << (shift_i & 63)) * (enable_i & 1) )
The verifier accepts the non-constant shift and the accumulated scalar, then allows adding that scalar to a map value pointer. With 64 slots this builds any 64-bit value, so we can point outside the map value and read/write arbitrary kernel memory near the map allocation. This is the “non-constant shift is too big of an extension” bug hinted by the flag.
1) Create a BPF array map with a large value size. The map value is a control structure:
op selects read or write.write_val is the 64-bit value to store.slots[64] encodes the offset bits.out[0x400] receives read data.2) Load a BPF program that:
offset from the slots.ptr = map_value + offset.op == 1, writes write_val to *ptr.ptr into out.3) Use the read primitive to scan memory around the map value for a cred struct. The scan checks:
usage is a small refcount.securebits == 0.capget, PR_CAPBSET_READ, and PR_CAP_AMBIENT.4) When a candidate matches, overwrite its fields:
If getuid() becomes 0, spawn a shell and read the flag. If not, restore the original fields and keep scanning.
From the challenge directory:
gcc -O2 -s exploit.c -o exploit
python run_exploit.py
The script solves the PoW, logs in as ctf, uploads the binary, and runs it. You should see uid=0(root) and the flag.
uoftctf{n0n_c0ns74n7_shif7_is_700_big_0f_4n_3x73nsi0n}