- #1 fortified import _chk strip (+4 CWE-121, S effort) - #2 binary format-string xref (+3 CWE-134, S effort) - #3 hardcoded-string re-enable (+2 CWE-798, S effort) - #4 BB encoder per-instruction granularity (+6 CWE-369/190/476, M) - #5 binary UAF observer (+2 CWE-416, M)
2.7 KiB
CTF Detector Gap Analysis — Binary Mode
Date: 2026-05-29 | Source: Task #158 | Baseline: 7.4% detection (2/27 ground-truth)
Summary
| Metric | Value |
|---|---|
| CTF binaries evaluated | 10 |
| Ground-truth vulnerabilities | 27 across 7 CWE categories |
| Current binary findings (real) | 2 (gets(), system()) |
| Current false positives | ~0 (post-FP fix, over-suppression) |
| Source-mode baseline | 66.7% (18/27) |
| Total gap | 25 undetected vulns across 6 CWEs |
Per-CWE Breakdown
Gap #1: CWE-121 — Fortified Import Normalization (S effort, +4 vulns)
Root cause: Mach-O binaries built with -D_FORTIFY_SOURCE import
___strcpy_chk instead of strcpy. normalizedSymbolName() strips _ prefix
but not _chk suffix. Fix: strip _chk suffix (~5 lines in r2_backend.cpp:315).
Gap #2: CWE-134 — Binary Format String (S effort, +3 vulns)
Root cause: Binary mode cannot distinguish printf("Hello") (safe) from
printf(user_input) (vulnerable). Source mode has full AST-based detection.
Fix: use r2 axtj xref to find call sites, then disassemble to check if
format arg is constant .rodata string or variable register (~50 lines).
Gap #3: Hardcoded-String Re-Enable (S effort, +2 vulns)
Root cause: Post-Loop-1 FP fix was over-aggressive — killed all credentials.
Fix: add bare-secret patterns (supersecret, adminpass), add sk- prefix
API key pattern, add build-artifact path filter (~30 lines).
Gap #4: Binary BB Encoder Granularity (M effort, +6 vulns)
Root cause: binary_bb_encoder uses abstract_blocks = true which produces
unconstrained Z3 variables. Needs per-instruction pdbj parsing to fire
per-instruction hooks (sdiv→div-by-zero, ldr/str→null-deref, add/sub/mul→int-overflow).
Pipeline IS wired in scan_runner.cpp:3279, observer logic IS correct — encoder
needs per-instruction granularity (~100 lines).
Gap #5: Binary UAF Observer (M effort, +2 vulns)
Root cause: BinaryBBObserver has onPointerDerefIr but doesn't track
whether ptr was freed. Fix: add std::unordered_set<unsigned> freed_regs_,
track on free() calls, check on derefs (~80 lines).
Detection Ranked by Gain-per-Effort
| Rank | Gap | Effort | CWEs Closed | +Vulns | Cumulative |
|---|---|---|---|---|---|
| #1 | Fortified import _chk strip |
S | CWE-121 | +4 | 6/27 (22%) |
| #2 | Binary format-string xref | S | CWE-134 | +3 | 9/27 (33%) |
| #3 | Hardcoded-string re-enable | S | CWE-798 | +2 | 11/27 (41%) |
| #4 | BB encoder per-instruction | M | CWE-369,190,476 | +6 | 17/27 (63%) |
| #5 | Binary UAF observer | M | CWE-416 | +2 | 19/27 (70%) |
3 remaining FPs require interprocedural binary analysis or runtime fuzzing.