cybersecurity · intermediate · ~15 min · safe pentest lab
Convert the abstract 'deny-by-default' principle into concrete C: validate every attacker-controlled count and pointer before use, enforce a fixed allowlist with exact string comparison, and respect an output bound so a crafted payload can neither smuggle a privileged field through nor overflow the destination buffer.
A mass-assignment (a.k.a. auto-binding / object-injection) vulnerability happens when an API blindly binds every field in a client's request body to a server-side object. The attacker's asset of interest is the privileged columns your app never meant to expose: is_admin, role, balance, user_id. The insecure assumption is "the client only sends the fields on my form" — but an attacker controls the payload and can bolt on extra keys to escalate privileges or tamper with money.
The defensive fix is a strict allowlist applied before anything is written: deny by default, and copy through only the field names you have explicitly blessed as client-settable.
Implement filter_fields. You are given in, an array of n field-name pointers taken from a fixed, attacker-influenced payload baked into the harness. Copy into out (capacity ocap) only the pointers whose names are on your allowlist, and return how many you kept. Everything else — including privileged fields and near-miss look-alikes — is dropped.
The allowlist to enforce is exactly: name, email, bio, avatar_url, phone, theme.
n, ocap of 0 or negative, and NULL in/out: return 0, never dereference.out[ocap-1] — stop once you have kept ocap items.strcmp): "Role" and "is_admin " are not on the allowlist.NULL element inside in without counting it.filter_fields(payload, PN, out, 16) -> 4
where payload mixes four allowlisted fields with four privileged ones; only the four allowed names are copied to out.
in: pointer to an array of n C-string pointers (const char *const ), the candidate field names. n: number of entries in in. out: caller-provided array of const char with capacity ocap where kept pointers are written. ocap: maximum number of entries that may be written to out.
Returns the number of allowlisted field-name pointers copied into out (0..min(n,ocap)). out[0..return) hold the kept pointers, in their original order.
C11, freestanding function (harness supplies all includes). Deny by default: only name, email, bio, avatar_url, phone, theme pass. Match with strcmp (exact). Bounds-check n, ocap, and the NULL-ness of in/out BEFORE any access; never write beyond ocap; return 0 on any invalid argument. No allocation, no I/O, no mutation of the input strings.
int filter_fields(const char *const *in, int n, const char **out, int ocap)
{
/* TODO: implement an allowlist filter that copies only permitted
field-name pointers from in into out (respecting ocap), and drops
privileged fields. This stub does nothing and does not filter. */
(void)in; (void)n; (void)out; (void)ocap;
return -1;
}
Using a denylist instead of an allowlist (any new privileged field you forget to blacklist leaks through); comparing with == on pointers or with strncmp/prefix matching so near-miss names slip past; forgetting to check kept < ocap and overflowing out; dereferencing in/out or in[i] before NULL-checking; treating negative n as a huge unsigned loop bound; copying the strings instead of the pointers (unnecessary and error-prone).
n <= 0 returns 0; ocap <= 0 returns 0; NULL in or NULL out returns 0; NULL element inside in is skipped; more allowed fields than ocap stops exactly at ocap; look-alike names ('Role', 'is_admin ' with trailing space) are rejected because matching is exact; duplicate allowed names are each kept (subject to ocap).
Solve this exercise in the browser editor — compile and run against the test harness, no setup required.