cybersecurity · intermediate · ~15 min · safe pentest lab
Recognize the Zip Slip / path-traversal class and write a deny-by-default validator that flags absolute paths and `../` components as whole path components, without being fooled by lookalikes such as `a..b` or `..hidden`.
When software extracts an archive (zip, tar, jar), each entry carries a name that becomes a destination path. If the extractor trusts that name blindly, a malicious archive can smuggle in an entry like /etc/passwd or ../../root/.ssh/authorized_keys and write outside the intended output directory. This is the Zip Slip vulnerability, and it has led to remote code execution in many real projects.
The insecure assumption is "the entry name is a harmless relative path." Your job is to break that assumption before extraction by validating the name.
Implement:
int is_zip_slip(const char *entry);
Return 1 if entry is unsafe to extract, 0 if it is safe. An entry is unsafe when it is an absolute path (starts with /) or contains a ../ parent-directory traversal component. ".." only counts as traversal when it forms a whole path component (bounded by a / or the string start on the left and a / on the right) — so a..b/file and ..hidden are safe. NULL or an empty string is treated as safe (0); there is nothing to extract.
is_zip_slip(names[0]) -> 0 // "docs/readme.txt", safe
is_zip_slip(names[2]) -> 1 // "/etc/passwd", absolute
is_zip_slip(names[4]) -> 1 // "a/../../etc/x", traversal
Work only against the fixed name table baked into the harness. Never touch a real filesystem: this is detection logic, deny-by-default, running in a sandbox.
A single NUL-terminated C string entry naming one archive entry (a relative or absolute path). May be NULL or empty.
Return int 1 if the entry is unsafe to extract (absolute or contains a ../ traversal component), otherwise 0.
C11, freestanding function; the harness provides all includes. Do not read from disk, network, or the environment. Treat entry as untrusted, read-only input; do not modify it. NULL or empty => 0. Handle names up to a few hundred bytes; do not read past the terminating NUL.
int is_zip_slip(const char *entry){
/* TODO: implement the zip-slip safety check.
Return 1 if `entry` is unsafe to extract (absolute path, or
contains a `../` traversal component), else 0. NULL/empty => 0.
This insecure stub trusts every entry name and must be replaced. */
(void)entry;
return -1;
}
Using a plain substring search for ".." (flags a..b) or for "../" without checking the left boundary (flags a..b/c at the b/? no — but still flags foo..bar/ styles depending on logic); forgetting the absolute-path / prefix check; dereferencing entry before the NULL check; reading entry[i+1]/entry[i+2] past the NUL (safe here only because the NUL short-circuits, but sloppy indexing can over-read).
NULL pointer (=>0); empty string (=>0); bare / (=>1, absolute); leading ../ (=>1); traversal in the middle like a/../../x (=>1); trailing considerations aside, .. not followed by / such as ..hidden (=>0); .. embedded in a component like a..b/file (=>0); a component that ends in .. with no following slash is not a traversal.
Solve this exercise in the browser editor — compile and run against the test harness, no setup required.