After the last lesson the Hacker added protection that strips the semicolon ; and && from your input. But a shell has many ways to chain commands besides those two.
The same diagnostics tool, but the developer added protection that strips ; and && from your input. The shell has other command separators the filter does not block.
Read the flag file again, bypassing the stripping of ; and &&.
You bypassed the stripping of ; and && with another chaining symbol and read the flag.
Why the solution worked
The protection was a blacklist that strips two symbols, but a shell has dozens of symbols and ways (|, $(), backticks, newline, &, and more), so stripping some is not enough. The fix is the same: do not go through a shell at all; pass arguments as an array (execFile). A blacklist of symbols always fails because the attacker finds one you did not think of.
The root cause
The blacklist blocks only two separators while the shell has many — pipe, newline, command substitution — so the bug remains; the root is still passing input to the shell.
How the code is fixed
Stripping shell symbols is a blacklist that is always broken. Get rid of the shell instead of chasing its symbols.
Vulnerable code
const host = req.body.host.replace(/;|&&/g, ''); // strips two of many
exec('ping -c 1 ' + host, ...);
Safe code
if (!/^[a-z0-9.-]+$/i.test(req.body.host)) return res.status(400).end();
execFile('ping', ['-c', '1', req.body.host], ...); // no shell, nothing to chain
Impact in the real world
Shell-metacharacter blacklists are always broken by a separator the developer did not think of, and server takeover stays possible.
How defenders detect it
Defenders drop blacklists, invoke the tool with separate arguments and no shell, and monitor for unexpected child processes.