cybersecurity · intermediate · ~20 min

Detect format-string injection in user input

Implement a tolerant scanner that recognises real printf specifiers and ignores `%%`.

Challenge

Implement int has_format_specifier(const char *s) returning 1 if s contains a character sequence that looks like a printf format specifier, else 0.

For simplicity treat any % that is followed (after optional flags -+ 0#, optional width digits, optional .precision, optional length modifier l/h/ll/z) by one of d, i, u, x, X, o, c, s, p, n, f, e, g, a, A as a specifier.

A literal %% (escaped percent) does NOT count.

Examples

has_format_specifier("hello %s")     -> 1
has_format_specifier("100%%")        -> 0  // escaped
has_format_specifier("rate=99%")     -> 0  // % at end, no letter
has_format_specifier("count: %5d")   -> 1
has_format_specifier("plain text")   -> 0
has_format_specifier("%n is bad")    -> 1  // %n writes to memory — particularly dangerous

Why this matters

printf(user_input) lets an attacker read stack memory with %x %x %x, dereference pointers with %s, and even write to memory with %n. The fix is to always call printf("%s", user_input), never printf(user_input). This validator is a defensive last line of defence when you're unsure whether an upstream caller obeyed the rule.

Why this matters

printf(user_string) is one of the oldest and most-exploited bugs in C. The fix is to refuse user-supplied format strings before they reach printf. This exercise builds a lightweight pre-check that flags any % that isn't escaped or in a known-safe position.

Input format

Null-terminated string.

Output format

0 or 1.

Constraints

O(strlen).

Starter code

int has_format_specifier(const char *s) { /* TODO */ return 0; }

Common mistakes

Returning 1 for any % (false positives on 99%). Forgetting %%. Ignoring width/precision/length modifiers.

Edge cases to handle

%% (escape); % at end; %5d; %.2f; %n (especially dangerous).

Complexity

O(strlen).

Background lessons

Up next

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.