cybersecurity · intermediate · ~15 min · safe pentest lab
Enforce per-object authorization on attacker-controlled identifiers: validate that each index is in range BEFORE dereferencing, then confirm ownership, applying deny-by-default so an IDOR attempt yields zero access.
An API endpoint lets a caller ask about a batch of object ids (rows, files, orders). A naive backend just counts or returns every id the client sends, trusting that the client would never ask for something it does not own. That trust is the vulnerability: an attacker enumerates ids they never should see, and out-of-range ids can even push the server into reading memory outside the ownership table.
The asset is an in-memory ownership table owner_of[0..nobj) mapping each object index to the id of the account that owns it. The threat is a caller supplying attacker-controlled ids[] containing indices they do not own, negative indices, or indices >= nobj.
Your task: implement count_owned so it returns how many of the requested ids[0..n) the caller_id may actually access. An id counts only when it is BOTH a valid index (0 <= id < nobj) AND owner_of[id] == caller_id. Deny by default and bounds-check every id BEFORE using it to index owner_of — never read the table out of range.
n <= 0) returns 0.>= nobj are rejected without indexing the table.nobj is one past the end and must be rejected.count_owned(mixed, 5, caller, owner_of, nobj) -> 3
A pointer ids to n requested object ids (int, may be negative or huge). caller_id is the requesting account. owner_of is an array of nobj ints where owner_of[i] is the account that owns object i.
An int: the number of entries in ids[0..n) that are a valid index into owner_of AND owned by caller_id.
0 <= result <= n. Never index owner_of with an index < 0 or >= nobj. n and nobj may be 0 or negative (treat as empty). ids and owner_of may be NULL when n/nobj indicate no work.
int count_owned(const int *ids, int n, int caller_id, const int *owner_of, int nobj){
/* TODO: implement bounds-safe ownership counting.
This insecure stub trusts every id without checking bounds
or ownership. Replace it. */
(void)caller_id; (void)nobj;
return n; /* wrong: counts all requested ids */
}Indexing owner_of[id] before checking bounds (out-of-range read); using id <= nobj instead of id < nobj (off-by-one one past the end); checking only ownership but not the lower bound, letting negative ids through; returning n outright; forgetting that duplicate valid ids should each count.
Empty or non-positive n returns 0; negative ids rejected before indexing; id == nobj and id > nobj rejected; very large ids rejected without overflow into the table; duplicate owned ids counted each time; a caller with no owned objects gets 0.
Solve this exercise in the browser editor — compile and run against the test harness, no setup required.